十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

LangChain Agent实战:从create_agent到结构化输出与性能优化

LangChain Agent实战:从create_agent到结构化输出与性能优化 1. 从“工具调用”到“自主决策”Agent 的本质是什么如果你用过 LangChain 的LLMChain或者RunnableSequence可能会觉得它们像是一个“听话的流水线”你给一个输入它按部就班地调用工具或模型然后给你一个输出。整个过程是线性的、确定的。但当你开始接触Agent时感觉就完全不一样了。它不再是那个被动的执行者而更像是一个拥有“思考回路”的自主决策者。它会根据你的指令自己决定下一步该做什么、用什么工具、什么时候该停下来。这种从“执行”到“决策”的转变正是 Agent 技术的核心魅力也是当前 AI 应用从“玩具”走向“生产力工具”的关键一步。简单来说一个 LangChain Agent 就是一个由大语言模型驱动的自主实体。它被赋予了一个目标比如“帮我查一下今天北京的天气然后根据天气推荐穿什么衣服”以及一套可以使用的工具比如“网络搜索”、“计算器”、“代码执行器”。Agent 的核心工作流程是一个经典的“感知-思考-行动”循环它接收用户的输入感知用大语言模型分析当前状态和目标思考决定调用哪个工具或直接给出答案行动然后根据工具返回的结果再次“思考”循环往复直到任务完成或认为无法继续。这听起来很酷但真正用起来新手往往会遇到几个典型问题Agent 动不动就陷入死循环在一个步骤里打转输出的结果格式乱七八糟难以被下游程序处理或者你明明想监控它的思考过程却只能看到一个最终结果中间发生了什么一概不知。这些痛点恰恰是 LangChain 1.x 中围绕create_agent、中间件、结构化与流式输出这些特性所要解决的核心问题。接下来我们就抛开概念直接进入实战看看如何用这些特性构建一个既强大又好用的智能体。2. 构建智能体的基石深入理解create_agent与执行器在 LangChain 的早期版本中构建一个 Agent 可能需要你手动组装AgentExecutor、定义复杂的Agent类过程比较繁琐。create_agent函数是 LangChain 1.x 提供的一个更高级、更声明式的 API它旨在简化智能体的创建过程让你能更专注于定义“做什么”而不是“怎么做”。2.1create_agent的核心参数与工作流create_agent函数的核心是三个部分大语言模型LLM、工具集Tools和提示词Prompt。我们来看一个最基础的创建示例from langchain_openai import ChatOpenAI from langchain.agents import create_react_agent, AgentExecutor from langchain.tools import Tool from langchain import hub # 1. 准备大模型 llm ChatOpenAI(modelgpt-4o, temperature0) # 2. 定义工具 def search_wikipedia(query: str) - str: # 这里简化实际应调用 Wikipedia API return f根据查询 {query}模拟返回的百科摘要信息... search_tool Tool( nameWikipediaSearch, funcsearch_wikipedia, description用于搜索维基百科获取事实信息。输入应为明确的搜索查询词。 ) def calculator(expression: str) - str: try: # 安全警告生产环境务必使用更安全的评估方式如 ast.literal_eval 或专用库 result eval(expression) return str(result) except: return 计算错误表达式无效。 calc_tool Tool( nameCalculator, funccalculator, description用于执行数学计算。输入应为有效的数学表达式如 3 5 * 2。 ) tools [search_tool, calc_tool] # 3. 获取预定义的提示词模板从 LangChain Hub prompt hub.pull(hwchase17/react) # 4. 创建智能体 agent create_react_agent(llm, tools, prompt) # 5. 创建执行器 agent_executor AgentExecutor(agentagent, toolstools, verboseTrue, handle_parsing_errorsTrue) # 6. 运行智能体 result agent_executor.invoke({input: 爱因斯坦的出生年份加上100等于多少}) print(result[output])这段代码清晰地展示了流程。但create_react_agent内部做了什么它实际上帮你封装了ReAct框架的逻辑。ReActReasoning Acting是一种让 LLM 将思考Reasoning和行动Acting结合起来的范式。在提示词中模型会被要求以Thought:、Action:、Observation:的格式进行输出。create_react_agent生成的智能体就内置了解析这种格式、调用对应工具、并将工具返回结果作为新Observation喂给下一轮思考的能力。注意create_react_agent是create_agent的一种具体实现针对 ReAct 框架。LangChain 也支持其他类型的智能体如 OpenAI Functions Agent、Conversational Agent 等它们有各自的创建函数或方式。2.2AgentExecutor掌控循环的“导演”AgentExecutor是真正驱动智能体运行的核心引擎。你可以把它想象成电影导演而agent对象是主角LLM。导演负责喊“卡”和“开始”控制着整个拍摄执行流程。它的几个关键参数直接决定了智能体的行为和稳定性max_iterations与max_execution_time这是防止智能体“鬼打墙”的最重要保险丝。max_iterations限制最大循环次数默认通常为15max_execution_time限制最大执行时间。一旦超过执行器会强制终止并返回当前结果或错误。对于复杂任务你可能需要调高max_iterations对于简单任务调低它可以节省成本和时间。handle_parsing_errors当 LLM 的输出无法被解析为有效的工具调用指令时比如格式错误、调用了不存在的工具名这个参数决定如何处理。设为True时执行器会将错误信息作为Observation反馈给 LLM让它有机会纠正自己。设为False则会直接抛出异常。在开发调试阶段强烈建议设为True。early_stopping_method决定何时提前停止。常用的是“force”达到迭代上限时强制停止和“generate”让 LLM 自己判断是否该最终输出答案了。后者更智能但可能增加开销。verbose设为True时会在控制台打印出完整的Thought/Action/Observation链这是调试和理解智能体思考过程的必备利器。一个常见的误区是只关注create_agent而忽略了AgentExecutor的配置。实际上一个智能体是否“好用”很大程度上取决于执行器的参数调优。例如一个需要多步检索和分析的任务如果max_iterations设得太低任务可能半途而废。3. 为智能体注入“可观测性”中间件Middleware实战当你把verboseTrue打开在控制台看到刷屏的Thought和Action时可能会想这些信息如果能被程序捕获、分析、甚至持久化到数据库该多好或者我想在每次调用工具前都做个日志记录每次LLM思考后都计算一下 token 消耗。这就是中间件Middleware的用武之地。在 LangChain 1.x 的上下文中中间件允许你在智能体执行的生命周期中的特定节点插入自定义逻辑。它就像是给执行流程加装的“监听器”或“过滤器”。3.1 理解执行流程与钩子点要使用中间件首先要理解AgentExecutor的执行流程。一个简化的 ReAct 循环如下接收输入获得用户问题。LLM 思考模型基于当前上下文历史问题生成Thought和Action。解析动作执行器解析出要调用的工具名和输入参数。执行工具调用对应的工具函数并获得结果Observation。更新上下文将Observation加入到历史上下文中。判断循环判断是否满足停止条件找到答案、达到上限等。若不满足回到第2步。生成最终输出满足停止条件后让 LLM 或直接返回最终结果。中间件可以在这些步骤之间挂载。LangChain 提供了BaseCallbackHandler作为基础的中间件接口但更灵活的方式是直接为AgentExecutor或底层组件添加钩子。不过在 1.x 中一个更直接的方式是利用Runnable的配置configurable和with_config方法或者为工具调用添加装饰器。这里我分享一个更实用、更“接地气”的方法包装工具函数本身。这虽然不是标准的“中间件”模式但能达到同样的效果且理解起来更直观。3.2 实战为工具调用添加日志与监控假设我们想记录每个工具被调用的时间、参数、结果以及耗时。import time import logging from functools import wraps from typing import Any, Callable # 设置日志 logging.basicConfig(levellogging.INFO) logger logging.getLogger(__name__) def tool_logging_middleware(func: Callable) - Callable: 一个简单的工具日志中间件装饰器 wraps(func) def wrapper(*args, **kwargs): tool_name func.__name__ start_time time.time() logger.info(f[Tool Middleware] 开始调用工具 {tool_name}参数: args{args}, kwargs{kwargs}) try: result func(*args, **kwargs) end_time time.time() duration end_time - start_time # 对过长结果进行截断 result_preview str(result)[:200] ... if len(str(result)) 200 else str(result) logger.info(f[Tool Middleware] 工具 {tool_name} 调用成功耗时: {duration:.2f}秒结果预览: {result_preview}) return result except Exception as e: end_time time.time() duration end_time - start_time logger.error(f[Tool Middleware] 工具 {tool_name} 调用失败耗时: {duration:.2f}秒错误: {e}, exc_infoTrue) raise e return wrapper # 使用装饰器包装我们之前的工具 tool_logging_middleware def logged_search_wikipedia(query: str) - str: # 模拟网络延迟 time.sleep(0.5) return f根据查询 {query}模拟返回的百科摘要信息阿尔伯特·爱因斯坦出生于1879年3月14日。 tool_logging_middleware def logged_calculator(expression: str) - str: try: result eval(expression) return str(result) except Exception as e: return f计算错误{e} # 重新定义工具 tools_logged [ Tool(nameWikipediaSearch, funclogged_search_wikipedia, description搜索维基百科。), Tool(nameCalculator, funclogged_calculator, description执行数学计算。), ] # 创建并使用带有“中间件”工具的智能体 agent_logged create_react_agent(llm, tools_logged, prompt) executor_logged AgentExecutor(agentagent_logged, toolstools_logged, verboseFalse) # 关闭verbose用我们的日志 print( 执行带日志的智能体 ) result executor_logged.invoke({input: 爱因斯坦的出生年份加上100等于多少}) print(f最终答案: {result[output]})运行这段代码你不仅能在控制台看到智能体的最终输出还能在日志中清晰看到INFO:__main__:[Tool Middleware] 开始调用工具 logged_search_wikipedia参数: args(爱因斯坦的出生年份,), kwargs{} INFO:__main__:[Tool Middleware] 工具 logged_search_wikipedia 调用成功耗时: 0.50秒结果预览: 根据查询 爱因斯坦的出生年份模拟返回的百科摘要信息阿尔伯特·爱因斯坦出生于1879年3月14日。... INFO:__main__:[Tool Middleware] 开始调用工具 logged_calculator参数: args(1879 100,), kwargs{} INFO:__main__:[Tool Middleware] 工具 logged_calculator 调用成功耗时: 0.00秒结果预览: 1979这样一来我们就实现了对工具层的监控。你可以将这个装饰器逻辑扩展集成到监控系统如 Prometheus、数据库记录每次调用或进行权限校验、输入清洗等这就是中间件思想的落地。4. 从混乱文本到规整数据驾驭结构化输出Structured Output智能体默认的输出是文本字符串。这对于直接给人看没问题但如果你想将智能体的回答作为输入自动传递给下一个系统比如存入数据库、触发另一个API、生成图表文本格式就非常麻烦了。你需要写复杂的正则表达式或解析逻辑去提取信息既脆弱又容易出错。结构化输出Structured Output就是为了解决这个问题。它让 LLM 按照你预先定义好的格式如 Pydantic 模型、JSON Schema来输出数据。在 LangChain 中这通常通过with_structured_output方法来实现。4.1 为智能体定义输出模型假设我们想让智能体在回答关于人物的问题时不仅给出文本答案还能结构化地返回人物的姓名、出生年份和一项主要成就。from pydantic import BaseModel, Field from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI # 1. 用 Pydantic 定义我们期望的输出结构 class PersonInfo(BaseModel): 关于一个人的结构化信息 name: str Field(description人物的全名) birth_year: int Field(description人物的出生年份) key_achievement: str Field(description一项主要成就或贡献) summary: str Field(description基于查询的简要文本总结) # 2. 创建一个支持结构化输出的 LLM Chain注意并非所有模型都完美支持GPT-4系列通常较好 structured_llm ChatOpenAI(modelgpt-4o, temperature0).with_structured_output(PersonInfo) # 3. 创建提示词 structured_prompt ChatPromptTemplate.from_messages([ (system, 你是一个信息提取助手。请根据用户的问题从提供的上下文中提取信息并严格按照要求的结构输出。), (human, 用户问题{question}\n\n相关上下文{context}) ]) # 4. 创建链 extraction_chain structured_prompt | structured_llm # 5. 运行 context 阿尔伯特·爱因斯坦Albert Einstein出生于1879年3月14日是德裔理论物理学家。他最为人所知的是提出了相对论包括狭义相对论和广义相对论并因此获得了1921年的诺贝尔物理学奖。他的质能方程 Emc² 被誉为世界上最著名的方程之一。 question 告诉我关于爱因斯坦的基本信息。 result extraction_chain.invoke({question: question, context: context}) print(type(result)) # 输出class __main__.PersonInfo print(result) # 输出类似 # name阿尔伯特·爱因斯坦 birth_year1879 key_achievement提出了相对论包括狭义相对论和广义相对论 summary阿尔伯特·爱因斯坦出生于1879年是德裔理论物理学家他最著名的成就是提出了相对论。现在result是一个PersonInfo对象你可以直接通过result.name、result.birth_year来访问属性完美适配后续的程序化处理。4.2 将结构化输出与智能体结合那么如何让一个使用了工具的智能体也输出结构化内容呢一个常见的模式是“两阶段法”阶段一智能体执行。让智能体像往常一样运行使用工具收集信息和推理最终生成一个丰富的文本摘要。阶段二结构化提取。将第一阶段生成的文本摘要作为上下文喂给另一个支持结构化输出的 LLM Chain就像上面的extraction_chain提取出最终的结构化数据。# 假设我们有一个已经能完成复杂任务的智能体 executor def get_agent_summary(question: str) - str: 使用智能体获取问题的文本摘要答案 result executor_logged.invoke({input: question}) return result[output] # 用户复杂问题 complex_question 请比较爱因斯坦和牛顿的主要贡献并总结他们的出生年份。 # 阶段一智能体获取文本摘要 text_summary get_agent_summary(complex_question) print( 智能体生成的文本摘要 ) print(text_summary) # 阶段二定义新的输出模型用于提取比较信息 class ScientistComparison(BaseModel): einstein_birth_year: int Field(description爱因斯坦的出生年份) newton_birth_year: int Field(description牛顿的出生年份) einstein_contribution: str Field(description爱因斯坦的主要贡献) newton_contribution: str Field(description牛顿的主要贡献) comparative_summary: str Field(description两者的比较总结) comparison_llm ChatOpenAI(modelgpt-4o, temperature0).with_structured_output(ScientistComparison) comparison_prompt ChatPromptTemplate.from_template( 请从以下文本中提取关于爱因斯坦和牛顿的结构化信息\n\n{summary} ) comparison_chain comparison_prompt | comparison_llm # 从摘要中提取结构化信息 structured_comparison comparison_chain.invoke({summary: text_summary}) print(\n 提取的结构化比较信息 ) print(f爱因斯坦出生年: {structured_comparison.einstein_birth_year}) print(f牛顿出生年: {structured_comparison.newton_birth_year}) print(f爱因斯坦贡献: {structured_comparison.einstein_contribution}) print(f牛顿贡献: {structured_comparison.newton_contribution}) print(f比较总结: {structured_comparison.comparative_summary})这种方法结合了智能体的强大推理、工具调用能力和结构化输出的规整性是构建生产级 AI 应用的常见模式。5. 告别“黑盒”等待实现流式输出Streaming当你向一个复杂的智能体提出一个需要多步工具调用的问题时如果等待十几秒后屏幕上才突然蹦出所有结果体验会很差。用户不知道程序是卡住了还是在工作。流式输出Streaming就是为了改善这种体验它允许你将智能体的思考过程Thought、行动Action、观察Observation以及最终答案像打字一样实时地、一块一块地返回给前端。在 LangChain 中流式输出主要依赖于Runnable接口的.stream()或.astream()异步方法。对于AgentExecutor我们需要确保其底层组件支持流式。5.1 让智能体的“思考过程”流式输出AgentExecutor本身可能不直接暴露最细粒度的流如每个Thought的 token但它可以流式返回每个步骤的完整输出块。更常见且实用的做法是我们流式获取智能体的最终文本答案。同时结合前面提到的中间件或回调我们可以将中间步骤也实时推送出去。首先我们创建一个支持流式输出的执行器。关键是要使用支持流式的 LLM如ChatOpenAI默认支持并且以流式方式调用。from langchain.agents import AgentExecutor, create_react_agent from langchain_core.runnables import RunnableConfig # 使用支持流式的LLM streaming_llm ChatOpenAI(modelgpt-4o, temperature0, streamingTrue) # 创建智能体和执行器工具沿用之前的 agent_stream create_react_agent(streaming_llm, tools_logged, prompt) executor_stream AgentExecutor(agentagent_stream, toolstools_logged, verboseFalse, handle_parsing_errorsTrue) # 定义异步流式调用函数 import asyncio async def stream_agent_response(question: str): 异步流式获取智能体的最终输出 print(f提问: {question}) print(回答流式: , end, flushTrue) full_answer async for chunk in executor_stream.astream({input: question}): # 注意AgentExecutor的流式输出可能包含多个键我们通常关心 output if output in chunk: content chunk[output] print(content, end, flushTrue) full_answer content # 你也可以处理中间步骤例如 chunk 中可能包含 intermediate_steps # elif intermediate_steps in chunk: # print(f\n[中间步骤更新]: {chunk[intermediate_steps]}) print() # 换行 return full_answer # 运行异步函数 async def main(): answer await stream_agent_response(珠穆朗玛峰的高度是多少米) print(f\n完整答案: {answer}) # 在 Jupyter 或异步环境中直接 await在脚本中 # asyncio.run(main())上面的astream会逐步返回包含output等字段的字典。但请注意对于复杂的多步 Agentoutput字段可能只在最后一步才出现。如果你希望看到更细粒度的Thought/Action/Observation流需要配置自定义的回调处理器AsyncCallbackHandler这是一个更进阶的话题。不过对于大多数需要“最终答案”流式输出的场景上述方法已经足够。5.2 实战构建一个简单的实时问答终端结合流式输出和中间件日志我们可以构建一个更有趣的演示import sys import asyncio from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler class ThoughtStreamingHandler(StreamingStdOutCallbackHandler): 一个简单的回调处理器尝试捕获并流式打印Thought等内容 def on_llm_new_token(self, token: str, **kwargs) - None: # 这个回调是针对LLM生成每个token的但无法直接区分是Thought还是最终输出。 # 更精细的控制需要解析LLM的原始输出这里做简单演示。 sys.stdout.write(token) sys.stdout.flush() # 创建一个带有流式回调的LLM llm_with_stream ChatOpenAI( modelgpt-4o, temperature0, streamingTrue, callbacks[ThoughtStreamingHandler()] # 添加回调 ) # 注意将回调添加到LLM而不是AgentExecutor。这样只能流式LLM的思考文本。 agent_with_thought_stream create_react_agent(llm_with_stream, tools_logged, prompt) executor_with_thought_stream AgentExecutor(agentagent_with_thought_stream, toolstools_logged, verboseFalse) print( 智能体思考过程流式演示LLM Token级) # 注意由于AgentExecutor的工作方式我们这里用invoke但LLM内部的生成会通过回调流式出来。 # 这并不能完美流式化整个Agent步骤但展示了可能性。 result executor_with_thought_stream.invoke({input: 计算圆周率π的前5位小数。}) print(f\n最终输出: {result[output]})重要提示让AgentExecutor完美地流式输出每一步的Thought、Action、Observation是一个复杂任务因为执行器需要协调LLM生成、工具调用、解析等多个环节。社区中常见的做法是使用LangGraph另一个库用于构建有状态的、多环节的AI工作流来更精细地控制流程和流式。对于 LangChain 1.x 的AgentExecutor获取最终答案的流式是稳定的但获取中间步骤的流式通常需要自定义CallbackHandler并深入理解其内部事件。6. 避坑指南与性能优化实战在实际项目中集成 Agent你会遇到各种预料之外的问题。下面是我从多个项目中总结出的常见“坑”及其解决方案。6.1 坑一智能体陷入死循环或无效动作现象智能体反复调用同一个工具或者不断生成无意义的Thought无法到达最终答案。根因分析工具描述不清晰LLM 不理解工具的具体功能或输入格式。提示词Prompt不佳没有明确指示停止条件或者思考框架如 ReAct的指令不够强。max_iterations设置不当设得过高放任了循环设得过低在复杂任务中提前终止。工具返回结果质量差如果工具总是返回“未找到”或错误信息LLM 可能不知道如何继续。解决方案优化工具描述描述要具体、包含示例。例如不要写“搜索网络”而是写“使用此工具搜索互联网获取最新信息。输入应为明确的关键词如‘2024年奥运会举办城市’。”强化提示词在系统提示中明确加入“如果你已经获得了足够的信息来回答问题请直接给出最终答案不要再使用工具。”或“在最多进行X步推理后你必须给出最终答案。”配置执行器参数合理设置max_iterations例如 5-10 步用于简单QA15-20 步用于复杂任务。同时可以启用early_stopping_methodgenerate让 LLM 自己判断是否应该停止。完善工具功能确保工具能处理边缘情况并返回对 LLM 友好的信息。例如搜索工具在无结果时应返回“未找到相关信息请尝试其他关键词。”而不是空字符串或错误堆栈。6.2 坑二解析错误Parsing Error现象控制台出现OutputParserException提示无法将 LLM 的输出解析为有效的动作。根因分析LLM 没有严格按照Action:后接JSON或指定格式输出。可能是模型能力问题也可能是提示词中格式指令不够突出。解决方案设置handle_parsing_errorsTrue这是第一道防线让执行器将错误反馈给 LLM 自我纠正。使用更强大的模型GPT-4 系列在遵循格式指令上通常比 GPT-3.5 好很多。在提示词中强化格式使用 json 代码块包裹示例或使用非常醒目的标记。尝试不同的 Agent 类型OpenAIFunctionsAgent或StructuredChatAgent可能比ReAct对格式错误更鲁棒因为它们依赖于函数调用Function Calling这种更结构化的协议。6.3 坑三Token 消耗与响应速度慢现象任务简单但耗时很长API 调用费用高昂。根因分析上下文Context膨胀每次循环所有历史Thought、Action、Observation都会作为上下文再次发送给 LLM导致 token 数快速增长。工具响应慢如果工具依赖外部 API如网络请求、数据库查询其延迟会直接加到每次循环中。不必要的复杂推理LLM 可能进行了过于详细的推理。优化策略上下文压缩与摘要这是最有效的优化手段。不要将完整的原始历史全部传递。可以使用ConversationSummaryBufferMemory或自定义中间件在每次循环后将冗长的历史对话摘要成一段简洁的文字再放入下一轮的上下文。LangChain 提供了ConversationSummaryBufferMemory但将其无缝集成到 Agent 循环中需要一些技巧通常需要自定义AgentExecutor或使用LangGraph。工具优化缓存为工具添加缓存层如functools.lru_cache对相同参数的调用直接返回缓存结果。超时与重试为网络工具设置合理的超时并实现重试逻辑避免单次失败阻塞整个流程。异步工具如果工具是 I/O 密集型如网络请求将其改造成异步函数并使用AsyncTool包装。然后在异步环境中运行智能体可以显著提升并发性能。模型选择对于不需要顶级推理能力的步骤可以考虑使用更小、更快的模型如gpt-3.5-turbo来承担部分工作或者使用大模型生成计划用小模型执行简单步骤。6.4 一个综合优化示例为智能体添加简易记忆摘要下面演示一个简化版的思路在工具调用后对历史进行摘要以减少上下文长度from langchain.memory import ConversationSummaryBufferMemory from langchain_openai import ChatOpenAI summary_llm ChatOpenAI(modelgpt-3.5-turbo, temperature0) # 可以用小模型做摘要 memory ConversationSummaryBufferMemory( llmsummary_llm, max_token_limit200, # 摘要后的最大token数 memory_keychat_history, return_messagesTrue ) # 注意将memory集成到AgentExecutor中需要自定义Agent或使用特定的Agent类型如ConversationalAgent。 # 以下是一个概念性代码展示思路 from langchain.agents import ConversationalChatAgent, AgentExecutor from langchain.tools import Tool # 假设我们有工具 tools [search_tool, calc_tool] # 创建支持对话记忆的Agent提示词 from langchain.agents import load_tools from langchain.agents.conversational_chat.base import ConversationalChatAgent # 这种方法需要更复杂的设置通常建议查阅最新LangChain文档中关于“Agent with Memory”的示例。 # 一个更直接但“笨”的办法是在每一轮Agent执行后手动将输入输出保存到memory然后在下一轮调用前从memory加载摘要作为系统提示的一部分。 def run_agent_with_memory(question: str): # 1. 从memory加载历史摘要 history memory.load_memory_variables({})[chat_history] history_summary if history: # 这里简化处理实际应使用memory的摘要功能 history_summary 之前的对话摘要 \n.join([msg.content for msg in history[-2:]]) # 取最后两条 # 2. 将历史摘要融入本次问题 enhanced_question f{history_summary}\n\n当前问题{question} # 3. 执行智能体使用一个没有内置记忆的普通执行器 result executor_logged.invoke({input: enhanced_question}) # 4. 将本轮问答保存到memory memory.save_context({input: question}, {output: result[output]}) return result[output] # 测试 print(第一轮:) ans1 run_agent_with_memory(爱因斯坦是谁) print(ans1) print(\n第二轮依赖历史:) ans2 run_agent_with_memory(他什么时候出生的) # 理想情况下智能体应该能利用历史中的“爱因斯坦”信息 print(ans2)这个示例展示了思路但生产环境需要更严谨的设计。对于复杂的记忆和状态管理LangGraph是比基础AgentExecutor更强大和灵活的选择它天然支持有状态的工作流和更精细的控制。
返回列表