前面几篇讲了工具(tool)和结构化输出。把它们串起来,就组成了 Agent(智能体):让模型自己「思考 → 调用工具 → 看结果 → 再思考」,直到解决问题。手动实现这个循环要写 bindTools、执行工具、塞回 ToolMessage……很啰嗦。LangChain 提供了 createAgent(Python 里叫 create_agent),把整个循环打包成一个对象。这篇讲清楚它怎么创建、怎么调用。
一、Agent 与 createAgent
Agent 的本质是一个 ReAct 循环:
用户提问
-> 模型决策(可能需要调用工具,输出 tool_calls)
-> 执行工具,把结果塞回消息历史
-> 模型再决策(继续调用 or 给出最终答案)
-> 重复,直到不再调用工具
createAgent 做的事情,就是把这个循环封成一个「可以 invoke 的对象」。你只需要提供模型、工具、系统提示词,剩下的编排它全包了:
import { createAgent, tool } from "langchain";
const agent = createAgent({
model: "deepseek:deepseek-chat",
tools: [getWeather],
systemPrompt: "你是一个简洁的中文助手,需要实时信息时请调用工具。",
});
对比一下:手动循环(见 demo-09)要自己 bindTools、遍历 tool_calls、构造 ToolMessage、再循环调用;createAgent 之后只剩这一处配置。
二、创建:createAgent 的参数
const agent = createAgent({
model: "deepseek:deepseek-chat", // 模型:字符串或模型实例
tools: [getWeather], // 工具数组,用 tool() 定义
systemPrompt: "你是简洁的中文助手", // 系统提示词(字符串或 SystemMessage)
checkpointer: new MemorySaver(), // 可选:多轮记忆
});
| 参数 | 作用 |
|---|---|
model | 模型。可以是 "provider:model" 字符串,也可以是配置好的模型实例 |
tools | 工具数组,每个工具用 tool(fn, { name, description, schema }) 定义 |
systemPrompt | 系统提示词,设定 agent 的角色和行为规范 |
checkpointer | 可选,传入记忆存储后支持多轮对话(见第五节) |
responseFormat | 可选,让 agent 直接产出结构化结果(见 demo-10) |
一个工具长这样(和 demo-09 完全一致):
const getWeather = tool(
({ city }) => `${city} 今天晴,气温 25℃`,
{
name: "get_weather",
description: "查询指定城市今天的天气",
schema: z.object({ city: z.string().describe("城市名,例如 北京") }),
}
);
三、调用:invoke 与 stream
创建好之后,用 invoke 或 stream 调用。两者输入相同:一个 { messages: [...] } 的 state。
3.1 invoke:一次性拿到完整结果
const res = await agent.invoke({
messages: [{ role: "user", content: "北京今天天气怎么样?" }],
});
console.log(res.messages.map((m) => m.type).join(" -> "));
// human -> ai -> tool -> ai
console.log(res.messages.at(-1).content);
// 北京今天天气晴朗,气温 25℃。适合外出,记得做好防晒。
返回值是一个 state,核心是 messages 数组,里面记录了整个循环过程:
human:用户输入;ai:模型的决策(可能带tool_calls);tool:工具执行结果;ai:最终的纯文本答案。
中间那条 ai 消息里的 tool_calls 就是模型「请求调用工具」的证据:
// res.messages[1].tool_calls
[{ name: "get_weather", args: { city: "北京" }, id: "call_xxx", type: "tool_call" }]
3.2 stream:流式拿到结果
stream 返回一个异步可迭代对象,适合做打字机效果或观察执行过程。常用的两种 streamMode:
streamMode: "messages"——逐 token 流式输出:
const stream = await agent.stream(
{ messages: [{ role: "user", content: "用一句话介绍你自己" }] },
{ streamMode: "messages" }
);
for await (const [chunk] of stream) {
if (chunk.content) process.stdout.write(chunk.content);
}
每个 chunk 是 [消息, 元数据],把 chunk.content 拼起来就是完整回复。
streamMode: "updates"——逐节点返回,观察 ReAct 循环:
const stream = await agent.stream(
{ messages: [{ role: "user", content: "上海今天天气怎么样?" }] },
{ streamMode: "updates" }
);
for await (const step of stream) {
console.log(Object.keys(step)); // ["model_request"] -> ["tools"] -> ["model_request"]
}
可以看到节点按 model_request → tools → model_request 推进,正是 ReAct 循环的每一步。
需要更细粒度的事件(单个 token、工具调用开始/结束等)可以用
agent.streamEvents(...)。
四、调用:多轮对话(记忆)
默认情况下每次 invoke 都是独立的,agent 不记得上一轮。 要让它记住,需要两步:
- 创建时挂一个
checkpointer(记忆存储); - 调用时用
thread_id区分会话。
import { createAgent } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
const agent = createAgent({
model: "deepseek:deepseek-chat",
tools: [],
systemPrompt: "你是助手,请记住用户告诉你的信息。",
checkpointer: new MemorySaver(), // 挂上记忆
});
const config = { configurable: { thread_id: "user-1" } };
await agent.invoke(
{ messages: [{ role: "user", content: "我叫小明,住在杭州。" }] },
config
);
const res = await agent.invoke(
{ messages: [{ role: "user", content: "我叫什么?住在哪?" }] },
config
);
console.log(res.messages.at(-1).content);
// 你叫小明,住在杭州。
要点:
- 同一个
thread_id= 同一段会话,历史会自动累积,agent 能记住上下文; - 换一个
thread_id就是另一段独立会话,互不影响; MemorySaver存在内存里,进程结束就没了;生产环境换成数据库/Redis 等持久化 checkpointer。
五、Python 对应写法
Python 里函数名是 create_agent,参数名基本一致(model / tools / system_prompt),调用方式也一样:
from langgraph.checkpoint.memory import InMemorySaver
from langchain.agents import create_agent
agent = create_agent(
model="deepseek:deepseek-chat",
tools=[get_weather],
system_prompt="你是一个简洁的中文助手,需要实时信息时请调用工具。",
checkpointer=InMemorySaver(),
)
result = agent.invoke({"messages": [{"role": "user", "content": "北京今天天气怎么样?"}]})
print(result["messages"][-1].content)
六、小结
创建:createAgent({ model, tools, systemPrompt, checkpointer })——给「模型 + 工具 + 系统提示词」,它自动完成整个 ReAct 循环。
调用:
agent.invoke(input[, config]):一次性拿到完整 state,看res.messages;agent.stream(input, { streamMode }):流式。"messages"逐 token,"updates"逐节点;agent.streamEvents(...):更细粒度的事件流。
多轮:挂 checkpointer + 用 config.configurable.thread_id 区分会话。
一句话:createAgent = 把「模型决策 + 工具执行」的循环编排打包成一个可 invoke 的对象,你负责给配置、给输入,它负责把 ReAct 循环跑完。