← 返回上一页

第8章: Agent 智能体入门:工具调用让AI会办事

构建自主决策的AI助手

🤖 什么是AI Agent(智能体)?

❌ 先理解大模型的局限性

大模型的工作原理:根据前面的文字,推理下一个词最可能是什么
就像接龙游戏,看到"今天天气",它会推测下一个词可能是"很好"、"不错"等。

这导致很多事情它做不了:

  • 问:"现在几点了?" → 答不出来(它只能推理,不能查看时钟)
  • 问:"帮我算一下 123.456 × 789" → 容易算错(它是推理,不是真正计算)
  • 问:"今天北京天气怎么样?" → 不知道(它的知识是训练时的,不是实时的)
  • 问:"帮我发个邮件" → 做不到(它只能生成文字,不能操作软件)

✅ AI Agent的解决方案

核心思路:给大模型配备工具,让它知道什么时候该用什么工具!

同样的问题,有了工具就能解决:

  • 问:"现在几点了?" → Agent调用获取时间工具 → 返回"2024年11月23日 20:30"
  • 问:"帮我算一下 123.456 × 789" → Agent调用计算器工具 → 返回精确结果
  • 问:"今天北京天气怎么样?" → Agent调用天气API工具 → 返回实时天气
  • 问:"帮我发个邮件" → Agent调用邮件发送工具 → 真的发送邮件

🎯 AI Agent的定义

AI Agent = 大模型(大脑)+ 工具集(手脚)+ 自主决策能力

它能自己判断什么时候需要用工具,自己选择用哪个工具,自己调用工具完成任务!

🧠 Skills(技能):让 Agent 可复用的“工作方法”

在 Agent 工程里,除了“工具(Tool)”之外,常见还有一个更高一层的概念:Skill(技能)。 你可以把 Skill 理解成:面向某类任务的可复用解决方案,它把“怎么做”沉淀成一套稳定流程。

Skill 到底包含什么?

  • 目标(Goal):这个技能要解决什么问题(例如“查询天气并给出穿衣建议”)。
  • 步骤(Steps):分解成可执行的流程(先拿城市 → 查天气 → 归纳重点 → 给建议)。
  • 工具编排(Tools Orchestration):需要调用哪些工具、按什么顺序调用、失败怎么兜底。
  • 约束与策略(Policy):权限、脱敏、输出格式、引用来源、重试/超时等。
  • 提示模板(Prompt Template,可选):固定的角色设定、输出结构、边界条件。

Tool(工具)是什么?

工具更像是一个原子能力:一次调用完成一件事。 例如:get_weather(city)sql_query(query)send_email(to, subject, body)

Skill(技能)是什么?

技能更像是一套可复用的“完成任务的方法论/流程”,通常会编排多个工具,并附带规则和输出格式。 例如“写周报技能”= 读取任务列表 + 总结进展 + 输出固定模板。

📌 一个直观例子:"查天气并给建议"

工具:get_weather(city)(只负责返回天气数据)
技能:“查天气并给建议” = 解析用户城市 → 调用 get_weather → 提取温度/降雨/风力 → 生成穿衣/出行建议 → 输出为固定结构

🏗️ 工程化怎么用 Skill?

  • Skill Library:把高频任务沉淀成技能库(例如“查询客户信息”“生成会议纪要”“故障排查”)。
  • 可观测:记录每次 Skill 的步骤、工具调用、耗时、失败原因,方便迭代。
  • 治理:给 Skill 配权限、限流、审计和灰度发布,避免“一个技能把系统打挂”。
  • 复用与组合:复杂任务往往是多个 Skill 串起来(Skill-of-Skills)。

🆚 LLM vs AI Agent:有什么区别?

对比项 普通LLM AI Agent
工作方式 被动响应 主动规划和执行
能力范围 只能对话 可以使用工具、搜索、计算等
任务处理 一次性回答 多步骤规划和执行
举例 "北京天气怎么样?" → 给出训练数据中的答案 "北京天气怎么样?" → 调用天气API → 返回实时天气

🧠 用人类行为理解AI Agent

人类处理任务的过程:信息输入 → 大脑处理 → 采取行动

