LangChain系列文章目录

第一章 LangChain 简介和核心包
第二章 LangChain 提示词工程
第三章 LangChain 工作流
第四章 LangChain服务部署与链路监控
第五章 LangChain 消息管理与聊天历史存储
第六章 LangChain多模态输入与自定义输出
第七章 LangChain 工具
第八章 LangChain工具调用Agent与MCP


文章目录


前言

本文主要整理 LangChain 工具调用 Agent 相关内容,包括 Agent 工作流程、Tool 与 Agent 的区别、Tavily 工具、检索器工具、模型绑定工具、工具代理执行器、create_tool_calling_agent、create_react_agent、Agent 记忆,以及 MCP 工具调用等内容。


LangChain工具调用Agent

一、创建和运行Agent

1.代理作用

  • 大语言模型无法执行实际操作,只能输出文本或内容(AIGC)。
  • LangChain代理是使用LLM作为推理引擎的系统,用于确定应采取某些操作以及这些操作应该的输入参数。然后操作结果可能会再反馈给代理,并确定是否需要更多操作,或者是否结束。
  • 使用LangChain构建代理适合入门级使用,如果希望更好的灵活性和控制性。要使用更高级的代理创建能力,可以使用LangGraph。

2.Agent工作流程

  • Agent工作流程
    • 第一次LLM调用:分析(用户输入),决定需要调用A工具(有可能异步调用多个工具)
    • 第二次LLM调用:整合分析(用户输入+A工具结果),决定需要调用B工具(如果需要)
    • 第三次次LLM调用:整合分析(用户输入+A工具结果+B工具结果),给出最终答案
  • agent_scratchpad:
    • prompt中最后有个placeholder,variable_name值是agent_scratchpad,存储Agent在执行过程中的中间思考步骤和工具调用记录。
    • 工具调用的记录
    • 工具返回的结果
    • Agent的思考过程。模型会看到类似这样的内容:
      • Thought: 用户问了关于时间的问题,我需要获取当前时间
      • Action: Current_Time-tool
      • Action Input: {}
      • Observation: 2024年1月15日,14时30分25秒
      • Thought: 现在我知道了当前时间,可以回答用户了
  • 最终输出的生成。模型会基于以下内容生成最终的回答
    • 系统提示词(小芳的人设)
    • 对话历史
    • 用户当前输入
    • 工具调用的完整记录和结果
  • 示例。如用户问:“现在几点了?”
    • Agent分析:需要获取时间信息
    • 调用工具:current_time()返回"2024年1月15日,14时30分25秒"
    • 模型整合:结合小芳的人设,生成回答
    • 输出:“哈哈哈,现在是2024年1月15日下午2点30分啦!怎么啦宝贝,是不是又熬夜了想确认时间?你这样下去身体会受不了的啦~”
  • 关键点
    • 工具结果不是直接输出,而是作为上下文提供给模型
    • 模型会根据人设和上下文重新组织语言
    • 最终输出是模型生成的,保持了角色一致性

3.创建和运行代理包括:

  • 选用具有调用工具能力的大语言模型
  • 使用工具(页面搜索和检索器也是一种工具)操作某个外部程序
  • 聊天历史,允许记住过去的交互,并在回答问题时考虑这些交互。
  • 使用LangSmith/ Verbose调试和跟踪程序

4.工具(Tool)与代理(Agent)的区别

特性 工具(Tool) 代理(Agent)
作用 完成单一任务(如搜索、计算) 协调多个工具,根据输入决定调用哪些工具
是否自主决策 否(被动执行) 是(依赖LLM推理)
例子 TavilySearch 仅执行搜索 需通过AgentExecutor组合多个工具

5.创建代理(Agent)过程

  • 创建工具对象Tool
  • 将多个工具组成一个工具列表Tools
  • 创建一个有工具调用能力的代理Agent,并指明调用的工具列表
  • 将有工具调用能力的代理创建成一个代理执行器Agent_executor
  • 在链中使用代理执行器
  • 创建有记忆能力的代理执行器(可选)
  • 创建有MCP调用能力的代理执行器(可选)

二、创建工具Tool

1.创建搜索工具(Tavily示例)

  • 工具目的是被大语言模型所调用,执行某些操作。
  • 使用Tavily搜索引擎作为工具。
  • Tavily需要API密钥。并且需要安装:pip install langchain_tavily
  • TavilySearch()封装了Tavily搜索引擎的API调用逻辑。返回工具对象。
  • TavilySearch()参数:
    • tavily_api_key:Tavily的API密钥(必填)。默认None
    • max_results:返回的最大结果数量。默认5
    • include_raw_content:是否在结果中包含网页原始内容(可能增加延迟)。默认False
    • include_images:是否包含图片结果。默认False
    • include_answers:是否尝试提取直接答案(如知识图谱)。默认False
    • search_depth:搜索深度"basic"(快速,默认)或 “advanced”(更全面)

代码

from langchain_tavily import TavilySearch

search_tool = TavilySearch(
    max_results=1, 
    tavily_api_key="************"
)
print(search_tool.invoke("今天上海天气怎么样"))

结果

[
    {
        "title": "今日天气-上海市气象局",
        "url": "http://sh.cma.gov.cn/sh/tqyb/jrtq/",
        "content": "05月27日 星期二·晴. 多云·19℃~27℃·东南风 风力4-5级.",
        "score": 0.78154784,
    }
]

