🛠️ Agent 工程 · 架构补篇

会推理不等于能上线:Agent 的判断与行动,如何变成不会丢失的软件事实?

页面一刷新,审批就消失;进程一重启,退款就重来。判断没错、授权没错,系统还是丢了现场。vivo 在一篇 KDC 工程补篇中,把 Agent 生产级运行的隐藏缺口拆到了最底层——判断与行动,必须先变成可恢复的软件事实

来源:综合公开信息整理 2026-07-28 全文约 5 分钟读完
#Agent工程 #KDC #状态管理 #AI可靠性
3 种 事实不能互相替代
领域现实 / 业务判断 / 软件运行
2 条 链路必须稳定连接
业务因果链 × 运行事实链
10 个 身份 ID 追溯每次判断与行动
runId → feedbackId

读法:这三个数字对应原文拆解的三层工程责任——事实分层、链路连接、身份追溯,在正文各节展开。

⚡ 30 秒速览

  • 故障现场:退款审批在页面刷新后消失,进程重启后重复扣款——判断正确 ≠ 执行可靠。
  • 三类事实:领域现实、业务判断事实、软件运行事实一旦混在一起,就会出现「调用成功 ≠ 退款到账」。
  • 双链结构:业务因果链 × 运行事实链,靠 runId、approvalId、toolCallId 等 10 个身份 ID 连接。
  • 五对象分离:Session 是历史,Context 是投影,State 是事实,Memory 是经验,Knowledge 是知识。
  • 工程原则:Runtime 写事实,View 读事实,Control 提交命令——UI 不替 Runtime 补写状态

01一次刷新,丢了什么?一个退款场景的两种死法

先从退款场景开始。

用户问售后 Agent:"帮我看看这笔订单能不能退。如果可以,就直接帮我处理。" Agent 查询订单、引用当前退款政策、判断订单符合条件,准备调用退款能力。由于退款会改变订单和资金状态,系统要求用户确认。页面随即显示了一条审批提示。

用户还没点击确认——页面突然刷新

刷新之后,审批提示消失。聊天记录里还能看到 Agent 说过"准备退款",但前端不知道审批是否仍然有效,后端不知道用户是否已经做过决定,能力运行时也不知道应该继续、暂停还是取消。

更危险的情况还在后面。

💸 故障一:页面刷新,审批消失 状态丢失

  • 用户发出退款请求,Agent 判断符合条件,系统要求确认
  • 用户未点击确认,页面刷新,审批提示从 UI 上消失
  • 聊天记录只留下"准备退款"的文字,但这不是可恢复的状态
根因:审批状态只存在于聊天文本与组件变量中,没有成为可持久化的运行事实。判断正确、授权正确,系统仍然丢失现场。

⚠️ 故障二:进程重启,重复退款 副作用失控

  • 退款请求已经提交给支付渠道,后端在写入本地完成状态前重启
  • 恢复后系统只看到"任务尚未完成",再次调用退款接口
  • 判断和授权规则都是正确的,用户却被重复扣款
根因:Tool Call 的结果状态没有被持久化,`result_unknown` 被误当作"尚未调用"。

这一次,问题不在知识是否可靠,也不在 Agent 是否理解了用户目标。判断和授权规则可能都是正确的,系统仍然可能因为缺少稳定运行事实而丢失现场、重复执行或无法追责。

从"Agent 做出了正确判断"到"系统可靠地完成了行动"之间,有一层不能省略的工程结构。

02三种事实:为什么"调用成功"不等于"退款到账"

在 Agent 系统中,至少存在三种需要分别管理的事实。它们相互关联,却有着完全不同的生命周期与可信等级。

🌍 领域现实

应用软件的最终参照物。软件只能通过接口、事件、人工确认和其他表示间接观察它。

🧠 业务判断事实

系统基于什么目标、知识和证据形成结论,又为什么建议某项行动。KDC 用推理对象表达这类责任。

⚙️ 软件运行事实

某次运行中已经发生了什么:Run 是否开始,审批是否挂起,Tool 是否执行,Checkpoint 位于哪里。

三者不能互相替代。下面三组"不等号"是最容易踩的坑:

tool.call.completed 退款已经到账
pendingApproval = null 用户已经同意退款
页面显示"已完成" 业务目标已经实现

读法:Runtime 可以权威地声明"这次 Tool Call 已经完成",却不能仅凭这个运行事实证明用户已经收到资金——前者是日志,后者是现实。

03两条链:业务因果 × 运行事实

生产级 Agent 需要两条可以连接的链。一条回答系统为什么行动,一条回答执行到了哪里

业务因果链 —— 为什么行动

Reality Knowledge / Memory Reasoning Skill Capability Policy Decision Action Feedback Reality

运行事实链 —— 执行到哪里

Runtime Event State View User Control Runtime Event
State Checkpoint / Resume

读法:上链由业务目标驱动,下链由事件驱动。User Control(批准 / 停止 / 重试)产生新事件,State 由此归约,View 从 State 派生。

两条链不能各自独立。只保留业务因果链,系统可能解释得清楚却无法恢复;只保留运行事实链,系统可以恢复调用,却不知道行动是否具备业务资格。

连接两条链的关键不是复制更多文本,而是稳定身份

runId turnId reasoningObjectId skillId capabilityId policyDecisionId approvalId toolCallId artifactId feedbackId

