模板套出来,用户跑了——法律文书智能填空,我推翻重做的两版方案
先说我做砸的部分
去年我和两个朋友做一个小产品:在线法律文书模板,用户选一份租房合同或劳动仲裁申请书,AI 根据用户描述自动填空生成完整文书。听起来很简单,对吧?我们第一版两周就上线了。
结果一个月后数据很难看:用户填到一半流失率超过一半,生成的文书被用户评价“不像律师写的”“字段张冠李戴”,客服邮箱里最多的投诉是“我的租金金额填成了押金”。
复盘下来,我们的失败做法集中在四点:
失败一:把整份模板丢给大模型一次生成。 我们的做法是拿着一份两千字的合同模板,加一句提示词“请把下面模板中的空填好”,再把用户的口语描述附在后面。模型确实能填,但它会顺手“润色”模板里不该动的条款,把“甲方”“乙方”偶尔弄反,金额有时凭空补齐——用户没说押金数额,它自己编了一个“押一付三”。
失败二:没有做字段抽取这一层。 我们指望模型直接从“我租了朝阳区一个两居室,月租六千五”里同时理解房东姓名、租期、付款方式。口语信息天然残缺,模型只能靠猜,猜错就是事故。
失败三:提示词散落在代码里。 三种文书模板,五处不同的提示词字符串硬编码在各处,改一个字段口径要在代码库里搜半天,改完还经常漏。
失败四:模型和供应商硬耦合。 我们只接了一家模型 API,密钥写在配置文件里。有一次该服务限流,产品整整瘫了一上午;后来想加一个便宜模型处理简单字段,又得重写一套调用逻辑。
这四个坑,前三个是产品设计问题,第四个是架构问题。下面讲我们推翻重做的路径。
正确的路径:把“生成”拆成“抽取 → 校验 → 填充”
重做后的核心认知是:法律文书的智能填空,难点不在生成,而在结构化。 模板本身是确定的,不确定的只是字段值。所以流程应该是:
- 模板字段化:每份文书模板拆成结构化的字段清单(字段名、类型、是否必填、取值约束、示例);
- 信息抽取:让模型做它擅长的事——从用户口语描述里抽取字段值,输出严格 JSON;
- 规则校验:用确定性代码校验金额格式、日期逻辑、必填完整性,缺什么向用户追问什么;
- 模板填充:确认无误后,用代码把字段值填回模板,一个字都不让模型碰原文。
这样一来,模型只负责“理解人话”,格式、逻辑、文本拼装全部由代码兜底。字段填错能定位到具体字段,追问有明确的缺口清单,条款永远不会被擅自改写。
配套的工程整改:
- 提示词模板化:所有提示词抽成带占位符的模板文件,字段清单动态注入,版本化管理;
- 模型分档:简单字段抽取用轻量模型,复杂陈述类字段(如“纠纷经过”)用强模型,成本降了六成;
- 统一 AI API 网关:所有模型调用走一个网关层,这是我要重点展开的部分。
为什么统一 AI API 网关能降低维护成本
我们重做时接了三个模型,分别擅长不同任务。如果不做网关,意味着三套 SDK、三套鉴权、三套错误码映射、三套重试逻辑,散布在业务代码里。每次调整模型配比、切换供应商、排查失败请求,都要动业务代码。
引入统一网关后:
- 一处接入,多模型可用:业务代码只对接一个统一接口,网关后面挂哪些模型、按什么规则路由,配置即可切换,不用改代码;
- 密钥集中管理:模型密钥不再散落在各环境配置里,轮换密钥是网关层的一次操作;
- 统一观测:所有调用的 token 消耗、延迟、失败率在一处看,排查“为什么这次生成慢了”不用登录三个后台;
- 降级与重试收口:某模型限流时网关自动切备用模型,业务代码零感知——这正是我们第一版瘫痪一上午的那个问题的根治方案;
- 成本可控:不同档位模型的调用配额、预算上限在网关层统一设置,避免某个字段抽取逻辑死循环把账单打爆。
对独立开发者和小团队来说,这套东西自己搭不现实,用现成网关服务是性价比最高的选择。
关键实现流程
以“劳动仲裁申请书”为例,填充主流程:
# 伪代码:抽取 → 校验 → 追问 → 填充
SCHEMA = {
"申请人姓名": {"type": "str", "required": True},
"被申请人公司": {"type": "str", "required": True},
"入职日期": {"type": "date", "required": True},
"离职日期": {"type": "date", "required": False},
"欠薪金额": {"type": "money", "required": True},
"欠薪区间": {"type": "date_range", "required": True},
"事实与理由": {"type": "text", "required": True},
}
# 1. 模型只做抽取,输出 JSON,不做任何"补全想象"
extracted = llm_gateway.chat(
model="light", # 简单字段用轻量档
prompt=EXTRACT_PROMPT.render(schema=SCHEMA, user_input=desc),
response_format="json",
)
# 陈述类字段单独用强模型生成
extracted["事实与理由"] = llm_gateway.chat(
model="strong",
prompt=NARRATIVE_PROMPT.render(facts=extracted),
)
# 2. 确定性校验:规则代码兜底,模型不碰
missing = validate(extracted, SCHEMA) # 必填缺失、日期逻辑、金额格式
if missing:
return followup_questions(missing) # 缺什么问什么,不猜
# 3. 代码填充模板,原文条款一个字符不动
return render_template("劳动仲裁申请书.docx", extracted)上线重做版本后,中途流失率明显下降,最直接的原因是:用户不再看到“填错的惊讶”,而是看到“缺什么的追问”——体验从不可控变成了可预期。
写在最后
这个项目给我最大的教训是:AI 应用里,模型的边界要画得比你想的更小。让模型只做理解和生成陈述,把校验和拼装留给代码,把多模型管理交给网关,小团队才能真正跑得快、睡得稳。
如果你也在做类似的多模型 AI 应用,不想在密钥管理、模型路由、失败降级这些事上重复造轮子,可以试试统一 AI API 网关:https://api.thistoken.ai/register
---
不想折腾多家供应商的接入差异?在 https://api.thistoken.ai/register 注册,用一个 base_url 调用所有模型。