Unsiloed 与 TypeSafe Jev 协同完成文档抽取复核
Document Review with Unsiloed and TypeSafe Jev
文章介绍用 Unsiloed 抽取结构化字段并附 confidence 分数和页面坐标,再把抽取结果交给 TypeSafe Jev 回答定向问题,组合两者判断文档中哪些字段需要人工复核。
用 OCR 抽取置信度与 LLM 合理性判断串联的文档复核流程,含阈值、API 代码和单文档成本估算,可直接迁移到其他字段抽取 + 审核场景。
提取流水线可以正确读取某个值,却仍然把错误的数据传递给下游。一个清晰打印的多余零位可以通过出色的光学字符识别(OCR)。
同时使用 Unsiloed 和 TypeSafe 的 Jev 可以帮助我们检查文档中的 OCR 错误和一般性失误。Unsiloed 提取结构化字段并在页面上定位它们。Jev 根据聚焦问题判断所提取的信息。我们可以综合这些结果来判断哪些字段需要复核。
Jev 将聚焦问题转化为有类型的答案
Jev 请求把它所考虑的信息(state)与它所做的决策(questions)分开。在我们的工作流中,state 包含 Unsiloed 提取的记录以及解释它们所需的上下文。每个问题告诉 Jev 要检查什么以及要返回哪种答案类型;它还可以为可能的答案定义判据。
类型决定了我们的代码会收到什么。Noul 返回一个陈述为真的概率。Choice 从命名选项中选择;Score 按有序等级的标准进行评估。Choice 和 Score 还会返回一个概率分布以及一个汇总该分布集中程度的置信度值。Noul 没有独立的置信度字段。
这三种类型可以在同一个请求中共享 state,如图所示。后面我们的示例中将只使用 Noul。

一个值为 0.04 的Noul 表示对我们的问题的是概率为 4%,即一个强烈的否。这并不意味着“Jev 置信度为 4%”。接近任一端点的值表示方向明确;接近中间的值则需要谨慎对待。
将 Unsiloed 提取的字段发送给 Jev,然后路由其结果
Unsiloed 保留原样打印的内容,并提供有助于识别歧义读数的置信度分数。在我们的示例中,我们使用一个包含六行的用药清单。一个咖啡渍遮挡了一处剂量,而另一行包含一个打印清晰但不合常理的数量。我们将贯穿提取流程和 Jev 的检查来跟踪这两个案例。

