Langchain-Chatchat 中的 ChatGLM3 结构化聊天代理:StructuredGLM3 输出解析与重试机制源码级详解
Langchain-Chatchat 中的 ChatGLM3 结构化聊天代理StructuredGLM3 输出解析与重试机制源码级详解【免费下载链接】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本文系统拆解 Langchain-Chatchat原 Langchain-ChatGLM为 ChatGLM3-6B 定制的一套结构化聊天代理Structured Chat Agent实现涵盖带重试机制的结构化输出解析器、代理的提示词构建与草稿本scratchpad机制以及顶层装配与工具执行流程。读完本文你将掌握 LLM 输出如何被解析为AgentAction/AgentFinish、tool_call 协议如何被截取与转写并能在当前仓库中定位到对应实现与注册入口为接入 ChatGLM3 系列模型做工具调用Agent定制打下基础。关联文档API 描述ChatGLM3Agent.md 记录了结构化输出解析器与结构化代理的完整接口语义当前仓库的实际运行代码将其实现为langchain_chatchat下基于 LangChain 的组件本文按“接口设计 → 方法语义 → 源码对照 → 运行链路”的顺序展开。一、背景与模块定位在 Langchain-Chatchat 中不同大模型对 Agent工具调用的“对话协议”支持程度不同。ChatGLM3-6B 并非原生支持 OpenAI 风格的 function calling因此在 agent 注册中心 中专门提供了glm3这一agent_typeif glm3 agent_type: # An optimized method of langchain Agent that uses the glm3 series model template get_prompt_template_dict(action_model, agent_type) prompt create_prompt_glm3_template(agent_type, templatetemplate) agent create_structured_glm3_chat_agent(llmllm, toolstools, promptprompt, ...) agent_executor PlatformToolsAgentExecutor(agentagent, toolstools, ...)不支持的 agent 类型会抛出ValueError可选集合中包含glm3见 agents_registry.py。整个 ChatGLM3 Agent 方案由三层构成输出解析层把模型输出的自由文本规范化成Action:\njson {...} \n结构再交给标准StructuredChatOutputParser解析为动作或最终答案代理装配层负责提示模板、工具清单 JSON 化、草稿本与停止符的组装执行层由PlatformToolsAgentExecutor驱动返回return_intermediate_stepsTrue的中间步骤供上层展示。二、带重试机制的结构化输出解析器2.1 类职责与属性关联文档中的StructuredChatOutputParserWithRetries继承自 LangChain 的AgentOutputParser其职责是“为结构化聊天代理提供带有重试机制的输出解析”。它持有两个关键属性属性类型说明base_parserStructuredChatOutputParser实例基础输出解析器负责按标准结构化协议解析output_fixing_parserOutputFixingParser可选输出修正解析器在基础解析失败或需要修正时做二次解析对应地当前仓库的实现位于 glm3_output_parsers.py类名为StructuredGLM3ChatOutputParser。该文件头部注释与关联文档同源本文件是 LangChain 官方glm3_agent.py针对 ChatGLM3-6B 的修改版。其字段定义体现了同样的“基础解析 修正兜底”设计class StructuredGLM3ChatOutputParser(AgentOutputParser): Output parser with retries for the structured chat agent. base_parser: AgentOutputParser Field(default_factoryStructuredChatOutputParser)2.2 parse() 的核心流程parse(text)是整个解析器的核心方法接收模型输出的字符串text返回AgentAction继续执行工具或AgentFinish结束并给出答案。其算法可分三步第一步定位特殊标记并截断文本。文档定义了一个special_tokens列表如Action:、|observation|找到其中在文本中最先出现的位置将文本截断到该位置之前丢弃多余噪声避免把模型在停止符之后“续写”的内容误解析进动作。第二步判定是动作调用还是最终答案。若文本中包含tool_call说明模型请求调用工具。解析器定位动作描述结束位置即 代码围栏的位置从中提取动作名称与参数参数被解析为键值对字典后整体组织成 JSON 动作对象。若文本中不包含tool_call则把整段文本视为最终答案action固定为Final Answeraction_input为原始文本。第三步二次解析。若配置了output_fixing_parser则使用它完成最终解析否则使用base_parser。任何异常都会被包装为OutputParserException抛出异常信息中携带无法解析的原始文本便于排障。文档给出的解析结果示例{ action: tool_call_example, action_input: { param1: value1, param2: value2 } }{ action: Final Answer, action_input: 这是一个最终答案的示例文本。 }2.3 源码级实现对照实际源码中ChatGLM3 模型的工具调用被约束为一种可被正则捕获的代码块形式——工具名后紧跟一段python tool_call(...) 围栏这正是 ChatGLM3-6B 官方微调格式的落地。见 glm3_output_parsers.pydef parse(self, text: str) - Union[AgentAction, AgentFinish]: exec_code None if s : re.search(r(\S\spython\stool_call\(.*?\)\s), text, re.DOTALL): exec_code s[0] if exec_code: action str(exec_code.split(python)[0]).replace(\n, ).strip() code_str str( exec_code.split(python)[1]).strip() _, params try_parse_json_object(code_str) action_json {action: action, action_input: params} else: action_json {action: Final Answer, action_input: text} action_str f Action:{json.dumps(action_json, ensure_asciiFalse)}try: parsed_obj self.base_parser.parse(action_str) return parsed_obj except Exception as e: raise OutputParserException(fCould not parse LLM output: {text}) from e对应到文档语义可以明确三点实现事实“截断”在实现层面体现为正则的精确匹配\S\spython\stool_call\(.*?\)\s使用非贪婪.*?只截取首个完整tool_call代码块围栏之外的前缀被split(python)[0]剥离作为动作名参数解析复用 try_parse_json_object.py 中的 JSON 容错解析即使模型输出的 JSON 里夹杂了说明文字也能尽力提取键值对“二次解析”的输入是重构后的标准协议字符串Action:\n {json} \n再由 LangChain 原生的StructuredChatOutputParser消费——这也是为什么它既能“兜底”又不破坏 LangChain 生态的数据契约。2.4 类型标识 _type()_type()返回解析器类型的硬编码标识。文档化的实现返回structured_chat_ChatGLM3_6b_with_retries当前源码中_type被实现为属性并返回StructuredGLM3ChatOutputParser见 glm3_output_parsers.py。这一标识用于区分解析器/代理处理逻辑与序列化配置由于是硬编码若需变更类型标识必须同步检查所有依赖该字符串的注册与路由逻辑避免新旧类型失配。三、结构化聊天代理 StructuredGLM3ChatAgent3.1 类职责与属性StructuredGLM3ChatAgent继承 LangChain 的Agent类是“基于 ChatGLM3-6B 的结构化聊天代理”负责与 LLM 交互、生成提示并解析输出。其核心属性如下属性默认值说明output_parserStructuredChatOutputParserWithRetries实例代理输出解析器即上文的带重试解析器observation_prefixObservation:观察结果工具返回前追加的前缀llm_prefixThought:语言模型调用思考过程前追加的前缀observation_prefix与llm_prefix是只读属性方法无参数分别固定返回字符串。这两个前缀的作用是在 ReAct 风格的消息循环里区分“模型的思考”与“工具返回的观察”保证把工具结果正确地回填到下一轮提示中观察统一以Observation:开头从而维持提示格式的一致性和可解析性。3.2 草稿本构建 _construct_scratchpad(intermediate_steps)_construct_scratchpad接收intermediate_steps——一个由(AgentAction, 工具输出字符串)元组组成的列表表示代理到目前为止执行过的中间步骤。实现逻辑为先调用父类Agent._construct_scratchpad生成草稿字符串agent_scratchpad校验返回值为str否则抛出ValueError若非空追加一段对模型友好的引导语把“历史工作”包装成上下文This was your previous work (but I havent seen any of it! I only see what you return as final answer): Step 1: Do something; Step 2: Do something else;若为空则返回空字符串。这段引导语的用意在于结构化代理每轮只能“看到”最终答案里携带的信息草稿本是让模型知晓自己已做工作的唯一通道因此文档强调它“以友好的方式向用户展示之前的工作成果”。3.3 默认解析器 _get_default_output_parser(cls, llm)类方法_get_default_output_parser(cls, llmNone, **kwargs)创建并返回StructuredChatOutputParserWithRetries(llmllm)实例。llm为可选的BaseLanguageModel**kwargs预留扩展但当前未直接使用。该方法由from_llm_and_tools在未显式传入output_parser时调用属于“默认装配”的一部分。3.4 停止符 _stop()_stop()返回[|observation|]。该标记与输出解析形成闭环一旦模型输出中出现|observation|即意味着本轮对话到达工具观察边界LLM 应停止生成把控制权交还给代理以注入真实工具结果。这一设计在当前 LCEL 版装配函数create_structured_glm3_chat_agent中同样可见其参数stop_sequence默认True此时内部执行llm.bind(stop[|observation|])如果所接模型不支持 stop 序列可传False或自定义列表见 glm3_agent.py。3.5 提示词构建 create_prompt(cls, tools, prompt, input_variables, memory_prompts)create_prompt根据工具清单与模板参数构造聊天提示模板参数语义如下参数类型说明tools实现BaseTool接口的对象序列代理可调用的工具promptstr最终提示字符串模板input_variablesList[str]输入变量名默认[input, agent_scratchpad]memory_promptsList[BasePromptTemplate]追加到模板中的记忆/历史提示默认为None处理流程遍历tools逐一提取name、description与参数 schema整理为简化 JSON含name、description、parameters三字段随后把工具信息、工具名列表、历史、输入与草稿板一起填充进模板最后包装成ChatPromptTemplate返回。文档给出的模板填充效果示意Available tools: Calculator: A simple calculator, args: {number1: Number, number2: Number} Translator: Translates text from one language to another, args: {text: String, target_language: String} Input: {input}源码级补充当前仓库把“工具 → 简化 JSON”这一步独立为render_glm3_json(tools)位于 glm3_agent.py。它的关键处理包括用model_schema(tool.args_schema)取工具参数 JSON Schema 并剔除title字段以压缩长度若tool.description含 - 分隔符则只保留分隔后的后半段作为送入模型的描述。每行输出一个带缩进的 JSON{name: tool.name, description: description, parameters: parameters}提示模板本身则由 create_prompt_glm3_template 生成其结构为三段式SystemMessage注入tools、可选的chat_history占位、HumanMessage注入agent_scratchpad与input并声明了input_variables[input, agent_scratchpad]与丰富的chat_history输入类型与文档描述的默认变量完全一致。3.6 工厂方法 from_llm_and_tools(cls, llm, tools, prompt, ...)from_llm_and_tools是构造代理实例的类方法参数包括llmBaseLanguageModel、tools、prompt、callback_managerBaseCallbackManager、output_parserAgentOutputParser默认None、human_message_template默认HUMAN_MESSAGE_TEMPLATE、input_variables、memory_prompts以及**kwargs。执行顺序为校验tools集合合法性调用create_prompt生成ChatPromptTemplate以llm prompt callback_manager构建LLMChain收集工具名称列表若未显式提供output_parser则调用_get_default_output_parser获取带重试的默认解析器组装并返回StructuredGLM3ChatAgent实例。值得注意的是文档描述的from_llm_and_tools属于类继承式class-basedAgent 的实现而当前仓库中glm3分支实际使用的create_structured_glm3_chat_agentglm3_agent.py已演进为LCEL Runnable 链RunnablePassthrough.assign(agent_scratchpad...) | prompt | llm_with_stop | PlatformToolsAgentOutputParser(instance_typeglm3)。两者的设计意图一脉相承——校验变量、注入工具、绑定停止符、用专用解析器收尾——区别仅在 LangChain 新旧两种 API 形态Agent类 vsRunnable。3.7 _agent_type() 的“强制子类实现”语义_agent_type在基类中不做任何返回而是直接抛出ValueError。这是典型的模板方法约束设计上强制要求子类重写该方法以返回具体代理类型标识。若在实际开发中触发该异常应检查是否误用了未被子类化/重写的中间类。四、顶层装配函数 initialize_glm3_agent文档提供了供上层业务直接调用的装配入口initialize_glm3_agent其完整参数表如下参数类型默认值说明tools实现BaseTool接口的对象序列必填工具集合llmBaseLanguageModel必填基础语言模型promptstrNone自定义提示可按需覆盖默认模板memoryConversationBufferWindowMemoryNone聊天历史窗口记忆用于多轮连贯对话agent_kwargsDictNone传给from_llm_and_tools的额外参数传None时被初始化为{}tags字符串序列None代理/执行器的标记或分类传入后被转为list**kwargs--透传给执行器构建的额外配置执行逻辑分三步先处理tags转列表与agent_kwargs空字典兜底再通过StructuredGLM3ChatAgent.from_llm_and_tools构造结构化代理最后用AgentExecutor.from_agent_and_tools(agent, tools, memory..., tags...)包一层执行器返回。文档给出的返回结构示意AgentExecutor( agentStructuredGLM3ChatAgent(...), tools[...], memoryConversationBufferWindowMemory(...), tags[example_tag] )源码级补充当前仓库对应位置的执行器为PlatformToolsAgentExecutor并显式开启return_intermediate_stepsTrueagents_registry.py这是为了让上层 UI/回调能拿到每一步(AgentAction, observation)用于逐步渲染 Agent 的思考—调用—观察过程。五、一次工具调用的完整生命周期综合文档设计与当前实现ChatGLM3 结构化代理的一次工具调用可串成如下链路发起用户输入input代理执行器启动提示组装create_prompt_glm3_template生成模板render_glm3_json把tools序列化为 JSON 文本注入 SystemMessage_construct_scratchpad/format_to_platform_tool_messages把历史intermediate_steps灌入agent_scratchpad模型调用LLM 绑定停止符[|observation|]后生成回复回复按协议输出工具名\npython tool_call({参数: ...})或直接输出最终答案文本解析转写parse先用正则捕获tool_call代码块命中则提取动作名 JSON 参数否则视为Final Answer随后把结果转写为Action:\njson {...}\n标准结构交给StructuredChatOutputParser产出AgentAction或AgentFinish见 platform_tools.py 中instance_type glm3分派执行与回填执行器调用对应工具得到观察结果以Observation:前缀回填进入下一轮循环收束当解析结果为AgentFinish时action_input即最终答案链路结束。六、使用注意事项与常见坑结合文档“注意”部分与源码实现可提炼出以下工程要点保持输出格式契约解析器对输入文本格式敏感尤其是tool_call代码块、Action:标记与围栏的位置关系。提示词与模型的约定格式必须一致否则会走OutputParserException分支异常信息包含原始文本可直接用于 Prompt 调优。output_fixing_parser兜底若配置了修正解析器基础解析失败后会触发二次解析能显著提升容错率未配置时则直接失败。停止符与模型的兼容性stop_sequence默认会绑定[|observation|]若目标 LLM 不支持 stop 序列应显式关闭或自定义避免生成行为异常。_type/_agent_type为硬编码与强制重写点前者改动需同步检查依赖标识的注册逻辑后者是子类必须重写的模板方法。记忆与草稿的协同memory_prompts负责注入历史对话agent_scratchpad负责注入本轮已执行步骤两者职责不同勿相互替代模板变量input与agent_scratchpad缺一不可create_structured_glm3_chat_agent会先校验 prompt 是否包含这两个必填变量缺失直接抛ValueError。七、可继续深入的文件索引API 文档文档描述见 ChatGLM3Agent.md模型加载与 Agent 相关背景见 model_contain.md 与 agent_chat.md输出解析器实现glm3_output_parsers.py类StructuredGLM3ChatOutputParser分派器 platform_tools.py代理装配实现glm3_agent.pyrender_glm3_json与create_structured_glm3_chat_agent提示模板create_prompt_template.pycreate_prompt_glm3_templateAgent 类型注册与执行器agents_registry.py容错 JSON 解析工具try_parse_json_object.py对 ChatGLM3 系列模型而言这套“专用正则捕获 标准协议转写 基础解析器兜底”的结构化方案是 Langchain-Chatchat 在不依赖原生 function calling 的前提下实现稳定工具调用的关键一环也为接入其他“非工具原生”模型提供了可复制的改造范式。【免费下载链接】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),仅供参考