构建智能体

在之前的笔记中,我们学习了如何调用基础模型、构建提示词、结构化输出以及使用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模式由以下几个步骤组成:

  1. Thought(思考):LLM分析当前任务,决定下一步做什么
  2. Action(行动):LLM决定调用某个工具,并给出调用参数
  3. Observation(观察):工具执行完毕,返回结果
  4. 重复:LLM基于观察结果继续思考,决定是再次调用工具还是直接给出最终答案
  5. 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则使用提供商原生的结构化输出能力,若不可用会自动回退到ToolStrategyAutoStrategy则会自动选择最合适的策略。

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
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。