Agent-智能体工程方法论

Agent 开发范式:自动化、可控性、可追溯、可干预的工程平衡

基于博主自己开发的合同资产底稿智能体的实践经验,提炼为通用 Agent 开发范式。

核心命题:如何在自动化、可控性、可追溯、可干预之间找到工程上最优的平衡点。


一、核心认知框架

1.1 Agent 的工程本质

复制代码
agent = model(生产能力) + harness(工具·约束·评估·重试) + memory(跨轮次的状态和知识)

大模型的智力是够的,关键在于 harness 的发挥。就像人会遗忘无关的内容,让语言模型达到工作记忆、长期记忆的效果即可。

Harness 就是把不确定变成工程上可控的不确定:

  • 锁定可控因素:固定 seed、temperature、system prompt
  • 记录重现路径:保存每一次生成的完整上下文
  • 重试+投票:多次生成,取多数结果
  • 可验证中间态:把黑盒决策变成可检查的步骤

大模型的可信,不是靠模型自己,而是靠围绕它的工程系统。

1.2 不确定性的本质

大模型预测生成,本质是 next token prediction。它没有独立立场,只有"上下文延续"的目标。这意味着:

  • 每次模型的输出都是随机的预测生成
  • 单纯 SOP 不可重放,结果会飘移
  • 智力的一部分来自幻觉------新的组合来自概率引擎的随机性

因此 harness 要允许智能体试错、检验,直到通过标准。技术路径是在特定时机抽出结果来检验,减少有害幻觉,提高有效智力。

1.3 行业智能体 vs 通用智能体

通用模型可以学 SOP,但行业定制智能体的不可替代性在于:

维度 通用模型能做 定制智能体必须做
流程理解 能学 SOP 模板保真 + 异常分级
数据处理 能处理标准格式 异构表格结构标准化
决策质量 概率置信 状态机 + 规则复核
结果交付 自由输出 结果可追溯 + 版本管理
失败处理 重试或放弃 分级降级 + 工程责任链

强化定制智能体的核心价值:模板保真 + 异常分级 + 状态机 + 结果可追溯------这套工程责任链是大模型自身能力演进无法直接覆盖的。


二、LLM 裁决节点设计范式

2.1 核心原则:候选输出 + 规则复核 + 可降级

避免自由式 agent 破坏可控性。LLM 根据规则和契约裁定,而非自由决策。

复制代码
规则定位 → [成功] → 直接采用
    ↓ [失败]
触发 LLM 候选 → 规则验证 → [通过] → 采用
    ↓ [仍失败]
换一种探索策略(扩大表头采样范围 / 变换匹配维度)→ 再试
    ↓ [仍失败]
降级(标记 REVIEW / 人工复核)

2.2 inferWithFeedback 标准模式

复制代码
async inferWithFeedback<T>(
  initialContext: unknown,
  llmMethod: (context: unknown) => Promise<T>,
  validate: (result: T) => { ok: boolean; feedback?: string },
  maxAttempts: number = 2
): Promise<T | null> {
  let context = initialContext
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    const result = await llmMethod(context)
    const validation = validate(result)
    if (validation.ok) return result
    context = { ...initialContext, previousAttemptFeedback: validation.feedback }
  }
  return null // 降级到规则兜底或人工复核
}

关键约束:

  • 不改变固定管线架构
  • 最多 2-3 次 LLM 调用(不是无限循环)
  • 失败后仍然降级到规则兜底
  • 每个高歧义节点独立使用,不串成跨节点的自由循环

2.3 LLM 节点的输入输出契约

每个 LLM 判断节点必须具备完整契约:

复制代码
┌─────────────────────────────────────────────────┐
│  LLM 判断节点契约                                │
├─────────────────────────────────────────────────┤
│  1. System Prompt 约束(禁止越权语义)            │
│  2. User Payload 结构(可序列化、可快照)         │
│  3. Schema 返回结构(Zod 校验)                  │
│  4. Fail Code 分支(每种失败有对应码)            │
│  5. 降级策略(失败时退回规则或人工)              │
│  6. 日志要求(输入规模/模型参数/校验结果/采纳态)  │
└─────────────────────────────────────────────────┘

2.4 禁止 LLM 越权的硬约束

LLM 不可直接参与:

  1. 最终金额口径裁定
  2. 关键数值的最终写回决定
  3. 状态机判定(DONE / REVIEW / PARTIAL)
  4. 模板结构、公式区、宏的修改决策
  5. 跨节点的自由编排调度

LLM 只允许:

  1. 输出结构化候选(排序、置信度、理由)
  2. 语义理解辅助(表头识别、名称消歧)
  3. 说明性文本草稿生成
  4. 标记 needsHumanReview 提示规则层

三、表格异构与结构标准化

3.1 处理范式:先结构标准化,再字段定位

复制代码
原始表格 → 结构标准化 → 字段定位 → 数据抽取
    │           │            │
    │    合并单元格展开      列映射推断
    │    多行表头合并        数据起始行识别
    │    空行/装饰行过滤     类型推断
    │
    └── 异构识别(表头矩阵、采样行、关键词命中)

3.2 列定位冲突时的降级策略

定位冲突时宁可降级,也不盲写:

复制代码
Level 1: 坐标锚点定位(列号硬编码,适用于标准模板)
    ↓ [列号命中但表头不符]
Level 2: 表头锚点定位(关键词匹配)
    ↓ [多候选或无命中]
Level 3: LLM 语义定位(候选排序 + 规则复核)
    ↓ [规则复核失败]
Level 4: 降级 → 标记异常 + 人工复核

每一级的结果都要回馈下一级决策。

3.3 合并单元格处理

复制代码
1. 读取时:展开为矩阵表达(每个逻辑单元格记录物理范围)
2. 表头识别:按层级拼接多行表头("借方" + "发生额" = "借方发生额")
3. 数据区定位:跳过装饰行,以首个全数值行为数据起始候选
4. 冲突处理:同一逻辑列有多个物理列候选时,输出候选清单而非强行选定

四、抽样输入与覆盖风险管理

4.1 问题本质

如果用户只给前后样本,中间异常可能遗漏。这不是模型能力能完全补掉的,必须在系统里显式暴露覆盖风险,并限制完成态。

4.2 设计原则

复制代码
1. 显式暴露覆盖风险
   - 任务结果必须标注"采样覆盖率"和"未覆盖区域"
   - 输出中明确标记"以下区域未经验证"

2. 限制完成态
   - 覆盖率不足时,禁止标记 DONE
   - 部分覆盖 → REVIEW(需人工补充验证)
   - 严重不足 → PARTIAL(半成品输出)

3. 异常检测策略
   - 不仅检查已匹配项,还要主动发现"未被任何规则命中的项"
   - 输出"未匹配残留项清单"作为覆盖风险指标

4. 增量验证接口
   - 允许用户补充样本后重新运行局部链路
   - 增量验证结果合并到已有结果集

4.3 覆盖率量化模型

复制代码
interface CoverageReport {
  totalItems: number
  matchedItems: number
  unmatchedItems: number
  coverageRate: number // 0~1
  uncoveredRegions: string[] // 未覆盖的区域描述
  riskLevel: 'low' | 'medium' | 'high'
  allowedStatus: ('DONE' | 'REVIEW' | 'PARTIAL')[] // 基于覆盖率限制可输出的状态
}

五、脏数据处理与鲁棒性验证

5.1 脏数据分类

脏数据类型 示例 处理策略
格式异常 金额列混入文本、日期格式不统一 类型检测 + 清洗规则 + 异常标记
结构异常 合并单元格打断数据区、隐藏行/列 结构标准化前置
语义异常 同名不同义、缩写混用、错别字 标准化 + 候选匹配 + LLM 消歧
缺失异常 关键字段为空、整列缺失 缺失检测 + 降级策略 + 待补标记
冲突异常 多源数据对同一字段给出不同值 优先级规则 + 冲突裁决 + 审计追踪
边界异常 超长文本、极大/极小数值、特殊字符 边界检查 + 截断/归一化

5.2 测试数据集设计原则

复制代码
1. 正常流样本:标准格式、字段完整、无冲突
2. 边界样本:最小输入、最大输入、极端值
3. 故意脏数据样本:
   - 表头错位(列号偏移 1-2 列)
   - 名称变体(简称、别名、错别字各 3-5 例)
   - 数据区插入空行/装饰行
   - 合并单元格跨越数据区
   - 金额列混入中文
   - 同一项目在不同源中名称不一致
4. 半成品样本:缺少 1-2 个关键输入文件
5. 冲突样本:多源数据给出矛盾值

5.3 鲁棒性验证流水线

复制代码
每次发布前必须通过:
1. 正常流全通过(基线)
2. 已知脏数据样本全降级到正确状态(不 crash、不盲写)
3. 新增脏数据样本至少覆盖上次失败的场景类型
4. 回归:新功能不破坏已有样本的处理结果

六、工具结果回灌与决策链路

6.1 核心思想:每一步工具结果回灌下一步决策

复制代码
┌──────────┐     ┌──────────┐     ┌──────────┐
│  Step N  │────→│ Tool Call │────→│ Result N │
└──────────┘     └──────────┘     └──────────┘
                                        │
                                        ▼
                                ┌──────────────┐
                                │ Context for  │
                                │  Step N+1    │
                                └──────────────┘
                                        │
                                        ▼
                              ┌────────────────────┐
                              │ Decision: Next Tool │
                              │ or Degrade or Done  │
                              └────────────────────┘

实现要点:

  • 工具返回结构化结果(不只是 success/fail,还有 what happened)
  • 结果中包含"下一步可选动作"的 hint
  • 失败结果中包含"失败原因分类",供决策层选择恢复策略

6.2 关键节点 Hook 系统

复制代码
interface AgentHookSystem {
  hooks: {
    beforeLlmCall: Hook[]    // 注入额外上下文、拦截超长输入
    afterLlmCall: Hook[]     // 审计日志、响应质量评分
    beforeToolCall: Hook[]   // 参数校验、权限检查
    afterToolCall: Hook[]    // 结果摘要、异常检测
    beforeWrite: Hook[]      // 最终校验、冲突检测
    afterWrite: Hook[]       // 死数扫描、一致性校验
    onDegrade: Hook[]        // 降级原因记录、通知
    onComplete: Hook[]       // 结果验证、覆盖率统计
  }
}

Hook 的价值:

  • 主循环可扩展,新增门禁不改 Service 主逻辑
  • LLM 用在歧义分支点的语义判断和探索循环
  • 在特定时机抽出结果来检验(过程审核)

6.3 歧义分支点的语义判断

复制代码
识别歧义分支点 → Hook 拦截 → LLM 语义判断 → 规则验证
                                    │
                          ┌─────────┼─────────┐
                          ▼         ▼         ▼
                      路径 A    路径 B    降级路径
                      (高置信)  (中置信)  (低置信/冲突)

LLM 在歧义分支点的角色是"探索循环":

  1. 给出候选路径
  2. 规则层验证可行性
  3. 如果不可行,换一种探索策略
  4. 如果多次不可行,降级

七、任务板与子任务摘要系统

