Harness Engineering: C 端 AIGC 内容生产自优化实践

简介: 本文介绍蚂蚁保“保险快查”深度解读页生成系统(DIPG),提出C端AIGC交付新范式:摒弃LLM实时直出,采用“离线生成→Harness校验闭环→DB持久化→C端直出”主路径。通过Host/Research/Verify三Agent协同,实现HTML结构与事实双校验,并将错误模式反哺Prompt优化,保障交付质量与持续演进。

在前一篇 《Harness Engineering:为 AI 打造可持续迭代环境的实践》 中,我们讲了 HelixVerify 如何在线下环境用 114 次迭代 把风险样本召回率从 8% 提升到 98.86%。那是一个典型的 线下 Harness。这一篇讲 Harness 思想搬到 C 端 AIGC 生产链路后的形态 —— 蚂蚁保保险快查深度解读页面生成系统(Deep Interpretation Page Generator,以下简称 DIPG)。

DIPG 不让 C 端用户直接吃 LLM 实时生成的结果,而是把架构翻转成  "host-generate-verify-modify → DB 按品开启 → C 端直出" 。离线生成由一个带 verify 闭环的 Agentic Loop 负责,只有通过 verify 的 HTML 才会刷入 DB 并暴露给用户。实时生成只保留作为未开启品的兜底路径。即通过 Harness 的方式让 对 C 端交付的HTML 有足够好的质量。

1779014151983-5ac5e32b-2bad-4960-8ca2-0c239cf63879 (1).png

为什么不能让实时生成直出?

最开始当然试过用户点开详情页时调 LLM 实时生成 HTML 直出。不过问题也很明显:

  1. 时延扛不住:一次完整的深度解读需要 agentic 检索素材与条款、 生成几千字 HTML,LLM 推理加起来几十秒。C 端用户等不起,"秒出"是基础体验要求。
  2. 质量扛不住:LLM 生成 HTML 会出两类错 —— 渲染类错误(孤儿闭合标签、组件层级错乱)让页面直接塌掉;幻觉类错误(数据不符、编造对比)让用户读到错信息。LLM 一次过做不到 100% 正确,直出就是赌。

C 端 AIGC 交付的本质要求是:用户点开那一刻看到的 HTML,必须是已经被校验过的。所以 DIPG 把实时生成降为兜底、把离线 Harness 的生产过程作为主路径。这篇文章就讲这个离线 verify 闭环在工程上怎么搭。


一、DIPG 的两条线上链路

DIPG 对外的产品形态:用户在支付宝保险快查里打开某款产品的详情页,页面中相应模块会出现一个"深度解读"模块 —— 那里渲染的就是 DIPG 产出的 HTML 片段(750 宽度移动端容器)。前端向后端带着 prodNo 发请求,后端返回 HTML 片段。

在线上生产环境里,这个系统同时跑着两条链路 —— 离线链路是主路径,负责交付质量;实时链路是兜底:

1779014151997-c4b3b3c6-6fe7-4002-9299-16552ae88122.png

  1. 两条链路的 Research Agent 完全同源 —— 离线链路在它之上套了一层 Host Agent(负责调度 Research/Verify、并自己按 verify 反馈 patch HTML);实时链路只跑一次 Research Agent,Host 和 Verify 都不参与,产出的 HTML 不经过后置校验也不会被修正。
  2. 用户读的都是离线产物。DIPG 当前采用"离线刷入 DB + 按品维度开启"的方案:后台批量预生成并刷入 DB,只对"已开启的品"向 C 端暴露 —— 用户请求时直接从 DB 读离线产物,命中率 100%,不依赖缓存层兜底(线上还会叠加缓存做进一步加速,但那是性能优化,不是可用性前提)。实时链路仅作为未开启品的兜底生成通道存在,默认情况下 C 端看到的永远是离线产物。
  3. 离线链路--质量可控。如果实时生成能 100% 不出错,我们根本不需要离线。离线的核心价值是:把 verify 闭环的修正机会还回来,让 Harness 有施展空间。
  4. "合格 HTML 送达用户:DIPG 的外层 Graph 末端有一个 callback 节点,它把 HTML + verify 结论 + error_code 一起通过 RPC 回调发给下游(insexpert 的 deepResearchCallBack);下游根据 error_code 决定是否把这份 HTML 刷入 DB(通过 verify 的才刷)。

DIPG 内部的三个 Agent

离线链路的"带 verify 闭环"不是一个魔法盒子 —— 它内部由三个分工明确的 Agent 协作完成。这是后面所有工程讨论的概念起点:

1779014151979-cd03c13b-e148-4349-a85b-3e7ea822a6be.png

  • Host Agent —— 总编排 + 精准修正。它读到用户请求后,按"研究 → 校验 → (若未通过)修正 → 再校验"的流程派活。当 Verify Agent 返回修正意见时,Host Agent 自己在已有 HTML 上做精准编辑 (按 fix_hint 定位段落、patch 掉问题点),而不是再派一次 Research Agent 去重新生成 —— 这是 DIPG 的一个关键设计,下面 5.4 节展开。
  • Research Agent —— 只负责从零生成。它拿到产品编号后下载素材、多轮读取条款、必要时搜网络,最后产出整份 HTML 片段。它不参与修正循环 —— 修正不是它的工作,Host Agent 不会拿"按修正意见改一下"这种请求去派它。它内部也是一个完整的 ReAct Agent,有自己的工具链(第三节 3.4 展开)。
  • Verify Agent —— 只负责校验、不改 HTML。它读 HTML 产物 + Research Agent 用过的原始素材,做"程序化结构校验 + LLM 事实校验"两层检查,产出结构化的修正意见(fix_hint 列表)交给 Host Agent(第五节 5.2 展开)。