我们的提取 schema 定义了 Unsiloed 应为每种药物返回的字段。它要求返回完整的剂量单元格,包括数量和单位,但不修正异常数量,也不根据常规处方习惯猜测被遮挡的文本。使用 enable_citations=true 启用来源引用,并等待提取任务完成后再构造 Jev 的 state。
每个字段有两个Unsiloed 置信度分数:extraction_score 衡量对所提取值的置信度,而 grounding_score 衡量对该值在文档中被定位的置信度。两者都从零到一。引用标识了它所在的页面和包围框,即围绕源文本的矩形。
以下是来自已保存响应中带有污渍的西替利嗪剂量,标注在原始 1,200 × 1,500 图像上的像素坐标:
{
"value": "1 tablet",
"score": { "grounding_score": 0.76, "extraction_score": 0.74 },
"citation": {
"page": 1,
"bbox": [582, 975, 649, 994],
"page_width": 1200,
"page_height": 1500
}
}
为每个用药行分配一个 ID,例如 row-5,并将其与提取的字段及其引用一起保存。这样我们的代码稍后可以将 Jev 的答案匹配回同一行。保留引用,以便复核人员能够在原始页面上定位每个提取的字段。
向 Jev 发送所提取的值和上下文。它在看不到图像和 Unsiloed 分数的情况下,对指令按所提取的内容进行判断。我们对每一行提一个 Noul 问题:对于此药物,这一剂量和频次是否合理?所有六行共用一个请求,但每个问题独立评估。
在连接两个 API 的服务器端模块中,构造此请求(展示一行及其对应的问题):
const request = {
model: "jev-1.13.0",
state: {
context: "Check for obvious quantity, unit or frequency errors. " +
"Dose means amount taken each time, not supply quantity. " +
"Do not assess patient-specific safety or propose corrections.",
careContext: "Adult outpatient medication reconciliation, age 52",
medications: [{
id: "row-5", medicine: "Cetirizine 10 mg tablet",
dose: "1 tablet", frequency: "Once daily as needed",
route: "Oral", instructions: "For allergy symptoms."
}]
},
questions: {
"row-5": {
type: "noul",
instructions: {
question: "Is the extracted dose and frequency plausible on its " +
"face for this medicine and strength in the stated adult " +
"outpatient context, without an obvious quantity or " +
"frequency transcription error?",
row_id: "row-5"
},
criteria: {
true: "Plausible on its face; no obvious quantity, unit or frequency error.",
false: "Clearly implausible; warrants review of the original prescription."
}
}
}
};
这里的上下文和评判标准有所省略;已记录的请求包含完整文本和全部六行。我们用 row-5 作为问题 ID,以便代码将答案匹配到对应行。我们还在说明中以 row_id 的形式包含它,让 Jev 知道要评判哪条记录:问题 ID 不会发送给模型。
将请求以 JSON 形式提交到 https://api.typesafe.ai/v1/systemone。在 Authorization: Bearer <API_KEY> 请求头中使用你的 TypeSafe API 密钥进行身份验证,密钥保存在服务器端。answers["row-5"] 处记录的答案是 { "type": "noul", "noul": 0.94 }。使用共享 ID 将该答案与已保存的 row-5 抽取结果进行匹配。
我们的代码现在判断该行是否需要审阅。对于读取检查,我们取该行所有字段中最低的 Unsiloed 分数,因此任意字段的低分都会标记整行。对于合理性检查,我们使用 Jev 的 noul 值。
在同一模块中,将这两个值作为 reading 和 plausible 传给此函数。它返回将行送审的原因,如果两项检查均未触发则返回空数组。这些示例规则也会标记缺失或无效的分数:
const valid = n => Number.isFinite(n) && n >= 0 && n <= 1;
function reviewReasons(reading, plausible) {
return [
...(!valid(reading) || reading < 0.90 ? ["Verify source reading"] : []),
...(!valid(plausible) || plausible < 0.50 ? ["Review instruction"] : [])
];
}
这里,读取分数低于 0.90 或合理性答案低于 0.50 会触发审阅。这些是示例阈值:在生产环境中使用前,请针对人工审阅过的文档进行测试。Jev 接近 0.50 的答案也可能需要审阅,因为"是"和"否"均未被明显偏向。
当两项检查均未通过时,保留两个原因。一个合理的说明仍可能包含被误读的文本,因此较高的 Jev 答案不能抵消较低的 Unsiloed 分数。请将原始抽取结果以及 Jev 的请求和响应与审阅者的决定一并保存,以便日后检查某行被标记的原因及解决方式。
跟踪从抽取到审阅的被标记行
逐步查看保存的 API 结果,了解 Unsiloed 读取了什么、Jev 检查了什么,以及为何这两条被标记的行需要不同类型的审阅。
对于有污渍的西替利嗪剂量,Unsiloed 抽取 "1 tablet",抽取分数为 0.74,定位分数为 0.76。Jev 针对合理性返回 0.94,但它从未看到污渍。该判断依赖于抽取文本是正确的。读取检查仍然标记此行,因为必须有人确认被遮盖的标记。
布洛芬行则呈现相反的模式。Unsiloed 读取 "10 tablets, three times daily",两个分数均为 0.99;Jev 对同样的合理性问题返回 0.04。审阅者需要调查这份清晰打印的说明。任一模型都不能用假设的预期剂量来替代。
每项检查都标记了另一项所遗漏的审阅需求。其他四行通过了两个示例阈值,如 记录的响应所示。解释和路由标签来自我们的应用;Jev 返回的是类型化答案,而非这些解释。
每份文档中 Jev 增加的成本
本示例中一份药品清单会产生包含六个问题的 Jev 请求,输入令牌为 1,812 个。按 Jev 1.13.0 公布的每十亿输入令牌 42 美元的价格计,输出免费(截至 2026 年 9 月 30 日核实),每份文档增加 $0.000076。处理 100,000 份类似文档将增加约 $7.61 的 Jev 费用。
此估算仅针对 Jev;Unsiloed 抽取和人工审阅的费用另计。实际费用取决于所发送的文本和问题。如需更精确的估算,请从一份具有代表性的 Jev 响应中获取 input_tokens 并套用公布的价格。
来源:Hacker News · unsiloed.ai