数据抽取校验
把单据交给 Jev 判断字段,置信度够就直接走规则,不够再交给人或大模型。
痛点
让大模型从发票、合同、报关单这类单据里抽字段,偶尔会编出根本不存在的数字;上规则兜底,又得逐条人工核对,反而比直接抽字段更贵。把判断这一步拆出来交给 Jev,让大模型只在不确定时才上场,是这类场景的常见解法。
Jev 判断什么
state 里放了原文 document 和四个候选项 a/b/c/d,整轮判断围绕「这四个候选里哪一个是某字段」和「文档有没有陈述某事实」这两类题型展开。
total_amount(Choice):题面「Which candidate is the invoice's total amount?」,选项 a/b/c/d 分别指向 candidates.a/b/c/d 的文本。这次返回选了 a(即 ¥1,280.00),四个选项的概率是 a=1.0、b=0.0、c=0.0、d=0.0,置信度 1.0。选择题四个概率构成清晰分布,a 直接拉满,说明 Jev 锁死了候选 a 这串文本。代码直接读 choice 字段拿选项 id,再回查 candidates[id] 拿真实字符串,跳过任何二次加工。
has_due_date(Noul):题面「Does the document state when payment is due?」。这次返回答案「是」的概率为 0.85。Noul 只返回 true 的概率(即 1 - p_false),0.85 已经远离 0.5 的灰色地带,可以判定为「是」。代码读这个概率即可:高于自己设定的阈值判 true,低于则升级复核,不用关心它内部是怎么算的。
和大模型怎么配合
流程是:先把多条题目打包进一份 state 调 Jev,代码按每道题的返回结果走规则——置信度足够高、概率一边倒的(如这次 total_amount 置信度 1.0、a 概率 1.0)直接走程序,不调大模型;置信度不足或 Noul 概率贴近阈值的,升级交给大模型或人工复核。这套思路下,绝大部分单据在 Jev 这一步就能闭环,只有少数灰区样本才真的需要花大模型的预算。
实测这次调用是模型 jev-1.14.0,耗时 1104 毫秒,输入 536 个 token,单次花费 0.000023 美元,按此折算每 1000 次约 0.0225 美元。Jev 定价是每百万输入 token 0.042 美元、输出不收钱,校验这一层跑批量非常便宜。
上线前注意
- 题面(question)用英文写,state 里的中文原文(如开票日期、付款期限)不要翻译成英文,否则 Jev 看到的关键线索会被你换掉。
- 不要拍脑袋设阈值,先拿 20–30 条真实单据跑一遍,看一眼置信度和概率分布,再定 Choice 的最低置信度门槛和 Noul 的判定阈值。
- 总价、金额这类关键字段,只要 Jev 置信度偏低或对应概率不一边倒,立刻升级人工或大模型复核,不要让规则直接放行。
- 不需要的字段不要写题问,state 里少塞一个 candidate、Jev 就少算一次,整批调用的 token 也会更省;同一份 document 下能合并的题目尽量合并到一次请求里发出去。
真实调用
请求
curl https://api.typesafe.ai/v1/systemone \
-H "Authorization: Bearer $TYPESAFE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "jev-1.14.0",
"state": {
"document": "发票号:INV-20261015-007,开票日期 2026 年 10 月 15 日,合计金额 ¥1,280.00(含税),付款期限 30 天。",
"candidates": {
"a": "¥1,280.00",
"b": "30 天",
"c": "INV-20261015-007",
"d": "2026 年 10 月 15 日"
}
},
"questions": {
"total_amount": {
"type": "choice",
"instructions": "Which candidate is the invoice's total amount?",
"criteria": {
"a": "The text in `candidates.a`",
"b": "The text in `candidates.b`",
"c": "The text in `candidates.c`",
"d": "The text in `candidates.d`"
}
},
"has_due_date": {
"type": "noul",
"instructions": "Does the document state when payment is due?"
}
}
}'import json, os, urllib.request
payload = json.loads("{\n \"model\": \"jev-1.14.0\",\n \"state\": {\n \"document\": \"发票号:INV-20261015-007,开票日期 2026 年 10 月 15 日,合计金额 ¥1,280.00(含税),付款期限 30 天。\",\n \"candidates\": {\n \"a\": \"¥1,280.00\",\n \"b\": \"30 天\",\n \"c\": \"INV-20261015-007\",\n \"d\": \"2026 年 10 月 15 日\"\n }\n },\n \"questions\": {\n \"total_amount\": {\n \"type\": \"choice\",\n \"instructions\": \"Which candidate is the invoice's total amount?\",\n \"criteria\": {\n \"a\": \"The text in `candidates.a`\",\n \"b\": \"The text in `candidates.b`\",\n \"c\": \"The text in `candidates.c`\",\n \"d\": \"The text in `candidates.d`\"\n }\n },\n \"has_due_date\": {\n \"type\": \"noul\",\n \"instructions\": \"Does the document state when payment is due?\"\n }\n }\n}")
headers = {
"Authorization": "Bearer " + os.environ["TYPESAFE_API_KEY"],
"Content-Type": "application/json",
}
req = urllib.request.Request(
"https://api.typesafe.ai/v1/systemone",
data=json.dumps(payload).encode("utf-8"),
headers=headers,
method="POST",
)
print(urllib.request.urlopen(req).read().decode("utf-8"))
返回
{
"model": "jev-1.14.0",
"answers": {
"total_amount": {
"type": "choice",
"choice": "a",
"confidence": 1,
"probabilities": {
"a": 1,
"b": 0,
"c": 0,
"d": 0
}
},
"has_due_date": {
"type": "noul",
"noul": 0.85
}
},
"usage": {
"input_tokens": 536,
"output_tokens": 65
}
}人话解读
- Choice:选了 a,置信度 1.00
- Noul:「是」的概率 0.85