引言:AI Agent的兴起与开发痛点

AI Agent(智能代理)是当前人工智能领域最前沿的技术之一。

请添加图片描述

它不再是简单的问答工具,而是具备感知环境、制定计划、调用工具并自主执行复杂任务的自主智能系统。从ChatGPT横空出世,到Claude、Gemini等大语言模型(LLM)相继发布,AI Agent彻底改变了开发者构建自动化系统的思路。传统软件开发侧重于代码逻辑和算法实现,而AI Agent则需要深度桥接LLM的自然语言输出与外部世界的交互,这带来了前所未有的开发范式转变。

AI Agent的概念起源于ReAct框架(Reason + Act),最早在2022年由谷歌DeepMind团队提出,其核心思想是将大型语言模型的推理过程与实际行动相结合。ReAct框架将每个推理步骤拆分为“思考”(Reasoning)、“行动”(Acting)和“观察”(Observation)三个阶段,这种设计显著提升了模型在复杂任务上的表现能力。2023年OpenAI正式推出工具调用API后,AI Agent正式进入大规模应用阶段。开发者不再局限于单轮对话,而是可以构建支持实时数据访问、API集成和多步骤执行的自主系统。

然而,开发AI Agent绝非易事。核心痛点体现在以下几个方面:

  • API集成与认证:如何安全调用OpenAI、Anthropic或Google等主流API?需要处理密钥管理、速率限制(RPM/TMP)、错误重试和流式输出等技术细节。特别是在生产环境中,频繁的API调用可能触发速率限制或导致成本激增,因此需要合理的缓存策略和重试机制。
  • 工具调用机制:LLM如何决定调用外部函数?模型输出必须兼容JSON格式的函数定义,才能正确解析参数并执行。模型的函数调用能力源于其对JSON Schema的理解能力,以及在生成过程中对工具描述的上下文关联判断。
  • 任务自动执行闭环:从用户输入到最终结果的完整流程,包括状态管理、对话历史记忆、多轮对话协调,以及工具执行后的反馈迭代。这种闭环设计要求Agent具备动态规划能力,能够根据工具返回的结果调整后续步骤。
  • 健壮性与扩展性:LLM存在幻觉、token消耗过高、工具失败或API变更等问题,如何优雅处理?如何实现异步并行、缓存优化和日志追踪?特别是在多Agent协作场景中,如何协调不同Agent之间的通信和状态同步。

这些痛点直接影响开发效率和系统稳定性。本文将从零开始,使用纯Python技术栈,构建一个完整可用的AI Agent框架。我们将逐步实现大模型调用、工具调用和任务自动执行,并通过实战案例加深理解。最后讨论常见踩坑与优化策略,帮助读者快速上手,甚至扩展到生产级系统。

本文目标是提供可直接运行的代码示例,并深入讲解原理,避免空泛理论。所有代码基于Python 3.10+和openai库(v1.0+),可无缝迁移到Ollama等本地模型。相比其他语言,Python以其简洁的语法和丰富的生态系统,成为AI Agent开发的理想选择。开发者可以轻松集成各种工具库,例如用于调试的pdb模块,或用于性能分析的cProfile。此外,Python的跨平台特性使其Agent可以轻松部署在服务器、边缘设备甚至移动端。

为了更好地理解AI Agent的潜在应用场景,我们来看几个典型的用例。首先,在数据分析领域,Agent可以自动调用计算工具和API接口,完成复杂的统计分析任务。例如,金融分析师可以使用Agent分析股票数据、生成投资报告,并根据实时市场数据调整策略。其次,在自动化运维中,Agent可以感知系统状态,调用命令行工具或API执行部署、故障排查和性能优化。这种自主能力显著降低了人工干预需求,提升了系统可靠性。

然而,构建AI Agent也会遇到一系列挑战。例如,模型的“幻觉”问题可能导致生成看似合理但实际上错误的答案,尤其是在需要高精度数据的场景下。此外,工具调用过程可能存在无限循环风险,例如Agent反复尝试调用同一工具而无法获得有效结果。因此,合理的循环控制、错误恢复机制和上下文管理至关重要。