7.1 从子任务摘要升级为任务板

复制代码
interface TaskBoard {
  taskId: string
  overallStatus: 'RUNNING' | 'DONE' | 'REVIEW' | 'PARTIAL' | 'FAILED'
  subtasks: SubtaskEntry[]
  timeline: TimelineEvent[]
  coverageReport: CoverageReport
  issuesSummary: IssueSummary
}

interface SubtaskEntry {
  id: string
  name: string
  status: 'pending' | 'running' | 'done' | 'skipped' | 'degraded' | 'failed'
  input: string   // 输入来源摘要
  output: string  // 输出结果摘要
  skipReason?: string
  degradeReason?: string
  issues: Issue[]
  duration?: number
  llmCallCount?: number
}

interface TimelineEvent {
  timestamp: number
  subtaskId: string
  event: 'start' | 'tool_call' | 'llm_call' | 'validation' | 'degrade' | 'complete' | 'error'
  detail: string
}

7.2 任务板的用户价值

复制代码
任务板提供:
1. 整体进度一目了然(哪些子任务完成/跳过/降级)
2. 异常定位(哪一步出了问题、为什么跳过)
3. 可干预点(用户可以对 REVIEW 状态的子任务补充信息后重跑)
4. 审计追踪(每个决策点的输入/输出/依据可追溯)
5. 成本可视化(LLM 调用次数、token 消耗)

7.3 可干预接口

复制代码
interface TaskInterventionAPI {
  // 用户补充信息后重跑某个子任务
  retrySubtask(taskId: string, subtaskId: string, additionalContext?: unknown): Promise<SubtaskResult>

  // 用户确认 LLM 候选结果
  confirmCandidate(taskId: string, subtaskId: string, candidateId: string): Promise<void>

  // 用户覆盖自动决策
  overrideDecision(taskId: string, subtaskId: string, manualResult: unknown): Promise<void>

  // 获取当前任务板快照
  getTaskBoard(taskId: string): Promise<TaskBoard>

  // 获取特定子任务的完整决策链路
  getDecisionTrace(taskId: string, subtaskId: string): Promise<DecisionTrace>
}

八、可追溯性工程

8.1 可追溯的四个层次

复制代码
Layer 1: 输入追溯
  - 每个输出值可回溯到来源文件的具体单元格
  - 数据流向图:源 → 中间态 → 最终输出

Layer 2: 决策追溯
  - 每个 LLM 判断节点的输入快照、模型参数、结构化输出
  - 规则复核结果(采纳/拒绝/降级)
  - 降级原因分类

Layer 3: 变更追溯
  - 每次写回的 diff(哪些单元格被写入了什么值)
  - 公式保护日志(哪些公式被保留、哪些被覆盖)
  - 版本树(v1 → v2 → v3,可回退)

Layer 4: 异常追溯
  - 异常分级与异常码字典
  - 每个异常的触发条件、影响范围、建议动作
  - 异常收敛路径(异常 → 子任务状态 → 整体状态)

8.2 决策追溯的实现

复制代码
interface DecisionTrace {
  subtaskId: string
  timestamp: number
  decisionType: 'rule' | 'llm' | 'hybrid' | 'manual'

  // 输入快照
  inputSnapshot: {
    sampleSize: number
    keyFields: string[]
    contextSummary: string
  }

  // LLM 相关(仅 llm/hybrid)
  llmTrace?: {
    model: string
    temperature: number
    promptHash: string  // 不存完整 prompt,存 hash 可回查
    candidateCount: number
    selectedCandidate: number
    confidence: number
    reasons: string[]
  }

  // 规则复核
  ruleValidation: {
    passed: boolean
    failedRules: string[]
    feedback?: string
  }

  // 最终结论
  outcome: 'accepted' | 'rejected' | 'degraded' | 'human_review'
  outcomeReason: string
}

8.3 日志规范

复制代码
必须记录(结构化日志):
1. 调用方法名 + 阶段标识
2. 输入样本规模(候选 sheet 数、候选行数、字段数)
3. 模型名、temperature 等关键参数
4. 校验结果(通过/失败/降级/人工复核)
5. 最终采纳态

禁止记录:
1. 完整敏感业务文本(用摘要/长度/截断片段替代)
2. 客户原始数据全文(用统计指标替代)
3. API 密钥或认证信息

九、错误恢复与降级策略

9.1 错误分类与恢复预算

错误类型 恢复策略 预算 降级路径
LLM 输出被截断 续写(带已输出部分要求继续) 最多 1 次 规则兜底
上下文超长 API 报错 压缩历史后重试 最多 1 次 摘要替换
网络抖动 退避重试(1s → 3s → 10s) 最多 3 次 标记网络异常
列映射校验失败 带失败原因反馈重试 最多 2 次 规则兜底 + REVIEW
JSON 解析失败 尝试修复格式后重解析 最多 1 次 降级模板
工具调用异常 参数修正后重试 最多 2 次 跳过该步 + 异常记录
模型置信度高但结果错误 规则复核拦截 + 换策略重试 最多 2 次 降级 + 人工复核

9.2 降级分级

复制代码
Fatal(任务失败无产物):
  - 核心模板损坏
  - 必要前置依赖完全缺失且不可恢复

Partial-Warning(可导出半成品):
  - 缺少关键输入文件(TB、合同资产汇总)
  - 主链路部分子任务失败

Review-Warning(可导出但需复核):
  - 项目匹配存在歧义
  - LLM 候选与规则冲突
  - 覆盖率不足
  - 数据源冲突

9.3 "模型给了置信度高但错误的路由"的处理

这是实际开发中最隐蔽的问题。解决方案:

复制代码
1. 永远不信任单一置信度
   - LLM 的 confidence 是自我评估,不是客观指标
   - 必须有独立的规则层验证

2. 对抗性验证
   - 对高置信结果做"反向验证":如果这个结果正确,其他约束是否也成立?
   - 例如:匹配置信度 0.95,但检查"该候选是否已被其他项目占用"

3. 多维度交叉验证
   - 不仅看语义相似度,还要看结构位置、数量级、上下文一致性
   - 任何单一维度的高置信都不够

4. 审计追踪
   - 记录所有高置信但被规则拒绝的 case
   - 积累失败模式库,反哺 prompt 和规则优化

十、上下文管理与记忆系统

10.1 上下文压缩策略

时机 压缩手段 保留什么
工具结果超过阈值时 大输出只保留摘要,全文写磁盘 关键统计 + 异常标记
同一工具第 N 次调用时 旧结果替换为占位 "已于第 N 轮读取,结果见摘要"
对话超过 N 轮时 整段历史总结为摘要 当前目标 + 已完成 + 关键决定 + 下一步
跨会话时 仅保留 memory 条目 客商别名、覆盖规则、项目约定

10.2 记忆系统设计

应进入记忆的内容(不可重新推导的知识):

  1. 客商别名:用户明确纠正过的映射
  2. 科目覆盖规则:用户指定的特殊映射
  3. 项目约定:函证阈值、排除规则
  4. 外部资源指针:历史文件路径

使用优先级:

  1. memory 作为方向提示
  2. 当前文件/配置/工具结果为准
  3. 如果 memory 与当前状态冲突,优先相信当前观察

增量开发的记忆衰减:使用频率下降时,逐步清空旧的知识,避免过期 memory 误导。

10.3 系统提示词分层架构

复制代码
Layer 1: 核心身份(固定)
  "你是审计助手,负责合同资产底稿编制。"

Layer 2: 工具列表(固定)
  各工具的 schema 和调用约束

Layer 3: Memory(跨会话,可选)
  "该客户上次纠正:'小马科技' = '小马快跑科技有限公司'"

Layer 4: 当前环境(每轮动态)
  "当前结果文件:xxx.xlsm,42行12列,存在3处异常"

Layer 5: Reminder(条件追加)
  "注意:上次匹配尝试因重名冲突失败,请注意单位字段"

十一、工程化 Agent Loop 的适用性判断

11.1 何时用 Agent Loop,何时用固定管线

复制代码
判断维度:
1. 数据结构是否确定?
   - 确定 → 固定管线
   - 不确定 → 需要探索机制

2. 步骤顺序是否固定?
   - 固定 → 固定管线编排
   - 需要动态决策 → Agent Loop 编排

3. 单步失败后是否有明确恢复路径?
   - 有 → 固定管线 + retry
   - 需要换策略 → Agent Loop 的反馈机制

推荐混合策略:
  固定管线编排 + 高歧义节点内嵌 Agent Loop 反馈

11.2 合同资产智能体的适用性分析

复制代码
15 个子任务中:
  - 11 个是纯确定性(固定管线)
  - 3-4 个有实质性不确定性(嵌入 inferWithFeedback)
  - 0 个需要自由式 Agent Loop

结论:固定管线 + 高歧义节点内嵌反馈,而非全链路 Agent Loop

11.3 不应引入 Agent Loop 的地方

  1. 子任务编排顺序:合同资产底稿编制就是固定流程
  2. 金额计算和写回:纯确定性操作
  3. 异常收集和状态判定:规则状态机
  4. 函证抽样和替代测试:纯规则逻辑

十二、评估与持续优化

12.1 评估驱动的正循环

复制代码
执行 → 评估 → 错误分类 → 修正 spec/prompt/规则 → 再执行
                  │
          ┌───────┼───────┐
          ▼       ▼       ▼
        逻辑    遗漏    幻觉
        错误    错误    错误
          │       │       │
          ▼       ▼       ▼
      修正规则  补充约束  加强校验

错误类型分类:

  • 逻辑错误:规则理解偏差 → 修正规则表达
  • 遗漏错误:边界条件未覆盖 → 补充约束和测试
  • 歧义错误:多解释空间 → 收紧 prompt 约束
  • 幻觉错误:无中生有 → 加强输出校验和事实核查

12.2 把生成评估从"感觉"变成"可计算"

复制代码
将输出结果分成数个命题 → 逐命题判断真伪 → 不达标就重来

评估指标:
1. 结构完整率:输出字段数 / 期望字段数
2. 数值准确率:正确数值 / 总数值
3. 匹配准确率:正确匹配 / 总匹配
4. 降级合理率:应降级且降级 / 应降级总数
5. 覆盖率:已处理项 / 总项
6. 异常捕获率:已识别异常 / 实际异常

12.3 对抗性测试设计

因为大模型"容易顺着话茬走",必须设计中立、对抗性或结构化提问:

复制代码
1. 故意提供矛盾信息,看模型是否能识别并标记
2. 提供"看似合理但实际错误"的候选,看规则层是否能拦截
3. 提供边界 case,看是否能正确降级而非强行给出结果
4. 模拟"前几轮正确、第 N 轮偏移"场景,验证长对话稳定性

十三、技术路线演进

13.1 当前阶段:harness + 通用模型

复制代码
at first we harness, and then, we will make a more suitable model

阶段 1: harness(当前)
  → 工具集 + 知识接入 + 约束层 + 评估与重试层
  → 获得验证和认可

阶段 2: harness + 微调
  → 用评估数据微调 prompt / 适配层
  → 降低 token 消耗,提高一致性

