核心概念层——深入理解 Agent 是什么

1 Agent vs ChatBot:从根本上理解区别

1.1 一个直观的例子

假设你对 AI 说:"帮我分析一下我们公司上周的销售数据,找出表现最好的产品,并给团队发一封总结邮件"。

ChatBot 的反应

复制代码
markdown

ChatBot: "要分析销售数据,你可以按以下步骤操作:

  1. 打开数据库,执行 SQL 查询获取上周的销售记录

  2. 使用 Excel 或 Python 进行数据汇总

  3. 找出销售额最高的产品

  4. 撰写邮件总结发送给团队

你需要我帮你写 SQL 查询语句吗?"

→ ChatBot 只能"说",不能"做"。它给你建议,但执行全靠你自己。

Agent 的反应

复制代码
vbnet

Agent 思考: 用户想要三件事: ①分析销售数据 ②找最佳产品 ③发邮件

Step 1: 调用 query_database 工具

→ 执行 SQL 查询上周销售数据

→ 结果: 获得 500 条销售记录

Step 2: 调用 data_analysis 工具

→ 分析数据,按产品分组统计

→ 结果: 产品 A 销售额最高($125,000),增长 23%

Step 3: 调用 send_email 工具

→ 撰写邮件: "团队好,上周销售总结如下..."

→ 发送给团队成员列表

Agent: "已完成分析并发送邮件!上周表现最好的产品是 A,

销售额 $125,000,环比增长 23%。详细总结已发送到团队邮箱。"

→ Agent 不仅"说",还能"做"。它自主规划、执行、汇报。

1.2 本质区别的三个维度

复制代码
makefile

维度一: 自主性 (Autonomy)

──────────────────────────────────────────→

ChatBot Agent

被动响应 主动行动

一问一答 自主循环

用户驱动每一步 用户只给目标

维度二: 能力边界 (Capability)

──────────────────────────────────────────→

ChatBot Agent

只能生成文本 能执行动作

无法访问外部系统 连接各种工具和API

知识受限于训练数据 可以实时获取信息

维度三: 状态管理 (State)

──────────────────────────────────────────→

ChatBot Agent

无状态/简单上下文 完整的状态管理

不记住跨会话信息 长期记忆

没有目标追踪 目标导向,跟踪进度

1.3 Agent 的定义

综合来说,Agent = LLM + 自主循环 + 工具调用 + 记忆

复制代码
java

// 伪代码展示 Agent 的核心循环

public class Agent {

private final LLM llm; // 大脑

private final List<Tool> tools; // 工具箱

private final Memory memory; // 记忆

public Agent(LLM llm, List<Tool> tools, Memory memory) {

this.llm = llm;

this.tools = tools;

this.memory = memory;

}

public String run(String userGoal) {

// Agent 的核心运行循环

// 把用户目标加入工作上下文

var context = memory.getRelevant(userGoal);

while (true) { // ← 关键: 自主循环,不是一问一答

// 1. 思考: 分析当前状态,决定下一步

var thought = llm.think(userGoal, context, tools);

// 2. 判断: 目标完成了吗?

if (thought.isGoalComplete()) {

return thought.finalAnswer();

}

// 3. 行动: 调用工具执行

var toolResult = executeTool(

thought.chosenTool(),

thought.toolParams()

);

// 4. 观察: 把工具结果加入上下文

context.add(toolResult);

// 5. 记忆: 存储有用的信息

memory.save(thought, toolResult);

// 循环继续,直到目标完成或超过最大步数

}

}

}


2 Agent 四大组成部分------深入理解

2.1 大脑(LLM):Agent 的决策中心

LLM 在 Agent 中扮演"指挥官"的角色:

复制代码
css

LLM 在 Agent 中的职责:

┌─────────────────────────────────────────────┐

│ LLM (大脑) │

│ │

│ ① 理解意图: 用户到底想要什么? │

│ "帮我查下订单" → 意图=查询, 实体=订单 │

