为什么要聊智能体?
周一早晨,程序员小王收到了老板发来的消息:
老板:"小王啊,公司要开发一个智能客服系统,能自动回答用户问题,还能查订单、处理退款。下周能搞定吗?"
小王盯着屏幕,脑子里第一反应是:
传统方案:写一堆 if-else?
问题是:
1. 用户问法千奇百怪,关键词根本列举不完
2. 新问题天天出现,规则会越堆越乱
3. 一旦超出预设流程,系统就只能转人工
也正是在这个时候,他想起最近总被提到的一个词:Agent(智能体)。
这篇文章不会一上来就丢一堆抽象概念,而是从一个更实际的问题出发:
- 智能体到底是什么?
- 它和传统软件到底有什么本质区别?
- 一个能工作的智能体,最少需要哪些组件?
- 什么时候值得用 Agent,什么时候其实不该硬上?
- 最后,怎么动手写出自己的第一个智能体?
如果你也刚开始接触 Agent,这篇就是给你的第一站。
第一章:什么是智能体?
1.1 一个够用的定义
先给一个适合入门的版本:
Agent = 感知 + 决策 + 行动
flowchart LR
A["感知\nPerception"] --> B["决策\nDecision"]
B --> C["行动\nAction"]
C --> D["环境反馈"]
D -.-> A
拆开来看:
- 感知(Perception):理解输入,知道发生了什么
- 决策(Decision):判断接下来该怎么做
- 行动(Action):把决定真正执行出来
用一个生活化的例子来理解:
场景:你是一名餐厅服务员
顾客说:"给我来一份宫保鸡丁"
感知:理解顾客在点菜
决策:判断菜品是否可下单、是否需要推荐搭配
行动:把菜下给厨房,并告知预计等待时间
这其实就是一个最小版本的智能体。
1.2 现代 LLM Agent 是什么?
如果放到今天的大模型语境里,Agent 可以理解成:
以 LLM 为决策核心,能够结合记忆、工具和任务流程,自主完成目标的系统。
这里有两个细节很重要:
- 智能体不等于聊天机器人如果它只能回复文本、不能调用工具、也不能根据目标调整策略,那它更像一个对话接口,而不是完整 Agent。
- 智能体也不等于“什么都能自动完成” Agent 的关键不是“无所不能”,而是它会根据上下文决定下一步怎么做,而不是完全依赖写死的分支逻辑。
所以更准确一点说:
现代 Agent = LLM + Memory + Tools + Workflow
前面的“感知 + 决策 + 行动”是概念骨架,这里的 “LLM + Memory + Tools + Workflow” 是工程落地时更常见的实现形态。
第二章:智能体和传统程序,到底差在哪?
很多人第一次接触 Agent 时都会问:
"不就是在 if-else 外面套了个大模型吗?"
这个问题很关键。因为只有搞清楚它和传统程序的区别,后面学工具、记忆、规划才不会变成“学一堆术语”。
2.1 控制流的本质区别
传统程序的核心是:
输入 -> 匹配规则 -> 命中分支 -> 输出结果
智能体的核心更像:
输入 -> 理解意图 -> 生成策略 -> 调用工具/执行动作 -> 根据结果继续调整
两者最大的差异,不是“用了没用大模型”,而是:
- 传统程序的控制流主要由开发者预先写死
- 智能体的控制流更多由模型在运行时动态决定
flowchart TB
subgraph T["传统程序"]
T1["用户输入"] --> T2["规则匹配"]
T2 --> T3["if-else 分支"]
T3 --> T4["输出结果"]
end
subgraph A["Agent"]
A1["用户输入"] --> A2["LLM 理解意图"]
A2 --> A3["动态决策"]
A3 --> A4["调用工具 / 执行动作"]
A4 --> A5["观察结果"]
A5 --> A3
A3 --> A6["最终回答"]
end
2.2 看一段代码就明白了
传统客服机器人:
function getResponse(userInput: string): string {
const input = userInput.toLowerCase();
if (input.includes("价格")) {
return "我们的产品价格为 99 元";
} else if (input.includes("退款")) {
return "请提供订单号,我们将为您处理退款";
} else if (input.includes("配送")) {
return "配送时间为 3-5 个工作日";
}
return "抱歉,我无法理解您的问题,请转人工客服。";
}
LLM 驱动的 Agent:
async function agentResponse(userInput: string, memory: Memory): Promise<string> {
const messages = [
{ role: "system", content: "你是专业的客服助手" },
...memory.getRecent(10),
{ role: "user", content: userInput }
];
const decision = await llm.think(messages);
if (needsToolCall(decision)) {
const toolResult = await executeTool(decision.tool, decision.input);
messages.push({ role: "assistant", content: decision });
messages.push({ role: "user", content: `工具执行结果:${toolResult}` });
return await llm.think(messages);
}
return decision;
}
从这两段代码里,你会看到一个非常本质的变化:
- 在传统程序里,是你告诉系统该怎么分支
- 在智能体里,是系统自己判断下一步该做什么
2.3 一句话总结这场区别
传统程序擅长处理“规则稳定、流程固定”的问题; 智能体更擅长处理“表达开放、路径不固定、需要动态决策”的问题。
这句话你现在先记住,后面判断“什么时候该用 Agent”时会非常有用。
第三章:一个完整智能体,通常有哪些组件?
当小王继续往下研究,他发现一个真正可用的智能体,通常不只是一个 LLM 调用,而是由几块核心能力共同组成。