核心原理:大模型调用、工具调用与任务自动执行

大模型调用原理

大模型调用是AI Agent的基础。它通过HTTP接口与LLM进行交互,核心流程包括:

  1. 准备API密钥和模型参数。
  2. 构建消息历史(Message History):系统提示(System Prompt)、用户消息、助手消息。
  3. 发送请求并解析响应。
  4. 处理流式输出(stream)以实现实时交互。

主流LLM提供商采用聊天完成接口(Chat Completions)。OpenAI库是最简洁的Python SDK,支持异步调用和工具集成。使用方式包括:

  • 直接POST请求到https://api.openai.com/v1/chat/completions,使用application/json格式。
  • 利用官方SDK简化认证和序列化。

代码示例1:基础大模型调用

import openai
import os
import json
from typing import List, Dict

# 推荐从.env文件或环境变量读取密钥
# os.environ["OPENAI_API_KEY"] = "sk-your-api-key-here"

def call_llm(prompt: str, model: str = "gpt-3.5-turbo", max_tokens: int = 500, temperature: float = 0.7) -> str:
    """基础大模型调用函数"""
    # 系统提示优化上下文
    messages: List[Dict[str, str]] = [
        {"role": "system", "content": "你是一个有帮助、专业的助手,回答要简洁准确。"},
        {"role": "user", "content": prompt}
    ]
    
    try:
        response = openai.ChatCompletion.create(
            model=model,
            messages=messages,
            max_tokens=max_tokens,
            temperature=temperature,
            top_p=0.9,
            frequency_penalty=0.0,
            presence_penalty=0.0
        )
        # 提取文本内容
        content = response.choices[0].message.content.strip()
        return content
    except Exception as e:
        return f"调用失败:{str(e)} - 请检查API密钥和网络连接"

# 示例调用
if __name__ == "__main__":
    test_prompt = "请用Python代码实现一个简单的斐波那契数列生成器,并解释其时间复杂度"
    result = call_llm(test_prompt)
    print("模型响应:")
    print(result)

在这个示例中,call_llm函数封装了调用逻辑。messages列表保持上下文连贯,避免重复发送完整提示。参数如temperature控制生成随机性(0.0最确定,1.0最创造性),max_tokens防止无限输出。实际项目中,建议使用response对象的usage属性监控token消耗,并实现重试逻辑(使用tenacity库实现指数退避)。例如,在处理敏感数据时,可以设置max_tokens=200以减少成本,并结合top_p=0.95进行一定程度的随机性控制以获得更自然的回答。

此外,top_p参数(核采样)进一步控制生成分布,通过累积概率密度函数决定是否选择下一个token。实际应用中,这有助于避免过于保守或激进的输出。在生产环境中,合理的token管理至关重要,因为每个模型都有严格的上下文窗口限制(例如GPT-3.5-turbo的8K tokens)。如果对话历史过长,建议使用滑动窗口技术定期清理旧消息,或者采用摘要机制将历史浓缩成关键点。

大模型调用的本质是理解其输入输出格式:模型将用户消息转换为内部表示,通过注意力机制生成下一个token序列。正确构建系统提示是关键——它能显著提升回答质量和一致性。例如,一个精心设计的系统提示可以包含角色定义、行为准则和特定任务约束,从而引导模型生成符合预期的输出。研究表明,优化后的提示词工程(Prompt Engineering)可以使模型性能提升20-30%。在实际开发中,建议使用chain-of-thought(CoT)提示技巧,引导模型逐步推理,这对于复杂问题尤其有效。

工具调用机制详解

工具调用(Tool Use / Function Calling)是LLM的革命性能力。它允许模型在响应中声明需要调用外部工具,而非直接生成答案。原理基于:

  • 函数定义JSON Schema:描述工具名称、描述和参数结构。JSON Schema是一种标准化的数据验证格式,用于定义API接口的输入参数类型、约束和描述信息。每个工具的函数定义必须严格遵循此格式,包括typepropertiesrequired等字段。
  • 模型决策过程:在生成响应时,LLM会输出tool_calls数组,指定调用哪个工具、参数是什么。模型在训练时学习了如何识别需要调用工具的场景,并通过微调生成符合JSON格式的tool_calls输出。
  • 代理解析执行:后端接收tool_calls,执行工具函数,获取结果,并将结果作为新消息追加到历史中。
  • 迭代闭环:直到模型决定提供最终答案。