三个 Agent 都是 LangGraph 意义上的独立子图,Host Agent 通过 task 工具异步调用另两个。LangGraph 在物理层面是三层嵌套(外层 Graph / 中层 Host / 内层两个 SubAgent),在逻辑层面就是这个三角分工


二、先从一个真实 badcase 说起

在讲两条链路怎么协作之前,先看一个真实出过问题的页面。

某天巡检到一份重疾险深度解读报告在 C 端偶发渲染错位 —— 最后一个"风险提示"卡片下,下一个无关模块被挤歪了。翻开 HTML 的最后几行:

...
    <div text-card class="desc">重大疾病赔付后合同终止,轻症赔付后该项责任终止...</div>
  </div>
</div>
</div>          ← 第 201 行: 多出来的孤儿闭合标签

问题很隐蔽。整份报告的顶层本来是平铺结构(

和各种 card 组件作为兄弟元素并列),没有外层包裹

。但 LLM 凭"印象"在末尾补了一个

当作收尾。这个孤儿闭合标签进到移动端容器,被容器当成关闭自身的信号,导致下一个兄弟组件的位置错乱。

同一时期,另一份惠民保产品的深度解读出了更隐蔽的问题。页面渲染完全正常,视觉上看不出任何毛病,但"特色保障分析"模块里赫然写着:

<div class="title" highlight-card>
优于市场<span class="title-highlight" highlight-card>85%</span>同类惠民保产品
</div>

"85%"这个数字从哪来的?翻遍 Research Agent 拉到的全部素材 —— 保险条款、投保须知、健康告知 —— 没有任何关于"市场排名"或"百分位"的数据。这是一个典型的幻觉:LLM 为了让页面更有说服力,凭空编造了一个具体数字。

如果说孤儿

让页面"塌掉",这个 badcase 让页面"骗人" —— 而且骗得很体面,不翻数据源根本看不出来。

这两个 badcase 恰好对应第一节提到的两类致命错误:渲染类(孤儿闭合标签让页面塌掉)和幻觉类(无中生有的数据让用户读到假信息)。共同特征:从文字上看完全"合理",LLM 生成时也没有"犹豫",但一个违反了 HTML 结构契约,一个违反了数据事实契约。

1779014152057-2e853445-6e37-4cbd-bce1-5f4bfbaf2f09.png

对这两类问题,我们要回答两个层次:

  1. 这次能不能抓到? — 孤儿 靠纯程序化校验(HTML parser + 闭合规则)就能抓到;"优于市场 85%"则需要拿 HTML 和数据源做对比,靠 LLM 事实校验才能发现。两种校验手段正好对应 Verify Agent 内部的两个节点。
  2. 下次能不能不犯? — 这才是关键。每次都能抓到,意味着离线链路有 verify 环节可以在刷入 DB 前把问题拦下,不会让 badcase 飘到用户面前。同时,抓到之后,这些错误模式也能回灌到 prompt,让后续生成从源头减少类似错误。

三、多 Agent 是怎么组合起来的

DIPG 工程上是三层 LangGraph 结构的嵌套,这三层正好一对一承载那三个 Agent 的协作。

3.1 三层结构

1779014151990-fe9a606e-cf86-45a1-87ad-a693181ef381.png

外层用的是 langgraph.graph.StateGraph,边硬编码。这层的拓扑不依赖 LLM 决策,callback 是必经节点 —— 保证 RPC 回调一定触发。

中层Host Agent 通过 build_domain_agent_v3(blueprint, resources) 构建。Blueprint 是声明式配置:

blueprint = AgentBlueprint(
    agent_id=DIPG_AGENT_ID,                # Host Agent 的唯一标识
    instructions=_prompt,                  # 三阶段闭环 prompt
    tools=[],                              # Host 默认加载了 DeepAgents 虚拟文件系统工具
    subagents=[                            # 挂上另两个 Agent
        await get_chacha_research_sub_agent(...),   # Research Agent
        await build_chacha_verify_agent(),          # Verify Agent
    ],
    state_schema=ExtendedAgentState,
)

底层基于 LangGraph 的 create_react_agent,所以 Host Agent 本身就是一个 ReAct 循环 —— LLM 决策是否调工具、调哪个,直到模型主动停止调工具。

"DeepAgent" 是什么? deepagents 是 LangChain 团队维护的开源库(langchain-ai/deepagents)。我在其上做了模型选择、Checkpointer 注入、MCP 工具加载、audit 自动审计记录这些运行时装配。