│ │

│ ② 制定计划: 需要几步?先做什么? │

│ 步骤1: 查订单 → 步骤2: 分析状态 → 步骤3: 回答 │

│ │

│ ③ 选择工具: 这一步该用什么工具? │

│ "查订单" → 使用 query_orders 工具 │

│ │

│ ④ 生成参数: 工具需要什么输入? │

│ query_orders(order_id="12345") │

│ │

│ ⑤ 分析结果: 工具返回了什么?达到目标了吗? │

│ 结果显示订单已发货 → 可以回答用户了 │

│ │

│ ⑥ 生成回答: 组织语言回复用户 │

│ "您的订单 12345 已发货,预计明天送达" │

└─────────────────────────────────────────────┘

选择合适的 LLM 作为大脑

复制代码
makefile

不同任务对"大脑"的要求:

简单任务(意图分类、格式转换):

→ 小模型即可 (Haiku / GPT-4o-mini)

→ 快速、便宜

中等任务(文档问答、代码修改):

→ 中等模型 (Sonnet / GPT-4o)

→ 平衡速度和质量

复杂任务(多步推理、架构设计):

→ 强力模型 (Opus / GPT-4o with reasoning)

→ 准确性最重要

实际做法: 一个 Agent 内部可以用多个模型!

  • 路由层: 小模型(判断用户意图)

  • 执行层: 中等模型(执行具体任务)

  • 难题: 大模型(处理复杂推理)

2.2 规划(Planning):Agent 的策略中心

规划能力是 Agent 区别于 ChatBot 的关键能力。

任务分解(Task Decomposition)

复制代码
vbnet

体验AI代码助手

代码解读

复制代码

用户: "帮我搭建一个博客网站"

Agent 的任务分解:

├── 1. 技术选型

│ ├── 1.1 确定前端框架 (React/Vue/Next.js)

│ ├── 1.2 确定后端方案 (Node.js/Python)

│ └── 1.3 确定数据库 (PostgreSQL/MongoDB)

├── 2. 项目初始化

│ ├── 2.1 创建项目目录结构

│ ├── 2.2 初始化包管理

│ └── 2.3 配置开发环境

├── 3. 核心功能开发

│ ├── 3.1 文章 CRUD API

│ ├── 3.2 用户认证

│ └── 3.3 前端页面

├── 4. 测试与部署

│ ├── 4.1 单元测试

│ ├── 4.2 Docker 容器化

│ └── 4.3 部署上线

计划调整(Re-planning)

好的 Agent 会根据执行结果动态调整计划:

复制代码
makefile

原计划: 使用 MySQL 作为数据库

执行步骤 2.1 时发现: 服务器只有 512MB 内存

Agent 反思: "MySQL 对内存要求较高,512MB 可能不够。

应该改用 SQLite 或使用云数据库。"

调整后的计划: 改用 SQLite(轻量级,适合小型博客)

→ 这就是 Re-planning,Agent 能根据实际情况灵活调整

2.3 记忆(Memory):Agent 的信息中心

三种记忆的详细解释

复制代码
css

┌───────────────────────────────────────────────────┐

│ Agent 记忆系统 │

│ │

│ ┌────────────────────────────────────────────┐ │

│ │ 短期记忆 (Short-term Memory) │ │

│ │ │ │

│ │ 本质: LLM 的上下文窗口 │ │

│ │ 内容: 当前对话的 messages 列表 │ │

│ │ 容量: 受上下文窗口限制 (如 128K tokens) │ │

│ │ 寿命: 当前对话结束就没了 │ │

│ │ │ │

│ │ 例: │ │

│ │ messages = [ │ │

│ │ {system: "你是助手"}, │ │

│ │ {user: "你好"}, │ │

│ │ {assistant: "你好!"}, │ │

│ │ {user: "帮我查天气"}, ← 短期记忆 │ │

│ │ {assistant: "北京今天晴"}, │ │

│ │ ... │ │

│ │ ] │ │