阶段 3: harness + 轻量垂直模型
  → 对高频判断节点用小模型替代
  → 成本和延迟优化

13.2 当前工程瓶颈的本质

理论瓶颈(Transformer 固有):

  • 自回归架构的 O(n²) 复杂度
  • 固定上下文窗口的硬约束
  • 概率生成导致的不可确定性

工程瓶颈(正在解决的):

  • 如何让有限的上下文包含最有效的信息(RAG、压缩、摘要)
  • 如何用更少的 token 传递相同的信息(结构化输出、schema 约束)
  • 如何在不重复传全文的情况下保持多轮一致性(记忆系统)

13.3 代码库知识索引

复制代码
代码库也可以做知识索引(Graph RAG 思路):
- 模块之间的依赖关系 = 图的边
- 每个模块的职责摘要 = 节点属性
- 增量开发时只加载相关子图
- 使用频率下降时逐步清空旧节点

实现:设计文档 + 增量日志 + 按需加载
  → 不是每次全读代码
  → 而是读一遍设计文档,每次开发做增量日志

十四、开发检查清单

14.1 每个 LLM 判断节点的检查清单

  • 是否有明确的 system prompt 约束(含越权禁止)
  • 是否有结构化 user payload(可序列化、可快照)
  • 是否有 Zod schema 校验
  • 是否有 fail code 分支
  • 是否有降级策略(失败时的具体退路)
  • 是否有规则复核层(不盲信 LLM 输出)
  • 是否有日志记录(输入规模、模型参数、校验结果、采纳态)
  • 是否有 stub 测试(不依赖真实模型也能测试所有分支)
  • 是否限制了最大重试次数(不是无限循环)
  • 是否保证了失败不阻断主链路

14.2 每个子任务的检查清单

  • 输入来源是否明确且可追溯
  • 输出结构是否有类型定义
  • 异常出口是否完备(不只是成功路径)
  • 降级行为是否定义清楚
  • 状态对整体任务的影响是否明确
  • 是否有独立的单元测试覆盖
  • 是否有脏数据样本测试
  • 工具结果是否回馈下一步决策

14.3 每次发布的检查清单

  • 正常流样本全通过
  • 脏数据样本正确降级
  • 回归测试无破坏
  • 模板保真验证通过(strict 模式至少跑 1 组样本)
  • 覆盖率报告生成
  • 异常码字典更新
  • 决策追溯日志可查询
  • 任务板结果展示正确
  • 共存测试通过(不影响其他智能体)

十五、从项目深度到简历叙事

15.1 真实问题与解决方案对照表

真实问题 根因分析 解决方案 验证方式
工具调用没全召回 上下文过长导致模型忽略部分工具 上下文压缩 + 工具列表精简 + 分阶段注入 工具覆盖率测试
多轮记忆在长对话里丢失 上下文窗口限制 + 信息密度下降 摘要替换 + 关键决策显式保留 + 任务板锚定 第 N 轮仍能引用第 1 轮决策
模型给了置信度高但错误的路由 模型自评不等于客观准确率 独立规则验证层 + 对抗性交叉验证 高置信错误路由的拦截率
表格异构导致列定位失败 合并单元格/多行表头/命名变体 结构标准化前置 + 三级回退定位 + LLM 候选 脏数据样本集通过率
半成品需求与完成态冲突 缺输入时系统不知道能做到什么程度 覆盖率量化 + 状态限制 + 显式暴露未覆盖区域 PARTIAL 状态判定准确率
LLM 判断与规则结果冲突 概率输出与确定性规则的设计差异 规则层拥有最终裁定权 + 冲突进异常清单 冲突场景自动化测试

15.2 项目深度的体现

复制代码
不是"我用了 LangChain 做了一个 agent"
而是:

1. 约束场景:审计底稿编制------信息不完整、工具调用、多轮记忆、错误路由
2. 设计决策:固定管线 + 高歧义节点嵌入反馈------为什么不用全 Agent Loop
3. 工程挑战:
   - 表格异构处理(合并单元格、多行表头)
   - LLM 高置信错误的发现与拦截
   - 半成品输出的覆盖风险显式化
   - 工具结果回灌决策链路
4. 可验证成果:
   - 脏数据测试集通过率
   - 决策追溯链路完整性
   - 降级行为正确率
   - 模板保真门禁

十六、设计决策原则总结

  1. 不确定性在哪里,反馈机制就嵌在哪里------不为确定性逻辑引入不必要的 LLM 调用
  2. 规则优先,LLM 辅助------LLM 输出必须经过规则校验,校验不通过时降级而非盲信
  3. 反馈比重试更有价值------带上"上次为什么失败"的信息重试,比无脑重试效果好一个量级
  4. 成本与延迟是硬约束------每一轮 LLM 调用都有 token 成本和延迟,必须在可控范围内
  5. 可测试性不能牺牲------嵌入反馈机制的节点仍需可 mock、可断言、可独立测试
  6. 失败必须可降级------任何 LLM 增强节点失败,都不应阻断主链路
  7. 覆盖风险必须显式------不能因为系统没报错就认为结果完整
  8. 可追溯是底线------每个决策点的输入/输出/依据都要可回查
  9. 允许试错但限制代价------harness 要允许探索循环,但每次探索有预算上限
  10. 先软约束,再硬优化------先用 harness 证明可行,再考虑微调或垂直模型

附录 A:合同资产智能体 LLM 节点优化路径

基于本范式,合同资产智能体下一阶段的 LLM 优化路径:

复制代码
当前状态:纯规则主链路已闭环,LLM 节点尚未接入

Phase 1: inferWithFeedback 基础设施
  - 实现标准化的反馈重试模式
  - 建立 LLM 调用的日志与追溯框架
  - 建立 stub client 测试基线

Phase 2: 高歧义节点逐个接入
  - inferTbNetRowCandidates(TB 净额行识别)
  - inferProjectMatchCandidates(项目名消歧)
  - inferWorkbookSheets(Sheet 定位回退增强)
  - inferColumnMapping(列映射推断)

Phase 3: 说明性文本生成
  - buildContractAssetNotes(异常摘要/编制说明)
  - 固定模板降级保底

Phase 4: 评估与迭代
  - 收集 LLM 节点的实际准确率
  - 识别"规则够用不需要 LLM"的场景(移除不必要调用)
  - 识别"LLM 帮助显著"的场景(强化 prompt 和校验)

附录 B:与学习路径的关联

学习主题 在本范式中的映射
superpower / brainstorm 问题发现与解决方案的发散式探索
纵横分析法 跨智能体的适用性分析(横向)+ 单智能体内的深度设计(纵向)
skill 可阅读重构 本文档本身即为"按可阅读方式重构"的范式整理
Graph RAG / 知识索引 代码库知识索引 + 增量开发的记忆管理
memorix 记忆系统 记忆衰减 + 增量日志 + 按需加载
harness 方向论 第十三节技术路线演进
评估正循环 第十二节评估与持续优化

补充专题:智能体范式与 Harness 工程

货币资金智能体 --- 智能体范式与 Harness 工程深度解读

这是《学习文档》的进阶篇。如果说《学习文档》回答的是"它是怎么做的",这一篇回答的是 "为什么这样做、它教会了我们什么、把这套方法迁移到下一个智能体上需要注意什么"。

阅读对象:已经看过《学习文档》、准备亲手设计或改造一个 SDK 原生智能体的开发者。


第一部分 关键概念深度阐述

下面这 8 个概念是理解整个系统的"承重墙"。每一个都不只是一个名词,而是一种设计取舍。

1.1 Agent Loop ------ 智能体的"心跳"

是什么

Agent Loop 是 LLM 与工具之间的多轮自动循环:

复制代码
┌─────────────────────────────────────────────────┐
│  while not done:                                │
│    1. 把 system + messages 送给 LLM 流式生成     │
│    2. 流里同时出 text / thinking / tool_use     │
│    3. 解析响应:有没有 tool_use?               │
│       ├─ 没有 → 收尾(done)                    │
│       └─ 有  → executeTools()                   │
│              → 把 tool_result 追加到 messages   │
│              → 回到第 1 步                       │
└─────────────────────────────────────────────────┘
为什么这是"范式",而不是"实现"

传统编程是 "开发者写流程,程序按流程跑" ;Agent Loop 是 "开发者给工具,LLM 自己决定调用顺序"。这意味着:

  • 你不再写 if 用户上传Excel → 解析 → 出报告 的硬编码
  • 你只需注册 read_table、bash_run、fs_write 等工具,LLM 看到任务后自己规划:先 read_table 探查 → 再 bash_run 跑 Python → 最后 fs_write 出底稿
  • 代码的复杂度从"流程编排"转移到了"工具设计"和"提示词设计"
实践经验
经验 说明
工具粒度要"原子化" 一个工具只做一件事。read_table ≠ read_and_analyze_table。粒度太粗,LLM 失去组合能力;粒度太细,LLM 会调爆
每个工具必须有清晰的副作用边界 attributes: { readonly: true, noEffect: true }(如 conclude)让 SDK 知道这个调用不需要走权限审批
工具返回值必须信息化 不要只返回 { ok: true }。要返回结构("找到 3 张表,分别是..."),让 LLM 下一步有依据
不要让工具"思考" 工具就该机械执行。决策要留给 LLM,不要把 if-else 逻辑下沉到工具里------那就退回到了传统编程

1.2 invoke / Agent / Session / agentId ------ 四个相关但不同的概念

新人最容易搞混的就是这四个词。它们的关系:

复制代码
Session(会话,前端概念)
   ├─ 多个 invoke(一次"用户发一句 → 智能体回完")
   │       ↓
   └─ 一个 Agent 实例(SDK 概念,agentId = "cce-${sessionId}")
           ├─ messages[](完整历史,跨 invoke 持久)
           ├─ template(系统提示词,每 invoke 浅克隆)
           └─ Store(自动持久化)
概念 谁定义 生命周期 标识
Session 项目(前端) 用户开关会话 sessionId
invoke 项目(编排层) 一来一回 无独立 ID
Agent SDK 进程内可恢复 agentId(本项目用 cce-${sessionId})
LLM 调用 SDK Agent Loop 内部 一轮(毫秒级) 无
为什么 agentId 要加前缀

cce-${sessionId},加了 cce-(cash-and-cash-equivalents 缩写)前缀。原因:

  • 同一个 session 可能切换不同类型的智能体。如果都用 sessionId 作 agentId,SDK Store 会把不同类型的对话混在一起,恢复时彻底乱套
  • 加前缀等于给 Store 分桶,每种智能体一个桶

泛化经验 :任何使用 SDK Store 的智能体,都应该用 <agent-type>-<sessionId> 格式做 agentId。这是 SDK 用法的隐性约定。


1.3 系统提示词的"静态段 / 动态段"模型

概念

把 system prompt 分成两层:

层 性质 来源 示例
静态段 不变 代码常量 "你是货币资金审计智能体..."
动态段 可变,运行时注入/替换 工具调用 + Runtime Map <workDir>、<conclusion>、<loaded_skills>
为什么必须分两层

如果只有静态段,LLM 永远不知道:

  • 当前在哪个工作目录(环境信息)
  • 之前几轮做了什么(任务记忆)
  • 加载了什么领域知识(按需扩展)

