Agent 核心原理:本质、ReAct 框架与工具设计最佳实践

导读 :当你使用豆包、Copilot 这类 AI 助手时,有没有想过------它为什么比普通聊天机器人"聪明"得多?答案就藏在 Agent 这个概念里。本文将从三个维度拆解 Agent 的核心原理:首先搞清楚 Agent 的本质,理解它与普通 Function Call 的根本区别;然后深入 ReAct 框架,看看模型是如何通过"思考-行动-观察"的循环完成复杂任务的;最后聚焦工具设计的最佳实践,一个好的 description 往往决定了 Agent 能不能跑通。无论你是刚接触 Agent 开发,还是已经在用 LangChain、Spring AI 等框架,理解这些底层原理都能让你写出更好的 Agent 应用。


一、Agent 的本质:从工具调用到自主决策的跨越

1.1 一个场景看懂区别

假设用户说:"帮我分析一下上月销售数据,找出问题并给出建议。"

普通 Chat + Function Call 模式

复制代码
用户提问 → 模型判断要用工具 → 调用工具拿到数据 → 分析后返回文字

整个过程一轮就结束了,工具调用的逻辑是固定的、被动的。模型只是一只"取数据的手",没有自己的思考过程和自主行为。

Agent 模式

复制代码
用户提问 → Agent 收到任务
  → 思考:我需要哪些步骤?
    1. 查上个月数据
    2. 对比历史数据
    3. 分析异常原因
    4. 生成报告
  → 执行:调用工具查数据
  → 观察:发现销售额下滑
  → 思考:为什么下滑这么多?
  → 执行:调用工具查库存、查竞品价格
  → 观察:发现品类库存积压、竞品降价
  → 思考:信息已经足够了
  → 生成最终报告

关键区别 在于:调用顺序不是人写死的,而是模型根据每一步的观察结果动态决定下一步做什么。这就是 Agent 与 Function Call 套壳的本质区别。

1.2 Agent 的三大核心要素

一个完整的 Agent 至少需要三个东西:工具记忆规划

工具------Agent 的手

工具就是 Agent 的"手"。没有工具的 Agent 只会说话,不会做事。

就像一个人如果没有手,你可以说话,但让你去做具体的事情,就做不了了。

这里有一个非常关键的避坑点:工具的质量决定了 Agent 能力的上限 。即便你的大模型再聪明,如果工具的 description 写得简陋,模型看不懂、不知道什么时候用,那大模型的能力就被白白浪费了。

在我们的项目中,以 CustomerServiceAgent 为例,工具通过 Spring AI 的 .defaultTools() 注册到 ChatClient 中:

复制代码
// com.jichi.agent.service.CustomerServiceAgent
@Service
public class CustomerServiceAgent {

    private final ChatClient chatClient;

    public CustomerServiceAgent(@Qualifier("dashScopeChatModel") ChatModel chatModel,
                                CustomerServiceTools tools) {
        this.chatClient = ChatClient.builder(chatModel)
                .defaultSystem("""
                        你是一个专业的电商客服助手,服务于一家耳机电商平台。

                        你可以:
                        - 根据订单号查询物流状态(getOrderTracking)
                        - 搜索商品、查询库存和价格(searchProducts)

                        工作原则:
                        - 需要数据时调用工具,绝对不要猜测或编造数据
                        - 如果用户提供的订单号格式不对(不是 ORD 开头),先提示用户确认
                        - 回答简洁友好,语气专业但不生硬
                        """)
                .defaultTools(tools)  // 注册工具
                .build();
    }