🍎 场景:小明想吃树上的苹果
👀 感知
小明看到树上有苹果
🧠 规划
思考:需要梯子 → 找到梯子 → 爬上去
🛠️ 行动
使用梯子,爬上去摘苹果
😋 反馈
苹果很甜,任务完成!
💡 AI Agent的工作方式完全一样!

感知环境 → LLM大脑规划 → 调用工具执行 → 观察结果反馈

🧩 MCP:把工具/数据接入 Agent 的标准协议

当你的 Agent 需要接入越来越多的外部能力(数据库、内部系统、文件、浏览器、代码执行器……),工程上最痛的一点是: 每个工具都要写一套“对接胶水”,不同框架还不通用MCP(Model Context Protocol)的目标就是:提供一个标准化方式,让模型/Agent 能“发现并调用工具”,并能读取结构化资源。

MCP 解决什么?

  • 工具标准化:用统一的 schema 描述工具输入/输出,让 Agent 可以“自发现”工具。
  • 资源标准化:把文件、数据库、知识库等暴露为“可读取资源(Resources)”。
  • 更好治理:权限、审计、限流、版本管理、沙箱化等更容易落地。

MCP 的核心概念

  • MCP Server:工具/资源提供方(例如“数据库查询服务”“文件系统服务”“浏览器服务”)。
  • Tool:可执行能力(带参数定义),例如 sql_query / send_email / get_weather
  • Resource:可读取的数据/上下文,例如 file://...、某个知识库条目等。
  • Client(通常在 Agent 框架里):负责连接 MCP Server,列举工具/资源,并把调用结果回传给模型。
MCP 在 Agent 工程体系中的位置(谁负责什么)
🤖 大模型(LLM) 🧠 Agent 框架(Client) 🧩 MCP Server 🌐 外部系统 1. 推理/决策 选择 tool / resource 输出 Action 指令 2. MCP Client 列举 Tools/Resources 执行调用并回填结果 3. MCP Server 统一协议暴露能力 Tools / Resources 4. 外部系统 DB / 文件 / Web / 内部服务 权限/审计/限流 Action Call/Read Execute Result Observation Context 要点:MCP 解决“怎么把工具/数据接进来并标准化治理”,而 Agent 框架负责“把模型决策翻译成调用并回填”。
text
# 一个典型的 MCP 工作流(概念级)

1) Agent 连接 MCP Server
2) 列举可用 Tools / Resources
3) 模型输出下一步动作:调用 tool(name, args)
4) Agent 执行 tool 调用 -> 得到结果
5) 将结果作为 Observation 追加到上下文 -> 继续 Think/Act/Observe
⚠️ 工程建议:
  • 先做白名单:只暴露必要工具,避免“全能工具”导致风险外溢。
  • 工具要可审计:记录工具名、参数、耗时、结果摘要,方便排错与安全审计。
  • 尽量幂等:同一调用重复执行不应产生不可控副作用(尤其是转账/删库/发邮件类)。

🔧 AI Agent的核心模块

一个完整的AI Agent系统包含4个核心模块:

1️⃣ LLM大脑(Planning & Reasoning)

LLM是Agent的"大脑",负责理解任务、规划步骤、做出决策。

核心能力:
  • 任务分解:将复杂任务拆分成多个简单步骤
  • 推理决策:分析当前状态,决定下一步做什么
  • 思维链(CoT):一步步思考,找到最优路径
  • 自我反思:从错误中学习,优化执行策略

2️⃣ 记忆模块(Memory)

Agent需要记住历史信息,才能做出更好的决策。

📝 短期记忆

作用:记住当前对话的上下文
实现:Prompt中的历史消息
限制:受模型上下文长度限制(如128k tokens)

🗄️ 长期记忆

作用:存储大量历史知识和经验
实现:向量数据库(如Chroma)
优势:可以存储海量信息,随时检索

记忆的作用:不是“让模型更聪明”,而是让 Agent 更稳定

1) 让多步任务可持续:Agent 经常要循环 Think→Act→Observe 多轮,没有记忆就会“做完就忘”,下一轮无法引用工具结果。
2) 降低上下文 token 成本:把冗长对话/中间步骤做摘要,保留关键信息,避免每轮把整段历史塞回模型。
3) 支持个性化与复用:把用户偏好/长期事实(如常用城市、预算范围)存下来,下一次对话无需重复问。
4) 便于回放与排错:把每轮 tool 调用与 observation 记录下来,出现错误时能复盘“模型当时看到什么、做了什么”。

