博主头像
Kurfuerst

ワクワク

From Agent Traces to Trust

Agent 系统的功能经常被描述为模型能力的自然延伸,当模型推理能力变得越来越强、上下文越来越长、工具调用更准确,它就能承担更加复杂的任务。但当 Agent 真正进入软件开发、企业流程、科学实验和 DevOps 等场景后,人们逐渐发现,决定系统是否可靠的往往不再只是模型能力,而是模型周围那套运行时基础设施。

Agent Harness 正是在这个背景下成为一个独立的工程对象。它逐渐从把一组 Prompt 包装成工作流,发展为把概率模型组织成一个有状态、有边界、有反馈、可验证、可恢复的执行系统。

Agent Harness 的本质——把认知负担迁移到模型之外

Externalization in LLM Agents用“外部化”解释了这一变化。早期语言模型的能力主要存在于参数中,知识、程序、偏好和行为倾向都依赖预训练、SFT 或偏好优化进入模型权重。随着应用复杂度上升,开发者开始把任务相关信息放进 Prompt 和 Context,通过 RAG、Few-shot 示例和上下文工程临时改变模型行为。到了 Agent 阶段,单一上下文已经无法稳定承载长期状态、复杂程序和外部交互规则,于是这些负担进一步迁移到 Memory、Skill、Protocol 和运行时基础设施中。

这种外部化更多的是在改变模型面对的问题。Memory 把跨会话的内部回忆问题转换成外部检索问题;Skill 把每次重新发明工作流的问题转换成程序选择和执行问题;Protocol 把自由文本中的交互猜测转换成结构化契约。Harness 则把这些外部对象装配成一个连续运行的系统,控制模型在什么上下文中推理、可以访问什么、怎样行动、怎样接收反馈,以及什么时候必须停止。

因此,Memory、Skill 和 Protocol 是三类外部化内容,而 Harness 是协调、约束和治理它们的运行时环境或者说执行控制层。

Memory 在其中承担跨时间的连续性。当前计划、打开的文件、失败测试和临时假设可以作为 Working Memory;过去几次次任务中的工具调用、错误和反思可以形成 Episodic Memory;从多次执行中抽象出的项目规范和稳定知识可以成为 Semantic Memory;用户偏好、团队习惯和环境约束则属于 Personalized Memory。Memory 的关键不在于保存得多,而在于如何维护,系统应该知道什么应该写入、什么时候检索、什么时候压缩、哪些内容已经过期。

Skill 解决的是另一类问题。模型可能原则上知道如何完成代码评审、部署检查或事故分析,但每次临时生成流程时,仍可能跳步骤、出现顺序错误、提前终止。一个好的 Skill 应同时包含程序步骤、分支启发式和规范性约束。它要说明任务怎样分解、异常时怎样处理、完成条件,以及哪些操作不能执行。

但外部化并不等于可靠。Skill 描述可能与实际任务不匹配,多个单独安全的 Skill 组合后可能产生危险副作用,过长的 Skill 文档也可能使模型沉迷局部程序而忘记全局目标。因此,Skill 必须有明确边界、版本、依赖和验证条件,不能被当成永久正确的知识。

Protocol 则负责把这些能力连接到外部世界。Tool Schema、参数类型、生命周期、Capability Discovery、权限边界和错误语义都属于 Protocol 的范围。Tool 决定系统能做什么,Skill 描述一类任务应该怎样完成,Protocol 则规定这些操作怎样被发现、调用和治理。

Harness 的工作,就是让三者在同一个受控循环中协作,而不是把它们都塞进一段巨大的 System Prompt。

从这个角度看,Harness 同时具有三种属性。它首先是外部认知环境,让模型不必把所有历史、规则和程序同时保存在 Context 中;其次是状态转移控制器,决定模型提出的动作能否真正作用于环境;最后还是证据固定系统,使每一次计划、工具调用、Memory 更新和外部动作都能被后续验证、审计。

代码让 Harness 从“提示模型”变成“控制执行”

这里的“代码”并不只指 Python、Java 或 Shell 程序,也包括形式化规范、证明脚本、API Schema、Tool Definition、测试、仓库、模拟器、配置文件,以及被执行系统产生或消费的 Trace 和 Log。

代码具有自然语言不具备的三个重要特征:它可以被执行,可以被检查与验证,也可以持续保存和修改状态。