这种机制突破了LLM知识截止日期问题,支持实时数据访问(如API、数据库、计算器)。OpenAI API最早在2023年推出,之后被广泛采用。工具调用不仅限于单一工具,还支持并行调用多个工具和嵌套调用。

代码示例2:带工具调用的完整LLM调用

import openai
import json
from typing import List, Dict, Callable, Any

def calculator(a: float, b: float) -> float:
    """计算两个数的和"""
    return a + b

def get_weather(city: str) -> str:
    """获取指定城市的天气(模拟真实API调用)"""
    # 生产环境应替换为真实API如OpenWeatherMap
    weather_data = {
        "北京": "晴朗,18°C",
        "上海": "多云,25°C",
        "广州": "阵雨,30°C"
    }
    return weather_data.get(city, f"无法获取{city}天气信息")

def get_current_time() -> str:
    """获取当前时间"""
    from datetime import datetime
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

tools = [
    {
        "type": "function",
        "function": {
            "name": "calculator",
            "description": "执行基本数学计算",
            "parameters": {
                "type": "object",
                "properties": {
                    "a": {"type": "number", "description": "第一个数字"},
                    "b": {"type": "number", "description": "第二个数字"}
                },
                "required": ["a", "b"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的天气信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "城市名称"}
                },
                "required": ["city"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "获取当前系统时间",
            "parameters": {
                "type": "object",
                "properties": {},
                "required": []
            }
        }
    }
]

def call_llm_with_tools(prompt: str, max_steps: int = 5) -> str:
    """带工具调用的高级调用函数"""
    messages: List[Dict[str, Any]] = [{"role": "user", "content": prompt}]
    tool_functions: Dict[str, Callable] = {
        "calculator": calculator,
        "get_weather": get_weather,
        "get_current_time": get_current_time
    }
    
    steps = 0
    final_response = None
    
    while steps < max_steps and final_response is None:
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=messages,
            tools=tools,
            tool_choice="auto",
            temperature=0.3,
            max_tokens=800
        )
        
        assistant_message = response.choices[0].message
        
        if assistant_message.content:
            final_response = assistant_message.content
            break
            
        # 处理工具调用
        if hasattr(assistant_message, 'tool_calls') and assistant_message.tool_calls:
            tool_call = assistant_message.tool_calls[0]
            func_name = tool_call.function.name
            args_str = tool_call.function.arguments
            
            try:
                func = tool_functions.get(func_name)
                if func:
                    args = json.loads(args_str) if args_str else {}
                    result = func(**args)
                    
                    # 追加消息历史
                    messages.append({
                        "role": "assistant",
                        "content": assistant_message.content if assistant_message.content else "",
                        "tool_calls": [tool_call.to_dict()]
                    })
                    messages.append({
                        "role": "tool",
                        "tool_call_id": tool_call.id,
                        "content": str(result)
                    })
                else:
                    messages.append({"role": "assistant", "content": f"未找到工具:{func_name}"})
            except Exception as e:
                messages.append({"role": "tool", "tool_call_id": tool_call.id, "content": f"执行错误:{str(e)}"})
            
            steps += 1
        else:
            break
    
    return final_response or "任务未能完成。"

# 测试调用
if __name__ == "__main__":
    test_query = "上海今天的天气怎么样?现在几点了?1加2等于多少?"
    result = call_llm_with_tools(test_query)
    print("Agent最终响应:")
    print(result)

上述代码的关键是正确解析tool_calls对象(.to_dict()用于序列化)和构建工具字典映射。tool_choice="auto"让模型自主决定是否调用工具。实际使用时,建议验证参数类型(使用jsonschema库)并处理并行工具调用(OpenAI支持一次多个tool_calls)。此机制本质上是LLM的"决策 + 执行"循环。