从理论上把 Memory 分 3 层(最常用的工程划分)

  • 对话上下文(Conversation Memory):本次会话的最近 N 轮消息(短期)。通常直接放在 messages 里。
  • 可检验的过程状态(State / Scratchpad):工具调用记录、关键中间结论、任务进度(例如第几步、已收集哪些信息)。这类记忆应尽量结构化(JSON)。
  • 长期记忆(Long-term Memory):跨会话保存的事实/偏好/经验。常见做法是“文本 + embedding”入向量库,再按 query 检索召回。

写入/读取策略(决定“记忆是否有用”的关键)

  • 什么时候写入:只把“可复用且稳定”的信息写长期记忆(用户偏好、已确认事实、最终产物摘要)。不要把猜测/幻觉写进去。
  • 什么时候读取:在决策前先检索与当前任务相关的记忆(Top-K),把召回结果作为补充上下文,而不是把全库塞进 prompt。
  • 怎么控制长度:短期对话可用“滑动窗口 + 摘要”;长期记忆用“检索 + 去重 + 阈值”控制进入上下文的数量。
  • 安全与隐私:避免存敏感信息(手机号/Token/身份证)。必须存时要脱敏、加密、并做用户授权/可删除。

3️⃣ 工具模块(Tools)

工具让Agent突破LLM的限制,能做更多事情!

常见工具类型:
🔍 搜索工具

Google搜索、网页爬虫

🧮 计算工具

计算器、代码执行器

📊 数据工具

数据库查询、API调用

🎨 创作工具

图片生成、视频编辑

4️⃣ 行动模块(Action)

Agent根据规划,执行具体的操作。

行动方式:
  • 文本输出:生成回答、撰写文章
  • 工具调用:执行搜索、计算、API请求
  • 环境交互:操作应用程序、控制机器人

📐 AI Agent公式

AI Agent = LLM大脑 + 记忆 + 工具 + 行动能力

🔄 ReAct机制:Agent的核心工作模式

ReAct(Reasoning + Acting)是AI Agent最重要的工作机制,让Agent能够边思考边行动,像人一样解决问题!

🤔 为什么需要ReAct?

❌ 问题:LLM虽然强大,但有局限性:

  • 知识会过时(训练后就固定了)
  • 不能做数学计算(容易算错)
  • 无法访问实时信息
  • 不能执行具体操作

✅ 解决:ReAct让LLM能够:

  • 自己决定什么时候需要使用工具
  • 选择合适的工具解决问题
  • 根据工具返回的结果继续思考
  • 一步步完成复杂任务

📊 ReAct工作流程

ReAct的核心是思考(Thought)→ 行动(Action)→ 观察(Observation)的循环:

经典 Agent 循环(Think → Act → Observe)
Think 思考/计划 Act 调用工具 Observe 观察结果 Environment 外部世界/工具系统
关键点:每一轮都会先“思考下一步”,再“执行动作(通常是工具调用)”,然后“观察动作结果”,再进入下一轮。
💭 步骤1:思考(Thought)

LLM分析问题,决定下一步该做什么
例如:"用户想知道周杰伦演唱会信息,我需要搜索最新消息"

🎬 步骤2:行动(Action)

执行具体操作,调用工具
例如:调用Google搜索API,搜索"2024周杰伦演唱会"

👀 步骤3:观察(Observation)

查看工具返回的结果
例如:搜索返回了多个网页链接和摘要

🔁 循环或结束

如果信息足够 → 生成最终答案
如果信息不够 → 继续思考、行动、观察

🎯 ReAct实际案例

问题:"2024年周杰伦最新演唱会的时间和地点是什么?"

💭 思考1:

用户想知道2024年周杰伦最新演唱会信息,需要搜索最新资讯

🎬 行动1:

调用Google搜索API,搜索"2024周杰伦演唱会"

👀 观察1:

搜索返回了6个相关网页链接

💭 思考2:

需要查看网页内容获取详细信息

🎬 行动2:

访问第一个网页,提取内容

👀 观察2:

