# ADR-0024: Team flows (parallel sub agents) + named-tool registry - Status: Accepted - Date: 2026-07-16 - Related: ADR-0008 (quote-probe main-sub protocol), ADR-0020 v4 (self-service flows), ADR-0023 (flow execution audit) ## 1. 背景 / Context 引擎的主子 agent 协作能力只有一个手写消费形态 (quotedispatch, 3000 行 verdict loop, billcost 生产在跑). PM 拍死方向: 把这能力开放成自助 flow 的 team 编排, 消费者 (flytoCall 质检 = 评分/违规/摘要并行再汇总) 在 GUI 里定义. 同时 flow.Definition.Tools 一直返 501 (INF-3): 缺平台侧 "名字 -> tools.Tool" 运行时注册表. ## 2. 决策 / Decision 1. **Definition 直接扩 `sub_agents`** (不另立 TeamDefinition kind): `[]SubAgentDef{name, system_prompt_template, output_schema, tools, model, max_turns}`. 空数组 = 单 agent flow, flowstore / CRUD / 发布闸 / 审计零改动. 2. **编排形态 = 先并行扇出后汇总** (fan-out-then-aggregate): 每个 sub 一个 enginefactory.BuildStructuredEngine 隔离引擎, 带自己的 output_schema 反射器 (sub 的 output_schema 就是它的反射器, 与主 flow 同一结构性回答) 与收窄工具 集; 全部完成后主 agent 跑一次, user prompt 模板可引用 `{{.sub_outputs.}}`, 主 OutputSchema 闸最终答案. verdict 驱动的多轮 交互形态留在 quotedispatch, 等有消费者需要再数据化. 3. **工具注册表复用 core `tools.Registry`** (不新造 map): FlowConfig.Tools 字段, cmd/common 只登记只读安全 builtin (Read / Grep / Glob); 有副作用业务工具按 消费者需要逐个过审登记. 未知工具名 400; 发布闸同规则 (published = runnable). `GET /api/v1/flows/tools` 暴露目录给 GUI 选择器; "tools" 成保留 flow 名. 4. **failover 语义扩展** (ADR-0020 v3): 消费者点名的备选按引擎运行套用 -- 主与每个 sub 各自主选失败切换一次, 切换记录进审计行. 5. **扇出上限 8** (maxSubAgents): 定义是租户数据, 不设限一个手滑 = 每请求 几百个引擎. ## 3. 替代方案 / Alternatives - **独立 TeamDefinition kind**: 存储/CRUD/GUI 全要加 kind 判别面; sub_agents 空数组自然退化, 判别面不值. 否决. - **复制 quotedispatch loop 泛化**: 引入第三份 model-B flag 集 (TODO L1189 债 是两份就该收敛); team flow 建在 enginefactory 上, 不再新增. 否决. - **core builtin.TeamTool (模型自主扇出)**: 编排权在模型不在定义, 消费者无法 在 GUI 里审计每个 sub 的 prompt/schema; 与自助 flow 的 "定义即数据" 哲学冲突. 保留为未来自主协作路线, 本轮否决. - **画布编辑器**: 表单友好 (定义驱动) 先行, 画布是 P2 UI 阶段的视觉层, 后置. ## 4. 影响 / Consequences - flytoCall 质检场景可全 GUI 自助: 三 sub 并行 + 主汇总, 发布即调. - flow 声明工具后引擎可读文件/grep (EngineCwd 下), 为 RAG 型 flow 铺路. - sub 失败语义: 任一 sub provider 级失败 = 502 (点名哪个 sub); 任一 sub schema 不合规 = 200 + verdict=invalid_output (业务结局, 消费者降级). - 审计行仍一次执行一行 (主 flow 粒度), sub failover 记录并入 Error 备注. ## 5. 验证 / Validation - flow 包: Prepare 闸拒 重名/非法名/缺 schema/超 8 个 sub; 渲染/校验单测. - server: team 扇出聚合 (主 prompt 必须看到全部 sub 输出) / sub invalid_output / sub 引擎错 502 / 工具收窄进 spec.ToolPolicy / 未知工具 400 / 目录端点. - 两 module `go test -race` 全绿; 前端 tsc + vite build 过. ## 6. 触发重新评估的条件 / Trigger conditions - 有消费者需要 sub 间通信或多轮 verdict 交互 -> 数据化 quotedispatch 协议 (agentprompt 包已就绪). - 扇出宽度 8 不够或需要 sub 嵌套 -> 重新评估编排引擎 (可能引入 DAG). - 注册表需要租户级工具 (每租户自带工具) -> 目录从部署级变租户级. ## 7. 工程量 / Engineering footprint platform/common: flow/team.go (新) + definition.go 扩展 + flows_run.go 编排 + flows_tools.go (新) + flows_crud.go 发布闸 + cmd/common 接线; frontend: flow.tsx 编辑器 team 区 + ToolPicker + api/types. 核心引擎零改动. ## 8. 修订记录 / Revision history - 2026-07-16 v1: 初版, R4 工具注册 + team flow 一包交付. - 2026-07-17 v2: 消费实战 (flytocall.lead_tag) 驱动的三项扩展: (1) httptool 包 -- 消费者 HTTP 接口按 spec 包成只读平台工具 (首个: lead.context.get); (2) SubAgentDef.ProviderInstance -- sub 钉自己的命名实例, 跨 provider team (worker 本地 gemma + judge MiniMax 云端一次请求), 钉实例的 sub 退出 请求级 failover 对; (3) B/C 作用域记忆 -- flow_memories 表 + REST 管理面 + 保留模板键 {{.memory}} 运行时注入 + 三层确定性压缩 (掉例 -> 折叠合并 注记), 纠正下一跑生效不用重发布. 另修执行器多轮文本折叠 bug (只取最后一 轮, 工具过场白不再污染最终 JSON).