JSON Schema在工具调用中的重要性在于,它不仅定义了参数结构,还提供了人类可读的描述信息。模型通过理解这些描述来决定是否调用特定工具。例如,当用户询问天气时,模型会匹配get_weather工具的描述,并解析city参数。对于更复杂的场景,可以定义多个工具并通过tool_choice参数控制模型的行为。tool_choice选项包括auto(自动)、none(禁用工具调用)和具体工具名称(强制调用特定工具)。

此外,工具调用支持流式输出,这在实时交互场景中尤为重要。通过设置stream=True,可以实现边生成边输出响应,显著提升用户体验。在生产环境中,建议结合异步编程模型(如asyncio)处理高并发调用,以避免阻塞事件循环。

任务自动执行的核心原理

任务自动执行构建在工具调用之上,形成闭环Agent系统。典型架构基于ReAct框架(Reason + Act):

  1. 观察(Observation):接收用户输入或环境状态。通过消息历史管理,Agent可以感知到当前上下文和之前的工具执行结果。
  2. 思考(Reasoning):模型分析问题、规划步骤。模型在生成响应时,会根据系统提示和对话历史思考如何解决问题,可能决定调用工具或直接回答。
  3. 行动(Action):决定并调用工具。模型在生成阶段输出tool_calls数组,指定需要执行的操作。
  4. 执行(Execution):运行工具函数并获取反馈。工具执行完成后,结果被追加到消息历史中,作为下一次调用的输入。
  5. 记忆(Memory):存储对话历史和工具结果,支持多轮对话。合理的记忆管理可以防止上下文溢出,并保持对话连贯性。
  6. 停止条件:达到最终答案、最大步数(避免无限循环)或用户确认。通过设置max_steps参数和检查assistant_message.content字段,Agent可以判断何时停止并返回最终答案。

实现原理包括循环控制、状态机管理和反馈迭代。Agent循环运行直到满足停止条件。核心挑战是管理上下文窗口和工具副作用。以下是一个基于ReAct框架的详细工作流程图示:

用户输入
    ↓
系统提示 + 消息历史
    ↓
模型生成响应
    ├── 有内容 → 返回最终答案
    └── 有tool_calls → 执行工具 + 更新消息历史
        ↓
循环至多max_steps步
    ↓
返回最终答案

这种循环设计使得Agent具备了自主完成多步骤任务的能力。例如,一个复杂的查询可能需要先获取天气数据,再进行计算,最后生成报告。每个步骤的执行都依赖于前一步的结果,从而形成完整的闭环。

在实际实现中,任务自动执行还涉及状态管理。例如,可以使用字典或数据库存储Agent的当前状态,包括已执行的工具、剩余步数和最终答案。这样的设计有助于在遇到错误时进行恢复,或者在多轮对话中保持上下文一致性。

实战案例:构建一个完整的AI Agent实现

下面我们通过实战构建一个端到端的AI Agent——“天气与计算专家”。它支持用户自然语言查询,并自动调用工具执行任务。

Agent类设计与实现

import openai
import json
import time
from typing import List, Dict, Callable, Any
from datetime import datetime

