资讯动态

LangChain Messages 实战:用 TaoToken 统一 Key 跑通多轮对话消息链

发布时间:2026/10/5 16:28:04 来源:尧图企业网站定制
1. 多轮对话为什么总在第三轮开始崩先说一个我踩过的坑。早期写客服机器人第一轮问“帮我查订单”模型答得挺好第二轮追问“那什么时候到”它开始答非所问第三轮直接忘了前面聊过什么。当时以为是模型不行换了个更大的模型问题照旧。后来才想明白模型本身没有记忆它每次看到的只是你这次喂进去的消息列表。多轮对话能不能成立取决于你有没有把历史消息按正确顺序、正确角色拼回去。LangChain Messages 就是解决这件事的标准答案。它把“谁说了什么”抽象成消息对象用角色区分身份用 content 装内容用元数据带附加信息。你只要维护一个不断增长的消息列表每次调用模型时把整个列表传进去多轮对话就成立了。听起来简单但真写起来System/Human/AI/Tool 四种消息的拼接顺序、tool_call_id 的对应关系、流式 chunk 的累加每一处都能让你调半天。这篇就聚焦一件事用 LangChain Messages 组织真实多轮对话的消息链并且用 TaoToken 统一 Key 把 Base URL 配好让你不用为每个模型厂商改一遍消息代码。适合谁已经会写 Python、想跑通多轮对话和工具调用的开发者或者你正在用 LangChain 但消息链老是拼错、报错看不懂的人。下面从环境准备到验证请求一步步来代码都能直接复制。2. TaoToken 统一 Key 的前置准备与 Base URL 配置LangChain 的好处是消息类型跨厂商统一但模型接入层如果每家都配一套 Key 和 Base URL换模型时还是得改代码。TaoToken 在这里的作用是提供一个统一的 OpenAI 兼容入口你只需要一个 Key、一个 Base URL就能在 LangChain 里切换不同模型消息代码一行不用动。先拿 Key。打开 https://taotoken.net/api-keys 登录后创建一个 API Key复制出来形如sk-xxxxxxxx。这个 Key 后面所有配置都用它。注意别把它硬编码进 Git 仓库用环境变量或者.env文件管理。Base URL 用https://taotoken.net/api这是 OpenAI 兼容端点。LangChain 里通过ChatOpenAI接入时把base_url指向它即可。模型对话的在线调试入口在 https://taotoken.net/models 你可以先在网页上确认目标模型 ID 能正常返回再写进代码省得在代码里反复试。安装依赖。LangChain 的消息类型在langchain-core里OpenAI 兼容接入用langchain-openaipip install -U langchain-core langchain-openai python-dotenv配置环境变量建一个.envTAOTOKEN_API_KEYsk-你的key TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_MODELgpt-4o-mini这里TAOTOKEN_MODEL填你在模型对话页确认过的模型 ID。不同模型对消息类型的支持程度不一样比如有些模型不支持reasoning内容块有些对name字段直接忽略所以先选一个通用性好的模型跑通再换。初始化模型对象import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI load_dotenv() model ChatOpenAI( modelos.getenv(TAOTOKEN_MODEL), api_keyos.getenv(TAOTOKEN_API_KEY), base_urlos.getenv(TAOTOKEN_BASE_URL), temperature0.3, )到这一步模型对象就绪。注意base_url不要带尾部斜杠LangChain 内部会自己拼/chat/completions。如果你用的是其他框架比如 Codex 的auth.json配置逻辑类似核心三件套永远是 Base URL、Key、Model ID缺一个都连不上。3. 可复制的消息链构造代码与配置片段这一节是核心把四种消息类型串成一条完整的多轮对话链。先给一个最小可运行的消息构造再逐步加上工具调用。最基础的多轮对话用 System 定调、Human 提问、AI 回复拼历史from langchain_core.messages import SystemMessage, HumanMessage, AIMessage messages [ SystemMessage(你是一个订单查询助手回答简洁涉及时间一律用北京时间。), HumanMessage(帮我查一下订单 A123 的状态), AIMessage(订单 A123 已发货预计明天下午送达。), HumanMessage(那能改地址吗), ] response model.invoke(messages) print(response.content)关键点第三条AIMessage是你手动插进去的历史回复。真实场景里这个对象应该是上一轮model.invoke()的返回值直接messages.append(response)就行。手动构造只用于演示或者给模型“喂”一段预设上下文。如果你习惯 OpenAI 风格的字典格式也可以直接传messages [ {role: system, content: 你是一个订单查询助手。}, {role: user, content: 帮我查订单 A123}, {role: assistant, content: 已发货明天到。}, {role: user, content: 能改地址吗}, ]两种写法结果一致但消息对象方式能拿到tool_calls、usage_metadata这些属性做工具调用和成本监控时更方便所以推荐用对象。接下来是工具调用链这是多轮对话里最容易出错的部分。模型发起工具调用后你要执行工具把结果包成ToolMessage传回去且tool_call_id必须和模型给的 id 对上from langchain_core.messages import AIMessage, ToolMessage, HumanMessage # 假设模型上一轮返回了工具调用 ai_message AIMessage( content, tool_calls[{ name: get_order_status, args: {order_id: A123}, id: call_abc123, }], ) # 你执行工具拿到结果 order_result 已发货承运商顺丰预计明天 14:00 前送达 tool_message ToolMessage( contentorder_result, tool_call_idcall_abc123, # 必须匹配 nameget_order_status, ) messages [ HumanMessage(帮我查订单 A123), ai_message, tool_message, ] response model.invoke(messages) print(response.content)tool_call_id对不上模型就不知道这个结果是回应哪次调用的轻则忽略重则报错。我试过把 id 写错一位模型直接回“我没有收到工具结果”排查了十分钟才发现是 id 不匹配。ToolMessage还有个artifact字段不发给模型但程序能访问。比如检索工具把文档正文放content给模型看把文档 ID、页码放artifact给你的程序用tool_message ToolMessage( content这是检索到的正文片段……, tool_call_idcall_abc123, namesearch_docs, artifact{doc_id: doc_789, page: 3}, )这样模型上下文不会被元数据污染你的程序又能拿到结构化信息做引用溯源特别顺手。流式场景下你收到的不是AIMessage而是AIMessageChunk需要累加full None for chunk in model.stream(messages): print(chunk.content, end, flushTrue) full chunk if full is None else full chunk print() print(完整消息类型:, type(full))累加后的full就是完整的AIMessage可以继续 append 进历史。注意 chunk 相加是 LangChain 重载了运算符别自己手动拼字符串否则会丢掉tool_calls等结构化字段。4. 验证请求与返回结果对照配置写完跑一次完整的多轮对话验证。下面这段代码包含两轮对话加一次工具调用你可以直接复制运行import os from dotenv import load_dotenv from langchain_core.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage from langchain_openai import ChatOpenAI load_dotenv() model ChatOpenAI( modelos.getenv(TAOTOKEN_MODEL), api_keyos.getenv(TAOTOKEN_API_KEY), base_urlos.getenv(TAOTOKEN_BASE_URL), ) messages [ SystemMessage(你是一个订单助手回答简洁。), HumanMessage(订单 A123 到哪了), ] # 第一轮 resp1 model.invoke(messages) print(第一轮回复:, resp1.content) print(token 用量:, resp1.usage_metadata) messages.append(resp1) # 第二轮带上历史 messages.append(HumanMessage(那能改收货地址吗)) resp2 model.invoke(messages) print(第二轮回复:, resp2.content) print(消息链长度:, len(messages))预期返回类似第一轮回复: 订单 A123 已发货预计明天下午送达。 token 用量: {input_tokens: 42, output_tokens: 18, total_tokens: 60} 第二轮回复: 已发货订单通常无法直接改地址建议联系承运商拦截。 消息链长度: 5usage_metadata里能拿到输入、输出、总 token 数算成本、做监控都靠它。如果模型支持推理还会有output_token_details.reasoning字段。再验证一次工具调用链的返回。用bind_tools把工具绑上去from langchain_core.tools import tool tool def get_order_status(order_id: str) - str: 根据订单号查询物流状态。 return f订单 {order_id} 已发货预计明天送达 model_with_tools model.bind_tools([get_order_status]) resp model_with_tools.invoke(帮我查订单 A123) print(是否有工具调用:, bool(resp.tool_calls)) for tc in resp.tool_calls: print(工具名:, tc[name]) print(参数:, tc[args]) print(调用 ID:, tc[id])预期输出是否有工具调用: True 工具名: get_order_status 参数: {order_id: A123} 调用 ID: call_xyz789拿到tool_calls后执行工具、包ToolMessage、再调一次模型就完成了一整轮“用户提问 → 模型发起调用 → 工具返回 → 模型最终回复”的消息流转。这套流程跑通多轮对话的消息链就算立住了。5. 常见报错排查401、local proxy failed、reading choices、OAuth跑不通的时候报错信息往往不直观。下面按真实遇到的顺序列几个高频问题。401 Unauthorized。最常见Key 错了或者没读到。先确认.env里TAOTOKEN_API_KEY没有多余空格和引号再确认load_dotenv()在读取环境变量之前执行。如果 Key 是从网页复制的注意别把前后空白带进去。还有一种情况是 Key 被禁用或额度耗尽去 https://taotoken.net/api-keys 确认状态。local proxy failed / connection error。这类报错通常是base_url写错。确认是https://taotoken.net/api不要带/v1后缀也不要带尾部斜杠。LangChain 的ChatOpenAI会自动拼路径你多写一段就 404。另外检查本机网络是否能正常访问该域名公司内网有时会拦截。Error reading choices / KeyError choices。返回体里没有choices字段说明请求根本没走到模型或者返回的是错误结构。先打印原始响应看看import httpx resp httpx.post( https://taotoken.net/api/chat/completions, headers{Authorization: fBearer {os.getenv(TAOTOKEN_API_KEY)}}, json{model: os.getenv(TAOTOKEN_MODEL), messages: [{role: user, content: hi}]}, ) print(resp.status_code) print(resp.text)如果这里返回正常但 LangChain 报reading choices多半是模型 ID 写错或者该模型不支持你传的消息格式。换一个通用模型试。OAuth / authentication 相关报错。如果你用的是 Codex 的auth.json或者 Claude Code 的配置注意它们的认证方式和纯 API Key 不同。Codex 的auth.json里需要OPENAI_API_KEY和base_url两个字段缺一个都会走 OAuth 流程然后失败。Claude Code 接入时Base URL、Key、Model ID 三件套要写全只填 Key 不填 Base URL 会默认走官方端点自然连不上。tool_call_id 不匹配。这个不报错但模型行为异常。表现是模型忽略工具结果或者重复发起同一个调用。检查ToolMessage.tool_call_id是否和AIMessage.tool_calls[i].id完全一致包括大小写。消息顺序错乱。SystemMessage必须在列表最前面ToolMessage必须紧跟在对应的AIMessage之后。顺序错了模型可能不报错但答非所问。建议每次 append 后打印一下角色序列肉眼扫一遍。6. 把消息链跑顺之后下一步做什么消息链跑通只是起点。真实项目里消息列表会越来越长迟早撞上上下文窗口上限。这时候要做两件事一是消息裁剪保留最近 N 轮加系统提示二是总结压缩把早期对话用模型总结成一条SystemMessage或AIMessage插回去。LangChain 有内置的历史管理工具但核心逻辑还是围绕消息列表做增删。另一个方向是持久化。现在消息列表在内存里进程一重启就没了。生产环境要把消息存进数据库或 Redis按会话 ID 取出来再拼。存的时候注意tool_calls和artifact这些结构化字段要序列化好别只存content。如果你要长期跑编码类 Agent消息链会更复杂涉及多轮工具调用和中间状态。这种场景建议直接上 Coding Plan把模型接入和额度管理交给平台你专注在消息编排逻辑上。接入文档在 https://taotoken.net/doc 里面有各框架的配置示例遇到接入问题先翻这里。最后给个实用技巧调试消息链时写一个print_messages(messages)函数把每条消息的角色、content 前 50 字、tool_calls 数量打出来。比看完整 JSON 快得多一眼就能发现顺序或 id 问题。这个函数我每个项目都留着省下的排查时间够写好几个功能了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