Learning
VOL. VII · NO. 105 · OTC Derivatives · 19 JUL 2026

Doc Agent Document Generation

OTC 衍生品 · 19 JUL 2026 · 6 min read · 1,127 words
· · ·

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
ISDAISDA 主协议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 + 产品类型 选择模板。若匹配不到精确模板,按以下优先级回退:

  1. {templateType}/{productType}_{variant}.ftl → 精确匹配
  2. {templateType}/{productType}.ftl → 产品级匹配
  3. {templateType}/default.ftl → 类型默认模板

与 Apache POI 的共存

方案Doc AgentApache POI (.docx)
输出格式PDF (不可编辑)Word (可编辑)
适用场景标准确认书、批量生成需要人工修改的草稿
模板引擎FreeMarkerApache 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                  ← 服务配置