# 13 Bot 工作组 > 状态:**一期(2026-10-26)、二期与三期(2026-10-27)已实施**;实施中的调整见 §15–§19。决策见 ADR-0023(工作组协作模型)、ADR-0024(Bot API 与独立的 Bot Key)。 ## 1. 背景与目标 现有的 AgentServer 是**单 Agent 执行内核**:Profile 定义角色,Session 保存一段对话,Run 执行一轮,MCP Gateway 提供工具,过程以事件形式记录、可回放。使用方式是"一个人对着一个 Agent"。 本功能在内核之上增加**协作层**: - 后台可以配置多个不同岗位的**机器人**,每个机器人有自己的技能 - 使用者创建**工作组**,拉入机器人后下达命令 - 复杂任务交给**组长**,由组长拆分并分派给**组员**,直到完成 - 使用者也可以直接 @ 某个组员,让它完成单项工作 通过独立的 **Bot API** 对外提供,与现有的 Agent API(`/v1`)不共用 Key。 不在范围内: - 多用户(工作组里只有一个使用者,见 §3) - 跨工作组的记忆 - 外部 IM 平台接入:由外部桥接服务调用 Bot API 实现,见 §10 ## 2. 术语 | 术语 | API | 含义 | |---|---|---| | 机器人 | `bot` | 后台配置的一个岗位:身份(名称、头像、岗位描述)+ 关联的 Profile(模型、技能、MCP 服务、资料库、权限) | | 组长 | `lead` | 类型为组长的机器人:负责拆分任务、分派、汇总,自身不带业务技能 | | 组员 | `worker` | 类型为组员的机器人:执行具体工作 | | 工作组 | `team` | 一次协作的容器:1 个成员 + 0–1 个组长 + 0–多个组员,有共享目录和消息记录 | | 成员 | `human` | 工作组里的人类使用者,每个工作组**有且只有一个** | | 任务 | `job` | 成员交给组长的一项复杂工作;同一工作组内的任务**依次执行,不并行** | | 子任务 | `task` | 组长分派给某个组员的单项工作 | | 单项工作 | `direct` | 成员直接 @ 组员发起的工作,不经过组长 | | Bot Key | `bk_…` | 调用 Bot API 的密钥,与 Agent API 的 `ask_…` 互不通用 | ## 3. 工作组 ### 3.1 组成规则 | 角色 | 数量 | |---|---| | 成员(人) | 恰好 1 个 | | 组长 | 0 或 1 个 | | 组员 | 0 到多个 | | 组长 + 组员 | **至少 1 个** | - 创建时和之后任何一次修改,都必须满足上面的规则:不能移除最后一个机器人;已有组长时不能再加第二个组长。 - 成员只是调用方传来的显示身份 `{id, name}`,AgentServer 不做认证(认证由外部平台负责,见 §10)。 - 所有对话都必须在工作组内进行,没有脱离工作组的"直接和机器人对话"。只想让一个机器人干活时,建一个只有这个组员的工作组即可,不需要组长。 ### 3.2 生命周期 | 状态 | 说明 | |---|---| | 进行中(`active`) | 正常收发消息 | | 已暂停(`paused`) | 成员或系统(超出限额时)暂停:正在执行的 Run 被中断,新消息只记录不唤醒机器人;恢复后继续 | | 已关闭(`closed`) | 不再接收消息,进行中的任务被取消;历史、文件只读保留;可以**重新打开**(各机器人的引擎会话可以续接) | | 删除 | 永久清除消息、文件和底层会话;用量统计保留 | 已关闭的工作组以后纳入 M3 的工作区自动清理(关闭超过 N 天后删除)。 ### 3.3 机器人与引擎会话 - 每个"机器人 × 工作组"对应一个独立的引擎 Session(`team_bot_sessions`),机器人的记忆就是这个 Session。离开工作组后不带走,也不跨工作组。 - Session 的来源(`sessions.source`)记为 `team:`,不会出现在 Agent API 的会话列表中。 - 组内所有机器人会话的**工作目录就是工作组的共享目录**(实施时的调整,见 §15)。共享目录与会话工作区同级,沿用现有的访问策略、systemd 可写目录和删除保护: ``` // ├─ uploads/ 成员上传的文件 ├─ outputs/ 各机器人交付的文件(消息里以文件卡片显示) └─ .work/<机器人>/ 各机器人的中间文件 ``` - 同一个机器人在同一个工作组内**按顺序执行**。执行期间到达的新消息会合并起来,在下一轮一起交给它。不同机器人之间并行执行,受 RunManager 的全局和按引擎的并发限制。 ## 4. 消息与唤醒规则 ### 4.1 消息 工作组的消息记录(`team_events` 中的 `message.created` 事件)是成员看到的时间线,和机器人内部的执行过程(引擎事件)分开存放: | 发送者 | 消息类型 | |---|---| | 成员 | 普通消息(文字、@、附件) | | 组长 | 回复、任务开始和结束、子任务分派、申请加入机器人 | | 组员 | 回复(单项工作)、子任务结果 | | 系统 | 成员变化、暂停和恢复、超出限额、审批卡片 | 机器人每一轮的工具调用、思考过程不进消息记录。成员可以从某条消息点开,查看对应 Run 的完整过程(复用现有的事件和回放)。 ### 4.2 谁被唤醒 | 来源 | 规则 | |---|---| | 成员发消息并 @ 了机器人 | 唤醒被 @ 的机器人(可以同时 @ 多个) | | 成员发消息,没有 @ | 有组长就交给组长;没有组长但只有一个组员,就交给这个组员;否则返回 400 `mention_required` | | 组长分派子任务 | 唤醒对应组员 | | 组员完成或失败一个子任务 | 唤醒组长 | | 组员的单项工作完成 | 不唤醒任何机器人,回复直接给成员 | **组员之间不能互相 @,组员也不能主动 @ 组长**,只能通过子任务的结果向组长汇报。机器人发出的消息里如果写了 @,只当作文字,不会唤醒任何人。 ### 4.3 交给机器人的输入 每次唤醒时,服务端把"自上次以来与它有关的新内容"拼成这一轮的输入: ``` 【工作组新消息】 [10:02] 成员 张三:@组长 帮我分析……(附件:shared/uploads/sales.xlsx) 【子任务 #3 已完成 · 数据分析员】 结果:…… 文件:shared/outputs/summary.csv ``` - 组长能看到工作组里的全部新消息。 - 组员只收到:@ 它的消息、分派给它的子任务,以及这些内容引用的上下文。 - 每个机器人 Session 记录"已读到的 seq",以后调试时能看到它在某一轮实际读到了哪些内容(作为事件记下来)。 ## 5. 组长与任务 ### 5.1 能力卡片 组长靠**能力卡片**了解每个组员会什么。卡片由服务端根据机器人的配置自动生成: - 名称、岗位描述(后台填写,写明擅长什么、不负责什么) - 技能:所用 Profile 中每个技能的 `name` 和 `description`(取自 SKILL.md 开头的元数据) - MCP 服务和工具摘要、可访问的资料库名称 - 权限模式(只读 / 自动) 组长的 Session 开始时,把当前所有组员的卡片注入它的提示词。成员变化时补一条系统消息,组长也可以随时调用 `team_members()` 重新获取。 ### 5.2 任务流程 ``` 成员 @组长 "……" └─ 组长判断这是一项任务 → job_start(标题, 计划) 同一时间只能有一个进行中的任务 ├─ task_create(组员, 说明, 依赖的子任务) × N 不同组员的子任务可以并行 │ └─ 组员执行 → task_complete(结果, 文件) / task_fail(原因) │ └─ 唤醒组长:继续分派 / 调整计划 / 重试 └─ job_complete(总结, 文件) 或 job_fail(原因) → 发到工作组,交给成员 ``` - 任务由组长判断后显式开始。闲聊、简单问题由组长直接回答,不开任务。 - 任务进行中,成员发给组长的消息算作对当前任务的补充说明。 - 已有进行中的任务时,`job_start` 会被拒绝;组长应先结束当前任务,或者告诉成员稍后再提。 - 组员在子任务的这一轮结束时如果没有调用 `task_complete` 或 `task_fail`,服务端就把它最后的回复作为结果,按"完成"处理。 - 成员可以随时取消任务:正在执行的子任务被中断,组长收到通知。 - 任务进行中,成员仍然可以直接 @ 组员做单项工作;如果这个组员正在执行子任务,单项工作会排队等它做完。 ### 5.3 组长申请加入机器人 工作组可以只建一个组长,再由组长根据实际需要申请加入机器人: 1. 组长调用 `team_bot_catalog()` 查看可以加入的机器人。范围是这个工作组所用的 Bot Key 允许使用、且不在本组的组员机器人,返回能力卡片。 2. 组长调用 `team_request_bot(botId, 理由)`,工作组里显示一张**审批卡片**,由成员批准或拒绝。 3. 批准后机器人加入,添加系统消息,组长收到新组员的卡片。 成员通过 API 直接增减机器人时不需要审批。移除一个正在执行子任务的组员时,该子任务按失败处理,并通知组长。 ### 5.4 协作工具(MCP Gateway 内置) Gateway 根据 Run 令牌中的 `teamId`、`botId` 和角色,只列出该角色可以用的工具: | 工具 | 组长 | 组员(子任务中) | 说明 | |---|---|---|---| | `team_members()` | ✓ | | 当前组员的能力卡片 | | `team_bot_catalog()` | ✓ | | 可以申请加入的机器人 | | `team_request_bot(botId, reason)` | ✓ | | 申请加入机器人,需要成员审批 | | `team_history(before?, limit?)` | ✓ | ✓ | 读取更早的消息 | | `job_start(title, plan)` / `job_complete(summary, files?)` / `job_fail(reason)` | ✓ | | 开始和结束任务 | | `task_create(botId, title, instructions, dependsOn?)` | ✓ | | 分派子任务 | | `task_cancel(taskId)` / `task_list()` | ✓ | | 管理子任务 | | `task_complete(result, files?)` / `task_fail(reason)` | | ✓ | 汇报子任务结果 | 机器人每轮的最终回复会自动作为一条消息发到工作组,不需要专门的"发消息"工具。 ## 6. 审批 - 设计上,机器人尽量**不触发审批**:Profile 的权限模式用 `auto` 或 `readonly`,第三方 MCP 工具的策略设成"直接调用"或"关闭"。 - 如果仍然触发了审批或提问(包括 `team_request_bot`),就在工作组里显示一张审批卡片,由成员处理。这复用现有的交互服务,引擎侧不改。 - 某个机器人在等审批时,只有它自己被挂起,其他机器人照常工作。 - 审批超时(`interactionTimeoutSec`)按拒绝处理。对于子任务,就是子任务失败,再唤醒组长处理。 ## 7. 防失控与限额 由于组员之间不能互相 @,协作链路只有"组长 ⇄ 组员"这一种往返,主要靠按任务设限额: | 限额 | 默认值(后台可改,工作组创建时可以调低) | |---|---| | 每个任务的子任务数 | 20 | | 每个任务中组长的执行轮数 | 30 | | 每个任务的成本上限 | 以美元计,按 Run 用量累加 | | 每个任务的时长上限 | 2 小时 | | 每个工作组同时执行的 Run 数 | 4 | 超出限额时,任务自动暂停,并发一条系统消息说明原因,由成员选择"继续"(这一项限额放宽一倍)或"取消"。工作组可以随时整体暂停。 ## 8. 数据模型(新增) | 表 | 主要字段 | |---|---| | `bots` | id、name、avatar、kind(lead / worker)、description(岗位描述)、profileId、enabled、时间戳 | | `bot_api_keys` | id、name、keyHash、keyPrefix(`bk_`)、enabled、allowedBotIds、时间戳 | | `teams` | id、source(Bot Key id 或 admin)、title、human(`{id, name}`)、status、workspaceDir(共享目录)、lastSeq、时间戳;二期增加 limits、debug(`{stepMode, stubBotIds}`,仅 admin,见 §12.2) | | `team_bots` | teamId、botId、role(lead / worker)、joinedAt | | `team_bot_sessions` | teamId、botId → sessionId、lastReadSeq | | `team_events` | teamId、seq、type、payload、ts。消息是 `message.created` 事件(sender、text、mentions、files、targets、runId);状态类事件(`bot.status`、`file.changed`、`team.updated`)只在内存缓冲中 | | `jobs` | id、teamId、title、plan、status、summary、用量合计、限额计数、时间戳 | | `tasks` | id、jobId、teamId、botId、title、instructions、dependsOn、status、result、fileIds、runId、时间戳 | 现有表的改动: - `sessions.source` 增加 `team:` - `interactions` 不改:通过机器人会话关联到工作组 - `usage_daily` 的 source 记为工作组的来源(Bot Key id 或 admin);按 botId 汇总留到二期 - `files` 不改:工作组共享目录的文件以 teamId 登记(`files.session_id = teamId`),只能经 `/bot/v1/files/:id` 下载 ## 9. Bot API 前缀 `/bot/v1`,只接受 Bot Key(`bk_`)、Bot 浏览器令牌或后台管理员的 Cookie(Bot Playground 使用,来源记为 `admin`),详见 ADR-0024。仅 admin 可用的调试接口(替身结果、逐步执行的"继续")也放在这个前缀下,其他调用方访问时返回 403。 | 方法与路径 | 说明 | |---|---| | `GET /bots` | 这个 Key 可以使用的机器人及其能力卡片 | | `POST /teams` | `{title, human:{id,name}, leadBotId?, workerBotIds[], limits?}` | | `GET /teams`、`GET /teams/:id` | 这个 Key 创建的工作组 | | `PATCH /teams/:id` | 修改标题、限额 | | `POST /teams/:id/bots`、`DELETE /teams/:id/bots/:botId` | 增减机器人(需满足 §3.1 的组成规则) | | `POST /teams/:id/messages` | `{text, mentions?, fileIds?}`,发送者固定是这个工作组的成员 | | `GET /teams/:id/messages?after=&limit=` | 消息历史 | | `GET /teams/:id/stream` | SSE,支持断线续传(`Last-Event-ID`) | | `POST /teams/:id/files`、`GET /teams/:id/files/:fileId` | 上传到 `shared/uploads/`,下载共享目录中的文件 | | `POST /teams/:id/interactions/:iid` | 处理审批或回答提问 | | `GET /teams/:id/jobs`、`POST /teams/:id/jobs/:jobId/cancel` | 任务和子任务的进度;取消任务 | | `POST /teams/:id/jobs/:jobId/continue` | 超出限额后继续 | | `POST /teams/:id/pause`、`/resume`、`/close`、`/reopen`、`DELETE /teams/:id` | 生命周期 | | `GET /teams/:id/runs/:runId/events` | 某一轮的完整执行过程(复用现有事件格式) | | `POST /browser-tokens` | 签发 Bot 浏览器令牌 | SSE 事件: - `message.created` - `bot.status`(空闲 / 排队 / 工作中 / 等待审批) - `job.updated`、`task.updated` - `interaction.required`、`interaction.resolved` - `team.updated`(成员、状态变化) ## 10. AgentServer 与外部的分工 | AgentServer 负责 | 外部负责 | |---|---| | 机器人定义、Bot Key(后台) | 使用者的身份和登录;把自己的用户映射为成员 `{id, name}` | | 工作组、成员、消息、共享文件的存储 | 工作组的聊天界面(可以用 SDK 提供的组件,见 §12) | | 唤醒规则、调度、组长和组员的协作工具 | 谁可以建工作组、可以拉哪些机器人等业务规则(Bot Key 的范围之内) | | 审批卡片、限额、暂停、关闭 | 通知和推送 | | 事件流、用量、审计 | 外部 IM(飞书等)接入:桥接服务调用 Bot API,把外部的群映射为工作组 | | 后台观察和调试页面 | | ## 11. 对现有模块的影响 | 模块 | 改动 | |---|---| | 引擎适配器、沙箱 | 不改 | | Profile | 不改,机器人引用 Profile | | 文件访问策略 | 不改:共享目录就是机器人会话的工作目录 | | RunManager | 新增 `start()` 入口(附件是共享目录中的文件);`/v1` 不能向工作组的会话发消息;用量来源可由工作组换成 Bot Key | | 会话服务 | `createForTeam()`;工作组的会话不能经 `/v1` 修改或删除,删除工作组时只删记录、不删共享目录以外的东西 | | MCP Gateway | Run 令牌带上 teamId、botId、role、jobId、taskId;新增 §5.4 的协作工具;`team_request_bot` 复用 Gateway 内发起审批的现有流程 | | 交互服务 | 不改:工作组服务订阅机器人会话的事件,转成工作组的 `interaction.*` 事件 | | 鉴权 | 按路径前缀区分 Key 类型(ADR-0024) | | 后台 | 一期新增"机器人"(含 Bot Keys 页签)和"Bot Playground"两个页面(§12) | 新增模块:`apps/server/src/teams/`,包括工作组服务、消息服务、调度器、协作工具、能力卡片和 Bot API 路由。 ## 12. 后台、Bot Playground 与 SDK ### 12.1 后台页面 - **机器人**:列表、新建和编辑(名称、头像、类型、岗位描述、Profile);预览能力卡片;"在 Playground 中测试" - **Bot Keys**:签发、停用、允许使用的机器人 - **Bot Playground**:管理员建工作组、以成员身份下命令,用于验证机器人(§12.2) - **工作组**:所有来源的工作组,视图与 Playground 相同但只读,只能暂停、关闭;可以"复制到 Playground"(按相同成员组成新建一个工作组),用来复现问题 ### 12.2 Bot Playground 做法与现有 Playground 相同:**没有单独的后端**,后台用同源 Cookie 调用 `/bot/v1`,工作组的来源记为 `admin`;界面用 `BotClient` 和 ``,再加后台专用的调试面板。 要验证的东西分三层: | 验证对象 | 关心什么 | 支持方式 | |---|---|---| | 单个组员 | 技能、权限、产出质量 | 只有这个组员的工作组,@ 它干活,查看每一轮的执行过程 | | 组长 | 是否读懂能力卡片,拆分、分派、收尾是否合理 | 能力卡片预览、每轮输入;替身组员 | | 整个工作组 | 端到端能否完成、成本、是否失控 | 任务看板、分项用量、逐步执行、调低限额 | 布局沿用现有 Playground 的工作台结构: ``` ┌ 侧栏 ────────────┬ 工作组时间线 ─────────────────────────────┬ 调试面板 ───────────────┐ │ BOT PLAYGROUND + │ [工作组标题] ● 进行中 [⏸暂停][⏭逐步][⋯] │ 成员│任务│过程│输入│文件│用量│ │ │ │ │ │ ▸ 周报测试 │ ┌ 我:@组长 分析 Q3 数据并出报告 📎 ┐ │ 👑 组长 ● 工作中 │ │ ▸ 组长拆分测试 │ └────────────────────────────────┘ │ 📊 数据员 ○ 空闲 [替身] │ │ ▸ 数据员单测 │ 👑 组长:开始任务「Q3 分析」,计划:… │ 📝 文档员 ◐ 排队 │ │ │ ├ ▸ #1 → 📊数据员:清洗并汇总 ✓ │ [+ 添加机器人] │ │ │ ├ ▸ #2 → 📝文档员:生成 Word 报告 … │ │ │ │ ⚙ 系统:组长申请加入「图表员」 [批准][拒绝] │ │ │ │ 📊 数据员 · #1 结果:… 📄summary.csv │ │ │ ├──────────────────────────────────────────┤ │ │ │ [@ 选择机器人] 输入命令… [➤] │ │ └──────────────────┴──────────────────────────────────────────┴─────────────────────────┘ ``` 调试面板的页签: | 页签 | 内容 | |---|---| | 成员 | 各机器人的状态(空闲 / 排队 / 工作中 / 等审批)、能力卡片预览、增减机器人、替身开关 | | 任务 | 任务和子任务的树形视图:状态、依赖、耗时、成本,点击跳到时间线中的位置 | | 过程 | 选择机器人和轮次,查看完整执行过程。每个机器人底层是普通 Session,复用 `Timeline` 和现有的 `DebugPanel` | | 输入 | 每一轮服务端实际交给该机器人的内容(拼好的新消息、读到的 seq)。排查组长问题时先看这里 | | 文件 | 共享目录,预览和下载 | | 用量 | 按机器人、任务、子任务分别统计 | 点击时间线中任意一条机器人消息,都会跳到"过程"页签的对应轮次。 **调试功能**(保存在工作组的 `debug` 字段中,只有来源为 `admin` 的工作组可以设置,Bot Key 设置会被拒绝): - **替身组员**(`stubBotIds`):分派给替身的子任务不执行,时间线中显示一张卡片,由管理员手动填写结果(可以附文件)或标记失败。用来在不花组员成本的情况下反复测试组长的拆分、重试和汇总,也可以故意返回失败,看组长怎么处理。 - **逐步执行**(`stepMode`):调度器每次唤醒机器人之前先停下,在时间线中显示"下一步:唤醒 📊数据员(子任务 #1)",管理员点"继续"才执行。用来观察协作链路,防止配置有问题时一下子跑出一大串 Run。 入口与联动: | 入口 | 行为 | |---|---| | 机器人编辑页"在 Playground 中测试" | 组员:新建只有它的工作组。组长:新建"组长 + 本后台可用组员的替身" | | 工作组页面"复制到 Playground" | 按相同成员组成新建工作组 | | 修改机器人配置 | 下一轮生效;组员的岗位描述变化后,组长收到一条更新能力卡片的系统消息 | | 回归用例(09 §3.6,M3) | 把"成员组成 + 第一条命令"保存为用例,以后批量重放并比对结果 | ### 12.3 SDK - `@agent-server/client` 增加 `BotClient`(Bot API、工作组 SSE 订阅、断线续传) - `@agent-server/chat-ui` 增加 `` 组件:消息时间线、@ 选择、任务进度、审批卡片、替身卡片(仅在 `debug` 生效时显示)、文件卡片 - 两者在一期实现,先由后台使用;三期随 SDK 发布 ## 13. 分期 | 期次 | 内容 | 验收 | |---|---|---| | 一期 | 机器人定义、Bot Key 和 `/bot/v1` 鉴权(含后台 Cookie)、工作组的组成规则与生命周期、消息、唤醒规则(只有 @ 组员的单项工作)、共享目录、工作组 SSE、审批卡片;`BotClient`、``;Bot Playground 基本形态(时间线、成员、过程、文件页签) | 在 Bot Playground 中建一个有两个组员的工作组,分别 @ 它们完成单项工作,过程中处理一次审批;用 Agent API 的 Key 访问 Bot API 被拒绝 | | 二期 | 组长、能力卡片、任务与子任务、协作工具、限额与暂停、组长申请加入机器人;Playground 的任务、输入、用量页签,替身组员,逐步执行,"在 Playground 中测试"入口 | 只建一个组长的工作组,组长申请加入两个机器人(经审批),把一项复杂任务拆成多个子任务完成并交付文件;人为触发子任务数上限后能继续或取消;全部组员设为替身时,能手动填写结果完成一项任务 | | 三期 | `BotClient`、`` 随 SDK 发布,接入示例,子任务依赖和并行的完善;后台工作组页面"复制到 Playground";回归用例 | 在 platform-demo 中完成一期、二期的验收流程 | ## 14. 待决事项 | # | 问题 | 倾向 | |---|---|---| | T1 | 组长的提示词和协作规范放在哪里 | ✅ 内置"组长"Profile(每个引擎一个)和 team-lead 技能,后台可以修改或复制(§18) | | T2 | 组长能否申请移除组员 | 一期、二期不支持,由成员操作 | | T3 | 工作组长期运行后,组长的 Session 越来越长 | ✅ 任务纪要(§18)+ 按上下文大小和闲置时间开始新会话(§19) | | T4 | 能力卡片里是否列出全部 MCP 工具 | 只列服务名和说明,工具较多时不展开 | ## 15. 一期实施记录(2026-10-26) 已实现:机器人与 Bot Key、`/bot/v1` 全部一期接口与鉴权隔离、工作组组成规则与生命周期、唤醒规则、同一机器人顺序执行与消息合并、审批卡片、共享目录与文件、工作组 SSE;`BotClient`、``;后台"机器人"页面与 Bot Playground(时间线、成员、过程、文件页签)。组长在一期只按普通机器人回复(任务与协作工具在二期)。 与前文设计相比的调整: | 设计 | 实施 | 原因 | |---|---|---| | 每个会话有自己的工作区,共享目录作为额外可写路径 | 会话的工作目录就是共享目录 `/` | 系统提示词中"工作目录 / uploads / outputs"的约定保持一致;沿用现有访问策略、systemd 可写目录与删除保护,不需要改访问策略 | | `team_messages` 表 | `team_events` 表,消息是其中的 `message.created` 事件 | 消息、交互卡片与状态共用一个 seq,SSE 断线续传与会话事件流一致 | | `interactions` 增加 teamId | 不改表 | 由机器人会话关联到工作组即可 | 实现要点: - 每轮开始前先把共享目录同步到该机器人会话的文件索引,本轮结束时只有本轮改动的 `outputs/` 文件挂到它的回复上。两个机器人同时改动文件时仍可能互相计入,二期再细化。 - 服务重启时未完成的工作被标记为中断;没有交给机器人的消息不会自动重新处理,成员再次发消息时一并交给它。 - 已用真实 Claude(haiku)验收:两个组员并行处理、其中一个触发审批并在工作组中批准、交付文件以卡片显示、续接会话保留记忆;Bot Key 访问 `/v1` 返回 401。 ## 16. 二期实施记录(2026-10-27) 已实现:能力卡片(`GET /bots` 返回,组长的提示词中注入组员的卡片)、任务与子任务(§5.2,含依赖与并行)、§5.4 的全部协作工具、组长申请加入机器人(审批卡片)、限额与"继续 / 取消"、工作组同时执行的 Run 数;Bot Playground 的任务、输入、用量页签,替身组员,逐步执行,能力卡片预览;机器人页面"测试"组长时新建"组长 + 全部可用组员(替身)"的工作组。系统设置增加"工作组限额"。 与前文设计相比的调整: | 设计 | 实施 | 原因 | |---|---|---| | Run 令牌带上 teamId、botId、role、jobId、taskId | 令牌只带会话;协作工具按会话所属机器人的角色固定提供(组长一套、组员一套),调用时再按当前状态检查 | Codex 会话沿用第一次连接时的工具列表,按 Run 变化的工具会失效;按会话查状态也不需要改令牌 | | `task_cancel(taskId)` | `task_cancel(task_no)`,子任务在消息中以 `#编号` 出现 | 模型更容易引用编号 | | `job_complete` 之后组长的最终回复照常回帖 | 同左;提示词要求之后的回复简短,组长本轮发过任务卡片时不再提示"本轮没有回复" | 避免与任务总结重复 | | 子任务的分派消息唤醒组员 | 分派消息不唤醒任何人(targets 为空),组员由子任务队列唤醒;结果消息的 targets 是组长 | 组员的单项工作(@ 它的消息)与子任务分开:先做排队的子任务,单项工作在之后处理 | | 后台可改的限额 | `settings.teamLimits`;工作组创建或修改时只能调低 | 与设计一致,写明存放位置 | 实现要点: - 子任务的状态:waiting(等依赖)→ queued → running → completed / failed / cancelled;替身的子任务为 stub,由管理员填写结果。依赖的子任务失败时,后续子任务保持 waiting,由组长决定取消或重新分派。 - 组员在子任务这一轮没有调用 `task_complete` / `task_fail` 时,以最后的回复作为结果按完成处理;Run 失败或被中断按失败处理;工作组暂停导致的中断会在恢复后重新执行。 - 交给组长的输入中不标注替身(替身用来测试组长,标注后组长会区别对待);逐步执行与替身的提示卡片不交给机器人。 - 任务暂停(超出限额)期间不唤醒组长,组员的结果先记录,成员选择继续后组长一并看到。 - 同一工作组的文件扫描依次进行(并发扫描会把同一个新文件登记两次)。 - 已用真实 Claude(haiku)验收:组长调用 `job_start`、`task_create`(含依赖)分派给两个替身组员,管理员填写结果后组长被依次唤醒,核对文件时触发的审批在工作组中处理,最后结束任务。 ## 17. 三期实施记录(2026-10-27) 已实现: - **SDK 0.3.0**:`BotClient`、``、`useTeamSession` 随 SDK 发布,接入方式写入 `docs/integration.md` §8。 - **platform-demo**:增加"工作组"页面,平台后端用 Bot Key 换令牌(`/api/bot-token`)。 - **子任务依赖与并行的完善**: - 新增协作工具 `task_retry(task_no, instructions)`:重新执行失败或已取消的子任务,编号不变,依赖它的子任务在它完成后继续。 - `task_create` 不能依赖已失败或已取消的子任务。 - 还有未结束的子任务时 `job_complete` 被拒绝。 - 组长每次被唤醒时,输入末尾附有当前任务的进度,并标出因依赖失败而无法开始的子任务。 - **后台**: - Bot Playground 的侧栏可以切到"全部来源",只读查看业务平台的工作组(只能暂停、关闭)并"复制到 Playground"。 - 任意工作组可以"保存为回归用例"。 - 新增"回归用例"页面:批量重放,每次的结果(任务总结或回复、轮数、子任务数、成本)列在一起。 与前文设计相比的调整: | 设计 | 实施 | 原因 | |---|---|---| | 单独的"工作组"页面 | 合并到 Bot Playground 的侧栏(Playground / 全部来源),平台的工作组以只读方式打开 | 视图本来就与 Playground 相同,不需要两套页面 | | 回归用例覆盖会话(09 §3.6)和工作组 | 先做工作组的用例:成员组成、替身、限额、第一条消息和附件(附件复制到 `data/team-cases/`);重放时新建来源为 `admin` 的工作组,记录 `teams.caseId` | 工作组的行为受提示词和组长的拆分影响最大,最需要回归;会话的用例以后按需再做 | | 结果比对 | 人工比对,不做自动判定 | 与 09 §3.6 一致 | 实现要点: - 组长没有组员时,提示词要求先申请而不是自己动手(验收时组长曾直接用 Bash 完成了本应分派的工作)。 - 重放会真实执行(替身组员除外),后台在重放前提示会产生成本。 - 已在 platform-demo 中用真实 Claude(haiku)验收: - 只有组长的工作组:组长申请加入数据员和文档员(在 `` 的审批卡片中批准),拆成有依赖的两个子任务,交付两个文件。 - 子任务数上限为 1 时任务暂停,选择继续后完成。 - 把这个工作组保存为回归用例并重放,结果一致。 ## 18. 组长 Profile 与任务纪要(2026-10-27) **组长 Profile**(T1): - 每个引擎一个内置的"组长"Profile(`profiles.builtinKey = 'lead'`),不能删除,可以修改,也可以复制后修改。 - 权限为只读:组长可以查看共享目录中的文件,但不能写文件、不能执行命令,从机制上防止它自己动手。协作工具不受只读限制。 - 系统提示词是"协作规范":什么时候开任务、怎么选组员、怎么写子任务说明、失败时怎么处理、怎么交付和写纪要。 - 技能只启用自定义技能 `team-lead`:子任务说明模板、拆分原则、验收清单、纪要格式。它不包含在"全部技能"中,组员不会加载。 - 服务端的提示词只保留与协作工具和调度方式绑定的"协作机制",加上组员的能力卡片和最近的任务纪要。 - 第一次启动时创建这两个 Profile 和技能,并把使用"通用助手"的组长机器人改为同一引擎的组长 Profile。 - 后台新建机器人时,类型选"组长"会自动选上组长 Profile;组长使用其他 Profile 时给出提示。 **任务纪要**(T3 的第一步): - 任务结束(完成、失败、取消)时,系统把任务的计划、每个子任务的说明、结果和文件、交付内容,以及组长在 `job_complete` / `job_fail` 的 `notes` 中写的要点,保存到共享目录的 `.team/jobs/job-<编号>.md`。 - 纪要由系统根据任务记录生成,不需要组长额外执行一轮;组长只需写几行"给以后的自己"的要点。 - 组长的提示词中列出最近 5 份纪要的路径和标题,遇到相关工作时可以读取。 ## 19. 开始新会话与文件归属(2026-10-27) **开始新会话**(T3 的第二步): - 机器人每一轮开始前判断是否换新会话,满足任一条件就换: - 上一轮最后一次模型调用的上下文超过 `limits.sessionMaxContextTokens`(默认 100k token) - 闲置超过 `limits.sessionIdleHours`(默认 24 小时;提示词缓存早已过期,续接长会话每轮都要按全价重读历史) - 成员要求(`POST /teams/:id/bots/:botId/reset-session`,Playground 成员页签的"新会话") - 组长只在两项任务之间换:任务进行中的来龙去脉在会话里,换了反而丢失。组员每一轮都可以换,因为子任务的输入是完整的。 - 换会话时工作组中出现一条系统消息。新会话靠两样东西接上之前的工作:组长提示词中的最近任务纪要,以及 `team_history`。 - 不按"每 N 个任务"换:这和任务长短无关,会让短任务也频繁丢掉上下文。 - 上下文大小:Claude 取主线程最后一条 assistant 消息的 usage,Codex 取 `tokenUsage.last`,都是输入 + 输出;记录在 `runs.context_tokens`。 - 两个阈值在"设置 › 工作组限额"中,工作组可以调低。 **文件归属**(一期记录中的遗留问题):一轮结束时,共享目录中改动的文件按以下规则算作这个机器人的产出: 1. 声明的文件(`task_complete` / `job_complete` 的 files)为准。 2. 写文件工具的记录:Claude 的 Write / Edit / MultiEdit / NotebookEdit、Codex 的文件修改、`recalc_workbook`(写回输入文件)、`convert_document`(结果中的"已生成"路径)。 3. 只能靠扫描发现的文件(命令、脚本生成的):只有这一轮没有其他机器人同时工作时才算给它;有并发时不挂到回复上,仍然可以在"文件"页签中看到。 - 组员的提示词要求把命令或脚本生成的交付文件列在 files 中。 - 没有采用"每个机器人写到自己的 outputs/<名字>/":交付路径变复杂,组长写说明时也要遵守这条规则。