Agent
|
---------------------
| | | |
LLM Memory Tools Planner
flowchart TD
U["用户目标"] --> G["Agent"]
G --> L["LLM\n负责理解与决策"]
G --> M["Memory\n负责上下文与偏好"]
G --> T["Tools\n负责调用外部能力"]
G --> P["Planner\n负责拆解复杂任务"]
T --> E["外部世界 / API / 数据库"]
3.1 LLM:智能体的大脑
LLM 是整个 Agent 的决策中心,主要负责:
- 理解用户意图
- 做出下一步判断
- 生成回答、计划或动作指令
const messages = [
{ role: "system", content: "你是一个智能助手" },
{ role: "user", content: "帮我查询北京明天的天气" }
];
const response = await openai.chat.completions.create({
model: "gpt-4",
messages
});
这里要注意一点:
LLM 很重要,但 LLM 本身不等于完整 Agent。
它像大脑,但大脑要想真的解决问题,还需要记忆、手脚和任务拆解能力。
3.2 Memory:让它不再像“七秒鱼”
如果智能体记不住你刚刚说过什么,每次都像重新认识你,那体验会非常糟糕。
记忆一般分成两类:
| 类型 | 作用 | 类比 | 常见实现 |
|---|---|---|---|
| 短期记忆 | 保存当前对话上下文 | 正在进行中的聊天记录 | 消息列表 |
| 长期记忆 | 跨会话保存用户偏好、事实和知识 | 你对某个人长期的了解 | 数据库 / 向量库 |
interface Message {
role: "user" | "assistant" | "system";
content: string;
timestamp: Date;
}
class Memory {
private history: Message[] = [];
add(message: Message): void {
this.history.push(message);
}
getRecent(n: number): Message[] {
return this.history.slice(-n);
}
}
3.3 Tools:让智能体真的“能做事”
纯 LLM 最大的限制是:它主要处理文本,本身不能直接访问外部世界。
这时就需要工具:
- 搜索工具:查实时信息
- 数据库工具:查订单、查库存
- 计算工具:执行公式和数值运算
- API 工具:调用业务系统
- 通知工具:发消息、发邮件、发工单
interface Tool {
name: string;
description: string;
execute: (input: string) => Promise<string>;
}
class SearchTool implements Tool {
name = "search";
description = "网页搜索引擎,用于查询实时信息";
async execute(query: string): Promise<string> {
const results = await searchAPI(query);
return results.map((r, i) => `[${i + 1}] ${r.title}\n${r.snippet}`).join("\n\n");
}
}
如果说 LLM 是大脑,那工具就像手脚。 没有工具,智能体往往只能“说得像会做事”,但不一定真的能完成任务。
3.4 Planner:处理复杂任务时的任务拆解器
不是所有 Agent 都需要 Planner。 简单问答型 Agent 可能完全不需要它。
但一旦任务变复杂,比如:
- 规划一次旅行
- 调研一个行业
- 生成多步骤业务流程
- 调用多个系统协作完成目标
就需要先拆解任务,再逐步执行。
interface PlanStep {
number: number;
description: string;
dependencies: number[];
status: "pending" | "completed" | "failed";
result?: string;
}
const plan: PlanStep[] = [
{
number: 1,
description: "搜索目的地景点推荐",
dependencies: [],
status: "pending"
},
{
number: 2,
description: "查询机票价格",
dependencies: [],
status: "pending"
},
{
number: 3,
description: "搜索酒店信息",
dependencies: [],
status: "pending"
},
{
number: 4,
description: "生成完整旅行计划",
dependencies: [1, 2, 3],
status: "pending"
}
];
到这里,我们可以把完整的 Agent 理解成这样:
LLM 负责决策
Memory 负责记住上下文
Tools 负责和外部世界交互
Planner 负责拆复杂任务
第四章:什么时候应该用 Agent,什么时候不要硬上?
这是很多入门文章都会跳过的一点,但我觉得它恰恰最重要。
因为“能做”不等于“值得做”。
4.1 适合用 Agent 的场景
如果你的问题有下面这些特征,Agent 往往会比较合适:
- 用户表达开放 同一个需求可能有很多种说法,关键词规则不好写。
- 任务路径不固定 系统要根据上下文动态决定下一步。
- 需要工具调用 比如搜索、查数据库、发消息、执行 API。
- 需要多轮上下文 系统必须记住前面的对话和状态。
- 问题规模变化大 有些是简单问答,有些是复杂任务拆解。
典型例子:
- 智能客服
- 办公助手
- 调研助手
- 编程助手
- 工作流自动化助手
4.2 不适合强行上 Agent 的场景
如果你的需求是下面这种,传统软件可能反而更好:
- 流程非常固定 比如表单校验、固定审批流、状态机控制。
- 结果必须完全确定 比如财务结算、权限判断、计费规则。
- 性能和成本极敏感 高频、简单、重复任务没必要每次都调 LLM。
- 问题本身没有开放性 明明一个 SQL 或 if-else 就能稳定解决,就不需要上 Agent。
你可以把它理解成一个简单判断:
规则稳定 -> 优先传统程序
决策开放 -> 更适合 Agent
flowchart LR
S["问题类型判断"] --> Q1{"规则是否稳定?"}
Q1 -- "是" --> R1["优先传统程序"]
Q1 -- "否" --> Q2{"是否需要动态决策、工具、上下文?"}
Q2 -- "是" --> R2["更适合 Agent"]
Q2 -- "否" --> R3["先用简单自动化或脚本"]
4.3 最容易踩的坑
很多团队一开始做 Agent,最常见的问题不是“模型不够强”,而是:
- 把本来适合规则引擎的问题强行做成 Agent
- 以为只接一个 LLM 就算 Agent
- 没有工具,导致智能体只能“空谈”
- 没有记忆,导致每轮都像第一次见面
- 没有边界控制,导致结果时好时坏
所以更成熟的做法通常是:
用传统程序兜住确定性部分, 用 Agent 处理那些开放、灵活、需要动态决策的部分。
第五章:智能体是怎么一步步发展到今天的?
智能体并不是突然冒出来的,它的演进其实很清晰。
5.1 规则驱动时代
在 LLM 出现之前,很多所谓“智能系统”本质上是:
- 专家系统
- 基于流程图的机器人
- 推荐系统
- 搜索系统
它们能解决问题,但核心仍然是规则、特征和预定义流程。
5.2 LLM 出现之后,局面变了
2022 年以后,大模型让系统第一次具备了更强的:
- 语义理解能力
- 泛化能力
- 零样本和少样本能力
- 多任务处理能力
这带来了一个重要变化:
系统不再只能执行我们写死的流程, 而是可以在运行时“推理出下一步该干什么”。
5.3 为什么后来又会出现 ReAct、Tool、Memory、MCP?
因为大家很快发现:只有 LLM 还不够。
- 只会回答,不会行动
所以要接
Tools - 只看当前输入,记不住上下文
所以要接
Memory - 复杂任务容易乱
所以要接
Planner - 工具生态太碎
所以开始需要更统一的协议,比如
Function Calling、MCP
也就是说,今天你看到的 Agent 框架,并不是一开始就长这样,而是一步一步“被问题逼出来”的。
第六章:动手实践,写一个最小可用的 SimpleAgent
理论看到这里,已经足够开始写代码了。
这一节我们不追求一步到位做复杂框架,而是先写一个最小版本的 SimpleAgent,把核心骨架搭起来。
flowchart LR
I["用户输入"] --> S["system prompt"]
S --> H["history"]
H --> L["HelloAgentsLLM"]
L --> O["模型输出"]
O --> R["写入 history"]
R --> F["返回结果"]
6.1 项目结构
agent/
├── src/
│ ├── core/
│ │ ├── llm.ts
│ │ ├── message.ts
│ │ └── agent.ts
│ └── agents/
│ └── simple.ts
├── package.json
├── tsconfig.json
└── .env
6.2 环境变量配置
在项目根目录创建 .env 文件:
LLM_API_KEY="your_api_key_here"
LLM_MODEL_ID="gpt-3.5-turbo"
LLM_BASE_URL="https://api.openai.com/v1/"
LLM_TIMEOUT=120
| 变量名 | 必填 | 说明 |
|---|---|---|
LLM_API_KEY | ✅ | LLM 服务的 API 密钥 |
LLM_MODEL_ID | ✅ | 模型名称 |
LLM_BASE_URL | ✅ | API 地址 |
LLM_TIMEOUT | ❌ | 请求超时时间,默认 60 秒 |
常见配置示例:
智谱 AI(GLM)
LLM_API_KEY="your_zhipu_api_key"
LLM_MODEL_ID="glm-4-flash"
LLM_BASE_URL="https://open.bigmodel.cn/api/paas/v4/"
OpenAI
LLM_API_KEY="sk-xxxxxxxxxxxxxxxx"
LLM_MODEL_ID="gpt-3.5-turbo"
LLM_BASE_URL="https://api.openai.com/v1/"
DeepSeek
LLM_API_KEY="your_deepseek_api_key"
LLM_MODEL_ID="deepseek-chat"
LLM_BASE_URL="https://api.deepseek.com/v1/"
本地模型(Ollama)
LLM_API_KEY="ollama"
LLM_MODEL_ID="llama3"
LLM_BASE_URL="http://localhost:11434/v1/"
代码中加载环境变量:
import "dotenv/config";
const apiKey = process.env.LLM_API_KEY;
const model = process.env.LLM_MODEL_ID;
const baseUrl = process.env.LLM_BASE_URL;
⚠️ 注意:
.env不要提交到 Git 仓库。
6.3 核心类型:Message
export type MessageRole = "user" | "assistant" | "system" | "tool";
export interface Message {
role: MessageRole;
content: string;
timestamp?: Date;
metadata?: Record<string, any>;
}
export function createMessage(role: MessageRole, content: string): Message {
return {
role,
content,
timestamp: new Date()
};
}
Message 是整个 Agent 系统的基础,因为后面无论是对话历史、记忆系统,还是工具调用,都会围绕它展开。
6.4 LLM 客户端封装
import OpenAI from "openai";
import type { ChatCompletionMessageParam } from "openai/resources/chat";
export class HelloAgentsLLM {
private model: string;
private client: OpenAI;
constructor(options?: {
model?: string;
apiKey?: string;
baseUrl?: string;
}) {
this.model = options?.model || process.env.LLM_MODEL_ID || "gpt-3.5-turbo";
this.client = new OpenAI({
apiKey: options?.apiKey || process.env.LLM_API_KEY,
baseURL: options?.baseUrl || process.env.LLM_BASE_URL
});
}
async think(
messages: ChatCompletionMessageParam[],
temperature: number = 0.7
): Promise<string> {
const response = await this.client.chat.completions.create({
model: this.model,
messages,
temperature
});
return response.choices[0]?.message?.content || "";
}
}
这一层的意义是:把具体模型服务的差异统一封装起来,后续 Agent 不需要关心是 OpenAI、智谱还是本地模型。
6.5 Agent 基类
import { Message, createMessage, toChatMessages } from "./message";
import { HelloAgentsLLM } from "./llm";
export interface AgentConfig {
name: string;
systemPrompt?: string;
maxHistoryLength?: number;
}
export abstract class Agent {
protected llm: HelloAgentsLLM;
protected config: AgentConfig;
protected history: Message[] = [];
constructor(llm: HelloAgentsLLM, config: AgentConfig) {
this.llm = llm;
this.config = {
maxHistoryLength: 100,
...config
};
}
abstract run(input: string): Promise<string>;
protected addToHistory(message: Message): void {
this.history.push(message);
if (this.history.length > this.config.maxHistoryLength!) {
this.history = this.history.slice(-this.config.maxHistoryLength!);
}
}
getHistory(): Message[] {
return [...this.history];
}
resetHistory(): void {
this.history = [];
}
}
基类的意义在于:先把骨架统一下来。 后面的 ReActAgent、ReflectionAgent、Plan-and-SolveAgent,本质上都会继续长在这个骨架上。
6.6 实现第一个 SimpleAgent
import { Agent, AgentConfig } from "../core/agent";
import { HelloAgentsLLM } from "../core/llm";
import { createMessage, toChatMessages } from "../core/message";
export class SimpleAgent extends Agent {
constructor(llm: HelloAgentsLLM, config: AgentConfig) {
super(llm, config);
}
async run(input: string): Promise<string> {
const messages = toChatMessages([
createMessage("system", this.config.systemPrompt || "你是一个有帮助的AI助手。"),
...this.history,
createMessage("user", input)
]);
const response = await this.llm.think(messages);
this.addToHistory(createMessage("user", input));
this.addToHistory(createMessage("assistant", response));
return response;
}
}
这个版本还很简单,但已经具备了一个最小 Agent 的核心特征:
- 有统一输入输出
- 有历史上下文
- 有系统提示
- 能通过 LLM 生成响应
6.7 运行示例
import "dotenv/config";
import { HelloAgentsLLM } from "./core/llm";
import { SimpleAgent } from "./agents/simple";
async function main() {
const llm = new HelloAgentsLLM();
const agent = new SimpleAgent(llm, {
name: "AI助手",
systemPrompt: "你是一个友好的AI助手,请用简洁清晰的方式回答问题。"
});
const questions = [
"你好,请介绍一下自己",
"我刚才问了你什么?",
"请解释什么是智能体"
];
for (const question of questions) {
console.log(`用户:${question}`);
const response = await agent.run(question);
console.log(`助手:${response}\n`);
}
}
main().catch(console.error);
6.8 这个 SimpleAgent 有什么局限?
也正因为它简单,所以它还做不到很多更高级的事情:
- 不会主动调用工具
- 不会拆解复杂任务
- 不会反思和纠错
- 长期记忆能力还没有建立
但这正是它的价值所在:
我们不是一开始就把 Agent 做复杂, 而是先搭一个最小骨架,再一层层加能力。
第七章:这篇文章最重要的,不只是“定义”
如果你读到这里,我更希望你带走的不是一句“智能体是感知 + 决策 + 行动”,而是下面这几个更关键的判断:
7.1 智能体的核心,不是“会聊天”
而是:
- 能理解目标
- 能动态决策
- 能结合上下文
- 能调用外部能力
7.2 智能体也不是银弹
它不是用来替代所有传统软件的。真正好的系统,通常是:
- 确定性部分交给传统程序
- 开放性部分交给 Agent
7.3 Agent 学习路径,应该是渐进式的
建议按这个顺序学习:
- 先理解 Agent 的概念和边界
- 再实现一个最小 SimpleAgent
- 然后给它加工具
- 再给它加记忆
- 再进入 ReAct、Reflection、Plan-and-Solve 等范式
这也是为什么这篇文章是整个系列的第一篇。
总结
今天我们主要解决了 5 个问题:
- 什么是智能体? 它是一个能够感知、决策并采取行动的系统。
- 它和传统软件有什么区别? 传统程序依赖预设规则,智能体更强调运行时动态决策。
- 一个完整 Agent 通常有哪些组成? LLM、Memory、Tools、Planner 是最常见的四块能力。
- 什么时候应该用 Agent? 当任务开放、路径不固定、需要上下文和工具调用时,Agent 更有优势。
- 怎么开始动手?
最好的方式不是直接上复杂框架,而是先实现一个最小可用的
SimpleAgent。
如果用一句话结束这篇文章,那就是:
Agent 不是一个“更会聊天的机器人”,而是一种把 LLM、记忆、工具和任务执行组织起来的新软件形态。
下篇预告
下一篇我们会进入一个真正让 Agent “动起来”的经典范式:ReActAgent。
也就是让智能体学会下面这个循环:
Thought -> Action -> Observation -> 再思考
flowchart LR
T["Thought"] --> A["Action"]
A --> O["Observation"]
O --> T
到那时,Agent 就不再只是“回答问题”,而是开始真正具备“边想边做”的能力。
我们会重点讲:
- 为什么需要 ReAct?
- Thought-Action-Observation 是怎么工作的?
- 如何用 TypeScript 实现一个可运行的 ReActAgent?
- 工具调用是怎么接进来的?