网页显示:2024年12月上海演唱会

💭 思考3:

已获取足够信息,可以回答用户

✅ 最终答案:

2024年周杰伦最新演唱会将于12月在上海举行

💡 提示:这个例子里“工具”只是概念上的

这里用的是“调用工具”的思路来理解 ReAct(多步推进任务)。如果你关心“工具规模变大后的复用与治理”,可以回看本章前面的 🧩 MCP 小节。

💻 使用通义千问实现简单Agent

现在让我们用通义千问API实现一个带工具调用的Agent。

📦 需要安装的库

text
pip3 install dashscope

步骤1:定义工具

python
import dashscope
from dashscope import Generation
import json
import os

# 设置API Key
dashscope.api_key = os.getenv('DASHSCOPE_API_KEY')

class SimpleAgent:
    def __init__(self):
        """初始化Agent"""
        self.messages = []
        self.tools = {
            "calculator": self.calculator,
            "get_weather": self.get_weather
        }
    
    def calculator(self, expression):
        """计算器工具"""
        try:
            result = eval(expression)
            return f"计算结果:{result}"
        except Exception as e:
            return f"计算错误:{str(e)}"
    
    def get_weather(self, city):
        """获取天气工具(模拟)"""
        # 实际应用中应该调用真实的天气API
        weather_data = {
            "北京": "晴天,15-25℃",
            "上海": "多云,18-26℃",
            "深圳": "小雨,22-28℃"
        }
        return weather_data.get(city, f"{city}的天气信息暂时无法获取")

步骤2:定义工具描述

通义千问需要知道有哪些工具可用,以及如何使用它们:

python
    def get_tool_definitions(self):
        """定义可用工具的描述"""
        return [
            {
                "type": "function",
                "function": {
                    "name": "calculator",
                    "description": "执行数学计算,支持加减乘除等基本运算",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "expression": {
                                "type": "string",
                                "description": "要计算的数学表达式,如:'3.14 * 2 + 5'"
                            }
                        },
                        "required": ["expression"]
                    }
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "get_weather",
                    "description": "获取指定城市的天气信息",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "city": {
                                "type": "string",
                                "description": "城市名称,如:北京、上海"
                            }
                        },
                        "required": ["city"]
                    }
                }
            }
        ]

步骤3:实现ReAct循环

python
    def run(self, user_input):
        """运行Agent - ReAct循环"""
        print(f"🙋 用户:{user_input}\n")
        
        # 添加用户消息
        self.messages.append({
            "role": "user",
            "content": user_input
        })
        
        # ReAct循环:最多执行5轮
        for i in range(5):
            print(f"--- 第{i+1}轮思考 ---")
            
            # 调用LLM进行推理
            response = Generation.call(
                model='qwen-plus',
                messages=self.messages,
                tools=self.get_tool_definitions(),
                result_format='message'
            )
            
            # 基础错误检查
            if response is None:
                print("❌ 错误:API响应为空")
                return "抱歉,无法获取响应"

            if response.status_code != 200:
                print(f"❌ 错误:API请求失败,状态码:{response.status_code}")
                if hasattr(response, 'message') and response.message:
                    print(f"错误信息:{response.message}")
                return "抱歉,API请求失败"

            if response.output is None:
                print("❌ 错误:API响应output为空")
                return "抱歉,无法获取响应内容"
                
            assistant_message = response.output.choices[0].message

            # 如果assistant_message是对象,转换为字典
            if not isinstance(assistant_message, dict):
                assistant_message = assistant_message.__dict__
            
            # 检查是否需要调用工具
            tool_calls = assistant_message.get('tool_calls')
            if tool_calls:
                # 有工具调用
                tool_call = tool_calls[0]
                # tool_call 可能是 dict,也可能是对象
                if isinstance(tool_call, dict):
                    function_name = tool_call['function']['name']
                    arguments = json.loads(tool_call['function']['arguments'])
                else:
                    function_name = tool_call.function.name
                    arguments = json.loads(tool_call.function.arguments)
                
                print(f"💭 思考:需要使用工具 {function_name}")
                print(f"🎬 行动:调用 {function_name},参数:{arguments}")
                
                # 执行工具
                if function_name in self.tools:
                    result = self.tools[function_name](**arguments)
                    print(f"👀 观察:{result}\n")
                    
                    # 将工具调用和结果添加到消息历史
                    self.messages.append({
                        "role": "assistant",
                        "content": "",
                        "tool_calls": [tool_call]
                    })
                    self.messages.append({
                        "role": "tool",
                        "content": result,
                        "name": function_name
                    })
                else:
                    print(f"❌ 错误:工具 {function_name} 不存在\n")
                    break
            else:
                # 没有工具调用,返回最终答案
                final_answer = assistant_message.get('content', '无法获取回答')
                print(f"✅ 最终答案:{final_answer}\n")
                return final_answer
        
        return "抱歉,无法完成任务"