│ └────────────────────────────────────────────┘ │

│ │

│ ┌────────────────────────────────────────────┐ │

│ │ 长期记忆 (Long-term Memory) │ │

│ │ │ │

│ │ 本质: 外部持久化存储 │ │

│ │ 内容: 跨对话的知识和用户信息 │ │

│ │ 容量: 理论上无限 │ │

│ │ 寿命: 永久(除非主动删除) │ │

│ │ │ │

│ │ 存储方式: │ │

│ │ ├── 向量数据库: 把文档变成数字向量存储, │ │

│ │ │ 能做"语义搜索"(详见模块五 5.2) │ │

│ │ ├── 知识图谱: 用"实体→关系→实体"的网络 │ │

│ │ │ 存储知识(详见模块五 5.3) │ │

│ │ ├── Key-Value 存储: 用户偏好等简单键值对 │ │

│ │ └── 关系数据库: 结构化历史数据 │ │

│ │ │ │

│ │ 例: │ │

│ │ - 用户偏好: "张三喜欢 Python,用 VS Code" │ │

│ │ - 历史知识: "订单系统用的是 Spring Boot" │ │

│ │ - 对话摘要: "上次讨论了 Redis 缓存方案" │ │

│ └────────────────────────────────────────────┘ │

│ │

│ ┌────────────────────────────────────────────┐ │

│ │ 工作记忆 (Working Memory) │ │

│ │ │ │

│ │ 本质: 当前任务的中间状态 │ │

│ │ 内容: 已执行的步骤、中间结果、当前计划 │ │

│ │ 容量: 较小,只保留关键信息 │ │

│ │ 寿命: 当前任务结束就清除 │ │

│ │ │ │

│ │ 例 (Agent 正在做数据分析): │ │

│ │ working_memory = { │ │

│ │ "current_plan": ["①查数据", "②分析", ...],│ │

│ │ "completed_steps": ["①查数据 ✓"], │ │

│ │ "intermediate_results": { │ │

│ │ "raw_data": "500条销售记录", │ │

│ │ "top_product": "产品A" │ │

│ │ } │ │

│ │ } │ │

│ └────────────────────────────────────────────┘ │

└───────────────────────────────────────────────────┘

上下文窗口管理策略

当对话太长,超出上下文窗口时怎么办?

复制代码
makefile

策略一: 滑动窗口

保留最近 N 轮对话,删除最早的

对话1, 对话2, 对话3, 对话4, 对话5

→ 删除对话1

对话2, 对话3, 对话4, 对话5, 对话6

优点: 简单

缺点: 可能丢失重要的早期信息

策略二: 摘要压缩

用 LLM 把旧对话压缩成摘要

对话1-10 的摘要, 对话11, 对话12, ...

优点: 保留了关键信息

缺点: 摘要本身消耗 token,可能丢失细节

策略三: RAG 检索

把历史对话存入向量库,需要时检索

问到相关话题 → 从向量库搜索相关历史 → 注入上下文

(RAG 是什么?见模块一 1.1.4;完整实现见模块五 5.1)

优点: 不丢失信息,按需检索

缺点: 实现复杂,检索可能不准

策略四: 混合方案(推荐)

最近 5 轮 → 完整保留(短期记忆)

更早的 → 压缩为摘要

关键事实 → 存入长期记忆(向量库/图谱)

2.4 工具(Tools):Agent 的执行中心

工具的本质

复制代码
arduino

体验AI代码助手

代码解读

复制代码

工具 = 一个函数 + 描述信息

┌─────────────────────────────────┐

│ 工具定义 │

│ │

│ 名称: search_web │

│ 描述: "在互联网上搜索信息" │ ← LLM 靠这个决定是否使用

│ 参数: │

│ - query (string, 必填) │ ← LLM 生成这个参数值

│ - max_results (int, 可选) │

│ 返回: 搜索结果列表 │

│ │

│ 实际执行: │