2.创建检索器工具

  • 检索器Retriever用于从文本集合或知识库中(使用embedding模型)检索相关信息。为大语言模型提供知识背景
  • WebBaseLoader()是LangChain提供的一个文档加载器(Loader),用于从网页URL中加载内容并转换为LangChain可处理的文档加载器(Loader对象)。参数
    • web_path:网页URL(字符串或列表)。支持多个URL批量加载。(必填)
    • continue_on_failure:加载URL失败时是否跳过或抛出异常,默认False。
    • verify_ssl bool:是否验证SSL证书(False跳过证书验证),默认True。
    • proxy:设置代理服务器(如 “http://your-proxy:port”)。
    • requests_kwargs:传递给requests.get()的额外参数,如headers、timeout
      • 如提示:USER_AGENT environment variable not set, consider setting it to identify your requests. 就是没有设置头部信息可以设置(设置了好像不起效果)
    • bs_kwargs:传递给BeautifulSoup解析器的参数(如指定解析器{“features”: “lxml”})。
  • 返回加载器Loader对象后,使用load()方法,返回Document文档对象列表
  • 递归字符文本分割,使用RecursiveCharacterTextSplitter(separators, chunk_size, chunk_overlap)方法,对文本分割,返回文本分割对象text_splitter。参数
    • separators为多级别分割字符列表,级别从高到低,默认[“\n\n”, “\n”, " ", “”]
    • chunk_size为目标分割块大小数值,默认4000
    • chunk_overlap为块之间的重叠大小数值,默认200
  • 返回文本分割对象text_splitter后,使用split_documents(documents)方法,对指定Document文档对象列表进行分割,返回切分好的Document文档对象列表
  • 使用FAISS.from_documents(documents, embeddings)方法,加载Document列表到向量数据库,返回向量数据库对象db。参数:
    • documents为要添加到向量库的Document列表。
    • embedding为嵌入函数。默认为None。
  • 返回向量数据库对象db后,使用as_retriever()方法,返回向量数据库检索器对象retriever
  • 返回向量数据库检索器对象retriever后,可使用retriever.invoke(input)方法,搜索数据库内向量值最接近的数据。
  • 使用create_retriever_tool()方法,用于将检索器对象retriever创建成工具对象。参数:
    • retriever:是 任何实现了BaseRetriever接口的检索器实例(如VectorStoreRetriever、BM25Retriever等)。
    • name:是 工具的唯一标识名称(Agent调用时使用的名称,需简洁无空格)。
    • description:工具的功能描述(Agent根据描述决定是否调用此工具)。

代码

from langchain.tools.retriever import create_retriever_tool
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter

embeddings = HuggingFaceEmbeddings(
    model_name="D:/python/envs/lc/hfmodel/sentence-transformers/all-MiniLM-L6-v2",
)

# 搜索搜狗百科,猫页面
custom_headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.9",
}
loader = WebBaseLoader(
    web_path="https://baike.sogou.com/v29867.htm",
    requests_kwargs={"headers": custom_headers}
)
documents = loader.load()
docs = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200,
).split_documents(documents)

vector = FAISS.from_documents(documents=docs, embedding=embeddings)
retriever = vector.as_retriever()

print(retriever.invoke("猫的特征")[0])

retriever_tool = create_retriever_tool(
    retriever=retriever,
    name="sogo_search",
    description="搜索搜狗百科",
)

结果

USER_AGENT environment variable not set, consider setting it to identify your requests.
page_content='分类
 主題
 專題' metadata={'source': 'https://zh.wikipedia.org/wiki/%E7%8C%AB', 'title': '猫 - 维基百科,自由的百科全书', 'language': 'zh'}

3.创建工具列表,将工具打包成一个工具列表,任由模型选择使用。

tools = [search_tool, retriever_tool]

4.模型绑定工具

  • 使用模型的bind_tools()方法,来让模型绑定工具。让模型有决定是否调用工具的输出(返回中有tool_calls属性),但不是真正执行调用工具。
    • 可以用来测试。如果有输入后,模型是否会使用工具,使用了什么工具。而不是真正的调用工具。
    • 参数tools为工具列表
  • 返回对象使用tool_calls属性获取使用的工具信息列表

代码

# 使用以上两个工具
...
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage

model = ChatOpenAI(
    model="Pro/deepseek-ai/DeepSeek-V3",
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
    temperature=0.7,
    max_tokens=8000,
)
tools = [search_tool, retriever_tool]
model_with_tools = model.bind_tools(tools=tools)

response = model_with_tools.invoke([HumanMessage(content="你好")])
print(f"ContentString: {response.content}")
print(f"ToolCalls: {response.tool_calls}")

response = model_with_tools.invoke([HumanMessage(content="今天上海天气怎么样")])
print(f"ContentString: {response.content}")
print(f"ToolCalls: {response.tool_calls}")

结果

ContentString: 你好!有什么可以帮助你的吗?
ToolCalls: []

ContentString: 为了获取上海今天的天气信息,我需要使用一个可靠的搜索引擎来查找最新的天气数据。让我来搜索一下。
ToolCalls:  
[
    {
        "name": "tavily_search_results_json",
        "args": {"query": "今天上海天气"},
        "id": "call_EOxYscVIVjttlbztWoR1CvTm",
        "type": "tool_call",
    }
]

三、代理和工具代理和工具代理执行器

1.原理

  • 代理:一个有prompt或有历史记录等背景信息的LLM链条。根据模型和prompt来回答用户的问题
  • 工具代理:一个代理,prompt内容上有可以被调用工具的列表和每个工具的调用规则。并且选中调用的工具后,指明输出的内容。(因为工具代理执行器会根据输出内容,给到工具来获取执行结果)
  • 工具代理执行器:根据用户输入,先用工具代理决定是否需要调用工具,(否就直接输出结果),是就输出调用工具的参数,再使用参数执行工具,输出工具执行结果。再一次工具代理,prompt是用户的输入和工具调用后的执行结果(可能包括步骤),输出是否需要再循环使用工具代理或者输出结果给用户。

2.工具代理执行器

  • 工具代理是工具代理执行器中的一步,作用是决定是否调用工具。
  • 使用model.bind_tools(tools=tools)方法,是模型绑定工具。就是让模型输出AIMessage消息,并且消息内部可能有tool_calls属性。
  • 使用create_tool_calling_agent()方法等,是生成一个工具代理。
  • 使用AgentExecutor()方法,是生成一个工具代理执行器。
  • 工具代理执行器类似LangGraph中的一个工具调用图。有工具节点和llm节点。
  • 工具代理执行器流程:
    在这里插入图片描述

四、创建工具代理和工具代理执行器

1.使用create_tool_calling_agent()方法,创建一个通用的工具调用代理对象agent,为支持工具调用(tool calling)的LLM设计。

  • 注:传递model,不是model_with_tools。因为create_tool_calling_agent会在内部调用bind_tools()。
  • 参数
    • llm:支持工具调用功能的语言模型
    • tools:工具列表
    • prompt:对话提示模板,用于控制agent的行为。需要特定的提示模板。

2.create_tool_calling_agent()方法生成的Agent对象,需要特定的提示模板prompt。

  • 可以使用hub下载:
prompt = hub.pull("hwchase17/openai-functions-agent")
  • 内部内容组成:

代码

from langchain import hub
prompt = hub.pull("hwchase17/openai-functions-agent")
# print(prompt.messages)  # 显示结构

相当于

