预计时间
2 周
学习目标
- 理解 ReAct 循环
- 掌握 Tool Calling 实操
- 理解 Planning / Memory / Reflection
- 能用 LangGraph 搭建 Agent
一、Agent 是什么?
text
普通 LLM:
用户:"帮我查一下深圳天气,然后决定穿什么"
LLM:"深圳今天 28 度,建议穿短袖" ← 它猜的,没真查
Agent:
用户:"帮我查一下深圳天气,然后决定穿什么"
Agent → 思考:"我需要先查天气"
Agent → 调 get_weather("深圳") → 拿到真实数据
Agent → 思考:"28 度,晴天"
Agent → 输出:"深圳今天 28 度晴天,建议穿短袖加薄外套" ← 基于真实数据一句话
Agent = LLM + 工具 + 循环决策 LLM 是大脑,工具是手脚,循环是不断尝试直到完成任务。
二、ReAct 模式
ReAct = Reasoning(推理)+ Acting(行动),是 Agent 最核心的运作模式。
text
循环:
Thought → Action → Observation → Thought → Action → ... → Answer
示例:用户问"公司去年利润多少?"
Thought: 我需要查公司去年的财务数据
Action: search_knowledge_base("2025 年利润")
Observation: 找到 3 条记录,其中一条显示"2025 年净利润 5000 万"
Thought: 找到了,可以直接回答
Answer: 公司 2025 年净利润为 5000 万元。
如果没找到:
Thought: 知识库里没有去年的数据,试试查"年度报告"
Action: search_knowledge_base("年度报告 2025")
Observation: 找到年度报告链接
Thought: 可以先给用户报告链接
Answer: 我没有找到直接的利润数据,但这里有一份 2025 年年度报告...三、Tool Calling 实战
3.1 定义工具
javascript
const tools = [
{
name: 'search_knowledge_base',
description: '搜索企业知识库',
parameters: {
type: 'object',
properties: {
query: { type: 'string' },
limit: { type: 'number', default: 5 },
},
required: ['query'],
},
execute: async ({ query, limit }) => {
const embedding = await getEmbedding(query);
return await vectorDB.search(embedding, limit);
},
},
{
name: 'send_email',
description: '发送邮件',
parameters: {
type: 'object',
properties: {
to: { type: 'string' },
subject: { type: 'string' },
body: { type: 'string' },
},
required: ['to', 'subject', 'body'],
},
execute: async ({ to, subject, body }) => {
// 敏感操作,需要用户确认
console.log(`准备发送邮件给 ${to},主题:${subject}`);
return { status: 'sent' };
},
},
{
name: 'calculator',
description: '执行数学计算',
parameters: {
type: 'object',
properties: {
expression: { type: 'string', description: '数学表达式,如 "2+3*4"' },
},
required: ['expression'],
},
execute: async ({ expression }) => {
return eval(expression); // 生产环境不要这样!仅 demo
},
},
];3.2 Agent 循环
javascript
async function agentLoop(userQuery, maxSteps = 10) {
const messages = [
{ role: 'system', content: `你是一个智能助手。可以使用工具来获取信息。
遇到不确定的事,先查工具再回答。不要编造信息。` },
{ role: 'user', content: userQuery },
];
for (let step = 0; step < maxSteps; step++) {
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages,
tools: tools.map(t => ({
type: 'function',
function: { name: t.name, description: t.description, parameters: t.parameters },
})),
});
const msg = response.choices[0].message;
// 没有 tool call → LLM 决定回答了
if (!msg.tool_calls) {
return msg.content;
}
// 有 tool call → 执行工具
for (const tc of msg.tool_calls) {
const tool = tools.find(t => t.name === tc.function.name);
const args = JSON.parse(tc.function.arguments);
console.log(`[Step ${step}] 调用 ${tc.function.name}(${JSON.stringify(args)})`);
try {
const result = await tool.execute(args);
messages.push({
role: 'tool',
tool_call_id: tc.id,
content: JSON.stringify(result),
});
} catch (error) {
messages.push({
role: 'tool',
tool_call_id: tc.id,
content: JSON.stringify({ error: error.message }),
});
}
}
}
return '抱歉,任务太复杂,我没能在限定步数内完成。';
}
// 使用
const answer = await agentLoop('帮我查一下公司去年的利润,算一下利润率');四、Memory(记忆)
text
三层记忆:
1. 短期记忆 = 当前对话历史
在 messages 数组里,受 Context Window 限制
2. 长期记忆 = 跨对话的信息
存在 Vector DB 或数据库中
下次对话前,检索相关历史
3. 工作记忆 = 当前任务的中间状态
正在处理的文件、当前的搜索关键词javascript
// 长期记忆示例
async function saveMemory(userId, content, metadata = {}) {
const embedding = await getEmbedding(content);
await db.memories.create({
data: {
userId,
content,
embedding: pgvector.toSql(embedding),
metadata,
},
});
}
async function recallMemory(userId, query) {
const embedding = await getEmbedding(query);
const memories = await db.$queryRaw`
SELECT content, metadata
FROM memories
WHERE user_id = ${userId}
ORDER BY embedding <=> ${embedding}::vector
LIMIT 5
`;
return memories;
}五、Reflection(自我修正)
让 Agent 在行动后"反思"自己的表现:
text
三种 Reflection 模式:
1. 执行后反思
Agent 完成一个任务 → 检查结果 → 不满意 → 重新做
"我刚查到的资料里没有利润数据,换个关键词再查"
2. 关键决策点暂停
涉及敏感操作 → 先解释理由 → 等待人工确认
"我需要发送一封邮件给全体,确认后执行?"
3. 多轮辩论
两个 Agent 角色互相质疑
Agent A: "方案 1 更好,因为快"
Agent B: "但方案 2 更安全,因为..."
→ 得出更周全的结论六、LangGraph 入门
LangGraph 是 LangChain 的 Agent 框架,用**图(Graph)**定义 Agent 流程。
javascript
// 概念(不需要实际跑,理解结构即可)
import { StateGraph, END } from '@langchain/langgraph';
// 定义 Agent 的状态
const agentState = {
messages: [], // 对话历史
next: 'agent', // 下一步去哪
};
// 定义节点(每个节点是一个函数)
const agent = async (state) => {
// LLM 决定:回答 or 调工具
const response = await llm.invoke(state.messages);
return { messages: [...state.messages, response], next: 'should_continue' };
};
const shouldContinue = (state) => {
const lastMsg = state.messages[state.messages.length - 1];
if (lastMsg.tool_calls) return 'tools'; // 有工具要调
return END; // 结束了
};
const tools = async (state) => {
// 执行工具
const results = await executeTools(state.messages[state.messages.length - 1]);
return { messages: [...state.messages, ...results], next: 'agent' };
};
// 构建图
const graph = new StateGraph({ channels: agentState })
.addNode('agent', agent)
.addNode('tools', tools)
.addEdge('__start__', 'agent') // 入口
.addConditionalEdges('agent', shouldContinue, { tools: 'tools', [END]: END })
.addEdge('tools', 'agent'); // 工具执行完回到 agent
const app = graph.compile();text
Graph 可视化:
__start__ → agent → (有 tool call?) → tools → agent → ... → END
↘ (没有) → END实践
- 写一个 3 个工具的 Agent:搜索知识库 + 计算器 + 获取当前时间
- 让 Agent 回答:"从 2020 年到今年经过了多少年?"(需要两步推理)
- 故意给 Agent 一个它解决不了的问题 → 观察它如何处理