│ String searchWeb(String query) │ ← 你写的代码

│ // 调用搜索 API │

│ return results; │

└─────────────────────────────────┘

常见工具类型

复制代码
arduino

Agent 工具箱:

├── 信息获取类

│ ├── 网络搜索 (Google/Bing API)

│ ├── 文档检索 (RAG 向量搜索)

│ ├── 数据库查询 (SQL 执行)

│ └── API 调用 (天气/股票/新闻)

├── 执行操作类

│ ├── 代码执行 (Java/Python/Shell)

│ ├── 文件操作 (读/写/修改)

│ ├── 发送消息 (邮件/Slack/钉钉)

│ └── 系统操作 (创建任务/更新状态)

├── 计算分析类

│ ├── 数学计算 (计算器)

│ ├── 数据分析 (Java Stream/Apache Commons Math)

│ └── 图表生成 (JFreeChart/ECharts)

└── 交互类

├── 浏览器操作 (Browser Use)

├── 屏幕操作 (Computer Use)

└── 人工确认 (Human-in-the-loop)

工具设计的好与坏

复制代码
java

体验AI代码助手

代码解读

复制代码

// ❌ 差的工具设计

var badTool = """

{

"name": "do_stuff",

"description": "执行各种操作",

"parameters": {

"type": "object",

"properties": {

"action": {"type": "string"},

"target": {"type": "string"},

"option1": {"type": "string"},

"option2": {"type": "string"},

"option3": {"type": "string"},

"option4": {"type": "string"}

}

}

}

""";

// 问题: 名字太模糊、描述不清楚、参数含义不明、参数太多

// ✅ 好的工具设计

var goodTool = """

{

"name": "search_user_orders",

"description": "根据用户ID查询该用户的订单列表。返回订单号、状态、金额、时间。",

"parameters": {

"type": "object",

"properties": {

"user_id": {

"type": "string",

"description": "用户的唯一标识符,如 'U12345'"

},

"status": {

"type": "string",

"enum": ["pending", "shipped", "delivered", "cancelled"],

"description": "按订单状态筛选(可选)"

},

"limit": {

"type": "integer",

"description": "返回结果数量上限,默认10",

"default": 10

}

},

"required": ["user_id"]

}

}

""";

// 优点: 名称精确、描述详细、参数含义清楚、只有必要的才 required


3 推理策略------Agent 如何思考

3.1 ReAct 模式(深入理解)

ReAct(Reasoning + Acting)是最经典、最常用的 Agent 推理模式。

完整的 ReAct 循环

复制代码
vbnet

用户: "帮我了解竞品公司 XYZ 最近的融资情况和主要产品"

===== 第 1 轮 =====

Thought: 用户想了解 XYZ 公司的融资和产品信息。

我需要先搜索该公司的基本信息。

Action: search_web(query="XYZ公司 融资")

Observation: "XYZ 公司于 2024 年 3 月完成 B 轮融资 5000 万美元,

由红杉资本领投..."

===== 第 2 轮 =====

Thought: 已获得融资信息。现在需要搜索他们的产品信息。

Action: search_web(query="XYZ公司 主要产品")

Observation: "XYZ 公司旗下有三款产品: ProductA(协作工具)、

ProductB(项目管理)、ProductC(文档工具)..."

===== 第 3 轮 =====

Thought: 我已经收集到了融资和产品两方面的信息,

可以综合整理回答用户了。

Action: [无需调用工具]

Answer: "XYZ 公司融资情况:

  • 2024年3月完成B轮融资5000万美元

  • 领投方: 红杉资本

主要产品:

  • ProductA: 协作工具

  • ProductB: 项目管理

  • ProductC: 文档工具"

ReAct 的 Prompt 模板

复制代码
java

