# Agent 架构对比:Cyber Agent vs Claude Code > 对比 **Cyber Agent**(本项目)与 **Claude Code v2.1.88**(非官方重建,仅供研究)的架构设计异同,目标是找到可借鉴的设计思路,以及两者各自的取舍。 --- ## 总览 | 维度 | Cyber Agent | Claude Code | |------|-------------|-------------| | **语言/运行时** | Python / asyncio | TypeScript / Bun + Node.js | | **定位** | 嵌入式 Agent 框架,供程序集成调用 | 面向开发者的交互式 CLI 工具 | | **LLM 接入** | 多 Provider(OpenRouter / Gemini / Anthropic 等) | 仅 Claude API | | **持久化** | 文件系统(JSONL + JSON),消息树结构 | 内存中维护会话,JSONL 转录为副产品 | | **多 Agent** | 递归子 Trace,独立持久化,可跨设备 | Coordinator-Worker,内存中,不持久化 | | **计划管理** | 原生 GoalTree(结构化、持久化、周期注入) | 无原生计划结构(TodoWriteTool 扁平备忘录) | | **记忆层次** | 三层(Trace / Knowledge / Skill),Agent 主动写入 | 二层(CLAUDE.md / 历史压缩),人工维护 | | **权限系统** | 预设白/黑名单(工具级) | 规则引擎(参数级 glob 匹配 + 交互审批) | | **可观测性** | REST + WebSocket,结构化事件流 | 终端 UI + OpenTelemetry,无外部 API | | **UI 层** | 无终端 UI | 自研 Ink 终端框架(251KB) | --- ## 核心结构类比 ``` Cyber Agent Claude Code ─────────────────── ─────────────────────── Trace ↔ QueryEngine (执行容器) AgentRunner.loop ↔ queryLoop (while true 循环) FileSystemTraceStore ↔ recordTranscript (持久化) GoalTree ↔ TodoWriteTool (计划管理,差距显著) KnowHub + Reflect ↔ CLAUDE.md (跨任务记忆,差距显著) ``` --- ## 一、执行容器:Trace vs QueryEngine 两者都是"单次执行会话"的容器,跨 turn 维护消息历史和统计数据。 | 维度 | Trace | QueryEngine | |------|-------|-------------| | **生命周期** | 持久化到文件系统 | 内存中,session 结束消失 | | **消息结构** | 消息树(`parent_sequence`,支持 Rewind) | 线性数组 `Message[]` | | **显式状态** | `running / completed / failed / stopped` | 无 | | **计划结构** | GoalTree(持久化,周期注入) | 无 | | **唯一标识** | `trace_id`(UUID,可跨设备引用) | 无专用 ID | | **子执行单元** | 所有子 Agent 都是 Trace,结构统一 | AgentTool 各自独立,无统一模型 | | **记忆去重** | `head_sequence` 管理主路径,消息树自动过滤 | `loadedNestedMemoryPaths` 防止 CLAUDE.md 重复注入 | `loadedNestedMemoryPaths` 和 `head_sequence` 解决了相似的问题(防止重复处理),但层次完全不同:前者是内容级去重,后者是结构级的消息主路径管理。 **核心差异**:Cyber Agent 把 Agent 执行视为持久化的、可被外部观测的**任务**;Claude Code 更偏向交互式**会话**,持久化是副产品而非一等公民。 --- ## 二、Agent 循环:AgentRunner.loop vs queryLoop 两者都是 `while true` → 调 LLM → 执行工具 → 继续 的循环,但在五个维度上有实质差异。 ### 2.1 循环状态的管理方式 queryLoop 用一个**整体替换**的 `State` 对象管理跨迭代状态,所有 `continue` 点都是 `state = { ...newState }`: ```typescript // queryLoop 的每个 continue 点——必须声明完整的下一轮状态 state = { messages: [...messagesForQuery, ...assistantMessages, recoveryMessage], maxOutputTokensRecoveryCount: maxOutputTokensRecoveryCount + 1, hasAttemptedReactiveCompact, // 显式保留 maxOutputTokensOverride: undefined, // 显式重置 pendingToolUseSummary: undefined, // 显式重置 stopHookActive, turnCount, transition: { reason: 'max_output_tokens_recovery', attempt: 2 }, } continue ``` 这带来三个好处: 1. **消灭"隐式携带"bug**:每个 continue 点必须声明每个字段,想沿用旧值也要显式写出来,不可能因为"忘了重置"而静默出错。 2. **每个 continue 路径自描述**:不需要追踪前面的赋值历史,任意找一个 continue 块就能知道下一轮的完整状态。 3. **`transition` 字段:零成本可观测性**:记录每次 continue 的原因(源码注释明说是专为测试设计的): ```typescript transition: { reason: 'next_turn' } transition: { reason: 'max_output_tokens_recovery', attempt: 2 } transition: { reason: 'reactive_compact_retry' } transition: { reason: 'stop_hook_blocking' } transition: { reason: 'collapse_drain_retry' } ``` 测试可以直接断言 `state.transition.reason === 'reactive_compact_retry'`,而不必解析消息内容推断"压缩是否发生了"。**这个字段也是天然的 debug 信息**:任何时候 dump 出 state 就能知道循环目前处于哪个恢复阶段。 注意:`state.messages` **不是** API payload。实际发给 API 的是 `prependUserContext(messagesForQuery, userContext)`,其中 `messagesForQuery` 是 `state.messages` 经过多层变换(compact 边界过滤 → 工具结果截断 → microcompact → autocompact)后的结果。State 本身不落盘,只有 queryLoop yield 出来的 Message 才被 QueryEngine 记录到 transcript。 AgentRunner 当前直接在迭代内修改变量,没有这个整体替换的抽象。 ### 2.2 工具执行时机(最大差异) queryLoop 有 `StreamingToolExecutor`(feature gate),工具在模型**还在流式输出期间**就并发开始执行: ``` 模型流式输出: [tool1 完整] ── [tool2 完整] ── [tool3 完整] ── 流结束 StreamingExecutor: └→ 立即开始执行 └→ 立即开始执行 └→ 立即开始执行 ↓ 执行完立即 yield 结果,不等流结束 ``` 工具执行时间被完全"藏"在模型输出时间里。AgentRunner 等 LLM response 完整后才执行工具。 ### 2.3 循环内多层恢复路径 queryLoop 内置了多条恢复路径,全部通过 `state = next; continue` 在**循环内**动态触发: ``` prompt_too_long → 1. context_collapse drain(轻量,清空 staged collapses) → 2. reactive compact(重新压缩,LLM 调用) → 失败 → 暴露错误,return max_output_tokens → 1. 升级到 64k(maxOutputTokensOverride,一次机会) → 2. 多轮文本恢复(最多 3 次,注入 meta 消息) → 3. 耗尽 → 暴露错误,return 模型降级(FallbackTriggeredError)→ 切换 fallbackModel,丢弃已有输出,重试 stop hook blocking → 注入 hook 错误消息,continue ``` AgentRunner 的恢复(`_heal_orphaned_tool_calls`)发生在进入循环**之前**(`_build_history` 阶段),不是循环内的动态恢复。 ### 2.4 周期性上下文注入 AgentRunner **主动、周期性**注入 GoalTree + Collaborators: ```python if iteration % 10 == 0: inject_context(goal_tree, collaborators) ``` queryLoop 的记忆是**被动消费**的:`startRelevantMemoryPrefetch` 在每轮开头触发异步预取,settled 且本轮尚未消费时才注入为 attachment。没有强制周期,也没有计划结构注入。 ### 2.5 Hooks:外部介入点 这是两个系统差距最大的维度之一。 **Cyber Agent `context_hooks`**:每 10 轮调用一次,返回 markdown 字符串注入为上下文。功能是**只读注入**,无法阻断或修改任何事物。 **CC hooks**:覆盖 25+ 事件,三种执行模式(shell / HTTP / TypeScript 回调),每个事件有专用的双向契约: | 维度 | Cyber Agent `context_hooks` | CC hooks | |------|---------------------------|---------| | **事件数量** | 1(周期注入) | 25+(全生命周期) | | **工具生命周期** | 无 | PreToolUse / PostToolUse / PostToolUseFailure / PermissionDenied | | **loop 控制** | 无 | Stop hook block → `stopHookActive=true` → 强制 continue | | **修改输入/输出** | 无 | PreToolUse 改 toolInput;PostToolUse 改 MCP 输出 | | **阻断执行** | 无 | PreToolUse deny;PostToolUse preventContinuation | | **执行模式** | Python 函数 | shell / HTTP / TypeScript callback / postSampling | | **会话级注册** | 随 AgentRunner 传入 | `addFunctionHook()`,Map-based,O(1),高并发友好 | 三个最重要的 hook 事件在 loop 上下文里: - **PreToolUse**:在权限检查之前调用,可 `allow`/`deny`/`ask` + 修改 toolInput。允许外部系统做"无人值守的审批代理",比交互弹窗更适合自主运行场景。 - **Stop hook**:每轮结束时调用,若返回 block,则 `state.transition = 'stop_hook_blocking'`,loop 强制再 continue 一轮。这是"外部系统驱动 Agent 继续工作"的接入点。 - **UserPromptSubmit**:用户消息进队列时调用,可注入 `additionalContext` 或改写消息本体。比 `context_hooks` 的定时注入更精确(每次用户输入时触发,而非每 10 轮)。 ### 2.6 工具摘要的异步隐藏 queryLoop 在工具执行完毕后 fire-and-forget 一个 Haiku 摘要任务,**不等它**,Promise 存入 `nextPendingToolUseSummary`,下一轮迭代开头才 await: ```typescript // 工具执行完毕后立即触发(不阻塞当前轮) nextPendingToolUseSummary = generateToolUseSummary({...}) // 下一轮迭代开头取结果——此时 Haiku ~1s 早已完成 const summary = await pendingToolUseSummary ``` Haiku 摘要的延迟被下一轮 5-30s 的 API 调用时间完全吸收。AgentRunner 没有对应机制。 --- ## 三、持久化与消息结构 ### Cyber Agent:消息树(非破坏性) 每条 Message 有 `sequence`(全局递增)和 `parent_sequence`,构成树结构: ``` 正常执行: 1 → 2 → 3 → 4 → 5 Rewind 到3: 3 → 6 → 7 (4,5 自动脱离主路径) 压缩 1-3: 8(summary,parent=None) → 6 → 7 侧分支: 5 → 6(reflection) → 7 (不在主路径) 5 → 8(summary, 主路径) ``` `trace.head_sequence` 始终指向主路径头,`build_llm_messages` 沿链回溯,侧分支自动被过滤。所有历史永久保留,Rewind 是非破坏性操作。 ### Claude Code:线性历史(有损压缩) 消息以数组形式存在内存中,转录文件是 append-only JSONL。没有树结构,压缩是破坏性的(原始消息不再恢复)。QueryEngine 维护完整的 `mutableMessages`,queryLoop 内的 `state.messages` 在压缩后被替换为压缩后版本。 **关键差异**:Cyber Agent 是非破坏性时间旅行;Claude Code 是有损折叠。代价是 Cyber Agent 存储开销更高,查询需要沿链回溯。 --- ## 四、计划管理 ### Cyber Agent:原生 GoalTree GoalTree 是一等公民,有独立数据模型(`Goal`)、专用工具(`goal`)、持久化(`goal.json`)和周期注入机制: ``` 1. [in_progress] 分析代码架构 1.1. [completed] 读取项目结构 1.2. [in_progress] 分析核心模块 1.2.1. [pending] 分析 runner.py 1.2.2. [pending] 分析 tool_registry.py ``` - Goal 与 Message 关联(`msg.goal_id`),压缩以 Goal 为粒度 - 每 10 轮自动注入,模型始终知道自己在做什么 - 支持 `focus` / `done`(附 summary)/ `abandon`(附原因) ### Claude Code:TodoWriteTool(扁平备忘录) 扁平的待办列表,markdown 文本,格式由模型自行决定。无层级结构,无与消息的关联,不跨 session 持久化,不参与压缩粒度决策。 **结论**:这是两个系统差距最大的维度之一。GoalTree 是架构级方案,TodoWriteTool 是临时补丁。 --- ## 五、记忆系统 ### Cyber Agent:三层记忆,Agent 主动写入 ``` Layer 3: Skills(技能库) Markdown 文件,领域知识和操作规范,启动时注入 system prompt ▲ 归纳 Layer 2: Knowledge(知识库) 数据库 + 向量索引(KnowHub),语义检索按需注入 ▲ 提取(Reflect 机制) Layer 1: Trace(任务状态) 当前任务的工作记忆:Messages + GoalTree ``` - **Reflect**:执行完成后触发侧分支 Agent,分析历史,调用 `knowledge_save` 结构化入库 - **跨任务学习**:知识从一个 Trace 提取后,下一个 Trace 可语义检索复用 ### Claude Code:二层记忆,人工维护 ``` Layer 2: CLAUDE.md(持久指令) 项目/用户级 Markdown,自动发现(目录树遍历),注入 system prompt Layer 1: Session 历史(工作记忆) 当前对话历史(内存),超长时自动压缩折叠 ``` 跨 session 保留的内容只有写入 CLAUDE.md 的部分,Agent 本身不会主动提炼知识。 ### 记忆组织轴的根本差异 | | Claude Code | Cyber Agent | |--|-------------|-------------| | **组织轴** | 人为定义的实体(用户、项目) | 执行本身涌现的语义主题 | | **边界划定者** | 人(目录结构、项目边界) | Agent(`type` + `tags` + `scopes`) | | **谁来写记忆** | 人(或由 Claude 辅助人写 CLAUDE.md) | Agent 自身(Reflect 触发后主动入库) | Claude Code 的记忆是**静态附加的上下文**,边界由人预先划定。Cyber Agent 的记忆可以从**执行本身涌现**,围绕"自主执行的任务"或"跨任务的全局线索"(如某类 bug 的修复模式)自然积累。 --- ## 六、上下文压缩 ### Cyber Agent:两级压缩 + 侧分支(非破坏性) **Level 1(Goal 完成压缩,零 LLM 成本)**:以 Goal 为粒度,completed goal 的消息折叠为 `long_term_memory` 摘要,确定性,无额外 API 调用。 **Level 2(LLM 侧分支压缩)**:`POST /compact` 触发,在 Trace 末尾创建 `branch_type="compression"` 侧分支,压缩结果作为 summary 消息插入主路径。原始消息保留在侧分支,可审计。 控制参数:`RunConfig(goal_compression="none"|"on_complete"|"on_overflow")` ### Claude Code:多层被动触发(有损) 每轮迭代前依次经过:`HISTORY_SNIP` → `microcompact` → `CONTEXT_COLLAPSE` → `autocompact`(token 接近阈值时触发)。触发后用摘要模型折叠历史,原始消息丢失。`prompt_too_long` 错误时进入 reactive compact(最后手段)。 | 方面 | Cyber Agent | Claude Code | |------|-------------|-------------| | 压缩粒度 | Goal 级(语义边界) | 全量历史折叠 | | 压缩成本 | Level 1 零成本;Level 2 LLM 成本 | 始终有 LLM 成本 | | 压缩可逆性 | ✅ 侧分支,原始消息保留 | ❌ 有损折叠,原始丢失 | | 触发时机 | 主动(on_complete / on_overflow) | 被动(超限后)+ 手动 | --- ## 七、多 Agent 协作 ### Cyber Agent:递归 Trace + 跨设备协议 ``` 主 Agent(Trace A) ├── agent(task="研究X") → 子 Trace B(独立持久化,可单独续跑) │ └── agent(task="分析子模块") → 子 Trace C(递归) └── agent(agent_url="https://remote/", task="...") → 远程 Trace(agent://remote/xxx,跨设备) ``` - **explore 模式**:`task: List[str]` 触发 `asyncio.gather()` 并行多 Agent - **evaluate 工具**:专用评估 Agent,从 GoalTree 自动注入评估标准 - **续跑子 Agent**:`agent(continue_from=sub_trace_id)` 可续跑被中断的子 Agent ### Claude Code:Coordinator-Worker(内存中) ``` Coordinator Agent ├── AgentTool(subagent_type="worker") → Worker 1(内存,非持久化) ├── AgentTool(subagent_type="worker") → Worker 2 └── SendMessageTool(to=agent_id) → 向活跃 Worker 发消息 ``` Worker 以 `` XML 上报状态,通过 `SendMessageTool` 持续对话。 | 方面 | Cyber Agent | Claude Code | |------|-------------|-------------| | 子 Agent 持久化 | ✅ 独立 Trace | ❌ 内存中 | | 续跑子 Agent | ✅ `continue_from` | ✅ `SendMessageTool` | | 跨设备协作 | ✅ `agent_url` + 远程 Trace ID | ❌ 不支持 | | 并行执行 | ✅ explore 模式 | ✅ 多 Worker 并行 | | 评估反馈循环 | ✅ evaluate 工具 | ❌ 无原生评估工具 | --- ## 八、工具系统 ### 工具定义 **Cyber Agent(装饰器,轻量):** ```python @tool(description="执行 shell 命令") async def bash(command: str, ctx: ToolContext) -> ToolResult: return ToolResult( output=result.stdout, long_term_memory=f"Ran: {command}", # 压缩后保留的摘要 ) ``` **Claude Code(接口,功能丰富):** 每个工具实现约 15 个方法:`call`、`description`、`checkPermissions`、`isReadOnly`、`isConcurrencySafe`、`prompt`(注入 system prompt 的说明)、`renderToolResultMessage`(UI 渲染)等。接口更重,但权限检查、并发安全标记、进度报告、UI 渲染都内置其中。 ### ToolResult 双层记忆(Cyber Agent 特有) ```python ToolResult( output="<10K tokens 完整内容>", # 第一次给 LLM 完整内容 long_term_memory="提取了 10000 字符", # 之后只看摘要 include_output_only_once=True ) ``` 大输出只在首次注入完整内容,后续轮次自动替换为摘要,有效节约 context window。Claude Code 没有对应机制。 ### Bash 安全(Claude Code 更精细) Cyber Agent 通过 `AgentPreset.denied_tools` 黑名单控制,粒度是整个工具。 Claude Code 有独立的 102KB 安全分析器(`bashSecurity.ts`): - 命令语义分析(参数级,不只是工具名匹配) - 路径白名单(工作目录 + 额外允许目录) - 破坏性命令检测(rm -rf, dd, mkfs 等) - 模式匹配规则:`"Bash(git *)"` 自动允许所有 git 命令 --- ## 九、权限系统 ### Cyber Agent:静态预设(工具级) ```python AgentPreset( allowed_tools=["read", "glob", "grep"], denied_tools=["write", "edit", "bash"], ) ``` 工具级粒度,静态配置,无动态审批。适合离线/批量 Agent 场景。 ### Claude Code:规则引擎(参数级)+ 交互审批 - 规则格式:`"Bash(git *)"` 支持 glob 模式匹配到参数级 - 三种规则:alwaysAllow / alwaysDeny / alwaysAsk - 权限模式:default(交互)/ auto(ML 分类器)/ bypass - **规则自动学习**:用户选择"始终允许"后自动写入配置,构建个性化规则库 - 企业远程策略:通过 managed settings 下发权限规则 适合面向开发者的交互式场景。 --- ## 十、可观测性 ### Cyber Agent:结构化事件流(服务视角) - **WebSocket** `/api/traces/{id}/watch`:实时推送所有 Message 事件 - **REST API**:查询 Trace、Messages(main_path / all)、GoalTree - **GoalTree 统计**:每个 Goal 有 `self_stats` 和 `cumulative_stats`(token、cost、duration) - **全历史可查**:包括 rewind 旧路径、侧分支 ### Claude Code:终端 UI + 遥测(工具视角) - **Ink 终端 UI**:实时 spinner、CoordinatorTaskPanel - **OpenTelemetry**:结构化日志(懒加载) - **无外部 API**:不暴露 HTTP 接口 **关键差异**:Cyber Agent 从架构上是**服务**,天然可集成监控;Claude Code 是**工具**,可观测性服务于操作者自己。 --- ## 总结:差异与互补 ### Cyber Agent 的架构优势 | 优势 | 价值 | |------|------| | **消息树 + Rewind** | 非破坏性时间旅行,支持回溯,全历史可审计 | | **GoalTree** | 结构化计划,与消息关联,驱动细粒度压缩 | | **Knowledge + Reflect** | 跨任务知识积累,Agent 主动从经验中学习 | | **记忆组织轴** | 围绕执行语义积累,不依赖人为划定边界 | | **evaluate 工具** | 内置评估-反馈循环,Agent 可自我校正 | | **ToolResult 双层记忆** | 精细的 context 节约机制 | | **跨设备 Agent 协作** | 分布式 Agent 网络,协议级支持 | | **Context Injection Hooks** | 灵活的外部事件注入(A2A IM、监控告警等) | | **服务化 API** | REST + WebSocket,天然可集成、可观测 | ### 值得从 Claude Code 借鉴的设计 | 设计 | 价值 | |------|------| | **Loop State 整体替换 + transition 字段** | 消灭"隐式携带"bug;transition 记录每次 continue 的原因,可用于测试断言和 debug,扩展后也可通过 WebSocket 推出去实现循环级可观测性 | | **流式工具并发执行** | 工具在模型流式输出期间就开始执行,延迟藏在模型输出时间里 | | **循环内多层恢复路径** | prompt_too_long / max_tokens / 模型降级等错误在循环内动态恢复,无需重启 | | **工具摘要异步隐藏** | Haiku 摘要在工具执行后 fire-and-forget,延迟藏在下一轮 API 调用时间里 | | **Bash 语义级安全分析** | 参数级权限控制,远比工具级黑名单精准,生产环境自主运行时重要 | | **ToolSearch 延迟加载** | 工具数量大时按需加载描述,节约 system prompt token | | **权限规则自动学习** | "始终允许"自动写入配置,构建个性化规则库 | | **工具生命周期 hooks(PreToolUse / PostToolUse)** | 在工具执行前后插入外部逻辑:审批代理(PreToolUse allow/deny)、MCP 输出改写(PostToolUse updatedMCPToolOutput)、失败后通知(PostToolUseFailure)。Cyber Agent 目前对工具执行无介入点 | | **Stop hook(loop 控制)** | 每轮结束时调用,可返回 block 强制 loop 再 continue 一轮(`transition='stop_hook_blocking'`)。这是"外部事件驱动 Agent 继续"的最直接接入点 | | **UserPromptSubmit hook** | 用户消息进队列时触发,可注入 `additionalContext` 或改写消息。比 `context_hooks` 定时注入更精确,也更适合做"收到外部信号时注入上下文"的触发器 | | **工具结果自动存档(toolResultStorage)** | 每个工具声明 `maxResultSizeChars`,超限时自动写入磁盘临时文件,API 收到 preview(前 2000 字节)+ 文件路径,模型可按需用 FileReadTool 读回完整内容。与 Cyber Agent 的 `long_term_memory`(手动摘要、原始内容丢弃)互补:这是零开发者工作量的兜底层,防止意外大输出撑爆 context,且原始内容可恢复 | --- ## 待办:值得引入的改进 | 优先级 | 改进项 | 说明 | |--------|--------|------| | ★★★ | **Loop State 整体替换 + transition 字段** | 将 AgentRunner loop 的跨迭代变量收拢为一个整体替换的 State 对象;每个 continue 点声明完整下一轮状态,消灭"忘记重置某字段"的隐患。新增 `transition` 字段记录每次 continue 的原因(`tool_use` / `compressed` / `healed_orphan` 等),可直接用于测试断言,也可通过 WebSocket 推出去作为循环级可观测事件 | | ★★★ | **工具结果自动存档** | 每个工具声明 `max_result_size`(字符数),超限时自动写入临时文件,给模型 preview(前 N 字节)+ 文件路径,模型可用 `read` 工具读回完整内容。与现有 `long_term_memory`(开发者手写精炼摘要)互补:这是零开发者工作量的兜底层,防止 `bash` 大输出、`webfetch` 整页 HTML 等意外撑爆 context | | ★★☆ | **工具并发执行** | 单次 LLM 响应包含多个 tool_use 时,用 `asyncio.gather()` 并行执行,而非顺序执行。对"同时读取多个文件"等场景延迟改善明显 | | ★★☆ | **工具摘要异步隐藏** | 工具执行完毕后 fire-and-forget 启动摘要生成(用小模型),不阻塞当前轮,下一轮迭代开头取结果。摘要延迟藏在下一次 LLM 调用时间里,对用户零感知 | | ★★☆ | **工具生命周期 hooks** | 在 `ToolRegistry` / `AgentRunner` 层面引入 `pre_tool_use` / `post_tool_use` / `post_tool_use_failure` 三个事件。最高价值用例:① `pre_tool_use` 实现无人值守审批代理(尤其对 `bash` 危险命令);② `post_tool_use_failure` 触发外部通知或重试策略;③ `post_tool_use` 对特定 MCP 工具的输出做后处理 | | ★★☆ | **Stop hook + Loop 控制接入点** | 在每轮 loop 结束时(得到 `stop_reason='end_turn'`)触发一个 hook,如果返回 block 则注入消息并 continue。这让外部系统(监控、测试框架)可以在运行时"插入"下一步指令,而无需重新发起整个对话 | | ★☆☆ | **循环内多层恢复路径** | 目前 `_heal_orphaned_tool_calls` 在进入循环前执行。可考虑将更多恢复逻辑移入循环内(context 超限时自动触发压缩并 continue,而非报错退出) | --- *对比基于 Cyber Agent 文档(`architecture.md`)与 Claude Code v2.1.88 npm 包 source map 分析(非官方重建,仅供研究),生成日期:2026-04-02。*