做一个客服聊天整理功能,让模型提取订单号和实付金额。
接口返回 200,有内容,耗时也正常。接着把结果交给程序,金额字段里却写着「待确认」。
这个故障值得在上线前主动模拟一次。更麻烦的是另一种情况:金额确实是数字,程序一路执行成功,但多了一个零。
先把输入缩短,方便看清问题:
A1024 这单原价 259.90 元,券后实付 199.90 元。
我们要提取的是实付金额,约定以整数「分」保存。期望结果是:
json
{"order_id": "A1024", "amount_cents": 19990}
这里有两层 JSON,容易混在一起。HTTP 响应体能解析,只说明拿到了接口返回的对象;message.content 里面那段模型生成的文字,还得单独检查。
为了把检查逻辑跑一遍,我构造了下面几组返回内容。它们是本地模拟数据,不代表某个模型的实际表现。
| 返回内容 | 交给程序后会怎样 |
|---|---|
这笔订单实付 199.90 元。 |
人能看懂,JSON 解析失败 |
{"amount_cents":19990} |
JSON 合法,但缺少订单号 |
{"order_id":"A1024","amount_cents":"待确认"} |
字段齐了,金额类型不对 |
{"order_id":"A1024","amount_cents":19990} |
结构和这道题的答案都对 |
只写一句 json.loads(content),只能发现第一种问题。
我给这个小例子加了一段检查。只用 Python 标准库,把它放在读取模型回答之后、写数据库之前:
python
import json
def parse_order(content, finish_reason):
if finish_reason != "stop":
raise ValueError(f"没有拿到最终文本:{finish_reason}")
if not isinstance(content, str) or not content.strip():
raise ValueError("回答为空")
try:
data = json.loads(content)
except json.JSONDecodeError as exc:
raise ValueError("回答不是合法 JSON") from exc
if not isinstance(data, dict):
raise ValueError("需要 JSON 对象")
order_id = data.get("order_id")
if not isinstance(order_id, str) or not order_id.strip():
raise ValueError("order_id 缺失或无效")
amount = data.get("amount_cents")
if type(amount) is not int or amount < 0:
raise ValueError("amount_cents 必须是非负整数")
return {"order_id": order_id, "amount_cents": amount}
这里用 type(amount) is int,是因为 Python 的 isinstance(True, int) 也会返回 True。金额不应该接受布尔值。
这段代码只处理最终返回 JSON 文本的流程。工具调用有自己的处理路径,tool_calls 不能直接算作模型故障;输出被截断时,也不能把已经收到的半段文字当作完整答案。
拿到非流式 Chat Completions 响应后,调用位置是:
python
choice = response["choices"][0]
order = parse_order(
choice["message"].get("content"),
choice["finish_reason"],
)
这里的 response 是已解析成字典的接口响应。通过检查,只代表这份数据符合刚才约定的字段和类型。
本地运行,前面三种异常都被拦住了。但再加一个例子:
json
{"order_id": "A1024", "amount_cents": 199900}
它会通过上面的检查。
订单号有了,金额是非负整数,JSON 也没有任何问题。可原文实付 199.90 元,这个结果变成了 1999.00 元。
演示脚本中,这两条的输出是:
text
正常结果:结构通过;与人工标注答案一致
金额多一个零:结构通过;与人工标注答案不一致
第二步能发现错误,是因为我提前写好了这条样本的参考答案。换成线上新数据,仍然需要核对原文或订单记录。字段校验不会替你判断事实。
这也影响模型怎么选。只比较「有没有返回」或者「能不能解析 JSON」,很容易选出一个格式很整齐、提取结果却不可靠的模型。
接入层可以沿用 moyi API 的 Chat Completions 接口。它在这里的用处是:对支持该接口的候选模型,保留同一套请求和验收代码,换模型后继续跑同一批样本。具体参数能力仍要逐个确认,接口兼容不代表功能完全相同。
样本不用一开始就堆得很大,但要有实付价和原价同时出现、缺少订单号、没写金额、同一段话提到两笔订单这些情况。把预期结果标好,再分别记录格式通过率和字段提取正确率,才知道模型差在哪里。
还有个容易被忽略的处理:原文没写金额,就应该进入待补充或人工确认。为了让程序继续运行,把「待确认」强行补成 0,后面反而更难查。
提示词当然也要约束格式。DeepSeek 的 JSON Output 文档要求配合 JSON 提示和格式示例,并提醒注意输出截断、空内容。即使开启这类功能,业务仍然要核对提取出的值。
对这个例子,我会把日志里的「成功」拆开记:HTTP 请求成功、结构检查通过、字段核对通过。遇到问题,至少能知道该查网络、返回格式,还是模型把原价当成了实付价。
你们接 AI 时,更费时间的是接口报错,还是它正常返回了,但结果不能直接用?