代码作为 Harness 的价值,首先体现在它能够把部分精确工作从模型中移出去。模型擅长理解问题和提出程序,但在复杂数值计算、符号推导和长时间状态追踪上往往不够稳定。在 Program-Aided Language Models 和 Program-of-Thoughts 等方向中,模型负责生成程序和高层逻辑,解释器负责执行并返回结果。这样,模型可以把计算过程交给一个确定性运行时。

当然,代码执行只能证明“程序按照既定语义运行了”,不能证明“模型生成了正确的程序”。如果模型把错误的业务规则写进代码,解释器仍会准确执行错误逻辑。因此,Code as Harness 并不是用解释器替代模型判断,而是先让模型负责提出候选计划和程序,再由Harness 在受控环境中执行、观察和验证。

这一分工在 Plan–Execute–Verify 循环中表现得最清楚。Plan 阶段不应只是模型上下文中的临时思考,而应该成为持久、可审查的 Artifact。一个长期任务可以把计划、允许修改的文件、验收标准、验证命令、风险操作和回滚点写入 PLAN.md 或其他结构化状态文件。这样,即使 Context 被压缩、会话重启或任务被交给另一个 Agent,系统仍然拥有权威的执行契约。计划也会明确规定预期状态变化、必须保持的不变量、完成所需证据以及需要人工介入的边界。

到了 Execute 阶段,计划要被转换成受限的状态转移。模型生成的 Tool Call 需要先通过 Tool Gateway。这个 Gateway 是 Harness 中最重要的硬约束层,负责检查 Tool Schema、参数类型、参数来源、权限、当前环境、预计副作用和审批状态;执行前可以通过 Hook 阻断危险调用,执行后还能清洗输出、压缩日志、更新 Memory 或触发额外验证。至于代码本身的执行,则应该尽量发生在 Sandbox 中,通过隔离文件系统、网络、资源和凭证,降低模型错误对真实环境的影响。

这种结构体现了概率判断和确定性规则之间的责任边界。模型可以提出操作建议,但是否允许执行,应由程序化策略根据路径、权限、参数、风险级别和人工审批状态决定。只在 Prompt 中告诉模型“不要进行危险操作”,无法提供同等强度的保证。

Verify 阶段用机器可检查的传感器决定新的状态是否可以被接受。编译器、类型检查、静态分析、单元测试、集成测试、Fuzz、Security Scanner、Runtime Monitor 和 Formal Verifier 都属于这类传感器。LLM Critique 可以帮助解释测试为什么失败、哪些模块可能受到影响,但不应替代测试运行器本身。模型可以提出“我认为所有测试都通过了”,真正的通过状态必须来自执行证据。

同样,Agent 是否停止,也不应该由模型主观决定。可靠终止需要由 Harness 检查必要验证是否通过、证据是否齐全、Retry Budget 是否耗尽、是否检测到循环,以及风险级别是否要求人工审批。模型可以给出停止的建议,但真正决定继续、重试、回滚还是结束的是 Harness。

由此可见,Harness 的本质并不是替模型完成所有工作,而是把不同类型的工作交给适合的执行主体:LLM 负责语义理解、候选生成和解释;确定性程序负责 Schema、权限、状态版本、路径和验证;Harness 负责把二者组织成受控的状态变化。

可观测性必须从 Log 走向 Execution Provenance

Agent 一旦开始执行长流程任务,普通日志记录很快就不够用了。日志可以告诉我们系统调用过搜索工具、修改过文件、运行过测试,但它无法说明错误结果与具体原因之间的对应关系。

所以我们应当区分 Log、Trace 和 Provenance。Log 记录的是单个事件发生了什么;Trace 通过 trace_idspan_idparent_span_id 等结构记录一次执行经历了哪些步骤;Execution Provenance 则进一步把这些步骤组织成带类型关系的执行图,说明前序行为造成的具体影响。

在执行图中,节点大致分成 Evidence Unit 和 Execution Unit。文档、agent获取到的观察与环境反馈、Tool Output、Memory Item 都可以作为 Evidence Unit;Retrieval Call、Tool Call、Tool Argument、Memory Read/Write、Environment Action、Agent Message 和 Recovery Action 则属于 Execution Unit。它们之间通过 UseGenerateDeriveSupportDepend-onContradictInvalidateUpdateTrigger 等关系连接。

