Python多工具AI Agent实战:从零搭建智能任务调度与自动执行系统

2026-05-23 0 907

在大模型技术迅猛发展的当下,AI Agent已成为连接语言模型与现实世界的关键桥梁。本文将手把手带你使用纯Python构建一个能够自主决策、调度多种工具并完成复杂任务的智能Agent系统。我们不会依赖重型框架,而是从底层原理出发,让你透彻理解Agent的核心工作机制。

一、什么是多工具AI Agent

多工具AI Agent是一个智能体程序,它能够接收用户的自然语言指令,自主分析任务需求,从可用的工具集中选择合适的工具(如搜索引擎、计算器、文件读写器、API调用器等),按正确的顺序调用它们,并将各工具返回的结果整合成最终答案。与传统的”一问一答”模式不同,Agent具备规划能力工具选择能力反思纠错能力

举个实际场景:用户问”帮我查一下旧金山今天的天气,然后换算成摄氏度,如果温度高于25度就帮我生成一封提醒带防晒霜的邮件草稿”。这个任务涉及天气查询、温度转换、条件判断和邮件生成四个环节,单个LLM调用无法完成,需要一个Agent来协调多个工具协同工作。

二、系统架构设计

我们的Agent系统采用模块化架构,核心包含以下组件:

  • LLM推理引擎:负责理解用户意图、生成工具调用计划和整合最终回答
  • 工具注册中心:管理所有可用工具的定义、参数规范和调用接口
  • 任务规划器:将复杂任务分解为可执行的步骤序列
  • 执行引擎:按计划依次调用工具,处理中间结果和异常
  • 记忆模块:保存对话上下文和中间执行状态

下面是整体架构的流程示意:用户输入经过LLM推理引擎解析后,任务规划器生成执行计划,执行引擎从工具注册中心获取对应工具并调用,结果经LLM整合后返回给用户。如果某一步执行失败,Agent会自动尝试替代方案或调整计划。

三、完整代码实现

我们将使用Python从零实现这个Agent。这里以OpenAI兼容接口作为LLM后端(你也可以替换为本地模型如Ollama),并集成四个实用工具:网页搜索模拟器、数学计算器、日期时间工具和文件操作工具。

3.1 工具定义与注册中心

首先定义一个统一的工具接口,让所有工具遵循相同的调用规范:

import json
import math
from datetime import datetime, timedelta
from typing import Any, Callable
import re

class Tool:
    """工具基类,定义统一的调用接口"""
    def __init__(self, name: str, description: str, func: Callable, parameters: dict):
        self.name = name
        self.description = description
        self.func = func
        self.parameters = parameters  # JSON Schema格式的参数定义

    def execute(self, **kwargs) -> str:
        """执行工具并返回字符串结果"""
        try:
            result = self.func(**kwargs)
            return str(result)
        except Exception as e:
            return f"工具执行出错: {str(e)}"

    def to_openai_format(self) -> dict:
        """转换为OpenAI函数调用格式"""
        return {
            "type": "function",
            "function": {
                "name": self.name,
                "description": self.description,
                "parameters": self.parameters
            }
        }

3.2 实现四个实用工具

下面是四个具体工具的实现,每个工具都有明确的职责和参数定义:

# 工具1: 数学计算器
def calculator_func(expression: str) -> str:
    """安全地计算数学表达式"""
    allowed_names = {"abs": abs, "round": round, "max": max, "min": min,
                     "pow": pow, "sqrt": math.sqrt, "sin": math.sin,
                     "cos": math.cos, "pi": math.pi, "e": math.e}
    # 只允许安全的数学函数和数字
    sanitized = expression.replace(" ", "")
    if not re.match(r'^[d+-*/().,%sw]+$', sanitized):
        return "错误: 表达式包含不允许的字符"
    try:
        result = eval(sanitized, {"__builtins__": {}}, allowed_names)
        return f"计算结果: {result}"
    except Exception as e:
        return f"计算错误: {str(e)}"

calculator_tool = Tool(
    name="calculator",
    description="执行数学计算,支持加减乘除、三角函数、开方等运算",
    func=calculator_func,
    parameters={
        "type": "object",
        "properties": {
            "expression": {
                "type": "string",
                "description": "数学表达式,例如: '2+3*4', 'sqrt(144)', 'sin(pi/2)'"
            }
        },
        "required": ["expression"]
    }
)