from langchain_core.prompts import (
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate,
    AIMessagePromptTemplate,
)
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.prompts import MessagesPlaceholder
from langchain_core.prompts import PromptTemplate

prompt = ChatPromptTemplate.from_messages(
    [
        SystemMessagePromptTemplate(
            prompt=PromptTemplate(
                input_variables=[],
                input_types={},
                partial_variables={},
                template="You are a helpful assistant",
            ),
            additional_kwargs={},
        ),
        MessagesPlaceholder(variable_name="chat_history", optional=True),
        HumanMessagePromptTemplate(
            prompt=PromptTemplate(
                input_variables=["input"],
                input_types={},
                partial_variables={},
                template="{input}",
            ),
            additional_kwargs={},
        ),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ]
)
  • 变量agent_scratchpad作用
    • agent_scratchpad是Agent的"工作区"或"草稿本",用于存储Agent在执行过程中的中间思考步骤和工具调用记录。
      • agent需要使用工具,就需要在prompt中预留agent_scratchpad存储。
      • 一般在prompt的最后一行,并以placeholder形式保存:
MessagesPlaceholder(variable_name="agent_scratchpad")
("placeholder", "{agent_scratchpad}")
  • 具体作用包括:
    • 存储工具调用历史:记录Agent调用了哪些工具。保存工具的输入参数和返回结果。维护工具调用的时间顺序
    • 支持多步推理:Agent可以基于之前的工具调用结果进行下一步决策。实现复杂任务的分步执行。避免重复调用相同的工具
    • 提供上下文信息:让Agent了解当前执行到哪一步。帮助Agent判断是否需要继续调用工具。为最终回答提供完整的推理链
    • 调试和可观察性:开发者可以查看Agent的完整思考过程。便于调试和优化Agent的行为。提高Agent决策的透明度
  • 例如
    • 初始状态:agent_scratchpad为空
    • 工具调用后:agent_scratchpad会包含类似这样的内容:
Thought: 我需要搜索当前天气信息
Action: time_aware_search
Action Input: {"query": "今天东莞天气"}
Observation: [搜索结果]

3.为create_tool_calling_agent()方法自定义提示模板prompt

  • 为特定目的可以自定义生成工具代理执行器的prompt,如,使得工具代理执行器有时效性。
  • 自定义prompt的特点
    • 每次使用Agent时,会使用当前时间工具,自动地获取时间。以便问答得更有时效性。
    • 问答时,自动地确认是否搜索工具,而无需用户强调使用搜索才用。
  • prompt规则
    • 用MessagesPlaceholder增加变量chat_history存储对话记录。放在用户输入前
    • 用MessagesPlaceholder增加变量agent_scratchpad存储Agent的记录。放在最后
  • 示例

代码

