Files
beaver_project/app-instance/backend/flow.md
steven_li ebfa242862 feat(outlook): 添加Outlook集成功能支持
添加完整的Outlook MCP集成,包括邮件和日历功能,通过AuthZ模式进行认证和权限管理,
支持邮箱连接、断开、状态检查和数据同步等功能。

fix(config): 统一配置文件路径从.nanobot到.beaver

将配置文件路径从/root/.nanobot统一更改为/root/.beaver,更新Dockerfile中的环境变量定义,
确保所有组件使用一致的配置目录结构。

feat(agent): 添加代理删除功能和助手身份提示

为代理注册表添加delete_agent方法,实现代理的动态删除功能;同时添加海狸助手身份提示,
确保AI助手在交互中保持一致的身份认知。

feat(engine): 增强引擎循环并添加意图决策快照

扩展AgentLoop类,添加intent_agent_decision参数用于意图驱动的代理决策,并在会话中记录
决策快照,便于后续分析和调试。

feat(authz): 扩展认证客户端功能

为AuthzClient添加设置权限、用户注册、后端注册和Outlook设置管理等新方法,增强系统
的认证和授权能力。
2026-05-14 16:01:46 +08:00

906 lines
37 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统一服务层所有入口都先汇总到这里
├─ Intent Agent / MainAgentRouter第一层意图判断simple chat / continue task / create task / close / abandon
├─ load intent-agent-router skill内部 skill 指引:只做路由,不回答用户,不使用工具)
├─ classify(...)LLM 语义判断)
├─ session hidden event: intent_agent_decision_snapshotted记录选择 simple_chat / create_task / continue_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, recent_messages, intent-agent-router skill)Intent Agent 语义分类)
│ ├─ Intent Agent 只返回 JSON 路由结果,不直接回答用户
│ ├─ Intent Agent 没有 tools凡是需要工具、实时/外部数据、文件、执行、验证的请求都应进入 Task
│ ├─ session hidden event: intent_agent_decision_snapshotted调试日志展示 choice / reason / short_title
│ ├─ simple简单问题
│ │ └─ runner(message, include_skill_assembly=False, include_tools=False)(不创建 Task不跑 skills/tools
│ │
│ ├─ continue_task继续当前 Task
│ │ └─ reuse active Task只要话题没有完全无关就继续当前 open Task
│ │
│ ├─ new_task明确开启新任务
│ │ └─ TaskService.create_task(...)(内部创建 Task并保存 short_title
│ │
│ ├─ close_task / abandon_task用户明确结束或放弃
│ │ └─ TaskService.close_task / abandon_task关闭当前 Task
│ │
│ └─ task execution
│ └─ AgentService._run_task_mode(...)(进入 Task 模式执行)
```
```text
TaskService内部 Task 状态机)
├─ TaskRecord
│ ├─ task_id
│ ├─ session_id
│ ├─ goal / description / constraints
│ ├─ metadata.short_title5-15 字左右的短标题,用于前端当前任务标识)
│ ├─ 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向量模型配置用于语义召回
├─ if include_skill_assembly=Falsesimple_chat 默认关闭)
│ └─ skip SkillAssembler不激活 skill不注入 skill 正文)
├─ if include_skill_assembly=TrueTask mode 默认开启,在 Task 创建/复用和规划之后执行)
│ └─ skill_assembler.assemble(...)(选择本轮应该激活哪些 published skill
│ ├─ input task_description = skill_selection_context or current user input
│ │ ├─ Task goal / description
│ │ ├─ current user request
│ │ ├─ attempt / revision / team synthesis phase
│ │ ├─ validation feedback重试时
│ │ ├─ team summary / planteam synthesis 时)
│ │ └─ previously activated skills只作为 reuse bias不是 pinned
│ ├─ SkillsLoader.build_selection_candidates()(列出候选技能摘要)
│ ├─ embedding retrieve skill candidates用向量召回相关技能
│ ├─ LLM shortlist candidate names先用摘要粗选少量候选
│ │ └─ if retrieved candidates <= max_detailed_candidates -> skip shortlist
│ ├─ SkillsLoader.load_published_skill(...)(系统侧内部读取粗选候选正文,不暴露 skill_view 给主 Agent
│ ├─ LLM final select activated skills结合候选正文做最终选择
│ ├─ if no matching skill -> return [] and continue run without 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(...)选择本轮应该暴露哪些工具simple_chat 默认跳过)
│ ├─ 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="skill_selection_context_snapshotted", hidden)(完整记录 skill query
├─ 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_run_receipts(...)(记录运行证据,不生成学习候选)
├─ 失败时(运行中出现异常)
│ ├─ append assistant error message写入 assistant 错误消息)
│ ├─ session_manager.append_message(event_type="run_failed", hidden)(记录隐藏事件:运行失败)
│ └─ _record_run_receipts(...)(即使失败也记录运行证据)
└─ 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工具调用循环
├─ session_manager.append_message(event_type="llm_request_snapshotted", hidden)(完整记录本次 provider messages / tools
├─ 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. Run Evidence / Skill Effect Recording
```text
AgentLoop._record_run_receipts(...)(记录本轮运行证据;不直接学习)
├─ 构造 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(...)(更新表现快照)
│ └─ never build learning candidates in runtime hot path运行完成时永不生成候选
└─ session_manager.append_message(...)(记录隐藏事件:技能效果快照)
├─ event_type="skill_effects_snapshotted"(技能效果已快照)
├─ hidden不进入普通聊天上下文
└─ payload隐藏数据
├─ run_record本轮运行记录
├─ skill_effects技能效果记录
├─ candidate_generation_allowed本轮是否允许生成候选runtime 固定 false
└─ learning_candidates学习候选默认空
```
```text
runtime invariant运行期不直接学习
├─ run completed / run failed
│ └─ 只写 RunRecord + SkillEffectRecord + performance snapshot
├─ simple chat
│ └─ 不创建 Task不触发 learning candidate
└─ Task attempt / sub-agent run
└─ 只留下证据,等待 feedback gate 决定是否学习
```
---
## 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_for_task(task_id, trigger_run_id)
│ └─ if validation not accepted
│ └─ 记录人工接受但保留验证风险;不自动生成 learning candidate
├─ revise
│ ├─ Task status -> needs_revision
│ ├─ 更新 run / skill effect 为需修订证据
│ └─ 下一条用户消息默认复用该 Task不生成 learning candidate
└─ abandon
├─ Task status -> abandoned
├─ 更新 run / skill effect 为失败证据
├─ 追加 task_failure_evidence_recorded 隐藏事件
└─ 默认不写主 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 生成的一次性 ephemeral guidance
│ └─ allow_candidate_generation=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 -> AgentLoopephemeral guidance 只在本次 run 注入)
│ └─ 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()(列出技能支持文件)
└─ get_always_skills()(获取 always 类型技能)
```
```text
SkillAssembler技能选择器
├─ input输入
│ ├─ task_descriptionTask-aware queryTask 描述 / 当前用户消息 / previous skills / attempt context / validation revision context / team context
│ ├─ candidate skill summaries候选技能摘要
│ ├─ embedding runtime向量模型配置
│ └─ selector provider/model用于选择技能的模型
├─ embedding retrieve candidates先用向量召回相关技能
├─ LLM shortlist names用摘要粗选需要查看正文的候选
│ └─ skip when candidate count <= max_detailed_candidates候选很少时直接读取正文
├─ internal load shortlisted SKILL.mdSkillAssembler 内部读取候选正文)
├─ LLM final select names结合候选正文选择最终技能名
├─ no match returns [](没有对应 published skill 时返回空,不阻塞任务)
└─ 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写入/管理长期记忆)
├─ 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[](返回本轮可用工具列表;不通过工具动态加载 skill
```
```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
│ └─ 无命中时创建 ephemeral guidance并写入 graph.nodes[].inherited_pinned_skill_contexts
└─ TaskExecutionPlan
├─ graph.nodes[].agent 只是 generic runtime trace identity
└─ to_event_payload() 写入 skill_queries / selected_skill_names / ephemeral_guidance_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
├─ evidence recording
│ ├─ every run -> RunRecord
│ ├─ activated skills -> SkillEffectRecord
│ └─ no candidates generated here
├─ feedback gate
│ ├─ validation accepted + satisfied -> scoped learning candidate
│ ├─ validation rejected + satisfied -> 记录人工接受风险,不生成候选
│ ├─ revise -> 保持 Task 打开,不生成候选
│ └─ abandon -> 失败证据,不写主 memory不生成成功候选
├─ scoped candidate generation
│ ├─ source = current task run_ids
│ ├─ no published skill -> new_skill
│ └─ published skill used -> revise_skill
├─ 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 元数据)
├─ WS /ws/{session_id}(网页 WebSocket 适配层)
│ ├─ ping -> pong
│ ├─ message -> agent_service.submit_direct(...)
│ ├─ return status / assistant message携带 run/task/validation 元数据)
│ └─ return session_updated通知前端刷新 session/process
└─ 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内部消息总线
├─ ChannelAdapterTelegram / Slack / Email / WhatsApp 等只作为 adapter
├─ inbound -> AgentService.handle_inbound_message(...)(外部消息进入 AgentService
├─ outbound <- OutboundMessageAgentService 返回结构化输出消息)
└─ ChannelManager按 message.channel 分发 outbound
```
---
## 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退出时清理未处理任务
```