# 工具2: 日期时间工具
def datetime_func(operation: str, date_str: str = "", days_offset: int = 0) -> str:
    """日期时间相关操作"""
    now = datetime.now()
    if operation == "get_current":
        return f"当前日期时间: {now.strftime('%Y-%m-%d %H:%M:%S')}, 星期{['一','二','三','四','五','六','日'][now.weekday()]}"
    elif operation == "add_days":
        target_date = now + timedelta(days=days_offset)
        return f"{days_offset}天后的日期: {target_date.strftime('%Y-%m-%d')}"
    elif operation == "diff_days":
        if date_str:
            target = datetime.strptime(date_str, "%Y-%m-%d")
            diff = (target - now).days
            return f"距离{date_str}还有{diff}天"
    return "未知操作"

datetime_tool = Tool(
    name="datetime_util",
    description="获取当前时间、计算日期差值、推算未来日期",
    func=datetime_func,
    parameters={
        "type": "object",
        "properties": {
            "operation": {
                "type": "string",
                "enum": ["get_current", "add_days", "diff_days"],
                "description": "操作类型"
            },
            "date_str": {"type": "string", "description": "目标日期,格式YYYY-MM-DD"},
            "days_offset": {"type": "integer", "description": "天数偏移量"}
        },
        "required": ["operation"]
    }
)

# 工具3: 模拟网页搜索
def web_search_func(query: str, num_results: int = 3) -> str:
    """模拟搜索引擎返回结果(实际项目中可接入真实搜索API)"""
    knowledge_base = {
        "旧金山天气": "旧金山今日多云转晴,气温18-28摄氏度,湿度65%,风力3级",
        "python最新版本": "Python最新稳定版本为3.12.4,于2024年6月发布,主要改进了性能和多线程支持",
        "langchain": "LangChain是一个用于构建LLM应用的开源框架,支持Python和JavaScript",
        "openai": "OpenAI是一家AI研究公司,开发了GPT系列大语言模型和DALL-E图像生成模型",
    }
    results = []
    for key, value in knowledge_base.items():
        if query.lower() in key.lower() or any(w in key.lower() for w in query.lower().split()):
            results.append(f"📎 {key}: {value}")
    if not results:
        results.append(f"🔍 关于'{query}'的搜索结果: 未找到精确匹配,建议尝试更具体的关键词。")
    return "n".join(results[:num_results])

search_tool = Tool(
    name="web_search",
    description="搜索互联网获取信息,适合查找事实性知识、实时数据等",
    func=web_search_func,
    parameters={
        "type": "object",
        "properties": {
            "query": {"type": "string", "description": "搜索关键词"},
            "num_results": {"type": "integer", "description": "返回结果数量,默认3"}
        },
        "required": ["query"]
    }
)

# 工具4: 文件读写工具
def file_ops_func(operation: str, filepath: str, content: str = "") -> str:
    """安全的文件读写操作"""
    import os
    safe_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "agent_workspace")
    os.makedirs(safe_dir, exist_ok=True)
    full_path = os.path.join(safe_dir, os.path.basename(filepath))
    if operation == "write":
        with open(full_path, "w", encoding="utf-8") as f:
            f.write(content)
        return f"文件已写入: {full_path} (大小: {len(content)}字符)"
    elif operation == "read":
        if os.path.exists(full_path):
            with open(full_path, "r", encoding="utf-8") as f:
                data = f.read()
            return f"文件内容:n{data[:2000]}" if len(data) > 2000 else f"文件内容:n{data}"
        return f"文件不存在: {full_path}"
    return "未知操作"

file_tool = Tool(
    name="file_operations",
    description="读写文件,支持创建新文件和读取已有文件",
    func=file_ops_func,
    parameters={
        "type": "object",
        "properties": {
            "operation": {"type": "string", "enum": ["read", "write"], "description": "操作类型"},
            "filepath": {"type": "string", "description": "文件名或路径"},
            "content": {"type": "string", "description": "要写入的内容(仅write操作需要)"}
        },
        "required": ["operation", "filepath"]
    }
)

# 工具注册中心
TOOL_REGISTRY = {
    "calculator": calculator_tool,
    "datetime_util": datetime_tool,
    "web_search": search_tool,
    "file_operations": file_tool
}

3.3 Agent核心引擎

这是Agent的大脑,负责与LLM交互、解析工具调用请求并管理整个执行循环:

import os
from openai import OpenAI

