为什么现在的AI不仅能聊天,还能帮你写代码、查股价、操作浏览器?背后全靠 Agent(大脑+手脚+记忆) 这三大支柱。本文将用最通俗的语言 + 可运行的 Node.js 代码,带你一次性搞懂 LLM、Tool、Data/Context 的铁三角关系,并亲手实现一个能多轮对话、自动查股票收盘价的完整 Agent。
1. 引言:AI 正在从"聊天"走向"干活"
2025 年以来,AI 领域最火的关键词已经不是"大模型"本身,而是 Agent(智能体) 。你会发现,Cursor、Claude Code、豆包、WorkBuddy 这些产品,不再只是你问我答,它们能 读文件、搜网络、写代码、操作浏览器、甚至控制你的电脑------这一切的核心,就是 Agent。
作为一名开发者,如果你还不理解 Agent、LLM、Tool 之间的关系,很快就会被时代甩开。今天,我就用最直白的语言和可运行的 Node.js 代码,带你一次性理清这些概念,并亲手造一个能查股票收盘价的 Agent。
2. Agent:能"干活"的 AI
Agent 是什么?
简单说,Agent = 大脑(LLM) + 手脚(Tools) + 记忆与知识(Data/Context) 。
它不再满足于"回答问题",而是 主动规划、调用工具、执行任务,最后把结果反馈给你。
例如:
- 你问:"青岛啤酒今天的收盘价是多少?"
普通 LLM 只能根据训练数据瞎猜(可能还是过时的)。
但 Agent 会识别出"需要查实时数据",于是调用股票价格查询工具,拿到结果后再组织成自然语言回答你。 - 你问:"帮我写一个 Python 脚本,爬取某网站标题。"
Agent 会调用代码执行工具,甚至直接操作你的 IDE。
Agent 有多强,取决于三个因素:
- 大脑(LLM) :推理能力强不强,能不能理解复杂指令。
- 手脚(Tools) :接入了多少外部能力(数据库、API、浏览器、文件系统)。
- 记忆与知识(Data/Context) :能拿到多少历史对话、系统指令和外部检索数据。
现在最值钱的岗位就是 Agent 工程师,他们负责把各种工具"装"给 LLM,并设计好上下文记忆机制,让 AI 真正落地到业务中,降本增效。
3. LLM:Agent 的"大脑"
LLM(大语言模型)是 Agent 的核心推理引擎。
它负责 理解你的意图、拆解任务、决定调用哪个工具、组织最终回复。
但 LLM 本身 只有推理和生成文本的能力 ,它无法主动去查数据库、发 HTTP 请求、操作文件系统------这些"行动"全靠 工具(Tool) 来完成。
举个例子:
- 你问:"青岛啤酒股价多少?"
- LLM 推理:用户想要实时股价 → 我需要调用
get_closing_price这个工具 → 参数是"青岛啤酒"。 - LLM 生成一个 工具调用请求(而不是直接回答),交给外部执行。
- 外部拿到结果后,再回传给 LLM,LLM 再组织成"青岛啤酒收盘价 67.92 元"返回给你。
整个流程就像:大脑发出指令 → 手脚去执行 → 大脑再汇报结果。
4. Tool:让 AI 长出"手脚"
Tool 就是 LLM 能够调用的外部函数或 API。
通过 Tool,LLM 可以查询数据库、发送邮件、控制浏览器、读写文件......没有 Tool,LLM 就是纸上谈兵。
OpenAI 和各大模型厂商都提供了标准的 Tool 定义接口(Function Calling),让开发者可以轻松地为 LLM 注册工具。
下面我们先看如何声明一个工具:
go
// index.mjs 中的工具声明部分
const tools = [
{
type: "function",
function: {
name: "get_closing_price",
description: "获取指定股票的收盘价",
parameters: {
type: "object",
properties: {
name: {
type: "string",
description: "股票名称,如'青岛啤酒'、'贵州茅台'"
}
},
required: ["name"]
}
}
}
];
关键点解析:
type: "function":固定写法,表示这是一个函数工具。function.name:工具名称,LLM 在调用时会引用它。function.description:极其重要!清晰描述工具功能,LLM 靠它判断何时调用。function.parameters:定义参数结构(JSON Schema),告诉 LLM 需要哪些参数、类型是什么。required:必填参数列表。
注意: 这里只是"声明"了工具,真正的执行逻辑是 get_closing_price 函数。LLM 不会直接执行它,而是返回一个工具调用请求,由我们的代码去执行。
5. 数据与上下文(Data & Context):Agent 的"记忆"与"知识库"
如果说 LLM 是大脑、Tools 是手脚,那么数据和上下文(Data & Context)就是 Agent 的血液和记忆体。一个没有上下文记忆的 Agent,每次对话都是"失忆"的;一个没有外部数据的 Agent,只能用训练集里的陈旧知识"胡编乱造"。
在实战中,"上下文"主要由以下三大块构成:
5.1 对话上下文(Messages / 短期记忆)
这就是你在笔记中提到的 messages 多轮对话列表。它承载着当前会话的所有历史信息,让 LLM 能够"记得"我们刚才说了什么。
为什么它至关重要?
如果没有将历史对话传回给 LLM,当你问"那茅台呢?"时,LLM 根本不知道你在延续关于"股票收盘价"的话题。它会孤立地回答问题,甚至反问"你指茅台的什么?"
代码对应:
在 main.mjs 中,正是通过不断累加 messages 数组来实现多轮上下文理解的。我们把历史问答都放进去,LLM 就能明白"那茅台呢?" = "查一下贵州茅台的收盘价"。
5.2 系统提示词(System Prompt / 指令上下文)
这也是数据上下文中极其重要的一环。它就像给 Agent 设定的"人设"和"工作守则",在每一次推理时都会被置入上下文。
举例:
在 main.mjs 中,system 角色扮演的是"足球专家"。如果将系统提示改为:
css
{ role: 'system', content: '你是股票交易助手,所有回答必须附带数据来源,且必须使用工具查询,禁止瞎猜。' }
那么在整个对话生命周期中,Agent 都会被这个指令上下文约束,确保它不会胡编乱造股价。
5.3 外部知识库与长期数据(RAG / 长期记忆)
这是现代 Agent 落地企业场景的杀手锏。因为 LLM 的训练数据是有截止日期的(比如截止 2024 年初),它不知道公司内部的财报、私有协议或实时政策。
"数据"如何补全 Agent 的能力?
当用户问"我们公司 Q3 的报销制度是什么?"时,通用 LLM 根本不知道。这时 Agent 会:
- 检索(Retrieve) :先去向量数据库或企业知识库中,检索出相关的 PDF 或 Word 文档内容(这就是外部 Data)。
- 注入(Augment) :把这些查到的文本内容,拼接到当前的
messages上下文中(比如伪装成system或user消息)。 - 生成(Generate) :LLM 基于这些新注入的数据来生成答案。
"拿到了什么信息"决定了 Agent 的能力。 如果 Agent 拿到了实时联网搜索的"信息",它就能回答最新新闻;如果拿到了企业数据库的"信息",它就能做商业智能分析。
🔥 升级版架构图:Agent 的四维拼图
结合新增的"数据与上下文",我们来更新一下核心逻辑图:
scss
┌─────────────────────────────────────────────────────────────────┐
│ AI Agent(智能体) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────────────┐ │
│ │ 1. 大脑 (LLM) │◄──►│ 2. 手脚(Tool)│ │ 3. 记忆与知识(Data│ │
│ │ 推理 & 规划 │ │ 操作外部世界 │ │ & Context) │ │
│ └──────────────┘ └──────────────┘ └───────────────────┘ │
│ │ ▲ ▲ │
│ └──────────────────┼──────────────────────┘ │
│ │ │
│ ┌──────────────┴──────────────┐ │
│ │ ① 短期记忆(Messages) │ │
│ │ ② 指令约束(System Prompt) │ │
│ │ ③ 长期知识(RAG/数据库) │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
6. 实战:从零搭建一个完整的股票查询 Agent
有了前面的理论,我们终于进入实战。这一次,我们不只打印 tool_calls,而是完整跑通"声明工具 → LLM请求调用 → 本地执行 → 结果回传 → 生成最终答案"的全链路。
6.1 项目结构 & 客户端初始化
less
.
├── client.mjs // 初始化 OpenAI 兼容客户端
├── index.mjs // 🌟 完整 Agent 逻辑(工具声明 + 往返调用)
└── main.mjs // 展示 reasoning_effort 深度推理
client.mjs(客户端封装):
arduino
import { OpenAI } from "openai";
import dotenv from 'dotenv';
dotenv.config();
const client = new OpenAI({
apiKey: process.env.DEEPSEEK_API_KEY,
baseURL: process.env.DEEPSEEK_API_BASE_URL,
});
export default client;
6.2 完整 Agent 代码(index.mjs)
下面这段代码是精华所在,我加上了详细注释,帮你理解每一步在做什么:
javascript
import client from "./client.mjs";
// ---------- 第一步:声明工具(告诉 LLM 它有这个能力) ----------
const tools = [
{
type: "function",
function: {
name: "get_closing_price",
description: "获取指定股票的收盘价",
parameters: {
type: "object",
properties: {
name: {
type: "string",
description: "股票名称,如'青岛啤酒'、'贵州茅台'"
}
},
required: ["name"]
}
}
}
];
// ---------- 第二步:真正干活的函数(模拟查数据库/API) ----------
function get_closing_price(name) {
if (name === '青岛啤酒') return "67.92 元";
if (name === '贵州茅台') return "1488.21 元";
return "未找到该股票数据";
}
// ---------- 第三步:发送消息(携带 tools 声明) ----------
const send_message = async (messages) => {
return await client.chat.completions.create({
model: 'deepseek-v4-flash', // 替换成你的模型
messages,
tools,
tool_choice: 'auto'
});
};
// ---------- 第四步:主流程(处理工具调用往返) ----------
const main = async () => {
// 初始化对话上下文(记忆从这里开始)
let messages = [
{ role: 'user', content: "青岛啤酒的收盘价是多少?" }
];
// 4.1 第一次请求 LLM
let response = await send_message(messages);
let assistantMessage = response.choices[0].message;
// 4.2 检查 LLM 是否要求调用工具
if (assistantMessage.tool_calls) {
console.log("🧠 LLM 决定调用工具:", assistantMessage.tool_calls[0].function.name);
// 解析参数
const toolCall = assistantMessage.tool_calls[0];
const args = JSON.parse(toolCall.function.arguments);
const stockName = args.name;
// 4.3 执行本地工具函数(这一步就是 Agent 的"手脚"在动)
const result = get_closing_price(stockName);
console.log(`⚙️ 执行工具: get_closing_price("${stockName}") -> ${result}`);
// 4.4 把 LLM 的调用请求和工具结果,都追加到上下文(Messages)里
messages.push(assistantMessage); // 加入 LLM 的请求调用记录
messages.push({
role: 'tool', // 工具回传的角色
tool_call_id: toolCall.id, // 关联本次调用 ID
content: result // 工具执行结果
});
// 4.5 第二次请求 LLM:把工具结果带回去,让它生成最终答案
response = await send_message(messages);
assistantMessage = response.choices[0].message;
}
// 4.6 输出最终答案
console.log("🤖 Agent 最终回答:", assistantMessage.content);
};
main();
运行结果示例:
css
🧠 LLM 决定调用工具: get_closing_price
⚙️ 执行工具: get_closing_price("青岛啤酒") -> 67.92 元
🤖 Agent 最终回答: 青岛啤酒的收盘价为 67.92 元。
6.3 验证"上下文记忆"的重要性
如果我们接着问"那茅台呢?",只需要把 messages 改成:
ini
let messages = [
{ role: 'user', content: "青岛啤酒的收盘价是多少?" },
{ role: 'assistant', content: "青岛啤酒的收盘价为 67.92 元。" }, // 记住上一轮
{ role: 'user', content: "那茅台呢?" }
];
因为上下文里有"收盘价"这三个字,LLM 就能准确推断出"茅台"指的是"查询贵州茅台的收盘价",并再次调用 get_closing_price("贵州茅台")。如果没有这个记忆上下文,LLM 就会一脸懵。
7. 推理能力与 reasoning_effort API
除了工具调用,LLM 的 推理过程(Reasoning) 也至关重要。
尤其是处理复杂问题时,我们不仅想要最终答案,还想看到它"思考"的步骤,便于调试和信任。
DeepSeek 提供了 reasoning_effort 参数,可以控制模型的推理深度。
我们在 main.mjs 中演示:
php
import client from './client.mjs';
const main = async () => {
const result = await client.chat.completions.create({
model: 'deepseek-v4-flash',
reasoning_effort: 'high', // 开启深度推理模式
messages: [
{ role: 'system', content: '你是一个足球领域的专家,请尽量帮我回答与足球相关的问题' },
{ role: 'user', content: 'c罗是哪个国家的足球运动员?' },
{ role: 'assistant', content: 'c罗是葡萄牙的足球运动员' },
{ role: 'user', content: '内马尔呢?' }
]
});
console.log('🧠 思考过程:');
console.log(result.choices[0].message.reasoning_content); // 推理链
console.log('\n📢 最终答案:');
console.log(result.choices[0].message.content);
};
main();
运行后,你会看到类似输出:
erlang
🧠 思考过程:
好的,用户问完C罗之后,接着问了内马尔。看来他可能是在了解不同知名足球运动员的国籍,或者是在对比不同球员的背景。
嗯,从之前的对话看,用户对足球领域有兴趣,但问题比较基础,可能是个刚开始关注足球的新球迷,或者只是想快速获取一些基本信息。
他的深层需求可能不仅仅是知道内马尔是哪个国家的,而是想了解内马尔的基本情况,比如他是否和C罗是同一时代的对手,或者他的技术特点是什么。
...
📢 最终答案:
内马尔是巴西的足球运动员。他目前效力于沙特阿拉伯的利雅得新月俱乐部,也是巴西国家队的核心球员。
reasoning_content 字段 展示了模型的思维链,对于调试 prompt、理解模型决策非常有帮助。
reasoning_effort 可选 low / medium / high,越高推理越深入,但也更耗时、更贵。
8. 普通对话(无工具)示例
如果你的需求只是简单问答,不需要调用外部工具,那么直接用基础的 completion 即可:
javascript
import client from './client.mjs';
export async function getCompletion(prompt) {
const response = await client.chat.completions.create({
model: process.env.DEEPSEEK_MODEL,
messages: [ { role: 'user', content: prompt } ]
});
return response.choices[0].message.content;
}
这是最简模式,适合简单问答。但一旦涉及到实时数据或操作,就必须引入我们前面讲的 Tools 和 Context。
9. 总结:一张图看懂 Agent 核心架构
scss
┌─────────────────────────────────────────────────────────────────┐
│ AI Agent(智能体) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────────────┐ │
│ │ 1. 大脑 (LLM) │◄──►│ 2. 手脚(Tool)│ │ 3. 记忆与知识(Data│ │
│ │ 推理 & 规划 │ │ 操作外部世界 │ │ & Context) │ │
│ └──────────────┘ └──────────────┘ └───────────────────┘ │
│ │ ▲ ▲ │
│ └──────────────────┼──────────────────────┘ │
│ │ │
│ ┌──────────────┴──────────────┐ │
│ │ ① 短期记忆(Messages) │ │
│ │ ② 指令约束(System Prompt) │ │
│ │ ③ 长期知识(RAG/数据库) │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
- LLM 负责"想"------推理、拆解任务、决定用什么工具。
- Tools 负责"做"------执行具体操作,获取数据或改变状态。
- Data/Context 负责"记"------保持多轮对话的连贯性,提供系统指令和外部知识。
而你作为开发者,最重要的工作就是:
- 为 LLM 精心设计 工具描述(description 和 parameters),让 LLM 能精准选用。
- 编写工具的真实实现(调用数据库、第三方 API 等)。
- 维护好 messages 上下文数组,让 Agent 拥有"记忆"。
- 处理多轮工具调用的往返流程(执行 → 回传 → 再生成)。
10. 下一步:进阶玩法
当你掌握了基础,可以尝试:
- 多工具协同:一个 Agent 同时具备查股价、查天气、发邮件的能力。
- ReAct 模式:让 LLM 进行"推理-行动-观察"的循环,自主完成复杂任务。
- 本地工具:比如操作文件系统、控制浏览器(Puppeteer)、执行 Shell 命令。
- RAG 接入:把企业私有文档向量化,注入到上下文里,打造专属行业专家。
现在,市场对 Agent 工程师的需求井喷,因为企业真正需要的是"能干活"的 AI,而不是只会聊天的玩具。希望这篇文章帮你迈出了扎实的第一步。
快去动手敲一遍代码吧,亲手感受一下 LLM 如何通过 Tools + Context 焕发新生!