Appearance
前面的 RAG 让模型「会查资料」,但它本质还是被动的——只会检索加回答。Agent(智能体) 更进一步:让模型自己判断「要不要调用工具、调用哪个、用什么参数」,拿到结果再继续思考,直到能回答为止。这一篇讲 Agent 的核心:工具(Tool)、ReAct 循环,以及用官方的 create_agent / createAgent 几行搭出一个会用工具的 Agent。顶部可切换 Python / Node.js。
本系列基于 LangChain 1.x / LangChain.js 1.0。它们都把
create_agent/createAgent作为构建 Agent 的推荐入口,两种语言几乎对称。
Agent 是怎么「思考」的:ReAct
最经典的 Agent 范式叫 ReAct(Reasoning + Acting,边推理边行动)。它是一个循环:
模型收到问题后先思考「我需要什么信息?」→ 决定调用某个工具(比如查天气)→ 系统执行工具、把结果返回给模型 → 模型看到结果继续思考「够了吗?」→ 不够就再调工具,够了就给出最终回答。
你不用手写这个循环——create_agent 内部就是这套逻辑。你只需要两件事:准备好模型、准备好工具。
工具就是一个带说明的函数
工具本质是个普通函数,但要给它写清楚名字、说明、参数——因为模型是靠这段「说明书」来判断什么时候该用它的。说明写得越清楚,模型用得越准。Python 用 @tool 装饰器,Node 用 tool() 配合 zod 定义参数。
python
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""查询指定城市的当前天气。参数 city 是城市名,比如“杭州”。"""
# 真实场景这里会调天气 API,这里先写死方便演示
return f"{city}今天晴,26℃,微风。"
@tool
def calculator(expression: str) -> str:
"""计算一个数学表达式,例如 '15 * 23'。"""
return str(eval(expression)) # 演示用;生产别直接 eval 用户输入javascript
import { tool } from "@langchain/core/tools";
import { z } from "zod";
const getWeather = tool(
async ({ city }) => `${city}今天晴,26℃,微风。`, // 真实场景调天气 API
{
name: "get_weather",
description: "查询指定城市的当前天气。参数 city 是城市名,比如“杭州”。",
schema: z.object({ city: z.string() }),
}
);
const calculator = tool(
async ({ expression }) => String(eval(expression)), // 演示用;生产别直接 eval
{
name: "calculator",
description: "计算一个数学表达式,例如 '15 * 23'。",
schema: z.object({ expression: z.string() }),
}
);组装 Agent
把模型和工具交给 create_agent,一个会思考、会调工具的 Agent 就好了。
python
import os
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
model="qwen-plus",
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
temperature=0,
)
agent = create_agent(model=model, tools=[get_weather, calculator])
# 一个问题里同时需要「查天气」和「算数」两个工具
result = agent.invoke({
"messages": [{"role": "user", "content": "杭州天气怎么样?顺便帮我算一下 15 乘 23。"}]
})
print(result["messages"][-1].content) # 最后一条是 Agent 的最终回答javascript
import { createAgent } from "langchain";
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({
model: "qwen-plus",
apiKey: process.env.DASHSCOPE_API_KEY,
temperature: 0,
configuration: { baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1" },
});
const agent = createAgent({ model, tools: [getWeather, calculator] });
// 一个问题里同时需要「查天气」和「算数」两个工具
const result = await agent.invoke({
messages: [{ role: "user", content: "杭州天气怎么样?顺便帮我算一下 15 乘 23。" }],
});
console.log(result.messages[result.messages.length - 1].content); // 最后一条是最终回答运行后,Agent 会自动判断出这句话需要先调 get_weather("杭州")、再调 calculator("15*23"),然后把两个结果综合成一句回答。整个「调用哪些工具、按什么顺序」都是模型自己决定的——这就是 Agent 和普通链的本质区别。
看清 Agent 的「内心戏」:流式观察每一步
invoke 只给最终答案。想看 Agent 中间「想了什么、调了什么工具」,用 stream 逐步观察。这对调试特别有用。
python
for step in agent.stream(
{"messages": [{"role": "user", "content": "杭州天气怎么样?顺便算一下 15*23。"}]},
stream_mode="values",
):
step["messages"][-1].pretty_print() # 打印每一步的最新消息(含工具调用与结果)javascript
const stream = await agent.stream(
{ messages: [{ role: "user", content: "杭州天气怎么样?顺便算一下 15*23。" }] },
{ streamMode: "values" }
);
for await (const step of stream) {
const last = step.messages[step.messages.length - 1];
// 有 tool_calls 说明模型决定调工具;有 content 说明是思考或最终回答
console.log(last.tool_calls?.length ? `🔧 调用工具: ${JSON.stringify(last.tool_calls)}` : last.content);
}你会看到类似这样的过程:模型先发出「调用 get_weather」的指令 → 工具返回天气 → 模型再发出「调用 calculator」 → 返回结果 → 模型给出综合回答。这就是 ReAct 循环在眼前跑起来的样子。
给 Agent 设定角色
和聊天一样,可以给 Agent 一段系统提示词,规定它的身份、语气、行为边界。create_agent 用 system_prompt / prompt 参数传入。
python
agent = create_agent(
model=model,
tools=[get_weather, calculator],
system_prompt="你是一个简洁的生活助手,回答控制在两句话以内。",
)javascript
const agent = createAgent({
model,
tools: [getWeather, calculator],
prompt: "你是一个简洁的生活助手,回答控制在两句话以内。",
});小结与预告
这一篇你迈出了从「会答」到「会做」的一步:理解了 ReAct 循环、学会了定义工具、并用 create_agent 搭出了一个能自主调用多个工具的 Agent,还能用 stream 观察它的思考过程。
现在的 Agent 还比较「素」。真实项目里,我们希望在它每次调用工具前后插入自己的逻辑(记录日志、做权限校验、监控耗时、按场景切换提示词)。下一篇《Agent 进阶》,就来讲多工具编排和中间件(middleware)——这是让 Agent 真正可控、可上生产的关键机制。