var REACT_PROMPT = """

你是一个助手,可以使用工具来回答问题。

可用工具:

%s

请使用以下格式:

Thought: 分析当前情况,决定下一步

Action: 工具名称

Action Input: 工具参数(JSON格式)

Observation: [工具返回的结果会自动填入这里]

... (可以重复 Thought/Action/Observation 多次)

当你认为已经有足够的信息来回答时:

Thought: 我已经有了足够的信息

Final Answer: 最终回答

开始!

用户问题: %s

""".formatted(toolsDescription, userQuestion);

Java 实现一个简单的 ReAct Agent

复制代码
java

import com.fasterxml.jackson.databind.ObjectMapper;

import com.fasterxml.jackson.databind.node.ObjectNode;

import java.util.*;

// 使用模块 1 中的 ClaudeClient(已定义 chat / chatWithTools / stream 方法)

// 定义工具

var toolsMap = Map.<String, java.util.function.Function<Map<String, String>, String>>of(

"search_web", args -> "搜索结果: 关于'" + args.get("query") + "'的信息...",

"calculator", args -> {

// 注意: 生产环境中应使用安全的表达式引擎

try {

var engine = new javax.script.ScriptEngineManager()

.getEngineByName("JavaScript");

return String.valueOf(engine.eval(args.get("expression")));

} catch (Exception e) {

return "计算错误: " + e.getMessage();

}

}

);

var toolsSchema = """

{ "name": "search_web", "description": "搜索互联网获取信息", "input_schema": { "type": "object", "properties": { "query": {"type": "string", "description": "搜索关键词"} }, "required": \["query"

}

},

{

"name": "calculator",

"description": "计算数学表达式",

"input_schema": {

"type": "object",

"properties": {

"expression": {"type": "string", "description": "数学表达式"}

},

"required": ["expression"]

}

}

]

""";

/** 一个简单的 ReAct Agent */

String runAgent(String userMessage, int maxSteps) {

var objectMapper = new ObjectMapper();

var tools = objectMapper.readTree(toolsSchema);

var messages = new ArrayList<Message>();

messages.add(new Message("user", userMessage));

var system = "你是一个有用的助手,可以使用工具来回答问题。";

for (int step = 0; step < maxSteps; step++) {

// 1. 调用 LLM(使用 ClaudeClient 的 chatWithTools)

var response = client.chatWithTools(system, messages, tools);

var stopReason = response.get("stop_reason").asText();

var content = response.get("content");

// 2. 检查是否需要调用工具

if ("tool_use".equals(stopReason)) {

// 将 assistant 回复加入消息列表

messages.add(new Message("assistant", content.toString()));

var toolResults = new ArrayList<Map<String, Object>>();

for (var block : content) {

if ("tool_use".equals(block.get("type").asText())) {

var funcName = block.get("name").asText();

var funcArgs = objectMapper.convertValue(

block.get("input"),

new com.fasterxml.jackson.core.type.TypeReference<Map<String, String>>() {}

);

var toolId = block.get("id").asText();

System.out.printf(" [Step %d] 调用工具: %s(%s)%n", step + 1, funcName, funcArgs);

// 3. 执行工具

var result = toolsMap.get(funcName).apply(funcArgs);

System.out.printf(" [Step %d] 工具结果: %s%n", step + 1,

result.substring(0, Math.min(100, result.length())));

// 4. 把工具结果传回 LLM

toolResults.add(Map.of(

"type", "tool_result",

"tool_use_id", toolId,

"content", result

));

}

}

messages.add(new Message("user", objectMapper.writeValueAsString(toolResults)));

} else {

// LLM 没有调用工具,说明它准备直接回答

for (var block : content) {

if ("text".equals(block.get("type").asText())) {

return block.get("text").asText();

}

}

}

}

return "Agent 达到最大步数限制";

}

// 使用

var answer = runAgent("北京和上海的面积分别是多少?哪个更大?", 5);

System.out.println("\n最终回答: " + answer);

3.2 CoT(思维链)的深入应用

Zero-shot CoT

复制代码
java

// 只需要在 prompt 末尾加一句话就能大幅提升推理准确率