这些身份让系统知道,一条审批属于哪次判断一次 Tool Call 来自哪个能力,一份 Artifact 支持哪个目标,一条反馈又应该修正哪次行动。

04五个对象:别把"让 Agent 记住"混为一谈

"让 Agent 记住"在工程讨论里经常同时指五件不同的事。它们都与历史有关,却不能共享同一个生命周期和可信等级

对象角色持久化边界
Session 当前交互线程的可恢复历史来源 进程外持久化
Context 一次模型调用对历史的选择性投影 可裁剪 / 压缩 / 重建
State 可恢复的运行事实,由 Runtime 写入 持久化 + 版本化
Memory 跨 Session 可检索的经验 跨线程检索
Knowledge 经过来源确认与时效治理的知识 治理后入库

判断标准:如果某个状态影响恢复、审批、继续执行、跨端一致、审计或结果检查,它就不应该只存在于聊天文本、Prompt、组件变量或临时缓存中。

最危险的误用是让 Context 承担 State 的职责。Context 可以被裁剪、压缩、重排或重新生成——一次上下文压缩如果遗漏了"用户已经批准",不该让审批重新变成待处理;一次新的模型调用如果没有看到全部 Trace,也不该改变已经发生的外部事实。

05三权分离:谁有资格写运行事实

一个常见失败方式,是让前端从消息文本中推断运行状态。例如模型输出"需要用户确认后才能发起退款",UI 就本地生成一个审批按钮——这个实现短期内能工作,却回答不了:这条审批对应哪个 Run 和 Tool Call?刷新后如何恢复?用户点击同意时,决定应该发送给谁?

因此需要区分三类责任:

State

可恢复的运行事实,由 Runtime 或明确的业务边界产生并持久化。

activeRun / currentTurn
pendingApprovals / toolCalls
checkpointId / runtimeStatus
Runtime 写入
View

从事实派生的展示。pendingApproval 存在所以展示审批条,activeRun 在运行所以允许 Stop。

isBusy / canStop
approvalBanner / toolBadges
subagentProgress
UI 读取
Control

命令,不是状态变更。表示"请求系统做什么",不表示事情已经发生。

resume(approvalId, decision)
stop(runId) / retry(toolCallId)
reload(threadId)
UI 提交
Runtime 写运行事实,View 读取运行事实,Control 提交命令——UI 不替 Runtime 补写运行事实语义责任必须分开,物理存储可以统一;能从事件确定性归约的状态不必重复写,不能可靠推导的高风险事实必须显式记录。

一个关键边界是 result_unknown。Tool 调用发出后,支付渠道可能已经受理但响应丢失,State 不应回退成"尚未调用",而应保留同一个 toolCallId 和幂等键:

State 片段 · 禁止盲目重试的恢复边界
{
  "toolCallId": "tool-call-031",
  "capabilityId": "refund-order-v2",
  "status": "result_unknown",
  "idempotencyKey": "refund:order-001:intent-031",
  "externalRequestId": "payment-request-8841",
  "reconciliationStatus": "pending",
  "lastError": "response_timeout"
}

result_unknown 不是失败的另一种写法,而是一个恢复边界——Runtime 必须先使用外部请求身份查询或对账,再决定完成、补偿还是转人工。

06行业实践:一组正在收敛的工程答案

这不是象牙塔理论。vivo 是手机厂商,有真实用户、真实售后、真实支付流程——退款场景就是他们系统里每天都在发生的事。

"Agent Harness"目前更像一组正在收敛的工程实践。不同项目从不同故障出发,各自强调了生产级 Agent 的一部分责任:

Managed Agents · Anthropic Durable Execution 12-Factor Agents Google ADK Session LangGraph Checkpointer KDC · vivo
一个诚实的注脚:这些实践共同暴露了状态连续性、外部副作用、长任务交接和系统评测问题,但对"状态是否应该独立持久化、能否从线程上下文推导",尚未形成统一结论。KDC 与它们的关系不是用新名称重新包装机制,而是提供更上层的业务因果约束。

在此基础上,vivo 提出了三个可被实现、检查和测试的工程契约,用于把判断与行动变为可交接的软件事实:

01Resume Contract

恢复契约:一次运行中断后,从哪个 Checkpoint、以什么前提继续,哪些外部结果需要先对账。

02Progress Contract

进度契约:任务当前进行到哪一步,下一步允许做什么,哪些条件变化会导致进度失效。

03Evaluation Contract

评估契约:用什么反馈证据、什么标准判定业务目标实现或失败,而非只看 Tool 是否返回 200。

读法:三个契约对应运行恢复、进度交接、结果验证三段生命周期,是双链结构在工程接口层面的具象化。

07编辑核心判断

编辑核心判断

模型参数可以卷,但"状态不丢、执行不重、追责有人"这三件事,卷不出奇迹——它们只能一行一行地写进工程里。

Agent 的推理能力可以靠模型迭代快速追平,但运行事实层的工程深度没有捷径。它正在成为国产 Agent 从 Demo 走向生产的分水岭。

继续阅读

KDC 系列共六篇。前五篇完成了从 Reality 到 Feedback 的理论闭环,这篇补充了最具体的一环:让判断与行动成为可恢复、可控制、可审计的软件事实。

KDC 系列 · 工程补篇 06/06 —— 理论闭环的最后一块拼图