数据查到了,回答去哪儿了?一次百炼 + LangChain 排查实录

我们在公司的业务软件里做了一个 AI 经营助手。以前用户想看当天的营业额,要进后台找报表、选日期。现在可以直接问"今天的经营情况怎么样?",看完还能接着问"那最近一周呢?"

我负责 Agent 这部分,用 LangChain 和 LangGraph 串起查询流程,模型接的是阿里云百炼的 OpenAI 兼容接口。有一次,日志上一行还写着"查询完成",收入、成本都在。再往下一行,却报了"未获取有效回答"。

我拿同一句话再试,它答得好好的。就这个时好时坏的问题,我跟 AI 一起查了好几天。最后确实找到一个兼容问题:百炼发来的思考内容,LangChain 的 ChatOpenAI 默认接不住,也不会替你传回下一轮。 这件事怎么查实的,以及它和那次空回答有什么关系,得分开说。

数据查完了,模型还得再开一次口

用户只问了一句,背后其实调了两次模型:

复制代码
第一次调模型 → 决定查什么,给出工具名和查询参数
程序执行工具 → 从业务接口拿到经营数据
第二次调模型 → 根据这些数据写出回答

第一次只要给出工具指令,正文空着也能继续。第二次才负责写用户等着看的那段话。

我最先怀疑的是流式代码。程序把模型生成的内容一小段一小段发给页面,遇到工具参数之类的分片会跳过。会不会模型已经回了字,被我一个 continue 跳过去了?

我让 AI 把每个分片都打出来。屏幕上果然刷过一排 content=''。我还以为抓到了,再往下翻,好家伙,同一个请求后面接着出了正文,最后答得好好的。

后来我把正常和失败的请求放在一起,直接看工具结果后面的最后一条完整消息。真正失败的那次,日志里是这样的:

ini 复制代码
content=''           # 最终正文为空
tool_calls=[]        # 没有新的工具调用
finish_reason='stop' # 这一轮结束了
output_tokens=0      # 应用日志记录的输出 Token 数

这才把问题缩小了。流式代码跳过某个分片,可能让页面漏几个字。可这次应用拿到的最终消息本身就是空的。业务查询已经完成,报错发生在最后那次模型调用之后。

第一次联网,日志给全了,搜来的办法还是没用

第一次让 AI 联网,我已经把失败日志、工具查询成功、同一个问题偶尔能正常回答这些情况都交代了。搜回来几个方向,我也照着试了:查分片过滤、核对 SDK 版本、换模型的初始化方式。

其中一个办法是用 LangChain 的 init_chat_model 创建模型。我改完特意打印了实际类型,出来的还是 ChatOpenAI。入口换了,读响应的还是同一套代码。至于满屏空分片,前面的成功样本已经说明,光看这个现象,分不出请求最后会不会失败。

第二次,我把搜索范围和要找的东西说得更具体:

去相关项目的 GitHub 仓库找 issue 和 PR。只看工具已经执行成功、下一轮模型结束却没有正文的案例。把对方用的模型、接口、复现步骤和修复状态一起带回来。

我说不准第一次为什么没搜到那些帖子。第二次真正帮上忙的地方,是搜回来的报告有可对照的故障阶段和能照着做的验证步骤 。比如 LangChain 的一条 issue,报告者发现第一轮模型返回的思考字段,没有跟着工具结果传回第二次请求。我之前一直在看"模型回了什么",这才想起来看看"我下一轮发了什么"。 LangChain #35901

三份公开资料,把这个兼容问题对上了

第一,百炼把思考内容和正文放在两个字段里。 开启思考时,content 放给用户看的正文,reasoning_content 单独放思考内容。一个分片可以只带思考内容,正文还是 null。这解释了为什么服务端有东西发出来,应用日志里的正文却可能是一串空字符串。 百炼深度思考文档

第二,LangChain 的通用客户端会略过这个额外字段。 LangChain 仓库里有对应报告,也有人提过修复 PR。回传相关的 issue 被关闭为"不计划修复",保留字段的 PR 也关闭了,未合并。我核对了 langchain-openai 1.7.4 的源码,里面明确说明,ChatOpenAI 面向标准 OpenAI API,第三方接口额外提供的 reasoning_content 不会被它通用提取和保留。这样看,之前换初始化入口没效果,也就说得通了。 字段丢失报告 · 修复 PR · 1.7.4 版源码