内层Research AgentVerify Agent 都是 CompiledSubAgent,各自是独立编译好的 LangGraph 图:

  • Research Agent 内部也是一个完整的 DeepAgent —— 拥有自己的 ReAct 循环 + 工具链 + 中间件栈
  • Verify Agent 内部是一个两节点串行的 StateGraph —— structural_check → llm_verify (HelixVerify 的简化版)

3.2 SubAgent 是怎么被注入给 Host Agent 的? —— task 工具

LangGraph 里没有"直接调另一个 agent"这种原生操作,所有异构执行必须包装成工具create_task_tool 做的事:

  1. 把 Research Agent 和 Verify Agent 按 name 注册到 agents 字典
  2. 创建一个 task(description, subagent_type) 工具
  3. task 工具加到 Host Agent 的工具列表

Host Agent 的 LLM 看到的是这样一个多态工具:

task(description: str, subagent_type: str)
  ├── subagent_type="chacha_research_agent": 专业的保险产品研究助手...(即 Research Agent)
  └── subagent_type="verify_agent":          有任何报告验证的工作都交给我...(即 Verify Agent)

这个设计有两个关键后果:

  • Host Agent 不需要为每个 SubAgent 单独实现调用代码,加新 SubAgent 只需要在 blueprint 里多挂一个
  • Host Agent 的 LLM 天然用"调工具"的心智模型去编排另两个 Agent —— prompt 里告诉它"阶段 2 把报告交给 Verify Agent",它就知道调 task(description, "verify_agent")

3.3 Agent 间事件触发:task 工具内部做了什么

当 Host Agent 的 LLM 决策调 task("研究 prodNo=xxx...", "chacha_research_agent") 时,LangGraph 把调用路由到 handle_langgraph_sub_agent,它做三件事:

  • 上下文隔离:每次调用都用新 thread_id + 全新 messages,SubAgent 看不到 Host Agent 的对话历史,也看不到兄弟 SubAgent 之前跑过什么。避免 Verify Agent 被 Research Agent 的"我觉得这段挺好"之类的自述污染。
  • 单一返回值:Host Agent 只收到一条 ToolMessage,SubAgent 内部的多轮工具调用、中间推理对它不可见。这保证 Host Agent 的 context 不会被 SubAgent 的细节炸穿。
  • files 合并:SubAgent 写的 state["files"] 通过 Command.update 合并回 Host Agent 的 state["files"]。这是跨 SubAgent 共享数据的主通道。

多 Agent 的时序编排由 Host Agent 的 LLM 自己完成,不是代码写死的:

1779014153983-ab25c8f0-fdc8-489f-8a84-b356faf5730f.png

Host Agent 拿到 fix_hint 后不会再派 Research Agent,而是自己用 edit_file / write_file 工具直接在 state["files"]["report.html"] 上做局部编辑。

换句话说,这个 verify-修正闭环不是硬编码的 Graph 边,而是 prompt 层约束下的行为契约 —— 它完全由 Host Agent 按指令驱动,循环多少轮、什么时候停,是 Host Agent 自己根据 Verify Agent 的反馈判断的。

3.4 Research Agent 的工具链 + ReAct

"深度搜索"不是独立的 agent,而是 Research Agent 自己作为 ReAct agent 多轮调工具逐步收集信息。它的工具集只有三类:

sub_agent_tools = [
    download_insurance_product_materials,  # 按 产品 拉取素材
    read_disk_file,                         # 读具体素材文件
    web_search,                             # 必要时网络检索
]

通常来说,Research Agent 会尝试:

  1. LLM 读 chacha_prompt 里的 章节,明确要分析的维度
  2. download_insurance_product_materials —— 返回素材到磁盘中与素材地图,形如 {"CLAUSE": [...], "INSURE_NOTICE": [...], "HEALTH_INFORM": [...]}
  3. LLM 串联多次 read_disk_file(path) —— 逐个读条款、投保须知、健康告知
  4. 条款里缺的数据(如行业发病率),LLM 调 web_search(query) 补充
  5. 所有信息收齐后,LLM 进入"页面生成阶段",产出 HTML

这个过程由 LLM 自主决策,没有固定编排。chacha_prompt 控制:

  • 告诉 LLM 素材类型和工具能力
  • 明确"阶段一:工具调用" vs "阶段二:页面生成"不得混用

3.5 HTML 产出:写进 state["files"] 作为跨 Agent 记忆

Research Agent 的 instructions 末尾被强制追加:

instructions = prompt + (
    "\n!!!IMPORTANT: 你交付的 HTML 文件必须是 HTML 组件, 以<div>开头, 以</div>结尾。"
    "\n!!!IMPORTANT: 你 必须 (MUST) 调用 write_file 工具将报告正文写入 `report.html` 文件。"
    "这是你产出报告的唯一方式。"
)

write_file 工具并不直接写物理磁盘 —— 它通过 Command(update={"files": {...}}) 把内容写进 LangGraph state 的 files 字段。这是一个跨 Agent 共享的虚拟文件系统

Verify Agent 启动时的 structural_check 节点调 extract_report_from_files(state),从 state["files"]["report.html"] 取 HTML:

async def structural_check(state: ExtendedAgentState) -> dict[str, Any]:
    html_content = extract_report_from_files(state)
    if not html_content:
        return {"messages": [AIMessage(content="...未找到报告文件...")]}
    issues = validate_html_string(html_content)
    ...

