ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

LLM信息抽取:为什么两次调用比一次更准更省钱?

LLM信息抽取:为什么两次调用比一次更准更省钱? 我最早意识到“一次调用不够用”是在处理一批合同文本的关键字段提取时。让模型直接输出规范JSON结果总有几个样本漏字段有时候模型还会在JSON外面补一句解释导致解析失败。后来我把操作拆成两步第一次只让模型把相关片段找出来第二次再把片段整理成固定结构。准确率上去了API账单反而降了不少。这不是个别幸运样本的偶然结果而是一个值得掰开揉碎讲清楚的工作流设计思路。复杂提取任务真正的难点往往不是模型看不懂文本而是我们要求模型在同一个请求里同时完成“理解、召回、归纳、格式化”四件不同性质的事。任何一环出问题整次调用就失败。而把任务拆成两次调用每次只做其中一部分往往能同时改善准确率和成本。1. 为什么两次调用反而比一次调用更便宜很多人第一反应会觉得荒谬多调一次模型怎么还能更省钱API按token计费两次调用消耗的总token数通常会比一次多怎么反而便宜原因在于我们真正该比较的不是单次请求的token数而是整个任务完成到可验收状态所花的期望成本。1.1 一次调用时成本都花在了哪里一次调用试图让模型完成“全文理解 - 相关性判断 - 关键信息抽取 - 格式组织”这条长链路。为了让模型输出稳定我们需要在提示词里写大量格式说明、字段定义、输出示例有时还会写“不要输出多余文字”这类强调。这些指令本身占掉不少输入token。更隐蔽的成本是失败后的重试。一次长链路请求很容易出现以下情况模型输出了一部分字段遗漏了另一个字段模型把输出格式理解错返回的是自然语言而不是JSON模型在JSON里插了注释或者反过来漏了右括号模型“好心”补了一个我们没定义过的字段。这些情况都会导致程序解析失败进而触发重试。重试不是只多花一次调用而是可能连发多次直到某次碰巧输出正确为止。如果你在提示词里因为失败而不断追加约束输入token还会继续膨胀。最终账单往往比想象中高得多。1.2 两次调用真正便宜在哪两次调用的设计核心是“职责分离”。第一次调用只做一件事从文本里找出所有可能与目标字段相关的片段。它不需要输出结构复杂的JSON甚至可以直接用普通文本或极简符号回复。第二次调用只做另一件事把第一次返回的候选片段映射到目标字段并格式化成JSON。它不是从全文里大海捞针只是整理一份已经缩小的材料。这样一来两次请求各自的提示词都更短任务更简单模型失败的概率也更低。也许每次请求的token数比一次调用少不了太多但重试率降下来了。重试率一旦降下来总成本自然降下去。标题里的“67% cheaper”很可能就是这样来的不是单次请求从10块变成3块而是整个流程从“反复失败、反复重试”变成了“两次稳定输出”。1.3 从“成本峰值”到“成本期望”只看单次成功调用的成本很容易被误导。一次调用如果第一次就成功也许只花几百token但如果你跑1000条数据失败率30%那额外重试消耗就可能接近成功路径的30%到100%。两次调用每次输出任务更简单失败率可能从30%降到3%即使总token多一点点成本分布也完全不一样。所以衡量这类方案时一定要看“完成1000条任务的总支出”而不是“一条请求多少钱”。把成本理解成分布而不是单点数值是判断这种优化是否成立的前提。2. 拆成哪两步推荐“先召回后校验”模式两次调用怎么拆直接决定成败。不是任意拆两段都能获得收益。我实际用过比较稳定的是“先召回后校验”两阶段模式。2.1 第一次调用只召回不整理第一次调用的目标是“别漏”。假设要从一段项目介绍里提取“项目名称、负责人、预算、交付时间”四个字段。第一次提示词建议不要规定JSON结构只要求模型把和这四个方向相关的内容挑出来可以按句子或短语返回允许冗余。这样做的原因很简单召回阶段如果加了过多的格式约束模型会把认知资源花在“怎么输出”而不是“看准内容”。一旦输出格式错了第一次调用也白费。不如放手让它找出可能信息宁可多抓一点也不可漏掉。示例结构你是一个信息抽取助手。下面有一份项目文档片段。请从中找出与以下信息最相关的原文内容 - 项目名称 - 项目负责人 - 预算金额 - 交付时间 不要归纳不要翻译尽量返回原文片段。找不到的内容可以写“未提及”。这种提示词足够宽松模型不太会卡住。它输出的内容可能有噪音但没关系那是下一步要处理的。2.2 第二次调用只校验不重读全文第二次调用拿到第一次的候选片段后把它和字段定义一起发给模型。这次的任务非常聚焦把候选内容归类到目标字段并整理成固定JSON。示例结构下面是从文档中提取的候选片段请把它们整理成JSON对象。 字段定义 - project_name项目名称字符串 - owner项目负责人字符串 - budget预算金额字符串或数字 - delivery_date交付时间字符串 候选片段 {第一次调用返回的内容} 要求 1. 只输出JSON对象不要解释。 2. 候选片段中没有的信息字段给null。 3. 如果候选片段有冲突优先选择表述更具体的值。第二次调用不需要看原始全文所以输入token大幅减小。它只做“格式化消歧”这件小事。模型几乎不需要从长文里找信息因此格式错误率和幻觉率都会下降。2.3 为什么这个流程能提升准确率一次调用时模型面对长文和字段清单很难同时覆盖所有字段。它可能把预算数字看漏也可能在长文后部丢掉前面的信息还有可能为了实现JSON结构而自行脑补内容。两次调用把“漏”和“乱”分开解决。第一步确保候选信息齐全第二步确保结构正确两个错误来源被隔离。标题里的“100% vs. 72%”应该来自某个特定测试集不是说这个流程在所有数据上都能做到满分。它真正说明的是在同一个数据集上拆分任务后因为漏召回和格式错误造成的失败样本大幅减少从而从72%提升到100%。这种提升在文本结构复杂、字段较多、格式要求严格的场景下尤其明显。2.4 不是所有两次调用都像这样有效拆分成“先做A再做B”时A的输出质量直接决定B的上限。如果第一次调用连候选片段都没有召回第二次再怎么整理也补不出来。所以这个模式有一个隐含前提源文档里确实有相关信息且模型有能力识别这些信息。如果一段文本本身就是模糊的、缺失的、多义的两步拆分只能减少“漏和乱”不能解决“没有这个信息”的问题。针对这种情况第二次调用的提示词里最好明确允许输出null。宁可返回空值也不要让模型强行编造。对下游任务来说一个显式null远比一个编出来的错误值更安全。3. 在你自己项目里怎么复现这个结论“67%便宜、100% vs 72%”是别人测试集上的结果。你自己的数据、字段和模型可能完全不一样。所以第一步不是直接改流程而是先做一轮可对标的对比实验。3.1 先定义“准确”的判定标准评判提取结果不能凭感觉。建议至少从三个维度打分字段存在性目标字段是否出现在结果里。字段正确性值是否与原文一致包括数字、日期、人名是否准确。格式合法性如果要求输出JSON是否能被json.loads正常解析字段类型是否符合预期。只有同时满足这三个维度才算一条数据通过。否则单独看“字段被提取到了”没有意义。这个标准需要写在实验脚本里统一评估。3.2 准备一个30条左右的小样本集从真实数据里挑30条有代表性的样本避免只挑简单的。应该覆盖以下情况字段齐全的长文本有干扰文本的文档字段缺失或部分缺失的样本日期、金额格式不统一的样本文本包含表格、列表等非纯段落结构。每条样本准备一个金标准答案也就是人工核对过的正确提取结果。3.3 分别运行两种流程并记录关键指标对同一批样本跑两组流程方案A一次调用让模型直接输出目标JSON。方案B两次调用先召回后校验。每组建议跑2到3遍避免随机性。记录以下指标指标说明通过率满足字段存在、正确、格式合法的样本比例总token数所有调用加起来的输入输出token总和API费用按你使用的模型单价折算失败重试次数因格式错误或字段异常导致的重试次数平均耗时单条样本从开始到拿到可用结果的时间如果方案B的通过率明显更高且总费用没有翻倍那它对你当前场景就是有价值的。如果方案B的通过率提升有限但费用高了不少那就需要进一步优化提示词或调整拆分方式。3.4 真实成本要算上人和排错时间API账单只是成本的一部分。一次调用如果失败了虽然API可能只收几百token的钱但你的排查时间、日志处理、重新跑批的时间都是成本。两次调用虽然多了一步但如果每条数据都能稳定跑通运维成本反而更低。用这个视角看“67% cheaper”一点都不夸张。它省去的是反复调提示词、反复人工复审、反复跑批处理的时间。4. 适用边界不是所有提取任务都适合“拆两半”“两次调用优于一次调用”是一个针对特定任务类型的结论。如果不问场景就套用很可能得到完全相反的结论。需要先判断你的任务到底属于哪一类。4.1 什么时候一次调用更合适如果任务满足以下特点一次调用反而更省事目标字段非常少比如只需要提取一个公司名输出格式可以很自由不需要严格JSON源文本非常规整几乎没有冗余内容字段之间没有相互依赖和歧义。比如从一句话里提取日期一次调用完全可以胜任。这时候硬拆成两步只会多花一次请求的时间并不会带来什么准确率提升费用反而增加。任何优化方案都要有阈值不能为了拆而拆。4.2 什么时候两次调用也不够源文本需要跨多份文档联合理解第一步召回本身就需要多轮搜索字段值需要外部知识才能判断比如“这个预算是否合理”单纯抽取无法完成模型本身就是小型模型第一步召回漏得太多第二步巧妇难为无米之炊延迟要求极低只能接受一次请求这时候两步架构可能不符合需求。遇到这些情况可能要改用RAG、多轮对话、微调或者规则后处理而不是单纯把请求次数变成两次。4.3 可选的替代方案约束解码、少样本、规则后处理除了两次调用还有其他方式可以提升提取准确率约束解码一些推理框架支持限制JSON输出结构从生成侧避免格式错误给一次调用增加更多少样本示例常见做法是给2到3个输入输出范例帮助模型理解任务规则后处理用正则或显式解析修正模型输出的常见错误。这些方案的取舍可以简单概括方案优势局限两次调用灵活、不需要额外工具、任务边界清晰增加一次请求延迟约束解码从源头解决格式问题依赖推理框架字段自由度有限少样本示例对已有流程改动小长提示词增加token成本规则后处理零模型成本可精确修正只能处理已知错误模式两次调用的独特价值在于它既不像约束解码那样要求特殊框架也不像规则后处理那样只能治标。它通过重新分配任务来根治“多任务互相干扰”的问题。4.4 如何从一次调用平滑迁移到两次调用建议分三步走保留原有一次调用流程作为对照组同时记录失败日志。针对失败样本人工判断失败原因是漏召回还是格式错误。按失败原因决定是调整为一次调用提示词还是切到两次调用模式。不要一次性全面改成两步流程。先选50条失败最严重的数据跑一版两步流程和原流程对比。如果效果明显再逐步扩大范围。5. 工程化落地最容易踩坑的五个环节从实验脚本到生产任务中间还有不少工程细节。这些细节如果不处理好实验时的100%会成为线上随时翻车的起点。5.1 第一次调用的输出格式必须能被稳定解析第一步召回阶段即使不要求JSON也要约定一种简单可解析的输出格式。常见做法是每条候选片段一行片段之间用空行分隔不需要额外说明文字。如果第一次调用的输出过于自由第二次调用拿到的内容可能包含大量无关描述会干扰整理过程。这时你不得不在第二次调用里加更多清洗逻辑整个流程又变复杂了。建议在第一次调用的提示词里明确写每条结果占一行不要编号不要解释。这样第二次调用可以直接按行读取候选片段。5.2 提示词、模型版本和解码参数都要记录两次调用流程涉及两套提示词任何一个改动都可能导致准确率变化。实际落地时必须做到每次实验记录提示词版本记录模型名称和版本记录temperature、top_p、max_tokens等解码参数记录输入文本的预处理方式。否则当指标出现波动时你很难定位是提示词改动、模型升级还是数据漂移导致的。建议把每次实验的配置放入一个配置文件中而不是散落在笔记本里。5.3 日志与追踪每个调用都要能看到全貌生产环境里应该记录以下信息原始输入文本ID或摘要第一次调用返回的候选内容第二次调用返回的最终JSON每次调用的token数和耗时是否触发重试及重试原因最终通过的校验结果。日志的目的是让你能在任意一条失败数据上回放整个流程看到是哪一步出了问题。没有这种可追溯性两步流程一旦出错排查成本可能比一次调用更高。5.4 成本控制不要一上来就拉满并发两步流程意味着每个任务会产生两倍的请求量。如果并发数设置过大一下就打满API配额或者某个中间步骤超时都会导致批量任务失败。建议先用较小并发跑20条数据确认全流程稳定后再逐步提高。同时给每个任务设置一个最大重试次数。比如第一次调用最多重试2次第二次调用最多重试3次超过后把任务放入人工队列。避免一个坏样本无限循环消耗预算。5.5 警惕“第二次调用幻觉”第二次调用的输入是第一次输出第一次输出里如果混入了原文没有的信息第二次调用可能基于这些错误信息生成更详细的错误值。这就是幻觉的级联。要缓解这个问题需要让第二次调用始终以“原文候选片段”为准而不是让它自由发挥补全。提示词里可以加一句不要补充候选片段里没有的内容。但这只是一道防线更可靠的做法是在最终输出的JSON中对每个字段保留原文证据字符串便于人工或程序校验。这不是必须项但在关键业务场景里很值得。6. 从“两次调用”到“流程设计”LLM应用的核心思维转变这个案例的价值不只在“提取任务”。它反映了一个更普遍的规律LLM应用不是“发一条提示词拿到完美结果”那么简单而是需要把一个大任务拆成多个子任务并设计检查点来控制不确定性。6.1 把LLM当“可调用的组件”而不是“万能接口”一次调用可以看成一个大而全的函数它接受文本输出结构化结果。但它内部的错误不可见、不可控。两次调用则相当于把一个函数拆成两个更小的函数每个函数的输入输出更简单更便于调试和替换。在复杂任务里可调试性比单次调用的便利性更重要。两步提取可以清楚看到“是第几步丢了信息”但一次调用只能看到一个失败JSON问题在哪一步无从得知。6.2 一个可复用的判断框架决定调用次数和流程复杂度在接到一个LLM任务时不要直接写提示词先按四个维度做判断维度适合一次调用适合两次甚至多次调用目标字段数量1到3个5个以上输出格式复杂度简单文本或少量键值嵌套JSON、数组、跨字段依赖源文本结构简短、无歧义长文、多段落、混杂表格失败成本可以容忍偶尔错误错误会导致人工返工、系统异常如果四项里有三项指向右侧建议优先设计分阶段流程。不要一开始就追求“一个大提示词搞定一切”。6.3 用确定性约束不确定性的部分两次调用的本质不是“多花钱多出力”而是把模型擅长做的事和程序擅长做的事分开。模型擅长理解语义、召回候选程序擅长严格的格式校验和规则检查。让模型负责语义让程序负责结构把确定性的部分用代码固定下来不确定性的部分才交给模型。在这个流程里第一次调用输出候选文本第二次调用把它整理成JSON中间的解析、字段映射、校验逻辑可以用代码实现。最终你获得的是一个更可控的流水线而不是一个“碰运气”的一次性调用。真正值得借鉴的不是“两次调用”这个具体数字而是“把任务拆细、让每步只做一件事、在每步之间建立可检查的中间产物”这种工程化思路。LLM能力越强越需要我们在它外面设计稳定结构。否则再强的模型也扛不住一份写得稀烂的需求说明书。下一次你遇到提取准确率卡在某个水平时先别急着换更大模型也别马上堆更多few-shot。拿20条最难的数据试一下“先召回、后校验”记录全流程的token消耗和失败次数。你会发现有时候少做一点反而能多得到一点。
返回列表