尧图精选

gpt-researcher:基于 Anthropic 原生 Token Usage 的精准成本追踪实现

🕒 发布时间:2026/9/10 1:50:46 📁 来源:尧图网络
gpt-researcher基于 Anthropic 原生 Token Usage 的精准成本追踪实现【免费下载链接】gpt-researcherAn autonomous agent that conducts deep research on any data using any LLM providers项目地址: https://gitcode.com/GitHub_Trending/gp/gpt-researcher在 gpt-researcher 的多供应商 LLM 架构中成本追踪是研究任务可观测性的重要组成部分。本文以仓库中的 PR 说明文档 PR_pr_feat-anthropic-real-usage-cost-tracking.md 为主线讲清这次变更解决的问题为什么用通用 tiktoken 估算器给 Anthropic 请求计费会失真以及项目如何通过gpt_researcher/utils/costs.py中新增的 Anthropic 定价规则、GenericLLMProvider中的响应元数据捕获、以及统一的成本回调链路让 Anthropic 的实际花费尤其是流式响应与工具调用场景基于供应商返回的原生 token 用量来精确计算。问题背景通用估算器为何不适用于 AnthropicPR 文档在 Problem 一节明确指出在改动之前Anthropic 请求使用的是基于文本长度的通用估算器而不是供应商原生的 token usage 元数据。这导致 Anthropic 的总成本不准确在以下两类场景尤其严重流式streaming响应token 用量通过流式 chunk 的响应元数据到达而不是随最终文本一次性可得工具调用tool-calling流程usage 信息携带在响应对象的结构化元数据中与纯文本长度没有对应关系。而项目原有的兜底估算器estimate_llm_cost()位于 costs.py的做法是用tiktoken的o200k_base编码对输入/输出文本分别分词再按 OpenAI 的单价输入 $0.000005/token、输出 $0.000015/token计费。对 Anthropic 模型来说这有两个系统性偏差一是 tokenizer 与 Anthropic 实际分词方式不同token 数本身就不准二是套用了 OpenAI 单价而 Anthropic 各模型Opus/Sonnet/Haiku的定价体系完全不同。变更方案总览PR 文档的 Solution 一节列出了五条改动每一条都能在仓库源码中找到对应实现在 gpt_researcher/utils/costs.py 中新增 Anthropic 专属定价规则ANTHROPIC_MODEL_PRICING当 Anthropic 返回原生 token 计数时优先使用response_metadata[usage]或usage_metadata在 GenericLLMProvider 中同时捕获普通响应和流式响应的 usage 与响应元数据create_chat_completion()与工具调用流程复用同一条成本计算路径calculate_llm_cost为定价与流式元数据捕获新增聚焦测试tests/test_costs.py、tests/test_llm_usage_tracking.py。源码剖析Anthropic 定价规则与优先级设计定价表ANTHROPIC_MODEL_PRICING核心数据结构是ANTHROPIC_MODEL_PRICING定义在 costs.py。它是一个(匹配模式, 输入 $/MTok, 输出 $/MTok)的有序元组列表首个命中的模式生效因此更具体的模式如带日期后缀的模型名必须排在基础模型之前匹配模式输入$/MTok输出$/MTokclaude-opus-4-75.025.0claude-opus-4-65.025.0claude-opus-4-5、claude-4-opus5.025.0claude-opus-4-115.075.0claude-opus-415.075.0claude-sonnet-4-63.015.0claude-sonnet-4-5、claude-4-sonnet3.015.0claude-sonnet-43.015.0claude-haiku-4-51.05.0claude-3-5-haiku0.84.0匹配用的是子串包含而非精确相等见 _get_anthropic_pricingany(pattern in normalized_model_name for pattern in patterns)因此带日期后缀的具体快照模型如claude-haiku-4-5-20251001也能被claude-haiku-4-5命中。这一点被 tests/test_costs.py 中的test_calculate_llm_cost_supports_dated_anthropic_model_names用例验证2000 输入 1000 输出 tokens 的claude-haiku-4-5-20251001请求计为2000×1/10⁶ 1000×5/10⁶ $0.007。模型名解析以响应元数据为准Anthropic 的实际计费模型以响应里的模型名为准请求参数里传的可能是别名。因此_resolve_anthropic_model_name()costs.py的取值优先级是response_metadata[model]→response_metadata[model_name]→ 请求传入的model参数最后统一转小写。这与定价表的子串匹配配合保证了即使调用方传入的模型名不规范也能按真实服务模型计价。usage 提取优先级response_metadata[usage] 优先_extract_anthropic_usage()costs.py实现了 PR 中“优先response_metadata[usage]/usage_metadata”的语义先取response_metadata里的usage字段——这是 Anthropic Messages API 原生返回结构input_tokens、output_tokens以及两个缓存字段若其中缺少input_tokens或output_tokens再退回 LangChain 标准化后的usage_metadata两个来源都拿不到完整计数时返回None触发后续的兜底逻辑。tests/test_costs.py中的test_calculate_llm_cost_prefers_native_anthropic_usage第 47-65 行专门验证了这个优先级同时传入response_metadata.usage {100, 50}和usage_metadata {999999, 999999}时claude-opus-4-7的计费结果是100×5/10⁶ 50×25/10⁶ $0.00175即明确采用了原生 usage 而非干扰值。另外_coerce_token_count()costs.py对缺失或非法的 token 数值一律降级为 0保证个别字段缺失不会让整条成本链路抛异常。Prompt 缓存 token 与 US 推理区域加价这是原通用估算器完全无法覆盖、而 PR 新增实现的两个计费细节Prompt 缓存calculate_anthropic_cost()costs.py把 usage 拆成四部分计费。注意input_tokens在 Anthropic 语义中只包含非缓存输入缓存字段单独计价cache_creation_input_tokens写缓存按输入价的1.25 倍计费cache_read_input_tokens读缓存按输入价的0.1 倍计费。以 tests/test_anthropic_cache_costs.py 中的用例为例模型claude-sonnet-4-5输入 50、输出 300 tokens场景计费构成结果无缓存50×3 300×15单位 $/MTok$0.00465另有 4000 缓存写入 tokens加上 4000×3×1.25$0.01965另有 4000 缓存读取 tokens加上 4000×3×0.1$0.00585US 推理区域加价_get_anthropic_pricing_multiplier()costs.py检查请求参数中的inference_geo当其值为us且模型属于ANTHROPIC_US_INFERENCE_GEO_MODELSclaude-opus-4-7、claude-opus-4-6、claude-sonnet-4-6时整笔成本乘以1.1的系数其他情况保持 1.0。该系数所需的request_options由调用方传入见下文成本链路的provider_kwargs。兜底行为拿不到 usage 时的降级calculate_llm_cost()costs.py的入口判断是llm_provider anthropic先尝试calculate_anthropic_cost()只有返回非None时才采用否则继续走通用路径LangChain 标准化 usage → tiktoken 估算。两个兜底点都有明确语义usage 缺失calculate_llm_cost退化为estimate_llm_cost(input_content, output_content)的 tiktoken 估算tests/test_costs.py的test_calculate_llm_cost_falls_back_without_usage验证了该回退结果与直接调用估算器完全一致模型无定价规则如出现未收录的新 Anthropic 模型_get_anthropic_pricing返回Nonecalculate_anthropic_cost会记录一条Missing Anthropic pricing rule for model ...的 warning 日志并返回None同样回退到 tiktoken 估算而不是中断研究流程。供应商侧元数据捕获GenericLLMProvider有了计价函数还不够前提是从 LLM 响应对象上稳定拿到 usage 数据。这部分对应 PR 的第三条方案实现在 gpt_researcher/llm_provider/generic/base.py 的GenericLLMProvider实例维护两个状态字段last_usage_metadataLangChain 标准化 usage与last_response_metadata原始响应元数据含 Anthropic 的usage与model字段在 构造函数 中初始化_reset_last_response_metadata()在每次新请求开始前清空旧值避免上一次调用的 usage 污染本次计费_capture_response_metadata()从消息对象读取usage_metadata与response_metadata属性兼容model_dump()的 pydantic 对象与普通 dict。捕获点覆盖了两条响应路径# 普通非流式响应ainvoke 后整体捕获一次 async def get_chat_response(self, messages, stream, websocketNone, **kwargs): self._reset_last_response_metadata() if not stream: output await self.llm.ainvoke(messages, **kwargs) self._capture_response_metadata(output) res output.content else: res await self.stream_response(messages, websocket, **kwargs) ...# 流式响应对每个 chunk 持续捕获usage 通常随最后一个 chunk 到达 async for chunk in self.llm.astream(messages, **kwargs): self._capture_response_metadata(chunk) ...其中response_metadata的合并方式是增量式{**旧值, **新值}因为流式 chunk 的元数据可能分多次补全。tests/test_llm_usage_tracking.py 用一个模拟流式 LLM 验证了关键边界第一个 chunk 只有内容Hello、第二个 chunk 内容为空但携带usage_metadata {input_tokens: 321, output_tokens: 123}和response_metadata {usage: {...}}断言stream_response在拼接完文本后provider.last_usage_metadata与provider.last_response_metadata[usage]均被正确填充。这正是 PR 问题描述中“streaming 场景 usage 通过响应元数据到达”的解决点。统一成本路径普通补全与工具调用两条链路PR 第四条方案要求create_chat_completion()与工具调用流程使用同一条成本计算路径仓库中对应的两处调用点如下。普通补全gpt_researcher/utils/llm.py 的create_chat_completion()在拿到非空响应后若调用方传入了cost_callback则以 provider 上捕获的元数据统一计价if cost_callback: llm_costs calculate_llm_cost( llm_providerllm_provider, modelmodel, input_contentstr(messages), output_contentresponse, response_metadataprovider.last_response_metadata, usage_metadataprovider.last_usage_metadata, request_optionsprovider_kwargs, ) cost_callback(llm_costs)注意这里response_metadata和usage_metadata来自 provider 实例流式场景下是逐 chunk 累计后的结果request_options传入provider_kwargs含inference_geo等参数以支持 US 区域加价判断。工具调用流程gpt_researcher/utils/tools.py 提供_track_response_cost()辅助函数从 LangChain 消息对象的response_metadata/usage_metadata属性上直接取值调用同一个calculate_llm_costcreate_chat_completion_with_tools()在首次 LLM 调用第 117-124 行和工具调用完成后的最终响应第 193-198 行各记账一次保证多轮工具交互的每步花费都被计入。这些cost_callback的最终汇聚点是GPTResearcher的 add_costs()它把成本累加到self.research_costs并按当前步骤名记入step_costs分步字典研究流程中的规划、写作、上下文压缩、深度研究等各环节如 skills/writer.py、skills/context_manager.py、actions/report_generation.py均以cost_callbackself.researcher.add_costs接入最终可通过get_costs()拿到整次研究的总成本。验证方式与测试覆盖PR 文档 Testing 一节给出的本地验证命令为uv run python -c import gpt_researcher; print(ok) uv run python -m unittest tests.test_costs tests.test_llm_usage_tracking仓库中对应的测试资产与断言要点测试文件覆盖点tests/test_costs.pyAnthropic 原生 usage 计价sonnet-4-6 1000/500 → $0.0105、带日期模型名命中、response_metadata.usage优先于usage_metadata、无 usage 时回退 tiktoken 估算另有 OpenAI 缓存折扣等用例tests/test_llm_usage_tracking.py流式场景下空内容 chunk 携带的usage_metadata/response_metadata[usage]被GenericLLMProvider正确捕获tests/test_anthropic_cache_costs.pycache_creation_input_tokens1.25×与cache_read_input_tokens0.1×分别抬高 Anthropic 成本的正确性小结这次变更的本质是把 Anthropic 从“按 OpenAI 单价 tiktoken 分词数”的近似计费升级为“按 Anthropic 原生 usage 分模型定价表”的精确计费并把流式捕获与工具调用两条链路收敛到同一个calculate_llm_cost入口。适用前提与限制定价表以仓库当前 costs.py 中收录的 Claude 4.x 系列模型为准注释标明价格截至 2026 年 7 月新模型上线后需要按“具体模式在前”的约定补充规则否则会自动降级为 tiktoken 估算并打 warning 日志缓存与inference_geo两个计费维度只有在请求/响应中真实携带相应字段时才会生效不携带时结果与非缓存、非 US 场景完全一致。PR 明确声明无破坏性变更Breaking changes: None对既有其他供应商的成本路径无影响。【免费下载链接】gpt-researcherAn autonomous agent that conducts deep research on any data using any LLM providers项目地址: https://gitcode.com/GitHub_Trending/gp/gpt-researcher创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →