构建智能体
在之前的笔记中,我们学习了如何调用基础模型、构建提示词、结构化输出以及使用LCEL链式调用,这些都是LangChain的基础组件。而在实际AI应用中,我们往往需要的不只是一次性的问答,而是能够根据用户意图自主决策、调用工具、观察结果并循环推进的智能体(Agent)。这篇笔记我们将介绍LangChain 1.x中构建智能体的核心机制,reAct流程,以及新版create_agent函数的使用。
什么是智能体
智能体(Agent)是构建在LLM之上的一个“决策循环系统”。与普通ChatModel输入 -> 输出的单次调用不同,智能体可以实现:
- 根据用户意图,自主选择并调用工具(如搜索、查询数据库、执行代码等)
- 观察工具的返回结果
- 基于结果进行下一步决策
- 循环上述过程,直到完成任务或触发停止条件
LangChain 1.x中,智能体底层基于LangGraph构建,这意味着它天然支持持久化状态、流式输出、human-in-the-loop等高级特性,内置的reAct智能体其实已经能解决绝大部分问题,而我们使用它时并不需要直接与底层的LangGraph打交道。
reAct流程
reAct(Reasoning + Acting)是目前最主流的智能体执行模式,它让LLM的“推理”和“行动”交替进行,形成一个循环。
reAct模式由以下几个步骤组成:
- Thought(思考):LLM分析当前任务,决定下一步做什么
- Action(行动):LLM决定调用某个工具,并给出调用参数
- Observation(观察):工具执行完毕,返回结果
- 重复:LLM基于观察结果继续思考,决定是再次调用工具还是直接给出最终答案
- Final Answer(最终答案):LLM判断信息已足够,输出最终回答给用户
flowchart TD
A([用户输入]) --> B[LLM推理(Thought)]
B --> C{是否需要调用工具?}
C -- 是 --> D[选择工具并生成参数(Action)]
D --> E[执行工具]
E --> F[获取工具返回结果(Observation)]
F --> B
C -- 否 --> G[生成最终回答(Final Answer)]
G --> H([输出给用户])
style A fill:#4a9eff,color:#fff,stroke:none
style H fill:#4a9eff,color:#fff,stroke:none
style G fill:#22c55e,color:#fff,stroke:none
style E fill:#f59e0b,color:#fff,stroke:none
以“查询洛杉矶今天的天气”为例,一次完整的reAct执行过程是下面这样的。
用户输入: 查询洛杉矶今天的天气如何?
Thought: 用户想知道洛杉矶的天气,我需要调用天气查询工具。
Action: get_weather
Action Input: {"city": "Los Angeles"}
Observation: 晴 14℃
Thought: 我已经获得了洛杉矶的天气信息,可以直接回答用户了。
Final Answer: 洛杉矶今天天气晴朗,气温14℃。
可以看到,在这个例子中,LLM经历了一次思考 -> 行动 -> 观察 -> 输出的完整循环。对于更复杂的任务,这个循环可能会重复多次,例如需要先查询某个数据再基于结果进行计算等场景,不过LLM会自动完成这一切,直到获得最终结果。
create_agent()创建智能体
LangChain 1.x提供了全新的create_agent()函数,这是当前推荐的智能体创建方式,它底层基于LangGraph实现,相比早期版本的initialize_agent()或AgentExecutor大幅简化了使用方式。
基本使用
下面例子在第一章已经出现过了,它演示了create_agent()的基本使用,以及如何为智能体绑定工具。我们可以执行下面例子,并在LangSmith中观察reAct循环。
from langchain.agents import create_agent
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
@tool
def get_weather(city: str) -> str:
"""Get current weather for a given city.
Args:
city: City name, for example "Los Angeles" or "Beijing".
Returns:
A weather summary string.
"""
return "晴 14℃"
model = ChatOpenAI(
model="qwen3:30b-a3b",
base_url="http://localhost:11434/v1/",
api_key="dummy",
temperature=1,
top_p=1,
max_tokens=16384,
timeout=120,
max_retries=6
)
agent = create_agent(
model=model,
tools=[get_weather],
system_prompt="You are a helpful AI assistant",
)
resp = agent.invoke(
{"messages": [{"role": "user", "content": "查询洛杉矶今天的天气如何?"}]}
)
print(resp["messages"][-1].content)
create_agent 参数说明
model:智能体使用的语言模型,支持两种传入方式。第一种是直接传入模型实例,即我们通过ChatOpenAI等类创建的对象。
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="qwen3:30b-a3b", ...)
agent = create_agent(model=model, ...)
第二种是传入模型标识符字符串,LangChain会自动推断并初始化,例如"openai:gpt-4o"、"anthropic:claude-opus-4-6"等。这种方式更简洁,但需要相应的Provider包已安装,且API Key通过环境变量配置好。
agent = create_agent(model="openai:gpt-4o", ...)
tools:工具列表,传入用@tool装饰器标注的函数列表。工具的docstring注释会被作为工具描述传递给LLM,LLM据此判断何时调用哪个工具,因此编写详细清晰的docstring非常重要。
system_prompt:系统提示词,接受字符串或SystemMessage对象。
调用智能体
create_agent()返回的智能体本身是一个LangGraph图,它遵循LangGraph的接口规范,支持invoke()、stream()等方法调用。
下面例子采用阻塞式调用,一次性阻塞获取完整的执行结果。
resp = agent.invoke(
{"messages": [{"role": "user", "content": "查询洛杉矶今天的天气如何?"}]}
)
print(resp["messages"][-1].content)
下面例子采用流式调用。
for chunk in agent.stream(
{"messages": [{"role": "user", "content": "查询洛杉矶今天的天气如何?"}]}
):
print(chunk)
下面例子采用异步流式调用,主要用于服务端高并发场景。
async for chunk in agent.astream(
{"messages": [{"role": "user", "content": "查询洛杉矶今天的天气如何?"}]}
):
# 处理每个输出片段
pass
结构化输出
前面章节我们曾介绍过基于基础模型ChatModel的结构化输出。而对于create_agent()创建的智能体,它则支持通过response_format参数实现这一点。对于智能体,和之前类似,LangChain提供了两种策略,ToolStrategy通过“假装”调用工具的方式生成结构化输出,适用于任何支持工具调用的模型;ProviderStrategy则使用提供商原生的结构化输出能力,若不可用会自动回退到ToolStrategy;AutoStrategy则会自动选择最合适的策略。
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
from langchain.agents import create_agent
model = ChatOpenAI(
model="qwen3:30b-a3b",
base_url="http://localhost:11434/v1/",
api_key="dummy",
temperature=1,
top_p=1,
max_tokens=16384,
timeout=120,
max_retries=6
)
class ContactInfo(BaseModel):
"""Contact information for a person."""
name: str = Field(description="The name of the person")
email: str = Field(description="The email address of the person")
phone: str = Field(description="The phone number of the person")
agent = create_agent(
model=model,
response_format=ContactInfo
)
result = agent.invoke({
"messages": [{"role": "user", "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567"}]
})
print(result["structured_response"])
代码中,response_format参数我们直接传了Pydantic对象,实际上它会被自动使用AutoStrategy包装。代码执行后,最终会返回Pydantic对象。
工具定义最佳实践
工具的定义质量直接影响智能体的表现,这里我们介绍一些推荐的编写方式。
使用@tool装饰器,它会自动从函数名和docstring提取工具的名称和描述。
from langchain_core.tools import tool
@tool
def search_product(product_name: str, category: str = "all") -> str:
"""Search for a product in the database.
Args:
product_name: The name of the product to search for.
category: Product category filter. Defaults to "all".
Returns:
Product information as a string, or error message if not found.
"""
# 实际逻辑...
return f"Found: {product_name} in {category}"
工具函数建议指明参数和返回值类型,LangChain会自动根据类型注解生成工具的参数schema。
@tool
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email to a specified recipient.
Args:
to: Recipient email address.
subject: Email subject line.
body: Email body content.
Returns:
Confirmation message indicating success or failure.
"""
# 实际发送逻辑...
return f"Email sent to {to}"
工具定义完成后,我们可以使用以下方式查看工具的元信息。
print(search_product.name) # 工具名称
print(search_product.description) # 工具描述
print(search_product.args) # 参数schema