from langchain.agents import create_tool_calling_agent
from langchain.agents import AgentExecutor
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
...
# 创建自定义prompt,让AI更主动地使用工具
custom_prompt = ChatPromptTemplate.from_messages([
    (
        "system",
"""
你是一个智能助手,具有以下能力和行为准则:

1. **时间感知**:在每次对话开始时,你应该主动获取当前时间,让用户知道现在的时间。

2. **智能搜索**:当用户询问以下类型的问题时,你应该主动使用搜索工具:
   - 最新新闻、时事、股价、天气等实时信息
   - 你不确定或可能过时的信息
   - 具体的事实查询、数据统计
   - 最新的技术发展、产品信息
   - 当前发生的事件
   
   **重要**:搜索工具已经会自动在查询前添加当前时间信息,所以你可以直接使用用户的原始查询,不需要手动添加时间。

3. **主动性**:不要等待用户明确要求搜索,要根据问题内容判断是否需要搜索最新信息。

4. **工具使用原则**:
   - 优先使用工具获取准确、最新的信息
   - 在回答前先思考是否需要搜索或获取时间
   - 将工具获取的信息整合到自然的回答中

请始终保持友好、专业的态度,并主动使用可用的工具来提供最准确和最新的信息。
""",
    ),
    MessagesPlaceholder(variable_name="chat_history"),
    ("human", "{input}"),
    MessagesPlaceholder(variable_name="agent_scratchpad"),
])
agent = create_tool_calling_agent(llm=llm, tools=tools, prompt=custom_prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
...

4.使用AgentExecutor()方法,将一个工具调用Agent对象,创建成一个工具代理执行器对象agent_executor。

  • 与Agent区别,AgentExecutor是对Agent调用工具的整个流程的监管(包括执行流程管理和错误处理机制)。是Agent的一个封装。
    • 例如,AgentExecutor会使用工具调用Agent来调用工具,但是发现结果不理想,或者调用时输入参数有误,会自动地修改输入重新调用多一次Agent来获取结果,直到结果满意为止。
    • AgentExecutor类似于LangGraph的图,也是整个工作流流程地监管。
  • 核心功能:
    • 负责调用和控制Agent直到获得最终输出
    • 迭代执行Agent返回的动作
    • 持续这个过程直到生成最终答案
  • 参数
    • agent:用于创建计划和决定执行步骤的代理实例
    • tools:代理可以调用的有效工具列表
    • max_iterations:最大执行步骤数(默认15步)模型最大执行次数
    • max_execution_time:执行循环的最大时间限制
    • early_stopping_method:提前停止方法(‘force’或’generate’)
    • handle_parsing_errors:执行失败是否不报错。默认False会报错,True则直接输出结果。
      • 设为True时,当Agent输出的结果无法被「输出解析器 (OutputParser)」成功解析时,让AgentExecutor不会直接抛出程序崩溃的异常,而是进行「容错处理」并继续执Agent流程,保障Agent稳定运行。
      • 如,使用ReAct风格代理(hwchase17/react),该解析器要求模型严格输出结构化标记:Thought:,Action:,Action Input:,Final Answer:。而模型输出是一段普通说明(还包含了反引号代码块和中文列表),没有包含必须的“Final Answer:”或正确的Action/Thought标记,解析器无法识别,触发错误。无法继续。设为True后,复跑验证结果正常,代理成功使用工具、并给出了最终答案。
    • verbose:是否显示详细日志

5.使用代理执行器对象agent_executor的invoke方法执行

代码

from langchain_openai import ChatOpenAI
from langchain_tavily import TavilySearch
from langchain.tools.retriever import create_retriever_tool
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain import hub
from langchain.agents import create_tool_calling_agent
from langchain.agents import AgentExecutor

# 模型
model = ChatOpenAI(
    model="Pro/deepseek-ai/DeepSeek-V3",
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
    temperature=0.7,
    max_tokens=8000,
)
embeddings = HuggingFaceEmbeddings(
    model_name="D:/python/envs/langchain2/hfmodel/sentence-transformers/all-MiniLM-L6-v2",
)

# 工具列表
search_tool = TavilySearch(
    max_results=1, tavily_api_key="************"
)
loader = WebBaseLoader("https://zh.wikipedia.org/wiki/%E7%8C%AB")
documents = loader.load()
docs = RecursiveCharacterTextSplitter(
    chunk_size=1000, chunk_overlap=200,
).split_documents(documents)
vector = FAISS.from_documents(docs, embeddings)
retriever = vector.as_retriever()
retriever_tool = create_retriever_tool(
    retriever=retriever,
    name="wiki_search",
    description="搜索维基百科",
)
tools = [search_tool, retriever_tool]

# 创建Agent执行器对象
prompt = hub.pull("hwchase17/openai-functions-agent")
# print(prompt.messages)  # 显示结构
agent = create_tool_calling_agent(llm=model, tools=tools, prompt=prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# 执行Agent执行器
# print(agent_executor.invoke({"input": "你好"}))
# print(agent_executor.invoke({"input": "猫的特征"}))
print(agent_executor.invoke(input={"input": "猫的特征?今天上海天气怎么样?"}))

结果

> Entering new AgentExecutor chain...

Invoking: `wiki_search` with `{'query': '猫'}`
responded: ### 猫的特征
为了回答“猫的特征”这个问题,我会先使用维基百科搜索工具(wiki_search)来获取关于猫的科学分类、生理特征、行为特征等详细信 息。

### 今天上海天气怎么样
为了回答“今天上海天气怎么样”这个问题,我会使用搜索工具(tavily_search_results_json)查询最新的上海天气信息。

现在我先执行这两个工具的调用:

本页面最后修订于2025年5月22日 (星期四) 00:47。
本站的全部文字在知识共享 署名-相同方式共享 4.0协议之条款下提供,附加条款亦可能应用。(请参阅使用条款)
Wikipedia®和维基百科标志是维基媒体基金会的注册商标;维基™是维基媒体基金会的商标。
...
而貓喜好的磨爪物各不相同。家貓可能會選擇沙發、牆壁甚至人類的衣物等作為自己的磨爪物。

感官[编辑]
貓的感官適於狩獵。在哺乳類動物中,貓的聽覺、視覺、嗅覺、味覺、觸覺極敏銳。

Invoking: `tavily_search_results_json` with `{'query': '今天上海天气'}`
responded: ### 猫的特征
为了回答“猫的特征”这个问题,我会先使用维基百科搜索工具(wiki_search)来获取关于猫的科学分类、生理特征、行为特征等详细信 息。

### 今天上海天气怎么样
为了回答“今天上海天气怎么样”这个问题,我会使用搜索工具(tavily_search_results_json)查询最新的上海天气信息。

现在我先执行这两个工具的调用:

[{'title': '今天上海市天气_今日天气预报- 墨迹天气', 'url': 'https://tianqi.moji.com/today/china/shanghai/shanghai', 'content': '上海市今天实况:21度阴,湿度:67%,北风:1级。白天:27度,多云。 夜间:多云,19度,季节交替,天气偏凉,墨迹天气建议您穿着单衣单裤,年老体弱者最好加一件贴身的背心或是', 'score': 0.85509074}]
### 猫的特征
猫是一种常见的家养动物,具有以下主要特征:
1. **爪子**:猫的爪子尖锐且可伸缩,能够伸张或缩藏。趾底有脂肪肉垫,行走无声。
2. **感官**:猫的听觉、视觉、嗅觉、味觉和触觉都非常敏锐,非常适合狩猎。
3. **行为**:猫有磨爪的习惯,可能会选择家具或衣物作为磨爪物。

### 今天上海天气
根据最新天气预报:
- **当前实况**:21°C,阴天,湿度67%,北风1级。
- **白天**:多云,最高气温27°C。
- **夜间**:多云,最低气温19°C。
  
建议穿着单衣单裤,年老体弱者可以加一件背心。

> Finished chain.

# 结果
{
    "input": "猫的特征?今天上海天气怎么样?",
    "output": "### 猫的特征\n猫是一种常见的家养动物,具有以下主要特征:\n1. **爪子**:猫的爪子尖锐且可伸缩,能够伸张或缩藏。趾底有脂肪肉垫,行走无声。\n2. **感官**:猫的听觉、视觉、嗅觉、味觉和触觉都非 常敏锐,非常适合狩猎。\n3. **行为**:猫有磨爪的习惯,可能会选择家具或衣物作为磨爪物。\n\n### 今天上海天气\n根据最新天气 预报:\n- **当前实况**:21°C,阴天,湿度67%,北风1级。\n- **白天**:多云,最高气温27°C。\n- **夜间**:多云,最低气温19°C。\n  \n建议穿着单衣单裤,年老体弱者可以加一件背心。",
}

6.代理异步使用工具

  • agent异步使用工具:如果模型确认调用多个工具时,会同时的发起多个调用,不会调用完一个再接着一个。
  • 异步使用工具有助提高速度。(建议异步使用工具)
  • 示例:同时调用时间和tavily工具

代码

import asyncio
from langchain_openai import ChatOpenAI
from langchain_tavily import TavilySearch
from langchain_huggingface import HuggingFaceEmbeddings
from langchain import hub
from langchain.agents import create_tool_calling_agent
from langchain.agents import AgentExecutor
import datetime
from langchain_core.tools import tool

async def main():
    # 模型
    model = ChatOpenAI(
        model="Pro/deepseek-ai/DeepSeek-V3",
        openai_api_key="************",
        openai_api_base="https://api.siliconflow.cn/v1",
        temperature=0.7,
    )

    # 工具列表
    search_tool = TavilySearch(
        max_results=1, tavily_api_key="************"
    )
    @tool(
        "Current_Time-tool",
        description="Used to retrieve the current date and time.",
        return_direct=False,
    )
    def current_time() -> str:
        """Get the current date and time"""
        res = datetime.datetime.now().strftime("%Y年%m月%d日,%H时%M分%S秒")
        return res
    tools = [search_tool, current_time]

    # 创建Agent执行器对象
    prompt = hub.pull("hwchase17/openai-functions-agent")
    agent = create_tool_calling_agent(llm=model, tools=tools, prompt=prompt)
    agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) 

    # 异步执行Agent执行器
    result3 = await agent_executor.ainvoke(
        {"input": "2024年巴黎奥运会吉祥物是什么?现在几点?"}
    )
    print(result3)

# 运行异步主函数
if __name__ == "__main__":
    asyncio.run(main())

相当于

{
    "input": "2024年巴黎奥运会吉祥物是什么?现在几点?",
    "output": "2024年巴黎奥运会的吉祥物是**弗里热(Phryge)**,它的设计灵感源自法国标志性的弗雷吉亚帽,象征着自由。  \n\n现在是**2025年7月29日,15:46**。",
}

五、创建agent方法:create_react_agent()与create_tool_calling_agent()对比

1.create_react_agent()

  • create_react_agent()是基于ReAct(Reasoning+Acting)框架实现的代理。
  • 特点
    • 使用文本格式的思维链(chain-of-thought)来决定何时以及如何使用工具
    • 代理的输出是自由格式的文本,包含思考过程和工具调用
    • 需要LLM能够理解特定的提示格式
    • 参数prompt建议使用"hwchase17/react"
    • 工具调用成功率:98%
  • 基本用法

代码

from langchain import hub
from langchain.agents import create_react_agent, AgentExecutor
from langchain_community.tools import Tool
from langchain_openai import ChatOpenAI

# 定义工具
tools = [
    Tool(
        name="Search",
        func=search_tool,
        description="Useful for searching the web"
    )
]
# 获取提示模板
prompt = hub.pull("hwchase17/react")
# 创建LLM实例
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建代理
agent = create_react_agent(llm, tools, prompt)
# 创建执行器
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 运行代理
result = agent_executor.invoke({"input": "What's the weather in New York today?"})

2.create_tool_calling_agent()

  • create_tool_calling_agent()是专为支持工具调用功能的LLM(如OpenAI的gpt-4)设计的代理。
  • 特点
    • 利用LLM原生的工具调用能力(OpenAI的Function Calling或Tool Calling)
    • 更结构化的工具调用方式
    • 通常更高效,因为不需要生成中间文本
    • 需要LLM明确支持工具调用功能
    • 参数prompt没有默认值
    • 工具调用成功率:80%
  • 基本用法

代码

from langchain import hub
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_community.tools import Tool
from langchain_openai import ChatOpenAI

# 定义工具
tools = [
    Tool(
        name="Search",
        func=search_tool,
        description="Useful for searching the web"
    )
]
# 获取提示模板
prompt = hub.pull("hwchase17/openai-tools-agent")
# 创建支持工具调用的LLM实例
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 创建代理
agent = create_tool_calling_agent(llm, tools, prompt)
# 创建执行器
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 运行代理
result = agent_executor.invoke({"input": "What's the weather in New York today?"})

3.主要区别

特性 create_react_agent create_tool_calling_agent
实现方式 基于文本的ReAct框架 基于LLM原生工具调用功能
LLM 要求 任何能理解提示的LLM 必须支持工具调用的LLM
输出格式 自由文本,包含思考过程 结构化工具调用
效率 相对较低,需要生成更多文本 相对较高,直接调用工具
适用场景 通用场景,LLM不支持工具调用时 LLM明确支持工具调用时
调试难度 较难,需要解析文本输出 较易,结构化输出

4.Function Calling与ReAct Agent

  • create_react_agent()创建的是一个ReAct Agent,来调用工具。
  • create_tool_calling_agent()使用Function Calling能力的模型来调用工具。
  • Function Calling = 模型主动返回结构化调用指令
  • ReAct Agent = 模型用“普通文本”写出 Action,ReAct框架去“读”并执行

5.选择建议

  • 如果使用OpenAI等支持工具调用的模型,选用create_tool_calling_agent
  • 如果追求更高的效率和可靠性,选用create_tool_calling_agent
  • 如果使用不支持工具调用的模型,选用create_react_agent
  • 如果需要更透明的思考过程用于调试,选用create_react_agent

6.LangChain的create_react_agent()

  • 引用两种方式:
    • 直接引用:create_react_agent()
    • 使用agent方法:agent.create_react_agent()
  • 使用agent时,必须有output_parser参数,定义输出格式。

代码

from langchain.agents import create_react_agent, AgentExecutor
...
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

相当于

from langchain.agents.react import agent
from langchain.agents.agent import AgentExecutor
# 与from langchain.agents import AgentExecutor引入一样
...
agent = agent.create_react_agent(
    llm=llm,
    tools=tools,
    prompt=prompt,
    output_parser=out_parser
)
agent_executor = AgentExecutor(agent=text_agent, tools=tools, verbose=True)
  • agent.create_react_agent()与create_react_agent()区别
    • 我测试后认为:功能基本一样,可能create_react_agent()会好些。
    • 示例

代码

from langchain.agents import create_react_agent, AgentExecutor
...
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
response = agent_executor.invoke({"input": "2024年巴黎奥运会吉祥物是什么?"})
print(response)

相当于

from langchain.agents.react import agent 
from langchain.agents.agent import AgentExecutor
...
text_agent = agent.create_react_agent(
    llm=model,
    tools=tools,
    prompt=text_prompt,
    output_parser=out
)
agent_executor = AgentExecutor(agent=text_agent, tools=tools, verbose=True)
response = agent_executor.invoke({"input": "2024年巴黎奥运会吉祥物是什么?"})
print(response)
  • 与LangGraph的create_react_agent()不一样:
    • LangGraph的create_react_agent()输出的是一个AgentExecutor,在LangGraph中可作为一个子图或节点使用。而不是一个Agent对象。
    • 引用:from langgraph.prebuilt import create_react_agent

7.create_react_agent()提示词模板

  • 可以使用hub下载:
prompt = hub.pull("hwchase17/react")
  • 其实内部组成:

代码

from langchain import hub
prompt = hub.pull("hwchase17/react")
# print(prompt)  # 显示结构

相当于

from langchain_core.prompts import PromptTemplate

prompt = PromptTemplate.from_template(
    input_types = {},
    partial_variables = {},
    metadata = {
        "lc_hub_owner": "hwchase17",
        "lc_hub_repo": "react",
        "lc_hub_commit_hash": "d15fe3c426f1c4b3f37c9198853e4a89b0e97ca7ea4d",
    },
    template = """Answer the following questions as best you can. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

Question: {input}
Thought:{agent_scratchpad}"""
)

8.create_react_agent()自定义提示词模板

  • 自定义提示词模板,必须PromptTemplate定义变量input_variables为:[“input”, “tools”, “tool_names”, “agent_scratchpad”]

代码

from langchain.agents.react import agent 
from langchain_core.prompts import PromptTemplate
from langchain.agents.structured_chat.output_parser import StructuredChatOutputParser
from langchain.agents.agent import AgentExecutor
# 与from langchain.agents import AgentExecutor引入一样
from langchain_openai import ChatOpenAI
from time_search_tool import TimeAwareSearchTool

model = ChatOpenAI(
    model="doubao-seed-1-6-flash-250615",
    openai_api_key="************",
    openai_api_base="https://ark.cn-beijing.volces.com/api/v3",
)

search_tool = TimeAwareSearchTool()
tools = [search_tool]

custom_prompt = """
你拥有以下工具:
{tools}

工具名称列表:{tool_names}

按照以下格式执行:
Question: 需要回答的问题
Thought: 思考过程(必须分析是否需要使用工具)
Action: 选择工具(必须是 [{tool_names}] 之一)
Action Input: 工具输入参数
Observation: 工具返回结果
...(可重复多次)
Thought: 现在可以生成最终答案
Final Answer: 最终回答

开始!

Question: {input}
{agent_scratchpad}"""

text_prompt = PromptTemplate(
    template=custom_prompt,
    input_variables=["input", "tools", "tool_names", "agent_scratchpad"],
)
out = StructuredChatOutputParser()

text_agent = agent.create_react_agent(
    llm=model,
    tools=tools,
    prompt=text_prompt,
    output_parser=out
)
agent_executor = AgentExecutor(agent=text_agent, tools=tools, verbose=True)
response = agent_executor.invoke({"input": "2024年巴黎奥运会吉祥物是什么?"})
print(response)

六、链中使用代理

1.链(Chain)与代理(Agent)

  • 在LangChain中,将工具(如搜索网页)结合到链(Chain)中通常需要通过代理(Agent)来实现。
  • Agent可以动态选择工具并决定工具执行顺序,而普通Chain是固定流程。
  • 链与代理核心概念区分
组件 特点 适用场景
Chain 固定顺序执行(如A→B→C) 确定性的标准化流程
Agent 动态选择工具/链 需要决策的复杂任务

2.链中使用代理

  • 在链中使用工具,一般是通过工具代理执行器。执行器的结果被链调用。
  • 工具代理执行器只是链针对工具处理的一个部分。在链中,可以链式地继续操作其他步骤。
  • 示例中,链条过程:
    • 先使用代理对输入内容作搜索,返回结果
    • 将结果再通过大语言模型进行处理,返回最终结果,输出。

代码

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_tavily import TavilySearch
from langchain.agents import AgentExecutor, create_tool_calling_agent

model = ChatOpenAI(
    model="Pro/deepseek-ai/DeepSeek-V3",
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
    temperature=0.7,
    max_tokens=8000,
)

# 创建工具
search_tool = TavilySearch(
    max_results=1, tavily_api_key="************"
)
tools = [search_tool]

# 创建Agen执行器
agent_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个助手,可以搜索网络信息。回答要简短。"),
        ("human", "{input}"),
        ("placeholder", "{agent_scratchpad}"),  # Agent默认预留中间步骤记录
    ]
)
agent = create_tool_calling_agent(llm=model, tools=tools, prompt=agent_prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools)

