第一章 初识智能体 · 学习笔记
学习材料 :《Hello-Agents》第一章 初识智能体
配套代码 :
code/chapter1/FirstAgentTest.py(已在本机跑通)关键词:智能体四要素 · 传统智能体演进 · PEAS · Agent Loop · Thought-Action-Observation · 提示工程 · ReAct 雏形
一、知识地图
第一章 初识智能体
├── 1.1 什么是智能体
│ ├── 1.1.1 传统视角:反射 → 基于模型 → 基于目标 → 基于效用 → 学习型
│ ├── 1.1.2 LLM 驱动的新范式
│ └── 1.1.3 三种分类维度(决策架构 / 时间反应性 / 知识表示)
├── 1.2 构成与运行原理
│ ├── 1.2.1 任务环境定义(PEAS + 环境五特性)
│ ├── 1.2.2 运行机制(Agent Loop)
│ └── 1.2.3 感知与行动(Thought / Action / Observation 交互协议)
├── 1.3 动手:5 分钟实现第一个智能体
│ ├── 1.3.1 准备工作(指令模板 + 两个工具)
│ ├── 1.3.2 接入大语言模型(OpenAI 兼容客户端)
│ ├── 1.3.3 执行行动循环(主循环 + 正则解析)
│ └── 1.3.4 运行案例分析
└── 1.4 协作模式:开发者工具 / 自主协作者 / Workflow vs Agent
一句话主线 :本章先把"智能体是什么"讲清楚,再告诉你它"怎么跑",最后让你亲手写一个不依赖任何框架的裸智能体 ------用 130 行代码把 Thought-Action-Observation 循环跑通。理解了这 130 行,后面所有框架(LangChain / AutoGen / LangGraph)都只是它的工程化封装。
二、核心概念
2.1 智能体的定义与四要素
智能体 = 任何能够通过传感器感知环境,并自主地通过执行器采取行动以达成特定目标的实体。
拆解出四个基本要素:
| 要素 | 含义 | 旅行助手中的对应 |
|---|---|---|
| 环境 Environment | 智能体所处的外部世界 | 天气 API、搜索引擎、用户、其他预订者 |
| 传感器 Sensors | 感知环境状态的手段 | HTTP API 返回的 JSON、用户输入 |
| 执行器 Actuators | 对环境施加影响的手段 | 调用 get_weather / get_attraction 函数 |
| 自主性 Autonomy | 基于感知与内部状态独立决策 | LLM 自行决定"先查天气再荐景点" |
⚠️ 我理解的易错点 :定义的重心不在"能感知/能行动"(恒温器也能),而在于 自主性 ------它必须能独立决策,而不是被动响应刺激或严格执行预设指令。这也是后面 Agent 与 Workflow 的本质分界线。
2.2 传统智能体的演进路线
这条演进线是本章最值得背下来的骨架,它本质上是在回答"当前感知不够用的时候,怎么办?":
| 类型 | 核心问题 | 决策依据 | 典型案例 | 局限 |
|---|---|---|---|---|
| 简单反射型 | ------ | 条件-动作规则 | 恒温器 | 无记忆、无预测,无法理解上下文 |
| 基于模型型 | 世界现在是什么样? | 条件-动作 + 内部世界模型 | 隧道中行驶的自动驾驶 | 有记忆但无目标 |
| 基于目标型 | 我该做什么才能达成目标? | 世界模型 + 目标 + 搜索/规划(A*) | GPS 导航 | 目标单一,不会权衡 |
| 基于效用型 | 哪种行为结果最让我满意? | 世界模型 + 效用函数 | 多目标路线规划(最快/最省油/避堵) | 效用函数仍需人类设计 |
| 学习型 | ------ | 性能元件 + 学习元件(RL) | AlphaGo Zero | 训练成本高 |
记忆口诀 :规则 → 记忆 → 目标 → 权衡 → 自学习。前四类靠人类先验知识,第五类才首次引入"从经验中自我修正"。
关键洞察:学习能力是一种"元能力",可以叠加到前面任何一种架构上。
2.3 LLM 驱动的新范式
核心差异:传统智能体的能力来自工程师的显式编程;LLM 智能体的能力来自海量数据预训练得到的隐式世界模型与涌现能力。
| 维度 | 传统智能体 | LLM 智能体 |
|---|---|---|
| 核心引擎 | 规则 / 模型 / 效用函数 | 大语言模型 |
| 知识来源 | 人工构建 | 预训练语料(隐式) |
| 交互方式 | 结构化信号 | 自然语言 |
| 行为边界 | 确定、有边界 | 灵活、通用 |
以"规划厦门之旅"为例,LLM 智能体展现出三种能力:
- 规划与推理 :把高层模糊目标分解为子任务链
[确认偏好] → [查目的地] → [订行程] → [订票务] - 工具使用:识别信息缺口并主动调用外部工具(查天气 → 发现下雨 → 倾向室内活动)
- 动态修正 :把用户反馈("酒店超预算")当作新约束,重新搜索
💡 思维转变 :从"开发专用自动化工具"变成"引导一个通用大脑去规划、行动和学习"。核心工作从写代码 变成了写提示 + 设计工具。
2.4 智能体的三种分类维度
维度一:按内部决策架构 (源自 Russell & Norvig《AIMA》)
即 2.2 节的演进阶梯;学习能力是叠加在上述所有类型上的元能力。
维度二:按时间与反应性(本章最有工程指导意义的分类)
| 类型 | 特点 | 优势 | 代价 | 例子 |
|---|---|---|---|---|
| 反应式 | 感知→行动直接映射 | 快、开销低 | 短视,易陷局部最优 | 安全气囊、高频交易 |
| 规划式 | 先探索未来可能性再行动 | 战略性、有远见 | 慢、算力贵 | 下棋 AI、商业计划 |
| 混合式 | 分层:底层快速反应 + 高层审慎规划 | 兼得二者 | 架构复杂 | 现代 LLM 智能体 |
⭐ 重点 :现代 LLM 智能体就是一种灵活的混合式架构------它把宏大的长期任务拆成一连串"规划-反应"微循环:
- 在 Thought(思考) 阶段做审议/规划(慢思考)
- 在 Action / Observation 阶段与环境交互并即时拿到反馈(快反应)
维度三:按知识表示(一场持续半个多世纪的辩论)
| 范式 | 知识形态 | 类比 | 优势 | 缺陷 |
|---|---|---|---|---|
| 亚符号主义(连接主义) | 分布式统计模式 | 凭直觉认猫的孩童 | 模式识别强、抗噪、擅非结构化数据 | 黑箱、不擅逻辑推理、会幻觉 |
| 符号主义 | 显式规则与知识图谱 | 一丝不苟的图书管理员 | 透明、可解释、可追溯 | 脆弱、知识获取瓶颈 |
| 神经符号主义 | 二者融合 | 卡尼曼的系统 1 + 系统 2 | 既能学又能推理 | 工程实现难 |
🔑 本章点睛之笔 :LLM 智能体本身就是神经符号主义的一个绝佳实践范例。
- 内核是巨大的神经网络 → 提供模式识别与语言生成(系统 1 / 亚符号)
- 运行中生成结构化的中间步骤(Thought、计划、API 调用)→ 这些是明确可操作的符号 (系统 2 / 符号)
换句话说,"Thought-Action-Observation" 这套文本协议,就是给神经网络装上的符号接口。
三、智能体如何运行
3.1 PEAS 模型
用四个字段精确描述任务环境:P erformance(性能度量)、E nvironment(环境)、A ctuators(执行器)、Sensors(传感器)。
以智能旅行助手为例:
| 维度 | 内容 |
|---|---|
| Performance | 推荐结果的准确性/相关性、用户满意度、响应速度 |
| Environment | 天气 API、搜索结果、用户对话、其他预订者 |
| Actuators | 调用 get_weather、调用 get_attraction、输出最终答案 |
| Sensors | 用户文本输入、API 返回的 JSON |
3.2 数字环境的五个特性(直接影响设计)
| 特性 | 含义 | 对设计的要求 |
|---|---|---|
| 部分可观察 | 一次 API 调用拿不到全局信息 | 需要记忆 (记住查过的)+ 探索(换查询条件) |
| 随机性 | 两次同样调用结果可能不同 | 需要处理不确定性、监控变化 |
| 多智能体 | 环境中有其他行动者(其他用户、调价系统) | 需要快速响应与策略选择 |
| 序贯 | 当前动作影响未来 | 要维护上下文历史(案例里的 prompt_history) |
| 动态 | 决策时环境自身还在变 | 循环必须快而灵活 |
3.3 Agent Loop(智能体循环)
┌──────────────────────────────────────┐
│ │
[环境 Environment] ──观察──▶ [感知 Perception]
▲ │
│ ▼
状态变化 State Change [思考 Thought]
│ ├── 规划 Planning
│ └── 工具选择 Tool Selection
│ │
└────行动 Action ◀───────────┘
(调用执行器/工具)
三个阶段:
- 感知 Perception:接收 Observation(可能是用户初始指令,也可能是上一步行动的反馈)
- 思考 Thought :核心决策阶段,细分为 规划 (分解目标、更新理解)与 工具选择(选工具 + 定参数)
- 行动 Action:通过执行器施加影响(调 API、跑代码)
行动不是终点:行动 → 环境状态变化 → 新观察 → 下一轮循环。循环往复,从初始状态逼近目标状态。
3.4 交互协议:Thought-Action-Observation
为了让 LLM 能驱动这个循环,需要一套明确的交互协议来规范输出格式。
Thought: 用户想知道北京的天气。我需要调用天气查询工具。
Action: get_weather(city="北京")
↓ (外部解析器 Parser 捕获并执行)
Observation: 北京当前天气为晴,气温25摄氏度,微风。
↓ (作为下一轮循环的输入)
两个关键工程点:
- Thought 是给"人看"的:自然语言的内部推理快照,帮助提升推理质量与可调试性。
- Observation 需要"翻译" :环境返回的原始 JSON 含有 LLM 不需要的冗余信息,感知系统要负责把它封装成简洁的自然语言再喂回去。
这套模式就是后来 ReAct(Reasoning + Acting) 范式的思想内核:用自然语言把推理和行动交织在一起。
四、代码精读(FirstAgentTest.py)
4.1 整体架构
┌─────────────────────────────────────────────────┐
│ AGENT_SYSTEM_PROMPT ← 提示工程:角色/工具/格式 │
├─────────────────────────────────────────────────┤
│ get_weather() ← 工具 1(wttr.in) │
│ get_attraction() ← 工具 2(Tavily Search) │
│ available_tools{} ← 工具注册表(名字→函数) │
├─────────────────────────────────────────────────┤
│ OpenAICompatibleClient ← 大脑(任何 OpenAI 兼容)│
├─────────────────────────────────────────────────┤
│ 主循环 for i in range(5) │
│ 构建 Prompt → 调 LLM → 截断 → 解析 → 执行 → 记录│
└─────────────────────────────────────────────────┘
设计精髓 :不在代码里写 if 天气==晴天 then 推荐颐和园,而是把决策权完全交给 LLM,代码只负责"解析意图 + 执行工具 + 回填观察"。这就是 Agent 与 Workflow 的分野。
4.2 系统提示词:智能体的"说明书"
python
AGENT_SYSTEM_PROMPT = """
你是一个智能旅行助手。你的任务是分析用户的请求,并使用可用工具一步步地解决问题。
# 可用工具:
- `get_weather(city: str)`: 查询指定城市的实时天气。
- `get_attraction(city: str, weather: str)`: 根据城市和天气搜索推荐的旅游景点。
# 输出格式要求:
你的每次回复必须严格遵循以下格式,包含一对Thought和Action:
Thought: [你的思考过程和下一步计划]
Action: [你要执行的具体行动]
Action的格式必须是以下之一:
1. 调用工具:function_name(arg_name="arg_value")
2. 结束任务:Finish[最终答案]
# 重要提示:
- 每次只输出一对Thought-Action
- Action必须在同一行,不要换行
- 当收集到足够信息可以回答用户问题时,必须使用 Action: Finish[最终答案] 格式结束
请开始吧!
"""
提示词的四段式结构拆解:
| 段落 | 作用 | 对应理论 |
|---|---|---|
| 角色定义 | 告诉模型"你是谁、要干什么" | 设定目标 |
| 可用工具 | 声明 Action 空间与参数签名 | Actuators |
| 输出格式 | 约定协议的语法 | 交互协议 |
| 重要提示 | 约束行为,防止跑偏 | 输出稳定性 |
⚠️ "每次只输出一对 Thought-Action" 是最关键的一条约束 。不加这条,模型倾向于一口气把多轮推理全写出来,导致正则解析拿到错误的 Action。即便如此,代码里仍然保留了截断逻辑作为兜底(见 4.5)。
4.3 工具层
工具 1:真实天气查询(免费服务 wttr.in)
python
def get_weather(city: str) -> str:
url = f"https://wttr.in/{city}?format=j1"
try:
response = requests.get(url)
response.raise_for_status() # 状态码非 200 时抛异常
data = response.json()
current_condition = data['current_condition'][0]
weather_desc = current_condition['weatherDesc'][0]['value']
temp_c = current_condition['temp_C']
return f"{city}当前天气:{weather_desc},气温{temp_c}摄氏度"
except requests.exceptions.RequestException as e:
return f"错误:查询天气时遇到网络问题 - {e}"
except (KeyError, IndexError) as e:
return f"错误:解析天气数据失败,可能是城市名称无效 - {e}"
要点:
format=j1让 wttr.in 返回 JSON 而非网页- 两层异常捕获:网络层(
RequestException)+ 数据解析层(KeyError/IndexError) - 错误也是字符串返回值 ,而不是抛出------这样错误会变成一条 Observation 回喂给 LLM,模型有机会自我修正(比如换个城市名重试)。这是智能体工具设计的通用原则。
工具 2:景点搜索(Tavily AI 搜索)
python
def get_attraction(city: str, weather: str) -> str:
api_key = os.environ.get("TAVILY_API_KEY")
if not api_key:
return "错误:未配置TAVILY_API_KEY。"
tavily = TavilyClient(api_key=api_key)
query = f"'{city}' 在'{weather}'天气下最值得去的旅游景点推荐及理由"
try:
response = tavily.search(query=query, search_depth="basic", include_answer=True)
if response.get("answer"):
return response["answer"]
# 兜底:手动拼接原始结果
formatted_results = [f"- {r['title']}: {r['content']}" for r in response.get("results", [])]
if not formatted_results:
return "抱歉,没有找到相关的旅游景点推荐。"
return "根据搜索,为您找到以下信息:\n" + "\n".join(formatted_results)
except Exception as e:
return f"错误:执行Tavily搜索时出现问题 - {e}"
要点:
include_answer=True让 Tavily 直接返回一段综合性总结,省去自己拼接结果 ------ 这正是 3.4 节说的"把原始数据翻译成自然语言"的现成实现- 查询串构造融入了
weather参数,体现上一步的 Observation 作为下一步的输入
工具注册表:
python
available_tools = {
"get_weather": get_weather,
"get_attraction": get_attraction,
}
一个字典就把"字符串工具名"映射到"可调用函数",主循环里 available_tools[tool_name](**kwargs) 一行完成分发。这是所有智能体框架 Tool/Function Calling 机制的极简原型。
4.4 大脑:OpenAI 兼容客户端
python
class OpenAICompatibleClient:
def __init__(self, model: str, api_key: str, base_url: str):
self.model = model
self.client = OpenAI(api_key=api_key, base_url=base_url)
def generate(self, prompt: str, system_prompt: str) -> str:
messages = [
{'role': 'system', 'content': system_prompt},
{'role': 'user', 'content': prompt}
]
response = self.client.chat.completions.create(
model=self.model, messages=messages, stream=False
)
return response.choices[0].message.content
要点:
- 只要服务商兼容 OpenAI 接口规范(OpenAI 官方 / Azure / Ollama / vLLM / DeepSeek...),这一份代码通吃 → 换服务只需改
base_url+model - 只用了 system + user 两种角色 :整段对话历史被压成一个 user 字符串 (见 4.5 的
full_prompt)。这是最朴素的"记忆"实现------用上下文窗口当记忆。
💭 我的思考 :这种"历史拼成一个字符串"的做法,在生产环境会有问题(token 膨胀、角色混淆)。更规范的做法是用真正的
messages数组,system / user / assistant 分明。这也是后续章节会升级的点。
4.5 主循环:逐行拆解
① 初始化
python
user_prompt = "你好,请帮我查询一下今天北京的天气,然后根据天气推荐一个合适的旅游景点。"
prompt_history = [f"用户请求: {user_prompt}"]
prompt_history 是整个智能体的短期记忆 ,后续每轮都会追加 模型输出 和 Observation。
② 构建 Prompt 并调用 LLM
python
full_prompt = "\n".join(prompt_history)
llm_output = llm.generate(full_prompt, system_prompt=AGENT_SYSTEM_PROMPT)
每轮都把完整历史重新喂给 LLM。LLM 本身是无状态的,"记忆"完全靠每次重放上下文。
③ 输出截断(防御性编程)
python
match = re.search(r'(Thought:.*?Action:.*?)(?=\n\s*(?:Thought:|Action:|Observation:)|\Z)',
llm_output, re.DOTALL)
if match:
truncated = match.group(1).strip()
if truncated != llm_output.strip():
llm_output = truncated
print("已截断多余的 Thought-Action 对")
这个正则值得逐块读:
| 片段 | 含义 |
|---|---|
(Thought:.*?Action:.*?) |
捕获组:从 Thought: 到 Action: 的非贪婪内容 |
| `(?=\n\s*(?:Thought: | Action: |
re.DOTALL |
让 . 也能匹配换行符 |
目的 :即使模型不听话吐出了 3 对 Thought-Action,也只保留第一对 ,保证后面 Action: (.*) 解析到的是本轮该执行的那一个。
⭐ 这是本章最"工程"的一个细节。凡是靠 Prompt 约束 LLM 输出格式的地方,都必须有代码兜底。
④ 解析 Action
python
action_match = re.search(r"Action: (.*)", llm_output, re.DOTALL)
if not action_match:
observation = "错误: 未能解析到 Action 字段。请确保你的回复严格遵循 'Thought: ... Action: ...' 的格式。"
prompt_history.append(f"Observation: {observation}")
continue # ← 注意:不是 break,而是把错误回喂后继续下一轮
解析失败不终止,而是把错误信息当作 Observation 回喂 ,让模型自己纠正格式。这是一个非常优雅的容错设计。
⑤ 分支:结束 or 调工具
python
action_str = action_match.group(1).strip()
# 分支 A:任务完成
if action_str.startswith("Finish"):
final_answer = re.match(r"Finish\[(.*)\]", action_str).group(1)
print(f"任务完成,最终答案: {final_answer}")
break
# 分支 B:调用工具
tool_name = re.search(r"(\w+)\(", action_str).group(1) # → get_weather
args_str = re.search(r"\((.*)\)", action_str).group(1) # → city="北京"
kwargs = dict(re.findall(r'(\w+)="([^"]*)"', args_str)) # → {'city': '北京'}
if tool_name in available_tools:
observation = available_tools[tool_name](**kwargs) # 动态分发
else:
observation = f"错误:未定义的工具 '{tool_name}'"
四个正则就是一套迷你"函数调用解析器":
| 正则 | 输入示例 | 输出 |
|---|---|---|
(\w+)\( |
get_weather(city="北京") |
get_weather |
\((.*)\) |
同上 | city="北京" |
(\w+)="([^"]*)" |
同上 | {'city': '北京'} |
设计亮点 :未知工具不抛异常,而是返回 错误:未定义的工具 'xxx' 作为 Observation。让 LLM 知道"这条路走不通",从而换个工具------这是智能体自主性的体现。
⑥ 记录观察、进入下一轮
python
observation_str = f"Observation: {observation}"
prompt_history.append(observation_str)
至此完成一轮闭环,prompt_history 增长一条,for i in range(5) 进入下一轮。
最大循环次数 5 的意义 :防止 LLM 陷入"思考-调工具-再思考"的死循环,是一种安全阀。
五、我的调试记录(环境配置)
章节原文用占位符写死了密钥,但是对后续使用API-key都得重新声明,且在代码内不安全。我做了如下改造,把配置抽到项目根目录的 .env:
python
from pathlib import Path
import requests
from dotenv import load_dotenv
env_path = Path(__file__).parent.parent.parent / ".env"
load_dotenv(env_path, override=True)
python
API_KEY = os.getenv("API_KEY")
BASE_URL = os.getenv("BASE_URL")
MODEL_ID = os.getenv("MODEL_NAME")
要点记录:
| 点 | 说明 |
|---|---|
| 路径计算 | __file__ → chapter1/ → code/ → 项目根 ,.env 放在仓库根目录 |
override=True |
强制用 .env 覆盖系统已有的同名环境变量,避免被旧值污染 |
| 变量名不一致 | 代码里变量叫 MODEL_ID,但 .env 里的键是 MODEL_NAME ⚠️ |
| 依赖安装 | pip install requests tavily-python openai python-dotenv |
| 密钥获取 | Tavily 需在 tavily.com 注册获取 |
🔐 安全提醒 :
.env已在.gitignore中,切勿把真实密钥提交到仓库。
六、运行结果分析
成功的执行流程是一个标准的三轮循环:
| 轮次 | Thought(推理) | Action(行动) | Observation(反馈) |
|---|---|---|---|
| 1 | 需要先拿到北京天气 | get_weather(city="北京") |
北京当前天气:Sunny,气温 26℃ |
| 2 | 已知晴天、温度适中,可以推荐景点了 | get_attraction(city="北京", weather="Sunny") |
颐和园(湖景+古建)、长城(壮观+历史) |
| 3 | 信息够了,可以给用户答复 | Finish[今天北京天气晴朗...推荐颐和园或长城...] |
------ 任务结束 |
四项核心能力的体现(对应 1.3.4):
- 任务分解 ------ 轮 1 就把"查天气+荐景点"拆成了两步
- 工具调用 ------ 自主构造参数
weather="Sunny"(把轮 1 的观察值准确传递给了工具 2) - 上下文理解 ------ 轮 2 的推理明确引用了"北京今天的天气是晴朗且温度适中"
- 结果合成 ------ 轮 3 把天气与景点融合成一段人性化回答
⭐ 特别注意第 2 点:模型不是机械地把
weather参数填成 "晴天",而是填了 API 返回的原始值Sunny。这说明它真正读懂了 Observation,而不是在猜。
七、踩坑与改进思考
7.1 现有实现的潜在问题
| # | 问题 | 说明 | 改进方向 |
|---|---|---|---|
| 1 | 历史被拼成单一 user 串 | 角色信息丢失,长对话 token 膨胀 | 改用 messages 数组,按 role 组织 |
| 2 | 正则解析脆弱 | 参数值含引号/换行/中文括号就可能解析失败 | 改用 JSON 格式输出 + json.loads;或用原生 Function Calling |
| 3 | 无重试机制 | 网络抖动时工具直接返回错误字符串 | 加指数退避重试 |
| 4 | 最大轮次写死为 5 | 简单任务够用,复杂任务不够 | 结合 token 预算与任务复杂度动态控制 |
八、习题思路速记
习题 1 · 判断是否是智能体
| Case | 是否智能体 | 分析要点 |
|---|---|---|
| A. 冯·诺依曼超算 | 否 | 只有算力,无感知-行动闭环,无自主性。它是工具而非智能体 |
| B. 特斯拉自动驾驶(刹车/变道) | 是 | 混合式:毫秒级反应 → 反应式;路径规划 → 规划式。环境:部分可观察、动态、多智能体 |
| C. AlphaGo | 是 | 典型规划式 + 学习型(基于效用:胜率即效用函数) |
| D. ChatGPT 智能客服 | 是 | LLM 智能体,混合式;任务分解 + 工具调用(查订单库)+ 多目标(解决问题 & 安抚情绪 → 效用权衡) |
习题 2 · 智能健身教练 PEAS
- P:训练目标达成度(减脂/增肌/耐力提升)、动作标准度、用户留存/满意度、安全性(避免受伤)
- E:用户身体、可穿戴设备、健身房/居家场景、饮食环境
- A:语音播报指导、调整训练计划、推送饮食建议、震动/提醒
- S:心率/配速/加速度传感器、用户手动输入(目标、反馈)、训练历史
- 环境特性 :部分可观察(看不到饮食作息)· 随机性(生理反应个体差异)· 动态(心率实时变化)· 序贯(今日训练影响明日状态)· 含人(用户本身也是环境中的行动者)
习题 3 · Workflow vs Agent(退款审批)
| 方案 A:Workflow | 方案 B:Agent | |
|---|---|---|
| 优点 | 确定、可审计、快、成本低、合规友好 | 灵活、能处理规则外情形、用户体验好 |
| 缺点 | 脆弱,遇规则外情形就卡住 | 不确定、难审计、成本高、可能误判 |
- Workflow 更合适:规则明确、金额敏感、需强合规审计(金融/医疗)、量大且标准化
- Agent 更有优势:情形复杂模糊、需综合多源信息、长尾案例多、追求用户满意度
- 方案 C(推荐) :Workflow 主流程 + Agent 兜底 ------ 明确的规则走确定性流程;规则未覆盖或置信度低的 case 转交 Agent 决策,并对 Agent 的决策保留人工复核与审计日志。既守住合规底线,又覆盖长尾。
习题 4 · 给旅行助手加功能
- 记忆用户偏好
- 简单版:把偏好写进
prompt_history开头,或单独维护一个user_profile字典 - 进阶版:引入外部长期记忆(向量库),按相关性检索后注入 System Prompt
- 简单版:把偏好写进
- 门票售罄自动备选
- 把
get_attraction的返回约定为结构化列表(含余票字段) - 在 Thought 中提示模型:"若首选不可用,须给出至少 2 个备选"
- 把
- 连续被拒 3 次后反思
- 在
prompt_history中维护一个计数器 - 达到阈值时,主动注入一条 Observation,如
"用户已连续拒绝3次推荐,请反思并调整策略(如更换风格/提高预算弹性/主动询问偏好)"------ 这其实就是**Reflection(反思)**模式的雏形
- 在
习题 5 · 系统 1 / 系统 2 协同(以医疗诊断助手为例)
- 系统 1(快、直觉):医学影像初筛、生命体征异常检测、急诊分诊、常见病快速初判
- 系统 2(慢、审慎):鉴别诊断推理、药物相互作用核查、结合病史的个体化方案、罕见病排查
- 协同 :系统 1 做高召回 的快速初筛并给出候选 → 系统 2 对候选做高精度 的深度推理与验证 → 两者结论不一致时必须升级到系统 2(或人类医生) 。这与 LLM 智能体的结构完美对应:神经网络直觉 + 符号化推理链。
习题 6 · 局限与评估
- 幻觉来源:预训练知识有噪声/过时;缺乏对事实的实时校验;目标是"生成流畅文本"而非"保证真实";上下文不足以约束 → 缓解:接工具查证、要求引用来源、输出后自检
- 不设最大轮次会怎样 :陷入无限循环 ------反复调用同一工具、在两个方案间来回摇摆、误判"信息不足"而不断探索,最终烧光 token 与 API 费用。所以必须有预算/步数/timeout 三重护栏
- 如何评估智能体 :仅看准确率远远不够 。至少还需关注:
- 过程指标:步数效率、工具调用正确率、是否走弯路、是否陷入循环
- 鲁棒性:面对异常输入/工具失败时的恢复能力
- 成本:token 消耗、延迟、API 花费
- 安全与合规:有无越权操作、有无幻觉输出
- 可解释性:Thought 链条是否合理可追溯
九、本章小结
本章回顾
- 是什么 :智能体 = 感知 + 思考 + 行动 的自主闭环,核心是自主性
- 怎么跑 :
Observation → Thought(规划+工具选择) → Action → Observation → ...,LLM 提供大脑,工具提供手脚,上下文提供记忆 - 怎么写 :好的 System Prompt(定义人设/工具/协议) + 健壮的输出解析(正则 + 截断 + 容错) + 安全的循环控制(最大轮次)
核心公式
LLM 智能体 = 大语言模型(大脑)
+ 工具集(手脚)
+ 提示词模板(说明书)
+ 解析器 & 循环控制(神经反射)
Agent vs Workflow 一句话
Workflow 是让 AI 按部就班地执行指令;Agent 是赋予 AI 自由度去自主达成目标。
判据:是否存在写死的
if 条件 then 动作规则 。旅行助手里没有写if 晴天 then 推荐颐和园------所以它是 Agent。
自测清单
- 能说出智能体四要素,并解释为什么"自主性"最关键
- 能画出传统智能体的五级演进阶梯及各阶段的核心局限
- 能用 PEAS 描述任意一个智能体的任务环境
- 能画出 Agent Loop 图,并说明 Thought 阶段的两个子环节
- 能默写 Thought-Action-Observation 协议,并解释 Observation 为什么要"翻译成自然语言"
- 能从零复现
FirstAgentTest.py,说清每个正则的作用 - 能解释"输出截断"和"解析失败 continue 而非 break"这两个设计的用意
- 能举出 2 个神经符号主义在 LLM 智能体中的对应关系
- 能说清 Agent 与 Workflow 的本质区别,并各举一个适用场景
下一步
下一章将追溯智能体的发展历史。带着本章留下的一个问题继续前进:
今天的 LLM 智能体,在架构上究竟是全新范式,还是传统智能体演进的延续?从本章看,答案偏向后者------LLM 智能体本质上是"学习型 + 效用型 + 混合式"架构在神经符号主义下的一次大融合。