🤖 搞懂 AI Agent:从聊天到干活的 5 个关键环节
摘要:2025 年,AI Agent 已经从概念走向落地。从 Cursor 到 Claude Code,从飞书 CLI 到各种自动化工具,Agent 正在重塑我们与软件交互的方式。本文从 LLM → Tool → Agent Loop → Memory → 架构模式 层层递进,用可运行的代码带你建立完整的 Agent 认知体系。
📌 前言
你有没有想过一个问题:
同样是调用 LLM API,为什么 ChatGPT 只能跟你聊天,而 Claude Code 能直接帮你写代码、读文件、执行命令?
答案就两个字:Agent。
Agent(智能体)不是一个新的模型,而是一套让 LLM「长出手脚」的系统设计。理解 Agent,是 2025 年每个开发者都必须补上的一课。
本文不会堆砌概念,而是用可运行的代码带你走完 Agent 的完整链路。
🎯 本文适合谁
- 🧑💻 使用过 LLM API,但不太理解 Agent 原理的开发者
- 🔧 想自己动手实现一个 Agent 的工程师
- 🤔 对 Function Calling、Tool Use 一知半解的 AI 爱好者
- 📚 想系统建立 Agent 认知体系的学习者
一、什么是 Agent?
一句话定义
Agent = LLM + Tools + Memory + Planning
| 组件 | 角色 | 通俗理解 |
|---|---|---|
| LLM | 大脑 | 负责思考和推理 |
| Tools | 手脚 | 负责执行具体操作 |
| Memory | 记忆 | 记住上下文和历史经验 |
| Planning | 规划 | 分解复杂任务,制定执行计划 |
传统软件是「你告诉它做什么,它就做什么」——流程固定、路径确定。而 Agent 是「你告诉它目标,它自己想办法完成」——动态规划、自主决策。
Agent 与普通 AI 对话的区别
| 维度 | 普通 AI 对话 | Agent |
|---|---|---|
| 能力边界 | 只能生成文本 | 能读文件、调 API、操作浏览器、写代码 |
| 决策方式 | 单次问答 | 多步推理、动态调整 |
| 工具使用 | 无 | 按需调用各种工具 |
| 记忆 | 仅当前对话 | 短期 + 长期记忆 |
一个 Agent 有多强?取决于三个要素:
- 🧠 用了什么大脑(LLM)— 决定推理上限
- 🔧 装了什么工具(Tools)— 决定行动能力
- 📋 拿到了什么信息(Context/Memory)— 决定决策质量
有图有真相
二、LLM — Agent 的大脑
LLM(大语言模型)是 Agent 的核心推理引擎。它负责理解任务、规划步骤、生成响应。
⚠️ 关键认知:LLM 只负责推理和生成,它没有「手」,无法直接操作外部世界。真正的行动能力来自 Tool。
推理过程的可见性
现代 LLM 提供了 reasoning_content(思维链),让我们能看到模型的推理过程:
const result = await client.chat.completions.create({
model: "your-model-name", // 替换为你使用的支持 Function Calling 的模型
reasoning_effort: 'high', // 开启深度推理
messages: [
{ role: 'system', content: '你是一个足球领域的专家。' },
{ role: 'user', content: 'c罗是哪个国家的足球运动员?' },
{ role: 'assistant', content: 'c罗是葡萄牙的足球运动员。' },
{ role: 'user', content: '内马尔呢?' }
]
});
// 查看推理过程
console.log('思考过程:', result.choices[0].message.reasoning_content);
// 输出:模型会分析上下文,识别"内马尔呢?"是在问国籍...
// 最终答案
console.log('最终答案:', result.choices[0].message.content);
// 输出:内马尔是巴西的足球运动员。
💡
reasoning_effort控制推理深度。设为'high'时,模型会花更多时间「思考」,适合复杂任务。代价是更慢、更贵。
多轮对话的本质
多轮对话不是模型「记住了」之前的对话,而是你每次请求都把完整的 messages 数组发过去。模型通过上下文理解来「假装」有记忆:
messages: [
{ role: 'user', content: 'c罗是哪个国家的?' },
{ role: 'assistant', content: '葡萄牙。' },
{ role: 'user', content: '内马尔呢?' } // 模型通过上下文推断"内马尔呢"=问国籍
]
这个机制是 Agent 记忆系统的基础——对话历史本身就是一种「短期记忆」。
三、Tool — Agent 的手脚
LLM 再强,也只是一个「大脑」。它能推理,但不能:
- 📈 查询实时股价
- 📁 读写本地文件
- 🌐 浏览网页
- 💻 执行代码
Tool(工具)就是给 Agent 装上「手脚」。 没有 Tool,Agent 只能「纸上谈兵」。
Function Calling 完整流程
这是 Agent 最核心的机制,一次完整的 Tool 调用需要 2 次 LLM 交互:
用户提问:"青岛啤酒的收盘价是多少?" │ ▼ 第 1 次 LLM 调用 LLM 推理:需要调用 get_closing_price 工具 返回:tool_call(函数名 + 参数) │ ▼ 代码执行 Tool 调用 get_closing_price("青岛啤酒") 返回结果:"67.92" │ ▼ 第 2 次 LLM 调用 将 tool 结果交给 LLM LLM 生成最终答案:"青岛啤酒今日收盘价为 67.92 元。"
💡 为什么是 2 次调用? 因为 LLM 本身不执行函数。第 1 次是「决策」(告诉你要调哪个函数),第 2 次是「总结」(拿到结果后生成自然语言回答)。
代码实现
第一步:定义 Tool Schema(告诉 LLM 有哪些工具可用)
const tools = [
{
type: 'function',
function: {
name: 'get_closing_price',
description: '获取指定股票的收盘价', // 这段描述是 LLM 理解工具的关键!
parameters: {
type: 'object',
properties: {
name: {
type: 'string',
description: '股票名称' // LLM 通过这个描述提取参数
}
},
required: ['name']
}
}
}
];
⚠️ 核心要点:
description写得好不好,直接决定 LLM 能否准确调用工具。这是 Prompt Engineering 在 Agent 场景的直接应用。
第二步:定义 Tool 的实际执行函数
// ⚠️ 演示数据,生产环境请替换为真实 API 调用
function get_closing_price(name) {
if (name === '青岛啤酒') return '67.92';
if (name === '贵州茅台') return '1488.21';
return '未找到股票';
}
第三步:完整的 Agent Loop(第 1 次 → 执行 → 第 2 次)
async function agentLoop(userQuestion) {
let messages = [
{ role: 'user', content: userQuestion }
];
try {
// ===== 第 1 次 LLM 调用 =====
const firstResponse = await client.chat.completions.create({
model: process.env.LLM_MODEL,
messages,
tools,
tool_choice: 'auto' // 让 LLM 自主决定是否调用工具
});
const message = firstResponse.choices[0].message;
// LLM 决定调用工具
if (message.tool_calls) {
messages.push(message); // 把 LLM 的"决策"加入对话历史
// ===== 执行 Tool =====
for (const toolCall of message.tool_calls) {
const functionName = toolCall.function.name;
const args = JSON.parse(toolCall.function.arguments);
let result;
if (functionName === 'get_closing_price') {
result = get_closing_price(args.name);
}
// 把 Tool 结果以 tool role 追加到消息列表
messages.push({
role: 'tool',
tool_call_id: toolCall.id,
content: result
});
}
// ===== 第 2 次 LLM 调用 =====
const secondResponse = await client.chat.completions.create({
model: process.env.LLM_MODEL,
messages, // 包含原始问题 + LLM 决策 + Tool 结果
tools,
});
return secondResponse.choices[0].message.content;
}
return message.content; // LLM 认为不需要调用工具,直接返回
} catch (error) {
console.error('Agent 执行出错:', error.message);
return `抱歉,处理你的请求时出错了:${error.message}`;
}
}
// 使用
const answer = await agentLoop('青岛啤酒的收盘价是多少?');
console.log(answer);
// 输出:青岛啤酒今日收盘价为 67.92 元。
💬 聊聊:你在实际项目中使用 Function Calling 遇到过什么坑?工具调用失败你是怎么处理的?评论区聊聊你的经验 👇
四、Agent Loop — Agent 的「心跳」
理解了 Tool 调用后,我们来看更大的图景:Agent Loop。
Agent 的运行不是一次性的,而是一个持续的循环:
感知 (Perceive)
接收用户输入、环境状态、Tool 返回结果
│
▼
推理 (Reason)
LLM 分析当前状态,决定下一步行动
│
▼
行动 (Act)
调用 Tool / 生成回复 / 修改状态
│
▼
观察 (Observe)
获取行动结果,更新上下文
│
▼
是否完成?── 否 ──→ 回到"感知"
│
是
▼
返回最终结果
与传统软件的核心区别:
| 传统软件 | Agent | |
|---|---|---|
| 控制流 | if-else 固定分支,开发者预设所有路径 | LLM 在每一步动态决策 |
| 可处理场景 | 只能处理预设情况 | 可以处理开发者未预见的情况 |
这就是为什么 Agent 能处理开放式任务——它不需要你穷举所有可能,而是「见招拆招」。
五、Memory — Agent 的记忆
一个没有记忆的 Agent,每次对话都是「失忆」重新开始。记忆系统让 Agent 能积累经验、保持上下文。
三层记忆架构
1. 短期记忆:对话上下文
就是 messages 数组。Agent 的每一轮对话都依赖这个数组来理解上下文。
let messages = [
{ role: 'system', content: '你是一个股票分析师。' },
{ role: 'user', content: '茅台怎么样?' },
{ role: 'assistant', content: '贵州茅台今日收盘价 1488.21 元...' },
{ role: 'user', content: '那青岛啤酒呢?' } // 通过上下文理解"那"指代股票
];
局限性:上下文窗口有限(通常 8K~128K tokens),太长的对话会被截断。
2. 长期记忆:向量数据库 / RAG
将历史信息存入向量数据库(如 ChromaDB、Pinecone),需要时通过语义检索召回:
用户提问 → Embedding → 向量检索 → 找到相关历史 → 作为 context 注入 prompt → LLM 回答
这就是 RAG(Retrieval-Augmented Generation)——检索增强生成。
一个最小化的 RAG 流程如下:
// 1. 将历史文档向量化并存入数据库
import { ChromaClient } from 'chromadb';
const collection = await chromaClient.createCollection({ name: 'agent_memory' });
await collection.add({
ids: ['doc_1'],
documents: ['贵州茅台 2024 年 Q3 营收 388 亿,同比增长 15%...'],
// embedding 由 ChromaDB 自动生成
});
// 2. 用户提问时,检索最相关的历史记忆
const results = await collection.query({
queryTexts: ['茅台最近的营收情况如何?'],
nResults: 3 // 取 top 3 最相关的文档
});
// 3. 将检索结果注入 prompt,增强 LLM 的回答
const context = results.documents[0].join('\n');
messages.unshift({
role: 'system',
content: `参考以下历史信息回答问题:\n${context}`
});
💡 这就是 Claude Code、Cursor 等工具能「记住」整个项目上下文的核心原理。
3. 工作记忆:当前任务状态
Agent 在执行复杂任务时,需要跟踪中间状态:
const taskState = {
goal: '帮我分析茅台的财务状况',
completedSteps: ['获取股价', '获取市盈率'],
currentStep: '分析营收趋势',
pendingSteps: ['生成报告'],
intermediateData: { pe: 28.5, revenue: '...' }
};
💬 你的经验:你是如何管理 Agent 的长期记忆的?用向量数据库还是其他方案?欢迎分享你的实践 👇
六、经典 Agent 架构模式
在 Agent 工程实践中,有两种被广泛验证的架构模式:
ReAct(Reasoning + Acting)
最经典的 Agent 模式。核心思想:交替进行推理和行动。
Thought: 我需要查询茅台的股价
Action: get_stock_price("贵州茅台")
Observation: 1488.21 元
Thought: 股价拿到了,用户可能还想知道涨跌幅...
Action: get_change_rate("贵州茅台")
Observation: +2.3%
Thought: 信息足够了,可以生成回答
Answer: 贵州茅台今日收盘价 1488.21 元,涨幅 2.3%...
每一步都有 Thought(推理)→ Action(行动)→ Observation(观察),循环往复。
Plan-and-Execute
适合复杂任务:先规划,再执行。
用户:帮我写一份竞品分析报告
Step 1 (Plan): 分解任务
- 1.1 确定竞品列表
- 1.2 收集各竞品数据
- 1.3 对比分析
- 1.4 生成报告
Step 2 (Execute): 逐步执行每个子任务
- 执行 1.1 → 完成
- 执行 1.2 → 完成
- ...
两种模式对比
| 维度 | ReAct | Plan-and-Execute |
|---|---|---|
| 适用场景 | 简单到中等任务 | 复杂、多步骤任务 |
| 决策方式 | 边做边想 | 先想后做 |
| 灵活性 | 高(随时调整) | 中(计划可能需要修订) |
| 可预测性 | 低 | 高 |
🐛 实战踩坑:在一次项目中,我用 ReAct 模式让 Agent 爬取网页数据。Agent 执行到第 3 步时 Tool 返回了一个超长的 HTML(约 200KB),远超模型的上下文窗口,导致下一轮 API 调用直接报错
context_length_exceeded。最终解决方案是:在 Tool 返回结果前做一层预处理截断——对超过 4000 字符的返回结果只保留摘要,并附上"完整数据已缓存,如需细节请明确指定"。这个教训是:Agent 的 Tool 设计不仅要关注"能不能调通",更要考虑返回数据的大小和格式是否在 LLM 的消化范围内。
💬 你的选择:ReAct(边做边想)和 Plan-and-Execute(先想后做),你在实际项目中更倾向哪种模式?为什么?
七、实战:完整的 Agent 代码
把前面所有概念串起来,这是一个最小但完整的 Agent 实现。
环境准备
开始之前,确保你的环境满足以下条件:
# 1. Node.js 版本要求 ≥ 18
node -v
# 2. 初始化项目并安装依赖
npm init -y
npm install openai dotenv
# 3. 创建 .env 文件,配置以下变量
# .env
LLM_API_KEY=your-api-key-here
LLM_API_BASE_URL=https://api.openai.com/v1
LLM_MODEL=gpt-4o
💡 说明:本文使用 OpenAI 兼容的 API 格式,你可以替换为任何支持 Function Calling 的模型(GPT-4o、Claude、DeepSeek 等),只需修改
LLM_API_BASE_URL和LLM_MODEL即可。
初始化 + 定义 Tools
import { OpenAI } from 'openai';
import dotenv from 'dotenv';
dotenv.config();
const client = new OpenAI({
apiKey: process.env.LLM_API_KEY,
baseURL: process.env.LLM_API_BASE_URL,
});
// 定义 Tool Schema
const tools = [
{
type: 'function',
function: {
name: 'get_closing_price',
description: '获取指定股票的收盘价',
parameters: {
type: 'object',
properties: {
name: { type: 'string', description: '股票名称' }
},
required: ['name']
}
}
}
];
// Tool 的实际执行函数
// ⚠️ 演示数据,生产环境请替换为真实 API 调用
const toolFunctions = {
get_closing_price(name) {
const prices = {
'青岛啤酒': '67.92',
'贵州茅台': '1488.21',
'宁德时代': '218.50'
};
return prices[name] || `未找到 ${name} 的股价数据`;
}
};
Agent Loop 核心逻辑
async function agent(userQuestion) {
console.log(`\n用户: ${userQuestion}\n`);
let messages = [
{ role: 'system', content: '你是一个专业的股票分析师,善于用简洁的语言回答用户关于股票的问题。' },
{ role: 'user', content: userQuestion }
];
try {
// Agent Loop:最多循环 5 次,防止无限循环
for (let i = 0; i < 5; i++) {
const response = await client.chat.completions.create({
model: process.env.LLM_MODEL,
messages,
tools,
tool_choice: 'auto'
});
const message = response.choices[0].message;
messages.push(message);
// LLM 不需要调用工具,直接返回答案
if (!message.tool_calls) {
console.log(`Agent: ${message.content}`);
return message.content;
}
// LLM 决定调用工具 → 执行 → 结果回传
console.log(`[调用工具] ${message.tool_calls.map(tc => tc.function.name).join(', ')}`);
for (const toolCall of message.tool_calls) {
const fn = toolFunctions[toolCall.function.name];
if (!fn) {
console.warn(`未知工具: ${toolCall.function.name}`);
continue;
}
const args = JSON.parse(toolCall.function.arguments);
// 通用参数提取:不假设参数名一定是 name
const argValue = args.name || args.symbol || args.query || Object.values(args)[0];
const result = fn(argValue);
console.log(` → ${toolCall.function.name}("${argValue}") = ${result}`);
messages.push({
role: 'tool',
tool_call_id: toolCall.id,
content: result
});
}
}
return '达到最大循环次数,请尝试简化你的问题。';
} catch (error) {
console.error('Agent 执行异常:', error.message);
return `抱歉,Agent 运行出错:${error.message}。请检查网络连接和 API 配置。`;
}
}
运行效果
await agent('青岛啤酒的收盘价是多少?');
await agent('贵州茅台和宁德时代,哪个更贵?');
用户: 青岛啤酒的收盘价是多少?
[调用工具] get_closing_price
→ get_closing_price("青岛啤酒") = 67.92
Agent: 青岛啤酒今日收盘价为 67.92 元。
用户: 贵州茅台和宁德时代,哪个更贵?
[调用工具] get_closing_price, get_closing_price
→ get_closing_price("贵州茅台") = 1488.21
→ get_closing_price("宁德时代") = 218.50
Agent: 贵州茅台收盘价 1488.21 元,宁德时代收盘价 218.50 元,贵州茅台更贵。
💡 注意第二个问题:LLM 自主决定同时调用两次工具来并行获取数据。这就是 Agent 的「智能」——它能根据问题动态决定工具调用策略,而不是呆板地一次只调一个。
💡 核心总结
黄金公式
Agent = LLM(推理) + Tools(行动) + Memory(记忆) + Planning(规划)
各组件的关系
用户输入
│
▼
┌─── Agent ──────────────────────┐
│ │
│ LLM (大脑) ◄──► Memory (记忆) │
│ │ ▲ │
│ │ Planning (规划) │
│ ▼ │
│ Tools (手脚) │
│ │
└────────────────────────────────┘
│
▼
最终输出
学习路径建议
| 阶段 | 内容 | 目标 |
|---|---|---|
| 🟢 入门 | LLM API 调用、多轮对话、Function Calling | 能调用 LLM API,理解 Tool 机制 |
| 🟡 进阶 | Agent Loop、ReAct 模式、Tool 设计 | 能手写一个简单的 Agent |
| 🔴 深入 | Memory 系统、RAG、Multi-Agent 协作 | 掌握生产级 Agent 框架(LangChain / LlamaIndex / AutoGen) |
🔗 参考资料
- OpenAI Function Calling 官方文档
- ReAct: Synergizing Reasoning and Acting in Language Models
- LangChain Agent 文档
- 【生成式人工智能应用实战(慕课版)】北航90后副教授何静带你走进新生小白人工智能的第一课
💬 交流讨论
本文从 LLM API 调用出发,到完整的 Agent Loop 实现,用可运行的代码串起了 Agent 的核心概念。如果你对某个环节还有疑问,或者想了解 Multi-Agent、RAG 等进阶话题,欢迎在评论区留言!
下一篇预告:《手把手带你接入 MCP Server,让 Agent 能力暴增 10 倍》 🔥
觉得有用?点个赞 👍 收藏 ⭐ 关注 👆,不错过后续 Agent 实战系列!