# 构建链:搜索→总结
chain = (
    RunnablePassthrough.assign(
        search_results=lambda x: agent_executor.invoke({"input": x["inputx"]})
    )
    | ChatPromptTemplate.from_template("{operate}:{search_results}")
    | model
    | StrOutputParser()
)

# 执行
result = chain.invoke(
    {"inputx": "2024年巴黎奥运会吉祥物是什么?", "operate": "用100字总结"}
)
print(result)

结果

2024年巴黎奥运会吉祥物名为“Phryge”(弗里吉),设计灵感源自法国象征自由的弗里吉亚帽。它以拟人化形象呈现,代表法国历史与奥运精神,传递团结与包容的理念。

3.异步执行链

  • 异步agent的工具调用。提高速度。

代码

import asyncio
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_tavily import TavilySearch
from langchain.agents import AgentExecutor, create_tool_calling_agent
import datetime
from langchain_core.tools import tool

model = ChatOpenAI(
    model="Pro/deepseek-ai/DeepSeek-V3",
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
)

# 创建工具
search_tool = TavilySearch(
    max_results=1, tavily_api_key="************"
)
@tool(
    "Current_Time-tool",
    description="Used to retrieve the current date and time.",
    return_direct=False,
)
def current_time() -> str:
    """Get the current date and time"""
    res = datetime.datetime.now().strftime("%Y年%m月%d日,%H时%M分%S秒")
    return res
