Doc Agent Document Generation
· · ·
题目进度 0 / 0 ✓ 0
ODTS-55: Doc Agent 文档生成服务——交易合同 PDF 的工厂
目标读者:想知道 ODTS 的合同 PDF 是如何从模板+交易数据自动生成的 BA/PM
相关文档:ODTS-17 (交易确认), ODTS-31 (合同运营)
为什么 BA/PM 需要理解文档生成
Doc Agent 是”交易数据变成法律文件”的关键环节。这个环节出错没有中间状态——要么生成了正确的 PDF,要么没生成。没有”差不多能用”的确认书。
文档生成失败的真实代价:
- 确认书生成失败 = T+1 发不出: 如果 Doc Agent 生成 PDF 失败(模板语法错误、HCP 存储不可用、服务 OOM),确认书无法在 T+1 发给客户。系统可能显示”生成失败”,但运营不一定立刻发现——确认书发送通常在 EOD 批处理中异步执行。如果运营到 T+2 才发现 20 份确认书没生成,所有交易在法律上处于”未确认”状态。客户可能拒绝承担 T+1 到 T+2 之间的市场风险。对于波动率大的标的(比如雪球挂钩的中证 500),两天的 P&L 差异可能达 2-3%。
- 模板错误 = 错误合同发出去: 运营在 FreeMarker 模板中修改措辞,误改了金额占位符:
${dataSource.notional}变成${dataSource.coupon},确认书上名义本金显示 100 万而不是 1000 万。客户看到确认书后可能按 100 万签署——然后要求只承担 100 万的风险。更正这样的错误需要:重新生成确认书、请客户重新签署、废弃前一份。客户体验极差,可能质疑交易台的严谨性。 某英国投行曾因确认书金额错误,客户假装没看到错误直接签署,后续实际交易按正确金额执行时,客户提出争议——这种”故意误解”在衍生品行业有先例。 - 回退到 Word 版本的人力成本: 当自动生成失败时,运营需要从 Apache POI 生成 Word 版本,手动导出为 PDF,通过邮件发送。每次人工生成耗时 15-30 分钟。如果月均 30 次回退 = 每月 7.5-15 小时的纯手工劳动。 更重要的是,人工生成的 PDF 不存入 HCP,无法被系统追溯——系统里记录的是”生成失败”,而不是”人工已补发”。
- 模板版本混乱: 每个新产品类型需要新模板。如果 Doc Agent 的模板选择逻辑(
{type}/{productType}_{variant}.ftl)没有及时更新,系统可能选错模板——比如用 vanilla_option 模板生成雪球确认书,确认书里完全没有敲入敲出条款。这是一种监管风险:客户签署了一份条款缺失的确认书,重大条款未披露。
概述
Doc Agent 是 ODTS 的文档生成服务,通过 HTTP REST 将结构化交易数据渲染为 PDF 合同。它作为独立部署的服务,统一接管了确认书、ISDA 协议、CSA 附件等法律文件的 PDF 生成工作。
与传统的 Apache POI .docx 模板方案相比,Doc Agent 专为 PDF 输出优化,支持更复杂的排版和批量生成。
API 定义
生成文档
POST /doc-agent/generate
Content-Type: application/json
{
"templateId": "confirm_snowball_v3",
"templateType": "CONFIRMATION",
"dataSource": {
"contractId": "SNB202507001",
"counterpartyName": "某某投资有限公司",
"tradeDate": "2025-07-16",
"effectiveDate": "2025-07-18",
"maturityDate": "2026-01-18",
"notional": "10000000",
"currency": "CNY",
"underlying": "600519.SH",
"underlyingName": "贵州茅台",
"knockInLevel": "75%",
"knockOutLevel": "103%",
"coupon": "18% p.a.",
"couponType": "SNOWBALL",
"barrierType": "EUROPEAN",
"dayCount": "ACT/365",
"settlement": "T+2",
"deliveryType": "CASH"
},
"outputFormat": "PDF",
"options": {
"watermark": "CONFIDENTIAL",
"language": "zh-CN",
"includeSignature": true
}
}
响应:
{
"docId": "DOC20250716001",
"status": "SUCCESS",
"url": "/hcp/confirm/SNB202507001.pdf",
"storageType": "HCP",
"pages": 12,
"fileSize": 245678,
"generatedAt": "2025-07-16T10:30:00+08:00"
}
查询生成状态
GET /doc-agent/status/{docId}
响应:
{
"docId": "DOC20250716001",
"status": "GENERATED",
"generatedAt": "2025-07-16T10:30:00+08:00",
"url": "/hcp/confirm/SNB202507001.pdf"
}
支持的文档类型
| 类型常量 | 文档类型 | 模板变量数量 | 生成时机 |
|---|---|---|---|
CONFIRMATION | 交易确认书 | 50+ | 每笔交易 T+1 |
ISDA | ISDA 主协议 | 30+ | 客户入市 |
CSA | 信用支持附件 | 25+ | 客户入市 |
AMENDMENT | 修改协议 | 20+ | 事件触发 |
MARGIN_STATEMENT | 保证金对账单 | 15+ | 日终/周终 |
REPORT | 监管报告 | 40+ | 日终批处理 |
模板系统
模板存储
Doc Agent 的模板按以下结构组织:
doc-agent/templates/
├── confirmation/
│ ├── snowball_v3.ftl ← FreeMarker 模板
│ ├── snowball_v2.ftl
│ ├── trs_stock.ftl
│ ├── vanilla_option.ftl
│ └── ndf_swap.ftl
├── isda/
│ ├── master_agreement.ftl
│ └── schedule.ftl
├── csa/
│ ├── csa_sac.ftl
│ └── csa_isda.ftl
├── amendment/
│ └── standard.ftl
└── margin/
├── daily_statement.ftl
└── call_notice.ftl
模板使用 FreeMarker 模板引擎,支持复杂条件逻辑和循环:
<#-- Snowball 确认书模板片段 -->
<h2>障碍条款</h2>
<table>
<tr><td>敲入水平</td><td>${dataSource.knockInLevel}</td></tr>
<tr><td>敲出水平</td><td>${dataSource.knockOutLevel}</td></tr>
<#if dataSource.barrierType == "EUROPEAN">
<tr><td>障碍观察</td><td>仅到期日</td></tr>
<#else>
<tr><td>障碍观察</td><td>每日</td></tr>
</#if>
</table>
模板选择逻辑
系统根据templateType + 产品类型 选择模板。若匹配不到精确模板,按以下优先级回退:
{templateType}/{productType}_{variant}.ftl→ 精确匹配{templateType}/{productType}.ftl→ 产品级匹配{templateType}/default.ftl→ 类型默认模板
与 Apache POI 的共存
| 方案 | Doc Agent | Apache POI (.docx) |
|---|---|---|
| 输出格式 | PDF (不可编辑) | Word (可编辑) |
| 适用场景 | 标准确认书、批量生成 | 需要人工修改的草稿 |
| 模板引擎 | FreeMarker | Apache POI XWPF |
| 部署 | 独立服务 | eds-web-app 应用内 |
运营在以下情况下使用 Word 版:
- 客户要求修改确认书措辞时
- 涉及非标准条款需要律师修改时
- 需要在发送前加注释/批注时
Doc Agent → HCP 集成
Doc Agent 生成的 PDF 直接存入 HCP 对象存储:
Doc Agent 生成 PDF (内存)
│
▼
PUT /hcp/ns/odts/confirm/{docId}.pdf
│
▼
HCP 返回存储 URL
│
▼
返回 { docId, url, status: "SUCCESS" }
│
▼
eds-web-app 将 URL 存入数据库: contract.documentUrl
关键代码目录
odyssey/doc-agent/
├── src/main/java/.../docagent/
│ ├── controller/
│ │ └── DocAgentController.java ← REST 端点
│ ├── service/
│ │ ├── DocumentGeneratorService.java ← 主生成编排
│ │ ├── TemplateResolver.java ← 模板选择
│ │ └── HcpStorageService.java ← HCP 存储集成
│ ├── template/
│ │ └── TemplateEngine.java ← FreeMarker 封装
│ └── model/
│ ├── GenerateRequest.java ← 请求 DTO
│ └── GenerateResponse.java ← 响应 DTO
├── templates/ ← FTL 模板文件
└── config/
└── application.yml ← 服务配置