小白入门:用 Python 和 OpenAI API 构建简单 Agent
小白入门:用 Python 和 OpenAI API 构建简单 Agent
一、引言 (Introduction)
1.1 钩子 (The Hook)
你是否曾在科幻电影中看到过那些能够理解人类指令、自主思考、甚至能够主动解决问题的智能助手?比如钢铁侠的贾维斯(Jarvis),它不仅仅是一个简单的语音助手,更是一个能够自主决策、执行复杂任务的智能体(Agent)。
又或者,你是否在日常工作中感到被大量重复性任务压得喘不过气?比如整理邮件、提取文档信息、数据分析、甚至是预约日程。如果有一个智能助手能够帮你自动完成这些任务,那该有多好?
好消息是,随着大语言模型(LLM)技术的快速发展,构建这样的智能体已经不再是科幻电影的专利,也不再是只有大公司才能涉足的领域。今天,作为一个 Python 初学者,你也可以利用 OpenAI 的 API,在短短几小时内构建出一个属于自己的简单但功能强大的 Agent!
1.2 定义问题/阐述背景 (The “Why”)
在深入探讨之前,让我们先明确几个核心概念:
什么是 Agent? 在人工智能领域,Agent(智能体)是指一个能够感知环境、做出决策并采取行动的实体。它可以是一个软件程序,也可以是一个机器人。在本文的语境下,我们讨论的是由大语言模型驱动的软件 Agent。
为什么 Agent 重要? 传统的软件程序需要明确的指令才能工作,而 Agent 则可以在给定目标后,自主规划步骤、调用工具、处理信息并完成任务。这代表了软件交互范式的重大转变——从"命令式"向"目标导向式"转变。
OpenAI API 在这里扮演什么角色? OpenAI 的 GPT 系列模型提供了强大的理解和生成能力,但仅凭模型本身,它无法直接与外部世界交互(比如查询数据库、访问网络、执行代码等)。通过 API,我们可以将模型的"大脑"与各种"工具"连接起来,赋予 Agent 真正解决实际问题的能力。
1.3 亮明观点/文章目标 (The “What” & “How”)
读完这篇文章,你将能够:
- 理解 Agent 的核心概念和工作原理:不再对"智能体"这个概念感到神秘。
- 掌握 OpenAI API 的基础使用方法:包括如何调用模型、处理响应。
- 从零构建一个简单而实用的 Agent:这个 Agent 将能够执行任务规划、信息检索和简单的工具调用。
- 了解 Agent 开发的最佳实践和进阶方向:为你未来的探索打下基础。
我们将通过以下步骤实现这一目标:首先,我们会铺垫必要的基础知识;其次,我们会设置开发环境;然后,我们会通过渐进式的代码示例,从最简单的对话机器人开始,逐步增加功能,最终构建一个完整的 Agent;最后,我们会探讨一些进阶话题。
准备好了吗?让我们开始这段激动人心的旅程!
二、基础知识/背景铺垫 (Foundational Concepts)
在我们开始写代码之前,让我们先花一些时间来理解一些关键概念。这将帮助你不仅知其然,更知其所以然。
2.1 核心概念定义
2.1.1 大语言模型 (LLM - Large Language Model)
核心概念: 大语言模型是一种基于深度学习的人工智能模型,它通过在海量文本数据上进行训练,学习语言的模式和结构。它能够预测下一个最可能出现的词(或称为 Token),从而生成连贯、有逻辑的文本。
问题背景: 传统的自然语言处理(NLP)系统通常是针对特定任务设计的,比如翻译、情感分析等,且需要大量的标注数据。LLM 的出现改变了这一格局,它是一种"通用"模型,可以通过"提示词(Prompt)"来适应各种不同的任务。
核心原理: LLM 的核心是 Transformer 架构,特别是其中的"自注意力机制(Self-Attention)",这使得模型在处理文本时能够"关注"到上下文的不同部分。
简单来说,当你向 GPT 输入一段文字时,模型会计算每个词与其他词之间的关联程度,然后基于这种理解来生成回复。
从数学角度看,自注意力可以通过以下公式表示:
A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K T d k ) V Attention(Q, K, V) = softmax(\frac{QK^T}{\sqrt{d_k}})V Attention(Q,K,V)=softmax(dkQKT)V
其中:
- Q Q Q (Query):查询向量,代表当前词。
- K K K (Key):键向量,代表上下文词。
- V V V (Value):值向量,代表上下文词的实际内容。
- d k d_k dk:是向量的维度,除以它是为了缩放点积,防止梯度消失。
OpenAI API 的角色: OpenAI 通过 API 将这个强大的模型"封装"起来,我们不需要去训练这个模型(这需要数十亿美金和超级计算机),只需要通过简单的 HTTP 请求,就可以使用它的能力。
2.1.2 智能体 (Agent)
核心概念: 在本文中,Agent 是一个以 LLM 为"大脑"的系统,它能够感知用户意图、制定计划、调用工具、执行动作并最终完成目标。
概念结构与核心要素组成: 一个典型的 Agent 通常包含以下几个核心部分:
- 规划 (Planning): 将大目标拆解为小的、可管理的步骤。
- 记忆 (Memory): 短期记忆(对话历史)和长期记忆(从过去的经验中学习)。
- 工具使用 (Tool Use): 调用外部 API、数据库、代码执行器等能力。
让我们用一个 Mermaid ER 图来展示 Agent 的核心组件及其关系:
2.1.3 提示工程 (Prompt Engineering)
核心概念: 提示工程是指如何设计和优化输入给 LLM 的文本(Prompt),以获得最佳、最可靠的输出结果。这是与 LLM 交互的关键技能。
问题背景: LLM 就像一个才华横溢但有时会"走神"的员工。你给它的指令越清晰、越结构化,它的工作成果就越好。
常见技巧:
- 角色设定 (Role Prompting): “你是一个资深的 Python 老师…”
- 思维链 (Chain-of-Thought, CoT): “让我们一步步思考…”
- 少样本学习 (Few-Shot Learning): 给模型几个示例。
- 结构化输出: 要求模型以 JSON、XML 等特定格式输出。
2.2 相关工具/技术概览
在开始我们的项目之前,让我们来认识一下我们的"工具箱"。
2.2.1 OpenAI API 介绍
OpenAI 提供了一系列的 API 服务,最核心的是 Chat Completions API(聊天补全),也就是我们常说的"对话接口"。
主要模型(截至 2024 年):
gpt-4o:最新模型,性价比高,视觉+文本。gpt-4-turbo:强大的推理能力。gpt-3.5-turbo:性价比之选,适合大多数简单任务。
对于我们的入门项目,gpt-3.5-turbo 或 gpt-4o-mini 会是非常好的起点。
2.2.2 LangChain vs. 原生开发
你可能听说过 LangChain,这是一个非常流行的用于构建 LLM 应用的框架。它提供了很多现成的组件(Chain、Agent、Memory 等)。
但是,对于小白入门,我强烈建议从原生 Python 代码开始,原因如下:
- 理解本质:LangChain 封装了太多细节,容易让人"知其然不知其所以然"。
- 灵活性:自己写代码,你拥有完全的控制权。
- 减少依赖:少一个外部依赖,少一个出问题的点。
等你掌握了底层原理后,再去使用 LangChain 这样的框架,你会发现你能更好地驾驭它。
2.2.3 Python 环境管理:Conda 与 Pip
为了保持我们的开发环境干净整洁,我们将使用虚拟环境。如果你是 Python 新手,Miniconda 是一个非常好的选择。
三、核心内容/实战演练 (The Core - “How-To”)
好了,概念讲得差不多了,让我们把手弄脏,开始写代码!这一部分是本文的重中之重。
3.1 项目准备与环境搭建
3.1.1 项目介绍
我们将构建一个名为 “TaskWhiz” 的简单 Agent。它的核心功能是:
- 理解用户的自然语言请求。
- 如果需要,向用户追问更多信息。
- 能够查询天气(模拟一个外部工具)。
- 能够进行简单的数学计算。
- 保存对话记忆。
这听起来可能很简单,但麻雀虽小,五脏俱全。通过它,你将理解 Agent 开发的所有核心环节。
3.1.2 环境安装
步骤 1:安装 Python 和 Conda(如果你还没有的话)
去 Python官网 或 Miniconda官网 下载并安装。建议使用 Python 3.10 或更高版本。
步骤 2:创建项目文件夹和虚拟环境
打开你的终端(Terminal 或 CMD),输入以下命令:
# 创建文件夹
mkdir taskwhiz-agent
cd taskwhiz-agent
# 使用 conda 创建虚拟环境(推荐)
conda create -n taskwhiz python=3.11
conda activate taskwhiz
# 或者使用 venv(如果你没有 conda)
# python -m venv venv
# source venv/bin/activate # Mac/Linux
# .\venv\Scripts\activate # Windows
步骤 3:安装必要的库
我们只需要两个核心库:openai(官方 SDK)和 python-dotenv(管理环境变量)。
pip install openai python-dotenv requests
步骤 4:获取 OpenAI API Key
- 访问 OpenAI 平台 并注册/登录。
- 进入 API Keys 页面:https://platform.openai.com/api-keys。
- 点击 “Create new secret key”。
- 重要: 复制这个 Key,你只会看到这一次!
步骤 5:设置环境变量
在项目根目录下创建一个名为 .env 的文件,内容如下:
OPENAI_API_KEY=这里粘贴你的OpenAI Key
OPENAI_MODEL=gpt-3.5-turbo
安全提示: 永远不要把 .env 文件提交到 Git 仓库。创建一个 .gitignore 文件(如果你打算用 Git 的话),并在里面写上 .env。
3.1.3 项目目录结构规划
我们将采用清晰的模块化结构。现在,在你的文件夹里创建以下文件:
taskwhiz-agent/
├── .env # 环境变量
├── .gitignore # Git 忽略文件
├── main.py # 程序入口
├── agent/
│ ├── __init__.py
│ ├── llm.py # LLM 交互封装
│ ├── memory.py # 记忆模块
│ ├── tools.py # 工具定义与执行
│ └── agent.py # Agent 核心逻辑
└── utils/
├── __init__.py
└── helpers.py # 辅助函数
这看起来可能有点复杂,但别担心,我们会一步步填满它们。
3.2 第一步:构建最基础的对话接口 (Hello GPT)
让我们从最简单的部分开始:写一段代码,能够发送消息给 OpenAI 并拿到回复。
文件位置: agent/llm.py
在深入代码之前,我们需要理解 OpenAI 的 Chat Completions API 的数据结构。它的核心是 messages 列表,列表中的每一个元素是一个字典,包含 role 和 content。
role 通常有三种:
system:设定模型的行为和背景。user:用户的输入。assistant:模型的输出。
现在,让我们写代码:
# agent/llm.py
import os
from dotenv import load_dotenv
from openai import OpenAI
# 加载环境变量
load_dotenv()
class LLMClient:
def __init__(self):
self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
self.model = os.getenv("OPENAI_MODEL", "gpt-3.5-turbo")
def generate(self, messages, temperature=0.7, max_tokens=1000):
"""
调用 OpenAI API 生成回复
参数:
messages (list): 消息历史列表
temperature (float): 控制随机性,0 更确定,1 更随机
max_tokens (int): 最大生成 tokens 数
返回:
str: 模型生成的文本内容
"""
try:
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
temperature=temperature,
max_tokens=max_tokens
)
return response.choices[0].message.content.strip()
except Exception as e:
print(f"调用 LLM 时出错: {e}")
return "抱歉,我暂时无法处理你的请求。"
# 简单的测试代码
if __name__ == "__main__":
llm = LLMClient()
test_messages = [
{"role": "system", "content": "你是一个友好的助手。"},
{"role": "user", "content": "你好,请介绍一下你自己。"}
]
reply = llm.generate(test_messages)
print("AI:", reply)
恭喜你!这是你与 OpenAI API 的第一次握手。现在你可以在终端运行 python agent/llm.py 来测试一下。
3.3 第二步:添加记忆 (Memory) 模块
一个没有记忆的 Agent 是可悲的。如果用户问 “我叫什么名字?”,然后下一句问 “我的名字是什么?”,如果 Agent 回答不上来,体验会很差。
记忆通常分为两类:
- 短期记忆 (Short-term Memory):即当前的对话历史。
- 长期记忆 (Long-term Memory):跨会话的信息存储,通常需要向量数据库。
对于我们的简单 Agent,我们先实现短期记忆。
文件位置: agent/memory.py
# agent/memory.py
from typing import List, Dict
class Memory:
def __init__(self, system_prompt: str = "你是一个有帮助的助手。"):
self.history: List[Dict] = []
# 初始化系统提示词
if system_prompt:
self.add_message("system", system_prompt)
def add_message(self, role: str, content: str):
"""添加一条消息到历史记录"""
self.history.append({"role": role, "content": content})
def get_history(self) -> List[Dict]:
"""获取完整的历史记录"""
return self.history
def clear(self):
"""清空记忆,保留 system prompt"""
system_msg = self.history[0] if self.history else None
self.history = []
if system_msg:
self.history.append(system_msg)
def truncate(self, max_tokens: int = 3000):
"""
简单的截断策略(防止上下文溢出)
注意:这是一个简化版,实际生产中通常需要计算 Token 数量
"""
# 保留 system prompt 和最近的几条消息
if len(self.history) > 10: # 假设超过10轮对话就截断
system_msg = self.history[0]
recent_msgs = self.history[-8:] # 保留最近 8 条
self.history = [system_msg] + recent_msgs
# 测试代码
if __name__ == "__main__":
mem = Memory(system_prompt="你是一个数学家。")
mem.add_message("user", "1+1等于几?")
mem.add_message("assistant", "1+1等于2。")
mem.add_message("user", "那我上一个问题是什么?")
print("历史记录:", mem.get_history())
3.4 第三步:定义工具 (Tools) 与函数调用 (Function Calling)
这是 Agent 最令人兴奋的部分:让 AI 能够使用工具。
如果我们只是问 GPT “今天天气怎么样?”,它会回答"我不知道,我的知识截止到…"。但是,如果我们给它一个"查询天气"的工具,并教会它如何使用,情况就完全不同了。
OpenAI 提供了一个名为 “Function Calling”(函数调用) 的特性。这不是说 AI 真的在执行代码,而是说 AI 会识别出"哦,用户的这个问题需要调用某个工具",然后它会以特定的 JSON 格式输出,告诉我们它想调用哪个函数、传入什么参数。
然后,我们的代码负责去调用那个函数,拿到结果,再把结果塞回给对话历史,让 AI 总结出最终答案。
让我们用一个 Mermaid 流程图来直观理解这个过程:
好,现在让我们来实现这个逻辑。
文件位置: agent/tools.py
在这里,我们将定义两个工具:一个是模拟天气查询,另一个是数学计算器。
# agent/tools.py
import json
import random
from typing import List, Dict, Any, Callable
# --- 工具定义 ---
def get_current_weather(location: str, unit: str = "摄氏度") -> str:
"""
模拟获取指定地点的当前天气
参数:
location (str): 城市名称,例如 "北京"
unit (str): 温度单位,"摄氏度" 或 "华氏度"
返回:
str: 天气信息的 JSON 字符串
"""
# 模拟天气数据
weather_conditions = ["晴朗", "多云", "小雨", "阴天"]
condition = random.choice(weather_conditions)
# 模拟温度
temp_c = random.randint(-10, 35)
if unit == "华氏度":
temp = round((temp_c * 9/5) + 32)
else:
temp = temp_c
unit = "摄氏度"
return json.dumps({
"location": location,
"temperature": temp,
"unit": unit,
"condition": condition
})
def calculate_math_expression(expression: str) -> str:
"""
安全地计算数学表达式 (简单实现)
参数:
expression (str): 数学表达式,例如 "2 * (3 + 5)"
返回:
str: 计算结果
"""
try:
# 注意:在真实环境中直接使用 eval 是危险的!
# 这里仅作演示,生产环境请使用 ast.literal_eval 或专门的数学库
result = eval(expression)
return json.dumps({"expression": expression, "result": result})
except Exception as e:
return json.dumps({"error": str(e)})
# --- 工具注册中心 ---
class ToolRegistry:
def __init__(self):
self.tools: Dict[str, Callable] = {}
self.schemas: List[Dict] = []
def register_tool(self, func: Callable, schema: Dict):
"""
注册一个工具及其 Schema
Schema 是告诉 OpenAI 这个函数长什么样的描述。
"""
self.tools[schema["name"]] = func
self.schemas.append(schema)
def execute_tool(self, tool_name: str, arguments: str) -> str:
"""
执行工具
参数:
tool_name (str): 函数名
arguments (str): JSON 格式的参数字符串
返回:
str: 工具执行结果
"""
if tool_name not in self.tools:
return json.dumps({"error": f"Unknown tool: {tool_name}"})
try:
# 解析参数字符串为字典
kwargs = json.loads(arguments)
# 执行函数
result = self.tools[tool_name](**kwargs)
return result
except Exception as e:
return json.dumps({"error": f"Execution failed: {str(e)}"})
def get_schemas(self) -> List[Dict]:
return self.schemas
# --- 初始化并注册工具 ---
def init_tools() -> ToolRegistry:
registry = ToolRegistry()
# 1. 注册天气工具
weather_schema = {
"type": "function",
"function": {
"name": "get_current_weather",
"description": "获取指定地点的当前天气信息",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称,例如 '上海'、'New York'",
},
"unit": {"type": "string", "enum": ["摄氏度", "华氏度"]},
},
"required": ["location"],
},
},
}
registry.register_tool(get_current_weather, weather_schema)
# 2. 注册数学计算工具
math_schema = {
"type": "function",
"function": {
"name": "calculate_math_expression",
"description": "计算数学表达式,支持加减乘除和括号",
"parameters": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "数学表达式字符串,例如 '(2 + 3) * 5'",
},
},
"required": ["expression"],
},
},
}
registry.register_tool(calculate_math_expression, math_schema)
return registry
这里的重点是 schemas。我们需要用一种非常清晰的 JSON 格式告诉 GPT:“我有一个工具,它叫什么,它是干嘛的,它需要什么参数”。
3.5 第四步:整合 Agent 核心逻辑
现在,我们有了大脑(LLM)、记忆(Memory)和手脚(Tools)。是时候把它们组装在一起了!
文件位置: agent/agent.py
我们需要修改 llm.py 来支持 Function Calling 的响应。实际上,OpenAI 的 SDK 会自动检测模型是否想调用工具。为了保持代码整洁,我们把新的逻辑写在 agent.py 里。
# agent/agent.py
import os
import json
from dotenv import load_dotenv
from openai import OpenAI
from typing import List, Dict, Optional
from .memory import Memory
from .tools import ToolRegistry, init_tools
load_dotenv()
class Agent:
def __init__(self, system_prompt: str):
self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
self.model = os.getenv("OPENAI_MODEL", "gpt-3.5-turbo")
# 初始化组件
self.memory = Memory(system_prompt)
self.tools = init_tools()
def run(self, user_input: str) -> str:
"""
Agent 的主循环:
1. 添加用户输入到记忆
2. 思考是否需要调用工具
3. 如果需要,执行工具并观察结果
4. 生成最终回复
"""
print(f"[Agent] 收到用户输入: {user_input}")
# 1. 将用户消息加入记忆
self.memory.add_message("user", user_input)
# 2. 进入思考-行动循环 (ReAct 模式)
max_iterations = 5 # 防止无限循环
for i in range(max_iterations):
print(f"[Agent] 思考循环... 第 {i+1} 次")
# 调用 LLM
response = self._call_llm()
# 检查是否是工具调用
if response.tool_calls:
print(f"[Agent] 决定调用工具...")
# 处理工具调用
self._handle_tool_calls(response)
else:
# 没有工具调用,说明可以直接回复用户了
final_answer = response.content
self.memory.add_message("assistant", final_answer)
print(f"[Agent] 生成最终回复")
return final_answer
return "抱歉,我思考得太久了,让我们换个话题吧。"
def _call_llm(self):
"""内部方法:调用 LLM"""
messages = self.memory.get_history()
tools = self.tools.get_schemas()
# 如果没有注册工具,就不传 tools 参数
kwargs = {
"model": self.model,
"messages": messages,
}
if tools:
kwargs["tools"] = tools
response = self.client.chat.completions.create(**kwargs)
return response.choices[0].message
def _handle_tool_calls(self, response_message):
"""
内部方法:处理工具调用
这是 ReAct 模式中的 'Act' 部分
"""
# 1. 先把 model 想要调用工具的这个想法存进记忆
# 注意:OpenAI 要求必须把 assistant 的 tool_calls 消息也存进去
self.memory.history.append(response_message)
# 2. 遍历所有工具调用(虽然通常只有一个)
for tool_call in response_message.tool_calls:
tool_name = tool_call.function.name
tool_args = tool_call.function.arguments
print(f"[Agent] 执行工具: {tool_name}, 参数: {tool_args}")
# 3. 执行工具
result = self.tools.execute_tool(tool_name, tool_args)
print(f"[Agent] 工具返回结果: {result}")
# 4. 将工具结果作为 'tool' 角色的消息加入记忆
self.memory.add_message("tool", result, tool_call_id=tool_call.id)
# 这里我们需要稍微修改一下 Memory 的 add_message 方法来支持 tool_call_id
# 为了方便,我们在这个 class 里覆盖一下逻辑,或者你可以去 memory.py 里修改
# 让我们简单点处理:
def _patch_memory_add(self):
# 这是一个临时的示意,更好的做法是直接修改 memory.py
pass
等一下,上面的代码有个小问题。OpenAI 的 Tool Calling 机制要求,当你把工具结果返回时,必须附上 tool_call_id,并且消息的 role 是 tool。我们去完善一下 memory.py。
修改 agent/memory.py:
找到 add_message 方法,修改如下:
def add_message(self, role: str, content: str, tool_call_id: str = None):
"""添加一条消息到历史记录"""
msg = {"role": role, "content": content}
if tool_call_id:
msg["tool_call_id"] = tool_call_id
self.history.append(msg)
好了,现在我们的 Agent 核心逻辑基本完成了。这里面实际上是实现了一种经典的 Agent 模式,叫做 ReAct (Reasoning + Acting)。
3.6 第五步:编写入口文件与最终测试
现在让我们写 main.py,把一切都串联起来,并给 Agent 设定一个专业的 System Prompt。
文件位置: main.py
# main.py
from agent.agent import Agent
def main():
print("===================================")
print(" TaskWhiz: 你的 AI 助手 ")
print("===================================")
print("输入 'quit' 或 'exit' 退出程序。")
print("-----------------------------------")
# 定义 Agent 的人设和能力范围
SYSTEM_PROMPT = """
你是 TaskWhiz,一个聪明且专业的 AI 助手。
你的性格:
- 友好、简洁、高效。
- 不要使用过于正式的书面语,像日常聊天一样。
你的能力:
1. 你可以使用提供的工具来帮助用户。
2. 如果用户的问题需要查询实时信息(如天气)或复杂计算,请务必使用工具。
3. 如果工具的结果是 JSON,请将其整理成自然语言回答。
4. 如果没有合适的工具,或者问题不需要工具,请直接回答。
注意事项:
- 不要编造你不知道的信息。
- 如果信息不足,可以礼貌地向用户询问。
"""
# 初始化 Agent
agent = Agent(system_prompt=SYSTEM_PROMPT)
while True:
user_input = input("\n你: ")
if user_input.lower() in ["quit", "exit", "退出"]:
print("TaskWhiz: 再见!期待下次为你服务。")
break
if not user_input.strip():
continue
try:
response = agent.run(user_input)
print(f"\nTaskWhiz: {response}")
except Exception as e:
print(f"\nTaskWhiz: 哎呀,出错了... ({e})")
if __name__ == "__main__":
main()
3.7 见证奇迹的时刻:运行你的 Agent!
激动人心的时刻到了!在终端中输入:
python main.py
如果一切顺利,你会看到欢迎界面。现在,让我们来测试几个场景:
场景 1:普通对话(不使用工具)
你: 你好
TaskWhiz: 你好!有什么我可以帮你的吗?
场景 2:使用计算器
你: 帮我算一下 23 的平方乘以 2 再加 5 等于多少?
(Agent 内部会调用 calculate_math_expression)
TaskWhiz: 计算结果是 1063。
场景 3:查询天气
你: 我明天要去北京出差,那里天气怎么样?
(Agent 内部会调用 get_current_weather)
TaskWhiz: 北京目前的天气是晴朗,气温大约 22 摄氏度。出行建议带一件薄外套。
场景 4:多轮对话(测试记忆)
你: 我叫小明
TaskWhiz: 你好小明!很高兴认识你。
你: 我叫什么名字?
TaskWhiz: 你刚刚告诉我你叫小明。
如果你看到了类似的输出,那么恭喜你!你已经成功构建了你的第一个 Agent!
四、进阶探讨/最佳实践 (Advanced Topics / Best Practices)
现在你已经拥有了一个能跑通的最小可行性产品(MVP)。在这一章节,我们将深入探讨一些"如果要做得更好"的话题。
4.1 常见陷阱与避坑指南
4.1.1 幻觉 (Hallucination)
问题描述: LLM 有时候会"一本正经地胡说八道"。比如,如果你没有给它天气工具,它可能会编造一个天气数据给你。
解决方法:
- 清晰的 System Prompt:明确告诉它"不知道就说不知道"。
- 强制工具调用:对于某些特定查询,在 Prompt 里要求它必须先调用工具。
- 结果验证:对于工具返回的结果,如果有必要,可以进行二次校验。
4.1.2 上下文窗口限制 (Context Window Limitation)
问题描述: 每个模型都有最大 Token 限制(比如 gpt-3.5-turbo 是 16K)。如果你的对话历史太长,程序就会报错。
解决方法:
- 滑动窗口 (Sliding Window):只保留最近的 N 条消息。
- 总结记忆 (Summarization Memory):当历史即将溢出时,让 LLM 把之前的对话总结成一段摘要,然后把摘要作为 System Prompt 的一部分,清空旧的历史。
- 向量数据库 (RAG):对于长期记忆,这是终极解决方案,把记忆向量化后存储,需要时检索相关片段。
4.1.3 工具调用失败重试
问题描述: 即使是 GPT-4,有时候生成的 JSON 参数格式也会错,或者调用外部 API 时网络波动。
解决方法:
- Retry 机制:使用
tenacity库,给执行逻辑加上重试装饰器。 - 反馈循环:如果执行报错,把错误信息塞回给 LLM,让它自己修正参数再试一次。
4.2 性能优化/成本考量
4.2.1 Model Selection (模型选择)
并不是所有任务都需要 GPT-4。让我们做一个简单的对比:
| 模型 | 速度 | 成本 (Input/Output per 1M tokens) | 推理能力 | 适用场景 |
|---|---|---|---|---|
| GPT-3.5-Turbo | ⚡⚡⚡⚡ 快 | $0.50 / $1.50 | ⭐⭐⭐ | 分类、提取、简单工具调用 |
| GPT-4o-mini | ⚡⚡⚡ 快 | $0.15 / $0.60 | ⭐⭐⭐⭐ | 大多数日常 Agent 任务 |
| GPT-4o | ⚡⚡⚡ 中 | $5.00 / $15.00 | ⭐⭐⭐⭐⭐ | 复杂规划、多步推理 |
建议: 在开发阶段,你可以先在 Prompt 或 Tool 使用上遇到困难时,再切到 GPT-4 测试。如果 GPT-4 能跑通,再想办法优化 Prompt 让 GPT-3.5 也能跑通。
4.2.2 Prompt 精简
System Prompt 每次都要发送,所以要惜字如金。去掉不必要的客套话,使用简洁的指令。
4.3 更好的 Agent 模式:从 ReAct 到更高级的规划
我们的简单 Agent 使用的是 ReAct 模式(想一步,做一步)。但在实际生产中,还有更复杂的模式。
让我们用一个 Mermaid 图来展示不同 Agent 模式的复杂度演进:
(我们的版本)] B --> C -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'
4.3.1 Plan-and-Execute (规划与执行分离)
我们的 Agent 是边想边做,这在复杂任务下容易迷失。一种更好的模式是:
- 规划者 (Planner):一个专门的 LLM 实例,负责把目标拆解为任务列表。
- 执行者 (Executor):另一个 LLM 实例,负责按照列表逐个调用工具完成任务。
4.4 安全最佳实践 (Security Best Practices)
当你把 Agent 从玩具变成产品时,安全是重中之重。
永远不要信任模型生成的参数!
在我们的 calculate_math_expression 中,我用了 eval(),这在生产环境中是极度危险的。如果模型生成的表达式是 __import__('os').system('rm -rf /'),你的服务器文件就没了。
安全建议:
- 沙箱运行 (Sandboxing):所有代码执行必须在 Docker 容器或专门的沙箱环境(如 E2B)中运行。
- 白名单 (Whitelisting):只允许特定的函数或操作。
- 人工确认 (Human-in-the-Loop):在执行高危操作(如发送邮件、转账)前,弹出确认框让用户点击。
五、结论 (Conclusion)
5.1 核心要点回顾 (The Summary)
如果你坚持读到了这里,非常棒!让我们回顾一下这篇长文的核心旅程:
- 我们从概念出发:理解了什么是 LLM、什么是 Agent、什么是 Function Calling。
- 我们搭建了骨架:创建了模块化的项目结构。
- 我们逐个实现了器官:
llm.py: 连接"大脑"。memory.py: 赋予"记忆"。tools.py: 制造"工具"。agent.py: 实现"决策逻辑"。
- 我们最终组装并唤醒了 TaskWhiz。
- 我们探讨了未来的进阶之路:如何优化、如何避免陷阱、如何保证安全。
你已经掌握了构建 Agent 的核心范式:感知 (Perception) -> 推理 (Reasoning) -> 决策 (Decision) -> 行动 (Action) -> 观察 (Observation)。这是一个通用的闭环,适用于绝大多数智能系统。
5.2 行业发展与未来趋势
Agent 技术的发展可谓日新月异。让我们用一个表格来简要回顾一下这个领域的简短历史:
| 时间 | 事件 | 意义 |
|---|---|---|
| 2022年底 | ChatGPT 发布 | 让 LLM 走进大众视野,开发者开始思考如何让 LLM 更"有用"。 |
| 2023年中 | OpenAI 发布 Function Calling | 这是一个里程碑。官方定义了 LLM 与工具交互的标准,Agent 开发难度骤降。同期,LangChain 爆发。 |
| 2023年底 | AutoGPT / BabyAGI 火爆 | 展示了 Agent 自主完成复杂任务的潜力(虽然也暴露了很多问题)。 |
| 2024年 | GPT-4o, Claude 3 发布 | 多模态能力加入,Agent 不仅能处理文本,还能"看"图。 |
| 未来 (2025+) | ? | Agent 可能会像今天的 App 一样普及。操作系统级的 Agent 集成(如 Windows Copilot+)可能会改变人机交互方式。 |
未来的 Agent 会是什么样?
- 更个性化:深度适配个人习惯和数据。
- 更具实体感:与机器人结合,或者在虚拟世界中。
- 社会化协作:多个 Agent 像团队一样分工合作(Multi-Agent System)。
5.3 行动号召 (Call to Action)
“纸上得来终觉浅,绝知此事要躬行。”
你的下一步行动:
- 把代码跑起来:如果你还没试,现在就去复制代码,申请一个 API Key,运行
main.py。 - 扩展功能:尝试给 TaskWhiz 加一个新工具。比如:
- 调用真实的天气 API(如 OpenWeatherMap)。
- 让它帮你读写本地的一个记事本文件。
- 深入学习:
- 研究一下 LangChain 或 LlamaIndex,看看框架是如何封装这些逻辑的。
- 关注 OpenAI Swarm (实验性多 Agent 框架) 或 CrewAI。
互动时间:
如果你在构建过程中有任何问题,或者你给 TaskWhiz 加上了很酷的新功能,欢迎在评论区分享!
感谢阅读! 如果你觉得这篇文章对你有帮助,请分享给更多想要入门 Agent 开发的朋友。编程的世界里,最重要的永远是好奇心和动手能力。祝你在 Agent 开发的旅程中玩得开心!
更多推荐
所有评论(0)