class AIAgent:
    def __init__(self, model: str = "gpt-3.5-turbo"):
        self.model = model
        self.tools = []
        self.tool_functions: Dict[str, Callable] = {}
        self.memory: List[Dict[str, Any]] = []
        self.max_steps = 5
        self.retry_attempts = 3
        
    def add_tool(self, name: str, description: str, params_schema: Dict, func: Callable):
        """注册工具"""
        tool_def = {
            "type": "function",
            "function": {
                "name": name,
                "description": description,
                "parameters": params_schema
            }
        }
        self.tools.append(tool_def)
        self.tool_functions[name] = func
    
    def _format_tools(self) -> List[Dict]:
        return self.tools
    
    def _build_messages(self, user_input: str) -> List[Dict[str, Any]]:
        """构建消息历史,保留记忆"""
        messages = self.memory + [{"role": "user", "content": user_input}]
        return messages
    
    def run(self, user_input: str) -> str:
        """执行Agent循环任务"""
        messages = self._build_messages(user_input)
        steps = 0
        final_answer = None
        last_error = None
        
        while steps < self.max_steps and final_answer is None:
            response = openai.ChatCompletion.create(
                model=self.model,
                messages=messages,
                tools=self._format_tools(),
                tool_choice="auto",
                temperature=0.3,
                max_tokens=800
            )
            
            assistant_message = response.choices[0].message
            messages.append({"role": "assistant", "content": assistant_message.content or ""})
            
            # 处理工具调用
            if hasattr(assistant_message, 'tool_calls') and assistant_message.tool_calls:
                tool_call = assistant_message.tool_calls[0]
                func_name = tool_call.function.name
                args_str = tool_call.function.arguments
                
                try:
                    func = self.tool_functions.get(func_name)
                    if func:
                        args = json.loads(args_str) if args_str else {}
                        result = func(**args)
                        
                        messages.append({
                            "role": "tool",
                            "tool_call_id": tool_call.id,
                            "content": str(result)
                        })
                    else:
                        messages.append({"role": "tool", "tool_call_id": tool_call.id, "content": "工具不存在"})
                except Exception as e:
                    last_error = str(e)
                    messages.append({"role": "tool", "tool_call_id": tool_call.id, "content": f"执行错误:{last_error}"})
            
            # 检查最终答案
            if assistant_message.content and not hasattr(assistant_message, 'tool_calls'):
                final_answer = assistant_message.content
                break
                
            steps += 1
            time.sleep(0.5)  # 避免速率限制
        
        self.memory = messages[-10:]  # 限制记忆长度
        return final_answer or last_error or "任务执行失败,请检查工具或模型"

# 注册工具
def get_weather(city: str) -> str:
    """真实天气API调用(示例)"""
    # 替换为实际API:requests.get("https://api.openweathermap.org/...", params=...)
    return f"{city}的天气:晴朗,25°C,风速3m/s"

def calculator(a: float, b: float) -> float:
    """数学计算工具"""
    return a + b

def get_time() -> str:
    """获取当前时间"""
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

# 实例化Agent
agent = AIAgent(model="gpt-3.5-turbo")
agent.add_tool("get_weather", "获取城市天气", {"type": "object", "properties": {"city": {"type": "string"}}}, get_weather)
agent.add_tool("calculator", "计算两个数字", {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}, calculator)
agent.add_tool("get_time", "获取当前时间", {"type": "object", "properties": {}}, get_time)

# 运行示例
if __name__ == "__main__":
    user_query = "上海今天的天气如何?现在几点了?1加3等于多少?帮我计算一下"
    result = agent.run(user_query)
    print("AI Agent最终输出:")
    print(result)

此Agent类包含内存管理(self.memory限制历史长度)、错误重试和步数控制。add_tool方法注册工具时同时保存映射。实战中,该Agent可处理复杂多步骤查询,并自动迭代。

在实战案例中,我们可以看到Agent类通过add_tool方法灵活注册自定义工具。这使得用户可以根据特定需求扩展Agent的功能。例如,可以添加一个数据库查询工具,用于检索历史数据,或者一个文件操作工具,用于读取配置文件。注册工具时,参数params_schema必须遵循JSON Schema格式,确保模型能够正确解析参数。

此外,Agent类的run方法实现了核心的循环逻辑。在每次迭代中,Agent会构建消息历史,调用LLM生成响应。如果响应包含tool_calls,则执行对应的工具函数并将结果追加到消息历史中。如果响应直接包含内容,则停止循环并返回答案。通过限制max_steps为5,我们可以有效防止无限循环,即使在工具调用失败的情况下,Agent也会在一定步数后停止并报告错误。

在这个示例中,我们还演示了如何添加多个工具并注册到Agent实例中。实际应用中,可以使用更复杂的工具,例如集成第三方API或执行Python函数。这些工具可以根据用户查询动态选择执行,从而实现更智能的自动化流程。

为了进一步丰富Agent的功能,可以扩展其支持多轮对话和记忆持久化。例如,在__init__方法中添加一个可选参数memory_file,并使用json库在每次运行后保存self.memory到文件中,下次初始化时从文件中加载。这样可以实现跨会话的记忆保留,提升Agent的连贯性。

运行输出示例(假设调用成功):

