尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

OpenCoworker 消息与会话架构指南:Channel Subscription 与 Inbox 的双机制设计

OpenCoworker 消息与会话架构指南:Channel Subscription 与 Inbox 的双机制设计 OpenCoworker 消息与会话架构指南Channel Subscription 与 Inbox 的双机制设计【免费下载链接】aisuiteSimple, unified interface to multiple Generative AI providers项目地址: https://gitcode.com/GitHub_Trending/ai/aisuite本篇技术指南围绕 OpenCoworker 的platform/docs/MESSAGING-AND-SESSIONS.md设计文档展开讲解消息平台Slack/Telegram如何接入 persona 会话Channel Subscription频道订阅入站 pub/sub与Inbox收件箱定向 request/reply两套语义相反、却共享同一消息通道的机制以及它们如何建立在会话永不结束的持久化模型之上。读完本文你将掌握 OpenCoworker 的订阅工具链subscribe_channel/unsubscribe_channel/list_subscriptions/get_channel_messages、Inbox 的关联correlation与状态机原理、镜像到频道的交互式按钮interactive buttons方案以及基于单一 Slack 机器人身份的分层路由设计并能在源码层面定位每个机制的实现。一、核心洞察把消息接入会话拆成两件相反的事连接消息平台与会话本质上是两种不同的语义把它们命名并拆开二者都会变得简单维度Channel Subscription频道订阅Inbox收件箱模式publish / subscribe广播request / reply点对点方向入站、环境式ambient代理提问 → 用户作答关联性无——消息只是对订阅者可见关键——答案必须回到一个会话 一次工具调用扇出一个频道可对应多个会话这是特性每个条目恰好一个等待者代理状态忙碌时到达 一条 steering空闲时到达 新的一轮代理阻塞等待答案让两者共享一个通道的判别器discriminator一条入站消息若携带[ocw:id]令牌或是对已投递 Inbox 消息的线程回复它就是一个Inbox 答案——它解决对应条目并被消费绝不广播。任何其他入站消息都是频道消息——扇出给每个订阅的会话。这正是既有的reply_resolver先于 handler模式见 gateway.py的推广。在源码层面这个判别逻辑由 Gateway._on_inbound 实现消息先经每平台 allowlist 校验然后_reply_resolver尝试把消息当作 Inbox 回复消费命中[ocw:id]即返回并停止分发未命中才交给普通 handler_dispatch_inbound做频道扇出或 DM 路由。二、会话是持久的——它们永远不会结束会话不绑定到某个 socket、进程或单轮对话。它活在会话存储conversation store里永远可恢复——一条用户消息、一次 self-wake、一次 Inbox 解决、一条频道消息都能在任何时刻唤醒任意会话忙碌时 → 作为 steering 注入当前轮空闲时 → 开启一轮新的后台对话全程不需要存活的 socket。由此推出三个实际结论会话不会真正死亡。唯一会移除会话的是用户显式删除。GUI 关闭、socket 掉线、进程重启都不会结束会话——它们只意味着下一次交互从持久化的线程中恢复它。持久标识绑定session_id。它是稳定句柄永远解析到一个可恢复的会话。这也是parked-Inbox-prompt模型成立的深层原因见 PERMISSIONS-AND-INBOX.md提示属于会话而不属于连接。订阅是持久的。一条(session_id, channel)订阅在显式拆除前永久有效不会因会话闲置或服务器重启而丢失。删除会话是唯一的隐式退订。实现佐证删除会话时SubscriptionStore.remove_session 会清掉该会话的全部订阅InboxStore.resolve_session 会把该会话所有仍 pending 的 Inbox 条目一并关闭——孤儿化的审批/提问永远无法被有意义地回答。三、Channel Subscription入站 pub/subv1 已构建订阅 ≠ Inbox 路由。路由是出站的把代理的审批/提问镜像 OUT 到 DM/频道request↔reply由[ocw:id]关联。订阅是入站的把频道消息带 IN广播。两者应放在不同的频道上——把 Inbox 指向一个你同时订阅的频道会混淆两个方向有一个守卫在冲突时告警。核心规则与实现订阅 一条持久化的(session_id, channel)记录channel是地址platform:chat_id如slack:C0123与网关的format_target/parse_target对应。多个会话可订阅同一频道两个代理、两种反应。由代理通过工具创建subscribe_channel/unsubscribe_channel/list_subscriptions见 subscription_tools为消息型 persona 注册。代理通过ask_user引导用户告知频道来 bootstrap——它不能自主知道频道。Slack 把带类型的#alerts编码成#C0123|name因此subscribe_channel能直接从回答里解析出 id裸地址同样可用。GUI 频道选择器是后续锦上添花。投递一条无令牌的频道消息 → 每个订阅会话经由 self-wake 路径忙碌 →queue_steering空闲 → 一轮新的后台对话。每个代理依据自身的 role/prompt判断消息是否相关——这个判断本身就是按代理的路由。无订阅者的频道消息会被缓冲但不会投递给任何人无订阅的 DM会落到默认 super-agent 会话。补课工具get_channel_messages(channel, n)返回内存环形缓冲ring buffer里最近 n 条消息缓冲在消息到达时填充代理可先订阅再问我错过了什么。回复复用既有的send_message(target, text)工具target 指向投递消息里携带的频道地址。防循环是免费的Slack 适配器已丢弃机器人自己的消息bot_id/subtype/userbot_user_id。单一机器人身份下每个代理的频道发帖都是机器人因此代理↔代理、自我循环都在源头被过滤。v1 过滤器被订阅的频道投递全部非机器人、非令牌的消息——订阅本身就是过滤器。仅提及及其线程是后续细化入站MessageEvent尚未携带是否 了机器人机器人用户 id 已知这是干净的后置适配器增强。3.1 实现细节SubscriptionStore 与地址解析SubscriptionStore 是一个带线程锁、JSON 持久化的简单存储默认落盘为subscriptions.json见 manager.pysubscribe幂等同(session_id, channel)已存在时只更新 filterunsubscribe返回是否真的移除了记录for_channel/for_session/all供分发与查询。地址解析resolve_channelsubscriptions.py支持四种输入输入示例结果Slack#mention令牌#C0123|alertsslack:C0123Slack Copy link URLhttps://acme.slack.com/archives/C0123ABCslack:C0123ABC大小写归一完整地址slack:C0999原样返回裸 chat idC0777按默认平台补全 →slack:C0777注意裸#general这类频道名无法本地解析会返回——字面存储一个名字只会造出永不匹配真实流量slack:C…的订阅所以必须失败。测试 test_resolve_channel 覆盖了全部这些分支。3.2 实现细节ChannelBuffer 环形缓冲与补课ChannelBuffer 按频道保存最近 N默认 50条消息可带state_path做 best-effort JSON 持久化channels.jsonrecord(channel, who, text, name…)在每条入站频道消息时填充即使该频道没有订阅者见 _dispatch_inboundrecent(channel, n)供get_channel_messages使用channels()提供最近见过的频道列表供将来的频道选择器使用损坏的缓冲文件绝不阻塞启动静默从空开始旧格式裸 messages dict也能兼容加载——这些都在 test_channel_buffer_persists_across_restarts 中验证。3.3 实现细节入站分发与扇出核心分发在 SessionManager._dispatch_inbound构造MessageSource结构化 sidecar仅展示用保留原始文本频道/群组消息 → 先缓冲无论是否订阅→ 查订阅者 → 逐会话调用deliver_to_session带mentions_me的提及走专门的提及路由_route_mention§31订阅会话必须回答未订阅频道则 spawn 每线程会话无订阅者的频道 → 静默返回消息已缓冲DM或任何非频道→ 投递给用户指定的 DM 会话未指定则记录为 unroutedno DM session designated。分发本身复用deliver_to_sessionmanager.py会话运行中 →engine.queue_steering不启动会冲突的并发轮次空闲 → 开启后台轮次并把事件流广播给正在查看该会话的 socket。这与 self-wake 共用同一条路径测试 test_dispatch_fans_out_to_subscribers 验证了双订阅者扇出、无订阅者缓冲、未指定 DM 会话 parked 三种情形。3.4 Slack 身份约束为什么按代理 mention原生行不通OpenCoworker 以一个 bot 用户连接一个 token 一个 Slack 身份。原生mention解析到这一个 bot——因此无法把 Ops 与 Research 当作两个独立 Slack 用户来mention除非每个 persona 各装一个 Slack app代价沉重且毁掉两次点击订阅的体验。于是提及分为两层Bot 级提及 激活过滤器原生单一身份。一条消息OpenCoworker了 bot、或是DM、或是 bot 所在的线程才被标记为给我而非频道闲聊。这是噪声/成本过滤器——不是按代理的路由。订阅 代理角色 精细路由。频道上的 bot 提及会投递给所有订阅会话每个会话从自己的 prompt 判断相关性。无需按代理身份。可选的显式定位 文本约定而非 Slack 提及。例如OpenCoworker ops: …按订阅声明的关键词从文本解析。锦上添花。默认过滤器只投递 bot 提及 / DM / 线程内回复空闲代理不会因繁忙频道的每行文字被唤醒。订阅可选全部消息用于专用频道。无频道上下文的 DM → 用户的默认 persona 会话即旧的 super-agent 角色。按 persona 的 Slack 身份OpsCoworker作为独立队友体验更好但属于 multi-install managed-connector 的故事——留作未来/付费路径而非 v1。四、把 Inbox 镜像到频道交互式按钮v1 已构建用户如何从 Slack回答一个 Inbox 提示不是用[ocw:id]回复文本Slack 不会在回复中保留令牌——一个裸 yes 就丢了它那条路很脆弱。取而代之的是镜像条目是一张Block Kit 卡片 按钮条目 id 藏在每个按钮的value里{id:…, r:allow}。点击发送一个 interaction 负载指名精确的条目 选项——无歧义、无令牌、无线程跟踪。离散选项 → 按钮审批Approve/Deny、ask_user的options每个选项一个按钮。interactions.buttons_for 构建它们encode/decodeinteractions.py拥有 value 的含义(item_id, resolution)。自由文本答案不通过消息提供已与 Rohit 商定此类提示显示open the app to respond——用户在 App 里输入。[ocw:id]令牌仅作为这些场景的 legacy 回复兜底。入站socket mode 在同一连接上投递点击无需公开端点——只需在 Slack app 中启用 Interactivity。链路SlackAdapterapp.action(ocw_*)→ gateway → manager._on_interaction →inbox.resolve(id, r)→ 按钮被替换为结果文本✅ Approved by you。解决会释放任何挂起的代理与应用共享 first-responder-wins。Provider 无关Button(label, value)由适配器原生渲染Telegram inline keyboard plan/directory 按钮是后续工作。v1 单选Slack。在 mirror_inbox_item 中可以看到镜像逻辑有按钮走gateway.deliver_interactive无按钮自由文本问题/通知走纯文本 Open the app to respond. [ocw:id]兜底。这大幅淘汰了脆弱的令牌回复路径的常见场景——按钮携带 id。相关测试见 test_interactions.py 与 test_gateway_inbox_reply.py。五、Inbox 关联定向 request/replyInbox 条目的id 就是关联键按钮的 value或 legacy 的[ocw:id]令牌携带它。1. 活体关联——已经可用。提问会话的 engine 在工具循环中途挂起于await store.wait(item.id)见 InboxStore.wait。按 id 解决会精确触发那一个await——正确的会话 正确的工具调用在结构上得到保证——答案回到精确的 engine 中的精确位置。进程存活期间无需额外簿记。2. 持久关联——难点尚未构建。若服务器重启或会话在答案到达前被驱逐内存中的await就没了。要存活于此Inbox 条目必须持久化足够信息以重建挂起session_idtool_call_id 工具名/参数恢复时重建会话 engine把答案作为该tool_call_id的工具结果注入然后继续那一轮而非重新调用工具。当前条目持有session_id但不持有tool_call_id解决只触发活体等待者。注意实现进度已超出设计文档——InboxItem现已在tool_call_id字段与for_tool_call幂等查询见 inbox.py。3.ask_user工具——通用 QA 原语新增。审批allow/deny已走 permission/approver 路径。自由文本 QA哪个 region需要一个一等公民工具代理调用它 → 创建KIND_QUESTION条目 → 阻塞 → 返回答案字符串——即对 approver 的泛化。它是(session_id, tool_call_id)捕获的天然主人。实现见 question_askeradd_question支持options/allow_text/multi→ 镜像或question_requested事件 →await manager.inbox.wait(item.id)返回答案。4. 审批没有工具级 id——所以Inbox 条目 id 是它们生成的关联句柄同时也捕获 engine 的 tool_call若存在。5.1 Inbox 条目状态机反竞态契约InboxStore 是权威存储每条目pending → resolved恰好解决一次幂等 first-responder-wins——从任何界面App 内、Slack、恢复后的 composer回答都安全。条目类型approval/question/notification/directory代理申请文件夹权限/plan代理提交计划待批见 inbox.py可见性inlineattended 会话在 composer 内回答服务端 parked、重连时重投vsinbox会话设为 Unattended进入跨会话 Inbox 队列——同一份 parked、可 await、随处可解决的记录只是可见性不同inbox.pyresolve恰好一次返回是否首次解决并触发等待者inbox.py恢复对账reconcile_on_resume在用户恢复 attended 控制时把该会话仍 pending 的条目 inline 呈现 汇总离开期间已答的条目inbox.pyapprover 路由inbox_approver把权限请求变成条目并挂起代理把 resolution 映射为ApprovalOutcomeallow → ONCE、always → ALWAYS_TOOL、否则 DENY见 inbox.py。六、与已构建内容的关系connectors/gateway.py——reply_resolver消费[ocw:id]→resolve_from_reply、allowlist、入站分发。频道订阅扩展了它非令牌 → 扇出给订阅者。inbox_routing.py—— 命名 inbox 队列 绑定Slack/Telegramdeliver嵌入[ocw:id]。订阅是出站绑定的入站对应物。路由层级为per-session override persona default defaultroute_for。inbox.py——InboxStore 状态机 wait/resolve。Self-wake busy→steer / idle→new-turnmanager.resume_due_wakes—— 原样复用于入站频道投递deliver_to_session。[ocw:id]令牌的解析细节见 resolve_from_reply命中令牌后按 allow/deny 关键词approve/allow/yes//✅、deny/reject/no//❌解析为 allow/deny否则把整条消息去掉令牌后当作自由文本答案。七、已定的决策2026-06-27 / 06-28机制层面06-27两种机制channel subscriptionpub/sub无关联vsInboxrequest/reply有关联[ocw:id]令牌在共享通道上判别入站。频道订阅 (session_id, channel, filter?)每频道多会话经 busy→steer / idle→new-turn 投递get_channel_messages补课工具。单一 bot 身份。Bot 提及/DM/线程 激活过滤器默认订阅 代理角色 路由可选文本关键词 显式定位按 persona 的 Slack 身份 未来路径。无频道的 DM → 默认 persona 会话。Inbox条目 id 关联键新增ask_user工具作为通用 QA 原语审批用条目 id 作为关联句柄。持久恢复v1 为 best-effort live-only重启孤儿化的问题由代理重新提出硬化版 replay-as-tool-result持久化tool_call_id恢复时作为工具结果注入是 Phase-2 后续。频道订阅构建层面06-27/28会话持久永不结束只有显式用户删除才移除持久绑定用session_id。订阅 INBOUNDInbox 路由 OUTBOUND——正交方向守卫在订阅与路由目标冲突于同一频道时告警subscribe_channel里实现见 subscriptions.py。订阅 持久化(session_id, channel)永久直到显式退订代理工具或用户或会话删除唯一隐式拆除跨重启存活。代理工具创建subscribe_channel/unsubscribe_channel/list_subscriptions代理经ask_userbootstrap 从用户学习频道。Slack#channel提及令牌#id|name解析出 idv1 不做 name→id API 查找。v1 过滤器 订阅本身投递订阅频道全部非 bot 消息。提及/线程过滤推迟需适配器暴露botbot 用户 id 已知是干净的后续增强。防循环已由适配器处理丢弃 bot 自身消息单一 bot 身份也使代理↔代理循环不可能。get_channel_messages 内存环形缓冲每频道最近 N 条非 Slack history API。投递复用manager.deliver_to_sessionbusy→steer / idle→后台轮次与 self-wake 共享经send_message回复频道地址。从 Slack 回答 Inbox 提示 按钮而非自由文本回复。条目 id 在按钮 value 里Block Kitsocket-mode action 回调 →inbox.resolve关联无歧义脆弱的[ocw:id]-in-reply 路径基本退役。消息上不提供自由文本——用户打开 App令牌仅作 legacy 兜底。v1 单选、审批 ask_user 选项、SlackTelegram inline keyboard plan/directory 后续。八、未决问题 / 后续工作✅ 订阅持久化——已完成subscriptions.jsonSubscriptionStore。GUI查看/管理每会话订阅仍未构建v1 由代理工具驱动HTTP 端点在create_app中已有/v1/subscriptions、/v1/channels/recent等见 test_subscriptions.py。授权订阅是网关 allowlist 之上的每会话 opt-in——确认信任模型谁能把会话订阅到哪些频道。v1 允许代理自行订阅。提及/线程过滤在入站MessageEvent上暴露botSlack 适配器有bot_user_id让订阅可选仅提及。Telegram 的 mention / 线程 类比群 vs DM——映射同样的过滤器概念。频道名→id 解析 / 频道选择器让用户在 GUI 里说 #alerts而非只能在 Slack 里拿编码后的 id。九、快速索引设计文档MESSAGING-AND-SESSIONS.md本文主体、PERSONAS.md会话、self-wake、无 always-on、PERMISSIONS-AND-INBOX.mdInbox、路由、gateway、IMPLEMENTATION-LEDGER.md订阅实现subscriptions.pySubscriptionStore/ChannelBuffer/resolve_channel/subscription_tools入站网关gateway.pyreply_resolver、allowlist、deliver/deliver_interactive会话分发server/manager.py_dispatch_inbound/deliver_to_session/mirror_inbox_item/_on_interaction/_resolve_inbox_replyInbox 存储inbox.pyInboxStore/InboxItem/inbox_approver路由与交互inbox_routing.py绑定 [ocw:id]关联、interactions.pyButton/encode/decode/buttons_for测试test_subscriptions.py、test_interactions.py、test_gateway_inbox_reply.py、test_inbox_routing.py、test_mention_router.py【免费下载链接】aisuiteSimple, unified interface to multiple Generative AI providers项目地址: https://gitcode.com/GitHub_Trending/ai/aisuite创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表