// ❌ 直接问(准确率低)

var directPrompt = "一个书架有 3 层,每层放 8 本书。小明拿走了 5 本,又放回 2 本。现在有多少本书?";

// ✅ 加上 CoT 引导(准确率大幅提升)

var cotPrompt = """

一个书架有 3 层,每层放 8 本书。小明拿走了 5 本,又放回 2 本。现在有多少本书?

请一步步思考。""";

// LLM 的回答:

// 1. 书架有 3 层,每层 8 本,总共 3 × 8 = 24 本

// 2. 小明拿走 5 本: 24 - 5 = 19 本

// 3. 又放回 2 本: 19 + 2 = 21 本

// 答: 现在有 21 本书

CoT 在 Agent 决策中的应用

复制代码
java

var AGENT_SYSTEM_PROMPT = """

你是一个智能助手。在选择工具前,请先进行推理分析。

推理格式:

<thinking>

  1. 用户的核心需求是什么?

  2. 需要哪些信息才能满足需求?

  3. 哪个工具最适合获取这些信息?

  4. 需要什么参数?

</thinking>

然后调用合适的工具。""";

// 这样 Agent 的工具选择准确率会显著提高

// 因为它在"行动"之前先"想清楚"了

3.3 Plan-and-Execute 策略详解

复制代码
vbscript

Plan-and-Execute 的核心思想:

把"规划"和"执行"分成两个独立阶段

┌─────────────────────────────────────────┐

│ Planner(规划者) │

│ │

│ 输入: 用户目标 │

│ 输出: 完整的步骤计划 │

│ │

│ "帮我写一个 Python 爬虫" │

│ → Plan: │

│ 1. 确定目标网站和数据 │

│ 2. 分析网页结构 │

│ 3. 编写爬虫代码 │

│ 4. 测试运行 │

│ 5. 处理异常和边界情况 │

└──────────────────┬──────────────────────┘

┌─────────────────────────────────────────┐

│ Executor(执行者) │

│ │

│ 逐步执行计划中的每一步 │

│ │

│ Step 1: search_web("Python 爬虫 教程") │

│ Step 2: analyze_html(url) │

│ Step 3: write_code(spec) │

│ Step 4: execute_code(code) │

│ Step 5: fix_errors(errors) │

│ │

│ 如果某步失败 → 通知 Planner 重新规划 │

└─────────────────────────────────────────┘

Plan-and-Execute vs ReAct 的选择

|---------|--------------------------|-----------------------|
| 场景 | 推荐策略 | 原因 |
| 简单查询 | ReAct | 一两步就能完成,不需要提前规划 |
| 明确的多步任务 | Plan-and-Execute | 步骤可预见,先规划效率更高 |
| 探索性任务 | ReAct | 不确定需要什么信息,边做边看 |
| 复杂项目 | Plan-and-Execute + ReAct | 大计划用 P&E,每步执行用 ReAct |

3.4 Reflection(反思)策略详解

复制代码
javascript

Reflection 的核心: Agent 执行后评估自己的输出,发现问题后改进

┌──────────┐ ┌──────────┐ ┌──────────┐

│ Generator│ ──→ │ Evaluator│ ──→ │ Reflector│

│ (执行) │ │ (评估) │ │ (反思) │

│ │ │ │ │ │

│ 生成代码 │ │ 跑测试 │ │ 分析错误 │

│ 写回答 │ │ 检查质量 │ │ 找出原因 │

│ │ │ 打分 │ │ 提出改进 │

└──────────┘ └──────────┘ └─────┬────┘

↑ │

└──────────────────────────────────┘

改进后重试

代码生成中的 Reflection 示例

复制代码
java

/** 带反思能力的代码生成 Agent */