tools = [search_tool, current_time]

# 创建Agent执行器
agent_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个助手,可以搜索网络信息。回答要简短。"),
        ("human", "{input}"),
        ("placeholder", "{agent_scratchpad}"),  # Agent默认预留中间步骤记录
    ]
)
agent = create_tool_calling_agent(llm=model, tools=tools, prompt=agent_prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 异步函数:搜索和时间
async def get_search_results(x):
    """异步获取搜索结果"""
    return await agent_executor.ainvoke({"input": x["inputx"]})

# 构建异步链:搜索→总结
chain = (
    RunnablePassthrough.assign(search_results=get_search_results)
    | ChatPromptTemplate.from_template("{operate}:{search_results}")
    | model
    | StrOutputParser()
)

# 异步主函数
async def main():
    """异步主函数"""
    result = await chain.ainvoke(
        {"inputx": "2024年巴黎奥运会吉祥物是什么?现在几点?", "operate": "用100字总结"}
    )
    print(result)

# 执行异步主函数
if __name__ == "__main__":
    asyncio.run(main())

结果

2024年巴黎奥运会吉祥物是弗里热(Phryge),灵感源于法国弗雷吉亚帽。当前时间为2025年7月29日15:33。(注:该时间应为假设性回答,实际需实时查询。)

七、记忆

1.手动prompt内增加记忆

  • 因为prompt内有chat_history参数,用于增加历史对话记录,可手动增加
  • 示例中没有使用到工具

代码

# 与上例内容一样,只是不再使用verbose=True参数,不显示操作过程
...
from langchain_core.messages import AIMessage, HumanMessage
response = agent_executor.invoke(
    {
        "chat_history": [
            HumanMessage(content="Hi,我的名字是Jack"),
            AIMessage(content="你好Jack,很高兴见到你!有什么我可以帮助你的吗?"),
        ],
        "input": "我的名字是什么?",
    }
)
print(response)

结果

{
    "chat_history": [
        HumanMessage(
            content="Hi,我的名字是Jack", additional_kwargs={}, response_metadata={}
        ),
        AIMessage(
            content="你好Jack,很高兴见到你!有什么我可以帮助你的吗?",
            additional_kwargs={},
            response_metadata={},
        ),
    ],
    "input": "我的名字是什么?",
    "output": "你刚刚告诉我你的名字是Jack,所以我记得你的名字是Jack!有什么需要我帮忙的吗?",
}

2.使用ChatMessageHistory和RunnableWithMessageHistory保存信息

  • 构建消息存储方法
    • 自建一个get_session_history()方法,用于将对话记录保存到自定义的store[session_id]中
    • session_id参数,用于指定需要保存的session_id。
    • 返回为ChatMessageHistory实例,LangChain提供的保存对象。
  • 创建一个带会话历史记录的Runnable(链条)
    • 使用RunnableWithMessageHistory()方法创建一个可以输入历史记录的链条(Runnable)对象。
    • runnable参数:(必需) 要包装的基础可运行对象,可以是任何实现了 Runnable 接口的 LangChain 组件
    • get_session_history参数 (必需) 工厂函数,接收session_id并返回对应的聊天消息历史存储对象的函数名称
    • input_messages_key 参数:(可选) 指定用户消息输入字典中的键名
    • history_messages_key参数:(可选) 指定历史消息在输入字典中的键名

代码

from langchain_openai import ChatOpenAI
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_tavily import TavilySearch
from langchain.tools.retriever import create_retriever_tool
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain import hub
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain.agents import create_tool_calling_agent, AgentExecutor

############################创建模型#####################################
model = ChatOpenAI(
    model="Pro/deepseek-ai/DeepSeek-V3",
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
    temperature=0.7,
    max_tokens=8000,
)
embeddings = HuggingFaceEmbeddings(
    model_name="D:/python/envs/lc/hfmodel/sentence-transformers/all-MiniLM-L6-v2",
)

######################创建工具代理执行器################################
search_tool = TavilySearch(
    max_results=1, tavily_api_key="************"
)
loader = WebBaseLoader("https://zh.wikipedia.org/wiki/%E7%8C%AB")
docs = loader.load()
docs_list = RecursiveCharacterTextSplitter(
    chunk_size=1000, chunk_overlap=200
).split_documents(docs)
vector = FAISS.from_documents(docs_list, embeddings)
retriever = vector.as_retriever()
retriever_tool = create_retriever_tool(
    retriever=retriever,
    name="wiki_search",
    description="搜索维基百科",
)
tools = [search_tool, retriever_tool]
prompt = hub.pull("hwchase17/openai-functions-agent")
agent = create_tool_calling_agent(model, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools)

######################创建历史记录代理################################
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]
agent_with_chat_history = RunnableWithMessageHistory(
    runnable=agent_executor,
    get_session_history=get_session_history,
    input_messages_key="input",
    history_messages_key="chat_history",
)