一个典型执行链可能从用户请求开始,经过 Retrieval Query 和 Retrieved Passage 形成 Intermediate Claim, Claim 再参与 Tool Argument 的生成,随后 Tool Call 返回的新结果被写入 Memory,最终影响一个外部动作。新证据出现后,它还可能与旧 Memory 形成 Contradict 关系,使旧 Claim 进入 Invalidate 状态,并触发 Recovery。

这里最关键的工程问题是:这些依赖关系究竟来自固定字段,还是由 LLM 判断?我认为必须采用分层构建,而且 LLM 不能成为唯一权威。

类似 Tool Call 使用了哪些参数、Tool Output 由哪个调用产生、Memory Write 消费了哪个输出这类信息都是可以由运行时直接记录的事实。系统可以通过 tool_call_idinput_refsoutput_refsmemory_idsstate_versioncheckpoint_id 和 Artifact Hash 建立确定性边。这部分不需要模型推断,也不需要模型事后补写。

SupportContradict 和语义层面的 Derive 通常无法只靠固定字段确定。此时可以要求模型显式声明它使用了哪些 evidence_idmemory_id,但这些关系只能先被视为模型声明,再由 Claim Decomposition、规则、NLI、独立 LLM Judge 或人工审核进行验证。因为模型召回或使用了这些 evidence,也不意味着它真的改变了最终行动。

更强的因果关系还需要反事实执行来确认。系统可以屏蔽某条 Memory、替换某个 Tool Output,或者从相同 Checkpoint 重新运行。如果移除某条 Evidence 后,Tool Argument 或最终动作稳定变化,才能得到比“时间上先出现”更强的影响证据。因此,Provenance Edge 本身也应带有状态:它可能是运行时直接观察到的 observed,由模型声明的 declared,由语义系统推断的 inferred,经过测试或反事实实验确认的 verified,也可能因为后续证据而变成 disputedinvalidated

真正可用的 Provenance Graph 是在运行过程中逐步构造。Harness 首先需要稳定的 Trace Schema 和全局对象 ID,在 Model Call、Retrieval、Tool Gateway、Sandbox、Memory、Validator、Approval 和 Recovery 等边界生成结构化事件。完整 Tool Output、Diff、测试报告和 Checkpoint 应作为不可变的 Evidence 保存下来,压缩摘要只用于 Context 和检索,不能替代原始证据。在此基础上,系统先靠固定引用建立确定性依赖,再通过语义验证器补上 Claim 级别的支持与冲突关系,最后建立一条能从失败动作反向追溯到原始来源的索引。

Provenance 的价值不在于图本身,而在于它能不能被其他控制组件真正用起来。如果这些 Trace 只能展示在 Dashboard 上,却不能影响控制和恢复,它仍然只是运维可观测性,而不是完整的 Evidence Plane。

从错误表现追溯到错误引入

传统调试通常从最后一个失败点开始。例如测试失败后,我们下意识会去检查最后一次代码修改。但在长流程 Agent 任务中,错误可能很早就被引入,经过多个组件传播,最后才在测试或外部动作中表现出来。

比如一个 Agent 可能最初检索了错误文件,随后根据错误代码生成计划;计划被另一个 Agent 接受并转换成 Patch;测试 Agent 又因为使用了过期仓库状态而没有发现问题;最终错误只在生产环境中出现。在这种情况下,最后那个 Patch 只是错误的表现位置,不是错误根因。

因此,Agent 错误定位需要区分三个阶段:Error Introduction 表示错误最初在哪里进入系统,Error Propagation 表示它经过哪些 Memory、消息、Claim 和工具传播,Error Manifestation 则表示它最终在哪里产生可观察故障。

一个更可靠的故障处理过程应该在检测到异常后立即冻结 Checkpoint,并把当时的 Repository、Memory、Tool Output、策略版本和权限状态保存下来。系统随后根据 Trace 重建 Provenance Graph,从失败结果触发,沿着 Depend-onDeriveUse 边反向切片,寻找最早违反 Task Contract、权限规则或程序不变量的状态转移。找到候选原因后,再通过 Mask、Replay 或 Fault Injection 验证它是否真正影响结果。

这也是 TRAIL、AgenTracer、Aegis、LADYBUG、AgentOps 和 AgentTrace 等研究方向的共同趋势。它们虽然使用不同的表示和评价方法,但都在推动错误定位从“阅读日志后猜原因”转向“通过结构化轨迹、依赖关系和反事实执行寻找责任步骤”。

