---
source_url: "https://mp.weixin.qq.com/s/ys_yLo-xP6Hy0PnHYXHEMg""
ingested: 2026-06-26
sha256: 2539a6f1fca08b7f
---
sha256: 1e6b0a05320208e7
---
title: "DIPG 蚂蚁保 保险快查 深度解读页面生成系统:Host-Research-Verify 三 Agent 离线 verify 闭环"
source_url: "https://mp.weixin.qq.com/s/ys_yLo-xP6Hy0PnHYXHEMg"
author: "晓灰 @antgroup.com"
feed_name: "蚂蚁集团技术"
publish_date: 2026-06-01
created: 2026-06-01
ingested: 2026-06-01
tags:
- dipg
- ant-group
- harness-engineering
- multi-agent
- host-research-verify
- offline-verify
- aigc
- c-end
- langgraph
- deepagents
- audit
- prompt-backflow
- wechat
type: article
review_value: 9
review_confidence: 9
review_recommendation: strong
review_stars: 5
sha256: a1b2c3d4e5f6789abcdef0123456789abcdef0123456789abcdef0123456789ab
---
# DIPG 蚂蚁保 保险快查 深度解读页面生成系统:Host-Research-Verify 三 Agent 离线 verify 闭环
> 来源:微信公众号|作者:晓灰 @antgroup.com|2026-06-01
## 核心设计:架构翻转
DIPG(Deep Interpretation Page Generator)是蚂蚁保保险快查的 C 端 AIGC 深度解读页面生成系统。它的核心创新是**架构翻转**:
> 不让 C 端用户直接吃 LLM 实时生成的结果,把架构翻转成 **"host-generate-verify-modify → DB 按品开启 → C 端直出"**。
- 离线生成:带 verify 闭环的 Agentic Loop 负责,只有通过 verify 的 HTML 才刷入 DB 并暴露给用户
- 实时生成:保留为未开启品的兜底路径
## 为什么不能让实时生成直出
### 1. 时延扛不住
一次完整的深度解读需要 agentic 检索素材与条款 + 生成几千字 HTML,LLM 推理加起来几十秒。C 端用户等不起,"秒出"是基础体验要求。
### 2. 质量扛不住
LLM 生成 HTML 出两类错:
- **渲染类错误**(孤儿闭合标签、组件层级错乱)让页面直接塌掉
- **幻觉类错误**(数据不符、编造对比)让用户读到错信息
LLM 一次过做不到 100% 正确,直出就是赌。C 端 AIGC 交付的本质要求是:用户点开那一刻看到的 HTML 必须已经被校验过。
## DIPG 的两条线上链路
| 链路 | 定位 | 角色 | 用户看到 |
|------|------|------|---------|
| **离线链路** | 主路径 | Host Agent 调度 Research + Verify,verify 不通过则 patch | 默认可见 |
| **实时链路** | 兜底 | 只跑 Research Agent,不经过 verify | 默认不可见(仅"未开启品"用) |
两条链路的 **Research Agent 完全同源**——离线链路在它之上套了一层 Host Agent(调度 Research/Verify、按 verify 反馈 patch HTML);实时链路只跑一次 Research Agent,Host 和 Verify 都不参与。
DIPG 当前采用"离线刷入 DB + 按品维度开启":后台批量预生成并刷入 DB,只对"已开启的品"向 C 端暴露——用户请求时直接从 DB 读离线产物,命中率 100%,不依赖缓存层兜底。
## DIPG 内部的 3 个 Agent(三角分工)
### Host Agent — 总编排 + 精准修正
读到用户请求后按"研究 → 校验 → (若未通过)修正 → 再校验"的流程派活。当 Verify Agent 返回修正意见时,**Host Agent 自己在已有 HTML 上做精准编辑**(按 fix_hint 定位段落、patch 掉问题点),而不是再派一次 Research Agent 重新生成。
### Research Agent — 只负责从零生成
拿到产品编号后下载素材、多轮读取条款、必要时搜网络,最后产出整份 HTML 片段。**不参与修正循环**——修正不是它的工作。内部也是完整 ReAct Agent,有自己的工具链。
### Verify Agent — 只负责校验、不改 HTML
读 HTML 产物 + Research Agent 用过的原始素材,做"程序化结构校验 + LLM 事实校验"两层检查,产出结构化的修正意见(fix_hint 列表)。
### 3 个 Agent 协作的 LangGraph 实现
三个 Agent 都是 LangGraph 意义上的独立子图,Host Agent 通过 `task` 工具异步调用另两个。LangGraph 物理上是**三层嵌套**(外层 Graph / 中层 Host / 内层两个 SubAgent),逻辑上就是三角分工。
## 真实 badcase:两类致命错误
### Badcase 1:孤儿 `` 让页面塌掉
某天巡检到一份重疾险深度解读报告在 C 端偶发渲染错位——最后一个"风险提示"卡片下,下一个无关模块被挤歪了。HTML 末尾多了一个孤儿闭合标签,进到移动端容器被当成关闭自身的信号,导致下一个兄弟组件位置错乱。
**问题很隐蔽**:整份报告顶层是平铺结构(`
` 和各种 card 组件作为兄弟元素并列),没有外层包裹 `
`,但 LLM 凭"印象"在末尾补了一个 `
` 当收尾。
### Badcase 2:惠民保"优于市场 85%" 幻觉
"特色保障分析"模块赫然写着"优于市场 85% 同类惠民保产品"。翻遍 Research Agent 拉到的全部素材——保险条款、投保须知、健康告知——**没有任何关于"市场排名"或"百分位"的数据**。
> 孤儿 `` 让页面"塌掉",这个 badcase 让页面"骗人"——而且骗得很体面,不翻数据源根本看不出来。
## 多 Agent 物理实现:三层 LangGraph 嵌套
### 三层结构
| 层 | 角色 | 拓扑 | 决策 |
|----|------|------|------|
| 外层 | StateGraph(边硬编码) | callback 必经节点 | 不依赖 LLM 决策 |
| 中层 | Host Agent(build_domain_agent_v3) | blueprint 声明式配置 | ReAct 循环 |
| 内层 | Research Agent + Verify Agent | CompiledSubAgent | 各自独立 LangGraph 图 |
### `task` 工具:SubAgent 注入机制
LangGraph 里没有"直接调另一个 agent"的原生操作,所有异构执行必须包装成工具。`create_task_tool` 做的事:
1. 把 Research Agent 和 Verify Agent 按 name 注册到 `agents` 字典
2. 创建一个 `task(description, subagent_type)` 工具
3. 把 `task` 工具加到 Host Agent 的工具列表
Host Agent 看到的是这样一个**多态工具**:
```
task(description: str, subagent_type: str)
├── subagent_type="chacha_research_agent": 保险产品研究助手
└── subagent_type="verify_agent": 报告验证
```
### `task` 内部三件事
- **上下文隔离**:每次调用都用新 thread_id + 全新 messages,SubAgent 看不到 Host 的对话历史,也看不到兄弟 SubAgent 之前跑过什么。避免 Verify Agent 被 Research Agent 的"我觉得这段挺好"之类的自述污染
- **单一返回值**:Host Agent 只收到一条 ToolMessage,SubAgent 内部的多轮工具调用、中间推理对它不可见
- **files 合并**:SubAgent 写的 `state["files"]` 通过 `Command.update` 合并回 Host 的 `state["files"]`。跨 SubAgent 共享数据的主通道
## state["files"] 与 /audit/ 数据契约
### state["files"]:跨 Agent 共享的虚拟文件系统
Research Agent 的 `write_file` 工具并不直接写物理磁盘——它通过 `Command(update={"files": {...}})` 把内容写进 LangGraph state 的 `files` 字段。Verify Agent 启动时的 `structural_check` 节点调 `extract_report_from_files(state)` 从 `state["files"]["report.html"]` 取 HTML。
Research Agent 和 Verify Agent **不需要通过显式的参数传递来交换 HTML**——`state["files"]` 作为共享 state 就是它们之间的通道。
### /audit/:生成原料(工具调用记录)
Verify Agent 要判"数据是否忠实",光看 HTML 不够——必须对比 HTML 里的每个数字和 Research Agent 当初拿到的原始数据供给。AuditWrapperMiddleware 包装所有工具调用,把 Research Agent 每次调 `download`、`read_disk_file`、`web_search` 的输入输出写到 `/audit/` 目录:
```
/audit/
├── download_insurance_product_materials_.json ← 产品素材列表
├── read_disk_file_.json ← 每次读素材
├── web_search_.json ← 网络搜索结果
```
| 位置 | 写入者 | 读取者 | 生命周期 |
|------|--------|--------|----------|
| `state["files"]` | write_file 工具 via Command.update | 任何能访问 state 的 SubAgent | 随 checkpointer 持久化 |
| `/audit/` | AuditWrapperMiddleware 包装所有工具调用 | Verify Agent 通过 read_file | 随 checkpointer 持久化 |
> `files` = 生成的产物(HTML、中间结果)。`/audit/` = 生成的原料(工具调用的输入输出)。Verify Agent 用 `ls /audit/` + `read_file` 就能读到 Research Agent 期间的全部工具调用记录。
## Research Agent 的 prompt 契约
### 4.1 合规用语硬规则(监管红线)
```
- 禁止: "0免赔""零免赔""无免赔" → 合规: "0免赔额""免赔额为0"
- 禁止: "100%全赔""多少都能赔" → 合规: "责任内,赔付比例100%"
- 禁止: "储蓄险" → 合规: "储蓄型保险"
- 禁止: "确诊即赔" → 合规: "首次确诊责任内疾病可赔"
...
```
监管敏感词绝不允许出现,硬约束清单。
### 4.2 事实性保证的 8 条规则
针对幻觉问题:
- **信源优先级**:产品档案 > 保险条款 > 网络搜索 > 通用知识
- **无数据不展示**:缺失字段直接隐藏,不做任何臆测
- **图表真实性**:单点数据不准画趋势图,降级为数字卡片
- **禁止盲目对比**:没有竞品数据不得使用"优于市场 85%"
- **否定约束**:`is_state_owned: false` 就严禁出现"国企/央企"
### 强制前置溯源(最关键的一条)
利用模型自回归特性,在生成任何关键数据之前,**先生成 HTML 注释说明数据来源**:
```html
数据...
```
如果写不出注释,说明该数据是幻觉,必须留空。
> 不是求 LLM "请标注来源",而是让"写不出来源就不要写数据"变成自然的生成顺序。**结构强制比语义强制有效得多。**
## Verify Agent 两层校验
### structural_check(程序化校验)
纯 Python,基于 `html.parser.HTMLParser` 自定义的 `StructureParser`,检查确定性规则:
| 规则 | 检查内容 |
|------|----------|
| rule1 | `