######################历史记录代理推理################################
response = agent_with_chat_history.invoke(
    {"input": "Hi,我的名字是Jack"},
    config={"configurable": {"session_id": "123"}},
)
print(response)

response = agent_with_chat_history.invoke(
    {"input": "我叫什么名字?"},
    config={"configurable": {"session_id": "123"}},
)
print(response)

response = agent_with_chat_history.invoke(
    {"input": "我叫什么名字?"},
    config={"configurable": {"session_id": "456"}},
)
print(response)

# 也可以链式调用
chain = agent_with_chat_history | (
    lambda response: {"output": response["output"]}
)
print(response)

结果

{
    "input": "Hi,我的名字是Jack",
    "chat_history": [],
    "output": "Hi Jack! Nice to meet you. How can I assist you today?",
}
###################
{
    "input": "我叫什么名字?",
    "chat_history": [
        HumanMessage(
            content="Hi,我的名字是Jack", 
            additional_kwargs={}, 
            response_metadata={}
        ),
        AIMessage(
            content="Hi Jack! Nice to meet you. How can I assist you today?",
            additional_kwargs={},
            response_metadata={},
        ),
    ],
    "output": "你刚才告诉我你的名字是Jack,有什么我可以帮你的吗?",
}
###################
{
    "input": "我叫什么名字?",
    "chat_history": [],
    "output": "我无法知道你的真实名字,因为我没有存储或记忆个人信息的功能。如果你愿意,可以告诉我你的名字,我会尽力帮助你!或者,如果你有其他问题,我也很乐意为你解答。",
}
###################
{
    "output": "你叫 **Jack**。\n\n2024年巴黎奥运会的吉祥物是 **弗里吉帽(Phryges)**。\n\n现在的时间是 **2025年7月29日,16时15分45秒**。如果你还有其他问题,随时问我哦! 😊",
}

八、Agent使用MCP工具

1.使用FastMCP创建一个MCP服务端

  • 可以使用stdio或sse方式
  • 示例:
    • 使用stdio创建一个算数MCP,需要在客户端指明环境执行路径的python.exe
    • 使用sse创建一个天气MCP,需要运行。

math_server.py代码

from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Math")

@mcp.tool()
def xx(a: int, b: int) -> int:
    """对两个整数xx运算"""
    return a + b + 1

@mcp.tool()
def add(a: int, b: int) -> int:
    """对两个整数相加"""
    return a + b

@mcp.tool()
def multiple(a: int, b: int) -> int:
    """对两个整数相乘"""
    return a * b

if __name__ == "__main__":
    mcp.run(transport="stdio")          # 使用标准输入输出

weather_server.py代码,需要执行:python.exe weather_server.py

from mcp.server.fastmcp import FastMCP
mcp = FastMCP(name="Weather",port=8021)

@mcp.tool()
async def get_weather(location: str) -> str:
    """获取位置的天气。"""
    return f"{location}当前天气晴朗,温度 33325°C"

if __name__ == "__main__":
    mcp.run(transport="sse")            # 使用SSE 传输,默认端口8021

