Files
beaver_project/app-instance/backend/flow.md
steven_li 8a12c30141 feat(beaver): 完成Task Team功能v1实现,重构后端架构支持统一内核
新增内部Task系统,包括验证、反馈门控机制,实现自动质量验证
(通过率>=0.75)和用户反馈闭环(satisfied/revise/abandon)。

实现Agent Team v1协调器,支持sequence/parallel/dag执行策略,
sub-agent复用主AgentLoop,每个run使用独立memory snapshot。

建立Skill学习pipeline,包含draft/审核/发布/回滚完整生命周期,
通过Task验证通过且用户满意才生成学习候选。

重构目录结构,移除third_party依赖,建立统一engine内核,
所有agent共享运行时基础组件。

更新ContextBuilder清理provider消息字段,增强SkillContext版本管理,
集成TaskExecutionPlanner和TaskSkillResolver实现技能解析机制。
2026-05-08 17:14:14 +08:00

842 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Beaver Backend Flow
这份文档只保留**树形运行结构**。
- 原理、参考项目边界、长期蓝图:看 `change.md`
- 施工顺序、阶段目标、完成标准:看 `施工指南.md`
---
## 1. 总入口
```text
用户输入(用户在不同入口发来一句话或一个任务)
├─ CLI命令行入口
├─ Web网页前端入口
├─ Gateway消息通道入口比如以后接 Slack / Telegram
└─ future channels未来扩展入口
└─ AgentService统一服务层所有入口都先汇总到这里
├─ MainAgentRouter自动判断 simple / task
├─ create_loop()(创建 AgentLoop 运行核心)
├─ start()(启动后台运行模式)
├─ submit_direct()(把任务提交到运行队列)
├─ process_direct()(直接处理一次任务,不走队列)
├─ submit_feedback()(记录聊天反馈并驱动内部 Task 状态)
├─ stop()(停止接收新任务,并等待队列收尾)
├─ shutdown()(停止运行并释放资源)
└─ close()(关闭已经创建的 runtime
```
---
## 2. Boot / Loader
```text
AgentService.create_loop()(服务层创建运行核心)
└─ AgentLoop(profile, loader)Agent 主循环:真正跑任务的核心对象)
└─ AgentLoop.boot()(启动前装配依赖)
└─ EngineLoader.load()(加载所有运行时模块)
├─ SessionManager会话管理保存聊天记录和隐藏事件
├─ MemoryStore长期记忆存储真正落盘的 curated memory
├─ MemoryService记忆服务运行时访问 memory 的唯一入口)
├─ RunMemoryStore运行记录存储保存每次 run 的结果)
├─ SkillLearningStore技能学习存储保存表现统计和学习候选
├─ ToolRegistry工具注册表登记系统有哪些工具
├─ ToolAssembler工具选择器决定本轮暴露哪些工具
├─ ToolExecutor工具执行器真正调用工具
├─ SkillsLoader技能目录加载器只加载可用技能
├─ SkillAssembler技能选择器决定本轮激活哪些 skill
├─ SkillSpecStore技能生命周期存储保存版本、草稿、审核
├─ DraftService草稿服务创建 skill draft
├─ ReviewService审核服务approve / reject draft
├─ SkillPublisher发布服务publish / disable / rollback
├─ EvidenceSelector证据选择器为学习闭环挑选历史证据
├─ SkillDraftSynthesizer草稿合成器让 LLM 生成 skill draft
├─ SkillLearningService技能学习服务生成学习候选和草稿
├─ TaskService内部 Task 服务:自动 Task 化、状态、事件、反馈)
├─ TaskExecutionPlannerTask 执行规划器:决定 single / team
├─ ValidationService结果验证服务Task run 完成后的自动验证)
└─ ContextBuilder上下文构建器拼 system prompt 和 messages
```
---
## 3. Main Agent Routing / Internal Task
```text
AgentService.process_direct / submit_direct聊天入口统一进入服务层
├─ resolve session_id复用请求 session或生成新 session
├─ task_service.get_latest_open_task(session_id)(查找同会话未关闭 Task
├─ MainAgentRouter.classify(message, active_task)(自动分类)
│ ├─ simple简单问题
│ │ └─ runner(message)(直接走原有 AgentLoop不创建 Task
│ │
│ └─ task复杂任务
│ ├─ if no active task or user starts new task
│ │ └─ TaskService.create_task(...)(内部创建 Task
│ ├─ else
│ │ └─ reuse active Task复用 awaiting_feedback / needs_revision Task
│ └─ AgentService._run_task_mode(...)(进入 Task 模式执行)
```
```text
TaskService内部 Task 状态机)
├─ TaskRecord
│ ├─ task_id
│ ├─ session_id
│ ├─ goal / description / constraints
│ ├─ status
│ │ ├─ open
│ │ ├─ running
│ │ ├─ validating
│ │ ├─ awaiting_feedback
│ │ ├─ needs_revision
│ │ ├─ closed
│ │ └─ abandoned
│ ├─ run_ids
│ ├─ skill_names
│ ├─ validation_result
│ └─ feedback
└─ TaskEvent
├─ created
├─ run_started
├─ run_completed
├─ validated
├─ feedback_satisfied
├─ feedback_revise
└─ feedback_abandon
```
```text
Task Mode Execution复杂任务执行
├─ attempt 1
│ ├─ task_service.start_run(...)
│ ├─ TaskExecutionPlanner.plan(...)LLM 规划 single / team
│ ├─ session hidden event: task_execution_planned
│ ├─ if plan.mode == team
│ │ ├─ TeamService.run_team(parent_task_id=task_id, parent_session_id=session_id)
│ │ ├─ sub-agent runs -> parent Task run_ids
│ │ ├─ session hidden event: task_team_run_completed / task_team_run_failed
│ │ └─ team summary + node results -> 主 Agent synthesis execution_context
│ ├─ AgentLoop.process_direct / submit_direct(..., task_id, task_mode=True, attempt_index=1)
│ ├─ ValidationService.validate_task_result(..., team_summaries=...)
│ ├─ TaskService.record_validation(...)
│ ├─ RunMemoryStore.update_run_record(validation_result=...)
│ └─ session hidden event: task_validation_snapshotted
├─ if validation accepted
│ └─ return result with task_id / task_status / validation_result
└─ if validation failed
├─ session_manager.set_run_context_visible(run_id, false)(隐藏失败草稿尝试)
├─ attempt 2 重新规划 single / team
├─ revision request + team result -> 主 Agent synthesis execution_context
└─ 第二次结果无论验证是否通过,都返回并等待用户反馈
```
---
## 4. Direct Run
```text
AgentLoop.process_direct(task)(直接执行一轮用户任务)
├─ 生成 session_id确定这句话属于哪个会话
├─ 生成 run_id给本次运行生成唯一编号
├─ memory_service.capture_snapshot_for_run()(每个 run 捕获独立记忆快照)
│ └─ fresh MemoryStore(root).load_from_disk()(不写共享 `_snapshot`,避免并发串记忆)
├─ session_manager.ensure_session(session_id)(确保会话存在)
├─ session_manager.append_message(event_type="run_started", hidden)(记录隐藏事件:本轮开始)
├─ make_provider_bundle()(装配模型 provider 组合)
│ ├─ main_runtime主模型配置
│ ├─ main_provider主模型调用器
│ ├─ fallback_runtime备用模型配置
│ ├─ fallback_provider备用模型调用器
│ ├─ auxiliary_runtime辅助模型配置
│ ├─ auxiliary_provider辅助模型调用器用于选 skill 等)
│ └─ embedding_runtime向量模型配置用于语义召回
├─ skill_assembler.assemble(...)(选择本轮应该激活哪些 skill
│ ├─ SkillsLoader.build_selection_candidates()(列出候选技能摘要)
│ ├─ embedding retrieve skill candidates用向量召回相关技能
│ ├─ LLM select activated skills让模型从候选里选择技能
│ └─ 返回 activated skills返回本轮被激活的技能
│ ├─ name技能名称
│ ├─ content技能正文
│ ├─ version技能版本
│ ├─ content_hash技能内容哈希用于追踪
│ ├─ activation_reason为什么激活
│ └─ tool_hints技能建议使用哪些工具
├─ ContextBuilder.build_skill_activation_messages(...)(把激活技能变成模型可读消息)
├─ 构造 SkillActivationReceipt[](构造技能激活收据)
├─ session_manager.append_message(...)(记录隐藏事件:本轮用了哪些技能)
│ ├─ event_type="skill_activation_snapshotted"(技能激活快照)
│ ├─ hidden不进入普通聊天上下文
│ └─ payload隐藏数据
│ ├─ receipts技能激活收据
│ └─ activation_messages实际注入给模型的技能消息
├─ tool_assembler.assemble(...)(选择本轮应该暴露哪些工具)
│ ├─ always tools默认总是可用的工具
│ ├─ activated skill tool hints被激活技能推荐的工具
│ ├─ embedding retrieve tools用向量召回相关工具
│ └─ 返回 selected ToolSpec[](返回本轮工具列表)
├─ session_manager.append_message(event_type="tool_selection_snapshotted", hidden)(记录隐藏事件:工具选择快照)
├─ ContextBuilder.build_messages(...)(构造发给模型的完整 messages
│ ├─ build_system_prompt()(构造 system prompt
│ │ ├─ base system prompt基础系统提示词
│ │ ├─ session metadata当前会话元信息
│ │ ├─ execution context本轮额外执行上下文
│ │ └─ frozen memory snapshot冻结记忆快照
│ ├─ insert activated skill messages插入已激活技能正文
│ ├─ append visible history追加可见历史聊天
│ └─ append current user input追加当前用户输入
├─ session_manager.update_system_prompt(...)(把本轮 system prompt 快照写回会话)
├─ session_manager.append_message(event_type="system_prompt_snapshotted", hidden)记录隐藏事件system prompt 快照)
├─ session_manager.append_message(event_type="user_message_added")(记录可见事件:用户消息)
├─ 进入 tool loop进入模型回答和工具调用循环
├─ 成功时(模型正常结束)
│ ├─ session_manager.append_message(event_type="run_completed", hidden)(记录隐藏事件:运行完成)
│ └─ _record_skill_learning(...)(记录技能使用效果,进入学习闭环)
├─ 失败时(运行中出现异常)
│ ├─ append assistant error message写入 assistant 错误消息)
│ ├─ session_manager.append_message(event_type="run_failed", hidden)(记录隐藏事件:运行失败)
│ └─ _record_skill_learning(...)(即使失败也记录技能效果)
└─ return AgentRunResult返回本轮结果
├─ session_id会话编号
├─ run_id运行编号
├─ output_text最终回复文本
├─ finish_reason结束原因
├─ tool_iterations工具循环次数
├─ provider_name模型供应商
├─ model模型名称
├─ usagetoken 用量)
├─ task_idTask 模式下返回)
├─ task_statusTask 模式下返回)
└─ validation_resultTask 模式下返回)
```
---
## 5. Tool Loop
```text
tool loop工具调用循环
├─ provider.chat(messages, tools=schemas)(把消息和工具 schema 发给模型)
├─ session_manager.update_usage(...)(累计 token 用量)
├─ session_manager.append_message(event_type="assistant_message_added")(记录 assistant 回复)
├─ ContextBuilder.add_assistant_message(...)(把 assistant 回复追加到本轮 messages
├─ if no tool calls如果模型没有要求调用工具
│ └─ finish结束本轮回答
└─ if tool calls如果模型要求调用工具
├─ ToolExecutor.execute_tool_call(...)(执行一个工具调用)
├─ session_manager.append_message(event_type="tool_result_recorded")(记录工具结果)
├─ ContextBuilder.add_tool_result(...)(把工具结果追加到 messages
└─ 回到 provider.chat(...)(带着工具结果继续问模型)
```
---
## 6. Skills Learning Baseline
```text
AgentLoop._record_skill_learning(...)(记录本轮技能效果)
├─ 构造 RunRecord构造本轮运行记录
│ ├─ run_id运行编号
│ ├─ session_id会话编号
│ ├─ task_text用户原始任务
│ ├─ task_id内部 Task 编号,简单问题可为空)
│ ├─ attempt_indexTask 模式下的尝试序号)
│ ├─ started_at开始时间
│ ├─ ended_at结束时间
│ ├─ success是否成功
│ ├─ finish_reason结束原因
│ ├─ validation_resultTask 模式下的验证结果)
│ ├─ feedback用户反馈
│ └─ activated_skills本轮激活过的技能收据
├─ 构造 SkillEffectRecord[](构造技能效果记录)
│ └─ 每个 activated skill 一条(每个被用到的技能都单独记一条)
├─ skill_learning_service.collect_run_receipts(...)(收集运行收据)
│ ├─ RunMemoryStore.append_run_record(...)(把 RunRecord 写入 memory/runs/runs.jsonl
│ ├─ RunMemoryStore.append_skill_effect(...)(把 SkillEffectRecord 写入 memory/runs/skill-effects.jsonl
│ ├─ SkillLearningService.rescore_skill_versions()(重新统计每个技能版本表现)
│ │ └─ SkillLearningStore.update_performance_snapshot(...)(更新表现快照)
│ └─ optionally build learning candidates默认不生成只由反馈门控显式触发
│ ├─ revise_skill建议修改已有技能
│ ├─ new_skill建议创建新技能
│ ├─ merge_skills建议合并相似技能
│ └─ retire_skill建议退役长期不用的技能
└─ session_manager.append_message(...)(记录隐藏事件:技能效果快照)
├─ event_type="skill_effects_snapshotted"(技能效果已快照)
├─ hidden不进入普通聊天上下文
└─ payload隐藏数据
├─ run_record本轮运行记录
├─ skill_effects技能效果记录
├─ learning_candidate_enabled本轮是否允许生成候选默认 false
└─ learning_candidates学习候选默认空
```
---
## 7. Chat Feedback / Learning Gate
```text
POST /api/chat/feedback聊天反馈接口不是 Task 管理 API
├─ input
│ ├─ session_id
│ ├─ run_id
│ ├─ feedback_type
│ │ ├─ satisfied
│ │ ├─ revise
│ │ └─ abandon
│ └─ comment?
├─ AgentService.submit_feedback(...)
│ ├─ TaskService.get_task_by_run_id(run_id)
│ ├─ reject if task/session mismatch
│ ├─ reject conflicting feedback for same run
│ ├─ same feedback is idempotent
│ └─ TaskService.add_feedback(...)
├─ satisfied
│ ├─ if validation accepted
│ │ ├─ Task status -> closed
│ │ └─ SkillLearningService.build_learning_candidates()
│ └─ if validation not accepted
│ └─ 记录人工接受但保留验证风险
├─ revise
│ ├─ Task status -> needs_revision
│ └─ 下一条用户消息默认复用该 Task
└─ abandon
├─ Task status -> abandoned
└─ write Failure Memory不生成成功 Skill draft
```
---
## 8. Agent Team v1 / Local Coordinator
```text
TeamService.run_team(...)(内部 team 执行入口,不暴露产品级 Task API
├─ validate parent task如果传 parent_task_id先校验 Task 存在且 session 匹配)
├─ TeamGraphScheduler.run(...)
│ ├─ graph.validate()
│ │ ├─ v1 implemented strategies: sequence / parallel / dag
│ │ └─ reserved strategies: moa / hierarchy / heavy / group_chat / forest / maker / router
│ ├─ provider_bundle_factory(node)(推荐:每个节点拿 fresh provider bundle
│ ├─ inherited_pinned_skills主 agent 明确委派给 sub-agent 的 pinned skills
│ ├─ inherited_pinned_skill_contextsmissing skill draft 生成的 ephemeral skill guidance
│ └─ learning_candidate_enabled=False默认只写 receipts不绕过 Task feedback gate
├─ LocalAgentRunner.run(envelope)
│ ├─ 生成 child_session_id
│ ├─ parent_session_id -> 主 session建立 session lineage
│ ├─ AgentLoop.process_direct / submit_direct(...)(复用主 AgentLoop / ContextBuilder / ToolAssembler / SkillAssembler / MemoryService
│ ├─ pinned_skill_names -> AgentLooppublished pinned skill 必须注入)
│ ├─ pinned_skill_contexts -> AgentLoopdraft-only ephemeral skill 必须注入)
│ └─ provider_bundle + node model/provider override 禁止混用
├─ strategy execution
│ ├─ sequence前一节点成功输出进入后一节点 dependency_outputs
│ ├─ parallel同层节点 asyncio.gather 真并发执行
│ └─ dag按依赖拓扑分批并发失败节点会阻断依赖它的后续节点
├─ node-level failure normalization
│ ├─ provider factory / runner 普通异常 -> NodeRunResult(success=False, finish_reason="error")
│ ├─ asyncio.CancelledError 继续抛出
│ └─ blocked dependency -> NodeRunResult(success=False, finish_reason="blocked")
├─ TeamRunResult
│ ├─ success
│ ├─ summary只聚合成功节点输出失败节点列入 Failed nodes
│ ├─ node_results
│ ├─ run_ids
│ ├─ session_ids
│ └─ task_id父 Task
└─ attach runs to parent Task
└─ TaskService.append_run(parent_task_id, sub_run_id, skill_names=...)
```
```text
Team v1 scope当前边界
├─ 已实现
│ ├─ Beaver 自有 coordinator models
│ ├─ sequence / parallel / dag 三个执行原语
│ ├─ pinned skill 继承 + open skill assembly
│ ├─ per-run memory snapshot支持真并发 prompt 构建
│ ├─ per-node provider factory 语义
│ ├─ parent Task 一致性校验
│ └─ 节点失败归一和 summary 失败区块
├─ 已接入 Task mode 内部执行链
│ ├─ TaskExecutionPlanner 先决定 single / team
│ ├─ team run 只作为内部 sub-agent 执行策略
│ ├─ TeamRunResult 不直接返回给用户
│ └─ 主 Agent synthesis run 生成用户可见最终回答
└─ 仍不暴露产品级 team / Task API
└─ 外部仍只使用聊天入口和反馈入口
```
---
## 9. Session Module
```text
SessionManager会话管理门面
├─ ensure_session(...)(确保会话存在)
├─ append_message(...)(追加一条事件或聊天消息)
├─ get_event_records(session_id)(获取完整事件流)
├─ get_run_event_records(session_id, run_id)(获取某次 run 的事件)
├─ update_latest_assistant_event_payload(...)(把 task/validation/feedback 状态投影到最新 assistant 消息)
├─ set_run_context_visible(session_id, run_id, visible)(隐藏失败重试草稿等 run
├─ list_run_ids(session_id)(列出某个会话下所有 run_id
├─ get_messages_as_conversation(session_id)(获取可作为聊天展示的消息)
├─ get_visible_history(session_id)(获取可进入 prompt 的历史)
├─ update_system_prompt(...)(更新当前会话 system prompt 快照)
├─ update_usage(...)(更新 token 用量)
├─ end_session(...)(结束会话)
├─ reopen_session(...)(重新打开会话)
├─ list_sessions_rich(...)(列出带摘要的会话)
├─ search_messages(...)(搜索历史消息)
└─ resolve_session_id(...)(根据前缀解析 session_id
```
```text
SessionStore (SQLite)SQLite 会话数据库)
├─ sessions table会话表
├─ messages table消息和事件表
├─ messages_fts全文搜索索引
├─ WALSQLite 写入日志模式)
├─ parent_session_id父会话字段给未来分支会话用
└─ hidden / visible event split隐藏事件和可见消息分离
```
```text
hidden events隐藏事件类型
├─ run_started运行开始
├─ skill_activation_snapshotted技能激活快照
├─ tool_selection_snapshotted工具选择快照
├─ system_prompt_snapshotted系统提示词快照
├─ run_completed运行完成
├─ run_failed运行失败
├─ skill_effects_snapshotted技能效果快照
├─ task_validation_snapshottedTask 验证快照)
└─ task_feedback_recordedTask 用户反馈快照)
```
---
## 10. Memory Module
```text
MemoryService记忆服务
├─ initialize()(初始化记忆存储)
├─ reload_for_new_run()(每轮开始前刷新记忆快照)
├─ get_snapshot()(获取本轮冻结记忆快照)
└─ get_store()(获取底层 MemoryStore
```
```text
MemoryStore长期记忆存储
├─ target: memory项目/任务级长期记忆)
├─ target: user用户偏好记忆
├─ add(...)(新增记忆)
├─ replace(...)(替换记忆)
├─ remove(...)(删除记忆)
├─ load_from_disk()(从磁盘读取)
├─ save_to_disk()(保存到磁盘)
└─ format_for_system_prompt(...)(格式化成 system prompt 段落)
```
```text
memory runtime semantics记忆运行语义
├─ run start本轮开始
│ └─ refresh live state -> capture frozen snapshot刷新 live memory并冻结本轮快照
├─ run middle本轮进行中
│ ├─ memory tool may write durable statememory 工具可以写入长期记忆)
│ └─ current run prompt snapshot stays frozen但本轮 prompt 里的记忆不变)
└─ next run下一轮
└─ newly written memory becomes visible上一轮写入的新记忆开始可见
```
---
## 11. Skills Module
```text
SkillsLoader技能加载器
├─ workspace published catalog工作区正式发布的技能目录
├─ workspace legacy skills/*/SKILL.md旧格式技能文件
├─ builtin skills内置技能
├─ list_skills()(列出运行时可见技能)
├─ list_published_skills()(只列正式发布技能)
├─ get_current_version()(获取当前正式版本)
├─ load_published_skill()(加载正式版本正文)
├─ get_skill_record()(获取技能元数据记录)
├─ get_skill_metadata()(获取 frontmatter 元数据)
├─ get_skill_tool_hints()(获取技能推荐工具)
├─ load_skills_for_context()(把多个技能加载成上下文块)
├─ build_skills_summary()(构造技能摘要索引)
├─ build_selection_candidates()(构造给 SkillAssembler 的候选摘要)
├─ list_skill_supporting_files()(列出技能支持文件)
├─ view_skill()(查看技能正文或支持文件)
└─ get_always_skills()(获取 always 类型技能)
```
```text
SkillAssembler技能选择器
├─ input输入
│ ├─ task_description用户任务描述
│ ├─ candidate skill summaries候选技能摘要
│ ├─ embedding runtime向量模型配置
│ └─ selector provider/model用于选择技能的模型
├─ embedding retrieve candidates先用向量召回相关技能
├─ LLM select names再让 LLM 选择技能名)
└─ return SkillContext[](返回技能上下文)
├─ name技能名
├─ content技能正文
├─ version技能版本
├─ content_hash内容哈希
├─ activation_reason激活原因
└─ tool_hints推荐工具
```
```text
skills lifecycle baseline技能生命周期基线
├─ SkillSpecStore技能生命周期文件存储
│ ├─ skill.json技能总信息
│ ├─ current.json当前版本指针
│ ├─ versions/(正式版本目录)
│ ├─ drafts/(草稿目录)
│ ├─ reviews/(审核记录目录)
│ └─ archive/(归档目录)
├─ DraftService草稿服务
│ ├─ create_new_skill_draft(...)(创建新技能草稿)
│ ├─ create_revision_draft(...)(创建修订草稿)
│ ├─ create_merge_draft(...)(创建合并草稿)
│ ├─ create_retire_proposal(...)(创建退役提案)
│ ├─ list_drafts(...)(列出草稿)
│ └─ get_draft(...)(读取单个草稿)
├─ ReviewService审核服务
│ ├─ submit_for_review(...)(提交审核)
│ ├─ approve(...)(批准草稿)
│ └─ reject(...)(拒绝草稿)
└─ SkillPublisher发布服务
├─ publish(...)(发布 approved 草稿为正式版本)
├─ apply_retire_proposal(...)(应用退役提案,不创建新版本)
├─ disable(...)(禁用技能)
└─ rollback(...)(回滚到旧版本)
```
---
## 12. Tools Module
```text
ToolRegistry工具注册表
├─ echo回显工具
├─ memory写入/管理长期记忆)
├─ skill_view查看完整 skill
├─ session_search搜索会话历史
├─ list_directory列目录
├─ read_file读文件
└─ search_files搜索文件
```
```text
ToolAssembler工具选择器
├─ selected = always tools先加入默认工具
├─ selected += activated skill tool hints再加入技能推荐工具
├─ selected += embedding top-k tools再用向量召回任务相关工具
└─ return ToolSpec[](返回本轮可用工具列表)
```
```text
ToolExecutor工具执行器
├─ normalize tool call规范化模型发来的工具调用
├─ resolve tool找到对应工具
├─ invoke tool执行工具
└─ return ToolResult返回工具结果
```
```text
filesystem tool boundary文件系统工具边界
├─ workspace scoped只能访问 workspace 范围)
├─ realpath enforcement用真实路径校验
├─ reject path escape拒绝路径逃逸
├─ reject symlink escape拒绝软链接逃逸
└─ reject binary file reads拒绝读取二进制文件
```
---
## 13. Provider Module
```text
make_provider_bundle(...)(创建模型调用组合)
├─ main_runtime主模型配置
├─ main_provider主模型调用器
├─ fallback_runtime备用模型配置
├─ fallback_provider备用模型调用器
├─ auxiliary_runtime辅助模型配置
├─ auxiliary_provider辅助模型调用器
└─ embedding_runtime向量模型配置
```
```text
provider roles模型角色分工
├─ main主模型
│ └─ assistant/tool loop负责正常回答和工具循环
├─ fallback备用模型
│ └─ main failure recovery主模型失败时兜底
├─ auxiliary辅助模型
│ └─ skill selection / future helper tasks负责选择技能等辅助任务
└─ embedding向量模型
└─ skill/tool semantic retrieval负责 skill / tool 的语义召回)
```
---
## 14. Service Lifecycle
```text
AgentService服务生命周期
├─ MainAgentRouter请求进入 AgentLoop 前先分类 simple / task
├─ submit_feedback(...)(聊天反馈入口,内部更新 Task 状态)
├─ direct mode直接模式适合 CLI / 单次调用)
│ └─ process_direct(...)(直接处理一次任务)
└─ running mode后台运行模式适合 Web / Gateway
├─ start()(启动 AgentLoop.run
├─ submit_direct(...)(向队列提交任务)
├─ stop(timeout_seconds, force)(停止并等待任务收尾)
├─ shutdown(timeout_seconds, force)(停止并释放 runtime
└─ close()(关闭已停止的 loop
```
```text
running mode semantics后台运行语义
├─ start()(启动)
│ └─ AgentLoop.run()(进入队列消费循环)
├─ submit_direct()(提交任务)
│ └─ enqueue _DirectRunRequest把任务放入队列
├─ stop()(停止)
│ ├─ stop accepting new tasks不再接收新任务
│ └─ drain queued tasks等待已排队任务处理完
└─ close()(关闭)
└─ requires loop already stopped必须先 stop才能 close
```
---
## 15. Task Team Registry / Process / Learning 闭环
```text
TaskExecutionPlannerTask 内部执行规划)
├─ LLM planner
│ ├─ 输出 single / team
│ └─ team 只允许 sequence / parallel / dag
├─ TaskSkillResolver
│ ├─ 从 published skill catalog 检索候选
│ ├─ 按 skill_query / required_capabilities / node task 选择 skill
│ ├─ 命中 published skill 后写入 graph.nodes[].inherited_pinned_skills
│ └─ 无命中时创建 draft-only skill并写入 graph.nodes[].inherited_pinned_skill_contexts
└─ TaskExecutionPlan
├─ graph.nodes[].agent 只是 generic runtime trace identity
└─ to_event_payload() 写入 skill_queries / selected_skill_names / generated_skill_draft_ids / skill_resolution_report
```
```text
Task mode attempt每次 Task attempt
├─ task_execution_planned隐藏事件
│ └─ plan_mode / strategy / node_ids / skill_resolution_report
├─ team run仅 team plan
│ ├─ sub-agent run_ids 回填父 Task
│ ├─ team summary 只进入主 Agent synthesis context
│ └─ task_team_run_completed / task_team_run_failed隐藏事件
├─ main Agent synthesis
│ ├─ 输出最终用户可见回答
│ └─ task_synthesis_completed隐藏事件
└─ validation
├─ task_validation_snapshotted隐藏事件
├─ 第一次失败隐藏草稿并重试一次
└─ 第二次或验证通过后等待反馈
```
```text
Frontend process projection
├─ GET /api/sessions/{session_id}/process
│ ├─ 读取隐藏 Task/team/validation 事件
│ ├─ 合并 run memory records
│ └─ 输出 processRuns / processEvents / processArtifacts / agents
└─ ChatWorkbench
├─ 桌面端显示 ProcessLane
├─ 移动端显示 Process tab
└─ 不直接暴露隐藏事件原始 JSON
```
```text
Learning pipeline
├─ feedback gate
│ └─ validation accepted + satisfied 才生成 learning candidate
├─ SkillLearningPipelineService
│ ├─ candidate -> queued / synthesizing
│ ├─ worker/run-once -> draft
│ ├─ draft -> safety report
│ ├─ draft -> lightweight eval report
│ ├─ safety_failed / eval_failed 阻断发布
│ ├─ draft -> submit review
│ ├─ approve / reject
│ ├─ approved + safety passed + eval not failed -> publish
│ ├─ retire proposal -> apply retire
│ └─ rollback / disable
├─ SkillLearningWorker
│ ├─ 默认按配置定时扫描 open candidates
│ ├─ 自动生成 draft_ready / safety_failed / eval_failed
│ └─ 永不自动 approve / publish
├─ Web review workbench
│ ├─ Candidates
│ ├─ Draft detail
│ ├─ Safety report
│ └─ Eval report
└─ Runtime catalog
└─ 只有 published skill 进入运行时选择draft 不生效
```
---
## 16. Web / Gateway
```text
Web网页入口
├─ FastAPI lifespanFastAPI 生命周期)
│ ├─ create or receive AgentService创建或接收 AgentService
│ ├─ start() when app owns lifecycle如果 Web app 拥有生命周期,就启动 service
│ ├─ start SkillLearningWorker when enabled按配置启动技能学习 worker
│ └─ shutdown() when app owns lifecycle如果 Web app 拥有生命周期,就关闭 service / worker
├─ GET /api/ping健康检查接口
│ └─ return status / running / mode返回状态、是否运行、运行模式
├─ POST /api/chat聊天接口
│ ├─ validate WebChatRequest校验请求体
│ ├─ agent_service.submit_direct(...)(把用户消息提交给 AgentService
│ └─ return WebChatResponse返回模型回复 + run/task/validation 元数据)
└─ POST /api/chat/feedback聊天反馈接口
├─ validate WebChatFeedbackRequest
├─ agent_service.submit_feedback(...)
└─ return WebChatFeedbackResponse
Skills learning admin API
├─ GET /api/skills/candidates
├─ POST /api/skills/candidates/{candidate_id}/draft
├─ POST /api/skills/candidates/{candidate_id}/regenerate
├─ POST /api/skills/learning/run-once
├─ GET /api/skills/{skill_name}/drafts/{draft_id}/safety
├─ GET /api/skills/{skill_name}/drafts/{draft_id}/eval
└─ POST /api/skills/{skill_name}/drafts/{draft_id}/publish
└─ requires approved review + safety passed + eval not failed
```
```text
Gateway消息通道入口
├─ MessageBus内部消息总线
├─ inbound -> AgentService.handle_inbound_message(...)(外部消息进入 AgentService
└─ outbound <- OutboundMessageAgentService 返回结构化输出消息)
```
---
## 17. Bus Mode Skeleton
```text
AgentLoop.run()(后台队列运行模式)
├─ create queue创建任务队列
├─ mark running标记为运行中
├─ consume _DirectRunRequest消费一个任务请求
├─ call _process_direct_impl(...)(调用真正的单轮执行逻辑)
├─ set future result / exception把结果或异常写回等待方
├─ stop() -> enqueue sentinel停止时放入结束标记
└─ drain pending queue on exit退出时清理未处理任务
```