class MultiToolAgent:
    """多工具AI Agent核心引擎"""

    def __init__(self, api_key: str = None, model: str = "gpt-3.5-turbo"):
        self.client = OpenAI(
            api_key=api_key or os.getenv("OPENAI_API_KEY"),
            base_url=os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
        )
        self.model = model
        self.tools = TOOL_REGISTRY
        self.conversation_history = []
        self.max_iterations = 8  # 防止无限循环

        # 系统提示词,定义Agent的行为规范
        self.system_prompt = """你是一个智能AI助手,可以调用多种工具来完成任务。
请遵循以下原则:
1. 仔细分析用户需求,确定需要哪些工具
2. 如果任务复杂,将其分解为多个步骤
3. 每次只调用一个工具,等待结果后再决定下一步
4. 如果工具返回错误,尝试其他方法或向用户说明
5. 最终用自然语言将结果清晰地呈现给用户
6. 在不需要调用工具时,直接给出你的回答
7. 在回答中引用工具返回的具体数据,让用户知道信息来源可靠"""

    def _build_messages(self):
        """构建发送给LLM的消息列表"""
        messages = [{"role": "system", "content": self.system_prompt}]
        messages.extend(self.conversation_history[-20:])  # 保留最近20条消息
        return messages

    def _execute_tool_call(self, tool_name: str, arguments: dict) -> str:
        """执行指定的工具调用"""
        if tool_name not in self.tools:
            return f"错误: 未知工具 '{tool_name}',可用工具: {list(self.tools.keys())}"
        tool = self.tools[tool_name]
        print(f"  🔧 调用工具: {tool_name} | 参数: {arguments}")
        result = tool.execute(**arguments)
        print(f"  ✅ 工具返回: {result[:150]}...")
        return result

    def run(self, user_query: str) -> str:
        """运行Agent处理用户查询"""
        print(f"n{'='*60}")
        print(f"🚀 Agent启动 | 用户查询: {user_query}")
        print(f"{'='*60}")

        self.conversation_history.append({"role": "user", "content": user_query})
        iteration = 0

        while iteration < self.max_iterations:
            iteration += 1
            print(f"n📍 第{iteration}轮推理...")

            try:
                response = self.client.chat.completions.create(
                    model=self.model,
                    messages=self._build_messages(),
                    tools=[t.to_openai_format() for t in self.tools.values()],
                    tool_choice="auto",
                    temperature=0.3
                )
            except Exception as e:
                return f"LLM调用失败: {str(e)}"

            assistant_message = response.choices[0].message

            # 检查是否需要调用工具
            if assistant_message.tool_calls:
                for tool_call in assistant_message.tool_calls:
                    tool_name = tool_call.function.name
                    arguments = json.loads(tool_call.function.arguments)

                    # 将Assistant的工具调用请求加入历史
                    self.conversation_history.append({
                        "role": "assistant",
                        "content": None,
                        "tool_calls": [{
                            "id": tool_call.id,
                            "type": "function",
                            "function": {
                                "name": tool_name,
                                "arguments": tool_call.function.arguments
                            }
                        }]
                    })

                    # 执行工具
                    tool_result = self._execute_tool_call(tool_name, arguments)

                    # 将工具结果加入历史
                    self.conversation_history.append({
                        "role": "tool",
                        "tool_call_id": tool_call.id,
                        "content": tool_result
                    })
            else:
                # 没有工具调用,说明任务完成
                final_answer = assistant_message.content or "Agent已完成任务,但未生成文本回复。"
                self.conversation_history.append({
                    "role": "assistant",
                    "content": final_answer
                })
                print(f"n🎯 最终回答:n{final_answer}")
                return final_answer

        return "达到最大迭代次数,Agent未能完成所有任务。请尝试简化您的请求。"

    def reset(self):
        """重置对话历史"""
        self.conversation_history = []
        print("🔄 Agent对话历史已重置")

3.4 Agent运行示例

现在让我们实际运行这个Agent,看看它如何处理需要多个工具协作的复杂任务:

# 初始化Agent
agent = MultiToolAgent(model="gpt-3.5-turbo")

# 示例1: 需要搜索+计算器的复合任务
response1 = agent.run("帮我查一下Python最新版本的发布时间,然后计算从那时到今天过了多少天")
print(f"n📋 任务1完成: {response1[:300]}")

# 重置对话
agent.reset()

# 示例2: 需要日期工具+文件操作
response2 = agent.run("获取当前日期时间,然后将其保存到名为current_time.txt的文件中")
print(f"n📋 任务2完成: {response2[:300]}")

# 重置对话
agent.reset()