AI Agent最终输出:
上海今天的天气是晴朗,25°C,风速3m/s。现在时间是2024-10-01 12:34:56。1加3等于4。

工具调用实战扩展

为了演示工具调用的高级用法,我们可以添加一个支持并行调用的示例。以下是一个修改后的call_llm_with_tools函数,支持同时调用多个工具:

def call_llm_with_tools(prompt: str, max_steps: int = 5) -> str:
    """支持并行工具调用的高级调用函数"""
    messages = [{"role": "user", "content": prompt}]
    tool_functions = {
        "calculator": calculator,
        "get_weather": get_weather,
        "get_current_time": get_current_time
    }
    
    steps = 0
    final_response = None
    
    while steps < max_steps and final_response is None:
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=messages,
            tools=tools,
            tool_choice="auto",
            temperature=0.3,
            max_tokens=800
        )
        
        assistant_message = response.choices[0].message
        
        if assistant_message.content:
            final_response = assistant_message.content
            break
            
        if hasattr(assistant_message, 'tool_calls') and assistant_message.tool_calls:
            tool_calls = assistant_message.tool_calls
            for tool_call in tool_calls:
                func_name = tool_call.function.name
                args_str = tool_call.function.arguments
                
                try:
                    func = tool_functions.get(func_name)
                    if func:
                        args = json.loads(args_str) if args_str else {}
                        result = func(**args)
                        
                        messages.append({
                            "role": "assistant",
                            "content": assistant_message.content if assistant_message.content else "",
                            "tool_calls": [tool_call.to_dict()]
                        })
                        messages.append({
                            "role": "tool",
                            "tool_call_id": tool_call.id,
                            "content": str(result)
                        })
                except Exception as e:
                    messages.append({
                        "role": "tool",
                        "tool_call_id": tool_call.id,
                        "content": f"执行错误:{str(e)}"
                    })
            steps += 1
        else:
            break
    
    return final_response or "任务未能完成。"

此扩展支持并行处理多个tool_calls,提高了Agent处理复杂查询的效率。例如,用户可以同时询问天气和当前时间,Agent可以一次性调用两个工具并返回结果。

记忆管理与持久化

在Agent类中,self.memory属性用于存储对话历史。为了进一步提升其功能,我们可以实现一个save_memory方法,将内存持久化到文件:

def save_memory(self, file_path: str = "agent_memory.json"):
    """保存记忆到文件"""
    with open(file_path, 'w') as f:
        json.dump(self.memory, f, ensure_ascii=False, indent=2)

def load_memory(self, file_path: str = "agent_memory.json"):
    """从文件加载记忆"""
    if os.path.exists(file_path):
        with open(file_path, 'r') as f:
            self.memory = json.load(f)

通过这样的扩展,Agent可以在重新启动后继续之前的对话,使其具备更强的长期记忆能力。这对于多轮对话场景尤其有用。

踩坑与优化建议

开发AI Agent时,常见问题包括:

  1. API限速与成本:OpenAI有每分钟速率限制。使用backoff库实现重试,并监控response.usage中的token消耗。实际成本取决于token数量和调用频率。
  2. token窗口溢出:对话历史过长导致超限。采用滑动窗口或摘要机制压缩记忆。可以定期清理旧消息,或使用提示技巧引导模型生成摘要。
  3. 工具执行异常:工具函数可能抛出异常。必须使用try-except捕获,并将错误反馈给模型。建议在工具函数中添加详细的错误处理逻辑。
  4. 模型幻觉:即使调用工具,模型有时仍会生成不一致答案。加入系统提示"如果不确定,请使用工具"。或者通过多次提示迭代进行验证。
  5. 参数解析错误tool_calls参数为JSON字符串。生产环境应使用jsonschema验证。可以使用jsonschema库在执行前验证参数。
  6. 并行工具调用:OpenAI支持一次多个工具。需修改代码以处理tool_calls列表。注意处理每个tool_call时可能出现的异常。
  7. 异步优化:高并发场景下,使用asyncioaiohttp代替同步请求。异步编程可以显著提升Agent的响应速度。
  8. 调试困难:添加详细日志记录模型输入、工具调用轨迹和响应。使用logging模块记录关键步骤,便于排查问题。