如果一股脑全塞静态段,token 爆炸,且无法跨轮变更。

为什么动态段用 XML 标签
复制代码
<conclusion>
任务情况:完成银行存款分析,下一步函证核对
</conclusion>

而不是 JSON 或 Markdown。原因:

  • 正则替换简洁可靠 :/<conclusion>[\s\S]*?<\/conclusion>\s*/
  • LLM 对 XML 标签遵守度高(Anthropic 官方推荐)
  • 与 SDK 的 <skills_system> 注入风格一致
  • 可嵌套 :<loaded_skills><skill name="x">...</skill></loaded_skills> 自然成立
实践要点
要点 说明
每个动态段一个标签 不要混用,便于独立替换
替换前先正则探测 找得到就替换,找不到就追加。两条路径都要写
跨 invoke 持久 → Runtime Map 不要塞 SDK Store;Store 是给 messages 的,不是给 system 的
跨进程持久 → 落盘 比如 vision_subagent 的 progress.json
session 隔离 → 每实例克隆 template 详见下一节

1.4 Template Registry 与浅克隆陷阱

陷阱场景
复制代码
// ❌ 直接修改共享模板
agent.template.systemPrompt += '<conclusion>...</conclusion>'

这一行代码看起来人畜无害,实际上会引发跨 session 数据污染:

复制代码
Runtime(单例)
  └─ TemplateRegistry(单例)
        └─ template { systemPrompt: "..." }   ← 共享对象!
              ↑
              ↑ resumeOrCreate 内部 this.template = registry.get(...)
              ↑    
       Agent A.template === Agent B.template ← 同一个引用

Session A 的 conclude 工具改了 systemPrompt,Session B 也跟着变了。客户的银行流水信息可能就这样泄漏到隔壁项目里去。

正确解法:浅克隆 + Runtime Map
复制代码
// ✅ 在 invoke 开始时切断引用
const sharedTemplate = agent.template
agent.template = { ...sharedTemplate }  // 浅克隆产生新对象

// 然后从 Runtime Map 恢复 session 自己的动态段
const stored = this.sessionConclusions.get(agentId)
if (stored) {
  agent.template.systemPrompt += `<conclusion>${stored}</conclusion>`
}
为什么浅克隆够用?

AgentTemplateDefinition 是扁平对象:

复制代码
{
  id: string,             // 不可变
  systemPrompt: string,   // ← 字符串是值类型,赋值=替换引用
  tools: string[] | '*',  // 数组,但本智能体不修改
  permission: ...
}

浅克隆 { ...x } 创建新对象,赋值 clone.systemPrompt = '...' 不会触及原对象。所以只要不修改嵌套字段,浅克隆就够了。

泛化经验

任何"由工具修改 Agent 状态"的设计,都要先问一句:"这个状态是 Agent 实例独有,还是 Runtime 单例共享?" 共享对象上的修改一定要先克隆。


1.5 Skill ------ "按需加载的领域知识包"

与"系统提示词里直接写 SOP"的对比
方案 优点 缺点
静态写在 system prompt 简单,每次都看得到 token 爆炸;不同任务用不同 SOP 时浪费
Skill 按需加载 元数据预注入,全文按需取 实现复杂;LLM 可能忘记加载

货币资金智能体选的是后者。原因是审计场景下 SOP 文档非常重------一个银行函证控制表的 SOP 几千字,加上 references 几万字。每个会话都全量塞进去,单次 LLM 调用就要几万 token。

三段式加载链路
复制代码
启动期
  SkillsManager 扫描 resources/skills/cash-and-cash-equivalents/
      ↓
  抽取每个 SKILL.md 的 frontmatter(name + description)
      ↓
  注入 <skills_system> 到 system prompt(仅元数据,几百字)

LLM 看到
  "我看到有 'bank-confirmation-control-schedule' 这个 skill,描述是...,
   现在用户问银行函证控制表,那我应该加载它"

运行期
  LLM 调 load_enhanced_skill(skill_names=["bank-confirmation-control-schedule"])
      ↓
  工具调 SkillsManager.loadSkillContent() 读全文
      ↓
  全文包成 <loaded_skills><skill>...</skill></loaded_skills>
      ↓
  注入到 systemPrompt(增量更新,老的 <loaded_skills> 被替换)
      ↓
  写入 sessionLoadedSkills Map(跨 invoke 持久)
