小白入门:用 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”)

读完这篇文章,你将能够:

  1. 理解 Agent 的核心概念和工作原理:不再对"智能体"这个概念感到神秘。
  2. 掌握 OpenAI API 的基础使用方法:包括如何调用模型、处理响应。
  3. 从零构建一个简单而实用的 Agent:这个 Agent 将能够执行任务规划、信息检索和简单的工具调用。
  4. 了解 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(dk QKT)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 通常包含以下几个核心部分:

  1. 规划 (Planning): 将大目标拆解为小的、可管理的步骤。
  2. 记忆 (Memory): 短期记忆(对话历史)和长期记忆(从过去的经验中学习)。
  3. 工具使用 (Tool Use): 调用外部 API、数据库、代码执行器等能力。

让我们用一个 Mermaid ER 图来展示 Agent 的核心组件及其关系:

控制核心

读写

调用

使用

交互

感知/作用

AGENT

string

id

string

name

string

goal

LLM

string

provider

string

model_name

float

temperature

MEMORY

string

type

int

max_tokens

TOOLS

string

name

string

description

function

implementation

PLANNER

string

strategy

USER

ENVIRONMENT

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-turbogpt-4o-mini 会是非常好的起点。

2.2.2 LangChain vs. 原生开发

你可能听说过 LangChain,这是一个非常流行的用于构建 LLM 应用的框架。它提供了很多现成的组件(Chain、Agent、Memory 等)。

但是,对于小白入门,我强烈建议从原生 Python 代码开始,原因如下:

  1. 理解本质:LangChain 封装了太多细节,容易让人"知其然不知其所以然"。
  2. 灵活性:自己写代码,你拥有完全的控制权。
  3. 减少依赖:少一个外部依赖,少一个出问题的点。

等你掌握了底层原理后,再去使用 LangChain 这样的框架,你会发现你能更好地驾驭它。

2.2.3 Python 环境管理:Conda 与 Pip

为了保持我们的开发环境干净整洁,我们将使用虚拟环境。如果你是 Python 新手,Miniconda 是一个非常好的选择。


三、核心内容/实战演练 (The Core - “How-To”)

好了,概念讲得差不多了,让我们把手弄脏,开始写代码!这一部分是本文的重中之重。

3.1 项目准备与环境搭建

3.1.1 项目介绍

我们将构建一个名为 “TaskWhiz” 的简单 Agent。它的核心功能是:

  1. 理解用户的自然语言请求。
  2. 如果需要,向用户追问更多信息。
  3. 能够查询天气(模拟一个外部工具)。
  4. 能够进行简单的数学计算。
  5. 保存对话记忆。

这听起来可能很简单,但麻雀虽小,五脏俱全。通过它,你将理解 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

  1. 访问 OpenAI 平台 并注册/登录。
  2. 进入 API Keys 页面:https://platform.openai.com/api-keys
  3. 点击 “Create new secret key”。
  4. 重要: 复制这个 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 列表,列表中的每一个元素是一个字典,包含 rolecontent

role 通常有三种:

  1. system:设定模型的行为和背景。
  2. user:用户的输入。
  3. 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 回答不上来,体验会很差。

记忆通常分为两类:

  1. 短期记忆 (Short-term Memory):即当前的对话历史。
  2. 长期记忆 (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 思考

生成 Function Call

直接回复

代码执行 Tool

结果返回给 LLM

LLM 总结结果

结束

好,现在让我们来实现这个逻辑。

文件位置: 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 模式的复杂度演进:

渲染错误: Mermaid 渲染失败: Parse error on line 2: ...> B[ReAct 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 是边想边做,这在复杂任务下容易迷失。一种更好的模式是:

  1. 规划者 (Planner):一个专门的 LLM 实例,负责把目标拆解为任务列表。
  2. 执行者 (Executor):另一个 LLM 实例,负责按照列表逐个调用工具完成任务。

4.4 安全最佳实践 (Security Best Practices)

当你把 Agent 从玩具变成产品时,安全是重中之重。

永远不要信任模型生成的参数!
在我们的 calculate_math_expression 中,我用了 eval(),这在生产环境中是极度危险的。如果模型生成的表达式是 __import__('os').system('rm -rf /'),你的服务器文件就没了。

安全建议:

  1. 沙箱运行 (Sandboxing):所有代码执行必须在 Docker 容器或专门的沙箱环境(如 E2B)中运行。
  2. 白名单 (Whitelisting):只允许特定的函数或操作。
  3. 人工确认 (Human-in-the-Loop):在执行高危操作(如发送邮件、转账)前,弹出确认框让用户点击。

五、结论 (Conclusion)

5.1 核心要点回顾 (The Summary)

如果你坚持读到了这里,非常棒!让我们回顾一下这篇长文的核心旅程:

  1. 我们从概念出发:理解了什么是 LLM、什么是 Agent、什么是 Function Calling。
  2. 我们搭建了骨架:创建了模块化的项目结构。
  3. 我们逐个实现了器官
    • llm.py: 连接"大脑"。
    • memory.py: 赋予"记忆"。
    • tools.py: 制造"工具"。
    • agent.py: 实现"决策逻辑"。
  4. 我们最终组装并唤醒了 TaskWhiz
  5. 我们探讨了未来的进阶之路:如何优化、如何避免陷阱、如何保证安全。

你已经掌握了构建 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)

“纸上得来终觉浅,绝知此事要躬行。”

你的下一步行动:

  1. 把代码跑起来:如果你还没试,现在就去复制代码,申请一个 API Key,运行 main.py
  2. 扩展功能:尝试给 TaskWhiz 加一个新工具。比如:
    • 调用真实的天气 API(如 OpenWeatherMap)。
    • 让它帮你读写本地的一个记事本文件。
  3. 深入学习
    • 研究一下 LangChain 或 LlamaIndex,看看框架是如何封装这些逻辑的。
    • 关注 OpenAI Swarm (实验性多 Agent 框架) 或 CrewAI

互动时间:
如果你在构建过程中有任何问题,或者你给 TaskWhiz 加上了很酷的新功能,欢迎在评论区分享!


感谢阅读! 如果你觉得这篇文章对你有帮助,请分享给更多想要入门 Agent 开发的朋友。编程的世界里,最重要的永远是好奇心和动手能力。祝你在 Agent 开发的旅程中玩得开心!

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