String codeAgentWithReflection(String task, int maxRetries) {

String code = null;

String reflection = null;

for (int attempt = 0; attempt < maxRetries; attempt++) {

// 1. 生成代码

if (attempt == 0) {

code = generateCode(task);

} else {

code = generateCode(task, code, reflection);

}

// 2. 执行代码并检查结果

var result = executeCode(code);

if (result.success()) {

return code; // 成功了,直接返回

}

// 3. 反思: 分析失败原因

reflection = client.chat(

"你是一个代码审查专家。",

List.of(new Message("user", """

任务: %s

生成的代码: %s

错误信息: %s

请分析:

  1. 代码为什么出错?

  2. 具体哪一行有问题?

  3. 应该如何修复?

""".formatted(task, code, result.error())))

);

System.out.printf("第 %d 次尝试失败,反思: %s%n", attempt + 1, reflection);

}

return null; // 多次尝试都失败

}


4 Agent 循环的完整生命周期

把所有概念串起来,一个 Agent 处理请求的完整过程:

复制代码
yaml

用户发起请求

┌──────────────────────────────────────────────────┐

│ Phase 1: 理解与规划 │

│ │

│ ① 检索长期记忆(用户历史、偏好) │

│ ② LLM 理解用户意图 │

│ ③ 判断是否需要规划(简单任务直接执行) │

│ ④ 如需规划 → 分解为步骤列表 │

└───────────────────────┬──────────────────────────┘

┌──────────────────────────────────────────────────┐

│ Phase 2: 执行循环(ReAct Loop) │

│ │

│ for each step in plan: │

│ ① Thought: 当前步骤需要什么?用什么工具? │

│ ② Action: 调用选定的工具 │

│ ③ Observation: 获取工具执行结果 │

│ ④ 更新工作记忆(记录中间结果) │

│ ⑤ 判断: 是否需要调整计划? │

│ - 如果结果不符预期 → Reflection → Re-plan │

│ - 如果正常 → 继续下一步 │

└───────────────────────┬──────────────────────────┘

┌──────────────────────────────────────────────────┐

│ Phase 3: 总结与输出 │

│ │

│ ① 综合所有中间结果 │

│ ② 生成最终回答 │

│ ③ 更新长期记忆(保存有价值的信息) │

│ ④ 返回结果给用户 │

└──────────────────────────────────────────────────┘


5 实战练习

练习 1: 手动模拟 ReAct 循环

复制代码
java

/**

* 目标: 不用任何框架,纯用 API 实现一个 ReAct Agent

* 功能: 能搜索天气和做数学计算

*/

import com.fasterxml.jackson.databind.ObjectMapper;

import com.fasterxml.jackson.databind.JsonNode;

import java.util.*;

import java.util.function.Function;

// 使用模块 1 中的 ClaudeClient 和 Message