关键设计点
点 解读
元数据 vs 全文分离 元数据预注入是"目录",全文按需加载是"翻书"。LLM 先看目录决定翻哪本
覆盖更新而非追加 load_enhanced_skill(skill_names=["A"]) 之后再调 ["B"],A 会被卸载,只剩 B。避免 LLM 不停堆叠
空数组卸载 skill_names=[] 表示清空。让 LLM 主动管理上下文
references 不预加载 SKILL.md 全文加载了,但 references/ 仍按需用 bash_run 读。避免一次性全爆
实践经验:写一个好 Skill 的三条铁律
  1. frontmatter 的 description 是 LLM 决定加载的唯一依据------一定要写得具体("用于编制银行函证控制表,包括从明细表提取数据..."),别写"通用工具"这种废话
  2. SKILL.md 主体应该是"SOP 索引 + 步骤摘要",而不是"所有事实性知识的拷贝"。事实性知识放 references/,让 LLM 按步骤读
  3. 每个 references/*.md 应当独立可读------LLM 通常只读一份,不会通读全部

1.6 双存储模型 ------ "上下文真相"与"展示真相"分离

问题:为什么 risk-assessment 用一份存储,cash-and-cash 用两份?

risk-assessment 的做法(旧路):

复制代码
session_messages 表
   ↓ 取出 → 转成纯文本 → 拼到 prompt
LLM

cash-and-cash 的做法(新路):

复制代码
SDK Store(messages[],含 tool_use/tool_result)
   ↓ 自动恢复
Agent.messages
   ↓
LLM

完成后单向同步:
Agent.messages → AgentPersistedMessage → session_messages(前端展示)
为什么不能合二为一?

如果让 SDK 直接读 session_messages 作为上下文:

问题 后果
格式不同:SDK 用 ContentBlock(text/tool_use/tool_result),项目用 blocks(main_text/thinking/tool) 转换层成本高、易错
tool_use 与 tool_result 必须配对 :Anthropic API 要求 tool_use_id 严格匹配 转换时只要一个 toolCallId 错位,整轮请求就被 reject
生命周期不同:Agent 内部还可能压缩、合并消息 同步双向写会导致竞态
单向同步的核心原则
复制代码
"Agent 上下文的真相 = SDK Store"
"前端展示的真相    = session_messages 表"

两者通过 MessagePersistenceAdapter 单向同步,
绝不让 session_messages 反向影响 SDK Store。
实践经验:什么时候双存储是合理的?
  • ✅ 当 SDK 提供完整的消息生命周期管理(恢复、压缩、持久化)
  • ✅ 当前端展示格式 ≠ LLM 消费格式
  • ❌ 当你只是为了"省一次写"------那不值得。直接单存储,自己处理转换

1.7 EventBridge ------ 异构事件系统的"翻译机"

上下游事件模型对比
SDK EventBus(三通道) 项目 AgentStream(单通道 SSE)
progress:text_chunk / think_chunk / tool:start / tool:end data 事件,chunk 字段含 type 区分
control:暂停/恢复/取消 无对应
monitor:监控用,不影响主流 无对应
复制代码
SDK 事件                          项目事件
  text_chunk_start         →       text-start
  text_chunk(delta)        →       text-delta
  text_chunk_end           →       text-end
  think_chunk*             →       thinking-*
  tool:start               →       tool-call
  tool:end                 →       tool-result
  done                     →       complete
为什么需要翻译

不翻译会怎么样?

  • 前端要重写:Cherry Studio 前端的 SSE 解析、消息块渲染都基于 AgentStream。如果直接发 SDK 事件,整套渲染管线得重写
  • 新旧智能体不能共存:risk-assessment 已经用 AgentStream,新智能体如果发别的格式,前端要分支判断
  • 职责模糊:把"事件翻译"放在前端意味着前端要懂 SDK,违反分层
设计原则

"统一对外接口":不同 Runtime 内部实现可以差异巨大(自写 ReAct vs SDK Agent vs 工作流引擎),但暴露给上层的事件流必须是同一种格式。

这就是 EventBridge 的全部价值------把内部异构性吞进去。

实践要点
要点 说明
事件映射要可逆 调试时能从前端事件反推 SDK 事件,便于排查
错误事件要兜底 任何 SDK 抛异常都翻译成 {type:'error'},再补一个 {type:'complete'},否则前端永远 loading
不要丢事件 EventBus 用 timeline + cursor 保证有序,订阅端必须 await 完所有迭代再 stop

1.8 Hook 机制 ------ 工具调用前后的"哨兵"

Hook 是什么
复制代码
{
  preToolUse: (call) => {
    // 工具执行前。可以:
    // - 改写参数(call.args.cmd = ...)
    // - 拒绝执行(return { allow: false })
    // - 记录监控
  },
  postToolUse: (outcome, ctx) => {
    // 工具执行后。可以:
    // - 校验结果
    // - 触发提醒(agent.remind())
    // - 副作用(重置计数器)
  }
}
为什么 Hook 是范式级的设计

传统做法是把这些逻辑写进工具内部。问题:

写在工具里 写在 Hook 里
工具变得臃肿,关注点混杂 工具保持原子,纯执行
不同工具的横切逻辑(命令改写、合规检查)要复制 一处定义,全工具生效
难以为工具添加监控 Hook 天然是观察者
测试工具时要 mock 监控 工具与 Hook 解耦,独立测试
货币资金智能体的 Hook 全景
复制代码
preToolUse
  ├─ bashCommandRewriter     ← 改 bash_run 的 cmd(python -c → 写脚本)
  └─ recordToolUse           ← 给 SkillComplianceMonitor 喂数据

postToolUse
  └─ onSkillLoaded           ← 检测到 load_enhanced_skill 成功 → 触发 remind
                              + 重置 33 轮窗口
实践经验:Hook 该写什么、不该写什么
该写 不该写
横切关注点(监控、日志、改写、重试) 业务逻辑(应该在工具里)
跨工具的共性逻辑 单工具特有的处理
防御性兜底(参数补全、格式归一) LLM 应该自己想清楚的事

黄金法则:Hook 是"基础设施",工具是"业务"。基础设施不该知道业务细节,业务也不该重复造轮子。


第二部分 智能体范式的学习经验

下面这一部分是踩坑后才懂的事。它不是"应该怎么做"的清单,而是"做错了会怎样"的教训。

2.1 经验一:智能体不是"更聪明的程序",而是"会做决策的员工"

心智模型转换

刚接触智能体的开发者最容易犯的错是 "用写程序的方式写智能体":

  • 用 if-else 限制 LLM 的下一步动作
  • 在 prompt 里写"步骤一做 X,步骤二做 Y",把流程钉死
  • 工具内部判断"如果 LLM 给的参数不对,自己补全"

这套做法表面上让智能体"更可靠",实际上:

  • ❌ 牺牲了 LLM 的灵活性(它可能有更好的解法)
  • ❌ 流程一变就要改代码(违背"自主"的初衷)
  • ❌ 让代码越来越像传统业务系统

正确的心智模型是 "实习员工":

维度 程序员视角(错) 雇主视角(对)
给指令 写死流程 给目标 + 边界
给资源 暴露 API 给工具 + SOP(Skill)
出问题 调试代码 给反馈、改 SOP
期待 100% 确定性 概率上正确,配合 harness 兜底
实践转化

写一个新智能体时,先回答三个问题:

  1. 它的角色是谁?(写进 system prompt 静态段)
  2. 它能用什么工具?(注册到 ToolRegistry)
  3. 它怎么知道做什么?(Skill 系统 / instructions)

而不是先问 "它的状态机有几个节点"。


2.2 经验二:上下文是稀缺资源,必须像 GC 一样管理

上下文的"四类敌人"

LLM 上下文窗口有限(即使是 1M token 的 Claude,实际有效注意力也会随长度衰减)。让 LLM 能稳定工作的第一原则是:保持上下文干净。

四类常见污染:

类型 例子 危害
巨型 tool_result read_table 直接返回 10 万行数据 后续每轮都要重新读,token 爆炸
图片 / Base64 视觉调用直接把图片塞回主上下文 一张图轻松吃 5k token
冗长思考链 模型 reasoning 不压缩 几轮后 thinking 占了 60%
重复元数据 每轮都在 prompt 里重复"你是审计师..." 静态段重复 N 次
货币资金智能体的应对

每一类都有专门的设计:

敌人 应对
巨型 tool_result fs_write 写脚本,bash_run 执行------结果存盘,对话只回传文件路径与摘要
图片 vision_subagent 隔离------图片只在子智能体上下文里,主智能体只看到子智能体的结论文本
冗长思考 SDK ContextManager 自动压缩(compressToTokens: 60k)+ DeepSeek 的 stripFromHistory
重复元数据 静态段不重复;动态段用 XML 标签,工具修改时 替换而非追加
实践经验:上下文预算思维

设计每个工具时,问自己:

"这个工具的输出会进入主上下文吗?如果会,最坏情况会有多大?"

如果答案是"可能很大",那么必须做以下之一:

  • 把大输出落盘(fs_write),返回路径
  • 用子智能体隔离(vision_subagent 模式)
  • 在工具层做摘要(artifacts 引用而非内联)

2.3 经验三:让 LLM 自己做决策,但用 Hook 兜底

矛盾点

智能体范式要求 LLM 自主决策,但 LLM 不可靠:

  • 它会忘记加载 Skill
  • 它会重复犯同样的错
  • 它会在死胡同里转圈

解法不是"剥夺它的决策权",而是"在它出错时悄悄拉一把"。

货币资金智能体的"Nudge 体系"

这套设计非常精巧,值得仿照:

复制代码
LLM 调用 load_enhanced_skill 加载 SOP
    ↓
PostToolUse Hook 触发 agent.remind()
    "skill 已加载,记得读 references/ 目录的具体步骤"
    ↓
LLM 接到 system-reminder(不是 user 消息!优先级高于 user)
    ↓
LLM 按提醒去读 references

------继续工作 33 轮------

SkillComplianceMonitor 检查滑动窗口
    "最近 33 轮工具调用,没有任何一次读 references"
    ↓
触发 agent.remind()
    "请按 SOP 走,事实性知识在 references 目录"
    ↓
LLM 收到,回到正轨
关键设计点
点 解读
用 remind() 而非 user 消息 system-reminder 优先级最高,不会被 ContextManager 压掉
基于事实而非猜测 滑动窗口检查的是"调用记录",不是"看 prompt 里有没有提到 references"------前者可验证
熔断保护 33 轮窗口检查 + 88 轮强制提醒,两道闸门
不阻塞 LLM remind 是异步的提示,LLM 仍然可以选择忽略(罕见但允许)
实践经验:Nudge 设计的三原则
  1. 可观测:监控的指标必须是工具调用记录、文件读写等可验证事件,不是"prompt 里写了什么"
  2. 温和:用 remind 而非强制------LLM 可能有更好的判断
  3. 递进 :先即时提醒,再窗口检查,再强制熔断。给 LLM 自我纠错的机会

2.4 经验四:嵌套智能体是"复杂任务的杀手锏"

什么时候需要嵌套?

不是越多智能体越好。嵌套的代价是:

  • 多一倍的 LLM 调用成本
  • 调试复杂度增加(两个 loop 嵌套)
  • 错误传播路径变长

但有些任务没有嵌套就做不了。判别标准:

特征 说明
大量重复处理 处理 50 张银行回函(vision_subagent Mode 3)
不同的上下文需求 主任务关心结构化数据,子任务关心图片像素
需要独立的"工作记忆" 子任务的探索期需要清空重启
结果是"小而结构化"的 子任务返回结构,不污染主上下文

vision_subagent 完美命中所有四条。

嵌套与并行的区别
复制代码
嵌套(vision_subagent)           SDK 的 delegateTask(并行)
主智能体 ─→ 调工具 ─→ 子 Loop      主 ─┬─→ 子 1
            (阻塞)                    ├─→ 子 2  (同时跑)
            返回结果                   └─→ 子 3
                                       聚合

货币资金选嵌套,是因为 vision Mode 3 需要主智能体一直挂着,等 vision 子智能体跑完一整个 CSV 再回来。如果改成并行,主智能体得自己写 join 逻辑,复杂度反而升高。

实践经验:嵌套智能体的三个必备设计
  1. 超时与心跳 :子智能体可能跑很久,必须有独立超时(calcTimeoutMs)+ 心跳保活
  2. 可恢复的进度:子智能体崩了怎么办?用 progress.json 落盘,下次能续作
  3. 结构化返回 :子智能体不能返回"我做完了",要返回 {processed_codes, total_images, summary_csv_path} 让主智能体能续作

2.5 经验五:状态机 + 双阶段循环 = 处理批量任务的范式

vision_subagent Mode 3 的设计值得专门拎出来讲。它解决的是 "用有限上下文处理大量同质任务" 这个普遍问题。

朴素做法的失败
复制代码
循环 50 次:
  load_image(i)
  VLM 分析 → 提取数据 → 追加 CSV

跑到第 10 张图,对话历史里有 10 张图的 base64 + 10 次分析记录,上下文炸了。

Mode 3 的状态机解法
复制代码
阶段 EXPLORE(采样 6 张图)
   ↓ VLM 学会了:表头是什么、关键信息在哪
   ↓ 调 conclude_and_compact 工具
       ├─ Hook 校验 CSV 真的写了
       ├─ 写 progress.json
       └─ 清空 messages,只留 system prompt
   ↓
阶段 EXECUTE(每张图独立处理)
   ↓ load_image → 提取 → 追加 CSV
   ↓ 每 N 张再 conclude_and_compact 一次(清上下文)
   ↓
阶段 DONE(processed_codes == total_images)
设计精髓
设计 解决的问题
EXPLORE / EXECUTE 两阶段 探索时需要全文样本对比;执行时只关心当前一张
conclude_and_compact 工具 让 LLM 主动声明"我学会了",触发上下文重置
PreToolUse Hook 校验 CSV mtime 防止 LLM 谎称"我处理完了"------必须有真实输出
progress.json 落盘 重置 messages 后,下一轮还能从磁盘恢复"已处理列表"
CSV 直接追加而非内存累积 数据持久化在文件里,不依赖上下文
这是一种"范式"而不是"技巧"

当任务是"对 N 个相似对象做同样处理 + 生成结构化输出"时,用"探索期建模 + 执行期单元处理 + 进度落盘"的三段式。

不仅 vision Mode 3 适用。下一个智能体如果要处理 100 张发票、500 条银行流水、200 个合同------都可以套这个范式。


2.6 经验六:失败是可观测的,不是 silent 的

反例:silent failure

裸 LLM 的常见失败:

  • 模型一次 fetch failed,整个 loop 崩,前端永远 loading
  • 工具执行报错,LLM 假装没看见继续编
  • 上下文超长,模型默默截断,结果失真

这些失败 没有异常、没有日志、没有提醒------你不打开 DevTools 永远不知道。

货币资金智能体的"可观测性栈"
复制代码
loggerService(每个模块带 context)
   ↓
SDK Monitor 通道(tool_executed / error / context_compression)
   ↓
ModelRetryHandler(吃掉暂态错误,但记日志)
   ↓
SkillComplianceMonitor(异常调用模式触发提醒)
   ↓
EventBridge 兜底 error → complete(保证前端不卡住)

每一层都对失败有响应:要么自愈,要么记录,要么向上抛。没有任何失败被吞。

实践经验:可观测性的三个层次
层次 例子 作用
日志 logger.error('Agent loop error', {...}) 事后排查
指标 tool_executed 计数、context_compression 次数 趋势监控
反馈 agent.remind() / 用户可见错误事件 实时纠偏

裸 LLM 是黑盒,加了 harness 才是白盒。


第三部分 Harness 工程的实践观点

"Harness" 这个词来自西方驯马术------给马套上的挽具。它的意思是:给一个本来不可控的东西套上一组装备,让它能稳定干活。

LLM 就是那匹野马。Harness 工程是给它套挽具的工程。

下面是从货币资金智能体提炼出的、可复用的 Harness 实践观点。

3.1 观点一:把 Harness 视为"产品的一部分",不是"调试工具"

错误认知

很多团队的开发顺序是:

  1. 先把核心功能做出来
  2. 上线后出问题,加日志、加重试、加监控
  3. Harness 是"补丁"

这种顺序的问题:harness 是事后的,与核心代码耦合差,加一个补丁多一个隐患。

正确认知

Harness 应该 从第一行代码就是架构的一部分。具体到货币资金智能体:

Harness 组件 何时引入
ModelRetryHandler Runtime 初始化时就 attach
SkillComplianceMonitor constructor 里就实例化
EventBridge 错误兜底 EventBridge 本身的一部分
Hook 注册 invoke 路径上必经环节

它们不是"事后加的层",而是 Runtime 的核心组成。少了任何一个,Runtime 就跑不了。


3.2 观点二:Harness 的设计层次 ------ "防、查、补、断"

四个层次,对应不同的问题类型:

层次 关键词 例子
防 不让错误发生 bashCommandRewriter(提前改写 python -c)
查 发现错误 SkillComplianceMonitor(监控 references 读取)
补 错误发生时自愈 ModelRetryHandler(暂态错误重试)
断 严重错误时熔断 conclude_and_compact 三连失败终止
实践原则

能防就不查,能查就不补,能补就不断。

理由:每一层的成本都比上一层高。能在 Hook 里改写命令,就别让 bash_run 报错;能监控提醒,就别让任务跑废重做;能重试就别让用户看到错误;只有真的没救了才熔断。

货币资金智能体的"四层"分布
复制代码
防:bashCommandRewriter、模板浅克隆、单向同步原则
查:SkillComplianceMonitor、tool_executed 计数器、PreToolUse Hook 校验
补:ModelRetryHandler、agent.remind() 提醒、ContextManager 压缩
断:vision Mode 3 三连失败熔断、超时控制、错误事件兜底

3.3 观点三:Harness 不是越多越好,要有"边际收益"判断

反例:过度 Harness
复制代码
工具调用 → Hook 1 → Hook 2 → Hook 3 → ...
           检查 A      检查 B    检查 C

每个 Hook 都"为了安全"加一个检查。后果:

  • 性能下降(每次工具调用要走 N 个钩子)
  • 调试困难(出错了不知道是哪层 Hook 的问题)
  • 维护噩梦(10 个 Hook 互相影响)
判断 Harness 是否值得加的"三问法"

加任何一个 Harness 之前,先问:

  1. 这个错误真的会发生吗? (不是理论上会,而是实际数据里见过)
  2. 不加这个 Harness,错误的代价是什么? (用户重试一次 vs 数据损坏)
  3. 加了这个 Harness,会不会引入新的复杂度?

货币资金智能体的所有 Harness 都通过了这三问。例如 bashCommandRewriter:

  1. 实际数据里 python -c 多行代码失败率高(docs/数据/bash_run_issues_grouped.json 有记录)
  2. 不加,LLM 会陷入"写代码 → 执行失败 → 重写 → 再失败"的死循环,浪费数十轮
  3. 加了只是一个改写函数,复杂度可控

3.4 观点四:Harness 必须可观测、可配置、可禁用

可观测

每个 Harness 组件都要回答:"它现在在做什么?做了多少次?"

复制代码
logger.info('bash_run command rewritten by hook', {
  agentId,
  original: originalCommand.substring(0, 200),
  rewritten: rewrittenCommand.substring(0, 200)
})

货币资金智能体的所有 Harness 都打 info 级日志。调试时能直接看到 Harness 在工作。

可配置

阈值不应该写死。SKILL_UPDATE_REMINDER_THRESHOLD = 88、CHECK_WINDOW = 33、compressToTokens = 60_000 都应该是可调的。生产环境的最优值与开发环境往往不同。

可禁用

Harness 偶尔会"过度反应",干扰正常工作。比如:

  • ModelRetryHandler 重试一个永久错误
  • SkillComplianceMonitor 在不需要 Skill 的简单任务上反复提醒

设计时要预留 关闭开关。例如通过环境变量、配置项、特殊 prompt 标记。


3.5 观点五:Harness 的实施顺序 ------ "可观测 → 验证 → 防御 → 优化"

不要一开始就上一堆 Harness。正确的顺序:

Phase 1:可观测先行
复制代码
loggerService + Monitor 通道
"我先要知道 LLM 在做什么"

什么都不加,先收集 1-2 周的真实运行数据。看看:

  • 哪些工具调用最频繁
  • 哪些步骤失败率最高
  • 上下文增长曲线如何
Phase 2:基于数据上验证
复制代码
SkillComplianceMonitor + tool_executed 计数器
"针对高频问题做监控"

挑出 Phase 1 数据里的痛点,加监控(不改行为,只观察)。

Phase 3:再上防御
复制代码
bashCommandRewriter + ContextManager 压缩
"把已验证的问题在源头堵掉"

Phase 2 数据证明这个问题真的影响 LLM 完成任务,且改写/重试能解决,再加防御。

Phase 4:最后做优化
复制代码
并行化、缓存、模型分层
"在已经稳定运行的基础上提升效率"

货币资金智能体的优化路线图(见 优化设计.md)正是这个顺序:先 E5(可观测)→ 再 R + A2(验证)→ 再 W1 + A3(防御)→ 再 E2 + E4(并行优化)。


3.6 观点六:Harness 的"反 pattern"清单

写完所有正面观点,列一些典型反 pattern作为警示:

反 pattern 为什么错
在工具内部写监控 工具应该只做事;监控写在 Hook 里
静态 prompt 里写"不要做 X" LLM 大概率忽视;用 PreToolUse Hook 强制阻止
重试无限次 永久错误会让重试器空转;必须设上限(货币资金用 3 次)
吞掉所有异常 隐藏问题,下次出事更难查;必须 logger.error
Harness 之间互相依赖 改一个炸一片;保持 Harness 之间独立
没有可禁用开关 排查问题时无法二分定位
用 user 消息做提醒 LLM 会把它当成新指令;用 system-reminder(remind())
跨 session 共享可变状态 数据污染;用 Map 按 sessionId 隔离

3.7 观点七:Harness 是智能体范式的"成人礼"

最后一个观点,偏哲学但很重要。

三个阶段

观察一个团队/开发者写智能体,往往会经历三个阶段:

第一阶段:天真期

  • "GPT/Claude 这么强,给个 prompt 就好了"
  • 没有 Harness,全靠 prompt 工程
  • 跑 10 次有 6 次成功,已经很满意

第二阶段:恐惧期

  • 上线后发现:用户的怪异 case 让 LLM 各种翻车
  • 开始疯狂加 if-else、加 fallback、加重试
  • prompt 越写越长,代码越来越像传统业务系统
  • 智能体的灵活性逐渐丧失,回到"程序"的形态

第三阶段:成熟期

  • 接受 LLM 是不确定的、概率的、会犯错的
  • 不再试图"消除"错误,而是"管理"错误
  • 用 Harness 在 LLM 周围搭一个"安全网"
  • LLM 在网内自由发挥,错误被网兜住

货币资金智能体处于第三阶段。它的代码量看起来不少,但你会发现:

  • 静态 prompt 很短,没有"步骤一步骤二"的硬编码
  • 工具描述是"做什么",不是"什么时候做"
  • 流程没有写在代码里,写在 Skill 的 SOP 里
  • Harness 在边缘默默工作,不干扰主流程
一句话总结

第一阶段相信 LLM,第二阶段不相信 LLM,第三阶段在不相信的前提下重新相信 LLM。

Harness 工程就是这种"重新相信"的物理基础。


第四部分 总结 ------ 把这套方法迁移到下一个智能体

如果你即将动手做"应收账款智能体""存货智能体"等下一个 SDK 原生智能体,下面是迁移清单。

4.1 必做项(缺一不可)

复制代码
┌──── 注册层 ──────────────────────────────────┐
│ □ AGENT_TYPES 加新类型                        │
│ □ capabilities/definitions/<type>.ts          │
│   └ 注册 PluginBinding(含 skillPaths)       │
│ □ AgentRegistry.register({type, runtime})     │
└──────────────────────────────────────────────┘
┌──── Runtime 五件套 ──────────────────────────┐
│ □ <Type>Runtime.ts (实现 AgentRuntime)        │
│ □ ModelConfigResolver / 复用 cce 的           │
│ □ AppModelFactory / 复用 cce 的               │
│ □ EventBridgeAdapter / 复用 cce 的            │
│ □ MessagePersistenceAdapter / 复用 cce 的     │
└──────────────────────────────────────────────┘
┌──── Skill 资源 ──────────────────────────────┐
│ □ resources/skills/<type>/<skill-name>/       │
│   ├ SKILL.md(含 frontmatter)                │
│   └ references/                                │
└──────────────────────────────────────────────┘
┌──── 工具集 ──────────────────────────────────┐
│ □ bash_run + fs_write(直接复用 SDK)         │
│ □ conclude(视需要)                           │
│ □ load_enhanced_skill(视需要)                │
│ □ 业务专属工具                                 │
└──────────────────────────────────────────────┘
┌──── Harness ─────────────────────────────────┐
│ □ Logger(每模块带 context)                   │
│ □ ModelRetryHandler(暂态错误重试)            │
│ □ ContextManager 配置(maxTokens等)           │
│ □ EventBridge 错误兜底                         │
└──────────────────────────────────────────────┘

4.2 视情况添加

复制代码
┌──── 高级 Harness ────────────────────────────┐
│ □ SkillComplianceMonitor(如果 Skill 加载频繁)│
│ □ 业务专属 Hook(如 bashCommandRewriter)      │
│ □ tool_executed 计数器(防 LLM 走偏)          │
│ □ vision_subagent(如果涉及图片处理)          │
│ □ 状态机循环(如果涉及批量处理)                │
└──────────────────────────────────────────────┘

4.3 慎选项(一定不要复制)

复制代码
┌──── cce 的特殊设计 ──────────────────────────┐
│ ✗ <skills_system> 清理(cce 特有的冲突,新   │
│   智能体如果不冲突就别清)                    │
│ ✗ 88 轮 skill 更新提醒(业务相关,重新选阈值)│
│ ✗ vision Mode 3 状态机(仅适合大批量图片)    │
└──────────────────────────────────────────────┘

4.4 心法

最后留三句话:

1. 先想清楚智能体的"角色",再写代码。 角色不清,工具就乱。
2. 上下文是预算,每个工具都要为它的输出大小负责。 别让一个工具污染整个对话。
3. Harness 不是炫技,是兑现"智能体范式"承诺的工程基础。 没有 Harness 的智能体只是个 demo。


附:推荐阅读路径

顺序 文档 重点
1 学习文档.md 全景理解
2 本文档 范式与 Harness 深度
3 架构设计.md 编排层与 Runtime 协作
4 系统提示词组装方案.md 动态段设计细节
5 技能架构设计.md Skill 注册全链路
6 vision_subagent架构设计.md 嵌套智能体范式
7 工具长时程运行设计.md 心跳与超时
8 优化设计.md 当前架构问题诊断
9 SDK 内部 packages/kode-agent-sdk/docs/ 底层机制

读完这条路径,应该能回答:

  • 为什么货币资金智能体选择基于 SDK 而不是自写?
  • 如果要做应收账款智能体,第一步该改哪几个文件?
  • 当 LLM 在第 50 轮还没读 references 时,到底是哪个组件触发的提醒?
  • vision Mode 3 处理到一半进程崩了,重启后能续上吗?为什么?

如果都能答上,恭喜你已经从"看懂代码"升级到"理解范式"了。


补充专题:Agent 落地讨论与路线图

Agent 落地与 Harness 工程化:从"自主性"到"可验证执行"

基于近期讨论整理,更新于 2026-07。本文讨论的不是某个 Agent 框架怎么用,而是:如何把概率性的模型能力,装进一个可观测、可验证、可恢复、可治理的生产系统。

先给结论

  1. Agent 和 Workflow 不是非此即彼。 更有用的划分是:哪些决策交给确定性代码,哪些决策交给模型。生产系统通常是"确定性骨架 + 局部模型自治",而不是纯工作流或无限循环的纯 Agent。
  2. Agent 的产品能力不等于模型能力。 可近似写成:Agent 系统能力 = 模型 × Harness × 工具/环境 × 上下文 × 验证器 × 运行预算。任何一项接近零,整体成功率都会坍塌。
  3. Harness 不是一层 Prompt 包装。 它是模型之外的执行控制面:状态管理、工具路由、权限、预算、重试、检查点、验收、追踪、回放和人工接管都属于 Harness。
  4. 自主性应放在"路径选择",确定性应放在"边界与验收"。 不必规定 Agent 每一步怎么走,但必须规定它能做什么、花多少、什么叫完成、证据是什么、失败后回到哪里。
  5. 评测不能只看最终回答。 至少同时看最终环境状态、过程轨迹、重复运行的稳定性、成本/时延和安全性。Agent 声称完成不是完成,环境中的可验证状态才是完成。
  6. "只完成一半却宣布完成"不是简单的幻觉问题。 它通常是任务契约、状态表示、终止条件和验收机制共同缺失的结果;换更强模型能缓解,却不能替代工程闭环。

一、Agent 到底是什么:不要争名词,要标注决策权

业内把很多东西都称为 Agent:固定工作流外包一层 LLM、带工具调用的 ReAct 循环、Skills 渐进披露、能长期修改环境的 Coding Agent,甚至多 Agent 协作系统。这些叫法都能成立,但抽象层次不同。

一个更便于工程沟通的连续谱是:

形态 谁控制下一步 优点 主要风险 适合场景
确定性 Workflow 代码/规则 稳定、易审计、易定位 长尾规则膨胀 SOP 清楚、错误代价高
Agentic Workflow 代码控制主干,模型决定局部分支 自主性和可控性平衡 边界设计复杂 大多数企业生产场景
单 Agent Loop 模型动态规划和选工具 灵活、覆盖开放任务 漂移、误终止、成本波动 研究、分析、编码、低风险开放任务
多 Agent 系统 多个模型角色协作/委派 上下文隔离、专业化、并行 协调税、错误传播、评测困难 可清晰分域或可并行的复杂任务

OpenAI 的实用定义强调:模型要能管理工作流执行、动态选择工具、识别完成并在失败时纠正或移交;Anthropic 则明确区分预定义代码路径的 workflow 与模型动态控制过程的 agent。两者真正一致的地方是:判断一个系统是否"Agentic",看模型是否拥有执行路径的决策权,而不是看它有没有聊天界面或 tool call。

因此,与其问"这到底算不算 Agent",不如为每个环节写出一张决策权表:

  • 路由由规则还是模型决定?
  • 参数由模板、模型还是二者共同生成?
  • 终止由模型声明,还是由外部验收器判定?
  • 失败由固定策略重试,还是由模型重新规划?
  • 写操作是否需要策略引擎或人工批准?

这张表比名词更能揭示系统的稳定性、成本和责任边界。

二、Harness:Agent 的"执行操作系统"

Prompt 告诉模型"应该怎么想",Harness 决定系统"实际上允许怎样运行"。可以把 Harness 分成六层:

1. 任务契约层

把模糊 goal 编译成机器可检查的任务契约:目标、范围、约束、交付物、禁止事项、验收标准和停止条件。对长任务,维护显式的任务账本,而不是只依赖对话历史:

复制代码
goal: 完成客户流失分析并生成可复核报告
deliverables:
  - analysis.md
  - query.sql
  - result_snapshot.csv
acceptance:
  - SQL 可执行且无截断
  - 报告中的数值均能映射到结果行
  - 时间范围、口径和过滤条件已披露
status: in_progress
open_items: []
evidence: []

任务拆解的价值不是让步骤全部写死,而是建立进度状态和可局部验收的边界 。模型可以动态修改计划,但不能无证据地把任务从 in_progress 改为 done。

2. 状态与上下文层

上下文窗口不是可靠数据库。长任务需要把状态外置为可恢复的工件:任务清单、决策日志、已验证事实、失败记录、产物索引、版本控制和检查点。

Anthropic 的长时 Agent 实践发现,仅靠上下文压缩不够:Agent 会一次做太多,留下半成品;后续会话也可能看到已有进展便误判全部完成。其有效做法是初始化环境、按增量推进,并通过进度文件和 Git 历史向下一会话交接。这证明了一个重要范式:Context engineering 的重点不是塞入更多文字,而是让模型在正确时机读取正确状态,并把关键状态沉淀到上下文之外。

3. 工具与环境层

工具不是普通 API 的机械暴露。Agent-facing API 应满足:

  • 名称和描述能区分相邻能力,参数有严格 schema;
  • 返回结构同时包含结果、状态、分页信息、错误类型和可引用证据;
  • 写操作支持幂等键、dry-run、事务或补偿操作;
  • 工具集按任务动态披露,避免一次暴露数百个相似工具;
  • 权限沿用用户身份,遵循最小权限,不给 Agent 一个万能服务账号;
  • 不把微服务拓扑直接当作 Agent 的工具拓扑。

最后一点尤其关键。微服务按组织、数据所有权和部署边界拆分;Agent 工具应按用户意图和业务能力设计。一个业务操作若跨十个微服务,应优先由稳定的领域工具封装,而不是让模型每次临场拼十次底层调用。

4. 控制与恢复层

控制面应包括最大轮数、token/时间/金额预算、超时、取消、重试分类、熔断、并发控制、检查点和恢复。

重试也不能一概而论:网络超时可以原参数重试;参数错误应把结构化错误返回模型修正;权限错误不该重试;副作用不明时必须先查幂等状态。复杂流程最好使用 durable execution:每个有副作用的步骤持久化,进程崩溃后从安全检查点恢复,而不是整条链重跑。

5. 验证与终止层

Agent 的 final answer 只能表示"模型想结束",不能作为"任务已完成"的唯一条件。正确的结束协议是:

  1. 模型提交完成声明和证据清单;
  2. 外部验证器检查环境状态与验收断言;
  3. 验证通过才提交 success;
  4. 验证失败则返回缺口,继续修复;
  5. 超预算、连续失败或高风险则进入 blocked/escalated,不能伪装成功。

验证器应优先采用确定性信号:单元测试、数据库断言、schema 校验、静态分析、引用覆盖率、文件存在性、业务状态机。LLM-as-judge 适合评价语义质量、风格或开放性结果,但应有清晰 rubric,并定期用人工样本校准。

6. 可观测与治理层

一次 Agent run 至少需要记录:模型与 Prompt/Skill 版本、输入、计划版本、每次工具调用及耗时、结果摘要、状态变更、重试、预算、验证结果、最终环境状态和人工介入。

追踪的目标不是展示一条漂亮的"思维链",而是回答:

  • 是路由错、工具错、数据错、权限错、模型错,还是验收器错?
  • 哪个版本开始退化?
  • 同类失败占多少业务量?
  • 修复应该落在 Prompt、工具、服务、数据语义层还是模型路由?

应记录可调试的 action/observation、状态和证据,避免依赖或暴露模型私有推理文本。生产 trace 经过脱敏后进入失败聚类,再回流为回归集,这才形成可持续优化闭环。

三、回答讨论中的三个核心问题

A. "怎么知道 Agent 变好了?"

"感觉更聪明"不是指标。应先定义业务效用,再拆成五类指标:

维度 示例指标
结果正确性 task success、业务状态断言、事实/引用正确率
完整性与稳定性 acceptance coverage、同任务多次成功率、pass@k、pass^k
过程质量 工具选择准确率、无效调用率、重试恢复率、步骤/计划偏离
效率 p50/p95 时延、token、工具调用数、单次成本、每成功任务成本
安全与体验 越权率、高风险误操作率、人工接管率、用户纠正率

其中 pass@k 回答"尝试 k 次至少成功一次的概率",偏能力上界;pass^k 回答"连续 k 次都成功的概率",更接近生产可靠性。对于每天大量重复执行的业务,后者往往更重要。

评测体系应有三层:

  1. 组件评测:路由、SQL 生成、检索、工具参数、摘要等单点能力;
  2. 端到端离线评测:在隔离环境运行完整 Agent,以最终环境状态评分;
  3. 线上评测:影子流量、canary、A/B、业务 KPI、人工反馈与事故指标。

数据集也要分层:核心能力集、历史失败回归集、长尾/对抗集、最新生产抽样集。每个 case 应多跑几次并报告置信区间,因为 Agent 输出具有随机性。

需要警惕 benchmark hacking。研究已经发现,一些 Agent benchmark 因任务环境或奖励设计缺陷,可能严重高估或低估能力。基准的 grader 本身也必须测试:是否存在"空回答也通过"、绕过预期路径、污染数据、测试覆盖不足等漏洞。Benchmark 不是一套题,而是一套需要被验证的测量仪器。

另外,比较模型时必须固定 Harness、工具、预算和评分;评估某个系统的最大能力时,则应给它合理优化过的 Harness。Harness 和推理预算会显著改变结果,因此任何 Agent 分数都应写全:model + harness version + tools + budget + dataset + grader。

B. "开放 Agent 怎么稳定,又不写死 Workflow?"

答案是约束边界,不约束路径。保留自主规划,但把系统设计成"自由度预算":

  • 读操作可较开放,写操作按风险逐级收紧;
  • 允许模型选择工具,但只披露当前域的候选集合;
  • 允许动态改计划,但任务契约和验收标准不可自行降低;
  • 允许自我修复,但设置最大重试、成本和时间;
  • 允许提交结果,但必须带 provenance,由外部 verifier 决定是否完成;
  • 可逆、低风险动作自动执行;不可逆、高金额、跨边界动作需批准。

可用一个简单决策原则决定"这一步该写死还是交给模型":

自治价值 = 长尾覆盖收益 - 失败损失 - 验证成本 - 时延/费用 - 合规成本

规则稳定、输入结构化、失败代价高的环节,交给代码;需要语义理解、开放搜索、例外处理的环节,交给模型;容易验证但难以生成的环节,尤其适合 Agent------例如写代码后跑测试。难生成又难验证的高风险任务,不适合直接全自动。

C. "几百个微服务里怎么找该改的项目?"

这不只是 service mesh 或 tool trace 问题,而是需要把业务意图、Agent 动作、分布式调用和代码所有权连成一条证据链:

user_goal → agent_run → plan_step → tool_call → domain_service → trace/span → error → repo/version → owner

落地要点:

  • Agent run ID 注入所有下游调用,贯通分布式 tracing;
  • 工具注册表记录 tool → API/服务 → repo → owner → schema/version;
  • 错误分类区分模型选错工具、参数错、服务失败、数据质量和权限问题;
  • trace 中保留 Prompt/Tool schema/模型版本,支持逐次回放;
  • 以失败聚类和业务影响排序,不靠人工浏览海量轨迹;
  • 用 contract test 检查工具层,避免下游服务变更悄悄破坏 Agent。

如果"一个操作需要模型直接挑选并组合大量微服务",根因通常不是 Agent 数量不够,而是缺少稳定的领域能力层。先做工具合并、命名去重、分域检索和候选集缩小;只有当上下文、权限、所有权或评测边界确实需要隔离时,再拆 Agent。

四、案例:开放数据查询 Agent 如何查全、有据、可恢复

原讨论把"只查一半"归因于 SQL 或工具错误,这只覆盖了一部分。至少要区分五类失败:

  1. 意图不完整:时间范围、指标口径、粒度或过滤条件不明确;
  2. 语义选错:找错表、join 错、把快照当流水、混用指标定义;
  3. 执行不完整:分页、行数上限、超时、采样、截断或部分分区失败;
  4. 综合不忠实:结果是对的,但回答补写了数据中不存在的结论;
  5. 错误终止:只完成子查询,却把局部结果当作最终结果。

推荐的执行协议不是"生成 SQL → 执行 → LLM 看一眼",而是:

  1. 问题规格化:输出指标、维度、时间、范围、口径和待澄清项;
  2. 语义检索:从数据目录、血缘、指标层、历史查询和 owner 注释中找候选;
  3. 生成查询计划:列出需要覆盖的子问题及 completeness 条件;
  4. 静态验证:解析 AST,做只读限制、表/列权限、危险语句、代价预估;
  5. 沙箱执行 :设置资源上限,返回 row_count / total_count / has_more / truncated / query_id / snapshot_time;
  6. 结果验证:检查空值、重复、基数、时间覆盖、聚合守恒和交叉查询一致性;
  7. 证据化综合:每个关键数字绑定 query ID、结果列/行或结果快照;明确假设和未覆盖项;
  8. 完成门禁:只有所有子问题和 coverage assertion 通过,才允许回答"已完成";
  9. 持久化与恢复:保存计划、查询、快照和检查点,失败后从最近安全步骤继续。

OpenAI 内部数据 Agent 的公开实践与此相近:它用组织知识、代码、元数据和历史使用信息丰富数据语义;评测时为自然语言问题配人工 golden SQL,同时比较生成 SQL 和实际结果,而非只做字符串匹配;答案链接到底层结果,并继承用户原有权限。更值得注意的是,他们后来收紧和合并了重叠工具------工具越多不等于能力越强。

"SQL 语法解析器"只能证明语法合法,不能证明业务语义正确;"让另一个 LLM 复核"也不是独立真值。强验证应尽可能落到可执行断言和数据不变量上,LLM 负责补充语义判断。

五、AI Coding:如何发现 TODO、半成品和虚假完成

Coding Agent 是数据查询问题的同构版本:生成代码很开放,但成功通常较容易验证。不能只让模型自评,应使用分层完成门:

  • 需求 → acceptance tests / 行为断言;
  • 代码 → 编译、类型检查、lint、单测、集成测试;
  • 变更 → diff scope、受影响模块和依赖分析;
  • 完整性 → 扫描 TODO、placeholder、空实现、跳过测试和 mock 泄漏;
  • 运行态 → 在临时环境启动并执行关键路径;
  • 交付 → 要求变更摘要、测试证据、已知限制和未完成项;
  • 高风险变更 → code review、安全扫描或人工批准。

关键不是禁止 TODO,而是禁止"未声明的 TODO 与成功状态并存"。真正无法完成时,合法终态应是 blocked 或 partial,并附缺口、原因和恢复点。

对于跨上下文长任务,建议采用:初始化任务账本 → 每次只完成一个可验收增量 → 测试 → 提交/检查点 → 更新进度工件 → 下一会话读取。Anthropic 公开实验中出现的"后续 Agent 看到已有进展就宣布结束",与讨论中的担心几乎完全一致,也说明这不是旧模型才会有的问题,而是长时自治的结构性风险。

六、是否应该拆成多 Agent

不要按微服务数量拆 Agent,也不要把"多 Agent"当作稳定性的默认答案。每增加一个 Agent,都会增加委派错误、上下文丢失、重复调用、延迟、成本和评测组合数。

更合理的拆分信号是:

  • 工具相似度太高,单 Agent 持续选错;
  • 不同子域需要独立权限或安全边界;
  • 上下文互相污染,需要明确隔离;
  • 子任务确实可以并行,且有清晰输入输出契约;
  • 不同任务需要显著不同模型、提示、预算或验收器;
  • 团队所有权本身清晰分域。

拆分顺序建议是:先优化一个 Agent 的工具设计和动态检索,再考虑 manager + specialists;只有在去中心化 handoff 确有价值时才使用对等多 Agent。多 Agent 的收益来自上下文与责任边界,不来自角色名更多。

七、成本、时延、质量与合规如何制衡

"解决方案就是加钱"只在原型阶段部分成立。生产优化应遵循:先用强模型建立质量上界和评测基线,再在可测量的节点降配。 不要一开始就处处用小模型,否则无法区分是架构失败还是模型能力不足。

常见策略:

  • 规则处理确定性任务,小模型做分类/检索重排,强模型做复杂规划与失败恢复;
  • 根据任务难度、风险和验证反馈动态升级模型,而非固定路由;
  • 缓存稳定检索与工具结果,但携带数据版本和过期策略;
  • 合并相邻工具调用,支持批量读和并行读;
  • 把无关历史压缩成结构化状态,不盲目保留全部对话;
  • 以"每个成功任务的总成本"优化,而不是只看单次 token 价格;
  • 高风险动作增加验证和人工介入,低风险可逆动作追求自动化率。

安全方面,Prompt injection 不能只靠另一个分类模型拦截。Agent 同时接触不可信内容和高权限工具时,应采用 source-sink 思维:限制不可信信息可以影响的动作,敏感数据外发或不可逆操作必须有确定性策略、沙箱、最小权限与用户确认。

八、当前主要研究思路

截至 2026 年中,值得持续跟踪的研究和工程主线包括:

  1. 长时任务与时间跨度:能力不再只以单题准确率衡量,而是以某个可靠度下能完成多长的人类工时任务来衡量;长任务中的错误累积、恢复和跨上下文连续性仍是核心难题。
  2. Harness / elicitation 科学:同一模型因工具、上下文压缩、重试、预算和脚手架不同可呈现显著不同能力。模型与 Harness 的贡献如何解耦,是评测中的开放问题。
  3. 过程监督与结果验证:何时只看最终环境状态,何时检查轨迹;如何防止 grader 漏洞,又不惩罚 Agent 找到更优的新路径。
  4. 自适应规划与再规划:plan-and-execute、ReAct、反思/验证循环、搜索和 test-time compute 怎样按任务难度动态分配,而不陷入无效循环。
  5. 记忆与上下文工程:工作记忆、长期记忆、情景记忆如何写入、检索、遗忘和处理冲突;怎样避免记忆污染。
  6. 工具发现与委派:大规模工具下的语义检索、分层路由、动态披露;模型是否知道何时自己做、何时委派,以及如何评价路由质量。
  7. Agent 安全与权限:间接 Prompt injection、数据外泄、身份委托、最小权限、可撤销执行、审计和责任归属。
  8. 真实世界评测:从 GAIA、SWE-bench、WebArena、τ-bench 等任务集走向组织私有分布、动态环境、长期回归与经济指标。

九、大家共同面对、尚未彻底解决的制衡点

自主性 vs. 可预测性

自治越强,越能覆盖未预见路径,也越难穷举测试。当前最佳实践不是消灭不确定性,而是把它限制在可验证、可回滚的区域。

长上下文 vs. 正确信息密度

上下文更长不等于记忆更可靠。全量历史会增加噪声、冲突与"上下文焦虑";压缩又可能丢失关键承诺。结构化外部状态和按需检索比无限堆上下文更稳。

最终结果评测 vs. 过程评测

只看结果允许创新路径,但可能掩盖危险过程;强约束过程便于审计,却可能把 Agent 退化成 workflow。实践中应对结果做强断言,对高风险过程设策略,对普通路径保持弹性。

通用 Agent vs. 垂直 Agent

通用 Agent 复用好、覆盖广,却更难建立完备语义层和验收器;垂直 Agent 更容易可靠,但维护多个系统有成本。常见落点是共用运行时与 Harness,按领域配置工具、权限、知识和 eval suite。

单 Agent vs. 多 Agent

多 Agent 能隔离上下文和权限,却引入协调税。没有清晰契约、独立验收或真实并行性时,拆分通常只是把一个难调试循环变成多个难调试循环。

强模型 vs. 系统工程

强模型能减少大量边缘错误,但无法替代权限、事务、可恢复性与业务验收。反过来,Harness 也无法把能力不足的模型无限"抬升"。二者是乘法关系,不是替代关系。

十、一套可执行的落地路线

阶段 0:先判断是否需要 Agent

若规则清晰、路径稳定、失败昂贵,先做 Workflow;若核心价值来自开放语义判断、长尾和动态工具选择,再引入模型自治。

阶段 1:建立能力上界

选代表性任务和最强可用模型,构建最小单 Agent;定义任务契约和最终状态 grader。此时先证明"做得到",不要急着压成本。

阶段 2:构建 Harness 闭环

加入结构化状态、工具 schema、预算、trace、检查点、验收器、失败终态和回放。把线上失败持续转成回归 case。

阶段 3:影子运行与受控发布

只读 → 建议模式 → 人工批准写入 → 低风险自动写入,逐级放权。按风险而非按功能一刀切。

阶段 4:优化成本和复杂度

根据 trace 找真实瓶颈:模型路由、工具重叠、上下文噪声、服务延迟或验证开销。逐节点替换小模型、缓存、批处理或确定性实现,并用 eval 防回归。

阶段 5:再决定是否多 Agent

只有单 Agent 的工具混淆、上下文、权限或并行瓶颈被数据证实时再拆分;每个子 Agent 必须有明确契约、owner、预算和独立 eval。

结语

Agent 工程的主问题已经从"模型会不会调用工具"转向"系统如何可靠地承接一个概率性执行者"。真正成熟的 Agent 产品,不是让模型永不犯错,而是做到:

  • 错误容易被发现,而不是被流畅语言掩盖;
  • 失败停在安全状态,而不是留下未知副作用;
  • 过程能够回放和归因,而不是靠拍脑袋调 Prompt;
  • 能力提升能被评测证明,成本下降不以暗中降质为代价;
  • 人只介入高风险和真正困难的部分,而不是给每一步兜底。

一句话概括:Workflow 规定路径,Agent 探索路径,Harness 管理探索,Verifier 决定是否抵达。


参考资料

相关推荐
lisw051 小时前
制造业信息化:现代工业的发展趋势!
大数据·网络·人工智能
AllData公司负责人1 小时前
AllData搭建大模型资产中台集成开源项目BISHENG/Crater/Coze-Studio/Coze-Loop/DataFIow,建设AI大模型开发底座
大数据·人工智能·开源·bisheng·coze-studio·crater·datafiow
DongQiShanRen1 小时前
六根硬件映射(四):归属澄清、两处不一致与卷十一总表
开发语言·人工智能·算法·rust·哈希算法·集成学习
麦豆GEO1 小时前
官网GEO优化指南:从展示窗口到AI品牌认知中心
人工智能
阡陌数智1 小时前
Gemini 4 Argon 百万 Token 输出能力解析:原生长输出模型的工程落地与并发成本治理
人工智能·架构
丑梨数据2 小时前
信创环境里跑文档工具:格式兼容与本地合规的几个坑
数据结构·人工智能·算法·决策树·排序算法
麦豆GEO2 小时前
GEO与传统内容营销的核心差异:AI时代内容运营的全面升级
大数据·人工智能·内容运营
禁默2 小时前
Embodied VideoAgent 论文解读:机器人怎样持续记住动态场景
人工智能·机器人
Rocky Ding*2 小时前
FLUX 3 Image Klein 技术解析:5.6B 生成器、Flow Map 蒸馏与 ComfyUI 可控编辑
论文阅读·人工智能·深度学习·机器学习·aigc·flux·ai-native