星澜

天接云涛连晓雾,星河欲转千帆舞

05. AI Agent 与 Harness:Agent Harness、Graph 与退款 Agent


在《04. AI Agent 与 Harness:V2 Harness 的验证设计》里,重点讲的是验证闭环,尤其是动作之后怎么确认结果真的成立。

再往前走一步,真正需要先落地的,其实是这件事:

一个业务 agent 的 harness,到底长什么样?

对退款、审批、客服分诊、工单流转这类系统来说,第一步通常不是先打磨 prompt,而是先把流程图画出来。
图一旦清楚,第一版系统骨架往往也就跟着出来了。

结论

agent harness = 把业务流程、工具调用、验证闭环和人工接管,真正做成能运行的系统。

为什么先画流程图,再谈 agent

这当然不是说:

流程图 = 代码

而是说,对于退款、审批、分诊、风控这类业务 agent,你一旦能把下面几件事画清楚,第一版系统骨架其实就已经出来了:

很多团队一开始觉得自己是在“做 AI agent”,后来真正落地时会发现,第一步其实更像:

这些图一旦清楚了,agent harness 的大部分骨架也就跟着清楚了。

能画出流程图,不代表已经把 agent 写完了;但通常已经走到了“能开始写 agent harness”的阶段。

Graph 在表达什么

这里说的 graph,不要只把它理解成某个具体框架的对象。
它更广义地指:

一个业务 agent 的 graph,通常至少会包含几类节点:

这时 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. 动作节点放在哪里

4. 验证节点放在哪里

你会发现,这张图一旦画出来,很多事情就已经不再抽象了。

LangGraph 和 Spring AI,基本就是在承载这套 graph 思维

只谈“图”容易显得太虚,所以这里最好把常见框架也带上。

如果是 Python 体系,很多团队会直接用 LangGraph
它的好处非常直接:

如果是 Java / Spring 体系,很多团队会落在 Spring AI
它未必总是像 LangGraph 那样把 graph API 摆在最前面,但官方文档里常见的几类 workflow,其实表达的是同一套东西:

也就是说:

框架不同,核心问题其实一样:

节点是什么,边怎么走,状态怎么存,失败怎么退,结果怎么验。

这张图里,什么才叫 agent harness

很多人一说 harness,脑子里会先想到:

这些当然都可以是 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

很多业务规则其实不该塞给模型去猜。

比如:

这些更适合做成明确规则节点。

5. validator

也就是动作之后,系统怎么确认结果真的成立。

6. risk router / manual handoff

不是所有路径都应该自动到底。
真正成熟的业务 agent,通常都要有人工接管口。

7. trace / audit / replay

你得知道系统怎么走到这一步,也得能回放真实 case 去做回归验证。

把这些合起来看,就会更容易明白:

agent harness 不是一段提示词,而是整套运行系统。

为什么 Post-condition Validator 属于动作后验证

这是很多人第一次接触时最容易迷糊的地方。

原因其实很简单:

它验证的不是“模型有没有说做完”,而是“动作之后,真实世界有没有真的被改成预期状态”。

比如退款 agent 里,模型调用了退款接口,接口返回:

{"ok": true}

这还不够。

因为你真正关心的是:

这几个问题都只能在动作之后检查。
所以它天然属于:

动作后验证

也可以把三层验证压成一张对照表:

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()

这段伪代码最重要的不是语法,而是你能直接看出几件事:

这就是 agent harness 的味道。

如果是 Spring AI,更常见的是把同一套思路落进 workflow pattern

如果团队是 Java / Spring 技术栈,很多时候不会先写一个和 LangGraph 长得一模一样的 StateGraph
更常见的做法是把同一套思路拆进几类 workflow:

如果压成伪代码,思路大概会像这样:

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”,而是:

比如下面这两种情况就不一样:

更偏 workflow-first

更偏 agentic

更贴切地说:

graph 不是 agentic 的反面,graph 只是把 agentic 能力放进受控运行骨架里。

agent harness 最难的,其实不是模型,而是边界

真正难的地方,通常不是“模型会不会理解退款请求”,而是:

这些问题一旦不清楚,graph 就会糊,系统骨架就会糊,最后你会误以为是模型不聪明。
但很多时候,本质上是:

agent harness 还没有把系统边界画清楚。

几句话总结

  1. agent harness 本质上是业务 agent 的运行系统。
  2. 退款、审批、分诊这类 agent,第一版往往都是从 graph 开始。
  3. graph 不是 agentic 的反面,它更像运行骨架。
  4. 真正决定系统更像 workflow-first 还是更 agentic 的,是关键边写死了多少。
  5. Post-condition Validator 属于动作后验证,因为它检查的是动作之后真实世界有没有被改成预期状态。
  6. 开发业务 agent 时,很多“AI 问题”最后都会落回状态、路由、规则、验证和人工接管。

参考