Agent Plan x DeepSeek Harness:Hook 系统与生命周期
版本:v1.0
适用范围:面向基于 DeepSeek 大模型的 Agent 计划编排框架,涵盖 Hook 系统设计、生命周期管理、执行引擎与可观测性。
阅读对象:架构师、Agent 工程师、平台 SRE、插件开发者。
目录
- 引言与背景
- 核心概念与术语
- 整体架构总览
- [Agent 生命周期模型](#Agent 生命周期模型)
- [Hook 系统设计哲学](#Hook 系统设计哲学)
- [Hook 类型与触发时机](#Hook 类型与触发时机)
- [Hook 注册与执行流程](#Hook 注册与执行流程)
- [DeepSeek Harness 集成详解](#DeepSeek Harness 集成详解)
- [Plan 执行引擎](#Plan 执行引擎)
- 上下文与数据流管理
- 错误处理、重试与回滚
- 可观测性与监控
- 扩展机制与插件生态
- 安全与权限模型
- 最佳实践与反模式
- 性能调优指南
- 演进路线与未来工作
- 总结
1. 引言与背景
1.1 问题域
随着大语言模型(LLM)从"单轮对话"演进到"多步自主任务执行",Agent 范式逐渐成为应用落地的主流。一个真正可用的 Agent 不仅需要 LLM 的推理能力,还需要一套外部脚手架来承担:
- 任务分解与计划编排(Plan)
- 工具调用与外部世界交互(Tool Use)
- 状态管理与会话持久化(State)
- 生命周期管理与可干预点(Lifecycle & Hooks)
- 可观测、可调试、可回放(Observability)
DeepSeek 作为开源的高性能推理模型,具备优秀的代码生成、长上下文理解与函数调用能力,是构建 Agent 的理想底座。然而,单纯把请求抛给 DeepSeek API 远远不够------我们需要一个 Harness(执行挽具) 来包裹模型,使其具备可控的执行流、可插拔的扩展点和完整的生命周期。
这就是 Agent Plan x DeepSeek Harness 的诞生背景。其中,"Plan"代表计划编排层,"DeepSeek Harness"代表模型执行挽具,而 Hook 系统则是连接两者的神经系统,贯穿整个生命周期。
1.2 为什么需要 Hook
一个健壮的 Agent 系统必须解决以下工程难题:
| 难题 | 没有 Hook 时 | 有 Hook 后 |
|---|---|---|
| 审计合规 | 请求/响应散落日志,难以聚合 | pre_model_call / post_model_call 统一拦截 |
| 安全过滤 | 提示词注入难防 | pre_tool_call 校验工具参数 |
| 限流计费 | 嵌入业务代码,耦合严重 | pre_model_call 注入配额检查 |
| 上下文压缩 | 满了才截断,丢信息 | on_context_overflow 策略化裁剪 |
| 工具准入 | 写死白名单 | on_tool_register 动态校验 |
| 流式渲染 | 业务层各自处理 | on_token_stream 统一消费 |
| 失败兜底 | 异常上抛中断 | on_error 决定重试/降级/终止 |
| 回放调试 | 难以复现 | Hook 记录完整事件序列 |
Hook 的本质是 "在生命周期的关键节点,以可插拔方式注入横切逻辑"。它让 Agent 的核心执行流保持纯净,把审计、安全、限流、压缩、观测等横切关注点剥离到独立模块。
1.3 设计目标
本框架遵循以下设计目标:
- 声明式优先:Hook 通过装饰器或配置声明,而非侵入核心流程。
- 可组合:多个 Hook 可并行/串行挂载,互不干扰,顺序可定义。
- 可中断:Hook 可阻断后续流程,返回结构化错误。
- 可观测:Hook 本身的执行也被纳入追踪链路。
- 幂等可重放:Hook 输入输出可序列化,支持回放。
- 版本兼容:Hook 协议具备语义版本,保证插件生态演进不破坏既有集成。
2. 核心概念与术语
为避免后续章节歧义,先统一术语:
- Agent(智能体):具备目标、计划、工具与记忆的执行单元。
- Plan(计划) :Agent 为达成目标而生成的有序任务序列,每个任务称 Step。
- Step(步骤):计划中的最小调度单元,对应一次模型调用或一次工具调用。
- Harness(挽具):包裹 DeepSeek 模型的执行外壳,负责请求构建、流式解析、工具路由、状态同步。
- Hook(钩子):在生命周期特定点被调用的回调函数,可读取/改写上下文,可中断流程。
- Lifecycle(生命周期):Agent 从启动到终止所经历的状态序列与触发的事件集合。
- Context(上下文):在一次 Agent 运行中,所有可被 Hook 与 Step 访问的可变状态容器。
- Tool(工具):Agent 可调用的外部能力,通过函数签名注册。
- Hook Chain(钩子链):挂载在同一事件点的多个 Hook 组成的有序执行链。
- Interceptor(拦截器):一类特殊 Hook,可改写输入输出,优先级高于普通 Hook。
- Guard(守卫):返回布尔/裁决结果的 Hook,用于准入判断。
3. 整体架构总览
框架采用分层架构,自顶向下依次为:接入层 → 计划编排层 → 执行挽具层 → Hook 总线 → 模型适配层 → 工具/存储层。
#mermaid-svg-PjrMNojZK4aM7pdm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PjrMNojZK4aM7pdm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PjrMNojZK4aM7pdm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PjrMNojZK4aM7pdm .error-icon{fill:#552222;}#mermaid-svg-PjrMNojZK4aM7pdm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PjrMNojZK4aM7pdm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PjrMNojZK4aM7pdm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PjrMNojZK4aM7pdm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PjrMNojZK4aM7pdm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PjrMNojZK4aM7pdm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PjrMNojZK4aM7pdm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PjrMNojZK4aM7pdm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PjrMNojZK4aM7pdm .marker.cross{stroke:#333333;}#mermaid-svg-PjrMNojZK4aM7pdm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PjrMNojZK4aM7pdm p{margin:0;}#mermaid-svg-PjrMNojZK4aM7pdm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PjrMNojZK4aM7pdm .cluster-label text{fill:#333;}#mermaid-svg-PjrMNojZK4aM7pdm .cluster-label span{color:#333;}#mermaid-svg-PjrMNojZK4aM7pdm .cluster-label span p{background-color:transparent;}#mermaid-svg-PjrMNojZK4aM7pdm .label text,#mermaid-svg-PjrMNojZK4aM7pdm span{fill:#333;color:#333;}#mermaid-svg-PjrMNojZK4aM7pdm .node rect,#mermaid-svg-PjrMNojZK4aM7pdm .node circle,#mermaid-svg-PjrMNojZK4aM7pdm .node ellipse,#mermaid-svg-PjrMNojZK4aM7pdm .node polygon,#mermaid-svg-PjrMNojZK4aM7pdm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PjrMNojZK4aM7pdm .rough-node .label text,#mermaid-svg-PjrMNojZK4aM7pdm .node .label text,#mermaid-svg-PjrMNojZK4aM7pdm .image-shape .label,#mermaid-svg-PjrMNojZK4aM7pdm .icon-shape .label{text-anchor:middle;}#mermaid-svg-PjrMNojZK4aM7pdm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PjrMNojZK4aM7pdm .rough-node .label,#mermaid-svg-PjrMNojZK4aM7pdm .node .label,#mermaid-svg-PjrMNojZK4aM7pdm .image-shape .label,#mermaid-svg-PjrMNojZK4aM7pdm .icon-shape .label{text-align:center;}#mermaid-svg-PjrMNojZK4aM7pdm .node.clickable{cursor:pointer;}#mermaid-svg-PjrMNojZK4aM7pdm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PjrMNojZK4aM7pdm .arrowheadPath{fill:#333333;}#mermaid-svg-PjrMNojZK4aM7pdm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PjrMNojZK4aM7pdm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PjrMNojZK4aM7pdm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PjrMNojZK4aM7pdm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PjrMNojZK4aM7pdm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PjrMNojZK4aM7pdm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PjrMNojZK4aM7pdm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PjrMNojZK4aM7pdm .cluster text{fill:#333;}#mermaid-svg-PjrMNojZK4aM7pdm .cluster span{color:#333;}#mermaid-svg-PjrMNojZK4aM7pdm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PjrMNojZK4aM7pdm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PjrMNojZK4aM7pdm rect.text{fill:none;stroke-width:0;}#mermaid-svg-PjrMNojZK4aM7pdm .icon-shape,#mermaid-svg-PjrMNojZK4aM7pdm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PjrMNojZK4aM7pdm .icon-shape p,#mermaid-svg-PjrMNojZK4aM7pdm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PjrMNojZK4aM7pdm .icon-shape .label rect,#mermaid-svg-PjrMNojZK4aM7pdm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PjrMNojZK4aM7pdm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PjrMNojZK4aM7pdm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PjrMNojZK4aM7pdm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 工具/存储层 Tool & Storage
模型适配层 Model Adapter
Hook 总线 Hook Bus
执行挽具层 Harness Layer
计划编排层 Plan Layer
接入层 Entry Layer
触发
触发
触发
触发
CLI 客户端
HTTP/gRPC API
语言 SDK
Plan Engine
Step Scheduler
Dependency Graph
Request Builder
Stream Parser
Tool Router
State Sync
Event Dispatcher
Hook Chain
Priority Resolver
DeepSeek Adapter
其他模型 Adapter
Tool Registry
Memory Store
Trace Store
上图揭示了三条核心流:
- 控制流:接入层 → Plan → Step → Harness → Model Adapter,自上而下驱动执行。
- 数据流:上下文在层间流转,Harness 产出 token 与 tool call,写回 Context。
- 事件流:任何层都可以向 Hook Bus 发出事件,Bus 调度 Hook Chain 执行横切逻辑。
三层解耦带来三大收益:Plan 可独立演进编排策略;Harness 可替换底层模型;Hook 可独立部署为 sidecar 或远端服务。
4. Agent 生命周期模型
4.1 生命周期状态机
Agent 的运行并非一条直线,而是一个状态机。每个状态对应一类 Hook 的触发窗口。
#mermaid-svg-cEXy60L9Jo3E7oUH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cEXy60L9Jo3E7oUH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cEXy60L9Jo3E7oUH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cEXy60L9Jo3E7oUH .error-icon{fill:#552222;}#mermaid-svg-cEXy60L9Jo3E7oUH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cEXy60L9Jo3E7oUH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cEXy60L9Jo3E7oUH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cEXy60L9Jo3E7oUH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cEXy60L9Jo3E7oUH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cEXy60L9Jo3E7oUH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cEXy60L9Jo3E7oUH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cEXy60L9Jo3E7oUH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cEXy60L9Jo3E7oUH .marker.cross{stroke:#333333;}#mermaid-svg-cEXy60L9Jo3E7oUH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cEXy60L9Jo3E7oUH p{margin:0;}#mermaid-svg-cEXy60L9Jo3E7oUH defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-cEXy60L9Jo3E7oUH g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-cEXy60L9Jo3E7oUH g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-cEXy60L9Jo3E7oUH g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-cEXy60L9Jo3E7oUH g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-cEXy60L9Jo3E7oUH g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-cEXy60L9Jo3E7oUH .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-cEXy60L9Jo3E7oUH .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-cEXy60L9Jo3E7oUH .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-cEXy60L9Jo3E7oUH .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-cEXy60L9Jo3E7oUH .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-cEXy60L9Jo3E7oUH .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-cEXy60L9Jo3E7oUH .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-cEXy60L9Jo3E7oUH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cEXy60L9Jo3E7oUH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cEXy60L9Jo3E7oUH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cEXy60L9Jo3E7oUH .edgeLabel .label text{fill:#333;}#mermaid-svg-cEXy60L9Jo3E7oUH .label div .edgeLabel{color:#333;}#mermaid-svg-cEXy60L9Jo3E7oUH .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-cEXy60L9Jo3E7oUH .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-cEXy60L9Jo3E7oUH .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-cEXy60L9Jo3E7oUH .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-cEXy60L9Jo3E7oUH .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-cEXy60L9Jo3E7oUH .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cEXy60L9Jo3E7oUH .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cEXy60L9Jo3E7oUH #statediagram-barbEnd{fill:#333333;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cEXy60L9Jo3E7oUH .cluster-label,#mermaid-svg-cEXy60L9Jo3E7oUH .nodeLabel{color:#131300;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-cEXy60L9Jo3E7oUH .note-edge{stroke-dasharray:5;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram-note text{fill:black;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram-note .nodeLabel{color:black;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagram .edgeLabel{color:red;}#mermaid-svg-cEXy60L9Jo3E7oUH #dependencyStart,#mermaid-svg-cEXy60L9Jo3E7oUH #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-cEXy60L9Jo3E7oUH .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cEXy60L9Jo3E7oUH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} new Agent()
on_agent_init
on_plan_start
on_plan_complete
on_step_start
on_tool_call
on_tool_return
on_model_call_start
on_model_call_end
on_stream_start
on_stream_end
下一步 on_step_end
所有 Step 完成
on_error (不可恢复)
on_pause
on_resume
on_terminate
on_finish
on_replan
重新规划
Created
Initialized
Planning
PlanReady
Executing
ToolCall
ModelCall
Streaming
Completed
Failed
Paused
Replanning
4.2 阶段语义
| 阶段 | 入口事件 | 出口事件 | 职责 |
|---|---|---|---|
| Created | 构造 | on_agent_init |
装配配置,校验依赖 |
| Initialized | on_agent_init |
on_plan_start |
加载记忆、注入系统提示 |
| Planning | on_plan_start |
on_plan_complete |
LLM 生成 Plan,可选人工评审 |
| Executing | on_step_start |
on_step_end |
逐 Step 执行,可能是模型调用或工具调用 |
| Paused | on_pause |
on_resume |
等待外部审批/人工输入 |
| Replanning | on_replan |
on_plan_complete |
计划失效后重新生成 |
| Failed | on_error |
on_terminate |
收集错误,触发回滚或降级 |
| Completed | 所有 Step done | on_finish |
汇总结果,持久化,释放资源 |
4.3 生命周期的可中断性
生命周期的每个状态转移都经过 Hook Bus。这意味着任意 Hook 都可以通过返回 Abort 信号让 Agent 跳转到 Failed 或 Paused。例如:
pre_model_call的安全 Hook 检测到提示词注入 →Abort,跳转Failed。on_tool_call的合规 Hook 检测到高敏感工具 →Pause,等待人工审批。on_step_end的预算 Hook 发现预算超限 →Abort,跳转Completed(部分结果)。
这种"事件驱动 + 状态机"的设计,使得 Agent 既能自主执行,又能在关键节点被外部约束。
5. Hook 系统设计哲学
5.1 三大设计原则
原则一:单向数据流 + 显式上下文
Hook 不通过全局变量传递信息,所有读写都经由一个显式的 HookContext 对象。这保证 Hook 的副作用可追踪、可序列化、可回放。
原则二:顺序敏感 + 优先级分层
同一事件点的多个 Hook 按优先级分组,组内按注册顺序执行。优先级分四档:
| 档位 | 角色 | 典型职责 |
|---|---|---|
P0 |
Security Guard | 鉴权、注入检测、PII 过滤 |
P1 |
Interceptor | 改写 prompt、改写响应 |
P2 |
Business Hook | 业务逻辑、计费、配额 |
P3 |
Observer | 日志、指标、追踪 |
低档位 Hook 在高档位之后执行,且 P0 的 Abort 会短路后续所有 Hook。
原则三:失败不污染主流程(默认)与显式升级(可选)
默认情况下,Hook 抛出异常会被 Bus 捕获并记录,不影响 Agent 主流程;但 Hook 可声明 escalation: true,将异常升级为 Abort。这避免了"一个观测 Hook 崩溃导致 Agent 中断"的灾难。
5.2 Hook 协议
一个 Hook 是一个实现下列协议的对象(伪码):
interface Hook {
event: LifecycleEvent // 监听的事件
priority: P0 | P1 | P2 | P3 // 优先级
escalation: boolean // 异常是否升级为 Abort
id: string // 全局唯一标识
run(ctx: HookContext): HookResult
}
type HookResult =
| { kind: "continue", patch?: Partial<HookContext> }
| { kind: "abort", reason: string, code: string }
| { kind: "pause", resumeToken: string }
| { kind: "transform", payload: any }
HookContext 携带当前快照:Agent 元信息、当前 Step、模型请求/响应、工具调用参数/返回、累计 token、预算余量、trace_id。
5.3 与中间件的关系
Hook 与传统中间件(Middleware)形似而神不同:
| 维度 | 中间件 | Hook |
|---|---|---|
| 形态 | 洋葱模型,包前后两段 | 事件点回调,单向 |
| 位置 | 链路中段 | 生命周期任意点 |
| 上下文 | 沿调用栈传递 | 显式对象注入 |
| 适用 | HTTP/RPC 请求 | Agent 全生命周期 |
| 组合 | 嵌套 | 优先级分桶 |
本框架中,Harness 内部请求链路用中间件(如重试、超时),而跨生命周期的横切逻辑用 Hook。二者互补。
6. Hook 类型与触发时机
6.1 Hook 全景表
下表列出框架内置的全部 Hook,按生命周期先后排序。
| # | 事件名 | 触发时机 | 可改写 | 可中断 |
|---|---|---|---|---|
| 1 | on_agent_init |
Agent 构造完成 | 配置 | 是 |
| 2 | on_plan_start |
Plan 生成前 | 系统提示 | 是 |
| 3 | on_plan_complete |
Plan 生成后,审批前 | Plan 结构 | 是 |
| 4 | on_plan_approved |
Plan 通过审批 | - | 是 |
| 5 | on_step_start |
Step 开始 | Step 输入 | 是 |
| 6 | pre_model_call |
构造模型请求后,发送前 | messages, tools | 是 |
| 7 | on_model_call_start |
请求实际发出 | - | 否 |
| 8 | on_token_stream |
每批 token 到达 | 增量文本 | 否 |
| 9 | on_stream_end |
流结束 | 完整响应 | 否 |
| 10 | on_model_call_end |
模型调用完成 | 响应对象 | 是 |
| 11 | on_tool_call |
模型决定调用工具 | 工具参数 | 是 |
| 12 | pre_tool_exec |
工具执行前 | 工具参数 | 是 |
| 13 | on_tool_exec_progress |
工具执行进度 | - | 否 |
| 14 | on_tool_return |
工具返回 | 返回值 | 是 |
| 15 | on_step_end |
Step 结束 | Step 状态 | 是 |
| 16 | on_context_overflow |
上下文超限 | 裁剪策略 | 是 |
| 17 | on_replan |
触发重规划 | - | 是 |
| 18 | on_pause / on_resume |
暂停/恢复 | - | 否 |
| 19 | on_error |
异常发生 | 错误对象 | 是(可转降级) |
| 20 | on_finish |
Agent 完成 | 最终结果 | 否 |
| 21 | on_terminate |
Agent 终止 | - | 否 |
6.2 触发时序图
Tool DeepSeek Hook Bus Harness Plan Engine 用户/接入层 Tool DeepSeek Hook Bus Harness Plan Engine 用户/接入层 #mermaid-svg-DsuqlvSKgDTg3zvI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DsuqlvSKgDTg3zvI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DsuqlvSKgDTg3zvI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DsuqlvSKgDTg3zvI .error-icon{fill:#552222;}#mermaid-svg-DsuqlvSKgDTg3zvI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DsuqlvSKgDTg3zvI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DsuqlvSKgDTg3zvI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DsuqlvSKgDTg3zvI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DsuqlvSKgDTg3zvI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DsuqlvSKgDTg3zvI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DsuqlvSKgDTg3zvI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DsuqlvSKgDTg3zvI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DsuqlvSKgDTg3zvI .marker.cross{stroke:#333333;}#mermaid-svg-DsuqlvSKgDTg3zvI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DsuqlvSKgDTg3zvI p{margin:0;}#mermaid-svg-DsuqlvSKgDTg3zvI .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DsuqlvSKgDTg3zvI text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-DsuqlvSKgDTg3zvI .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-DsuqlvSKgDTg3zvI .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-DsuqlvSKgDTg3zvI .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-DsuqlvSKgDTg3zvI .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-DsuqlvSKgDTg3zvI #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-DsuqlvSKgDTg3zvI .sequenceNumber{fill:white;}#mermaid-svg-DsuqlvSKgDTg3zvI #sequencenumber{fill:#333;}#mermaid-svg-DsuqlvSKgDTg3zvI #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-DsuqlvSKgDTg3zvI .messageText{fill:#333;stroke:none;}#mermaid-svg-DsuqlvSKgDTg3zvI .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DsuqlvSKgDTg3zvI .labelText,#mermaid-svg-DsuqlvSKgDTg3zvI .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-DsuqlvSKgDTg3zvI .loopText,#mermaid-svg-DsuqlvSKgDTg3zvI .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-DsuqlvSKgDTg3zvI .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-DsuqlvSKgDTg3zvI .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-DsuqlvSKgDTg3zvI .noteText,#mermaid-svg-DsuqlvSKgDTg3zvI .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-DsuqlvSKgDTg3zvI .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DsuqlvSKgDTg3zvI .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DsuqlvSKgDTg3zvI .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DsuqlvSKgDTg3zvI .actorPopupMenu{position:absolute;}#mermaid-svg-DsuqlvSKgDTg3zvI .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-DsuqlvSKgDTg3zvI .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DsuqlvSKgDTg3zvI .actor-man circle,#mermaid-svg-DsuqlvSKgDTg3zvI line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-DsuqlvSKgDTg3zvI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} alt 需要工具 loop 每个 Step 启动 Agent on_agent_init ok on_plan_start ok (可改写 prompt) 生成 Plan Plan on_plan_complete ok / abort on_plan_approved on_step_start 执行 Step pre_model_call ok (可改写 messages) 流式请求 token 批 on_token_stream 流结束 on_stream_end / on_model_call_end on_tool_call ok pre_tool_exec 执行工具 结果 on_tool_return on_step_end on_finish 最终结果
6.3 分类视角
从职责视角,Hook 可归为五类:
- Guard 类 :
pre_model_call的安全校验、pre_tool_exec的准入。 - Interceptor 类 :
pre_model_call改写 prompt、on_model_call_end改写响应。 - Observer 类 :
on_token_stream渲染、on_tool_exec_progress进度上报。 - Policy 类 :
on_context_overflow裁剪、on_error重试策略、预算守卫。 - Lifecycle 类 :
on_pause/on_resume、on_replan、on_finish清理。
7. Hook 注册与执行流程
7.1 注册方式
框架支持三种注册方式,覆盖从开发期到运行期的全部场景:
声明式(装饰器)
@hook(event="pre_model_call", priority=P0)
def prompt_injection_guard(ctx):
if detect_injection(ctx.request.messages):
return Abort(reason="injection", code="SEC_001")
return Continue()
配置式(YAML)
yaml
hooks:
- id: quota_check
event: pre_model_call
priority: P2
handler: mypkg.hooks.quota_check
- id: pii_filter
event: pre_model_call
priority: P0
handler: mypkg.hooks.pii_filter
动态式(运行时注册)
bus.register(Hook(id="dynamic_audit", event="on_model_call_end",
priority=P3, run=audit_fn))
7.2 执行流程
Hook Bus 收到事件后,执行如下流程:
#mermaid-svg-M1UqVrKxgJl6ddjA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-M1UqVrKxgJl6ddjA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-M1UqVrKxgJl6ddjA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-M1UqVrKxgJl6ddjA .error-icon{fill:#552222;}#mermaid-svg-M1UqVrKxgJl6ddjA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-M1UqVrKxgJl6ddjA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-M1UqVrKxgJl6ddjA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-M1UqVrKxgJl6ddjA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-M1UqVrKxgJl6ddjA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-M1UqVrKxgJl6ddjA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-M1UqVrKxgJl6ddjA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-M1UqVrKxgJl6ddjA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-M1UqVrKxgJl6ddjA .marker.cross{stroke:#333333;}#mermaid-svg-M1UqVrKxgJl6ddjA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-M1UqVrKxgJl6ddjA p{margin:0;}#mermaid-svg-M1UqVrKxgJl6ddjA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-M1UqVrKxgJl6ddjA .cluster-label text{fill:#333;}#mermaid-svg-M1UqVrKxgJl6ddjA .cluster-label span{color:#333;}#mermaid-svg-M1UqVrKxgJl6ddjA .cluster-label span p{background-color:transparent;}#mermaid-svg-M1UqVrKxgJl6ddjA .label text,#mermaid-svg-M1UqVrKxgJl6ddjA span{fill:#333;color:#333;}#mermaid-svg-M1UqVrKxgJl6ddjA .node rect,#mermaid-svg-M1UqVrKxgJl6ddjA .node circle,#mermaid-svg-M1UqVrKxgJl6ddjA .node ellipse,#mermaid-svg-M1UqVrKxgJl6ddjA .node polygon,#mermaid-svg-M1UqVrKxgJl6ddjA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-M1UqVrKxgJl6ddjA .rough-node .label text,#mermaid-svg-M1UqVrKxgJl6ddjA .node .label text,#mermaid-svg-M1UqVrKxgJl6ddjA .image-shape .label,#mermaid-svg-M1UqVrKxgJl6ddjA .icon-shape .label{text-anchor:middle;}#mermaid-svg-M1UqVrKxgJl6ddjA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-M1UqVrKxgJl6ddjA .rough-node .label,#mermaid-svg-M1UqVrKxgJl6ddjA .node .label,#mermaid-svg-M1UqVrKxgJl6ddjA .image-shape .label,#mermaid-svg-M1UqVrKxgJl6ddjA .icon-shape .label{text-align:center;}#mermaid-svg-M1UqVrKxgJl6ddjA .node.clickable{cursor:pointer;}#mermaid-svg-M1UqVrKxgJl6ddjA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-M1UqVrKxgJl6ddjA .arrowheadPath{fill:#333333;}#mermaid-svg-M1UqVrKxgJl6ddjA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-M1UqVrKxgJl6ddjA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-M1UqVrKxgJl6ddjA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M1UqVrKxgJl6ddjA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-M1UqVrKxgJl6ddjA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M1UqVrKxgJl6ddjA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-M1UqVrKxgJl6ddjA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-M1UqVrKxgJl6ddjA .cluster text{fill:#333;}#mermaid-svg-M1UqVrKxgJl6ddjA .cluster span{color:#333;}#mermaid-svg-M1UqVrKxgJl6ddjA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-M1UqVrKxgJl6ddjA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-M1UqVrKxgJl6ddjA rect.text{fill:none;stroke-width:0;}#mermaid-svg-M1UqVrKxgJl6ddjA .icon-shape,#mermaid-svg-M1UqVrKxgJl6ddjA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M1UqVrKxgJl6ddjA .icon-shape p,#mermaid-svg-M1UqVrKxgJl6ddjA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-M1UqVrKxgJl6ddjA .icon-shape .label rect,#mermaid-svg-M1UqVrKxgJl6ddjA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M1UqVrKxgJl6ddjA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-M1UqVrKxgJl6ddjA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-M1UqVrKxgJl6ddjA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
Continue
是
否
Transform
Abort
Pause
抛异常
否
是
事件触发
查表取该事件的 Hook Chain
Chain 为空?
直接返回 Continue
按优先级分桶 P0/P1/P2/P3
桶内按注册顺序排序
逐个执行
返回类型?
合并 patch 到 ctx
还有下一个?
替换 payload
短路 + 写入失败原因
返回 Abort
挂起 + 持久化 resumeToken
返回 Pause
escalation?
记录 + 继续
7.3 优先级短路规则
为了性能与安全,P0 桶的 Abort 会立即短路整个 Chain,跳过 P1/P2/P3。这是"安全先于一切"的体现。但 P1 的 Interceptor 改写不会被 P2 看到------除非显式声明 propagate: true。这种隔离避免了"一个改写 prompt 的 Hook 被业务 Hook 意外覆盖"。
7.4 异步与并发
on_token_stream、on_tool_exec_progress 这类高频事件默认异步派发,Hook 在独立协程/线程执行,不阻塞主流程。其返回值被忽略(Observer 语义)。低频但关键的事件(pre_model_call、pre_tool_exec)同步执行,确保 Guard/Interceptor 的判断生效。
框架提供 dispatch_mode: sync | async | fire_and_forget 三档,由 Hook 显式声明,避免"默认异步导致 Guard 失效"。
8. DeepSeek Harness 集成详解
8.1 Harness 职责
DeepSeek Harness 是包裹 DeepSeek 模型的执行外壳,职责包括:
- 请求构建:把 Agent 上下文(系统提示、历史消息、工具定义)组装为 DeepSeek 兼容的请求体。
- 流式解析:解析 SSE 流,识别文本增量、工具调用增量、结束信号。
- 工具路由:把模型产出的 tool_call 路由到 Tool Registry,并把结果回填。
- 状态同步:把模型轮次的产出同步到 Context,驱动 Plan 前进。
- 重试与降级:对网络错误、速率限制、上下文超限做策略化重试。
8.2 请求构建
DeepSeek 兼容 OpenAI 风格的 Chat Completions,但 Harness 在其上做了若干增强:
- system_prompt 模板化:支持 Jinja2 模板,注入当前时间、用户身份、可用工具摘要。
- tool 定义压缩 :长工具签名折叠为摘要,降低 token 占用(由
pre_model_callHook 控制)。 - 历史消息裁剪 :超过上下文窗口时,触发
on_context_overflow让 Hook 决定裁剪策略(保留首尾 / 摘要中段 / 丢弃最早工具调用)。 - few-shot 注入 :在特定 Step 注入示例,由
on_step_startHook 动态拼装。
8.3 流式解析
#mermaid-svg-CyALBaJmxba0yoQs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CyALBaJmxba0yoQs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CyALBaJmxba0yoQs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CyALBaJmxba0yoQs .error-icon{fill:#552222;}#mermaid-svg-CyALBaJmxba0yoQs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CyALBaJmxba0yoQs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CyALBaJmxba0yoQs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CyALBaJmxba0yoQs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CyALBaJmxba0yoQs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CyALBaJmxba0yoQs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CyALBaJmxba0yoQs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CyALBaJmxba0yoQs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CyALBaJmxba0yoQs .marker.cross{stroke:#333333;}#mermaid-svg-CyALBaJmxba0yoQs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CyALBaJmxba0yoQs p{margin:0;}#mermaid-svg-CyALBaJmxba0yoQs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CyALBaJmxba0yoQs .cluster-label text{fill:#333;}#mermaid-svg-CyALBaJmxba0yoQs .cluster-label span{color:#333;}#mermaid-svg-CyALBaJmxba0yoQs .cluster-label span p{background-color:transparent;}#mermaid-svg-CyALBaJmxba0yoQs .label text,#mermaid-svg-CyALBaJmxba0yoQs span{fill:#333;color:#333;}#mermaid-svg-CyALBaJmxba0yoQs .node rect,#mermaid-svg-CyALBaJmxba0yoQs .node circle,#mermaid-svg-CyALBaJmxba0yoQs .node ellipse,#mermaid-svg-CyALBaJmxba0yoQs .node polygon,#mermaid-svg-CyALBaJmxba0yoQs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CyALBaJmxba0yoQs .rough-node .label text,#mermaid-svg-CyALBaJmxba0yoQs .node .label text,#mermaid-svg-CyALBaJmxba0yoQs .image-shape .label,#mermaid-svg-CyALBaJmxba0yoQs .icon-shape .label{text-anchor:middle;}#mermaid-svg-CyALBaJmxba0yoQs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CyALBaJmxba0yoQs .rough-node .label,#mermaid-svg-CyALBaJmxba0yoQs .node .label,#mermaid-svg-CyALBaJmxba0yoQs .image-shape .label,#mermaid-svg-CyALBaJmxba0yoQs .icon-shape .label{text-align:center;}#mermaid-svg-CyALBaJmxba0yoQs .node.clickable{cursor:pointer;}#mermaid-svg-CyALBaJmxba0yoQs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CyALBaJmxba0yoQs .arrowheadPath{fill:#333333;}#mermaid-svg-CyALBaJmxba0yoQs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CyALBaJmxba0yoQs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CyALBaJmxba0yoQs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CyALBaJmxba0yoQs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CyALBaJmxba0yoQs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CyALBaJmxba0yoQs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CyALBaJmxba0yoQs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CyALBaJmxba0yoQs .cluster text{fill:#333;}#mermaid-svg-CyALBaJmxba0yoQs .cluster span{color:#333;}#mermaid-svg-CyALBaJmxba0yoQs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CyALBaJmxba0yoQs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CyALBaJmxba0yoQs rect.text{fill:none;stroke-width:0;}#mermaid-svg-CyALBaJmxba0yoQs .icon-shape,#mermaid-svg-CyALBaJmxba0yoQs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CyALBaJmxba0yoQs .icon-shape p,#mermaid-svg-CyALBaJmxba0yoQs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CyALBaJmxba0yoQs .icon-shape .label rect,#mermaid-svg-CyALBaJmxba0yoQs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CyALBaJmxba0yoQs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CyALBaJmxba0yoQs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CyALBaJmxba0yoQs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} content
tool_calls
完整
finish_reason=stop
finish_reason=tool_calls
usage
SSE chunk
delta 类型
拼接到 token_buffer
触发 on_token_stream
累加到 tool_call_buffer
判断是否完整
触发 on_tool_call
触发 on_stream_end
等待工具执行后继续
累计 token 计费
Harness 维护两个增量缓冲:token_buffer(文本)与 tool_call_buffer(工具调用)。流式过程中,Harness 周期性触发 on_token_stream(默认每 16 token 或 50ms),让 UI 可以实时渲染,但又不至于过频拖慢。
8.4 工具路由
当模型决定调用工具,Harness:
- 触发
on_tool_call(可改写参数,可拒绝)。 - 触发
pre_tool_exec(P0 守卫,做准入与参数校验)。 - 调用 Tool Registry 中注册的实现,带超时与重试。
- 触发
on_tool_exec_progress(对长任务)。 - 收到返回后触发
on_tool_return(可改写返回值,做脱敏)。 - 把工具结果以
tool角色消息回填到 messages,进入下一轮模型调用。
工具调用可能链式(模型连续调用多个工具),Harness 用一个内部循环驱动,直到 finish_reason=stop。
8.5 状态同步
Harness 把每个轮次的产出封装为 Turn 对象写入 Context:
Turn {
turn_id: str
step_id: str
model: "deepseek-chat" | "deepseek-reasoner"
messages_in: [Message]
messages_out: Message
tool_calls: [ToolCall]
usage: {prompt_tokens, completion_tokens, reasoning_tokens}
latency_ms: int
trace_id: str
}
Plan Engine 通过读取最新的 Turn 决定下一步是继续、重规划还是结束。
9. Plan 执行引擎
9.1 Plan 结构
Plan 是一棵由 Step 组成的有向无环图(DAG),而非线性列表。Step 之间可有依赖:
Plan {
plan_id: str
goal: str
steps: [Step]
edges: [(from_step_id, to_step_id, condition?)]
created_at, updated_at
}
Step {
step_id, kind: "model" | "tool" | "sub_agent",
input, expected_output, depends_on: [step_id]
}
DAG 形态允许并行 Step 被并发调度,显著缩短执行时间。
9.2 调度策略
#mermaid-svg-NkrY0CMIGdEFcW6t{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NkrY0CMIGdEFcW6t .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NkrY0CMIGdEFcW6t .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NkrY0CMIGdEFcW6t .error-icon{fill:#552222;}#mermaid-svg-NkrY0CMIGdEFcW6t .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NkrY0CMIGdEFcW6t .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NkrY0CMIGdEFcW6t .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NkrY0CMIGdEFcW6t .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NkrY0CMIGdEFcW6t .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NkrY0CMIGdEFcW6t .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NkrY0CMIGdEFcW6t .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NkrY0CMIGdEFcW6t .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NkrY0CMIGdEFcW6t .marker.cross{stroke:#333333;}#mermaid-svg-NkrY0CMIGdEFcW6t svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NkrY0CMIGdEFcW6t p{margin:0;}#mermaid-svg-NkrY0CMIGdEFcW6t .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NkrY0CMIGdEFcW6t .cluster-label text{fill:#333;}#mermaid-svg-NkrY0CMIGdEFcW6t .cluster-label span{color:#333;}#mermaid-svg-NkrY0CMIGdEFcW6t .cluster-label span p{background-color:transparent;}#mermaid-svg-NkrY0CMIGdEFcW6t .label text,#mermaid-svg-NkrY0CMIGdEFcW6t span{fill:#333;color:#333;}#mermaid-svg-NkrY0CMIGdEFcW6t .node rect,#mermaid-svg-NkrY0CMIGdEFcW6t .node circle,#mermaid-svg-NkrY0CMIGdEFcW6t .node ellipse,#mermaid-svg-NkrY0CMIGdEFcW6t .node polygon,#mermaid-svg-NkrY0CMIGdEFcW6t .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NkrY0CMIGdEFcW6t .rough-node .label text,#mermaid-svg-NkrY0CMIGdEFcW6t .node .label text,#mermaid-svg-NkrY0CMIGdEFcW6t .image-shape .label,#mermaid-svg-NkrY0CMIGdEFcW6t .icon-shape .label{text-anchor:middle;}#mermaid-svg-NkrY0CMIGdEFcW6t .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NkrY0CMIGdEFcW6t .rough-node .label,#mermaid-svg-NkrY0CMIGdEFcW6t .node .label,#mermaid-svg-NkrY0CMIGdEFcW6t .image-shape .label,#mermaid-svg-NkrY0CMIGdEFcW6t .icon-shape .label{text-align:center;}#mermaid-svg-NkrY0CMIGdEFcW6t .node.clickable{cursor:pointer;}#mermaid-svg-NkrY0CMIGdEFcW6t .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NkrY0CMIGdEFcW6t .arrowheadPath{fill:#333333;}#mermaid-svg-NkrY0CMIGdEFcW6t .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NkrY0CMIGdEFcW6t .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NkrY0CMIGdEFcW6t .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NkrY0CMIGdEFcW6t .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NkrY0CMIGdEFcW6t .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NkrY0CMIGdEFcW6t .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NkrY0CMIGdEFcW6t .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NkrY0CMIGdEFcW6t .cluster text{fill:#333;}#mermaid-svg-NkrY0CMIGdEFcW6t .cluster span{color:#333;}#mermaid-svg-NkrY0CMIGdEFcW6t div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NkrY0CMIGdEFcW6t .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NkrY0CMIGdEFcW6t rect.text{fill:none;stroke-width:0;}#mermaid-svg-NkrY0CMIGdEFcW6t .icon-shape,#mermaid-svg-NkrY0CMIGdEFcW6t .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NkrY0CMIGdEFcW6t .icon-shape p,#mermaid-svg-NkrY0CMIGdEFcW6t .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NkrY0CMIGdEFcW6t .icon-shape .label rect,#mermaid-svg-NkrY0CMIGdEFcW6t .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NkrY0CMIGdEFcW6t .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NkrY0CMIGdEFcW6t .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NkrY0CMIGdEFcW6t :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
否
是
是
否
Plan 就绪
计算就绪 Step: 依赖全部完成
有就绪 Step?
所有 Step 完成?
Plan 完成
等待 in-flight Step
按并发上限取出 N 个
并行派发到 Harness
收集 Turn
更新 Step 状态
需要重规划?
触发 on_replan
9.3 重规划触发
下列情况触发 on_replan:
- 模型在某 Step 反复失败(超过 retry 上限)。
- 工具调用返回
needs_replan信号(如发现前置假设错误)。 - 外部
on_step_endHook 主动返回Replan。 - 上下文超限且裁剪后无法保留关键信息。
重规划时,Plan Engine 把失败原因、已完成 Step 的产出、原始目标一起喂给 DeepSeek,生成新的 Plan,并触发 on_plan_complete 重新走审批。
9.4 子 Agent
kind: "sub_agent" 的 Step 会派生一个子 Agent。子 Agent 拥有独立 Context、独立 Hook Bus(继承父 Bus 的部分 Hook),其生命周期嵌套在父 Agent 的一个 Step 内。父 Agent 通过 on_step_end 收到子 Agent 的最终结果。
10. 上下文与数据流管理
10.1 Context 结构
Context {
agent_id, run_id, trace_id
config: AgentConfig
plan: Plan
current_step: Step | null
turns: [Turn]
memory: MemoryStore
budget: BudgetTracker
metadata: Map (供 Hook 自由读写)
}
metadata 是 Hook 之间传递临时状态的黑板。例如 pre_model_call 写入 metadata.risk_score,on_error 据此决定是否降级。
10.2 上下文裁剪
DeepSeek-chat 上下文窗口为 64K(可配),deepseek-reasoner 更长但推理 token 计费。当累积消息逼近阈值,Harness 触发 on_context_overflow,Hook 可选策略:
| 策略 | 描述 | 适用 |
|---|---|---|
drop_oldest_tool |
丢弃最早的工具调用与其结果 | 工具调用密集场景 |
summarize_middle |
中段消息摘要 | 长对话保留首尾 |
keep_system_and_tail |
仅保留系统提示与最近 N 轮 | 默认 |
archive_to_memory |
归档到长期记忆 | 跨会话延续 |
fail |
抛错,要求人工介入 | 合规场景 |
10.3 记忆分层
#mermaid-svg-HljFEZGhRIq4a4g3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HljFEZGhRIq4a4g3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HljFEZGhRIq4a4g3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HljFEZGhRIq4a4g3 .error-icon{fill:#552222;}#mermaid-svg-HljFEZGhRIq4a4g3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HljFEZGhRIq4a4g3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HljFEZGhRIq4a4g3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HljFEZGhRIq4a4g3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HljFEZGhRIq4a4g3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HljFEZGhRIq4a4g3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HljFEZGhRIq4a4g3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HljFEZGhRIq4a4g3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HljFEZGhRIq4a4g3 .marker.cross{stroke:#333333;}#mermaid-svg-HljFEZGhRIq4a4g3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HljFEZGhRIq4a4g3 p{margin:0;}#mermaid-svg-HljFEZGhRIq4a4g3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HljFEZGhRIq4a4g3 .cluster-label text{fill:#333;}#mermaid-svg-HljFEZGhRIq4a4g3 .cluster-label span{color:#333;}#mermaid-svg-HljFEZGhRIq4a4g3 .cluster-label span p{background-color:transparent;}#mermaid-svg-HljFEZGhRIq4a4g3 .label text,#mermaid-svg-HljFEZGhRIq4a4g3 span{fill:#333;color:#333;}#mermaid-svg-HljFEZGhRIq4a4g3 .node rect,#mermaid-svg-HljFEZGhRIq4a4g3 .node circle,#mermaid-svg-HljFEZGhRIq4a4g3 .node ellipse,#mermaid-svg-HljFEZGhRIq4a4g3 .node polygon,#mermaid-svg-HljFEZGhRIq4a4g3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HljFEZGhRIq4a4g3 .rough-node .label text,#mermaid-svg-HljFEZGhRIq4a4g3 .node .label text,#mermaid-svg-HljFEZGhRIq4a4g3 .image-shape .label,#mermaid-svg-HljFEZGhRIq4a4g3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-HljFEZGhRIq4a4g3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HljFEZGhRIq4a4g3 .rough-node .label,#mermaid-svg-HljFEZGhRIq4a4g3 .node .label,#mermaid-svg-HljFEZGhRIq4a4g3 .image-shape .label,#mermaid-svg-HljFEZGhRIq4a4g3 .icon-shape .label{text-align:center;}#mermaid-svg-HljFEZGhRIq4a4g3 .node.clickable{cursor:pointer;}#mermaid-svg-HljFEZGhRIq4a4g3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HljFEZGhRIq4a4g3 .arrowheadPath{fill:#333333;}#mermaid-svg-HljFEZGhRIq4a4g3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HljFEZGhRIq4a4g3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HljFEZGhRIq4a4g3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HljFEZGhRIq4a4g3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HljFEZGhRIq4a4g3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HljFEZGhRIq4a4g3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HljFEZGhRIq4a4g3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HljFEZGhRIq4a4g3 .cluster text{fill:#333;}#mermaid-svg-HljFEZGhRIq4a4g3 .cluster span{color:#333;}#mermaid-svg-HljFEZGhRIq4a4g3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HljFEZGhRIq4a4g3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HljFEZGhRIq4a4g3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-HljFEZGhRIq4a4g3 .icon-shape,#mermaid-svg-HljFEZGhRIq4a4g3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HljFEZGhRIq4a4g3 .icon-shape p,#mermaid-svg-HljFEZGhRIq4a4g3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HljFEZGhRIq4a4g3 .icon-shape .label rect,#mermaid-svg-HljFEZGhRIq4a4g3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HljFEZGhRIq4a4g3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HljFEZGhRIq4a4g3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HljFEZGhRIq4a4g3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 裁剪
归档
检索召回
on_finish
工作记忆 Working: Context.turns
短期记忆 Short: 会话级
长期记忆 Long: 跨会话向量库
结果记忆 Result: 任务产物
三层记忆各有 Hook:on_context_overflow 在工作→短期之间搬运,on_finish 把产物写入结果记忆,on_plan_start 从长期记忆召回相关片段注入 prompt。
11. 错误处理、重试与回滚
11.1 错误分类
| 类别 | 示例 | 默认策略 |
|---|---|---|
| 瞬时网络 | 连接重置,5xx | 指数退避重试 |
| 速率限制 | 429 | 按 Retry-After 等待 |
| 上下文超限 | prompt过长 | 触发裁剪 Hook 后重试 |
| 工具失败 | 工具抛业务错 | 把错误回填模型,让其自纠 |
| 模型输出非法 | 非 JSON | 修复提示词后重试 1 次 |
| 安全拦截 | Guard Abort | 终止,返回合规错误 |
| 预算耗尽 | 预算守卫 Abort | 终止,返回部分结果 |
11.2 错误处理流程
#mermaid-svg-cwMrtzzYHLJeczx2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cwMrtzzYHLJeczx2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cwMrtzzYHLJeczx2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cwMrtzzYHLJeczx2 .error-icon{fill:#552222;}#mermaid-svg-cwMrtzzYHLJeczx2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cwMrtzzYHLJeczx2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cwMrtzzYHLJeczx2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cwMrtzzYHLJeczx2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cwMrtzzYHLJeczx2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cwMrtzzYHLJeczx2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cwMrtzzYHLJeczx2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cwMrtzzYHLJeczx2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cwMrtzzYHLJeczx2 .marker.cross{stroke:#333333;}#mermaid-svg-cwMrtzzYHLJeczx2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cwMrtzzYHLJeczx2 p{margin:0;}#mermaid-svg-cwMrtzzYHLJeczx2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cwMrtzzYHLJeczx2 .cluster-label text{fill:#333;}#mermaid-svg-cwMrtzzYHLJeczx2 .cluster-label span{color:#333;}#mermaid-svg-cwMrtzzYHLJeczx2 .cluster-label span p{background-color:transparent;}#mermaid-svg-cwMrtzzYHLJeczx2 .label text,#mermaid-svg-cwMrtzzYHLJeczx2 span{fill:#333;color:#333;}#mermaid-svg-cwMrtzzYHLJeczx2 .node rect,#mermaid-svg-cwMrtzzYHLJeczx2 .node circle,#mermaid-svg-cwMrtzzYHLJeczx2 .node ellipse,#mermaid-svg-cwMrtzzYHLJeczx2 .node polygon,#mermaid-svg-cwMrtzzYHLJeczx2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cwMrtzzYHLJeczx2 .rough-node .label text,#mermaid-svg-cwMrtzzYHLJeczx2 .node .label text,#mermaid-svg-cwMrtzzYHLJeczx2 .image-shape .label,#mermaid-svg-cwMrtzzYHLJeczx2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-cwMrtzzYHLJeczx2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cwMrtzzYHLJeczx2 .rough-node .label,#mermaid-svg-cwMrtzzYHLJeczx2 .node .label,#mermaid-svg-cwMrtzzYHLJeczx2 .image-shape .label,#mermaid-svg-cwMrtzzYHLJeczx2 .icon-shape .label{text-align:center;}#mermaid-svg-cwMrtzzYHLJeczx2 .node.clickable{cursor:pointer;}#mermaid-svg-cwMrtzzYHLJeczx2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cwMrtzzYHLJeczx2 .arrowheadPath{fill:#333333;}#mermaid-svg-cwMrtzzYHLJeczx2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cwMrtzzYHLJeczx2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cwMrtzzYHLJeczx2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cwMrtzzYHLJeczx2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cwMrtzzYHLJeczx2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cwMrtzzYHLJeczx2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cwMrtzzYHLJeczx2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cwMrtzzYHLJeczx2 .cluster text{fill:#333;}#mermaid-svg-cwMrtzzYHLJeczx2 .cluster span{color:#333;}#mermaid-svg-cwMrtzzYHLJeczx2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cwMrtzzYHLJeczx2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cwMrtzzYHLJeczx2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-cwMrtzzYHLJeczx2 .icon-shape,#mermaid-svg-cwMrtzzYHLJeczx2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cwMrtzzYHLJeczx2 .icon-shape p,#mermaid-svg-cwMrtzzYHLJeczx2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cwMrtzzYHLJeczx2 .icon-shape .label rect,#mermaid-svg-cwMrtzzYHLJeczx2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cwMrtzzYHLJeczx2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cwMrtzzYHLJeczx2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cwMrtzzYHLJeczx2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Retry
Degrade
Replan
Terminate
Continue
是
否
异常发生
触发 on_error
Hook 决策?
按策略重试
降级:换模型/简化 Plan
触发 on_replan
触发 on_terminate
忽略错误继续
重试次数超限?
恢复执行
Failed
11.3 补偿与回滚
Agent 的工具调用可能产生副作用(发邮件、写数据库)。框架支持声明式补偿:工具注册时可附带 compensation 回调,on_terminate 时按调用栈逆序触发补偿。例如:
@register_tool(compensation=undo_send)
def send_email(...): ...
补偿本身也是工具调用,经过完整的 Hook 链路,确保补偿过程同样可审计。
12. 可观测性与监控
12.1 三支柱
#mermaid-svg-4AFREbmpWpw0POw7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4AFREbmpWpw0POw7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4AFREbmpWpw0POw7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4AFREbmpWpw0POw7 .error-icon{fill:#552222;}#mermaid-svg-4AFREbmpWpw0POw7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4AFREbmpWpw0POw7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4AFREbmpWpw0POw7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4AFREbmpWpw0POw7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4AFREbmpWpw0POw7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4AFREbmpWpw0POw7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4AFREbmpWpw0POw7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4AFREbmpWpw0POw7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4AFREbmpWpw0POw7 .marker.cross{stroke:#333333;}#mermaid-svg-4AFREbmpWpw0POw7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4AFREbmpWpw0POw7 p{margin:0;}#mermaid-svg-4AFREbmpWpw0POw7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4AFREbmpWpw0POw7 .cluster-label text{fill:#333;}#mermaid-svg-4AFREbmpWpw0POw7 .cluster-label span{color:#333;}#mermaid-svg-4AFREbmpWpw0POw7 .cluster-label span p{background-color:transparent;}#mermaid-svg-4AFREbmpWpw0POw7 .label text,#mermaid-svg-4AFREbmpWpw0POw7 span{fill:#333;color:#333;}#mermaid-svg-4AFREbmpWpw0POw7 .node rect,#mermaid-svg-4AFREbmpWpw0POw7 .node circle,#mermaid-svg-4AFREbmpWpw0POw7 .node ellipse,#mermaid-svg-4AFREbmpWpw0POw7 .node polygon,#mermaid-svg-4AFREbmpWpw0POw7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4AFREbmpWpw0POw7 .rough-node .label text,#mermaid-svg-4AFREbmpWpw0POw7 .node .label text,#mermaid-svg-4AFREbmpWpw0POw7 .image-shape .label,#mermaid-svg-4AFREbmpWpw0POw7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-4AFREbmpWpw0POw7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4AFREbmpWpw0POw7 .rough-node .label,#mermaid-svg-4AFREbmpWpw0POw7 .node .label,#mermaid-svg-4AFREbmpWpw0POw7 .image-shape .label,#mermaid-svg-4AFREbmpWpw0POw7 .icon-shape .label{text-align:center;}#mermaid-svg-4AFREbmpWpw0POw7 .node.clickable{cursor:pointer;}#mermaid-svg-4AFREbmpWpw0POw7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4AFREbmpWpw0POw7 .arrowheadPath{fill:#333333;}#mermaid-svg-4AFREbmpWpw0POw7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4AFREbmpWpw0POw7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4AFREbmpWpw0POw7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4AFREbmpWpw0POw7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4AFREbmpWpw0POw7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4AFREbmpWpw0POw7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4AFREbmpWpw0POw7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4AFREbmpWpw0POw7 .cluster text{fill:#333;}#mermaid-svg-4AFREbmpWpw0POw7 .cluster span{color:#333;}#mermaid-svg-4AFREbmpWpw0POw7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4AFREbmpWpw0POw7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4AFREbmpWpw0POw7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-4AFREbmpWpw0POw7 .icon-shape,#mermaid-svg-4AFREbmpWpw0POw7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4AFREbmpWpw0POw7 .icon-shape p,#mermaid-svg-4AFREbmpWpw0POw7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4AFREbmpWpw0POw7 .icon-shape .label rect,#mermaid-svg-4AFREbmpWpw0POw7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4AFREbmpWpw0POw7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4AFREbmpWpw0POw7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4AFREbmpWpw0POw7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Agent Run
结构化日志
指标
追踪跨度
Hook Bus 事件流
Logs
Metrics
Traces
统一仪表盘
12.2 关键指标
| 指标 | 含义 | 告警阈值 |
|---|---|---|
agent.run.latency_p95 |
端到端时延 | 业务定 |
model.call.latency_p95 |
单次模型调用 | > 8s |
model.call.token_per_sec |
流式吞吐 | < 20/s |
tool.exec.failure_rate |
工具失败率 | > 10% |
hook.chain.latency_p99 |
Hook 链耗时 | > 100ms |
hook.abort.rate |
Abort 比例 | 突增即告警 |
replan.rate |
重规划比例 | > 30% |
context.overflow.rate |
裁剪触发比例 | > 20% |
budget.burn_rate |
预算消耗速率 | 接近上限告警 |
12.3 追踪跨度
每个 Hook 执行、每次模型调用、每次工具调用都是一个 span,挂在统一 trace_id 下。OpenTelemetry 兼容,可导出到 Jaeger/Tempo。这让"为什么 Agent 这次跑偏了"可被可视化为一条完整调用链。
12.4 回放
由于 Hook 输入输出与 Context 均可序列化,框架支持"事件流回放":把一次运行的 Hook 事件序列存档,离线在新进程按序回放,重现 Agent 行为。这对复现安全事件、调试新 Hook 极其有用。
13. 扩展机制与插件生态
13.1 插件包
Hook 可打包为插件(一个声明了 manifest 的目录/包):
my-plugin/
manifest.yaml # 元数据、依赖、提供哪些 Hook
hooks/
guard.py
observer.py
tools/
search.py
config_schema.json
框架在 on_agent_init 阶段扫描插件,注册其 Hook 与工具,触发 on_plugin_load。
13.2 插件依赖与冲突
- 插件可声明
requires与conflicts。 - 同事件点同优先级的两个插件 Hook 冲突时,按
manifest.priority_weight决胜,再按加载顺序。 - 插件可声明
provides_capability,其他插件requires_capability,实现能力发现。
13.3 远端 Hook
对重量级 Hook(如基于大模型的内容审核),支持远端模式:Hook 通过 gRPC 调用外部服务。Bus 用流式协议,避免阻塞主流程。远端 Hook 自身也可挂载 Hook(元 Hook),用于监控远端调用的健康。
14. 安全与权限模型
14.1 多层防御
#mermaid-svg-A3YB6H7uX0f7SWmK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-A3YB6H7uX0f7SWmK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-A3YB6H7uX0f7SWmK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-A3YB6H7uX0f7SWmK .error-icon{fill:#552222;}#mermaid-svg-A3YB6H7uX0f7SWmK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-A3YB6H7uX0f7SWmK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-A3YB6H7uX0f7SWmK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-A3YB6H7uX0f7SWmK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-A3YB6H7uX0f7SWmK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-A3YB6H7uX0f7SWmK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-A3YB6H7uX0f7SWmK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-A3YB6H7uX0f7SWmK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-A3YB6H7uX0f7SWmK .marker.cross{stroke:#333333;}#mermaid-svg-A3YB6H7uX0f7SWmK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-A3YB6H7uX0f7SWmK p{margin:0;}#mermaid-svg-A3YB6H7uX0f7SWmK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-A3YB6H7uX0f7SWmK .cluster-label text{fill:#333;}#mermaid-svg-A3YB6H7uX0f7SWmK .cluster-label span{color:#333;}#mermaid-svg-A3YB6H7uX0f7SWmK .cluster-label span p{background-color:transparent;}#mermaid-svg-A3YB6H7uX0f7SWmK .label text,#mermaid-svg-A3YB6H7uX0f7SWmK span{fill:#333;color:#333;}#mermaid-svg-A3YB6H7uX0f7SWmK .node rect,#mermaid-svg-A3YB6H7uX0f7SWmK .node circle,#mermaid-svg-A3YB6H7uX0f7SWmK .node ellipse,#mermaid-svg-A3YB6H7uX0f7SWmK .node polygon,#mermaid-svg-A3YB6H7uX0f7SWmK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-A3YB6H7uX0f7SWmK .rough-node .label text,#mermaid-svg-A3YB6H7uX0f7SWmK .node .label text,#mermaid-svg-A3YB6H7uX0f7SWmK .image-shape .label,#mermaid-svg-A3YB6H7uX0f7SWmK .icon-shape .label{text-anchor:middle;}#mermaid-svg-A3YB6H7uX0f7SWmK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-A3YB6H7uX0f7SWmK .rough-node .label,#mermaid-svg-A3YB6H7uX0f7SWmK .node .label,#mermaid-svg-A3YB6H7uX0f7SWmK .image-shape .label,#mermaid-svg-A3YB6H7uX0f7SWmK .icon-shape .label{text-align:center;}#mermaid-svg-A3YB6H7uX0f7SWmK .node.clickable{cursor:pointer;}#mermaid-svg-A3YB6H7uX0f7SWmK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-A3YB6H7uX0f7SWmK .arrowheadPath{fill:#333333;}#mermaid-svg-A3YB6H7uX0f7SWmK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-A3YB6H7uX0f7SWmK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-A3YB6H7uX0f7SWmK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A3YB6H7uX0f7SWmK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-A3YB6H7uX0f7SWmK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A3YB6H7uX0f7SWmK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-A3YB6H7uX0f7SWmK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-A3YB6H7uX0f7SWmK .cluster text{fill:#333;}#mermaid-svg-A3YB6H7uX0f7SWmK .cluster span{color:#333;}#mermaid-svg-A3YB6H7uX0f7SWmK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-A3YB6H7uX0f7SWmK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-A3YB6H7uX0f7SWmK rect.text{fill:none;stroke-width:0;}#mermaid-svg-A3YB6H7uX0f7SWmK .icon-shape,#mermaid-svg-A3YB6H7uX0f7SWmK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A3YB6H7uX0f7SWmK .icon-shape p,#mermaid-svg-A3YB6H7uX0f7SWmK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-A3YB6H7uX0f7SWmK .icon-shape .label rect,#mermaid-svg-A3YB6H7uX0f7SWmK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A3YB6H7uX0f7SWmK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-A3YB6H7uX0f7SWmK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-A3YB6H7uX0f7SWmK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户输入
P0 Prompt Guard: 注入/PII 检测
Harness: 请求构建
P0 Tool Guard: 工具准入与参数校验
Tool 执行: 沙箱/权限隔离
P0 Response Guard: 敏感信息脱敏
输出给用户
14.2 工具权限
每个工具声明所需权限,Agent 运行时携带一个 capability token。pre_tool_exec 的 Guard 校验 token 是否覆盖所需权限。高敏感工具(如 delete_file、send_email)额外要求 confirm: true,触发 on_pause 等待人工审批。
14.3 提示词注入防护
- 输入侧:
pre_model_call的 Guard 对用户输入做模式匹配(已知注入特征)与轻量分类器。 - 输出侧:
on_model_call_end的 Guard 检测模型是否"被诱导泄露系统提示"。 - 工具结果侧:工具返回的文本被标记为
untrusted,pre_model_call重新拼装时用特殊分隔符包裹,降低污染。
14.4 密钥与凭证
Agent 的密钥由外部 Secret Store 注入,Context 中只持有引用 ID,真实凭证在 Harness 边界处替换,Hook 永远看不到明文。这避免了观测类 Hook 误把密钥写入日志。
15. 最佳实践与反模式
15.1 最佳实践
- Guard 优先级锁死在 P0 :安全 Hook 必须最高优先级,且
escalation: true。 - Observer 用异步模式 :渲染、日志、指标类 Hook 用
async,避免拖慢主流程。 - Interceptor 谨慎用:改写 prompt 的 Hook 必须留下审计记录(改前/改后都存)。
- Hook 保持幂等:同一事件多次触发(重试场景)应得到一致结果。
- Hook 不做重活:Hook 内禁止同步调用外部 LLM;若必须,用远端 Hook + 异步。
- 优先级文档化:每个事件点的 Hook 顺序应在插件文档显式列出。
- 补偿优先于回滚:对副作用工具,声明 compensation 比"全量回滚"更现实。
- 预算守卫显式:每个 Agent 必须挂载预算 Hook,防止失控烧钱。
- Context.metadata 命名空间化 :Hook 写 metadata 用前缀(如
pii_filter.score),避免冲突。 - 回放先行:新 Hook 上线前,用历史事件流回放验证行为。
15.2 反模式
| 反模式 | 后果 | 正解 |
|---|---|---|
| 在 P3 改写 prompt | 被 P0/P1 覆盖,无效 | 改写提级到 P1 |
| Hook 同步调外部 LLM | 主流程阻塞数十秒 | 远端 Hook + 异步 |
| 用全局变量传状态 | 不可回放,并发错乱 | 用 Context.metadata |
| Observer 抛未捕获异常 | 默认被吞,但若 escalation=true 会杀掉 Agent | Observer 显式 escalation: false |
| 一个 Hook 干多件事 | 难复用,难排序 | 拆分为多个单一职责 Hook |
| 不设预算守卫 | 单次运行烧光配额 | 强制挂载 budget Hook |
| 工具无 compensation | 失败时无法补救 | 关键工具声明补偿 |
| 重规划无限循环 | Agent 卡死 | 重规划次数硬上限(默认 3) |
16. 性能调优指南
16.1 关键路径
Agent 端到端时延主要由三段构成:模型推理(占 60--80%)、Hook 链(占 5--15%)、工具执行(占 10--30%)。优化应优先攻模型段(选对模型、减少上下文、用好流式),其次工具并发,最后才是 Hook。
16.2 Hook 链优化
- 同步事件点的 Hook 数量建议 ≤ 5,超过应评估能否合并或迁到异步。
- P0 链上 Guard 应保持轻量(模式匹配 < 1ms,分类器 < 10ms)。
- 用
dispatch_mode: fire_and_forget把纯观测 Hook 移出关键路径。 - Hook 内禁止阻塞 IO;若必须,改远端 + 异步。
16.3 上下文优化
- 工具签名压缩:长签名折叠为摘要,降低 prompt token。
- 历史裁剪:早裁剪比晚裁剪省得多,设置 70% 阈值即触发,而非 95%。
- 记忆召回注入:只注入 top-k 相关片段,k 不超过 5。
- few-shot 动态化:只在失败重试时注入示例,降低常规开销。
16.4 并发优化
- DAG 中无依赖 Step 并发派发,上限根据 DeepSeek 配额设置(默认 4)。
- 工具调用若 IO 密集,用线程池;若 CPU 密集,用进程池,避免阻塞事件循环。
- 子 Agent 的并发受全局预算约束,避免雪崩。
17. 演进路线与未来工作
17.1 短期
- Hook 协议升级到 v2,支持流式改写(对
on_token_stream的 Interceptor)。 - 引入 Hook 热重载:不重启 Agent 即可替换 Hook。
- 内置合规插件包:GDPR、个保法、金融审计。
17.2 中期
- 多模型 Harness 抽象:同一 Hook 生态适配 DeepSeek 之外的模型,统一事件协议。
- Plan 编译器:把自然语言 Plan 编译为可验证的 DAG,带类型与契约。
- 自适应重规划:用强化学习决定何时重规划,而非硬阈值。
17.3 长期
- Hook 市场:可信插件签名、分发、计费。
- 跨 Agent 协作:多 Agent 共享 Hook Bus,支持 Agent 间事件订阅。
- 形式化验证:对安全关键 Agent,用形式化方法证明 Hook 链的可终止性与边界。
18. 总结
Agent Plan x DeepSeek Harness 把"大模型推理"升级为"可控的自主执行",其核心支撑是两根支柱:
- 生命周期模型:用状态机刻画 Agent 从诞生到终止的全过程,使运行可被理解、可被干预。
- Hook 系统:在生命周期的每个关键节点提供可插拔的扩展点,把横切关注点(安全、审计、限流、压缩、观测)从核心执行流中剥离。
二者合在一起,带来了三项工程价值:
- 可控性------任意节点可被 Guard 中断、被 Interceptor 改写、被 Pause 暂停,Agent 永不"放飞"。
- 可演进性------Plan、Harness、Hook 三层解耦,各自演进;插件生态让能力随业务生长。
- 可观测性------完整事件序列、统一追踪、可回放,让"黑盒 Agent"变透明。
工程实践上,务必守住三条红线:安全 Guard 锁死 P0、预算守卫强制挂载、副作用工具必须声明补偿。在此之上,让 Hook 保持单一职责、轻量、幂等,框架就能在保持简洁的同时,承载从简单问答到复杂多步任务的全部场景。
Hook 系统不是"加完功能就忘"的胶水,而是 Agent 的神经系统。把它设计好,Agent 才能既聪明,又守规矩;既自主,又可问责。这正是 DeepSeek Harness 区别于"裸调用 API"的根本所在,也是 Agent 走向生产级应用的必经之路。
附录索引(可后续扩展)
- A. Hook 协议完整 Schema
- B. 内置 Hook 事件参考表
- C. 插件 manifest 规范
- D. 远端 Hook gRPC 协议
- E. 回放文件格式
- F. 与 OpenTelemetry 的 span 映射