Research Agent 和 Verify Agent 不需要通过显式的参数传递来交换 HTML —— state["files"] 作为共享 state,就是它们之间的通道

这也解释了前面反复提到的"同一份 /audit/ 数据契约"——  /audit/ 是通过上下文切面自动保存的,属于files 的子目录:


位置

写入者

读取者

生命周期

state["files"]

LangGraph state(内存中的虚拟文件系统)

write_file 工具 via Command.update

任何能访问 state 的 SubAgent

随 checkpointer 持久化

/audit/

虚拟文件系统(workspace/audit_logs/)

AuditWrapperMiddleware(包装所有工具调用)

Verify Agent 通过 read_file 读取state["files"]["audit"]

随 checkpointer 持久化

- files = 生成的产物(HTML、中间结果)

  • /audit/ = 生成的原料(工具调用的输入输出)

Verify Agent 用 ls /audit/ + read_file 组合就能读到 Research Agent 期间的全部工具调用记录。

Verify Agent 两者都要看 —— 产物 vs 原料做对齐,才能判忠实性。


四、Research Agent 的 prompt 契约:让它一次过

进入离线 verify 闭环之前,先讲注入给 Research Agent 的 prompt。这部分规则同时服务两件事:

  • 离线链路: Research Agent 一次过的质量越高,Verify Agent 需要返回的修正意见就越少,verify-修正闭环收敛得更快,计算成本更低。
  • 实时链路(兜底): 没有 Verify Agent 兜底时,prompt 的约束强度基本等于交付质量的上限。

4.1 合规用语硬规则(监管红线)

<compliance_wording>
- 禁止: "0免赔""零免赔""无免赔"   → 合规: "0免赔额""免赔额为0"
- 禁止: "100%全赔""多少都能赔"    → 合规: "责任内,赔付比例100%"
- 禁止: "储蓄险"                  → 合规: "储蓄型保险"
- 禁止: "确诊即赔"                → 合规: "首次确诊责任内疾病可赔"
...
</compliance_wording>

监管敏感词,绝不允许出现,硬约束清单。

4.2 事实性保证的 8 条规则

针对幻觉问题,prompt 里明确了 8 条事实性保证规则(节选):

  • 信源优先级:产品档案 > 保险条款 > 网络搜索 > 通用知识
  • 无数据不展示:缺失字段直接隐藏,不做任何臆测
  • 图表真实性:单点数据不准画趋势图,降级为数字卡片
  • 禁止盲目对比:没有竞品数据不得使用"优于市场 85%"(第二节那个惠民保 badcase 的直接教训)
  • 否定约束:is_state_owned: false 就严禁出现"国企/央企"

其中最有意思的是强制前置溯源:

利用模型自回归特性,在生成任何关键数据之前,先生成 HTML 注释说明数据来源。格式

数据...

。如果写不出注释,说明该数据是幻觉,必须留空。

不是求 LLM "请标注来源",而是让"写不出来源就不要写数据"变成自然的生成顺序。结构强制比语义强制有效得多。

4.3 这些规则从哪来?

简单回答:一部分是业务合规团队直接给的底线(如合规用语),但相当一部分来自离线链路 Verify Agent 反复抓到的高频错误模式。这份清单是活的,会随着 Verify Agent 的持续发现而扩充 —— 具体的回灌机制见第六节。

4.4 从 prompt 约束到 Verify 兜底的过渡

上面的 prompt 契约做得再严,LLM 也不可能 100% 遵守。每多一条规则,违规的概率会降低,但不会归零。回到第二节那个惠民保 badcase:"禁止盲目对比"写进 prompt 之后,Research Agent 写"优于市场 85%"的频率会大幅下降,但偶尔仍会出现 —— 惠民保那个 badcase 就是"写了规则仍然犯"的例子。

这正是离线链路需要 Verify Agent 兜底的根本原因:prompt 负责让一次过的概率尽量高,Verify Agent 负责把剩下那些没遵守的抓回来。两者配合,才有离线链路"每一份刷入 DB 的 HTML 都合格"的交付保证。

下面第五节进入 Verify 闭环本身。


五、Verify Agent 怎么把关交付质量

5.1 Host Agent 的三阶段闭环 prompt

Host Agent 自己也是一个 ReAct Agent, prompt 示例(原有的比较长):

#### 阶段 1|研究与生成
  委派给 chacha_research_agent(Research Agent), 输出 HTML 组件并写入文件系统
#### 阶段 2|验证循环(核心闭环)
  ┌─→ 将报告路径 + 数据供给路径(/audit 目录)提交给 verify_agent(Verify Agent)
  │       ↓
  │   Verify Agent 返回验证结论
  │       ↓
  │   ┌─ 通过 → 退出循环, 进入阶段 3
  │   └─ 未通过(返回修正意见)
  │       ↓
  │     仅按修正意见精准编辑报告, 不做任何额外更改
  │       ↓
  └──── 重新提交 Verify Agent
  循环终止的唯一条件: Verify Agent 不返回任何修正意见
  安全上限: 超过 5 轮未通过 → 暂停并汇报分歧点
#### 阶段 3|交付
  输出最终报告路径 + 验证摘要 + 修正记录