步骤4:使用示例

python
# 创建Agent
agent = SimpleAgent()

# 测试1:需要使用计算器
print("=" * 50)
print("测试1:计算任务")
print("=" * 50)
agent.run("请帮我计算 123.456 * 789 + 456 的结果")

# 测试2:需要查询天气
print("\n" + "=" * 50)
print("测试2:天气查询")
print("=" * 50)
agent = SimpleAgent()  # 重新创建,清空历史
agent.run("北京今天天气怎么样?")

# 测试3:不需要工具
print("\n" + "=" * 50)
print("测试3:普通对话")
print("=" * 50)
agent = SimpleAgent()
agent.run("请介绍一下Python编程语言")

✅ 运行结果示例

text
==================================================
测试1:计算任务
==================================================
🙋 用户:请帮我计算 123.456 * 789 + 456 的结果

--- 第1轮思考 ---
💭 思考:需要使用工具 calculator
🎬 行动:调用 calculator,参数:{'expression': '123.456 * 789 + 456'}
👀 观察:计算结果:97846.784

--- 第2轮思考 ---
✅ 最终答案:计算结果是 97846.784

==================================================
测试2:天气查询
==================================================
🙋 用户:北京今天天气怎么样?

--- 第1轮思考 ---
💭 思考:需要使用工具 get_weather
🎬 行动:调用 get_weather,参数:{'city': '北京'}
👀 观察:晴天,15-25℃

--- 第2轮思考 ---
✅ 最终答案:北京今天是晴天,气温在15-25℃之间。

🤖 Agent 实战:多工具规划助手(ReAct + 工具路由 + 记忆)

这一章的核心是 Agent:模型不只是“一次性回答”,而是会在多轮中 思考 → 选择工具 → 观察结果 → 更新记忆 → 再行动。 下面用一个更贴合 Agent 的案例: “帮我做一个周末出行计划:查天气、估算预算、输出最终清单”

🧭 Agent 的数据流(先看图再看代码)

数据流:User Input → Think → Act → Observe → Memory/State → Output User Input 用户问题 / 目标 (需求、约束) Think 任务分解 / 决策 下一步 Action Act 调用工具 / 读资源 参数可校验 Observe 获取结果 / 证据 异常可重试 Memory 写入状态/记忆 影响后续回合 Output 最终答案 (报告/清单) Tools / Resources get_weather(city) · calculator(expr) · export_markdown(content) · file://... · db://...

新知识点:工具路由(Tool Router)与“可检验的中间状态”

初学者做 Agent 常见误区:只写“一个大函数”,把所有逻辑塞进 prompt。 更好的方式是让 Agent 每轮都产出一个结构化动作(要不要调用工具、调用哪个、参数是什么),并把结果写入 memory

代码示例:一个最小可读的 ReAct Agent(教学版)

python
import json
import os
from typing import Any, Dict, List

import dashscope
from dashscope import Generation


dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")


def get_weather(city: str) -> str:
    # 示例:真实项目请调用天气 API
    return "晴 15-25℃"


def calculator(expression: str) -> float:
    # 注意:这里只是教学示例,不要在生产环境 eval
    return eval(expression)


def export_markdown(content: str) -> str:
    # 教学版:真实项目可以写文件、上传对象存储、生成下载链接等
    return "已导出 markdown(示例)"