public class ReactAgentDemo {

private static final ObjectMapper objectMapper = new ObjectMapper();

// 模拟工具(实际中你会调用真正的 API)

static String getWeather(Map<String, String> args) {

var city = args.get("city");

var weatherData = Map.of(

"北京", "晴,25°C,湿度30%",

"上海", "多云,28°C,湿度65%",

"广州", "雷阵雨,32°C,湿度80%"

);

return weatherData.getOrDefault(city, "未找到 " + city + " 的天气数据");

}

static String calculate(Map<String, String> args) {

try {

// 注意: 生产环境中不要用 ScriptEngine,这里仅为演示

var engine = new javax.script.ScriptEngineManager()

.getEngineByName("JavaScript");

return String.valueOf(engine.eval(args.get("expression")));

} catch (Exception e) {

return "计算错误: " + e.getMessage();

}

}

// 工具注册

static final Map<String, Function<Map<String, String>, String>> availableTools = Map.of(

"get_weather", ReactAgentDemo::getWeather,

"calculate", ReactAgentDemo::calculate

);

static final String toolsForApi = """

{ "name": "get_weather", "description": "获取指定城市的实时天气信息", "input_schema": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"} }, "required": \["city"

}

},

{

"name": "calculate",

"description": "计算数学表达式,如 '2+3*4' 或 '100/7'",

"input_schema": {

"type": "object",

"properties": {

"expression": {"type": "string", "description": "数学表达式"}

},

"required": ["expression"]

}

}

]

""";

/** ReAct Agent 实现 */

static String reactAgent(ClaudeClient client, String question, int maxSteps)

throws Exception {

var tools = objectMapper.readTree(toolsForApi);

var messages = new ArrayList<Message>();

messages.add(new Message("user", question));

var system = "你是一个有用的助手。请使用提供的工具来回答用户问题。";

System.out.println("\n[思考] 问题: " + question + "\n");

for (int step = 0; step < maxSteps; step++) {

var response = client.chatWithTools(system, messages, tools);

var stopReason = response.get("stop_reason").asText();

var content = response.get("content");

// 如果 LLM 决定直接回答(不调用工具)

if (!"tool_use".equals(stopReason)) {

for (var block : content) {

if ("text".equals(block.get("type").asText())) {

var answer = block.get("text").asText();

System.out.println("[完成] 最终回答: " + answer);

return answer;

}

}

}

// 处理工具调用

messages.add(new Message("assistant", content.toString()));

var toolResults = new ArrayList<Map<String, Object>>();

for (var block : content) {

if ("tool_use".equals(block.get("type").asText())) {

var funcName = block.get("name").asText();

var funcArgs = objectMapper.convertValue(

block.get("input"),

new com.fasterxml.jackson.core.type.TypeReference<Map<String, String>>() {}

);

var toolId = block.get("id").asText();

System.out.printf(" [工具] Step %d: %s(%s)%n", step + 1, funcName, funcArgs);

// 执行工具

var result = availableTools.get(funcName).apply(funcArgs);

System.out.printf(" [结果] %s%n", result);

// 工具结果传回 LLM

toolResults.add(Map.of(

"type", "tool_result",

"tool_use_id", toolId,

"content", result

));

}

}

messages.add(new Message("user", objectMapper.writeValueAsString(toolResults)));

}

return "达到最大步数限制";

}

// 测试

public static void main(String[] args) throws Exception {

var client = new ClaudeClient();

reactAgent(client, "北京和上海今天哪个城市更热?温差是多少度?", 10);

}

}


本模块学习检查清单

  • 能清晰解释 Agent 和 ChatBot 的 3 个核心区别
  • 能画出 Agent 的四大组成部分架构图
  • 理解 ReAct、CoT、Plan-and-Execute、Reflection 四种推理策略的区别和适用场景
  • 理解三种记忆(短期/长期/工作记忆)的区别
  • 理解工具调用的完整流程
  • 能手写一个简单的 ReAct Agent(使用 API + 工具调用)

原文链接:https://juejin.cn/post/7621769753745326107

相关推荐
川石课堂软件测试1 小时前
软件测试|常见面试题整理
数据库·python·jmeter·mysql·appium·postman·prometheus
大囚长1 小时前
意识与物质的一体两面
人工智能
●VON1 小时前
小米突然发短信:送你100万亿Token!有人已收到,有人还没?手把手教你白嫖
数据库·人工智能·skills
星辰徐哥1 小时前
AI时代最容易上手的5个副业,月入5000+
人工智能·ai·chatgpt·工具·副业·ai副业
bang冰冰1 小时前
Trae工具安装和使用教程(新手零基础入门,全程无坑)
java·人工智能·python
●VON1 小时前
四大AI生图工具横评:GPT Image 2 一骑绝尘!但Gemini的免费策略才是真正的王炸
人工智能·gpt·chatgpt·大模型·image
User_芊芊君子1 小时前
聊聊自由开发者常用的学习机会全解析
开发语言·人工智能·python
码农阿豪1 小时前
AI时代,国产数据库的黄金机遇:以KB数据库为例,看自主创新如何引领未来
数据库·人工智能·oracle
AI玫瑰助手1 小时前
Agent架构:规划、记忆、工具、反思
人工智能·架构·agent