1779014153994-9a60d763-a79c-4eea-8b66-1361add5dd05.png

5.2 Verify Agent 内部:程序化 + LLM

Verify Agent 由两节点子图组成:

1779014153985-ffbb6dba-3b58-4b87-baee-46b26941092b.png

structural_check(程序化校验) —— 纯 Python,基于 html.parser.HTMLParser 自定义的 StructureParser,检查若干条确定性规则:

规则编号

检查内容

rule1

<style> 标签不应出现在片段中

rule3

<h2> 之间必须有实质内容(防止连续空 h2)

rule4

<h2> 文本不得手动加序号("1. ""① "等)

rule5

标签完全闭合 / 无孤儿闭合标签 / 无交叉嵌套

rule6

<h2> 必须在顶层,不能被非组件 <div> 包裹

rule7

禁止内容重复

其他

reference-card 位置规范、标题仅允许 h2 等

(编号不连续是代码的历史原因——早期规则筛选后保留了原始编号。)

毫秒级响应,零假阳性。前面那个 badcase —— 孤儿

—— 就是被 rule5 的 TAG_ORPHAN 直接命中的。而第二节那个惠民保"优于市场 85%"的 badcase,structural_check 对它无话可说(HTML 结构没问题)—— 它需要下一个节点来抓。

llm_verify(语义 + 事实校验) —— 消费 /audit/ 下的原始数据供给 + 生成的 HTML,产出结构化 JSON。继续跟踪惠民保 badcase —— Host Agent 把报告交给 Verify Agent 校验时,传入的任务描述:

{
  "description": "请验证报告文件 report.html 的内容准确性。数据供给目录为 /audit 目录。请检查:1. 报告中的保障责任数据是否与原始资料一致 2. 保额、赔付比例等数字是否准确 3. 免责条款描述是否正确 4. 产品基本信息是否准确",
  "subagent_type": "verify_agent"
}

Verify Agent 遍历 /audit/ 下的全部工具调用记录,发现没有任何数据源包含"市场排名"或"百分位"信息,产出结构化 JSON 结论:

{
  "fact_accuracy_score": 3,
  "html_format_score": 5,
  "fact_accuracy_issues": [
    {
      "score": 3,
      "subcategory": "页面数据遵循供给",
      "detail": [{
        "module": "特色保障分析",
        "desc": "存在无数据支撑的市场排名表述",
        "evidence": "报告中写'优于市场85%同类惠民保产品',但数据供给(保险条款、投保须知)中没有任何关于市场排名或百分比的数据",
        "fix_hint": "删除'优于市场85%同类惠民保产品'的表述,或改为基于条款的客观描述如'保障责任较为全面'"
      }]
    },,,
  ],
  "html_format_issues": []
}

注意两个细节:一是 html_format_score: 5 —— structural_check 已确认结构没问题,LLM 不重复检查;二是两条 issue 从不同角度("页面数据遵循供给" / "无供给时不捏造")指向同一处幻觉,fix_hint 都给出了具体的替换建议。

两个节点的分工原则:能用程序判定的,不让 LLM 看。 Verify Agent 的 system prompt 里明确告诉 LLM:

程序化 HTML 校验结果:在你的消息历史中,会有一条 [程序化HTML校验结果] 消息……请直接信任并引用程序化校验发现的所有 ERROR 和 WARN。

这样一来,LLM 的 token 预算全部投给它真正擅长的事实性判断,不浪费在数"有几个未闭合标签"这种机械活上。

**llm_verify 用什么模型? Verify Agent 的模型和 Research Agent 的模型最好是不同选型,这样可以减轻"同一模型既当运动员又当裁判"带来的偏置,Verify Agent 的判定更接近独立观察者。

5.3 Verify Agent 必须看得到生产原料 —— /audit/

这是容易被忽略但非常重要的设计。Verify Agent 要判"数据是否忠实",光看 HTML 是不够的 —— 必须对比 HTML 里的每个数字和 Research Agent 当初拿到的原始数据供给

做法是:Research Agent 每次调用 downloadread_disk_fileweb_search,审计 middleware 把工具输入输出写到 /audit/ 目录。Verify Agent 能读 /audit/,就看得到生成时依赖的每一份原料:

/audit/
  ├── download_insurance_product_materials_<ts>.json   ← 产品素材列表
  ├── read_disk_file_<ts>.json                         ← 每次读素材
  ├── web_search_<ts>.json                             ← 网络搜索结果
  └── ...

这样,"声称优于市场 85%,但没有市场对比数据"这类幻觉就能被抓到 —— /audit/ 里任何工具返回都找不到"市场对比"。

架构意义:Verify Agent 不是对 HTML 做静态语言分析,而是对 HTML 和它的数据源做对齐分析。缺少 /audit/ 这一层,事实性校验就失去工程意义。

5.4 强制闭环 —— 靠 Host Agent 的 prompt 守纪律

到这里有个关键的设计选择需要解释:修正动作由谁来做?

直觉上有两条路:

  • 路 A:再派一次 Research Agent —— 让 Research Agent 重新生成一份。
  • 路 B:Host Agent 自己 patch —— Host Agent 直接在已有 HTML 上按 fix_hint 做局部编辑。