class SimpleReActAgent:
    def __init__(self):
        # memory:用于保存“可检验的中间状态/工具执行轨迹”。
        # - 这不是为了让模型“凭空更聪明”,而是为了让多轮任务更稳定、可复盘。
        # - 建议结构化存储(step/tool/args/observation),方便调试与复现。
        # - 若要做“长期记忆”,应把摘要写入数据库/向量库,而不是一直塞在内存列表里。
        self.memory: List[Dict[str, Any]] = []
        self.messages: List[Dict[str, Any]] = []
        self.tools = {
            "get_weather": get_weather,
            "calculator": calculator,
            "export_markdown": export_markdown,
        }

    def get_tool_definitions(self):
        return [
            {
                "type": "function",
                "function": {
                    "name": "get_weather",
                    "description": "获取指定城市的天气信息",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "city": {"type": "string", "description": "城市名称,如:北京、上海"}
                        },
                        "required": ["city"],
                    },
                },
            },
            {
                "type": "function",
                "function": {
                    "name": "calculator",
                    "description": "执行数学计算,支持加减乘除等基本运算",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "expression": {"type": "string", "description": "要计算的数学表达式"}
                        },
                        "required": ["expression"],
                    },
                },
            },
            {
                "type": "function",
                "function": {
                    "name": "export_markdown",
                    "description": "导出 markdown 内容(教学示例)",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "content": {"type": "string", "description": "要导出的 markdown 正文"}
                        },
                        "required": ["content"],
                    },
                },
            },
        ]

    def run(self, user_goal: str, max_steps: int = 8):
        if not dashscope.api_key:
            raise RuntimeError("请先设置环境变量 DASHSCOPE_API_KEY")

        # messages:短期记忆(Conversation Memory)
        # - 本次对话的上下文在这里滚动累积,并随每轮请求发给模型。
        # - 真正工程化时会做:滑动窗口、总结摘要、敏感信息过滤、工具结果裁剪。
        self.messages = [
            {
                "role": "system",
                "content": """你是一个严谨的规划型 Agent。请按照以下规则工作:
1. 如果用户没有提供必要信息(如城市、预算等),请先询问这些信息
2. 只有获得必要信息后,才开始调用工具
3. 每轮你可以选择调用一个工具来推进任务
4. 最后导出完整的出行计划清单

必要信息:
- 出行城市(必须)
- 预算范围(可选,如果没有会按中等预算估算)
- 出行人数(可选,默认1人)""",
            },
            {"role": "user", "content": user_goal},
        ]

        for step in range(max_steps):
            resp = Generation.call(
                model="qwen-plus",
                messages=self.messages,
                tools=self.get_tool_definitions(),
                result_format="message",
            )
            assistant_message = resp.output.choices[0].message
            if not isinstance(assistant_message, dict):
                assistant_message = assistant_message.__dict__

            tool_calls = assistant_message.get("tool_calls")
            if not tool_calls:
                # 如果没有工具调用,检查是否是询问信息的回复
                content = assistant_message.get("content", "")
                self.messages.append({
                    "role": "assistant", 
                    "content": content
                })
                
                # 如果是询问信息,我们需要模拟用户提供信息来继续
                if "城市" in content or "预算" in content or "人数" in content:
                    # 模拟用户提供完整信息
                    simulated_response = "北京,预算2000元,2人"
                    self.messages.append({
                        "role": "user", 
                        "content": simulated_response
                    })
                    print(f"👤 模拟用户回复:{simulated_response}")
                    continue  # 继续下一轮
                else:
                    return content

            tool_call = tool_calls[0]
            if isinstance(tool_call, dict):
                tool_name = tool_call["function"]["name"]
                tool_args = json.loads(tool_call["function"]["arguments"])
            else:
                tool_name = tool_call.function.name
                tool_args = json.loads(tool_call.function.arguments)

            if tool_name not in self.tools:
                return f"工具不允许:{tool_name}"

            # 过滤参数,只传递函数实际需要的参数
            import inspect
            tool_func = self.tools[tool_name]
            func_params = inspect.signature(tool_func).parameters
            filtered_args = {k: v for k, v in tool_args.items() if k in func_params}
            
            observation = tool_func(**filtered_args)
            
            print(f"🔧 调用工具:{tool_name}({filtered_args}) → {observation}")

            # 把"本轮动作与结果"写入 memory(State/Scratchpad)
            # - 这里存的是工具调用轨迹,方便后续:复盘、调试、或做摘要写入长期记忆。
            # - 注意:不要把敏感原文(Token/手机号等)直接写入长期存储;必要时先脱敏。
            self.memory.append({
                "step": step + 1,
                "tool": tool_name,
                "args": filtered_args,
                "args": tool_args,
                "observation": observation,
            })

            self.messages.append({
                "role": "assistant",
                "content": "",
                "tool_calls": [tool_call],
            })
            self.messages.append({
                "role": "tool",
                "content": str(observation),
                "name": tool_name,
            })

            if tool_name == "export_markdown":
                return observation

        return "未在步数内完成(示例)"