错误类型不同,恢复策略也应该不同。Tool Argument 错误可能只需要局部重试,Memory 污染需要失效并隔离相关记录,Plan 错误需要重新规划,权限不足需要人工审批,已经产生外部副作用的动作可能需要补偿事务。Harness 不应对所有故障进行同一种 Blind Retry,否则不仅浪费资源,还可能重复扩大副作用。

Memory 在这套故障治理中尤其重要。Memory Write 必须区分原始观察、从文档提取的事实、模型推断的偏好、从失败轨迹抽象出的 Reflection,又或者是其他 Agent 传入的结论。每条 Memory 都应该带上来源、时间、证据基础、转换方式、置信度、更新历史和验证状态。否则未来只能看到一句被压缩后的文本,无法判断它究竟来自可靠事实还是模型幻觉。

Memory Retrieval 同样需要 Provenance。系统需要记录为什么触发检索、当时的有效性、原始来源,以及它最终影响了哪个 Claim、Tool Argument 或 Action。当新证据推翻旧 Memory 时,Harness 需要找到所有下游依赖,决定哪些结果必须重算,哪些动作必须回滚,哪些用户需要被通知。这样一来,Memory 就不再只是一个单纯的上下文缓存,而变成了一种带有生命周期和责任关系的长期状态。

Harness 的下一阶段,是受治理的自我演化

随着 Trace 和 Provenance 越来越完整,Harness 自身也开始变成了一个可以被优化的对象。Agentic Harness Engineering 的关注点正在逐渐从怎样修改 Prompt转向怎样利用 Deep Telemetry 改进 Memory Policy、Tool Schema、Validator、Retry Strategy、Context Packing、Permission Rule 和 Workflow。

但 Harness 修改比普通任务 Patch 风险更高,因为它改变的是未来所有 Agent 的行为分布。因此,Harness 演化必须受到比普通代码修改更严格的治理。候选改动需要在 Sandbox 中运行,通过历史 Trace Replay、固定 Regression Suite 和 Held-out Task 评价,同时检查可靠性、成本、权限和安全是否发生回归。高风险变更还应经过 Canary 和人工审批,并保留明确的策略版本与 Rollback 能力。

这条演化链路可以概括为:

Failure Trace → Diagnosis → Scoped Harness Patch → Static and Permission Check → Replay → Frozen Regression → Held-out Evaluation → Canary → Publish → Monitoring → Rollback。

运行时 Harness 产生的计划、工具调用、测试、失败和恢复轨迹,还可以成为 SFT、DPO、RL 或 OPD 的数据来源。但必须保持清晰边界:轨迹被导出为训练数据,并不等于后训练收益已经得到证明。要验证 Harness、Memory 或某种策略更新的真实价值,仍然需要固定模型版本、数据版本、Policy Identity 和隔离评测,区分相关性改善与因果收益。

结语

Agent Harness 的出现,标志着 Agent 研究正在从“怎样让模型回答得更好”,转向“怎样把概率模型组织成可信执行系统”。

在这套系统中,Memory 保存跨时间的状态,Skill 把程序性知识外部化,Protocol 规定交互契约,Code 把意图变成可执行的状态变化,Sandbox 和 Tool Gateway 建立行动边界,Validator 判断结果是否可接受,Trace 记录执行过程,Provenance 连接证据、依赖和责任,Recovery 则根据错误类型选择重试、隔离、回滚或人工介入。

模型仍然是整个系统中最重要的语义推理组件,但它不应该独自承担所有责任。模型适合提出方案、解释证据和处理开放式的语义问题;路径、Schema、权限、状态版本、指标和验证应该由确定性程序负责;Harness 则负责把二者连接起来,并确保每次行动都处在可观察、可审计、可恢复的运行边界之内。

一个真正可信的 Agent,在完成任务的基础上,还必须能够解释为什么执行、依据是什么、改变了什么、怎样证明完成、错误在哪里产生,以及失败后如何恢复。

从这个意义上说,Harness 并不是模型外部的附属脚手架,而是 Agent 从语言模型走向真实生产系统时最核心的工程层。

From Agent Traces to Trust
http://blog.kurfuerst.online/index.php/archives/50/
本文作者 Großer Kurfürst
发布时间 2026-08-04
许可协议 CC BY-NC-SA 4.0
发表新评论