DIPG 走的是路 B。这不是细节差异,是体系性的设计选择:

  1. Research Agent 不擅长改,只擅长生成。它的 prompt 和工具链都是为"理解需求 → 拉素材 → 综合产出整份报告"设计的。一旦再派给它"按以下意见修正",它会重新进入研究模式,容易全盘重写 —— 把好的部分一起改掉。这就违反了 prompt 里那条"精准编辑优于全盘重写"。
  2. Verify Agent 给的是已经精确定位过的 fix_hint(含 module 名、evidence 行号、具体修正动作),修正动作此时已经退化成"在已有文档里找到 X,改成 Y"这种轻量编辑。这件事完全不需要再启动一次素材调研流程 —— Host Agent 直接调 edit_file / write_file 工具就能完成。
  3. Host Agent 拿着完整的 verify 反馈上下文(它刚通过 task 工具调用 Verify Agent 拿到 fix_hint),让它就地改是最直接的;而再派 Research Agent,反而要把 fix_hint 序列化成自然语言任务描述传过去,损失精度。

所以 DIPG 的离线链路里,三个 Agent 的分工是不对称的:

Agent

在闭环里被调用的次数

职责

Research Agent

只在第 1 轮被调一次(从零生成)

创造

Verify Agent

每轮被调一次

校验 + 提 fix_hint

Host Agent

全程在线

编排 + 自己按 fix_hint 精准修正 HTML

这也就是 prompt 里那两条硬性约束的真实意图:

  • "HTML 只能由 chacha_research_agent 产出,你不可自行编写" —— 指 Host 不可从零写整份(那是 Research 的事)
  • "仅按修正意见精准编辑报告,不做任何额外更改" —— 指 Host 可以而且应该做局部 patch,但只限于 verify 提到的点

两条结合起来才是完整的语义:Host 不创造,只修正;修正只动 verify 指出的地方

1779014154121-039fe21a-d748-420f-baad-3581273f4337.png

继续跟踪第二节那个惠民保 badcase。Verify Agent 返回的两条 fix_hint 都指向同一处:"优于市场 85% 同类惠民保产品"。Host Agent 拿到后,直接调 edit_filestate["files"]["report.html"] 上做精准替换:

edit_file(
  file_path = "report.html",
  old_string = "<div class=\"title\" highlight-card>\n优于市场
                 <span class=\"title-highlight\" highlight-card>85%同类惠民保产品</span>  \n</div>",
 new_string = "<div class="title" highlight-card>\n保障责任
                 <span class="title-highlight" highlight-card>较为全面全面\n</div>")
→ Successfully replaced 1 instance(s)


Host Agent 按 fix_hint 里"改为基于条款的客观描述如'保障责任较为全面'"的建议,只改了这一处,没动报告的其他部分。改完后再次调 task("verify_agent") 做二次校验 —— 这次 Verify Agent 不再返回修正意见,闭环收敛,进入阶段 3 交付。Research Agent 全程不参与修正,它在第一轮交付完 HTML 之后就退场了。

这就是为什么我们不做成硬编码的 LangGraph 边,而是通过 Host Agent 的 prompt 层行为契约来驱动这个三方协作:

  • HTML 的从零生成只能由 Research Agent 完成,Host 不可自行从零编写
  • HTML 的修正由 Host 自己完成,只能执行 Verify Agent 给出的 fix_hint,禁止附加其他更改
  • 循环终止的唯一条件是 Verify Agent 不再返回修正意见

其中"5 轮"是异常兜底上限:

  • 正常情况下,Verify 的意见应在 1~3 轮内被 Host 完全 patch 完,循环自然结束。跑到 4、5 轮已经是少数。
  • 如果真的 5 轮仍未通过,通常不是生成/校验本身的问题,而是意外情况 —— 比如素材里缺了某个关键数据,Host 修不出 Verify 想要的事实,双方拉锯。这时候应该停下来,而不是继续 LLM 互卷
  • 到上限时,Host 会暂停并把分歧点抛给下游(error_code 透传到 callback),由业务侧决定怎么处理(通常是标记"待人工介入")。


六、让生成过程更可靠

到这里为止,离线 verify 闭环就是一个标准的 Agentic 闭环 —— 它的直接价值已经实现:只有通过 Verify Agent 的 HTML 才刷入 DB,C 端用户能看到的就是合格产物。

但我们还需要把它高频发现的错误模式回灌给 Research Agent 的 prompt,让下一轮生成从源头减少类似错误。

6.1 两重价值的区别

价值

作用对象

生效时机

把关(直接价值)

当次生成的 HTML

离线生成时立即生效:不合格的产物不刷入 DB

回灌(间接价值)

后续所有生成(含实时兜底)

下次生成时生效:Research Agent "一次过"的概率更高

注意区别:离线链路本身不依赖回灌 —— 哪怕回灌机制完全不存在,离线链路凭 Verify Agent 把关也足以保证交付质量。但有了回灌,离线链路的 verify-修正闭环收敛更快(Verify Agent 需要抓的高频错更少),实时兜底链路的出错率也随之下降。

6.2 回灌流程:半自动提炼

当前的回灌流程是半自动的:

1779014154026-272fcb7c-e76a-4674-bb75-f71dd455e4b9.png

举一个具体例子。早期 LLM 经常把"集团/母公司"的数据(总资产、世界 500 强排名)直接套用到"子公司/产品"上 —— 比如某子公司被写成"世界 500 强第 25 位",而那其实是其集团的排名。离线 Verify Agent 反复抓到这类实体对齐错误,修正意见高频出现。我们把它抽象成一条通用规则写进 Research Agent 的 prompt:

实体对齐(信源优先级原则的子条款):严禁混用主体。禁止将"集团/母公司"的数据(如总资产、世界 500 强排名)直接套用到"子公司/产品"上,除非明确说明是"依托于集团"。

第二节那个惠民保"优于市场 85%"的 badcase 也走了同样的路径。Verify Agent 反复抓到"数据供给中不存在市场排名数据,但 HTML 里出现了具体百分位"的 issue,聚合后写进了 Research Agent 的 prompt:

禁止盲目对比:没有竞品数据不得使用"优于市场 XX%"等市场排名表述。缺少明确数据支撑时,使用基于条款本身的客观特色描述替代。

至此,这个 badcase 走完了从"Verify 抓到 → Host 修正 → 回灌 prompt → 下次不犯"的完整闭环。

再举一个 —— 第二节的另一个 badcase(孤儿

)。我们保留 structural_check 的 rule5 在离线 Verify Agent 里兜底,同时更新 Research Agent 的 prompt:

严禁额外包裹容器:不要在输出的最外层添加任何包裹性质的

容器标签。组件(head-card、text-card、table-card 等)和

标题必须直接作为顶层元素平铺输出,彼此之间保持平级关系。

规则写进 prompt 之后,Verify Agent 抓这类错误的频率将会下降-- 不是 Verify 变弱了,是 Research Agent 一次过得更多。

6.3 为什么这个回灌是必要的

只有把关、没有回灌会怎样?—— 离线链路会无限跑下去,每次都要 Verify Agent 抓一堆相同的错,然后 Host Agent 按一堆相同的 fix_hint 反复 patch。计算成本浪费,且不收敛。

有了回灌,Verify Agent 发现的每条 issue 都成了 Research Agent prompt 的"弱监督信号"。当信号累积到足够量,就蒸馏成硬规则,进入 Research Agent 的默认纪律。Verify Agent 不只是质检员,它同时在替 Research Agent 的 prompt 产出训练信号 —— 这就是第四节 4.3 留的问题(Research Agent prompt 里那些规则从哪来)的完整答案。

离线链路每运行一段时间,就把一批 Verify 发现蒸馏进 Research prompt,整个系统的一次过能力就进一步前移到生成侧 —— 这也是 DIPG 质量能持续演进而非停留在某个固定水位的根本原因。


七、把视野再拉大:三级 Harness 嵌套

继续往外拉一层,你会看到整个 DIPG 系统实际上是三级嵌套的 Harness 反馈回路,跨越了线下 / 线上、离线 / 实时四个象限:

1779014155049-a0d95993-7edd-491d-a19b-13ade98c08c9.png

每一层 loop 的时间尺度差了一到两个数量级,但它们共享:

  • 同一个 Verify Agent(Level 3 迭代它,Level 2 使用它)
  • 同一份 Research Agent 的 prompt chacha_prompt.py(Level 2 蒸馏出规则,Level 1 跟着升级)
  • 同一套 /audit/ 数据契约
  • 同一组 benchmark 样本(线上 badcase 回流补充)

注意三级Harness的角色分工:

  • Level 3 的意义是让 verify 本身越来越强(召回更准、误报更少)
  • Level 2 是 DIPG 的主干,真正决定"用户看到什么"的那条链
  • Level 1 是兜底,仅在"品未开启"或"DB 暂未写入"的情况下被触发,常态下 C 端看不到它的产物

我觉得这是目前 Harness Engineering 在 C 端 AIGC 场景下的完整形态: ** 线下迭代 verify 能力 → 线上离线用 verify 把关主交付 + 沉淀 prompt → 线上实时靠升级后的 prompt 兜底 ** 。三层各司其职,且每一层的输出都喂给下一层当输入。


八、线上 AIGC 的 Harness 踩坑经验

1779014155039-bf198b76-f356-4b6b-a6de-85f07b752c91.png

经验 1:不要让 LLM 实时产物直出给 C 端用户

当产物质量事关用户体验,且 LLM 难以一次过时,在链路架构上就应该把"实时直出"排除在主路径之外。改为"离线生成 + Harness 把关 + 刷入数据/存储层 + 按需直出",实时只留作兜底。

经验 2:生成器代码 / prompt 在两条链路之间严格同源

离线链路的所有改进(包括 Verify Agent 发现回灌 prompt、新加的合规规则等)能自动传导到实时兜底链路 —— 前提是两条链路共用同一份 Research Agent 代码和同一份 Research prompt。一旦分叉,后续收益就断了。

经验 3:能用确定性程序判定的,不要留给 LLM 判

LLM 不擅长数标签、对正则。把这类 check 交给 HTMLParser + 规则函数,LLM 的 token 投给语义层判断。两层分工明确,token 效率和准确率都更高。

经验 4:verify 必须看得到生产原料

事实性校验不是对 HTML 做语言学分析,而是对"HTML 数值 vs 数据源"做对齐。/audit/ 这类审计通道是事实性校验的前提。


结语

HelixVerify 告诉我们 AI 可以自举到自主迭代。DIPG 在这个基础上更进一步:AI 实时生成的产物,可以因为一条带 verify 闭环的离线主路径,变得 C 端可交付

核心设计其实很简单,但概念上需要翻转:

C 端 AIGC 不应该把"实时生成给用户"作为默认假设。默认假设应该是 "离线生成 → Harness 把关 → 持久化产物给用户",实时只作为兜底。

离线 Harness 在这里承担两重价值:直接价值是不合格的 HTML 不会被刷入 DB、不会被 C 端看到,间接价值是高频错误蒸馏回 prompt 减少下次犯错。这两重价值不是可选项 —— 没有它们,C 端 AIGC 的质量不可收敛。

这个模式可以迁移到其他"AI 产物直接面对用户"的场景:

  • AI 生成的图表、图片、视频:离线跑合规 + 质量 verify,合格才入 CDN;实时兜底只在极少数未覆盖场景下触发
  • AI 写的文档、摘要:离线跑事实 + 风格 verify,合格才入产品;实时兜底场景有限
  • AI 生成的营销素材、广告词:离线跑监管 + 事实 verify,合格才投放

:本文里,线上 / 线下指部署环境(生产 / 测试),实时 / 离线指调用模式(单次请求 / 批量任务)。这两组概念正交。DIPG 的故事主要发生在线上环境里的离线链路上;实时链路是兜底;HelixVerify 则跑在线下,持续迭代离线链路用的 verify agent。

本文中的 DIPG 项目由团队共同建设,感谢 @时越、@其奔、 @雪霜、@千璃 各位老师和同学的指导与支持。DIPG 的 Research Agent Prompt 主要由 @雪霜 实现。

如果有问题或者希望有更多讨论欢迎联系笔者 @晓灰 ,邮箱:xiaohui.wyh@antgroup.com



来源  |  阿里云开发者公众号

作者  |  晓灰


相关文章
|
15天前
|
人工智能 自然语言处理 文字识别
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
Qwen3.7-Max是阿里云百炼面向智能体时代推出的新一代旗舰模型,对标GPT-5.5、Claude Opus 4.7等闭源旗舰。该模型支持百万级token上下文窗口,具备顶级推理能力、多模态搜索与视觉理解增强、流式输出低延迟响应等核心优势,覆盖编程、办公、长周期自主执行等复杂场景。同时支持OpenAI接口兼容,便于系统快速迁移。用户可通过Token Plan团队或节省计划等订阅方式灵活调用,适合企业级高要求场景使用。
5728 29
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
|
10天前
|
存储 定位技术 数据库
CodeGraph 如何让 Claude Code减少 7 成工具调用?
CodeGraph 为 Coding Agent 提供本地代码知识图谱,把函数、类、调用链和框架路由提前整理成“项目地图”,减少盲目搜索和文件读取。它不是新 Agent,而是上下文基础设施,让 Agent 更快找到正确代码路径,平均减少 7 成工具调用。
1165 2
|
7天前
|
人工智能 安全 定位技术
CodeGraph深度解析 让Claude Code工具调用直降七成的核心原理与实操教程
如今以Claude Code为代表的AI编程智能体已经成为开发者日常编码、项目重构、漏洞修复的必备工具。但在长期使用过程中,几乎所有开发者都会遇到同一个明显痛点:AI虽然具备强大的代码生成与分析能力,却常常陷入盲目探索的循环中。
927 1
|
17天前
|
人工智能 自然语言处理 供应链
|
7天前
|
人工智能 弹性计算 安全
阿里云618活动时间、活动入口、优惠活动详细解读
2026年阿里云618创新加速季已全面开启,作为年度力度最大的云产品促销活动,本次大促覆盖轻量应用服务器、ECS云服务器、GPU云服务器、数据库、AI算力、安全服务、CDN等全品类产品,推出5亿元算力补贴、新用户限时秒杀、普惠满减、企业专享、免费试用、云大使返佣等多重福利,个人开发者、中小企业、AI团队均可享受专属低价。本文将系统梳理2026年阿里云618活动的完整时间节点、官方参与入口、各类优惠细则、使用规则、热门产品推荐及实操代码,帮助用户精准参与、高效省钱,以最低成本完成上云部署。
704 3
|
23天前
|
人工智能 开发工具 iOS开发
Claude Code 新手完全上手指南:安装、国产模型配置与常用命令全解
Claude Code 是一款运行在终端环境中的 AI 编程助手,能够直接在命令行中完成代码生成、项目分析、文件修改、命令执行、Git 管理等开发全流程工作。它最大的特点是**任务驱动、终端原生、轻量高效、多模型兼容**,无需图形界面、不依赖 IDE 插件,能够深度融入开发者日常工作流。
3826 15
|
8天前
|
运维
欢迎报名|2026 Agentic AICon—智能体基础设施与AgentOps专场,邀您参会
欢迎报名|2026 Agentic AICon—智能体基础设施与AgentOps专场,邀您参会
1421 0