# 示例3: 复杂多步骤任务
response3 = agent.run(
    "先搜索旧金山的天气信息,然后计算sqrt(2024)的值,"
    "最后把这两个结果汇总写入一个叫summary.txt的文件"
)
print(f"n📋 任务3完成: {response3[:300]}")

四、深入理解Agent的执行循环

Agent的核心是一个ReAct(推理+行动)循环。每一轮迭代中,LLM会收到完整的对话历史(包括之前的工具调用和结果),然后做出决策:是继续调用工具,还是给出最终回答。这个设计有几个关键优势:

  1. 上下文感知:LLM能看到所有已执行步骤的结果,避免重复操作
  2. 错误恢复:如果某个工具返回错误,LLM可以尝试替代方案
  3. 动态规划:根据中间结果灵活调整后续步骤,而非死板地执行预设流程
  4. 可追踪性:所有工具调用都有记录,便于调试和审计

执行流程可以概括为:用户输入 → LLM分析 → 判断是否需要工具 → [是] 调用工具 → 获取结果 → 反馈给LLM → 继续判断 → [否] 生成最终回答。这个循环会持续到LLM认为任务完成或达到最大迭代次数。

五、生产环境优化建议

上述实现是一个可工作的原型,但要投入生产环境,还需要考虑以下优化:

  • 异步工具调用:当多个工具调用之间没有依赖关系时,可以并行执行以提升效率。使用asyncioasyncio.gather实现并发调用。
  • 工具结果缓存:对相同参数的调用结果进行缓存,避免重复请求外部API(尤其是付费API)。
  • 流式输出:使用SSE(Server-Sent Events)将Agent的思考和执行过程实时推送给前端,改善用户体验。
  • 安全沙箱:代码执行类工具应在隔离的Docker容器中运行,防止恶意代码影响宿主机。
  • 工具权限分级:根据用户角色限制可调用的工具集,敏感操作(如数据库写入)需要额外确认。
  • 监控与日志:记录每次工具调用的耗时、成功率和错误信息,建立告警机制。
  • 成本控制:设置每日Token消耗上限,对长对话自动压缩历史消息以节约成本。

六、扩展:接入真实外部服务

将模拟工具替换为真实服务非常简单,只需修改工具的执行函数。例如接入真实的搜索引擎API:

# 接入SerpAPI实现真实网页搜索
def real_web_search(query: str, num_results: int = 3) -> str:
    import requests
    api_key = os.getenv("SERPAPI_KEY")
    url = "https://serpapi.com/search"
    params = {"q": query, "api_key": api_key, "num": num_results, "engine": "google"}
    resp = requests.get(url, params=params)
    data = resp.json()
    results = []
    for item in data.get("organic_results", [])[:num_results]:
        results.append(f"📎 {item['title']}: {item['snippet']}n   🔗 {item['link']}")
    return "nn".join(results) if results else "未找到相关结果"

# 直接替换工具定义中的func即可
search_tool_v2 = Tool(
    name="web_search",
    description="使用Google搜索引擎搜索互联网信息",
    func=real_web_search,
    parameters={...}  # 参数定义保持不变
)

同样的模式适用于接入数据库查询、邮件发送、Slack通知、GitHub操作等任何外部服务。这种统一接口+插件化的设计让Agent的能力可以无限扩展。

七、总结与展望

本文我们从零搭建了一个完整的多工具AI Agent系统,核心代码不到200行,但已经具备了处理复杂多步骤任务的能力。回顾关键要点:

  • ✅ 统一的工具接口设计是Agent可扩展性的基石
  • ✅ ReAct循环让LLM能够在推理和行动之间灵活切换
  • ✅ 对话历史管理确保Agent拥有完整的上下文感知能力
  • ✅ 模块化架构使得替换LLM后端或添加新工具变得非常简单

随着大模型能力的持续提升,AI Agent正在从概念验证走向生产应用。掌握本文介绍的底层原理和实现模式,你将能够构建出适应各种业务场景的智能自动化系统。下一步可以探索的方向包括:多Agent协作(使用CrewAI或AutoGen)、Agent的记忆增强(向量数据库+RAG)、以及Agent的自我反思与学习机制。

动手实践是最好的学习方式,建议你复制本文代码,替换为自己业务场景中的工具,亲身感受AI Agent带来的效率变革。

Python多工具AI Agent实战:从零搭建智能任务调度与自动执行系统
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 python Python多工具AI Agent实战:从零搭建智能任务调度与自动执行系统 https://www.taomawang.com/server/python/1837.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务