行业资讯
📅 2026/8/29 19:10:44
两次LLM调用替代一次:信息提取成本降67%准确率升至100%
单次 LLM 调用做信息提取准确率不够高还烧钱改成两次调用后成本反而降了 67%准确率从 72% 拉到 100%。这不是玄学是拆分任务、缩小模型输出范围之后带来的直接收益。这篇就拆解这套“两次调用替代一次调用”的 LLM 提取优化方案讲清楚为什么更便宜、为什么更准并给出一套可以直接改用的部署和测试流程。如果你在处理合同解析、发票信息抽取、简历结构化、知识库入库这类场景并且已经在用 LLM 做字段提取这篇文章值得收藏。1. 核心能力速览能力项说明方案类型LLM 信息提取优化策略两次调用协作完成提取核心思路第一次调用做粗提取第二次调用做校验修正成本效果相比单次大模型全量提取成本降低约 67%准确率效果在材料对应测试中从 72% 提升到 100%适用任务结构化字段提取、实体抽取、文档解析、知识库入库底层模型不限定可接 API也可接本地部署的 LLM硬件要求视模型而定本地小模型可 CPU 推理大模型建议 GPU是否支持 API支持本质就是两次普通 LLM 接口调用是否支持批量任务支持可写成批处理脚本或任务队列适合读者做 RAG、文档解析、数据清洗、自动化管线的开发者这个方案的价值不在“模型”而在“调用策略”。你不需要换更强的大模型只需要把一次复杂提取任务拆成两次简单任务让每次调用都专注于更小的范围成本自然下降准确率反而更容易控住。2. 两次调用代替一次调用为什么更便宜也更准2.1 为什么“两次”反而比“一次”便宜先说结论两次调用的总成本低于一次调用因为两次调用各自处理的 token 范围大幅缩小了。单次调用的典型做法是这样的把整篇文本塞给一个大模型让它一次性输出所有目标字段。这个过程中输入 token 可能占几百到几千输出 token 因为要覆盖所有字段也往往很长。更麻烦的是模型为了“完整输出”会把很多重话、补充说明、格式描述一起生成出来这些都属于无效成本。两次调用则把任务拆成两段。第一次调用用小模型或低成本的模型做“粗提取”。它只需要识别出字段所在的片段输出一个较小范围的结构化结果不需要输出长解释。这次调用输出 token 少模型规模也可以小成本很低。第二次调用把第一次的结果连同原文里对应的上下文片段交给一个校验模型让它只做三件事检查字段值是否合理、补缺漏字段、修正格式。这一次调用的输入是“原文局部片段 第一次提取结果”输出只是修正后的差异部分输出 token 同样远小于一次全量提取。所以总 token 消耗比单次大模型全量提取少很多模型单价也可能更低。从材料看这套组合能把成本压到原来的三分之一左右对应标题里“便宜 67%”。需要说明的是这个比例和模型选择、文本长度、字段数量都有关不能当固定结论用但它下降的趋势是成立的。2.2 为什么准确率反而更高一次提取容易出错是因为模型在长文本里同时要完成“理解全文 定位字段 生成结构化内容”三件事。任务复杂时定位不准确、字段冲突、格式混乱都会发生出错后又没有修复机制最终准确率只能停在 72% 左右。两次调用的设计天然解决这个问题第一次调用的目标非常窄只做定位和初提取不追求一次写对全部字段。第二次调用的目标是修正它能看到第一次的结果能对着原文片段做针对性强校验。这就相当于给提取结果加了一层质检环节。第二次输出的是差异修正范围小模型即使能力不强也容易把任务做对。一百个字段里挑出一个错误字段比从零生成一百个正确字段简单得多。准确率从 72% 到 100%本质上是把“一次生成正确”变成了“生成一次 修正一次”后者的成功率自然高于前者。2.3 这套思路的适用范围从材料看这个方案主要针对的是“从非结构化文本中提取结构化字段”的任务。典型特征是输出字段固定、字段值来自原文、允许做核对验证。如果你的任务符合这三条它的收益最明显。如果任务是创意写作、开放问答、总结等没有固定答案的场景就不能直接套用。3. 适用场景与使用边界3.1 适合谁用这套方案适合以下人群正在做文档解析的开发者需要从 PDF、Word、扫描件中提取合同字段、发票字段、简历字段。做知识库入库的工程师需要把非结构化文本批量转换成结构化条目。使用 LLM 做数据清洗的数据工程师需要从大量文本中抽实体、抽关系、抽属性。在 RAG 链路里需要做召回后命中断言抽取的开发者。3.2 能解决什么问题第一个问题是成本。大模型按 token 计费输出 token 往往比输入 token 更贵。减少输出 token 是控制成本最直接的手段。两次调用把每次输出都控制在小范围整体输出量下降。第二个问题是准确率。单次提取如果出错往往只能加大模型、加提示词、再做多轮重试每轮都是一次全量调用的成本。两次调用用一次低成本校验来解决不依赖无限堆 prompt 技巧。第三个问题是稳定性。第二次调用可以把输出格式强制规整到统一 schema即使第一次输出乱第二次也能修正。4. 环境准备与前置条件这套方案不是一个独立软件而是调用 LLM 的一种方式所以环境准备重点是模型访问和运行脚本。4.1 模型选择你可以选择两种路线API 路线使用 OpenAI、Anthropic、国内大模型厂商的 API只需要申请 key不需要显卡。本地部署路线使用 Qwen、Llama、DeepSeek 等开源模型用 Ollama、vLLM 或 Transformers 加载需要 Python 环境和 CUDA 环境。如果你已经有可用的 LLM 服务不管 API 还是本地都可以直接跑通方案。建议第一次用小参数模型减少资源压力。4.2 通用环境清单项目要求操作系统Windows / Linux / macOS 均可Python3.9 及以上依赖库requests 或 openai 等 SDKLLM 服务API 或本地 Ollama/vLLM 服务网络API 路线需可访问对应服务本地模型无需磁盘本地模型按大小需要数 GB 到数十 GB显存本地大模型建议 8G 以上小模型可 CPU 运行如果走本地路线先把模型服务起起来。以 Ollama 为例# 安装 Ollama 后拉取一个小模型示例模型名需要替换 ollama pull qwen2.5:7b ollama serve这里强调一下模型名、服务端口、请求格式要以你实际使用的框架为准。下面代码里的地址和模型名都是占位符。5. 两步提取方案设计与核心实现我用一个通用 Python 示例来说明完整流程。案例目标从一段简历文本中提取“姓名、工作年限、技能列表、最近一份工作公司”四个字段。5.1 第一次调用粗提取第一次调用只做一件事把可能包含目标字段的原文片段找出来并给出初步值。import requests LLM_URL http://127.0.0.1:11434/api/generate MODEL_NAME qwen2.5:7b def first_extract(text): prompt f 你是一个信息提取助手。请从下面的文本中提取字段姓名、工作年限、技能列表、最近一份工作公司。 只输出 JSON不要解释。 文本 {text} payload { model: MODEL_NAME, prompt: prompt, stream: False, format: json } resp requests.post(LLM_URL, jsonpayload, timeout120) return resp.json()[response]这一步的要点是只要求输出 JSON不要求模型做任何额外解释。如果模型输出格式不对可以配合正则提取 JSON 片段或者在 prompt 中给一个示例格式。5.2 第二次调用校验修正第二次调用输入第一次的结果同时只返回修正后的差异部分。def second_validate(text, first_result): prompt f 请校验下面这个信息提取结果找出错误或缺失的字段。 原文 {text} 第一次提取结果 {first_result} 要求 1. 对照原文核对每个字段。 2. 如果有错误给出正确值。 3. 如果有缺失字段补上正确值。 4. 如果都正确返回一个空的 JSON 对象{} 5. 只输出 JSON不要解释。 payload { model: MODEL_NAME, prompt: prompt, stream: False, format: json } resp requests.post(LLM_URL, jsonpayload, timeout120) return resp.json()[response]第二次调用的 prompt 做了几件关键事明确告诉模型要“对照原文核对”。要求“错误则修正缺失则补全正确则输出空对象”。限制输出为 JSON。这样第二次调用的输出 token 会非常少。绝大多数情况下输出就是一个空 JSON 或只包含少数修改字段成本自然可控。5.3 合并最终结果拿到两次调用的结果后在代码里合并而不是让模型再合并import json def merge_result(first_result, second_result): first json.loads(first_result) second json.loads(second_result) # 第二次结果就是修正或补漏的内容覆盖到第一次结果上 merged {**first, **second} return merged full_text 张三5年Python开发经验精通FastAPI、Docker目前在某某科技有限公司担任后端工程师。 first first_extract(full_text) second second_validate(full_text, first) result merge_result(first, second) print(result)这里要注意合并动作放在代码里做不要再让 LLM 做一次合并。合并是确定性逻辑不该让模型参与重复调用只会增加成本和延迟。5.4 成本估算参考假设文本 1000 token四个字段固定输出一次大模型全量提取输入 1000 token输出 300 token输出很多是解释和格式内容。两次小模型提取第一次输入 1000 token输出 120 token第二次输入 300 token输出 30 token。总量和单价都下降明显。这里只是一个估算方式。实际成本你需要按模型单价、token 长度、字段数量重新算但“单次全量输出 粗提取输出 校验修正输出”的规律是稳定的。6. 功能测试与效果验证这个方案不是装完就跑而是需要一个评测集来确认它是否在你的数据上真的有效。6.1 测试目的验证两点准确率是否比单次调用提升。成本是否比单次调用下降。6.2 构造评测集从一个更小的样本开始。准备 20 到 30 条真实文本人工标注出正确结果。注意这组数据必须是你自己业务里的真实样本不要用测试生成的无意义文本。6.3 测试流程对于每条样本分别执行单次调用和两次调用两种策略记录结果、token 消耗、耗时。# 可以用一个简单的目录结构来组织测试 data/ input/ # 原始文本 labels/ # 人工标注结果 run_single/ # 单次调用输出 run_two/ # 两次调用输出 cost_logs/ # 每次调用的 token 统计6.4 判断准确率准确率的标准要提前定义好。常见做法是字段级准确率抽取结果与标注结果完全一致才算该字段正确。def field_accuracy(pred, label, fields): correct 0 for field in fields: value pred.get(field) expected label.get(field) if isinstance(value, str): value value.strip() if isinstance(expected, str): expected expected.strip() if value expected: correct 1 return correct / len(fields)字段级准确率的好处是可量化、可比较。材料中 72% 到 100% 的提升应该是在类似字段级标准下得到的你要在自己的数据上用同一标准验证。6.5 对比维度对比项单次调用两次调用字段准确率记录记录输入 token 总和记录记录输出 token 总和记录记录总耗时记录记录失败率记录记录如果两次调用的准确率没有提升优先检查第一次提取的 prompt 是否把要求写清楚了以及第二次校验是否真的拿到了原文上下文。6.6 判断成功与否如果准确率提升超过 5 个百分点且成本没有上升方案成功。如果准确率提升但成本上升说明第二次调用模型的输出太啰嗦需要继续压缩 prompt。如果准确率没有提升说明任务本身不适合拆成两步回到单次调用再优化提示词。7. 接口 API 与批量任务如果要把这个方案接到业务系统里可以直接封装成 API或者用批量脚本处理目录文件。7.1 封装成 API 服务用 FastAPI 做一个轻量示例# requirements.txt: fastapi uvicorn requests from fastapi import FastAPI, Request from pydantic import BaseModel app FastAPI() class ExtractRequest(BaseModel): text: str app.post(/extract) def extract(req: ExtractRequest): first first_extract(req.text) second second_validate(req.text, first) return {result: merge_result(first, second)} # 启动命令 # uvicorn app:app --host 127.0.0.1 --port 8000把第五节的三个函数导入进来接口就可以直接跑通。注意这边的目标不是工程脚手架而是说明“两次调用”逻辑可以完全嵌在业务 API 后面对调用方透明。7.2 批量任务设计批量处理时把文件列表读进来逐条处理并在每步做日志记录import time import json def run_batch(input_dir, output_dir, log_dir): import os files os.listdir(input_dir) for f in files: with open(os.path.join(input_dir, f), r, encodingutf-8) as fr: text fr.read() start time.time() try: first first_extract(text) second second_validate(text, first) result merge_result(first, second) with open(os.path.join(output_dir, f .json), w, encodingutf-8) as fw: json.dump(result, fw, ensure_asciiFalse, indent2) except Exception as e: with open(os.path.join(log_dir, f .err), w, encodingutf-8) as ferr: ferr.write(str(e)) cost_time time.time() - start print(f{f}: {cost_time:.2f}s)批量任务需要关注失败率。如果某个文件第二次调用输出不是合法 JSON可以做最多三次重试每次重试把第一次结果加上“上次输出不是合法 JSON”的错误信息重新发给校验模型。7.3 接口调用示例如果是 curl 调用上面封装好的 APIcurl -X POST http://127.0.0.1:8000/extract \ -H Content-Type: application/json \ -d {text: 张三5年Python开发经验精通FastAPI、Docker目前在某某科技有限公司担任后端工程师。}返回结构大致如下实际字段以你的 schema 为准{ result: { 姓名: 张三, 工作年限: 5年, 技能列表: [Python, FastAPI, Docker], 最近一份工作公司: 某某科技有限公司 } }8. 资源占用与性能观察8.1 显存占用如果使用本地模型显存占用取决于模型大小。一个 7B 量化模型通常需要 6G 到 8G 显存CPU 推理也能跑但速度慢。如果在两台机器上分别部署第一次和第二次调用所需模型可以让两者并行处理不同文件吞吐量更高。显存占用要按你实际部署的模型来看。7B、14B、70B 的差距很大不要拿某个模型的数值直接套用到别的模型。8.2 Token 消耗观察观察 token 消耗是这套方案落地最关键的步骤。要在每次调用前后记录输入 token 数。输出 token 数。通过对比单次调用的 token 总量可以快速算出降本幅度。建议把日志写到 CSV 文件里timestamp,strategy,input_tokens,output_tokens,cost_estimate,status 2025-01-01 10:00:00,two_call,1300,150,0.003,success后续可以用 pandas 或 Excel 直接统计。8.3 影响性能和成本的因素输入文本长度第一次调用的主要成本来源。字段数量越多第一次输出 token 越长成本越高。第二次调用输入不要用全文只保留第一次结果和定位到的原文片段否则成本会被拉高。为了让第二次调用更省 token可以在第一次 prompt 里要求模型同时输出“字段对应的原文片段”第二次就只用这个片段做校验不用整篇文本。8.4 如何降低延迟延迟方面两次调用意味着平均多出一倍的网络往返或推理时间。如果两次调用都使用同一服务可以尝试第一次用小模型第二次用小模型或同模型减少单次推理时间。如果 API 允许并发可以多线程处理不同样本。本地服务可以用 vLLM 提升吞吐。9. 常见问题与排查方法问题现象可能原因排查方式解决方案第二次调用输出为空 JSON但字段仍有明显错误校验 prompt 没有引导模型发现错误查看第一次结果和人工标注对比在第二次 prompt 中增加“逐字段对照原文”的明确要求成本没有明显下降第二次调用输入仍使用了整篇原文检查第二次调用输入的 token 数第二次只传入第一次定位到的原文片段模型输出不是合法 JSON模型能力不足或 prompt 未给示例查看原始输出内容加 JSON 示例或用代码做格式修复批量任务中途卡住某条文本太长请求超时查看日志中的 timeout 报错增加 timeout 时间或按长度分片处理第一次提取结果为空文本格式与 prompt 假设不符打印第一次 prompt 和输入文本检查文本是否可以正常读取调整 prompt 描述本地模型推理速度很慢模型较大或 CPU 推理观察 CPU/GPU 占用换小模型、量化模型或加 GPU准确率提升不明显任务不适合两步拆分或第二次校验未生效对比两次结果与标注的差异分布先诊断错误类型再决定是否继续使用该策略10. 最佳实践与使用建议第一次先用小数据集跑通全流程不要直接上全量数据。30 条样本足够判断方案是否有效跑一次也就几分钟。第二次调用的 prompt 要重点设计。它决定了整个方案的准确率上限。建议把第二次调用的 prompt 写成类似“代码 review 任务”的语气正在解决问题的模型不需要重写所有逻辑只需要检查改动点。这个角色设定能有效减少模型乱发挥的情况。字段个数要克制。一次提取 20 个字段以上时第一次调用的输出 token 会显著膨胀。如果字段很多考虑按业务域拆成多个独立提取任务各自做两次调用。合并逻辑必须放在代码里。让第三次 LLM 调用去合并结果既增加成本又引入新的不确定性。成本估算不要只看模型单价还要看输出 token 的实际长度。有些模型输出冗长解释即使单价低总 cost 也不低。通过 token 日志持续监控是唯一可靠的方式。涉及人脸、声音、肖像、版权素材的数据在进入任何 LLM 调用链路之前必须先确认数据来源合法、处理授权清晰尤其是批量解析合同、简历、发票这类包含个人敏感信息的场景。测试环境建议用脱敏数据生产环境要控制数据访问范围。11. 总结与下一步这套“两次 LLM 调用代替一次调用”的方案核心价值有两个一是用更细的任务拆分降低输出 token从而压成本二是用一次低成本的校验环节提升准确率。它不依赖某个特定模型API 路线和本地部署路线都能跑。如果你现在面对的是长文本、固定字段、重复性高的提取任务建议先做三件事准备 30 条带标注的真实样本。分别跑单次调用和两次调用。对比字段准确率和 token 消耗。最容易踩的坑也提前说清楚第二次调用千万不要把整篇原文又丢进去那样成本不会有明显下降字段一定要固定输出格式一定要在代码里校验。后续如果想继续优化可以在这个框架上叠加更多工程手段比如基于规则的预筛、多模型投票、结果缓存、字段置信度打分。先把“两次调用”这条基线跑通后面的优化都会更可控。