2.Agent使用MCP客户端作为工具

  • 下载langchain的MCP插件:pip install langchain-mcp-adapters
  • 使用MultiServerMCPClient()配置Agent的MCP客户端(MCP服务端信息)
    • 使用MultiServerMCPClient()配置MCP客户端返回MCP服务端信息对象。
    • 参数为MCP服务端信息字典
    • MultiServerMCPClient()可以配置多个MCP客户端
  • 返回MCP服务端信息对象后,使用get_tools()获取工具对象列表
  • 使用LangGraph的create_react_agent()配置Agent执行器的MCP客户端(MCP服务端信息)
    • 参数model为模型对象
    • 参数tools为工具对象列表
    • 参数prompt为Agent的提示词(有默认值可不填写,变量为messages)
    • 返回一个工具代理执行器对象agent_executor(不是代理agent)。
  • 异步调用示例

代码

from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableLambda
import asyncio

# 创建模型
model = ChatOpenAI(
    model="doubao-seed-1-6-flash-250615",
    openai_api_key="************",
    openai_api_base="https://ark.cn-beijing.volces.com/api/v3",
)

# 定义异步函数来处理agent
async def run_agent_chain():
    # 初始化MCP客户端
    client = MultiServerMCPClient({
        "math": {
            "command": "C:\\Users\\62845\\.conda\\envs\\mcp_test\\python.exe",
            "args": [r"D:\python\envs\mcp_test\math.py"],
            "transport": "stdio",
        },
        "weather": {
            "url": "http://localhost:8021/sse",
            "transport": "sse",
        },
    })
    
    # 创建工具代理执行器对象agent_executor
    tools = await client.get_tools()
    agent_executor = create_react_agent(model, tools)

################################################
    # 可以直接使用代理执行器执行
    # math_response = await agent_executor.ainvoke({"messages": "请问3和5进行xx运算,结果多少?"})
    # print("Math Response:", math_response["messages"][-1].content)
    # weather_response = await agent_executor.ainvoke({"messages": "请问北京今天天气怎么样?"})
    # print("Weather Response:", weather_response["messages"][-1].content)
    # response = await agent_executor.ainvoke({"messages": "请问北京今天天气怎么样?3和5进行xx运算结果多少?"})
    # print(response)
    
################################################
    # 定义agent处理函数
    async def process_with_agent(inputs):
        result = await agent_executor.ainvoke({"messages": inputs["user_input"]})
        return result["messages"][-1].content

    # 创建后处理提示模板
    post_prompt = ChatPromptTemplate.from_template(
        "请将以下agent回复整理成简洁的合理的最终答案:{agent_response}"
    )
    
    # 创建输出解析器
    parser = StrOutputParser()
    
    # 构建完整的链
    chain = (
        RunnableLambda(process_with_agent)
        | {"agent_response": lambda x: x}
        | post_prompt
        | model
        | parser
    )
    
    # 测试链
    result = await chain.ainvoke({"user_input": "请问北京今天天气怎么样?3和5进行xx运算多少?"})
    return result

# 运行示例
if __name__ == "__main__":
    result = asyncio.run(run_agent_chain())
    print(f"最终输出: {result}")

结果

最终输出: 北京今天天气晴朗,但温度888°C不符合实际情况;3和5进行xx运算结果为9不符合正常运算逻辑(3与5常见运算如加为8、乘为15等,非9)

3.mcp的uvx的json数据改为MultiServerMCPClient()里的客户端设置

  • 网络上提供的mcp,使用mcp-server-sqlite为例
  • 原uvx的json数据

json数据

{
  "mcpServers": {
    " sqlite": {
      "command": "uv",
      "args": [
        "--directory", 
        "parent_of_servers_repo/servers/src/sqlite",
        "run",
        " mcp-server-sqlite" ,
        "--db-path",
        "~/test.db"
      ],
    }
  }
}
  • 先用conda环境安装mcp-server-sqlite模块:
pip install mcp-server-sqlite
  • 直接用python.exe代替uv,如果失败,是因为:mcp_server_sqlite 这个包没有 main.py,所以不能直接 python -m mcp_server_sqlite
  • 查找包的入口点:pip show mcp-server-sqlite
    • Location 字段显示目录:C:\Users\62845.conda\envs\mcp_test\Lib\site-packages\
    • 在这里找mcp_server_sqlite目录,并检查有没有cli.py、init.py文件
  • 再打开环境Scripts文件夹:C:\Users\62845.conda\envs\mcp_test\Scripts
    • 里面应该有一个叫mcp-server-sqlite.exe或mcp-server-sqlite-script.py的文件。
  • 如果有mcp-server-sqlite-script.py文件,打开:

mcp-server-sqlite-script.py代码

from mcp_server_sqlite.cli import app
...
if __name__ == "__main__":
    app()
  • 可以用:python -m mcp_server_sqlite.cli
  • 或用:python -c “import mcp_server_sqlite.cli as cli; cli.app()”
  • 相当于:uvx run mcp-server-sqlite
  • 结果是:

代码

...
client = MultiServerMCPClient({
    ...
    "sqlite": {
        "command": "C:\\Users\\62845\\.conda\\envs\\mcp_test\\python.exe",
        "args": [
            "-m",
            "mcp_server_sqlite.cli",
            "--db-path",
            "D:\\python\\envs\\langchain2\\db\\database.db",
        ],
        "transport": "stdio",
    },
})
...
  • 如果有mcp-server-sqlite.exe文件,直接用这个exe代替python:
C:\Users\62845\.conda\envs\mcp_test\Scripts\mcp-server-sqlite.exe
  • 结果是:

代码

...
client = MultiServerMCPClient({
    ...
    "sqlite": {
        "command": "C:\\Users\\62845\\.conda\\envs\\mcp_test\\Scripts\\mcp-server-sqlite.exe",
        "args": [
            "--db-path",
            "D:\\python\\envs\\langchain2\\db\\database.db",
        ],
        "transport": "stdio",
    },
})
...

总结

本文整理了 LangChain 工具调用 Agent 的核心知识,包括工具创建、工具列表、模型绑定工具、工具代理、工具代理执行器、ReAct Agent、带记忆的 Agent,以及通过 MCP 接入外部工具服务。实际开发中,建议根据模型是否支持原生 tool calling 来选择 create_tool_calling_agent 或 create_react_agent;如果需要更强的状态控制、分支流程和可视化调试,可以进一步使用 LangGraph 构建更灵活的 Agent 工作流。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