AI Agent开发实战(一):什么是Agent —— 从 Chatbot 到自主体
一、引言:
本文是《AI Agent 开发实战系列》的开篇,过去一年AI领域迎来了飞速发展,LLM从聊天助手进化为了可自主执行甚至可自进化的系统也就是Agent。今天我们不谈玄乎的"通用人工智能",而是回答一个非常具体的问题:当我们说"做一个 Agent"时,到底在做什么?
二、先破一个误解:Agent 不是"更聪明的聊天机器人"
很多人第一次接触 Agent,是从"我用 ChatGPT 帮我完成一道面试八股文"开始的。于是自然会觉得:Agent 就是能力更强的 Chatbot 吧?
不是。这两者有一个本质区别,用一句话概括:
Chatbot 生成"答案",Agent 完成"任务"。
举个例子感受一下差异。你说:"帮我查一下明天北京到上海的高铁,选一班上午出发的,把车次发我。"·
Chatbot 的反应:"抱歉,我无法访问实时购票系统。你可以去 12306 查询……"——它只能基于已有知识生成文本。
Agent 的反应:调用查询工具 → 拿到车次列表 → 筛选上午班次 → 把结果整理后返回。它采取了行动,并根据行动的结果决定下一步。
区别的核心不在于"谁更聪明",而在于是否能与外部世界交互并自主推进。Chatbot 是一个"文本函数":输入文本,输出文本。Agent 是一个"回路":它会观察环境、做出决策、执行动作、再观察动作带来的变化,如此循环直到任务完成。
三、Agent 的核心回路:感知—规划—行动—观察
几乎所有 Agent,不管用什么框架、多复杂,骨架都是同一个循环:
┌──────────────────────────────────────────┐
│ │
▼ │
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ 感知 │───▶ │ 规划 │───▶ │ 行动 │───▶ │ 观察 │
│ Perceive│ │ Plan │ │ Act │ │ Observe │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
接收目标/ LLM 决定下 调用工具 获取结果,
环境状态 一步做什么 执行操作 判断是否完成
│
完成? ──否──────┘
│
是
▼
返回结果
拆开看这四步:
感知(Perceive):Agent 接收当前的状态——用户的目标、历史对话、上一步动作的结果。本质上是把这些信息组织成一段上下文,喂给 LLM。
规划(Plan):LLM 基于当前上下文,判断"接下来该做什么"。可能是"我需要调用搜索工具",也可能是"信息够了,可以给出最终答案"。这一步是 Agent 的"大脑"。
行动(Act):执行 LLM 决定的动作。通常是调用一个工具(函数)——搜索、查数据库、发请求、写文件。
观察(Observe):拿到动作的结果(搜索返回了什么、API 报了什么错),把它加入上下文。然后判断:任务完成了吗?没完成就回到"感知",带着新信息再走一轮。
关键点在于:是 LLM 在每一轮里自主决定下一步,而不是程序员写死流程。 这就是 Agent 与传统自动化脚本的分水岭。脚本的分支是人预先编排好的;Agent 的分支是模型在运行时决定的。
四、Agent 的四个能力维度
回路是骨架,让骨架"活起来"需要四种能力。你可以用它们来评估任何一个 Agent 系统的成熟度:
1. 工具使用(Tool Use) Agent 通过"工具"与世界交互。工具本质是一个函数 + 一段描述,告诉 LLM"这个工具能干什么、需要什么参数"。没有工具的 Agent 只能空想,有了工具才能查天气、发邮件、跑代码。
2. 记忆(Memory) 短期记忆是当前任务的上下文(这次对话说了什么、做了哪几步);长期记忆是跨会话沉淀的信息(用户偏好、历史结论)。记忆决定了 Agent 能不能处理长任务、能不能"记住你是谁"。这是本系列第四部分的重头戏。
3. 规划(Planning) 简单任务一步到位,复杂任务需要拆解:先做什么、后做什么、某步失败了怎么办。规划能力决定了 Agent 能处理多复杂的任务。ReAct、Plan-and-Execute 等范式(第 03 篇详谈)都是在解决"如何规划"。
4. 多步自主(Autonomy) 能不能在没有人逐步指挥的情况下,连续走完多个步骤直到目标达成。自主度越高,人干预越少,但可控性和风险也随之变化。
一个成熟的 Agent = 稳定的回路 + 这四种能力的组合。你会发现,后面所有的技术话题——MCP 是在强化"工具使用",向量库是在强化"记忆",多智能体、harness都是在强化"规划与自主",——都能挂到这个框架上。
五、落到工程:当代 Agent 的组成要素简介
| 要素 | 一句话是什么 | 对应能力 |
|---|---|---|
| Harness(执行框架) | 承载回路的运行时骨架,管状态、调度、错误恢复 | 回路载体 |
| Prompt(提示/指令) | 定义角色、约束、工具契约与输出格式的系统指令 | 规划 |
| Loop(智能体循环) | 感知—规划—行动—观察的迭代内核 | 多步自主 |
| Tools / Function Calling | Agent 与外部世界交互的函数接口 | 工具使用 |
| MCP(模型上下文协议) | 工具/数据源接入的标准化协议,解决 M×N 集成 | 工具使用 |
| A2A(智能体协作协议) | Agent 之间跨系统协作与任务委派的标准 | 规划/自主 |
| Skill(技能) | 可复用、可组合的能力封装(含指令+工具+流程) | 工具使用/规划 |
| Memory(记忆) | 短期上下文 + 长期跨会话记忆 | 记忆 |
| RAG / 检索 | 从外部知识库动态召回相关信息注入上下文 | 记忆 |
| Context Engineering | 动态管理窗口内该放什么、如何压缩 | 记忆/规划 |
| Planner(规划器) | 任务分解、子目标管理、反思纠错 | 规划 |
| Guardrails(护栏) | 输入输出校验、权限控制、风险拦截 | 安全 |
| Observability(可观测) | Trace/日志/指标,追踪每步执行 | 工程支撑 |
一句话:一个 Agent 项目 = Harness 跑 Loop,Loop 靠 Prompt 驱动,通过 Tools/MCP 行动,用 Memory/RAG 记忆,由 Guardrails 兜底,被 Observability 观测。 本篇聚焦最核心的 Loop,其余要素后续逐一拆解。
六、一个不依赖框架的最小 Agent
import java.util.*;
public class MiniAgent {
// ---------- 1. 定义工具(Act 的载体)----------
static String calculator(String expr) {
try {
// 仅演示:用脚本引擎算式子,生产环境需做沙箱与白名单
Object r = new javax.script.ScriptEngineManager()
.getEngineByName("JavaScript").eval(expr);
return String.valueOf(r);
} catch (Exception e) {
return "计算错误: " + e.getMessage(); // 错误如实返回给 LLM
}
}
static String getWeather(String city) {
var db = Map.of("北京", "晴,26°C", "上海", "多云,29°C", "广州", "雷阵雨,31°C");
return db.getOrDefault(city, "暂无 " + city + " 的天气数据");
}
// 工具注册表:名字 -> 实际实现
static String callTool(String name, Map<String, String> args) {
return switch (name) {
case "calculator" -> calculator(args.get("expression"));
case "get_weather" -> getWeather(args.get("city"));
default -> "未知工具: " + name;
};
}
// 工具描述(JSON):告诉 LLM 有哪些工具可用,这是"规划"的依据
static final String TOOLS_SCHEMA = """
[
{"type":"function","function":{"name":"calculator",
"description":"计算数学表达式,如 '23*45'",
"parameters":{"type":"object",
"properties":{"expression":{"type":"string","description":"数学表达式"}},
"required":["expression"]}}},
{"type":"function","function":{"name":"get_weather",
"description":"查询指定城市的当前天气",
"parameters":{"type":"object",
"properties":{"city":{"type":"string","description":"城市名,如'北京'"}},
"required":["city"]}}}
]""";
// ---------- 2. Agent 回路 ----------
static String runAgent(String userQuery, int maxSteps) {
// 感知:把目标放进上下文
List<Map<String, Object>> messages = new ArrayList<>();
messages.add(Map.of("role", "system",
"content", "你是一个助手。需要时调用工具,信息足够后直接回答用户。"));
messages.add(Map.of("role", "user", "content", userQuery));
for (int step = 1; step <= maxSteps; step++) {
System.out.println("\n--- 第 " + step + " 步 ---");
// 规划:调用大模型,让它决定下一步(回答 or 调工具)
LlmReply reply = chat(messages, TOOLS_SCHEMA); // 封装 HTTP 请求
// 分支 A:没有工具调用 -> 任务完成
if (reply.toolCalls.isEmpty()) {
System.out.println("[最终回答] " + reply.content);
return reply.content;
}
// 分支 B:有工具调用 -> 行动 + 观察
messages.add(reply.raw); // 先把 LLM 的决策记入上下文
for (ToolCall tc : reply.toolCalls) {
System.out.println("[规划] 调用工具: " + tc.name + tc.args);
String result = callTool(tc.name, tc.args); // 行动
System.out.println("[观察] 工具返回: " + result);
// 把观察结果加入上下文,进入下一轮
messages.add(Map.of("role", "tool",
"tool_call_id", tc.id, "content", result));
}
}
return "达到最大步数,未能完成任务。";
}
// ---------- 3. 试跑 ----------
public static void main(String[] args) {
runAgent("北京今天天气怎么样?顺便帮我算一下 128 * 39 等于多少", 5);
}
}
运行后你会看到类似的输出:
--- 第 1 步 ---
[规划] 调用工具: get_weather({'city': '北京'})
[观察] 工具返回: 晴,26°C
[规划] 调用工具: calculator({'expression': '128 * 39'})
[观察] 工具返回: 4992
--- 第 2 步 ---
[最终回答] 北京今天天气晴,26°C。128 × 39 = 4992。
请仔细体会这段输出里发生了什么:
- 我们从没写过"先查天气再算数"这样的流程,是 LLM 自己在第 1 步决定同时调用两个工具的。
- 第 2 步 LLM 拿到两个工具的结果后,自主判断"信息够了",于是不再调用工具,直接给出最终答案——循环自然终止。
- 整个 runAgent 方法就是那个"感知—规划—行动—观察"回路的忠实实现,核心不过一个 for 循环加两个分支。
这就是 Agent 的全部本质。 后面所有框架(LangGraph、CrewAI、AutoGen……)做的事情,归根结底都是在这个回路上加东西:更好的状态管理、更强的记忆、多个 Agent 协作、可观测性、错误恢复……但内核不变。理解了这个循环,你就理解了 Agent。
七、总结
这一篇我们建立了理解 Agent 的核心心智模型,回顾一下:
- Agent 与 Chatbot 的本质区别是完成任务 vs 生成答案,关键在于能否与外部世界交互并自主推进;
- 所有 Agent 的骨架都是感知—规划—行动—观察的回路,由 LLM 在每轮自主决策下一步;
- 让回路活起来的四种能力是工具使用、记忆、规划、多步自主,后续所有技术都能挂到这个框架上;
- 自主度是按场景权衡的设计选择,不是越高越好,大多数生产系统落在光谱中间;
- 一个真正的 Agent 内核可以只有 40 行,框架做的是在此之上的工程增强。
更多推荐

所有评论(0)