Chapter 01
第一章 初识智能体
这一章解决入门阶段最重要的三个问题:智能体是什么,大语言模型为什么改变了智能体的构建方式,以及如何用一个最小代码示例体验“观察-思考-行动-反馈”的循环。
本章学习目标
1.1 什么是智能体?
智能体不是大语言模型时代才出现的概念。更早的人工智能教材会把智能体描述为:处在某个环境中,能够通过传感器感知环境,并通过行动影响环境的系统。这个定义很宽,扫地机器人、游戏角色、交易程序、自动驾驶系统都可以被看作某种智能体。
对开发者来说,可以把智能体理解成一个“有目标的软件执行者”。它不是只回答一句话,而是围绕目标持续读取状态、选择行动、观察结果,并决定是否继续。这个持续循环让它和普通函数、单轮问答、固定工作流拉开距离。
| 组成 | 解释 | 例子 |
|---|---|---|
| 目标 | 智能体要达成的结果。 | 整理一份资料摘要、完成一次网页表单填写、生成并验证测试。 |
| 环境 | 智能体能读取和影响的外部世界。 | 网页、文件系统、数据库、对话上下文、代码仓库。 |
| 观察 | 智能体从环境中获得的新信息。 | 搜索结果、命令输出、接口响应、用户反馈。 |
| 行动 | 智能体对环境采取的操作。 | 调用工具、写文件、请求 API、询问用户、结束任务。 |
1.1.2 大语言模型驱动的新范式
传统 Agent 往往依赖人工设计的状态空间、动作空间和规则。大语言模型出现后,很多原本要手写的理解和决策逻辑可以交给模型完成:它可以读懂自然语言任务,解释工具说明,生成计划草稿,选择函数调用参数,并把观察结果转化为下一步判断。
这并不意味着 LLM 本身就是完整 Agent。模型更像一个强大的决策核心,而 Agent 系统还需要工具层、状态层、权限层、执行层、日志层和人工接管层。只把一个聊天模型接到前端,不会自动变成可靠智能体。
1.1.3 智能体的类型
入门阶段可以从决策方式和自治程度给智能体分类。分类不是为了背概念,而是为了帮助你判断应该采用什么实现。
| 类型 | 特点 | 适合任务 | 实现重点 |
|---|---|---|---|
| 简单反射型 | 根据当前输入直接匹配动作。 | 规则提醒、客服分流、简单分类。 | 规则库、优先级、兜底回复。 |
| 基于状态型 | 保存历史和当前状态,再决定下一步。 | 多轮客服、学习助手、表单补全。 | 状态结构、历史摘要、恢复机制。 |
| 目标驱动型 | 围绕目标规划和执行多个步骤。 | 资料调研、代码修复、旅行规划。 | 计划拆解、工具调用、停止条件。 |
| 效用驱动型 | 在多个候选行动中选择收益更高的行动。 | 推荐、调度、资源分配、策略选择。 | 评分函数、反馈信号、风险约束。 |
| 学习型 | 根据反馈不断调整策略。 | 个性化助手、游戏智能体、自动优化系统。 | 数据闭环、评估、在线或离线学习。 |
1.2 智能体的构成与运行原理
1.2.1 任务环境定义
设计 Agent 前,先定义任务环境。经典方法会从性能指标、环境、执行器和传感器四个维度分析。换成工程语言,就是先问清楚:怎么判断成功,系统能看见什么,能做什么,做错了有什么后果。
| 维度 | 要回答的问题 | Agent 项目示例 |
|---|---|---|
| 性能指标 | 系统怎样算完成得好? | 答案准确率、引用完整性、执行耗时、人工接管率。 |
| 环境 | Agent 与哪些外部对象交互? | 浏览器、知识库、代码仓库、数据库、用户会话。 |
| 执行器 | Agent 能采取哪些动作? | 搜索、读取文件、写入草稿、提交表单、调用接口。 |
| 传感器 | Agent 如何获得反馈? | 工具返回值、测试结果、页面文本、用户确认、日志状态。 |
1.2.2 智能体的运行机制
现代 LLM Agent 的最小运行机制可以抽象为五步:输入目标、构造上下文、模型决策、执行行动、记录反馈。这个过程会反复进行,直到任务完成、预算耗尽、遇到风险或需要人工确认。
1.2.3 智能体的感知与行动
“感知”不只是摄像头或传感器。在软件 Agent 里,感知可以是读网页、读文件、读数据库、读命令行输出。行动也不只是机器人运动,它可以是调用函数、生成文件、发送请求、运行测试或把任务交回给人。
生产系统里,行动必须比感知更谨慎。读取网页通常是低风险,删除数据、发邮件、付款、封禁账号就是高风险。高风险行动应该要求人工确认,并记录完整审计信息。
1.3 动手体验:实现第一个智能体
下面的示例是一个最小 Agent。它有两个工具:计算器和笔记保存器。模型决策部分用离线脚本模拟,这样你不用配置 API Key 就能运行;实际接入 LLM 时,只需要替换 call_model 方法。
import json
import re
from dataclasses import dataclass
from typing import Callable
@dataclass
class Tool:
name: str
description: str
handler: Callable[[dict], str]
def calculator(args: dict) -> str:
expression = str(args.get("expression", ""))
if not re.fullmatch(r"[0-9+\-*/(). ]+", expression):
return "表达式包含不允许的字符"
return str(eval(expression, {"__builtins__": {}}, {}))
def write_note(args: dict) -> str:
title = str(args.get("title", "untitled"))
content = str(args.get("content", ""))
return f"已写入笔记:{title},正文 {len(content)} 字"
TOOLS = {
"calculator": Tool("calculator", "执行基础数学计算", calculator),
"write_note": Tool("write_note", "保存一条文本笔记", write_note),
}
class FirstAgent:
def __init__(self, tools: dict[str, Tool], max_steps: int = 5) -> None:
self.tools = tools
self.max_steps = max_steps
self.observations: list[str] = []
def build_prompt(self, task: str) -> str:
tool_text = "\n".join(f"{tool.name}: {tool.description}" for tool in self.tools.values())
history = "\n".join(self.observations[-3:]) or "暂无观察"
return (
f"任务:{task}\n"
f"可用工具:\n{tool_text}\n"
f"历史观察:\n{history}\n"
'请输出 JSON:{"action":"tool","tool":"工具名","args":{}} '
'或 {"action":"finish","answer":"最终答案"}'
)
def call_model(self, prompt: str) -> str:
if not any("calculator" in item for item in self.observations):
return json.dumps(
{"action": "tool", "tool": "calculator", "args": {"expression": "23 * 17 + 8"}},
ensure_ascii=False,
)
if not any("write_note" in item for item in self.observations):
return json.dumps(
{
"action": "tool",
"tool": "write_note",
"args": {"title": "第一次 Agent 运行", "content": "先计算,再保存观察结果。"},
},
ensure_ascii=False,
)
return json.dumps({"action": "finish", "answer": "计算结果为 399,笔记已保存。"}, ensure_ascii=False)
def parse_action(self, raw: str) -> dict:
action = json.loads(raw)
if action.get("action") not in {"tool", "finish"}:
raise ValueError("action 必须是 tool 或 finish")
return action
def run(self, task: str) -> str:
for step in range(1, self.max_steps + 1):
prompt = self.build_prompt(task)
action = self.parse_action(self.call_model(prompt))
if action["action"] == "finish":
return action["answer"]
tool_name = action.get("tool")
if tool_name not in self.tools:
return f"停止:未知工具 {tool_name}"
result = self.tools[tool_name].handler(action.get("args", {}))
self.observations.append(f"第 {step} 步 {tool_name} 返回:{result}")
return "停止:超过最大步数"
if __name__ == "__main__":
agent = FirstAgent(TOOLS)
print(agent.run("计算 23 * 17 + 8,并保存运行记录"))
print("\n执行轨迹:")
for observation in agent.observations:
print("-", observation)
配套文件:first_agent.py。如果你想看更短的版本,也可以打开 pocket_agent.py。
1.3.2 接入大语言模型
把离线脚本换成真实模型时,推荐保持同一个接口:输入 messages,输出严格 JSON。不要让工具层直接执行模型生成的自然语言;先解析、校验、确认工具存在,再调用工具。
def call_model_with_api(messages: list[dict]) -> str:
"""
伪代码:把这里替换成你使用的模型 SDK。
关键要求是让模型只返回 JSON,不要夹带解释。
"""
response = client.chat.completions.create(
model="your-model",
messages=messages,
temperature=0.1,
)
return response.choices[0].message.content
实际项目还要加入超时、重试、限流、日志脱敏和成本统计。这些不是“工程洁癖”,而是 Agent 能否稳定运行的基础。
1.3.4 运行案例分析
运行示例时,你会看到 Agent 先调用计算器,再保存笔记,最后返回结果。这个流程虽然简单,但已经具备智能体骨架:目标来自用户,工具来自系统,模型决定下一步,观察写回状态,循环直到完成。
| 步骤 | 发生了什么 | 为什么重要 |
|---|---|---|
| 构造 Prompt | 把目标、工具和历史观察交给模型。 | 模型只能基于它看到的信息决策。 |
| 解析 JSON | 把模型文本转成结构化行动。 | 防止自然语言直接进入工具层。 |
| 调用工具 | 执行计算或写笔记。 | Agent 通过工具影响环境。 |
| 记录观察 | 把工具返回值写入历史。 | 下一轮决策需要基于反馈调整。 |
1.4 智能体应用的协作模式
1.4.1 作为开发者工具的智能体
开发者工具类 Agent 常见于代码编辑器、测试修复、文档生成和运维排障。它们通常拥有读写文件、运行命令、检查错误的工具,但权限应该受目录、命令白名单和人工确认限制。
1.4.2 作为自主协作者的智能体
自主协作者类 Agent 更接近“你给它一个目标,它自己推进”。例如调研助手会搜索资料、筛选来源、生成报告、补引用。它的价值在于处理开放路径任务,但也更需要预算、日志和接管机制。
1.4.3 Workflow 和 Agent 的差异
| 维度 | Workflow | Agent |
|---|---|---|
| 执行路径 | 预先定义,节点顺序固定。 | 根据观察动态选择下一步。 |
| 适合任务 | 稳定流程、审批、格式转换。 | 调研、排障、复杂问答、多工具任务。 |
| 可控性 | 高,行为容易预测。 | 需要额外边界和评估。 |
| 失败处理 | 按固定分支处理。 | 需要根据失败原因重试、改计划或交给人。 |
1.5 本章小结
本章从传统 Agent 定义讲到 LLM Agent 新范式,再用代码实现了一个最小智能体。你应该已经能看出:Agent 的关键不是“会聊天”,而是能围绕目标在环境中行动,并用反馈调整下一步。
下一章会回到历史:为什么早期 AI 如此重视符号和规则,专家系统解决了什么问题,ELIZA 这类规则聊天机器人为什么重要,以及这些思想如何影响今天的大语言模型智能体。
习题
- 练习 1.1:概念判断。 分别判断“自动生成 10 个标题”“根据搜索结果写调研报告”“自动删除违规内容”更适合 Chatbot、Workflow、Copilot 还是 Agent,并说明理由。
-
练习 1.2:新增工具。
给
FirstAgent增加get_time工具,并让模型在写笔记前先读取当前时间。 -
练习 1.3:加入工具白名单。
修改
parse_action,当模型请求未授权工具时抛出错误,而不是直接返回字符串。 -
练习 1.4:接入真实模型。
把
call_model替换为你熟悉的模型 API,并保证模型只输出 JSON。 - 练习 1.5:写任务环境分析。 选一个你想做的 Agent,按性能指标、环境、执行器、传感器四项写成表格。
参考文献
- Artificial Intelligence: A Modern Approach:传统 Agent 和任务环境分析。
- ReAct:把推理和行动结合到语言模型中的代表性工作。
- Toolformer:语言模型学习使用工具的研究。