优化建议

  • 使用python-dotenv管理密钥。可以通过load_dotenv()加载.env文件,避免硬编码密钥。
  • 集成向量数据库(如Chroma)存储长期记忆。使用向量数据库可以实现更复杂的记忆搜索和总结。
  • 添加进度条显示执行步骤。使用tqdm库在循环中添加进度提示,增强用户体验。
  • 单元测试工具函数和Agent循环。编写单元测试确保工具函数正确性,并测试Agent的循环逻辑。
  • 迁移到本地模型(如Ollama + llama.cpp)降低成本和隐私风险。使用本地模型可以完全避免API费用,并保护数据隐私。
  • 实现智能重试机制。使用tenacity库根据错误类型和次数动态调整重试策略。
  • 添加对话历史摘要功能。在每次迭代后,对消息历史进行总结,保持上下文窗口在可控范围内。
  • 支持自定义系统提示。通过__init__方法添加system_prompt参数,并将其添加到messages列表中。
  • 监控Agent性能。通过记录每次调用的token消耗和步数,分析Agent在不同场景下的性能表现。

通过上述建议,您可以构建稳定高效的Agent系统。例如,在处理高频查询时,可以实现一个简单的缓存层,将近期结果存储在内存中,避免重复调用API。对于复杂任务,可以使用分层提示技巧,先让模型规划步骤,然后执行每个步骤。

常见问题FAQ

Q1: 如何处理API密钥泄露风险?
A: 使用环境变量或python-dotenv加载密钥,避免硬编码。定期轮换密钥,并使用密钥管理系统如HashiCorp Vault管理敏感信息。

Q2: Agent循环中如何避免无限循环?
A: 通过设置max_steps参数,并在每次迭代后检查assistant_message.content字段决定是否停止。还可以添加tool_call_count限制以防止重复调用同一工具。

Q3: 如何处理工具函数抛出异常?
A: 在工具函数中添加try-except块,将错误信息返回给模型。或者在Agent的run方法中捕获异常并提供友好错误提示。

Q4: 为什么模型有时不调用工具?
A: 可能因为系统提示不够明确,或者模型在某些场景下更倾向于直接回答。可以通过调整tool_choice参数为"none"强制模型调用工具,或者优化提示词引导模型决策。

Q5: 如何支持多轮对话?
A: 保留完整的消息历史,并在每次运行时传递给call_llmrun方法。可以通过self.memory属性管理对话上下文。

Q6: 如何优化Agent的响应速度?
A: 使用异步编程模型替代同步调用,减少等待时间。也可以通过缓存工具结果或使用更高效的模型(如GPT-4o-mini)提升速度。

总结与展望

本文从零介绍了AI Agent的Python实现,涵盖大模型调用、工具调用和任务自动执行的核心原理。我们构建了一个可直接运行的天气与计算Agent,展示了完整闭环。代码示例基于纯Python和OpenAI API,可轻松扩展。

通过本文,读者可以深入理解AI Agent的原理和实现细节,包括消息历史的构建、工具调用的JSON Schema解析、ReAct循环的各个阶段,以及内存管理的最佳实践。这些知识构成了构建生产级Agent的基础。

未来展望包括:

  • 多Agent协作系统(如CrewAI框架)。多个Agent可以协同完成更复杂的任务,例如一个Agent负责数据收集,另一个负责分析,第三个负责生成报告。
  • 增强型Agent(如Plan-and-Execute或Reflexion)。这些框架在ReAct基础上增加了规划和反思步骤,进一步提升了Agent的智能水平。
  • 集成实时数据流和边缘计算。通过WebSocket或MQTT协议连接实时数据源,实现更动态的任务执行。
  • 开源替代方案,使用Llama3、Mistral等模型本地部署。通过Ollama或llama.cpp等工具,可以在本地运行开源模型,降低成本并保护隐私。

通过本文,读者可掌握从原理到代码的完整路径。

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

请添加图片描述

更多硬核网安与AI工具包,请扫码获取完整源码!
希望您的AI Agent开发<|eos|>

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