做大模型应用时,最怕的不是模型不会回答,而是回答“看起来对,但程序没法用”。尤其是发票识别、简历解析、工单分类、合同字段抽取这类场景,后端需要的是稳定 JSON,而不是一段自然语言。我在做模型评估时,通常会先通过 t.877ai.cn 这类 AI模型聚合平台 对比不同模型的结构化输出能力、字段稳定性和解析成本,再决定是否接入到业务链路里。
传统做法是让模型“请按 JSON 格式输出”,然后后端再用正则或 json.loads() 解析。这个方案在 Demo 阶段能跑,但一到生产环境就容易翻车:字段名变了、少了逗号、多了一句解释、数组结构不一致,都会导致解析失败。JSON Schema 的价值就在这里,它把“希望模型怎么输出”变成了明确约束。
以简历抽取为例,我们希望从文本中提取姓名、工作年限、技能列表和最近一家公司。Schema 可以这样设计:
json
{ "type": "object", "properties": { "name": {"type": "string"}, "years": {"type": "number"}, "skills": { "type": "array", "items": {"type": "string"} }, "last_company": {"type": "string"} }, "required": ["name", "skills"]}
接入 Gemini 时,核心思路是把输出格式交给模型配置,而不是只写在 Prompt 里。Prompt 负责说明任务,Schema 负责约束结果。两者结合,稳定性会比单纯提示词高很多。
Python 侧可以封装成一个通用抽取函数:
python
def extract_resume(text): prompt = f""" 请从以下简历内容中抽取结构化信息。 如果某字段不存在,请返回空字符串或空数组。 简历内容: {text} """
response = client.models.generate_content( model="gemini-1.5-pro", contents=prompt, config={ "response_mime_type": "application/json", "response_schema": resume_schema } )
return response.text
实际项目中,不建议直接把 response.text 入库。更稳妥的做法是先做一次 JSON 解析,再用 Pydantic 或 Marshmallow 做字段校验。比如 years 必须是数字,skills 必须是数组,必填字段不能为空。模型负责抽取,程序负责验收,这是比较清晰的边界。
结构化输出还有一个常见坑:Schema 设计过复杂。很多人一上来就定义十几层嵌套,结果模型输出不稳定,后端也难维护。我的经验是先从核心字段开始,比如订单号、金额、时间、状态,跑通后再逐步增加字段。Schema 越贴近业务真实需求,越容易稳定。
和传统规则抽取相比,Gemini 这类模型的优势是适应非标准文本。比如用户描述“上周五买的耳机还没发货”,规则系统很难直接识别订单问题,但模型能理解语义并归类。规则系统的优势是确定性强、成本低,适合固定格式表单。更合理的方案是两者结合:固定字段走规则,开放文本走模型。
在生产环境里,还需要设计兜底机制。第一,抽取失败要记录原文和错误原因,方便回放。第二,关键业务字段要人工复核,比如金额、合同期限、客户身份等。第三,对于高频模板,可以缓存抽取结果,减少重复调用。不要把所有风险都交给模型,这是工程系统的基本原则。
从趋势看,结构化输出会成为大模型落地的基础能力。过去大家关注“模型会不会写文章”,现在更关注“模型能不能稳定接入数据库、工作流和业务系统”。JSON Schema、Function Calling、RAG 本质上都在解决同一个问题:让大模型从聊天界面进入真实应用。
我的观点是,结构化输出不是小功能,而是大模型工程化的分水岭。只要输出稳定,后面就能接审批、检索、报表、监控和自动化流程;如果输出不可控,再强的模型也只能停留在体验层。用 Gemini 做数据抽取,关键不在于一次回答多漂亮,而在于一千次调用后,字段仍然可靠、可解析、可追踪。