CLAUDE.md@apps/server/agent · diff
git:20261013.e85669c to git:20261014.f02f003
1 added, 433 removed. Audit A to B.
- # Agent 模块架构文档
-
- 本文档描述 zenstory Agent 系统的架构、流程和关键组件。
-
- ## 目录结构
-
- ```
- agent/
- ├── service.py # 主服务入口
- ├── suggest_service.py # 智能建议生成服务
- ├── stream_adapter.py # LangGraph 事件适配器
- ├── context/ # 上下文组装模块
- │ ├── assembler.py # 上下文组装器
- │ ├── budget.py # Token 预算管理(含请求级 prompt 台账)
- │ └── prioritizer.py # 优先级管理
- │ # 注:没有会话压缩/总结模块,历史只按 token 预算取最近窗口
- ├── core/ # 核心基础设施
- │ ├── events.py # SSE 事件定义
- │ ├── llm_client.py # OpenAI 兼容 LLM 客户端
- │ ├── message_manager.py # 消息和系统提示管理
- │ ├── session_loader.py # 会话加载器(历史窗口 + 面包屑 + 跨轮工作集)
- │ └── stream_processor.py # 文件流处理器
- ├── graph/ # LangGraph 工作流
- │ ├── state.py # 工作流状态定义
- │ ├── writing_graph.py # 图执行入口
- │ ├── nodes.py # 流式节点实现
- │ └── router.py # 意图路由
- ├── llm/ # LLM 集成
- │ └── openai_agents/ # openai-agents-python / DeepSeek 写作 Agent 适配层
- ├── prompts/ # 共享协议与角色提示(项目配置仅存数据库)
- │ ├── base.py # 基础提示
- │ ├── subagents.py # 子代理提示 (planner/writer/quality_reviewer)
- │ └── suggestions.py # 建议生成提示
- ├── schemas/ # 数据模型
- │ ├── context.py # 上下文数据模型
- ├── skills/ # 技能系统(标准 SKILL.md,渐进式加载,永不执行脚本)
- │ ├── active_skills.py # 当前用户启用中的技能视图(目录/工具/显式选择共用)
- │ ├── context_injector.py # L1 技能目录(只含名称 + 用途)
- │ └── package.py # SKILL.md 解析、zip 导入安全检查、导出打包
- └── tools/ # 工具实现
- ├── tool_schemas.py # provider-neutral 工具 schema 定义
- ├── file_executor.py # 文件操作执行器
- ├── mcp_tools.py # MCP 格式工具函数
- └── permissions.py # 权限检查
- ```
-
- ## 核心流程
-
- ### 1. 请求处理流程
-
- ```
- 用户消息
- │
- ▼
- ┌─────────────────────────────────────────────────────────┐
- │ AgentService.process_stream() [service.py] │
- │ - 设置 ToolContext │
- │ - 组装上下文 (ContextAssembler) │
- │ - 加载会话历史 (SessionLoader) │
- │ - 构建系统提示 (MessageManager) │
- │ - 调用工作流 │
- └─────────────────────────────────────────────────────────┘
- │
- ▼
- ┌─────────────────────────────────────────────────────────┐
- │ run_writing_workflow_streaming() [writing_graph.py] │
- │ - 路由策略选择初始 agent(默认 llm,可配置 off) │
- │ - 循环执行 agent 直到完成或达到最大迭代次数 │
- └─────────────────────────────────────────────────────────┘
- │
- ▼
- ┌─────────────────────────────────────────────────────────┐
- │ router (llm / off) [router.py] │
- │ - llm: 调用 router_node()(DeepSeek Chat Completions) │
- │ - off: 固定从 writer 开始 │
- │ - 返回: initial_agent + workflow_plan + workflow_agents │
- └─────────────────────────────────────────────────────────┘
- │
- ▼
- ┌─────────────────────────────────────────────────────────┐
- │ run_streaming_agent() [nodes.py] │
- │ - 组合基础提示 + 专业 agent 提示 │
- │ - 调用 openai_agents.runner │
- │ - 通过 openai-agents-python 处理工具调用和 handoff │
- └─────────────────────────────────────────────────────────┘
- │
- ▼
- ┌─────────────────────────────────────────────────────────┐
- │ StreamAdapter.adapt_langgraph_events() [stream_adapter]│
- │ - 转换 LangGraph 事件为 SSE 事件 │
- │ - 处理文件流式写入 (<file>...</file>) │
- │ - 发送事件到前端 │
- └─────────────────────────────────────────────────────────┘
- ```
-
- ### 2. 多 Agent 协作流程
-
- ```
- ┌─────────────┐
- │ Router │ ─── 分析意图,确定工作流
- └─────────────┘
- │
- ▼
- ┌───────────────────────────────────────┐
- │ 工作流类型 (workflow_plan) │
- ├───────────────────────────────────────┤
- │ quick : writer(必要时再 review) │
- │ standard : planner → writer(必要时再 review)│
- │ full : planner → hook_designer → writer(必要时再 review)│
- │ hook_focus : hook_designer → writer(必要时再 review)│
- │ review_only : quality_reviewer │
- └───────────────────────────────────────┘
- 上表是计划序列的上限:用户没要正文(write_content=false)时剔除 writer,
- 用户明确只读(read_only=true)时整条序列清空(router.plan_workflow_agents)。
- │
- ▼
- ┌─────────────┐ handoff ┌─────────────┐ handoff ┌─────────────┐
- │ Planner │ ───────────────► │ Writer │ ───────────────► │ Quality Reviewer │
- │ 大纲规划师 │ │ 内容创作者 │ │ 质量审稿人 │
- └─────────────┘ └─────────────┘ └─────────────┘
- ```
-
- ### 3. Agent 交接机制
-
- Agent 可以通过两种方式交接:
-
- 1. **显式 handoff**: Agent 调用 `handoff_to_agent` 工具
- 2. **工作流自动交接**: 按照 router 规划的 workflow_agents 顺序执行
- 3. **自动质检门**: writer 本轮写进正文类文件(draft / script)的字数达到阈值后自动交给 quality_reviewer
- (`graph/body_writes.py`:create_file 之后的 `<file>` 正文、edit_file / parallel 子任务写进去的文字,
- 按 `count_words` 计;改大纲、设定、角色等不算,writer 的聊天文字也不算)
-
- 用户范围优先于交接(writing_graph.py):
-
- - agent 以向用户提问收尾(最后一次工具调用之后的文本以问号结尾,见
- `nodes.ends_with_question_to_user`)时,计划交接不触发,本轮停下等用户回答;
- 结构化的 `request_clarification` 仍是首选信号。显式 handoff 不受影响。自动质检门不看这个
- 信号(writer 收尾"需要我继续写第二章吗?"时,开启了自动质检/高质量模式仍照常送审)。
- 例外:规划类角色(planner / hook_designer)以提问收尾、但路由 `write_content=true` 且计划里
- 还有 writer 时不清空,writer 的交接说明里写明按推荐方案写、不再问
- (`graph/author_scope.planning_question_keeps_writer`)。
- - 计划交接的交接说明固定带「已完成的不重做、不改刚交付的文件、不追加作者没要的产出」。
- - 「帮我优化一下」这类整句笼统的要求(`author_scope.is_vague_edit_request`)不走路由:writer 单独
- 一轮先问清楚改哪里(`state["clarify_first"]`,写文件工具以 `error_type="clarify_first"` 拒绝,
- 显式交接丢弃);上一轮收尾只提了一处修改(作者是在答应它)、或上一轮已经问过一次时不问。
- 这个判断先于「沿用上一轮路由」。
- - 作者要了规划(原话里有大纲 / 分章 / 分集 / 规划,不是只想聊聊)、规划师却把至少 5 个
- 「第 N 章 / 集」的规划只贴在对话里、本请求的大纲文件又没覆盖时,同一个规划师补一轮把规划写进
- 大纲文件(每请求一次,`author_scope.chat_only_plan_units` / `request_asks_for_a_plan`)。
- - 作者手动改过的文件,**修改不再拦**(2026-10-12):`tools/author_changes.py` 按版本历史算出作者在 AI 写过之后
- 的净差异,放进工作集旁的 `<author_edits>`,`query_files` 按 id 读全文时带 `author_edits`,由模型保留作者写法或按作者
- 新要求改。`edit_file` 在锁内比对本请求里模型看到的正文(`_ReadSnapshots`:本轮开始时以全文给出的上下文/工作集、当前 agent
- 这一程 query_files 读到的(下一次模型调用起才算,`usage_hooks.ReadSnapshotPromotionMixin`)、本请求自己写入的(提交前记);换 agent 时清掉「读到的」),作者生成期间又改了就拒绝(`stale_content`,列出新改动,卡片按「有意没动」显示),本请求没看过全文的文件只按逐字匹配;
- 改到作者写的段落时结果带 `author_edits_touched`,卡片如实提示、可撤销。
- - 删除和整份覆盖仍拦:作者本轮没点名要删 / 重写、也不是正开着它提出时,`delete_file`(含递归删文件夹里的这类
- 文件)/ 剧本 `create_file` 复用同名剧集都不执行,返回 `error_type="author_edit_protected"`,让 agent 在回复里问作者;
- 作者在消息里点名这个文件并说删(或整份重写)时才执行。删除(单个文件或递归删文件夹里的)要作者在点名的那一处说删。
- (原先随 routing 落库的 `author_confirm_file_ids` 已删:它只服务过 edit_file 的放行。)
- 规则见 `tools/author_edit_guard.py`;工作集也会标注这类文件。前端工具卡片把
- `author_edit_protected` / `clarify_first` 显示成「保留了你改过的内容」/「先问清楚再改」,不显示「失败」。
- - 每个 agent run 的 TEXT 经 `core/author_facing_text.AgentTextShaper` 整理:和前一个角色的文字之间、
- 同一个 agent 隔着工具调用的两段文字之间补空行;作者写中文时,紧接着工具调用的英文过程句
- (「Now create the … file.」)不发给作者。
- - 作者只看到一份总结:writer(非快速模式、非返修、未到审查上限,且开着自动质检门或计划里下一个
- 是 quality_reviewer)写完正文类文件、可能送审时(create_file 正文还没写出来;edit_file /
- write_chapter 累计字数到了送审阈值;或计划送审),图发 `StreamEventType.CHAT_HOLD(begin)`,
- StreamAdapter 把之后的对话文字(正文不扣)先扣住;本轮真的交给 quality_reviewer 时图在 HANDOFF
- 前发 `CHAT_HOLD(discard)` 丢掉它,由审稿人(或返修后的 writer)写唯一一份总结,writer 收尾时问
- 作者的问题以 `[内容创作者要问作者的问题]` 原样交给它们;不送审时扣着的文字在下一个 agent / 交接 /
- 收尾 / 出错事件前、或流结束时原样放出(run 中途的 STEERING_RECEIVED 不放出、不结束扣留)。
- CHAT_HOLD 只在图和适配器之间,不发给前端。
- - 送审交接附 `[本轮正文字数]`(本请求写过的 draft / script 落库后的 `count_words`,
- 也写进交接包 evidence)和 `[作者的篇幅要求]`(项目简介 / 写作风格 / 备注、本会话作者说过的话、
- 本轮原话里带「数字 + 字」的篇幅分句,`review_evidence.find_length_requirements`),审稿人按它们判篇幅。
- 两次读取在同一个工作线程里按顺序做,共用一份 0.5 秒预算(超时时用已读到的);之后的逐字复读 /
- 数字事实检查另有自己的 0.5 秒。
- - StreamProcessor 在 IDLE(没有进行中的捕获)时丢掉独占一行的 `<file>` / `</file>`(不在 ``` 围栏里),
- 行内提到、反引号包着的照常保留。
- - 只读请求(router `read_only=true`)不交接给有写权限的 agent,显式 handoff 也拦下,并给一张
- `WORKFLOW_STOPPED(reason="read_only_handoff_blocked")` 提示卡片;交接给 quality_reviewer 这类
- 只读 agent 照常。前端收到任何 workflow_stopped 都会结束流式状态,所以拦下时只记录、不 break,
- 提示推迟到收尾才发:紧挨在终止事件(WORKFLOW_COMPLETE / 澄清或无效交接 / 轮数耗尽 / ERROR)
- 之前,或自然结束时作为图的最后一个事件。`api/agent.py` 判定终止事件时跳过这个 reason
- (`core/events.NON_TERMINAL_WORKFLOW_STOPPED_REASONS`),之后若异常仍补发兜底 error 帧并退款。
- graph 同时置 `state["read_only"]`,`tools_adapter` 对
- `registry.FILE_WRITE_TOOL_NAMES`(create/edit/delete_file、parallel_execute)的调用一律拒绝执行、
- 返回 `error_type="read_only_request"` 的可恢复错误(工具仍在清单里,描述标明本轮不可用),
- 拒绝经熔断器记账;`parallel_execute` 整次拒绝,即使这一批只有 query_files
- 子任务;`update_project`(项目信息/任务计划,不是文件)不在其中,只读请求下照常执行。
- 只读请求万一留下空文件,直接回滚、不安排补写。
- - runner 的 `RunConfig` 设 `tool_not_found_behavior="return_error_to_model"`:模型调用工具集里
- 没有的工具(如审稿人调 edit_file)时得到 `error_type="tool_not_found"` 的结构化错误并经熔断器
- 记账(同名连续 3 次的熔断原因为 `repeated_unavailable_tool_call`),而不是 SDK 抛
- `ModelBehaviorError` 以致命 ERROR 结束。
- - 路由的范围判断转成 `state["scope_directive"]`,由 `nodes.run_streaming_agent` 追加到本轮每个
- agent 的系统提示末尾;graph 在每次跑 agent 前按当前 agent 重新生成,「作者已要正文、规划完直接
- 交接 writer」那句只给规划类角色(planner / hook_designer)。计划交接的 handoff_packet.todo 也带上用户的 `scope`。
- - 送审内容(`[待审查内容]`)取本请求写过的正文落库后的样子,每个文件带系统按编辑器口径算的字数;
- 本请求之前就有正文、整份放不进送审预算的长文件只附本请求改动的那一段(写入前正文由
- `ToolContext.remember_pre_write_content` 在 AI 第一次写入前记下)。
- - 空文件纠偏轮由建了空文件的 agent 自己补写(不再硬编码 writer);被纠偏打断的那一轮若以提问
- 收尾,「等用户回答」会带过纠偏轮,纠偏完成后照样停下。
- - 自动质检门只认成功的写入:writer 的 create_file/edit_file 全部失败时不送审。
- - 写完 `</file>` 就结束了 run(openai-agents 把不带工具调用的回复当作最终输出):有写权限的
- agent 这次 run 里 create_file 成功、最后一次工具调用之后的文字以写完的 `<file>` 正文结束
- (`graph/completion_evidence.ends_on_file_block`),且没有显式交接、澄清、轮数耗尽、错误时,
- 同一个 agent 接着跑一轮(交接说明以 `FILE_STREAM_CONTINUATION_MARKER` 开头,带已保存的文件、
- 作者范围、任务板上没完成的任务):还有没写的接着写,写完了给作者总结。放在计划交接 / 自动质检
- 之前,正文与正文写入记录(`writer_body_writes`)接续给续写轮;受 `max_iterations` 约束,每一轮都要
- 真的新建并写完文件才会再续。
- - 回复里「已保存 / 已还原 / 已修改」是否属实不做文字检测,靠提示词约束(`prompts/base.py` 统一执行
- 协议:只有本轮对应的工具调用成功后才这样说,没改就直说没改)。
- - 收尾补发的 `update_project(tasks)`(`_auto_finalize_task_board_on_completion`)只按本请求实际写出、
- 仍存在且有正文的文件收尾 in_progress 任务:编号范围(「第2-5集」,`tools/task_progress.py`)全部有
- 对应文件才 done,只写了一部分时保持 in_progress 并记 `done_units` / `progress`(任务板提示里显示;
- 模型重存任务板时 `execute_update_plan` 把同名任务的记录接过去,收尾时剔除已删掉文件的编号);
- 带 `artifact` 的要写出这个文件;本请求什么都没写不补。模型自己把范围任务标 done 而项目里缺文件时,
- `execute_update_plan` 在结果里返回 `unverified_done` 提醒。
- - 空文件守卫(一次只流式写一个文件)挡住的 create_file,若要建的正是挡路的那个空文件(同标题、
- 同父目录、同类型,且没有建库途中的占坑),返回那个文件(`content=""`、`next_step` 提示直接写
- `<file>` 正文),不再报 `pending_empty_file_unwritten`;挡路的是别的文件时照旧拒绝
- (`mcp_tools._repeat_of_pending_empty_file`)。
- - 项目备注(AI 记忆 notes)按来源分三块存成纯文本:未标来源 / 【作者确认】/ 【AI 提议·待作者确认】
- (`config/project_notes.py`)。`update_project` 的 notes 新条目逐条核对:那一条本身得是 `author_quotes`
- 里某句逐字出现在作者本轮消息里的原话(或其中连续的一段),才进「作者确认」,否则记成 AI 提议
- (结果里 `notes_recorded_as_proposals`);一句原话不给别的条目作保。`proposals` 整体替换 AI 提议块,
- 和 notes 共用 4000 字(超出返回 `notes_over_budget`,不写任何字段)。上下文里三块分开渲染
- (`render_project_notes`),标题行能被解析回原分区,AI 提议写明不是设定。
- - 回复语言跟随作者最近一条消息(`message_manager.detect_author_language`),系统提示末尾写明本轮
- 语言,所有角色(含审稿人)共用;界面语言只在消息看不出语言时兜底。
-
- ## 关键组件详解
-
- ### AgentService (service.py)
-
- 主服务类,处理用户消息的流式响应。
-
- ```python
- async def process_stream(
- session: Session,
- project_id: str,
- user_message: str,
- ...
- ) -> AsyncIterator[str]:
- # 1. 设置工具上下文
- ToolContext.set_context(session, user_id, project_id, session_id)
-
- # 2. 组装项目上下文
- context_data = session_loader.assemble_context(...)
-
- # 3. 构建系统提示
- system_prompt = message_manager.build_system_prompt(...)
-
- # 4. 执行工作流
- async for event in run_writing_workflow_streaming(state):
- yield stream_adapter.adapt_event(event)
-
- # 5. 保存消息历史
- await message_manager.save_messages(...)
- ```
-
- ### Router (router.py)
-
- 意图分类器,决定使用哪个 agent 和工作流。
-
- **输入**: 用户本轮原话;有上一条 assistant 消息时,user 消息为
- `[上一轮助手结尾]`(最后 600 字 + 状态卡摘要)+ `[上一轮路由]`(落库 routing 的一行 JSON)+
- `[用户本轮原话]`(`build_router_user_message`,系统提示保持不变)。「继续」直达和简短回答的
- 确定性沿用(`resume_route_after_exhaustion` / `inherit_routing_after_clarification`)先于 LLM 路由,
- 命中时不调用路由模型。ROUTER_PROMPT 对回应上一轮提问的回复分两种:补问作答(任务没做完)沿用上一轮
- 全部路由字段;确认提议的下一步(「要我继续写第二章吗?」「要我帮你改吗?」)按提议的那一步路由,
- 不沿用 read_only=true / write_content=false / 已交付的 scope。
- **输出**:
- - `current_agent`: 初始 agent (planner/writer/quality_reviewer)
- - `workflow_plan`: 工作流类型 (quick/standard/full/hook_focus/review_only)
- - `workflow_agents`: 后续要执行的 agent 列表(已按用户范围裁剪)
- - `routing_metadata`: RouterDecision 全量,含用户范围字段 `write_content`(是否要正文,
- None=旧格式未给出)、`read_only`(明确不改文件)、`scope`(交付范围摘要:ROUTER_PROMPT 要求 <=40 字,`MAX_SCOPE_CHARS`=80 为硬截断上限)
-
- ### OpenAI Agents SDK Runner (openai_agents/runner.py)
-
- 写作 agent 的模型循环由 `openai-agents-python` 承担,项目侧只保留图节点入口、工具适配和事件映射。
-
- ```python
- async def run_openai_agents_streaming_agent(...):
- # 1. 归一化会话历史和当前用户消息
- api_messages = build_history_messages(state)
-
- # 2. 构建 SDK Agent(DeepSeek deepseek-flash + 项目工具)
- # 熔断器取 state["tool_failure_breaker"](writing_graph 每个请求建一个,跨 agent run 共享);
- # state["read_only"] 为 True 时写文件工具仍在工具集里,但调用一律拒绝执行(read_only_request)
- sdk_agent = _build_agent(agent_type, system_prompt, failure_breaker=..., read_only=...)
-
- # 3. 运行 Runner.run_streamed 并映射 SDK 事件
- async for sdk_event in result.stream_events():
- # response.output_text.delta -> TEXT
- # reasoning delta -> THINKING
- # tool_called -> TOOL_USE
- # tool_output -> TOOL_RESULT / HANDOFF / WORKFLOW_STOPPED
- # (工具失败熔断后:MESSAGE_END(stop_reason=tool_failure_circuit_open) + ERROR)
- yield mapped_event
-
- # 4. 写回 assistant/tool turn,供后续 graph agent 使用
- state["messages"] = updated_messages
- ```
-
- ### StreamAdapter (stream_adapter.py)
-
- 事件适配器,处理文件流式写入。
-
- **核心功能**:
- - 转换 LangGraph 事件为 SSE 格式
- - 处理 `<file>...</file>` 标记的文件内容流
- - 在文件写入完成后更新数据库
-
- ### ContextAssembler (context/assembler.py)
-
- 上下文组装器,收集项目相关内容。
-
- **收集内容**:
- - 焦点文件 (当前编辑的文件)
- - 附加文件 (用户选择的参考文件)
- - 用户引用文本
- - 相关大纲、角色、设定
-
- **优先级** (prioritizer.py):
- 1. CRITICAL - 焦点文件、用户引用
- 2. CONSTRAINT - 角色设定、高重要度设定、前一章(档内角色卡与设定排在章节之前)
- 3. RELEVANT - 兄弟章节、中重要度设定、检索片段
- 4. INSPIRATION - 其他参考内容
-
- 非 CRITICAL 档截断后不足 500 字且不到原文一半的残片直接丢弃(仍在文件清单里);
- 检索只排除最终入选的条目;角色/设定名作为子串出现在查询里时加分。
-
- **预算与渲染**:
- - 组装上下文预算 `AGENT_CONTEXT_TOKEN_BUDGET`(默认 32000),历史窗口
- `AGENT_CHAT_HISTORY_TOKEN_BUDGET`(默认 32000),两者与系统提示、工作集共用
- `budget.DEFAULT_PROMPT_TOKEN_LEDGER_CEILING`(160000)台账,台账只收缩历史。
- - 每个条目标题行渲染为 `标题 (id=…) [全文]` 或
- `[已截断:显示 x/y 字;全文用 query_files(id="…")]`(检索片段标 `[检索片段,非全文…]`),
- 「相关内容详情」段头声明「标注[全文]的条目就是该文件当前完整内容(截至本请求开始;本请求内被修改过的文件除外)」。
- - `MessageManager` 只对原始上下文做 strip + 兜底封顶(保留换行);已在原始上下文
- 完整出现的条目,world_model truth/surface 里只列「标题 (id=…)(全文见项目上下文)」。
-
- ### 跨轮工作集 (core/session_loader.py)
-
- runner 回放历史时丢弃工具结果,所以新一轮靠两样东西知道上一轮做过什么:
- - 面包屑:assistant 历史消息末尾的纯文本「[此前的工具操作]」,列创建/编辑/删除,
- 以及按 id 精确读取(query_files(id=…),含 parallel_execute 子任务)的文件标题 + id。
- - 工作集:最近 2 条 assistant 回复里读/写过的文件,按**当前库内容**整份注入(最多
- `AGENT_WORKING_SET_MAX_FILES`=8 份、`AGENT_WORKING_SET_MAX_CHARS`=60000 字,超出只列
- 标题 + id;已在组装上下文中以 [全文] 出现的只列一行)。`service.py` 把它拼在本轮用户
- 消息之前(`<previous_turn_working_set>` 块 + 「【本轮用户消息】」),不进系统提示也不改
- 历史消息,前缀缓存可以一直命中到上一轮。
-
- ## 工具系统
-
- ### 可用工具 (tool_schemas.py)
-
- | 工具名 | 描述 |
- |--------|------|
- | `create_file` | 创建新文件 (大纲/角色/设定/草稿) |
- | `edit_file` | 精确编辑 (replace/insert/append/delete) |
- | `delete_file` | 删除文件 |
- | `query_files` | 查询和搜索文件 |
- | `update_project` | 更新项目状态 |
- | `handoff_to_agent` | 交接给另一个 agent |
- | `request_clarification` | 请求用户澄清并暂停工作流 |
- | `parallel_execute` | 并行执行多个只读/工具任务 |
- | `load_skill` | 按名称或 id 加载已启用技能的方法与资源清单(技能 L2,记录用量;受每请求技能内容 token 预算限制,超出截断;每请求最多读 3 个不同技能,与 read_skill_resource 共用计数) |
- | `read_skill_resource` | 读取技能附带的一个参考文件,或 `path="SKILL.md"` 读正文;支持 `offset` 分段续读(技能 L3) |
-
- ### 文件流式写入协议
-
- 创建文件时使用 `<file>...</file>` 标记流式输出内容:
-
- ```
- 1. Agent 调用 create_file (不传 content)
- 2. StreamAdapter 检测到 create_file 结果,进入等待模式
- 3. Agent 输出 <file> 标记
- 4. StreamAdapter 开始收集文件内容
- 5. Agent 输出 </file> 标记
- 6. StreamAdapter 将内容写入数据库
- ```
-
- ## SSE 事件类型 (core/events.py)
-
- | 事件类型 | 描述 |
- |----------|------|
- | `thinking` | 模型思考过程 |
- | `content` | 文本内容 |
- | `tool_call` | 工具调用开始/进行中 |
- | `tool_result` | 工具执行结果 |
- | `file_created` | 文件创建完成 |
- | `file_updated` | 文件更新完成 |
- | `file_content` | 文件内容流 |
- | `agent_selected` | Agent 被选中 |
- | `handoff` | Agent 交接 |
- | `error` | 错误信息 |
- | `done` | 流结束 |
-
- ## 提示词系统 (prompts/)
-
- ### 项目类型提示
- - `novel.py` - 长篇小说
- - `short_story.py` - 短篇故事
- - `screenplay.py` - 剧本
-
- ### Agent 专业提示 (subagents.py)
- - `ROUTER_PROMPT` - 意图分类规则
- - `PLANNER_PROMPT` - 大纲规划指南
- - `WRITER_PROMPT` - 内容创作规范
- - `QUALITY_REVIEWER_PROMPT` - 质量检查标准
-
- ## 开发指南
-
- ### 添加新工具
-
- 1. 在 `tools/tool_schemas.py` 添加工具定义
- 2. 在 `tools/mcp_tools.py` 实现工具函数
- 3. 在 `tools/registry.py` 注册工具映射
-
- ### 添加新 Agent 类型
-
- 1. 在 `prompts/subagents.py` 添加专业提示
- 2. 在 `graph/router.py` 更新路由逻辑
- 3. 在 `graph/nodes.py` 的 `specialized_prompts` 注册
-
- ### 调试技巧
-
- - 查看日志: `utils/logger.py` 的 `log_with_context`
- - 检查工具执行: `tools/mcp_tools.py` 的返回值
- - 跟踪事件流: `stream_adapter.py` 的事件转换
+ @AGENTS.md