    public String chat(String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

注意看 System Prompt 的设计------不仅告诉模型"你是谁",还明确列出了可用工具的名称和使用场景,甚至写了"不要猜测或编造数据"这种约束规则。

记忆------Agent 的上下文

Agent 的记忆分为两种:

类型 作用 示例
短期记忆 当前任务执行过程中每步的结果 上一步查到的城市名、价格数据
长期记忆 跨任务的信息持久化 用户的偏好设置、历史对话记录

没有记忆会怎样?Agent 每执行完一步就忘了前面干了什么。比如写一个查天气助手,查完一个城市的天气后,第二轮它就忘了之前查的是哪个城市------这就是记忆没做好导致的"翻车"。

规划------Agent 的大脑

当面对一个复杂的大任务时,Agent 需要有能力将其拆解成多个小任务,然后逐步执行。规划能力本质上来自大模型的推理能力:好的模型推理强,规划就靠谱;差的模型可能会陷入循环或者跑偏。

1.3 Agent Loop:核心运行机制

Agent 的运行模式本质上就是一个循环

复制代码
观察 → 思考 → 执行 → 观察 → 思考 → 执行 → ... → 任务完成(退出循环)

退出条件非常重要。早期很多国产模型的 Agent 实现有一个通病------停不下来。模型不知道什么时候该结束,一直在循环。因此我们必须给 Agent 设计"刹车系统",比如设置最大循环次数、检测终止标志等。

1.4 Function Call vs Agent:一张表说清楚

维度 Function Call Agent
执行轮次 通常一轮 多轮循环
决策方式 固定逻辑 / 用户触发 模型自主决定下一步
任务复杂度 简单查询 复杂多步骤任务
有无规划 有(思考阶段)
Token 消耗 多(每步都在推理)

一句话总结:Function Call 是 Agent 的手,Agent Loop 才是 Agent 的大脑。有手没大脑,那就只是工具调用;有大脑加上手,那才是 Agent。

1.5 什么场景适合用 Agent?

适合的场景

  • 任务需要多步骤、多工具协作

  • 执行路径不固定,需要根据中间结果动态决策

  • 任务描述本身比较模糊,需要 AI 自己判断

不适合的场景

  • 简单问答,一次对话就搞定

  • 固定流程,可以写成 if-else 决策树

  • 延迟要求很高(Agent 推理可能几秒到几十秒)

  • 金融交易、合同签署等需要确定性流程控制的高风险场景

判断标准:如果你能把业务流程写成一棵 if-else 决策树,那大概率不需要 Agent。但如果分支本身不确定、无法穷举,就必须靠模型实时判断------这时候就需要 Agent 了。


二、ReAct 框架详解:思考-行动-观察循环

2.1 什么是 ReAct?

ReAct = Re asoning + Acting(推理 + 行动)。

这个概念源自 2022 年 Google 的一篇论文,核心思路只有一句话:让模型在行动之前,先说出它的推理过程。

与传统 Function Call 的对比:

复制代码
传统 Function Call:
  用户问题 → 模型直接输出工具调用 → 返回结果

ReAct:
  用户问题
  → Thought: 我需要先查当前价格
  → Action: 调用 get_price("iPhone")
  → Observation: 当前价格 5999 元
  → Thought: 价格拿到了,还需要看库存
  → Action: 调用 get_stock("iPhone")
  → Observation: 库存 23 台
  → Thought: 价格和库存都有了,可以回答了
  → Final Answer: 当前 iPhone 售价 5999 元,库存充足(23台)

2.2 为什么要先思考再行动?

三个核心原因:

1. 准确率更高

就像做数学题先打草稿一样,思考过程清楚了再动手,出错概率会低很多。如果不思考直接行动,很可能一顿操作猛如虎,结果全是错的。

2. 方便调试

我们可以直接看到模型为什么做了某个决策。出了问题不用猜,看 Thought 的输出就能精准定位问题在哪。

3. 支持自我修正

思考过程让模型可以在执行中重新评估。比如查到的数据看起来不对,模型可以在 Thought 中说"这个结果有点奇怪,需要再验证一下",然后调用其他工具进行核实。这种自我修正能力是传统 Function Call 做不到的。

2.3 ReAct 的三个阶段

阶段一:Thought(思考)

模型根据用户的自然语言描述,分析当前情况,制定下一步计划。这段文字一般不展示给用户,但对整个流程能否跑好至关重要

思考的质量很大程度上取决于 System Prompt 的设计。如果你发现模型的思考过程经常写废话,很可能是 System Prompt 没写好。

阶段二:Action(行动)

模型输出要调用的工具名和参数。注意,模型本身输出的是文本而不是函数调用------框架(如 Spring AI、LangChain4j)负责解析这段文本,找到对应函数并执行。

阶段三:Observation(观察)

将工具的执行结果作为下一轮思考的输入。观察结果会被追加到消息历史中,这样模型在下一轮就能看到之前所有的结果。

重要提醒 :工具的反馈要精简!不要把第三方 API 的原始 JSON 全丢给模型,几十个字段只会让模型"懵圈"。

2.4 手写一个最简单的 ReAct Agent

下面是项目中 SimpleReActAgent 的完整实现------不依赖任何 Agent 框架,纯手写 ReAct 循环,基于 Spring AI 的 ChatClient

复制代码
// com.jichi.agent.service.SimpleReActAgent
@Service
public class SimpleReActAgent {

    private final ChatClient chatClient;

    // 工具注册表:工具名 → 工具实现
    // 模型输出 "Action: getWeather",就直接拿名字去这里找对应函数
    private final Map<String, AgentTool> tools;

    public SimpleReActAgent(@Qualifier("dashScopeChatModel") ChatModel chatModel) {
        this.chatClient = ChatClient.builder(chatModel).build();
        this.tools = Map.of(
                "getWeather", this::getWeather,
                "getDate", this::getDate
        );
    }

    /**
     * 执行 ReAct 循环
     *
     * @param userTask      用户任务描述
     * @param maxIterations 最大循环次数(防止死循环)
     */
    public String run(String userTask, int maxIterations) {
        List<Message> messages = new ArrayList<>();
        String systemPrompt = buildSystemPrompt();

        messages.add(new UserMessage(userTask));

        for (int i = 0; i < maxIterations; i++) {
            // 每轮都把完整消息历史传给模型
            // 模型是无状态的,它"记得"上下文靠的就是这个列表
            String modelOutput = chatClient.prompt()
                    .system(systemPrompt)
                    .messages(messages)
                    .call()
                    .content();

            messages.add(new AssistantMessage(modelOutput));

            System.out.println("=== 第 " + (i + 1) + " 轮 ===");
            System.out.println(modelOutput);

            // 检测是否输出了最终答案
            if (modelOutput.contains("Final Answer:")) {
                return extractFinalAnswer(modelOutput);
            }

            // 解析模型想调用哪个工具、参数是什么
            String toolName = extractAction(modelOutput);
            String toolInput = extractActionInput(modelOutput);

            if (toolName == null) {
                return "模型输出格式异常,无法继续执行";
            }

            AgentTool tool = tools.get(toolName);
            String observation;
            if (tool == null) {
                observation = "工具 " + toolName + " 不存在,请换一个";
            } else {
                observation = tool.execute(toolInput);
            }

            System.out.println("Observation: " + observation);

            // 把观察结果加回消息历史,下一轮模型就能看到这个结果
            messages.add(new UserMessage("Observation: " + observation));
        }

        return "超过最大迭代次数(" + maxIterations + "),任务未完成";
    }

    @FunctionalInterface
    interface AgentTool {
        String execute(String input);
    }
}

再来看 System Prompt 怎么写------这是整个 ReAct 能跑通的关键:

复制代码
private String buildSystemPrompt() {
    return """
            你是一个智能助手,按照以下格式严格输出,每次只做一个动作:

            可用工具:
            - getWeather(input: JSON {"city": "城市名", "date": "today/tomorrow"}):查询天气
            - getDate(input: 无):获取今天的日期

            输出格式(严格遵守):
            Thought: [你的分析和下一步计划]
            Action: [工具名]
            Action Input: [工具参数,JSON 格式]

            收到 Observation 后继续思考,直到可以回答为止:
            Thought: [分析观察结果]
            Final Answer: [给用户的最终回答]

            注意:
            - 每次只输出一个 Action 或 Final Answer,不要一次输出多个
            - 工具名必须和上面列表完全一致
            """;
}

文本解析部分------从模型的自由文本输出中抽取 Action 和参数:

复制代码
private String extractFinalAnswer(String output) {
    int idx = output.indexOf("Final Answer:");
    if (idx == -1) return output;
    return output.substring(idx + "Final Answer:".length()).strip();
}

private String extractAction(String output) {
    for (String line : output.split("\n")) {
        if (line.startsWith("Action:")) {
            return line.substring("Action:".length()).strip();
        }
    }
    return null;
}

private String extractActionInput(String output) {
    for (String line : output.split("\n")) {
        if (line.startsWith("Action Input:")) {
            return line.substring("Action Input:".length()).strip();
        }
    }
    return "";
}
几个关键设计点

为什么用 Map<String, AgentTool> 存工具?

这是"工具注册表"的思路。模型输出 Action: getWeather,我们直接通过 key 在 Map 中找到对应的工具实现。注意 AgentTool 是一个函数式接口,工具通过方法引用(this::getWeather)注册进去。这和 Spring AI 的 @Tool 注解底层是同一个思路------本质都是把工具注册到一个映射表中,通过名称反射调用。

为什么每轮都要传完整的消息历史?

因为大模型本身是无状态的,每次调用都是一个全新请求,它不知道之前发生了什么。Agent Loop 的"记忆"就体现在我们把所有历史消息塞进去------这样模型才能接着推理。

当然,消息越多 Token 越多,这就是 Agent 比普通聊天贵的原因。

System Prompt 为什么每次都要传?

System Prompt 是 Agent 的"规则书",丢了就可能违背各种原则。所以每轮都要带上。

2.5 循环退出控制的几个关键点

控制手段 说明
最大循环次数 简单任务设 3-5 次,复杂任务设 10-15 次,防止死循环
Final Answer 检测 检测到终止标志立即退出
工具不存在处理 返回提示信息,让模型换一个工具
格式异常处理 模型输出格式不符合预期时的兜底逻辑
多 Action 处理 模型一次输出多个 Action 时,只取第一个

来看 SimpleReActAgent 中的退出控制是怎么实现的:

复制代码
// 1. 最大循环次数------for 循环的上限
for (int i = 0; i < maxIterations; i++) { ... }

// 2. Final Answer 检测------检测到关键词立即 return
if (modelOutput.contains("Final Answer:")) {
    return extractFinalAnswer(modelOutput);
}

// 3. 格式异常处理------解析不到 Action 时直接终止
if (toolName == null) {
    return "模型输出格式异常,无法继续执行";
}

// 4. 工具不存在处理------返回提示让模型换工具
if (tool == null) {
    observation = "工具 " + toolName + " 不存在,请换一个";
}

// 5. 超时兜底------循环结束仍未完成
return "超过最大迭代次数(" + maxIterations + "),任务未完成";

Controller 层的调用也很简洁,默认传入最大循环 10 次:

复制代码
// com.jichi.agent.controller.ReActController
@RestController
@RequestMapping("/api/agent/react")
public class ReActController {
    private final SimpleReActAgent agent;

    @PostMapping
    public String run(@RequestBody TaskRequest request) {
        return agent.run(request.task(), 10);
    }

    record TaskRequest(String task) {}
}

血泪教训:如果没有这些控制,模型可能陷入"上次结果不对->再查一次->还是不对->继续查"的死循环。而且这些调用都是付费的,一跑起来可能直接把预算跑没。


三、工具设计最佳实践:Description 决定成败

3.1 第一原则:Description 是给模型看的,不是给人看的

写代码注释、写 JavaDoc 时,我们习惯给同事看,用的是人类的表达方式。但工具的 description 是直接喂给模型的,需要机器能理解的完整性

反面教材

复制代码
@Tool(description = "获取天气")

模型看到这个,只知道和天气有关,但不知道:是天气预报还是历史天气?返回中文还是英文?参数是城市名还是经纬度?

正确写法 ------来看项目中 AssistantTools 的真实实现:

复制代码
// com.jichi.agent.tools.AssistantTools
@Tool(description = """
        查询指定城市的实时天气。
        适用于:用户询问某城市当前天气、今天天气、是否需要带伞等问题。
        返回:城市、天气状况、温度范围、湿度、风力。
        注意:只查当前天气,不查天气预报。
        """)
public String getWeather(
        @ToolParam(description = "城市名,中文,例如:北京、上海、深圳") String city) {
    return String.format("""
            城市:%s
            天气:晴转多云
            温度:12~20°C
            湿度:55%%
            风力:东南风 2 级
            查询时间:%s
            """, city, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
}

这个 description 覆盖了四个维度:功能说明、适用场景、返回内容、边界约束。模型看完就清楚了:查的是什么天气、返回什么内容、什么时候该用、什么情况不该用。

自检方法:写完 description 之后,把自己当成一台机器来读------不要脑补,纯粹看文字,能不能知道这个工具干什么。如果你都搞不清楚,模型更搞不清楚。

3.2 多工具场景下的区分度

单个工具时问题不大,但 Agent 场景通常有 5-10 个工具,这时候区分度就至关重要。

反面教材

复制代码
@Tool(description = "查询订单")     // 工具 A
@Tool(description = "获取订单信息")  // 工具 B

模型看到这两个几乎一样的描述,只能随机选一个------这就解释了为什么有时候 Agent 表现好、有时候表现差。

正确写法 ------来看项目中 CustomerServiceTools 的两个工具是怎么做区分的:

复制代码
// com.jichi.agent.tools.CustomerServiceTools
@Tool(description = """
        根据订单号查询订单状态和物流信息。
        适用于:用户询问订单发货情况、快递位置、预计到达时间等物流相关问题。
        返回:订单状态、物流单号、当前物流位置、预计到达时间。
        不适用于:查询订单商品详情或价格(用 getOrderDetail 工具)。
        """)
public String getOrderTracking(
        @ToolParam(description = "订单号,格式 ORD + 数字,例如 ORD20240115001")
        String orderId) { ... }

@Tool(description = """
        根据关键词搜索商品,查看商品信息。
        适用于:用户咨询某类商品是否有货、价格区间、推荐哪款商品等。
        返回:匹配商品的名称、价格、库存数量、商品评分。
        """)
public String searchProducts(
        @ToolParam(description = "搜索关键词,例如:耳机、无线、降噪") String keyword,
        @ToolParam(description = "最多返回几个结果,默认 3,最多 10") int limit) { ... }

每个工具的职责边界清清楚楚:一个管物流、一个管商品。注意 getOrderTracking 还特意写了"不适用于"------这是在告诉模型什么时候不该选这个工具,比直接告诉"该用什么"更有效。

3.3 工具粒度:不能太细,也不能太粗

太细的问题

复制代码
@Tool(description = "获取用户名")
@Tool(description = "获取用户邮箱")
@Tool(description = "获取用户手机号")

获取一个用户的基本信息,要调三次工具、跑三轮循环,Token 飙升,体验极差。

太粗的问题

复制代码
@Tool(description = "获取所有信息")

模型看了也懵------"所有信息"到底是什么?这个工具到底能干什么?而且这种工具的内部实现必然很复杂,调用了大量接口,出问题也很难排查。

粒度原则:一个工具完成一件完整的事,这件事有明确的输入和输出。

来看 AssistantTools 中的工具设计,每个工具都遵循了这个原则:

复制代码
// com.jichi.agent.tools.AssistantTools

// 工具1:查天气------一件完整的事
@Tool(description = """
        查询指定城市的实时天气。
        适用于:用户询问某城市当前天气、今天天气、是否需要带伞等问题。
        返回:城市、天气状况、温度范围、湿度、风力。
        注意:只查当前天气,不查天气预报。
        """)
public String getWeather(@ToolParam(description = "城市名,中文") String city) { ... }

// 工具2:查时间------另一件完整的事
@Tool(description = "获取当前日期和时间。适用于用户询问今天几号、现在几点、这周星期几等时间相关问题。")
public String getCurrentDateTime() { ... }

// 工具3:查汇率------又一件完整的事
@Tool(description = """
        查询两种货币之间的汇率。
        适用于:用户询问人民币兑换外币的比率、外汇换算、出国换多少钱等问题。
        返回:汇率数值和换算说明。
        """)
public String getExchangeRate(
        @ToolParam(description = "源货币,3位代码,例如:CNY、USD、EUR、JPY") String from,
        @ToolParam(description = "目标货币,3位代码") String to) { ... }

天气是一件事,时间是一件事,汇率是一件事------分开但不过分细分。

3.4 工具返回值:给模型看的,不是给程序员看的

反面教材:直接把对象序列化成 JSON 扔给模型

复制代码
{"orderId":"ORD123","status":"SHIPPED","items":[{"sku":"SKU001","name":"iPhone","qty":1,"price":5999.00,"discount":0.0,"taxRate":0.13,...}],"createTime":"2024-01-15T10:30:00Z","updateTime":"2024-01-16T08:00:00Z",...}

程序员看得懂,但模型解析起来很吃力,而且大量无关字段会干扰判断、浪费 Token。

正确做法 ------看 CustomerServiceTools.getOrderTracking() 的返回值设计:

复制代码
// com.jichi.agent.tools.CustomerServiceTools
public String getOrderTracking(String orderId) {
    String[] order = MOCK_ORDERS.get(orderId);
    if (order == null) return "未找到订单:" + orderId + ",请确认订单号是否正确";

    return String.format("""
            订单号:%s
            状态:%s
            物流单号:%s
            当前位置:%s
            预计到达:%s
            """,
            orderId, order[0],
            order[1] != null ? order[1] : "暂无(订单未发货)",
            order[2] != null ? order[2] : "暂无信息",
            order[3] != null ? order[3] : "待定");
}

返回的是结构化自然语言 ,而不是原始 JSON。模型处理自然语言的能力远强于解析复杂 JSON,回答质量会高很多。同时注意空值处理------null 不是直接返回给模型,而是翻译成"暂无(订单未发货)"这样的可读文本。

3.5 写操作必须明确返回执行结果

模型需要知道操作到底成功还是失败,才能决定下一步怎么做。看 AssistantTools 中创建提醒的返回值:

复制代码
// com.jichi.agent.tools.AssistantTools
@Tool(description = """
        创建一个定时提醒事项。
        适用于:用户说"提醒我..."、"帮我记一下..."、"x点提醒我"等需要设置提醒的场景。
        返回:提醒创建结果(成功或失败)。
        """)
public String createReminder(
        @ToolParam(description = "提醒内容描述") String content,
        @ToolParam(description = "提醒时间,格式:yyyy-MM-dd HH:mm,例如:2024-03-20 09:00") String remindAt) {
    return String.format("提醒已创建:「%s」,将在 %s 提醒你", content, remindAt);
}

返回明确告诉模型"已创建"以及具体内容和时间。如果返回一个模糊的结果,模型就不知道是该继续还是该重试,整个 Agent 流程可能会卡住。

3.6 工具的异常处理:超时与错误

Agent 场景中,工具调用外部 API 可能超时或失败。关键原则是:不要抛异常,而是返回字符串告知模型------这样模型可以基于错误信息做后续决策。

来看 TimeoutAwareTools 的实现:

复制代码
// com.jichi.agent.tools.TimeoutAwareTools
@Component
public class TimeoutAwareTools {

    private final ExecutorService executor = Executors.newCachedThreadPool();

    @Tool(description = "查询外部天气 API,获取实时天气数据")
    public String getWeatherFromApi(
            @ToolParam(description = "城市名") String city) {

        Future<String> future = executor.submit(() -> callExternalWeatherApi(city));

        try {
            return future.get(5, TimeUnit.SECONDS);  // 5 秒超时
        } catch (TimeoutException e) {
            future.cancel(true);
            return "天气服务响应超时(5秒),无法获取实时数据,请告知用户稍后重试";
        } catch (Exception e) {
            return "天气服务异常:" + e.getMessage() + ",请告知用户稍后重试";
        }
    }
}

超时后返回的字符串是给模型看的指令------"请告知用户稍后重试"。模型读到这个,就知道该怎么回复用户了。如果直接抛异常,Agent 循环可能直接崩掉。

3.7 安全边界:Agent 场景下的三条铁律

与普通 Function Call 不同,Agent 中的工具调用是模型自动触发的,没有人在中间点"确认执行"。一旦出现越权操作,后果可能很严重。

三条安全原则

  1. 写操作必须做权限校验:验证用户身份、检查操作权限,可以直接对接公司内部的权限系统。

  2. 错误信息必须明确:让模型知道失败原因,才能做出合理的下一步决策。

  3. 高风险操作加人工确认:比如取消订单、删除数据等操作,即使是 MVP 阶段,也建议加一个用户确认的逻辑。

    @Tool(description = "取消指定订单,需要验证用户身份和订单状态")
    public String cancelOrder(String orderId, String userId) {
    // 1. 权限校验
    if (!authService.hasPermission(userId, "ORDER_CANCEL")) {
    return "取消失败:当前用户无权取消此订单。";
    }
    // 2. 状态校验
    Order order = orderService.getById(orderId);
    if (order.getStatus() == OrderStatus.SHIPPED) {
    return "取消失败:订单已发货,请走售后流程。";
    }
    // 3. 执行操作
    orderService.cancel(orderId);
    return "订单 " + orderId + " 已成功取消。";
    }


四、总结

本文从三个维度拆解了 Agent 的核心原理:

  1. Agent 的本质:不是 Function Call 的简单包装,而是从"被动工具调用"到"自主决策"的跨越。核心在于 Agent Loop------一个由模型自己驱动的"观察-思考-执行"循环。三大要素是工具、记忆和规划。

  2. ReAct 框架:目前最主流的 Agent 推理框架,通过 Thought-Action-Observation 三阶段循环,让模型在行动前先说出推理过程,从而提升准确率、方便调试、支持自我修正。循环退出控制(最大次数、终止标志、异常处理)是生产环境必须关注的重点。

  3. 工具设计最佳实践:Description 写给模型看而非给人看;多工具场景要有清晰的区分度;粒度遵循"一个工具一件事"原则;返回值用结构化自然语言而非原始 JSON;写操作必须做权限校验和结果反馈;异常情况返回字符串而非抛异常。

理解了这些底层原理之后,无论使用 LangChain、Spring AI 还是其他框架,你都能知道框架背后到底在干什么------所有框架的底层,跑的都是这些套路。


如果这篇文章对你有帮助,欢迎点赞收藏,后续会继续更新 Agent 实战系列,包括多 Agent 协作、Plan & Execute 等进阶内容。

相关推荐
Bill Adams2 小时前
如何基于Harness Engineering设计一个Agent OS
人工智能·prompt·agent·智能体·harness
knqiufan2 小时前
拆解 Claude Code SubAgent:隔离、专业化与权限设计
ai·agent·claude code
Old Uncle Tom2 小时前
Claude Code 上下文压缩分析
人工智能·ai·agent
gz7seven2 小时前
大模型学习笔记------微调之LoRA
lora·大模型·大模型微调·lora原理
在修仙3 小时前
我用 Electron + Ollama,手搓了一个真正能干活的本地 AI Agent
agent
码徒3 小时前
2026 前端技术十大趋势:84% 的开发者已经在用 AI 写代码了
前端·agent·ai编程
beiju3 小时前
Agent 工具系统:如何给 Agent 装上双手
agent
竹之却3 小时前
【Agent-阿程】AI先锋杯·14天征文挑战第14期-第1天-大模型微调技术实战
人工智能·机器学习·lora·大模型·qlora·微调技术
CoderJia程序员甲4 小时前
GitHub 热榜项目 - 日榜(2026-04-08)
人工智能·ai·大模型·github·ai教程