尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Instructor 用量追踪实战:非流式请求的 Token 统计、截断异常与多 Provider 差异

发布时间:2026/9/15 12:45:40

资讯中心
01
ARTICLE

Instructor 用量追踪实战:非流式请求的 Token 统计、截断异常与多 Provider 差异

Instructor 用量追踪实战:非流式请求的 Token 统计、截断异常与多 Provider 差异
Instructor 用量追踪实战非流式请求的 Token 统计、截断异常与多 Provider 差异【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor本文是一份以 Instructor 为核心的 Token 用量追踪指南聚焦非流式结构化输出场景如何使用create_with_completion读取单次请求的CompletionUsage、在上下文超限时捕获IncompleteOutputException并按超额 Token 裁剪 Prompt以及理解重试累计用量、Provider 计费口径差异与 Hook 快照的正确姿势。读完你可以在生产代码中准确计量每一次 LLM 调用的 Token 消耗避免把账算错。一、为什么需要单独追踪非流式请求的用量Instructor 的核心工作是把 LLM 的自由文本输出约束成 Pydantic 模型这个过程中真实发生的 Token 消耗并不总是等于你心里估算的 Prompt 长度。尤其是结构化输出触发工具调用/函数调用时模型会额外产出 JSON 骨架与字段说明而一旦触发验证失败重试reask一次请求背后可能隐藏着多次 provider 调用。因此单次请求的成本需要精确计量用于计费、配额、成本分摊上下文长度超限max_tokens不足导致的截断需要被识别并优雅处理多次重试的用量不能重复累加或遗漏需要一套统一的累计用量语义。本文所讲的场景均围绕 OpenAI 风格的非流式请求展开即client.create(...)/client.create_with_completion(...)返回完整结果后再做处理的方式。二、用create_with_completion读取单次用量在 Instructor 中最简单的用量检查方式是访问create_with_completion返回的 completion 对象。该方法与create的唯一区别是返回值是(模型实例, 原始 completion)的元组让你同时拿到验证后的结构化对象和底层 API 的完整响应。import instructor from pydantic import BaseModel client instructor.from_provider(openai/gpt-4.1-mini) class UserExtract(BaseModel): name: str age: int user, completion client.create_with_completion( response_modelUserExtract, messages[ {role: user, content: Extract jason is 25 years old}, ], ) print(completion.usage) CompletionUsage( completion_tokens10, prompt_tokens79, total_tokens89, completion_tokens_detailsCompletionTokensDetails( accepted_prediction_tokensNone, audio_tokens0, reasoning_tokens0, rejected_prediction_tokensNone, ), prompt_tokens_detailsPromptTokensDetails(audio_tokens0, cached_tokens0), ) 几点说明client instructor.from_provider(openai/gpt-4.1-mini)是 from_provider 统一客户端创建方式传入provider/model-name字符串即可无需手动 patch SDKcreate_with_completion的签名见 客户端实现支持messages、response_model、max_retries默认 3以及透传给底层 SDK 的**kwargs并同时提供同步与异步两个版本输出的CompletionUsage来自 OpenAI SDK 类型prompt_tokens是输入 Tokencompletion_tokens是输出 Tokentotal_tokens是二者之和子字段cached_tokens、reasoning_tokens等则进一步拆分了缓存命中与推理 Token 的分布。三、捕获IncompleteOutputException处理上下文超限当请求内容超过模型上下文长度、输出被max_tokens截断时Instructor 会抛出IncompleteOutputException。典型触发场景包括过大的结构化输出、过详细的回答要求、max_tokens设置过低。该异常定义在 异常模块兼容导出见 core/exceptions.py并携带last_completion属性指向被截断前 LLM 返回的部分响应。你可以据此拿到截断时的用量并按超额 Token 数裁剪 Prompt 后重试from instructor.core.exceptions import IncompleteOutputException import instructor from pydantic import BaseModel client instructor.from_provider(openai/gpt-4.1-mini) class UserExtract(BaseModel): name: str age: int try: client.create_with_completion( response_modelUserExtract, messages[ {role: user, content: Extract jason is 25 years old}, ], ) except IncompleteOutputException as e: token_count e.last_completion.usage.total_tokens # type: ignore # your logic here实践中常用的处理策略直接重试提高max_tokens后重新调用异常文档建议参考 errors.py 中的示例裁剪 Prompt读取e.last_completion.usage.total_tokens据此截断或摘要输入再发起第二次调用拆分任务把大提取任务拆成多个小模型任务避免单次输出过长改用流式 Partial 模型通过 Stream Partial 拿到不完整但可用的中间数据。四、重试累计用量语义与陷阱结构化输出往往伴随验证失败后的自动重试reask此时用量统计有一套特殊规则本小节即文档中 Retry totals and provider differences 的展开。4.1 累计而非覆盖对于 OpenAI Chat Completions 或稳定版 Anthropic Messages 用量Instructor 会跨验证重试累计用量并原地更新返回 completion 的usage字段。也就是说重试结束后completion.usage是多次尝试的累计值而不仅仅是最后一次尝试的原始用量。因此不要把你保留的多次重试 completion 各自的 usage 相加否则会重复计数。成功返回的 Pydantic 模型实例以及ListResponse列表响应结果在所有观察到的响应都具有兼容用量元数据时还会暴露_total_usage属性直接携带最终累计快照。该逻辑的源码实现在 用量累计模块update_total_usage会按字段递归累加数值_accumulate_models见 usage.py并支持 OpenAICompletionUsage与 AnthropicUsage两类结构重试主循环在 重试实现 中调用has_compatible_usage判定兼容性后执行累计并在解析成功时通过_finalize_parsed_response把快照挂到结果对象上retry.py。4.2 累计并非所有 SDK 都支持该记账能力并不适用于每个 SDK 或 APIOpenAI Responses使用不同的用量类型不参与累计Anthropic beta Messages以及大多数原生 provider SDK同样不提供累计_total_usage它们的原始 usage 可能仍可通过 completion 读取缺失的可选用量字段不等于零消耗。当前累计逻辑可能把部分缺失字段填成 0或沿用已知的小计因此不要根据字段缺失就断言这次没花钱流式解析结果不提供最终累计用量记录被中断的流甚至可能永远等不到最终用量。4.3 不要对 Token 字段无脑求和不同 provider 的 Token 分类口径差异很大直接相加所有数字字段会算错账OpenAIcached_tokens缓存命中的 prompt token与reasoning_tokens推理 token是外层总量的细分项二者已包含在prompt_tokens/completion_tokens中不能再加一遍Anthropiccache_read_input_tokens与cache_creation_input_tokens缓存读/缓存写入 token必须加到未缓存的input_tokens上才能得到包含缓存的完整输入量因此把每个数值字段都加一遍或仅凭 total_tokens 推导账单都是错误姿势。这一点也有对应测试佐证例如 tests/test_usage_accounting_contracts.py 中验证 OpenAI 子集不会被重复累加、Anthropic 缓存字段的独立语义等。4.4 用 Hook 拿到每次尝试的累计快照文档建议使用completion:usageHook 获取复制的累计快照。该事件在重试循环内所有响应均具有兼容用量元数据时触发且每个事件都收到一份独立深拷贝修改它不会影响后续事件或 Instructor 自身记账。注意快照是累计值处理时应用新值替换旧值而不是把多次事件相加该 Hook 不是流式用量 Hook仅适用于非流式场景。要保留每次尝试的 provider 原始元数据而非累计值应在completion:responseHook 内、Instructor 更新响应之前立刻深拷贝from copy import deepcopy attempt_usage [] def record_attempt(response): attempt_usage.append(deepcopy(getattr(response, usage, None))) client.on(completion:response, record_attempt)注意事项源自原文档的适用范围说明该示例适用于带usage属性的非流式响应Google 响应使用usage_metadata而非usage原生 xAI 走独立路径本地响应缓存命中时可以恢复历史 completion 用量而无需发起新的 provider 请求这与 provider 侧的 prompt caching 是两回事前者是应用层缓存后者是模型服务商缓存。五、端到端示例完整用量感知调用把上述要点整合为一个带成本记账的调用骨架import instructor from copy import deepcopy from pydantic import BaseModel from instructor.core.exceptions import IncompleteOutputException client instructor.from_provider(openai/gpt-4.1-mini) attempt_usage [] def record_attempt(response): attempt_usage.append(deepcopy(getattr(response, usage, None))) client.on(completion:response, record_attempt) class UserExtract(BaseModel): name: str age: int try: user, completion client.create_with_completion( response_modelUserExtract, messages[ {role: user, content: Extract jason is 25 years old}, ], ) # completion.usage 为跨重试的累计值attempt_usage 为逐次原始快照 print(fcumulative: {completion.usage.total_tokens}) print(fattempts: {len(attempt_usage)}) except IncompleteOutputException as e: # 上下文超限按超额 token 裁剪 prompt 后重试 exceeded e.last_completion.usage.total_tokens print(ftruncated at {exceeded} tokens)六、总结与最佳实践关注点正确姿势读取单次用量用create_with_completion返回的completion.usage上下文超限捕获IncompleteOutputException读last_completion.usage.total_tokens裁剪 Prompt 或提升max_tokens重试后用量completion.usage是累计值不要把多次 completion 的 usage 相加结果对象上的用量兼容场景下 Pydantic 模型 /ListResponse暴露_total_usage逐次原始快照在completion:responseHook 中立即deepcopy(response.usage)累计快照使用completion:usageHook用新值替换旧值而非求和跨 provider 记账OpenAI 细分字段已含于总量Anthropic 缓存 token 需加到input_tokensOpenAI Responses、Anthropic beta、原生 SDK 不保证累计流式与缓存流式解析无最终累计用量本地响应缓存可恢复历史用量与 provider prompt caching 不同进一步阅读用量与 Token 追踪概念索引Hooks 完整事件表与累计用量语义from_provider 统一客户端创建原始响应访问获取 Pydantic 响应模型【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。