快速结论:报错 “Streaming a large tool call re-parses its arguments in pure Python on every chunk” 通常出现在使用 LangChain 流式输出一个包含大段工具调用参数(tool call args)的链式调用时,例如 (prompt | model).stream() 或 with_structured_output(...).stream()。优先排查是否在链式流式累积中反复对不断增长的 tool call args 执行 parse_partial_json,这是导致耗时随参数体积近似平方增长的主要原因。
适用环境:Issue 中已确认的环境为 langchain-core 1.6.5(讨论中提及 1.6.x),触发路径为链式流式累积 (prompt | model).stream();直接 model.stream() 基本不受影响。Issue 正文未明确说明操作系统、CUDA、显卡等环境,无需额外排查这些项。
最快修复方案:暂无确认的一步修复方案。Issue 讨论中提出的可优先尝试方案是:缓冲链式流式产生的 AIMessageChunk,在流结束时一次性合并,而不是逐块相加;同时将 parse_partial_json 重试时的修剪起点改为 JSONDecodeError.pos。这些改动来自讨论中的提案(PR #40841),尚未在 Issue 中标记为已合并或已验证。
注意事项:上述修复只针对链式流式累积路径。with_structured_output(...).stream() 这类必须逐块输出部分解析结果的路径,以及文档示例里 full = full + chunk 的写法,仍会逐块解析,性能问题可能依旧存在。Issue 讨论中提到的正则重写方案属于可选优化,未纳入最小改动范围。
问题场景
用户在 LangChain 中使用链式流式调用,例如 ChatPromptTemplate | BaseChatModel 然后 .stream(),让模型流式返回一个较大的 write_file 工具调用,参数约 5KB 到 20KB,并以很小的 token 级片段逐块到达。直接调用 model.stream() 速度正常,但经过 prompt 链包装后的 (prompt | model).stream() 随着 tool call 参数变大而显著变慢。Issue 示例中参数每翻倍一次,链式调用约慢 3.5 倍。
报错原文
Streaming a large tool call re-parses its arguments in pure Python on every chunk
原因分析
可能原因是:链式流式累积在 _transform_stream_with_config 中逐块把输出合并到最终结果,以便在 on_chain_end 时提供完整输出。每次合并 AIMessageChunk 都会构造新 chunk,其 init_tool_calls 校验器会对“已累积的完整 tool call args”重新执行 parse_partial_json。因此单块成本随累积体积线性增长,整体呈二次增长。讨论中给出的 profile 显示,100KB、6551 个块、总耗时 46.5s 时,约 45.6s 花在 parse_partial_json 上,其中大部分消耗在纯 Python 字符循环和逐字符 list.append。直接 model.stream() 和 invoke 路径基本不受影响。相关历史 Issue 为 #24021、#24031。
环境排查
- 确认
langchain-core版本是否为 1.6.5 或 1.6.x;Issue 已在该版本确认问题。 - 区分触发路径:仅
model.stream()是否正常,(prompt | model).stream()是否明显变慢。 - 确认是否在链式流式场景中累积包含大段 tool call args 的
AIMessageChunk。 - 如需复现,可用 Issue 中的
ToolCallStreamer示例,分别测量 5KB、10KB、20KB 参数体积下的耗时。 - Issue 未涉及 CUDA、显卡、PyTorch 或操作系统版本,无需将这些作为排查项。
解决步骤
- 先用最小复现代码确认问题:直接
model.stream()与(prompt | model).stream()在大 tool call args 下的耗时差异是否符合“链式累积二次增长”的特征。 - 如果确认是链式累积路径的问题,可优先尝试缓冲流式块:收集
AIMessageChunk,在流结束时一次性合并生成最终输出,而不是在每块到达时立即相加。讨论指出这与generate_from_stream的 invoke 路径惯用做法一致,且不改变逐个 yield 的块内容。 - 可优先尝试优化
parse_partial_json的重试修剪逻辑:在捕获JSONDecodeError后,从e.pos处截断,而不是逐字符删除;讨论认为更长的候选前缀包含同一错误字符及上下文,结果应保持一致。 - 如仍在本地维护补丁,需回归流式 JSON 相关测试,覆盖多 index 交错的 tool call、usage metadata、id 排序以及
strict两种模式,确认合并结果与逐块折叠一致。 - 关注对应修复 PR 的合并状态;在未合并前,不建议直接把讨论中的补丁当作稳定方案用于生产。
验证方法
使用 Issue 中的复现脚本,分别对比 5KB、10KB、20KB 参数体积下 model.stream() 与 (prompt | model).stream() 的耗时。修复后,链式调用不应再随参数体积呈近似平方增长,讨论中给出的参考结果是 Issue 复现从 2.06s 降到 0.10s 并恢复线性扩展。同时确认最终合并输出的 tool call args 与修复前一致,且流式过程中逐块 yield 的内容没有改变。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


