构建你自己的代码解释器:使用o3-mini动态生成与执行工具

本教程面向有编程基础的开发者,深入讲解如何构建自定义代码解释器,利用o3-mini模型动态生成并执行Python代码。通过Docker隔离环境、面向对象的代理设计,实现安全、灵活的数据分析。教程涵盖环境搭建、代理定义、编排流程,并分析核心原理与架构,提供常见错误排查与生产优化建议。

技术背景

传统工具调用需要预定义函数,限制了LLM处理新问题的能力。动态工具生成允许模型在运行时创建代码,适用于数据分析、自动化等场景。o3-mini模型在STEM任务上表现优异,且成本低。构建自定义解释器可解决内置解释器的语言限制、成本、集成等问题。

核心原理

核心原理是让LLM(o3-mini)根据用户问题生成Python代码,并在隔离的Docker容器中执行。模型通过推理生成代码,无需显式思维链。代码执行环境限制网络和文件系统访问,确保安全。代理通过工具接口调用执行,实现动态工具调用。

架构分析

系统由三个组件构成:Docker容器环境、两个代理(FileAccessAgent和PythonExecAgent)、编排循环。核心类包括BaseAgent、ChatMessages、ToolManager、ToolInterface,采用面向对象设计,确保接口一致性和可扩展性。FileAccessAgent负责读取文件,PythonExecAgent负责生成并执行代码,两者分离以隔离权限。

完整实战教程

构建你自己的代码解释器:使用o3-mini动态生成与执行工具

在赋予LLM代理与外部世界或其他代理交互能力的核心是“工具(或函数)调用”,即LLM可以调用带有参数的函数(代码块)。通常,这些函数及其预期输入和输出由开发者预先定义。然而,在本教程中,我们探索一种更灵活的范式——使用LLM模型(此处为o3-mini)动态生成工具,并能够使用代码解释器执行该工具。

使用代码解释器动态生成工具调用

动态生成工具是由LLM本身在运行时根据用户提示创建的函数或代码块。这意味着你不必在代码库中预定义每种可能的情况——从而实现更开放、更具创造性和适应性的问题解决。

动态生成工具调用更进一步,赋予LLM即时生成工具和执行代码块的能力。这种动态方法对于涉及以下任务特别有用:

使用o3-mini进行动态工具生成

o3-mini模型于2025年1月31日发布,具有卓越的STEM能力——尤其在科学、数学和编码方面表现出色——同时保持了较小模型的低成本和低延迟。在本教程中,我们将展示o3-mini生成Python代码以解释数据和提取见解的能力。

推理模型特别擅长生成动态工具来分析数据,因为它们可以自行推理,无需显式的思维链提示。事实上,提供显式的思维链指令可能会干扰模型的内部推理,导致次优结果。你可以在此了解更多关于o3-mini的信息。

为什么构建你自己的代码解释器

许多API提供商(如OpenAI的Assistants API)提供内置的代码解释器功能。这些内置的代码解释器可能非常强大,但在某些情况下,开发者可能需要创建自己的自定义代码解释器。例如:

  1. 语言或库支持:内置解释器可能不支持你的任务所需的特定编程语言(例如C++、Java等)或库。
  2. 任务兼容性:你的用例可能与提供商的内置解决方案不兼容。
  3. 模型约束:你可能需要提供商解释器不支持的语言模型。
  4. 成本考虑:代码执行或模型使用的成本结构可能不符合你的预算或约束。
  5. 文件大小:输入数据的文件大小过大或不受提供商解释器支持。
  6. 与内部系统集成:提供商的解释器可能无法与你的内部系统集成。

你将学到什么

通过遵循本教程,你将学习如何:

你将学习如何从零开始构建自定义代码解释器工具,利用 LLM 的强大能力生成复杂代码,并在隔离环境中安全执行这些代码——所有这一切都是为了让你的人工智能应用更加灵活、强大且成本效益更高。

示例场景

我们将使用 Key Factors Traffic Accidents 提供的示例数据来回答一系列问题。这些问题不需要预先定义,我们将赋予 LLM 生成代码以回答此类问题的能力。

示例问题可能包括:

使用传统的预定义工具调用方法,开发人员需要为每个问题预先定义函数。这限制了 LLM 回答预定义函数集之外任何其他问题的能力。我们通过使用动态工具调用方法克服了这一限制,其中 LLM 生成代码并使用代码解释器工具执行代码。

概述

让我们深入了解构建这个具有动态生成工具调用的 Agentic 应用程序的步骤。该应用程序包含三个组件:

步骤 1:设置隔离的代码执行容器环境

我们需要一个安全的环境,让 LLM 生成的函数调用可以在其中执行。我们希望避免直接在主机上运行 LLM 生成的代码,因此将创建一个资源访问受限(例如,无网络访问)的 Docker 容器环境。默认情况下,Docker 容器无法访问主机的文件系统,这有助于确保 LLM 生成的任何代码都被限制在容器内。

##### ⚠️ 警告:为 LLM 生成的代码实施强有力的防护措施

LLM 可能生成具有意外后果的有害代码。作为最佳实践,请隔离代码执行环境,仅按任务需要授予必要的资源访问权限。避免在主机或笔记本电脑上运行 LLM 生成的代码。

步骤 2:定义并测试代理

“什么是代理?”在本 Cookbook 的上下文中,代理是:

  1. 一组供 LLM 遵循的指令,即开发者提示词
  2. 一个 LLM 模型,以及通过 API 调用模型的能力
  3. 对函数的工具调用访问权限,以及执行该函数的能力

我们将定义两个代理:

  1. FileAccessAgent:此代理将读取文件,并向 PythonCodeExecAgent 提供上下文。
  2. PythonCodeExecAgent:此代理将生成 Python 代码来回答用户的问题,并在 Docker 容器中执行代码。

步骤 3:设置代理编排以运行应用程序

根据应用程序需求,有多种编排代理的方式。在此示例中,我们将使用简单的编排,用户提供任务,代理按顺序调用以完成任务。

整体编排如下所示:

让我们开始吧

前提条件

在开始之前,请确保您的宿主机上已安装并配置以下内容:

  1. Docker:已在本地机器上安装并运行。您可以了解更多关于 Docker 的信息并从此处安装。
  2. Python:已在本地机器上安装。您可以了解更多关于 Python 的信息并从此处安装。
  3. OpenAI API 密钥:已在本地机器上设置为环境变量,或位于根目录的 .env 文件中。您可以了解更多关于 OpenAI API 密钥的信息并从此处设置。

步骤 1:设置隔离的代码执行环境

让我们定义一个 Docker 化容器环境,用于执行我们的代码。我在 resources/docker 目录下定义了 dockerfile,它将用于创建具有以下规格的容器环境:

Docker 镜像创建过程中包含的 requirements.txt 包含了我们的 LLM 生成的代码可能需要的所有潜在包,以完成其任务。由于我们将限制容器访问网络,因此需要预安装任务所需的包。出于安全目的,我们的 LLM 将不允许安装任何额外的包。

您可以创建自己的 Docker 镜像,包含所需的语言(如 Python 3.10)并预安装任务所需的包,或者创建自定义 Docker 镜像,包含特定语言(如 Java、C++ 等)和任务所需的包。

让我们用以下命令构建 Docker 镜像。为简洁起见,我将输出重定向到 grep 以获取成功消息,并在构建失败时打印一条消息。

!docker build -t python_sandbox:latest ./resources/docker 2>&1 | grep -E "View build details|ERROR" || echo "Build failed."

让我们以受限模式运行容器。容器将在后台运行。这是我们为容器定义安全策略的机会。良好的实践是仅允许容器具备完成任务所需的最基本功能。默认情况下,容器无法从容器内部访问主机文件系统。我们还要限制其对网络的访问,使其无法访问互联网或任何其他网络资源。

# Run the container in restricted mode. The container will run in the background.
!docker run -d --name sandbox --network none --cap-drop all --pids-limit 64 --tmpfs /tmp:rw,size=64M   python_sandbox:latest sleep infinity

让我们使用 docker ps 确认容器正在运行,该命令应列出我们的容器。

!docker ps

步骤 2:定义并测试智能体

为了我们的目的,我们将定义两个智能体。

  1. 智能体 1:文件访问智能体(使用预定义工具调用)
  1. 智能体 2:Python 代码生成与执行智能体(使用动态生成的工具调用和代码执行)

这种将文件访问(智能体 1)与代码生成和执行(智能体 2)分离的做法至关重要,可以防止 LLM 直接访问或修改宿主机。

将智能体 1 限制为静态工具调用,因为它可以访问宿主机的文件系统。

| 智能体 | 工具调用类型 | 访问宿主机文件系统 | 访问 Docker 容器文件系统 | 访问代码解释器 |

|-------|--------------|--------------------|--------------------------|----------------|

| 智能体 1:文件访问 | 预定义工具 | 是 | 是 | 否 |

| 智能体 2:Python 代码生成与执行 | 动态生成工具 | 否 | 是 | 是 |

为了保持 Agents 和 Tools 的组织性,我们定义了一组核心类,用于使用面向对象编程原则创建两个代理,以保证一致性。

这些类定义在 object_oriented_agents/core_classes 目录中。

核心类的 UML 类图

以下类图展示了核心类之间的关系。该 UML(统一建模语言)图是使用 Mermaid 生成的。

定义 Agent 1:带有 FileAccessTool 的 FileAccessAgent

让我们从定义继承自 ToolInterface 类的 FileAccessTool 开始。FileAccessTool 工具定义在 resources/registry/tools 目录下的 file_access_tool.py 文件中。

现在,让我们定义FileAccessAgent,它继承自BaseAgent类,并将FileAccessTool绑定到该代理。FileAccessAgent定义在resources/registry/agents目录下的file_access_agent.py文件中。FileAccessAgent是:

定义 Agent 2:带 PythonExecTool 的 PythonExecAgent

类似地,PythonExecTool 继承自 ToolInterface 类,并实现了 get_definition 和 run 方法。get_definition 方法返回符合 OpenAI 函数调用 API 格式的工具定义。run 方法在 Docker 容器中执行 Python 代码并返回输出。该工具定义在 resources/registry/tools 目录下的 python_code_interpreter_tool.py 文件中。

同样,PythonExecAgent 是 BaseAgent 类的具体实现。它定义在 resources/registry/agents 目录下的 python_code_exec_agent.py 文件中。PythonExecAgent 具有以下特点:

您可以在此处了解更多关于 reasoning_effort 参数的信息:这里。

步骤 3:设置代理编排以运行应用程序

定义好代理后,现在我们可以定义运行应用程序的编排循环。该循环将提示用户输入问题或任务,然后调用 FileAccessAgent 读取文件并将上下文提供给 PythonExecAgent。PythonExecAgent 将生成 Python 代码来回答用户的问题,并在 Docker 容器中执行代码。代码执行的输出将显示给用户。

用户可以输入 'exit' 来停止应用程序。我们的问题是:哪些因素对事故频率的影响最大? 请注意,我们并未预先定义回答此问题的函数。

# Import the agents from registry/agents

from resources.registry.agents.file_access_agent import FileAccessAgent
from resources.registry.agents.python_code_exec_agent import PythonExecAgent


prompt = """Use the file traffic_accidents.csv for your analysis. The column names are:
Variable    Description
accidents    Number of recorded accidents, as a positive integer.
traffic_fine_amount    Traffic fine amount, expressed in thousands of USD.
traffic_density    Traffic density index, scale from 0 (low) to 10 (high).
traffic_lights    Proportion of traffic lights in the area (0 to 1).
pavement_quality    Pavement quality, scale from 0 (very poor) to 5 (excellent).
urban_area    Urban area (1) or rural area (0), as an integer.
average_speed    Average speed of vehicles in km/h.
rain_intensity    Rain intensity, scale from 0 (no rain) to 3 (heavy rain).
vehicle_count    Estimated number of vehicles, in thousands, as an integer.
time_of_day    Time of day in 24-hour format (0 to 24).
accidents    traffic_fine_amount
"""


print("Setup: ")
print(prompt)

print("Setting up the agents... ")

# Instantiate the agents with the default constructor defined values
# Developer may override the default values - prompt, model, logger, and language model interface if needed

# This agent use gpt-4o by default
file_ingestion_agent = FileAccessAgent()

# Let's make sure agent uses o3-mini model and set the reasoning_effort to high
data_analysis_agent = PythonExecAgent(model_name='o3-mini', reasoning_effort='high')

print("Understanding the contents of the file...")
# Give a task to the file ingestion agent to read the file and provide the context to the data analysis agent 
file_ingestion_agent_output = file_ingestion_agent.task(prompt)

# Add the file content as context to the data analysis agent
# The context is added to the agent's tool manager so that the tool manager can use the context to generate the code 

data_analysis_agent.add_context(prompt)
data_analysis_agent.add_context(file_ingestion_agent_output)

while True:

    print("Type your question related to the data in the file. Type 'exit' to exit.")
    user_input = input("Type your question.")

    if user_input == "exit":
        print("Exiting the application.")
        break

    print(f"User question: {user_input}")

    print("Generating dynamic tools and using code interpreter...")
    data_analysis_agent_output = data_analysis_agent.task(user_input)

    print("Output...")
    print(data_analysis_agent_output)

在此示例中,o3-mini 根据用户的问题动态生成了一个工具(Python 脚本)来分析数据。请注意,o3-mini 使用了多种方法(如相关性分析、线性回归和随机森林模型)来检查问题。这种方法突出了以下几点:

reasoning_effort:模型执行的推理深度(例如,在此案例中为方法的数量)通常随着参数从低、中到高而增加。您可以尝试不同级别的推理努力来查看差异。

动态生成的工具调用:用于分析数据的工具(Python 脚本)并非由开发人员手动编写或预先确定。相反,o3-mini 模型在运行时创建了相关的数据探索和相关性分析代码。

隔离的代码执行:为了确保安全性并避免在主机上运行不受信任的代码,Python 脚本通过 execute_python_code 工具在 Docker 容器内执行。该容器具有受限的资源访问权限(例如,无网络和有限的文件系统访问),从而最大限度地降低了任意代码执行带来的潜在风险。

结论

本Cookbook为开发自定义代码解释器提供了指南,以满足特定应用需求,解决了供应商提供的解决方案中的局限性,如语言限制、成本考虑以及需要与不同LLM或模型灵活配合的问题。

管理代理和工具的方法:我们还定义了一组核心类来管理代理和工具。这种方法确保了代理和工具具有一致的接口,并且可以在不同应用中重用。可以创建代理和工具的资源库,例如registry文件夹,来管理这些代理和工具。

o3-mini模型:我们展示了o3-mini模型在运行时根据用户极简提示生成复杂代码来分析数据的能力。然后,o3-mini模型对分析结果进行推理,向用户解释结果。

最后,回顾一下,构建具有动态工具调用的代理应用的三个步骤是:

  1. 设置隔离的代码执行容器环境
  2. 定义并测试代理
  3. 设置代理编排以运行应用

我们讨论了隔离代码执行环境的重要性,以确保安全性,避免在主机上运行不受信任的代码。通过CSV文件的用例,我们演示了如何动态生成工具(Python脚本)来分析数据并回答用户的问题。我们还展示了如何在Docker容器中执行代码并将输出返回给用户。

常见问题

常见错误包括:Docker未启动或镜像构建失败;容器未运行或网络限制导致包缺失;API密钥未设置;模型参数配置不当。排查方法:检查docker ps、构建日志、环境变量;确保容器内预装所需包;调整reasoning_effort参数。生产优化:使用更安全的沙箱(如gVisor)、资源限制、日志监控、缓存机制。

企业应用扩展

可扩展方向:支持更多语言(Java、C++等);集成更多工具(如数据库访问);实现多代理协作;添加代码审查和错误处理;优化模型选择(如gpt-4o-mini);部署为微服务;增加用户界面。

总结

本教程面向有编程基础的开发者,深入讲解如何构建自定义代码解释器,利用o3-mini模型动态生成并执行Python代码。通过Docker隔离环境、面向对象的代理设计,实现安全、灵活的数据分析。教程涵盖环境搭建、代理定义、编排流程,并分析核心原理与架构,提供常见错误排查与生产优化建议。

内容来源与验证

继续系统学习

站内还有更多完整实战教程

加入会员,解锁全部教程、完整代码与持续更新内容。

加入会员
查看全部AI 智能体教程