agent = SimpleReActAgent()
result = agent.run("帮我做一个周末出行计划:查天气、估算预算、输出清单")
print(result)
print("memory=", agent.memory)

✅ 这个案例让学员学到的新知识点

ReAct 循环:思考→行动→观察→更新记忆。
Tool Router:每一轮只选择一个工具 + 明确参数(可测试、可复盘)。
Memory:把工具结果存下来,下一步决策就不需要“凭空记住”。
可扩展:后续把 decide_action 换成 LLM,就得到真正的智能 Agent。

📚 课后习题与面试题

请完成这些练习,巩固 Agent 的核心能力(ReAct / 工具路由 / 记忆 / 终止条件 / 安全)

⚠️ 提示:Agent 面试高频点一般集中在:工具安全边界、终止/防死循环、可观测性(日志)、评测与回放

🟢 基础练习题(概念 + 代码理解)

1. 什么是 ReAct?为什么它比“一次性回答”更像 Agent?

请用自己的话说明:Think/Act/Observe 各自负责什么,以及它们如何形成闭环。

2. Tool Router(工具路由)解决了什么问题?

为什么要让每轮输出结构化动作(tool + args),而不是把逻辑写死在 prompt 里?

3. Memory(记忆/状态)应该存什么?不应该存什么?

从“可复盘/可控/隐私安全”的角度回答。

4. 为什么要有终止条件(stop condition)?举 2 个例子。

比如完成任务、或无法完成时退出。你会如何设计 max_steps?

🧠 进阶练习题(更贴近真实工程)

5. 为什么示例里的 eval 有风险?如何替换成“安全计算工具”?

请写出 2 个安全策略(白名单语法/沙箱/解析器等)。

6. 工具输出为什么要“结构化”?你会用什么格式?

请举例说明:工具返回 JSON 对 Agent 调试与可观测性有什么帮助。

7. 如何防止 Agent 反复调用同一个工具(死循环)?

请给出 2 个检测/打断策略(重复模式、无进展检测、成本预算等)。

💼 常见面试题(概念 + 场景)

8.【系统设计】如何设计一个“可审计”的 Agent?

回答时请覆盖:动作日志、工具输入输出、错误与耗时、回放能力。

9.【安全题】工具调用场景下,提示注入(Prompt Injection)通常怎么发生?怎么防?

请至少给出 2 个防护点:工具白名单、参数校验、上下文隔离等。

10.【评测题】如何评测一个 Agent 是否“真的更好”?

请给出 3 个指标:成功率、成本、步数、工具错误率、用户体验等。

📝 本章小结

🎓 你学到了什么?

  • AI Agent概念:具备自主性、感知、决策和执行能力的智能系统
  • 核心模块:LLM大脑、记忆、工具、行动四大模块
  • ReAct机制:思考→行动→观察的循环,让Agent像人一样解决问题
  • 工具调用:突破LLM限制,能够搜索、计算、访问API等
  • 实际应用:使用通义千问API实现完整的Agent系统
  • 工具路由与记忆:Agent 在多轮中选择工具并保存中间状态,从而完成复杂任务

🚀 下一步学习

掌握了AI Agent的基础后,下一章我们将学习Prompt Engineering(提示词工程)的高级技巧, 让你能够更好地控制和优化Agent的行为!

💡 实践建议

  • 尝试为Agent添加更多工具(如文件操作、邮件发送等)
  • 优化ReAct循环的逻辑,提高任务完成效率
  • 设计多Agent系统,让不同Agent协作完成复杂任务
  • 研究开源Agent框架(如AutoGPT、LangChain Agent等)
← 上一章 返回上一页