ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Langchain-Chatchat 对话历史核心模型:深入解析 `History` 类的源码设计与调用链路

Langchain-Chatchat 对话历史核心模型:深入解析 `History` 类的源码设计与调用链路 Langchain-Chatchat 对话历史核心模型深入解析History类的源码设计与调用链路【免费下载链接】Langchain-ChatchatLangchain-Chatchat原Langchain-ChatGLM基于 Langchain 与 ChatGLM, Qwen 与 Llama 等语言模型的 RAG 与 Agent 应用 | Langchain-Chatchat (formerly langchain-ChatGLM), local knowledge based LLM (like ChatGLM, Qwen and Llama) RAG and Agent app with langchain项目地址: https://gitcode.com/GitHub_Trending/la/Langchain-Chatchat本文以 markdown_docs/server/chat/utils.md 为骨架结合仓库源码系统讲解 Langchain-Chatchatchatchat-server中管理多轮对话历史的History数据类——它的字段约束、三种核心转换方法to_msg_tuple/to_msg_template/from_data以及在普通对话、知识库问答、文件对话、Agent 平台工具等场景下的完整调用链路。读完你可以准确理解并复用该类的多形态转换逻辑、避免在接续开发中踩到角色映射与模板转义的坑并在自行扩展 chat 类接口时正确构造history请求体。定位History是贯穿各 chat 接口的历史消息统一载体在 Langchain-Chatchat 服务端几乎所有聊天类接口普通对话、知识库问答、临时知识库文件问答、联网搜索、Agent 工具对话都需要接收并转发“多轮历史消息”。为了在OpenAI 风格字典 / LangChain 消息对象 / Jinja2 消息模板之间自由切换服务端在 libs/chatchat-server/chatchat/server/chat/utils.py 中定义了History类from chatchat.server.pydantic_v2 import BaseModel, Field class History(BaseModel): 对话历史 可从dict生成如 h History(**{role:user,content:你好}) 也可转换为tuple如 h.to_msy_tuple (human, 你好) role: str Field(...) content: str Field(...)继承关系History继承自chatchat.server.pydantic_v2的BaseModelpydantic v2 兼容封装同目录 pydantic_v2.py因此天然具备字段校验、JSON Schema 生成、序列化/反序列化能力——这也让它可以被直接用作 FastAPI 接口的Body参数类型。字段role: str发言角色如user、assistant、ai、human等声明为Field(...)即必填字段content: str发言内容同样必填。注释性方法名提示类内注释中的to_msy_tuple是注释笔误真实方法名为to_msg_tuple对应 utils.py使用时请注意。从源码位置看该类位于 libs/chatchat-server/chatchat/server/chat/utils.py被同目录的chat.py、kb_chat.py、file_chat.py以及langchain_chatchat侧的 Agent 模块广泛引用是各对话链路的公共“历史消息类型”。方法一to_msg_tuple—— 角色归一化到 LangChain 二元组def to_msg_tuple(self): return ai if self.role assistant else human, self.content行为要点若role assistant输出元组第一元素为ai其余所有角色一律归一为human。返回(human | ai, content)二元组这正是 LangChain 早期chat_history/convert_to_messages等 API 期望的 (role, content) 结构参见 langchain_chatchat/agents/platform_tools/base.py其中convert_to_messages(_chat_history)处理该类元组。注意assistant会被映射为ai而user会变成human如果业务方直接传入自定义角色名例如system它同样会落入human分支。因此在历史数据中应保证role取值为受支持枚举否则可能发生语义偏差。典型调用点Agent 执行器构造历史见 libs/chatchat-server/langchain_chatchat/agents/platform_tools/base.py把从数据库读出的历史先History.from_data再to_msg_tuple回调处理器把 LLM 收到的chat_history统一转为 tuple见 libs/chatchat-server/langchain_chatchat/callbacks/agent_callback_handler.py。输出示例对应文档第 46-56 行# roleassistant, content你好我是AI助手。 History(roleassistant, content你好我是AI助手。).to_msg_tuple() # (ai, 你好我是AI助手。) # roleuser, content这是一个用户消息。 History(roleuser, content这是一个用户消息。).to_msg_tuple() # (human, 这是一个用户消息。)方法二to_msg_template—— 生成带 Jinja2 转义的ChatMessagePromptTemplatedef to_msg_template(self, is_rawTrue) - ChatMessagePromptTemplate: role_maps { ai: assistant, human: user, } role role_maps.get(self.role, self.role) if is_raw: # 当前默认历史消息都是没有input_variable的文本。 content {% raw %} self.content {% endraw %} else: content self.content return ChatMessagePromptTemplate.from_template( content, jinja2, rolerole, )行为要点角色再归一化role_maps将ai → assistant、human → user未命中的角色保留原值源码在 utils.py。可以看出它与to_msg_tuple的映射恰好互为逆向前者面向 LangChain prompt 模板需要标准角色名后者面向 LangChain 旧版消息 API。is_raw与 Jinja2 转义当is_rawTrue默认内容会被包裹{% raw %} ... {% endraw %}标签。原因是历史消息往往是纯文本其中可能包含用户输入的大括号/模板片段若不转义langchain 使用 jinja2 解析模板时可能将其误解释为变量或表达式is_raw让历史文本按字面值参与模板渲染。只有当消息本身需要被当作 prompt 模板内含input_variable如知识库系统提示语时才应传is_rawFalse让其真正参与模板编译。返回对象ChatMessagePromptTemplate来自langchain.prompts.chat。随后可被ChatPromptTemplate.from_messages([...])组合成完整对话 prompt。输出示例对应文档第 79-83 行h History(rolehuman, contentHello, AI!) h.to_msg_template(False) # ChatMessagePromptTemplate(contentHello, AI!, template_typejinja2, roleuser)典型调用点文档提到三个 iterator均有真实对应知识库问答 kb_chat.pyprompt_template get_prompt_template(rag, prompt_name) input_msg History(roleuser, contentprompt_template).to_msg_template(False) chat_prompt ChatPromptTemplate.from_messages( [i.to_msg_template() for i in history] [input_msg]) chain chat_prompt | llm这里有两个细节值得注意历史的每条History使用默认is_rawTrue直接进模板知识库的系统指令get_prompt_template(rag, ...)返回的模板字符串内含{context}、{question}等占位变量则用is_rawFalse构造为真正的可编译消息模板二者拼接成一个ChatPromptTemplate最终以{context: context, question: query}作为输入执行见 kb_chat.py。临时文件知识库对话 file_chat.py逻辑相同使用LLMChain(promptchat_prompt, llmmodel)串联无命中文档时自动换用empty提示模板。方法三from_data—— 把异构输入统一成History实例classmethod def from_data(cls, h: Union[List, Tuple, Dict]) - History: if isinstance(h, (list, tuple)) and len(h) 2: h cls(roleh[0], contenth[1]) elif isinstance(h, dict): h cls(**h) return h行为要点支持三种输入list/tuple且长度 ≥ 2取前两个元素作为(role, content)构造多余的第三项及其后元素会被忽略dict通过cls(**h)关键字展开构造因此字典键必须与role/content字段名一致其他形态原样返回此时返回值不是History实例调用方需保证输入合法。长度不足 2 的 list/tuple 不会被转换直接原样返回存在隐患但符合“只处理已知形态”的设计意图。输出示例对应文档第 100-101 行History.from_data([user, 今天天气怎么样]) # History(roleuser, content今天天气怎么样) History.from_data({role: assistant, content: 今天是晴天。}) # History(roleassistant, content今天是晴天。)典型调用点知识库问答入口先把 FastAPI body 里的历史列表整体转换history [History.from_data(h) for h in history]kb_chat.py文件对话同样在开始时转换一次file_chat.pyAgent 平台工具执行器读取已持久化历史时再次转换langchain_chatchat/agents/platform_tools/base.py。从 API 视角理解History如何正确构造多轮对话请求在 kb_chat.py 与 file_chat.py 中history参数被声明为 FastAPIBody(List[History])Swagger 示例直接给出字典形态history: List[History] Body( [], description历史对话, examples[[ {role: user, content: 我们来玩成语接龙我先来生龙活虎}, {role: assistant, content: 虎头虎脑}, ]] ),也就是说调用/chat/kb_chat、/chat/file_chat等接口时history的推荐请求形态是有序的{role, content}字典数组越靠前越早服务端经History.from_data逐条解析后再按上文方法拼接进 prompt。这也正是文档所述“history 通过 History 类的实例来管理和传递确保数据一致性和易用性”的落点。扩展langchain_chatchat侧的同名增强版与from_message仓库中还存在另一处History见 libs/chatchat-server/langchain_chatchat/utils/history.py。它与chatchat.server.chat.utils.History保持了相同的方法签名与转换语义to_msg_tuple、to_msg_template、from_data但在其上扩展了_convert_message_to_dict(message: BaseMessage) - dicthistory.py把 LangChain 各类消息HumanMessage、AIMessage、SystemMessage、FunctionMessage、ToolMessage、ChatMessage及其 Chunk 变体映射成统一字典并保留function_call/tool_calls/name/tool_call_id等附加信息from_message(cls, message: BaseMessage) - Historyhistory.py先转 dict 再走from_data从而让「LangChain BaseMessage → History」也能一行完成。该增强版服务于 Agent 链路的回调处理例如 agent_callback_handler.pyif chat_history in inputs: inputs[chat_history] [ History.from_message(message).to_msg_tuple() for message in inputs[chat_history] ]这种“双实现”设计体现了仓库的分层chatchat.server.chat.utils.History面向 HTTP 接口层与 RAG 对话链路langchain_chatchat.utils.history.History面向 LangChain Agent 集成层两者在转换语义上保持一致使历史消息可以无缝跨层流转。全链路一图梳理一条历史消息是如何流转的综合 chat.py、kb_chat.py、file_chat.py 与 Agent 平台工具实现可以归纳出History在服务端的两条主要流转路径RAG / 文件问答链路非 AgentHTTP 请求体List[{role,content}]→History.from_data批量转换 →i.to_msg_template()历史is_rawTrueHistory(roleuser, contentprompt_template).to_msg_template(False)系统指令模板→ChatPromptTemplate.from_messages→ 交给llm/LLMChain。Agent / 平台工具链路create_models_chains从消息表读取历史并按时间正序组装为{role,content}字典列表见 chat.py→ 传入PlatformToolsRunnable.create_agent_executor(historyhistory, ...)→ 执行时History.from_data(h).to_msg_tuple()再交给convert_to_messagesplatform_tools/base.py→ 最终作为chat_history进入 agent 提示模板。无论哪条链路History都只负责“一种数据、三种视图”面向外部接口的字典视图由 pydantic 原生支持面向 LangChain 消息 API 的(role, content) 元组视图面向提示词模板的ChatMessagePromptTemplate视图。易错点与使用建议角色映射的双向不一致是“特性”而非“缺陷”to_msg_tuple将assistant → ai、其余 →humanto_msg_template将ai → assistant、human → user。如果绕过这两个方法直接用原始role拼 prompt会出现角色名漂移建议所有历史消息统一经由History再进入链。is_raw决定历史文本能否参与模板编译历史中的普通用户文本应保持默认True只有当你确需把该条消息当作“带占位符的模板”时才传False。仓库在拼接 RAG 提示语时正是用这一开关把“可编译的系统指令”与“不可编译的历史文本”区分开kb_chat.py。from_data的边界情况list/tuple 长度必须 ≥ 2dict 的键必须与role/content对齐遇到其他类型会原样返回而不会报错调用链中应避免传入未知形态以免把非History对象带进后续to_msg_*调用。扩展新 chat 接口时的复用姿势直接参考kb_chat的参数声明history: List[History] Body([])进入逻辑后先执行一次[History.from_data(h) for h in history]做规整即可在后续任意位置复用to_msg_tuple/to_msg_template。结语History是 Langchain-Chatchat 服务端处理对话历史的“最小公共类型”两个必填字段、三个转换方法支撑起了从 HTTP 入参、知识库 RAG、文件问答到 Agent 平台工具的全链路多轮对话。理解它就等于掌握了在 chatchat-server 上阅读和扩展一切 chat 相关代码的钥匙。若要进一步查看它在各类对话接口中的完整用法可对照阅读 chat.py、kb_chat.py、file_chat.py 及 markdown_docs/server/chat/chat.mdutils.md的原始出处chatchat.server.chat.utils的源码即文档描述对象详见本仓库同级 api.md 中关于文档生成机制的说明。【免费下载链接】Langchain-ChatchatLangchain-Chatchat原Langchain-ChatGLM基于 Langchain 与 ChatGLM, Qwen 与 Llama 等语言模型的 RAG 与 Agent 应用 | Langchain-Chatchat (formerly langchain-ChatGLM), local knowledge based LLM (like ChatGLM, Qwen and Llama) RAG and Agent app with langchain项目地址: https://gitcode.com/GitHub_Trending/la/Langchain-Chatchat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进