第三,Qwen 官方说明过,缺少这段记录可能影响多步工具调用。 Qwen3 的部署文档提到,在 vLLM、SGLang 等自部署场景中,如果请求预处理丢掉了上一轮思考内容,多步工具调用的效果可能下降。百炼文档也给了把历史思考内容传回下一轮的方式。两份资料让我明白了为什么值得查这个字段:前端可以只显示正文,模型的下一步仍可能用到前一步的思考记录。 Qwen3 官方部署说明 · 百炼 Chat Completions 文档

光看文档还不够,我在本地拿一段模拟响应做了个对照。它只带思考字段,正文为空。分别让原版客户端和自定义适配器处理,再检查构造出来的下一轮请求:

本地验证 原版 ChatOpenAI 加了适配的客户端
处理流式响应后,消息里有思考字段 无 有
构造下一轮请求后,历史消息里有思考字段 无 有
这条分片的可见正文 空 空

这个对照把事情说清楚了:字段确实会丢,补上以后也确实能传回去。 至于线上那次空回答,当时没留模型服务的原始响应,所以还不能确认就是这个字段引起的。

最后怎么改?补字段,再给空回答一次重试机会

如果业务用不上思考,模型也支持关闭,可以先关掉思考模式,把调用链简化。如果必须开启,我会优先选能完整处理该服务商字段的适配器。继续用 ChatOpenAI 的话,自定义适配要改两个地方:

复制代码
接收响应 → 把思考内容单独存进模型消息,流式分片到齐后合并
发送请求 → 把思考内容写回上一轮模型消息,再附上这次的工具结果

这里最容易只做前半段。日志能打印出思考内容,说明程序收到了。要确认模型下一轮能看到,还得检查实际发出去的请求 。至于百炼是否把历史思考用到下一轮,按具体模型的接口说明配置即可。 百炼历史思考参数说明

项目里还加了一层处理。模型结束了,正文仍然为空,而且没有新的工具调用时,拿已经查到的数据,只重新请模型答一次。第二次还没有回答,就走原来的错误处理。这样即便再次碰到空返回,也用不着为了等一句话,把同一份经营数据再查一遍。

写到最后,我想起以前查 MyBatis 的自己

以前查 MyBatis,我会从堆栈里抠出半句报错,去 Google 搜,再到 GitHub 翻帖子。好不容易找到一个症状很像的,点进去一看,人家的版本跟我差了好几代,只好关掉继续找。折腾半天,最后顺着源码找到那个奇怪的判断,心里会冒出一句:"哦,原来是这里。"那一刻还挺开心的。

现在用 AI 排查,方法用对了,确实快很多。这次我把失败日志和复现条件说清楚,再指定它去 GitHub 找原始报告,它很快就把相关 issue 和源码摆到了我面前。以前我要开好几个标签页来回对照,现在可以早点动手验证。我很喜欢这种效率。

但有时也会觉得,排查好像少了一点乐趣。以前那个"哦,原来是这里",是自己顺着几条线索一点点摸出来的。现在 AI 已经把解释写好了,我读完觉得有道理,顺手就让它改。等代码跑起来,事情办完了,那点自己想通的高兴劲儿,有时也跟着省掉了。

以后遇到这种问题,我还是会让 AI 先帮我把资料找齐。只是碰到那个看着不太对劲的地方,我想留几分钟,自己点进源码看一眼。毕竟,把 Bug 修好很痛快,自己弄明白它为什么会发生,也挺痛快的。

相关推荐
DigitalOcean41 分钟前
Y Combinator 开源 Agent 运行框架 QM,现已支持在 DigitalOcean 一键部署
agent
isfox1 小时前
Python 虚拟环境:别再把所有项目塞进一个"大仓库"了
后端
易师傅1 小时前
技术面试已死?AI 时代,真正拉开程序员差距的是什么?
前端·后端·程序员
苍何1 小时前
办公 Agent 权威评测榜单出炉了,豆包第一
后端
赢乐2 小时前
Agent 聊到第 20 轮就“失忆“?——长对话上下文丢失的排查与修复完全指南
agent·长对话·上下文丢失·失忆·关键信息·滑动窗口截断·摘要压缩
阿里云云原生2 小时前
让 Agent 成为企业生产力丨Alibaba Cloud AgentCore 企业智能体构建与治理沙龙·杭州站精彩回顾 & PPT 下载
agent
AiMan2 小时前
别再把秘塔会话逐个点开:这可把秘塔多个会话一次导出?AI导出鸭
前端·后端·面试
lucas_AI2 小时前
AGENTS.md 烂了没人管?732 次实测:Coding Agent 每个会话都在替你付「文档债」利息
agent
泯泷2 小时前
AI 最坑的失败:不是不会做,而是"假装做完了"
人工智能·llm·agent