Trang này ban đầu là một đề xuất thiết kế hướng tới tương lai. Phần cốt lõi của
thiết kế đó sau này đã được phát hành trong
src/channels/message/* và các đường dẫn con công khai
openclaw/plugin-sdk/channel-outbound / channel-inbound. Đối với
API hiện tại, hãy sử dụng API gửi đi của kênh và
API nhận vào của kênh. Trang này theo dõi những gì
đã được phát hành, những điểm triển khai khác với bản phác thảo ban đầu và những gì
vẫn còn bỏ ngỏ.Tại sao việc tái cấu trúc này diễn ra
Ngăn xếp kênh phát triển từ một số bản sửa lỗi cục bộ: các trình trợ giúp nhận vào riêng biệt cho từng mức độ hoàn thiện (runtime.channel.inbound.run cho các bộ điều hợp đơn giản,
runtime.channel.inbound.runPreparedReply cho các bộ điều hợp phong phú), các trình trợ giúp điều phối phản hồi
cũ (dispatchInboundReplyWithBase, recordInboundSessionAndDispatchReply),
phát trực tuyến bản xem trước dành riêng cho từng kênh và độ bền của lần phân phối cuối cùng được bổ sung vào
các đường dẫn tải trọng phản hồi hiện có. Cấu trúc đó tạo ra quá nhiều khái niệm công khai và
quá nhiều nơi mà ngữ nghĩa phân phối có thể sai lệch.
Khoảng trống về độ tin cậy buộc phải thiết kế lại:
Những gì đã được phát hành
Miền nội bộ nằm trongsrc/channels/message/*:
Bề mặt công khai:
openclaw/plugin-sdk/channel-outbound (các trình trợ giúp gửi/biên nhận/bền vững/trực tiếp/pipeline phản hồi)
và openclaw/plugin-sdk/channel-inbound (ngữ cảnh nhận vào, runChannelInboundEvent,
dispatchChannelInboundReply). Xem các trang đó để biết ví dụ về bộ điều hợp, tên kiểu hiện tại
và ghi chú di chuyển — chúng là nguồn thông tin chuẩn xác cho cấu trúc API,
không phải các bản phác thảo bên dưới.
Ngữ cảnh gửi
withDurableMessageSendContext cung cấp cho mã kênh các bước render, previewUpdate,
send, edit, delete, commit và fail xoay quanh một thông báo
gửi đi. sendDurableMessageBatch là trình bao bọc cho trường hợp phổ biến: kết xuất, gửi,
sau đó ghi nhận khi sent/suppressed hoặc báo lỗi khi có lỗi.
sendDurableMessageBatch trả về một kết quả phân biệt:
Độ bền là một trong
required, best_effort hoặc disabled
(MessageDurabilityPolicy trong src/channels/message/types.ts). required
đóng an toàn khi không thể ghi ý định bền vững; best_effort chuyển tiếp
sang gửi trực tiếp khi không có khả năng lưu bền; disabled giữ nguyên
hành vi gửi trực tiếp trước khi tái cấu trúc. Các trình trợ giúp tương thích cũ mặc định dùng
disabled và không suy ra required chỉ vì một kênh có bộ điều hợp
gửi đi chung.
Ranh giới vẫn còn nguy hiểm: sau khi lệnh gọi nền tảng thành công và trước khi
biên nhận được ghi nhận. Nếu tiến trình dừng tại đó, lõi không thể biết liệu
thông báo nền tảng có tồn tại hay không, trừ khi bộ điều hợp khai báo reconcileUnknownSend.
Hook đó phân loại một lần gửi bị gián đoạn thành sent, not_sent hoặc
unresolved; chỉ not_sent cho phép phát lại. Các kênh không có khả năng đối soát
sẽ quay về trạng thái unknown_after_send (src/channels/message/state.ts,
src/infra/outbound/delivery-queue-recovery.ts) và chỉ có thể chọn phát lại
ít nhất một lần nếu các thông báo hiển thị trùng lặp là một sự đánh đổi có thể chấp nhận
và đã được ghi lại cho kênh đó.
Ngữ cảnh nhận
createMessageReceiveContext theo dõi trạng thái xác nhận/từ chối xác nhận cho mỗi sự kiện nhận vào với
ack() có tính lũy đẳng và nack(error) tường minh. Chính sách xác nhận
(ChannelMessageReceiveAckPolicy) là một trong:
Cơ chế thăm dò Telegram sử dụng điều này để lưu bền một mốc cập nhật đã hoàn tất an toàn
(
safeCompletedUpdateId trong extensions/telegram/src/bot-update-tracker.ts):
grammY vẫn quan sát mọi bản cập nhật khi chúng đi vào chuỗi phần mềm trung gian, nhưng
OpenClaw chỉ đẩy mốc khởi động lại đã lưu qua các bản cập nhật
đã hoàn tất điều phối, nên các bản cập nhật thất bại hoặc vẫn đang chờ sẽ được phát lại sau khi khởi động lại.
Độ lệch getUpdates thượng nguồn của Telegram vẫn do grammY sở hữu; một nguồn thăm dò
hoàn toàn bền vững kiểm soát việc phân phối lại ở cấp nền tảng ngoài
mốc này vẫn chưa được xây dựng (xem Các câu hỏi mở).
Bản xem trước trực tiếp
src/channels/message/live.ts mô hình hóa xem trước/chỉnh sửa/hoàn tất thành một vòng đời:
createLiveMessageState, markLiveMessagePreviewUpdated,
markLiveMessageFinalized, markLiveMessageCancelled và
deliverFinalizableLivePreviewAdapter (xây dựng một bản chỉnh sửa cuối cùng từ bản nháp, áp dụng
nó và quay về gửi bình thường khi không thể chỉnh sửa hoặc chỉnh sửa thất bại).
LiveMessageState.phase là idle | previewing | finalizing | finalized | cancelled; canFinalizeInPlace kiểm soát liệu bản xem trước có thể trở thành thông báo cuối cùng
thông qua chỉnh sửa thay vì gửi mới hay không.
Biên nhận bền vững
MessageReceipt (src/channels/message/types.ts) chuẩn hóa một hoặc nhiều
id thông báo nền tảng từ một lần gửi logic thành platformMessageIds cộng với
parts cho từng phần (loại, chỉ mục, id luồng, id phản hồi). Một id chính được giữ lại
để tạo luồng và chỉnh sửa sau này. Đây là điều giúp các lần phân phối nhiều phần (văn bản
cộng với nội dung đa phương tiện, văn bản được chia đoạn, phương án dự phòng cho thẻ) có thể phát lại và loại bỏ trùng lặp sau
khi khởi động lại.
Thu gọn SDK công khai
Quá trình tái cấu trúc đã hấp thụ hoặc ngừng dùng:reply-runtime, reply-dispatch-runtime,
reply-reference, reply-chunking, các trình trợ giúp reply-payload được công khai dưới dạng
API, inbound-reply-dispatch, channel-reply-pipeline và hầu hết cách sử dụng công khai
của facade gửi đi cũ. src/plugin-sdk/channel-message.ts hiện là một
barrel tái xuất @deprecated trỏ đến channel-outbound /
channel-inbound; các bí danh thời gian chạy channel.turn đã bị xóa và trang tài liệu
/plugins/sdk-channel-turn cũ chuyển hướng đến
API nhận vào của kênh. Mã plugin mới nên
nhắm trực tiếp đến channel-outbound và channel-inbound.
Những điểm triển khai khác với thiết kế ban đầu
Bản phác thảo thiết kế bên dưới chưa bao giờ được phát hành đúng theo mô tả. Hồ sơ được giữ lại để đảm bảo tính chính xác lịch sử; không coi các tên kiểu này là API hiện tại.- Không có
MessageOrigin/shouldDropOpenClawEcho. Kế hoạch ban đầu yêu cầu một thẻ nguồnsource: "openclaw"trên các thông báo lỗi Gateway cùng với một vị từ dùng chung loại bỏ các tiếng vọng do bot tạo có gắn thẻ trong các phòng dùng chung trước khi ủy quyềnallowBots. Kiểu và vị từ đó không tồn tại trong cơ sở mã. Bản thânallowBotslà một khóa cấu hình thực tế cho mỗi kênh (Slack, Discord, Google Chat và các kênh khác), nhưng cơ chế gắn thẻ nguồn vốn được dùng để bảo vệ nó chưa bao giờ được xây dựng. Việc ngăn tiếng vọng lỗi Gateway trong các phòng bật bot vẫn là một khoảng trống chưa giải quyết, không phải một bảo đảm đã được phát hành. - Không có không gian tên
core.messages.receive/send/live/statethống nhất. Các hàm đã phát hành nằm trực tiếp trongsrc/channels/message/*(withDurableMessageSendContext,createMessageReceiveContext,createLiveMessageState,classifyDurableSendRecoveryState) thay vì nằm sau facadecore.messages.*. - Không có kiểu thông báo chuẩn hóa chung
ChannelMessage/MessageTarget/MessageRelation. Lõi vẫn truyền các tải trọng phản hồi cụ thể (ReplyPayload) và ngữ cảnh dành riêng cho từng kênh qua các bộ điều hợp gửi thay vì một cấu trúc thông báo trung lập với nền tảng có quan hệkind: "reply" | "followup" | "broadcast" | "system". - Tên chính sách xác nhận khác với bản phác thảo. Đã phát hành:
after_receive_record | after_agent_dispatch | after_durable_send | manual. Bản phác thảo ban đầu sử dụngimmediate | after-record | after-durable-send | manualvới trường lý do hết thời gian chờ webhook; cấu trúc đó chưa được xây dựng. - Các khóa khả năng
DurableFinalDeliveryRequirementMapđã thay thế đối tượngMessageCapabilitiestrong bản phác thảo. Các khả năng là các cờ boolean phẳng (text,media,poll,payload,silent,replyTo,thread,nativeQuote,messageSendingHooks,batch,reconcileUnknownSend,afterSendSuccess,afterCommit) được xác minh thông quaverifyDurableFinalCapabilityProofsthay vì một cấu trúc lồng nhau theo kiểutext.chunking/attachments.voice.
Các rủi ro di chuyển cụ thể (vẫn còn liên quan)
Các hiệu ứng phụ dành riêng cho từng kênh này có trước quá trình tái cấu trúc và phải tiếp tục hoạt động thông qua các đường dẫn gửi mới. Chúng không phải là giả định: từng hiệu ứng đều được triển khai và đang giữ vai trò thiết yếu hiện nay.- iMessage (
extensions/imessage/src/monitor/echo-cache.ts,persisted-echo-cache.ts): trình giám sát ghi các tin nhắn đã gửi vào bộ nhớ đệm phản hồi sau khi gửi thành công. Các lượt gửi cuối cùng bền vững vẫn phải điền vào bộ nhớ đệm đó, nếu không OpenClaw có thể tái nhập chính các phản hồi của mình dưới dạng tin nhắn người dùng gửi đến. - Tlon (
extensions/tlon/src/monitor/index.ts): thêm một chữ ký mô hình tùy chọn và ghi lại các luồng đã tham gia sau khi trả lời nhóm. Cơ chế phân phối bền vững không được bỏ qua các hiệu ứng đó. - Discord và các bộ điều phối đã chuẩn bị khác đã tự quản lý việc phân phối trực tiếp và hành vi xem trước. Một kênh chưa bền vững từ đầu đến cuối cho đến khi bộ điều phối đã chuẩn bị của kênh đó định tuyến rõ ràng các kết quả cuối cùng qua ngữ cảnh gửi; không được giả định rằng chỉ riêng adapter chung đã bao phủ trường hợp này.
- Cơ chế phân phối dự phòng im lặng của Telegram phải phân phối toàn bộ mảng payload đã chiếu, không chỉ payload đầu tiên, sau khi chia đoạn/chiếu dự phòng.
- LINE, Zalo, Nostr và các đường dẫn trợ giúp tương tự có thể có cơ chế xử lý token trả lời, proxy phương tiện, bộ nhớ đệm tin nhắn đã gửi hoặc đích chỉ dành cho callback. Chúng vẫn sử dụng cơ chế phân phối do kênh quản lý cho đến khi các ngữ nghĩa đó được biểu diễn bởi adapter gửi và được kiểm thử bao phủ.
- Các trình trợ giúp DM trực tiếp có thể có callback trả lời là đích truyền tải chính xác duy nhất. Cơ chế gửi đi chung không được suy đoán đích từ các trường nền tảng thô rồi bỏ qua callback đó.
Phân loại lỗi
Các adapter phân loại lỗi truyền tải thành các danh mục đóng kiểuDeliveryFailureKind
(tạm thời, giới hạn tốc độ, xác thực, quyền, không tìm thấy, payload không hợp lệ,
xung đột, đã hủy, không xác định). Chính sách lõi:
- Thử lại các lỗi tạm thời và lỗi giới hạn tốc độ.
- Không thử lại các lỗi payload không hợp lệ trừ khi có phương án dự phòng khi kết xuất.
- Không thử lại các lỗi xác thực hoặc quyền cho đến khi cấu hình thay đổi.
- Khi không tìm thấy, cho phép quá trình hoàn tất trực tiếp chuyển từ chỉnh sửa sang gửi mới khi kênh tuyên bố việc đó là an toàn.
- Khi xảy ra xung đột, sử dụng trạng thái biên nhận/tính lũy đẳng để xác định tin nhắn đã tồn tại hay chưa.
- Mọi lỗi xảy ra sau khi lệnh gọi nền tảng có thể đã thành công nhưng trước khi commit
biên nhận sẽ trở thành
unknown_after_send, trừ khi adapter chứng minh thao tác trên nền tảng đã không xảy ra.
Câu hỏi còn bỏ ngỏ
- Liệu cuối cùng Telegram có nên thay thế trình chạy polling grammY (
1.43.0) bằng một nguồn polling hoàn toàn bền vững, kiểm soát việc phân phối lại ở cấp nền tảng, thay vì chỉ watermark khởi động lại được duy trì của OpenClaw (safeCompletedUpdateId) hay không. - Liệu trạng thái xem trước trực tiếp nên nằm trong cùng bản ghi với ý định gửi cuối cùng hay trong một kho trạng thái trực tiếp đồng cấp.
- Liệu việc ngăn phản hồi lặp lại khi Gateway gặp lỗi trong các phòng dùng chung có bật bot có cần cơ chế gắn thẻ nguồn gốc đã được lên kế hoạch ban đầu, một hợp đồng đơn giản hơn cho từng kênh, hay nằm ngoài phạm vi.
- Những kênh nào hỗ trợ nguồn gốc/siêu dữ liệu gốc để ngăn phản hồi lặp lại giữa các bot, so với những kênh cần một sổ đăng ký gửi đi được duy trì.