05. AI Agent 与 Harness:Agent Harness、Graph 与退款 Agent
mingzaily / Codex / 2026-03-29
在《04. AI Agent 与 Harness:V2 Harness 的验证设计》里,重点讲的是验证闭环,尤其是动作之后怎么确认结果真的成立。
再往前走一步,真正需要先落地的,其实是这件事:
一个业务 agent 的 harness,到底长什么样?
对退款、审批、客服分诊、工单流转这类系统来说,第一步通常不是先打磨 prompt,而是先把流程图画出来。
图一旦清楚,第一版系统骨架往往也就跟着出来了。
结论
- 对业务 agent 来说,第一版
agent harness往往就是先把 graph 画出来 - 这里说的图,不只是口头描述,很多时候直接能先画成
mermaid graph不等于不agentic,它更像运行骨架- 真正决定系统是不是更
workflow-first,不是有没有图,而是关键路径写死了多少 Post-condition Validator属于动作后验证,因为它检查的是动作之后,世界状态有没有真的变成预期的样子
agent harness = 把业务流程、工具调用、验证闭环和人工接管,真正做成能运行的系统。
为什么先画流程图,再谈 agent
这当然不是说:
流程图 = 代码
而是说,对于退款、审批、分诊、风控这类业务 agent,你一旦能把下面几件事画清楚,第一版系统骨架其实就已经出来了:
- 有哪些节点
- 节点之间怎么流转
- 哪些节点必须确定性执行
- 哪些节点可以交给模型判断
- 哪些地方要加人工接管
- 哪些地方要加验证和返工
很多团队一开始觉得自己是在“做 AI agent”,后来真正落地时会发现,第一步其实更像:
- 画流程图
- 画状态流转
- 画工具调用图
- 画失败回退路径
这些图一旦清楚了,agent harness 的大部分骨架也就跟着清楚了。
能画出流程图,不代表已经把 agent 写完了;但通常已经走到了“能开始写 agent harness”的阶段。
Graph 在表达什么
这里说的 graph,不要只把它理解成某个具体框架的对象。
它更广义地指:
- 节点
- 边
- 状态
- 路由规则
- 重试和返工路径
一个业务 agent 的 graph,通常至少会包含几类节点:
LLM 节点- 负责意图理解、信息提取、回复生成、某些开放判断
工具节点- 查订单、查政策、调退款接口、写工单、发通知
规则节点- 权限判断、风控判断、政策判断
验证节点- 检查动作结果是否真的生效
人工节点- 升级审批、转人工处理、人工兜底
这时 graph 真正表达的,不只是“业务流程长什么样”,还包括:
- 这个系统允许哪些动作发生
- 这些动作在什么条件下可以发生
- 失败后应该回哪里
- 什么情况下必须停止自动化
也正因为这样:
graph 不是 agent 的对立面,graph 更像 agent 的运行骨架。
退款 agent 的第一版 graph,应该怎么画
退款 agent 这类业务系统,第一版图通常就可以直接画成这样:
flowchart TD
A[用户发起退款请求] --> B[LLM 解析意图与关键信息]
B --> C{输入前验证}
C -->|信息不足| D[追问用户]
C -->|可继续| E[查询订单 / 用户 / 政策]
E --> F{动作前验证}
F -->|reject| G[拒绝并解释原因]
F -->|manual| H[转人工审批]
F -->|pass| I[执行退款]
I --> J{Post-condition Validator}
J -->|passed| K[回复用户 + 记录审计]
J -->|retryable| L[修复 / 重试]
J -->|manual| H
L --> J
如果再把节点职责拆得更清楚一点,大概会是这样:
1. LLM 更适合放在哪里
- 解析用户意图
- 提取退款原因和关键信息
- 生成用户可读的解释
- 在信息不足时决定怎么追问更自然
2. 确定性逻辑更适合放在哪里
- 订单是否已签收
- 是否超过退款窗口
- 是否命中高金额阈值
- 是否需要人工审批
- 是否允许自动调用退款接口
3. 动作节点放在哪里
- 查询订单系统
- 查询用户信息
- 调用退款接口
- 写 CRM / 工单
- 发送通知
4. 验证节点放在哪里
- 退款单是否真的创建成功
- 订单状态是否真的更新
- CRM / 工单是否真的落库
- 是否留下审计记录
你会发现,这张图一旦画出来,很多事情就已经不再抽象了。
LangGraph 和 Spring AI,基本就是在承载这套 graph 思维
只谈“图”容易显得太虚,所以这里最好把常见框架也带上。
如果是 Python 体系,很多团队会直接用 LangGraph。
它的好处非常直接:
StateGraph本身就是显式 graph- 节点、边、条件路由都写得很清楚
- 很适合表达“执行 -> 验证 -> 重试 / 转人工”这种闭环
如果是 Java / Spring 体系,很多团队会落在 Spring AI。
它未必总是像 LangGraph 那样把 graph API 摆在最前面,但官方文档里常见的几类 workflow,其实表达的是同一套东西:
- routing
- orchestrator-workers
- evaluator-optimizer
- tool calling / advisor loop
也就是说:
LangGraph更像显式画图Spring AI更像把同样的图思维落进 workflow pattern 和运行时编排
框架不同,核心问题其实一样:
节点是什么,边怎么走,状态怎么存,失败怎么退,结果怎么验。
这张图里,什么才叫 agent harness
很多人一说 harness,脑子里会先想到:
- prompt
- skill
- MCP
- tool description
这些当然都可以是 harness 的组成部分。
但如果是自己开发退款 agent,这还不够。
真正的 agent harness,至少包括下面几层:
1. graph / orchestration
也就是:
- 有哪些节点
- 节点之间如何流转
- 哪些路径固定
- 哪些路径动态
2. state schema
系统在每一轮到底保存什么状态,不能全靠模型自己记。
比如退款 agent 的状态里,至少可能会有:
request_id:
user_id:
order_id:
refund_reason:
policy_result:
risk_level:
action_result:
validation_result:
trace_id:
3. tool adapters
工具不是只要“能调”就够了,还要定义:
- 输入输出结构
- 错误码
- 重试策略
- 幂等策略
4. policy engine
很多业务规则其实不该塞给模型去猜。
比如:
- 未签收不可自动退款
- 高金额必须人工审批
- 超过 48 小时自动升级
这些更适合做成明确规则节点。
5. validator
也就是动作之后,系统怎么确认结果真的成立。
6. risk router / manual handoff
不是所有路径都应该自动到底。
真正成熟的业务 agent,通常都要有人工接管口。
7. trace / audit / replay
你得知道系统怎么走到这一步,也得能回放真实 case 去做回归验证。
把这些合起来看,就会更容易明白:
agent harness 不是一段提示词,而是整套运行系统。
为什么 Post-condition Validator 属于动作后验证
这是很多人第一次接触时最容易迷糊的地方。
原因其实很简单:
它验证的不是“模型有没有说做完”,而是“动作之后,真实世界有没有真的被改成预期状态”。
比如退款 agent 里,模型调用了退款接口,接口返回:
{"ok": true}
这还不够。
因为你真正关心的是:
- 退款单是否真的创建成功
- 订单状态是否真的从
paid变成refund_pending - CRM 里是否真的有记录
- 审计链路是否真的留下痕迹
这几个问题都只能在动作之后检查。
所以它天然属于:
动作后验证
也可以把三层验证压成一张对照表:
输入前验证- 这单能不能接
动作前验证- 这步能不能做
动作后验证- 做完之后,结果是不是真的成立
而 Post-condition Validator 检查的,就是第三层。
退款 agent 的最小骨架
如果不谈具体框架,只看结构,骨架通常会是这样:
flowchart TD
A[parse_intent] --> B[validate_input]
B --> C[query_order_and_policy]
C --> D{evaluate_policy_and_risk}
D -->|reject| E[reject]
D -->|manual_review| F[manual_review]
D -->|execute_refund| G[execute_refund]
G --> H{validate_post_condition}
H -->|success| I[success]
H -->|retry_or_repair| J[retry_or_repair]
H -->|manual_review| F
J --> G
如果换成代码视角,大概会是这样:
state = parse_intent(request)
if not input_is_valid(state):
return ask_user_for_more_info(state)
state = enrich_with_order_and_policy(state)
decision = evaluate_policy_and_risk(state)
if decision == "reject":
return explain_rejection(state)
if decision == "manual":
return handoff_to_human(state)
action_result = refund_tool.invoke(state)
validation = validate_post_condition(state, action_result)
if validation.passed:
return reply_success(state)
if validation.retryable:
return retry_or_repair(state)
return handoff_to_human(state)
这时候你会发现,所谓“写一个退款 agent”,其实已经很像在写一个带状态、带路由、带校验的运行系统了。
如果用 LangGraph,伪代码大概会长这样
下面这段不是可直接运行的代码,但已经很接近真实实现:
from typing import TypedDict, Literal
from langgraph.graph import StateGraph, START, END
class RefundState(TypedDict):
user_input: str
order_id: str | None
refund_reason: str | None
order_snapshot: dict | None
policy_result: dict | None
action_result: dict | None
validation_result: dict | None
reply: str | None
def parse_intent(state: RefundState) -> RefundState:
...
def validate_input(state: RefundState) -> RefundState:
...
def route_after_input(state: RefundState) -> Literal["ask_user", "query_order_and_policy"]:
...
def query_order_and_policy(state: RefundState) -> RefundState:
...
def evaluate_policy_and_risk(state: RefundState) -> RefundState:
...
def route_after_policy(state: RefundState) -> Literal["reject", "manual_review", "execute_refund"]:
...
def execute_refund(state: RefundState) -> RefundState:
...
def validate_post_condition(state: RefundState) -> RefundState:
...
def route_after_validation(state: RefundState) -> Literal["reply_success", "retry_or_repair", "manual_review"]:
...
builder = StateGraph(RefundState)
builder.add_node("parse_intent", parse_intent)
builder.add_node("validate_input", validate_input)
builder.add_node("ask_user", lambda s: {...})
builder.add_node("query_order_and_policy", query_order_and_policy)
builder.add_node("evaluate_policy_and_risk", evaluate_policy_and_risk)
builder.add_node("reject", lambda s: {...})
builder.add_node("manual_review", lambda s: {...})
builder.add_node("execute_refund", execute_refund)
builder.add_node("validate_post_condition", validate_post_condition)
builder.add_node("retry_or_repair", lambda s: {...})
builder.add_node("reply_success", lambda s: {...})
builder.add_edge(START, "parse_intent")
builder.add_edge("parse_intent", "validate_input")
builder.add_conditional_edges("validate_input", route_after_input)
builder.add_edge("query_order_and_policy", "evaluate_policy_and_risk")
builder.add_conditional_edges("evaluate_policy_and_risk", route_after_policy)
builder.add_edge("execute_refund", "validate_post_condition")
builder.add_conditional_edges("validate_post_condition", route_after_validation)
builder.add_edge("ask_user", END)
builder.add_edge("reject", END)
builder.add_edge("manual_review", END)
builder.add_edge("reply_success", END)
builder.add_edge("retry_or_repair", "execute_refund")
refund_graph = builder.compile()
这段伪代码最重要的不是语法,而是你能直接看出几件事:
- state 是显式的
- 路由是显式的
- validator 是固定节点
- retry / manual review 都是显式边
这就是 agent harness 的味道。
如果是 Spring AI,更常见的是把同一套思路落进 workflow pattern
如果团队是 Java / Spring 技术栈,很多时候不会先写一个和 LangGraph 长得一模一样的 StateGraph。
更常见的做法是把同一套思路拆进几类 workflow:
Routing Workflow- 先决定这单是追问、拒绝、自动处理还是转人工
Orchestrator-Workers- 把查订单、查政策、查风控拆成不同 worker
Evaluator-Optimizer- 执行动作后,再验结果,不通过就返工
Advisor / Tool loop- 把日志、trace、数据库、外部服务查询接进来
如果压成伪代码,思路大概会像这样:
RefundState state = parseIntent(userInput);
if (!inputValidator.passed(state)) {
return askUserForMoreInfo(state);
}
state = enrichWithOrderPolicyAndRisk(state);
Decision decision = policyRouter.route(state);
if (decision == Decision.REJECT) {
return reject(state);
}
if (decision == Decision.MANUAL_REVIEW) {
return handoffToHuman(state);
}
ActionResult action = refundExecutor.execute(state);
ValidationResult validation = postConditionValidator.validate(state, action);
if (validation.passed()) {
return replySuccess(state, validation);
}
if (validation.retryable()) {
return retryOrRepair(state, validation);
}
return handoffToHuman(state);
框架表面长得不一样,但骨架还是同一套:
解析 -> 校验 -> 执行 -> 验证 -> 路由
graph 能不能很 agentic
可以,而且现实里很常见。
很多人一看到 graph,就会担心它是不是天然更偏 workflow-first。
但真正的问题不是“有没有 graph”,而是:
- graph 里有没有大量写死的关键边
- 还是说 graph 只是外层骨架,内部仍然允许很多动态决策
比如下面这两种情况就不一样:
更偏 workflow-first
A -> B -> C基本固定- 规则分支几乎都在设计时写死
- 模型主要负责解释和局部提取
更偏 agentic
- 外层还是
接单 -> 执行 -> 验证 -> 交付 - 但执行阶段里,模型可以动态决定:
- 先查订单还是先查政策
- 需不需要补充信息
- 需不需要回到上一步修复
- 哪个工具更合适
更贴切地说:
graph 不是 agentic 的反面,graph 只是把 agentic 能力放进受控运行骨架里。
agent harness 最难的,其实不是模型,而是边界
真正难的地方,通常不是“模型会不会理解退款请求”,而是:
- 哪些情况可以自动做
- 哪些情况必须人工接管
- 哪些规则必须确定性执行
- done 到底是什么
- 验证失败后到底往哪里退
这些问题一旦不清楚,graph 就会糊,系统骨架就会糊,最后你会误以为是模型不聪明。
但很多时候,本质上是:
agent harness 还没有把系统边界画清楚。
几句话总结
agent harness本质上是业务 agent 的运行系统。- 退款、审批、分诊这类 agent,第一版往往都是从 graph 开始。
graph不是 agentic 的反面,它更像运行骨架。- 真正决定系统更像
workflow-first还是更agentic的,是关键边写死了多少。 Post-condition Validator属于动作后验证,因为它检查的是动作之后真实世界有没有被改成预期状态。- 开发业务 agent 时,很多“AI 问题”最后都会落回状态、路由、规则、验证和人工接管。
参考
- LangGraph Docs: StateGraph
- LangGraph Docs: Why use LangGraph
- Spring AI Docs: Building Effective Agents