DLAI-工具执行的代码智能体构建笔记-全-

DLAI 工具执行的代码智能体构建笔记(全)

001:课程介绍 🎯

在本课程中,我们将学习如何构建能够生成并执行代码的智能体,以此作为其完成任务的方式。课程由Teresa Tshkover和Francesco Zubucchini讲授,并与EDB合作开发。

课程核心概念

许多智能体被预先定义了一组函数供其调用。然而,与其仅使用一个预定义的小型函数集,不如让大语言模型或你的智能体编写并执行任意的Python代码。

这样一来,智能体就能访问所有内置的Python函数,甚至可以安装常见的Python包并调用其中的函数。这极大地扩展了智能体能够处理的任务范围。

此外,不同于常见的、一次一个的顺序函数调用方式(这在处理稍复杂的任务时很常见),你的智能体现在可以编写大段代码,并高效地执行一长串操作序列,这也使其能力更强。

安全考量与解决方案

但在本地机器上运行由大语言模型生成的代码可能不安全。代码可能会执行一些预期之外的操作。例如,一个高度自主的编码工具曾删除过我团队成员的重要项目文件;另一个则执行了有缺陷的数据库迁移,删除了生产数据(幸好我们有备份)。

为了安全地运行生成的代码,一个良好的实践是在沙箱环境中运行它,从而使代码执行与主系统隔离。

课程内容概览

在本课程中,我们将首先剖析AI编码智能体的本质,以及它与其他AI智能体的区别。你将学习大语言模型如何逐步推理、决定调用哪个工具、执行代码,并利用结果来规划下一步。

接着,你将构建自己的智能体,使其能够运行Python代码、读写文件,并迭代任务直至达成最终目标。我们还将讨论如何管理智能体交互过程中的上下文窗口。

理解了智能体的思考和行为方式后,你将探索不同的执行环境,例如本地执行、容器和安全的沙箱化微虚拟机,并学习如何根据你的使用场景选择合适的方案。

你将学习如何创建托管沙箱、远程运行不同语言的代码,并与沙箱的文件系统交互,使你的智能体能够使用上传的数据、保存输出,甚至创建和托管小型Web应用程序。

你将构建一个数据分析师智能体,它能够使用pandas处理CSV文件并生成清晰的可视化图表。在最后一课中,我们将创建一个全栈智能体,它能添加多个文件,并在沙箱内构建一个可运行的Next.js Web应用。

为了确保你的智能体即使在大型多文件项目中也能保持专注,我们还将讨论通过运行时符号化来管理长上下文的技术。

总结

本节课我们一起了解了本课程的核心目标:构建能够安全、高效执行代码的智能体。我们探讨了其优势、安全风险以及沙箱环境的重要性,并对后续章节的学习内容有了整体认识。

下一节视频将深入介绍编码智能体的内部工作原理及其构建过程中的一些挑战。让我们开始学习吧。

002:代码智能体内部机制解析 🧠

在本节课中,我们将学习什么是代码智能体,了解它如何通过推理来处理任务,以及它如何使用代码执行和文件系统等工具来完成任务。

概述

代码智能体是一种能够将用户请求转化为实际应用程序的AI系统。它通过编写代码文件、执行代码并即时部署Web应用来实现这一目标。本质上,AI智能体可以被定义为一个大语言模型在一个循环中调用工具,并根据上下文不断迭代。

什么是AI智能体?

AI智能体可以被简单地定义为一个在循环中调用工具的大语言模型。上下文是提供给LLM的任何输入,例如系统指令、工具定义、用户消息等。基于上下文,LLM决定是否以及调用哪个工具。智能体调用并运行该工具,将结果附加到上下文中,然后重复此循环,直到任务完成。

代码智能体的独特需求

上一节我们介绍了通用AI智能体的概念,本节中我们来看看专门用于编码任务的智能体有何特殊之处。

与处理航班预订或研究总结的通用智能体不同,代码智能体(如Gemini、Claude Code或Cursor)专注于编写和运行代码。它们需要执行以下任务:

  • 生成长脚本
  • 调试代码
  • 在项目中编辑数十个文件

为了实现这些功能,代码智能体需要满足几个核心需求:

  1. 受控环境中的代码执行:需要一个安全、隔离的环境来反复迭代和运行代码。
  2. 文件系统访问:能够读取、编辑和写入项目文件。
  3. 长时间运行会话:由于编码任务复杂,通常需要长时间会话来安装依赖、编译、运行测试、修复错误和重试。
  4. 更强的安全性:必须妥善处理不受信任的代码,防止实验破坏你的机器或恶意行为者访问你的系统。

如何选择核心大语言模型?

像所有智能体一样,你的代码智能体将有一个LLM作为核心。问题在于,如何选择合适的大语言模型?以下是几个关键考量因素:

以下是选择大语言模型时需要考虑的三个主要方面:

  • 函数调用支持:模型必须支持函数调用(或工具调用)。这意味着LLM能够接收一组工具定义,并可以自主决定何时以及如何使用它们。
  • 上下文长度:研究表明,大约32K个令牌的上下文长度是智能体能够可靠处理真实大型任务的第一个基准点。这大约相当于40到50页文本,类似于一个中等规模的GitHub仓库。处理像Django或PyTorch这样的大型项目时,模型需要轻松读取和编辑许多文件,这可能会消耗数万个令牌。
  • 评估与基准测试:为了做出正确决策,我们可以用编码特定的基准来评估LLM。最广泛使用的基准之一是SWE-bench。它测试模型能否处理真实的GitHub问题、生成补丁并通过仓库的单元测试。你可以根据你的具体用例来评估模型。

上下文工程的艺术

上下文是LLM看到的一切。对于代码智能体,上下文可能包括:

  • 系统提示词
  • 外部输入(如PR或数据库搜索结果)
  • 用户提示
  • 工具定义和工具执行结果
  • 其他信息(如当前分支、提交、文件摘要、之前的对话、依赖版本或早期运行的短期记忆)

上下文工程是一门艺术,旨在保持上下文简洁的同时,为模型提供完成当前任务最相关的信息。我们需要管理上下文长度,因为对于代码智能体来说,它会迅速膨胀。每一次编辑、运行、调试的循环都会向对话中添加更多文本。工具输出可能非常庞大(例如大型JSON或日志),再加上重复的错误堆栈,结果会导致“上下文稀释”——随着令牌数量增长,模型对重要信息的注意力下降,输出质量也随之降低。

管理上下文膨胀的策略

我们刚刚看到了代码智能体中上下文是如何爆炸式增长的。一个常见的令牌来源是原始输出。以下是几种缓解策略:

以下是几种有效管理上下文长度、防止信息过载的策略:

  • 精简工具输出:保持工具输出小巧、一致且结构化。例如,可以解析API响应,只返回LLM实际需要的数据,而不是整个response.data
  • 裁剪日志:日志可能变得非常庞大。我们可以将其裁剪到最后几行,并丢弃重复的消息,以保持上下文简洁。
  • 分页与过滤文件列表:代码智能体需要频繁扫描文件系统(例如,基于某种模式列出文件)。在大型项目中,一个目录可能包含数百个文件,全部返回会淹没上下文。常见的策略是对结果进行分页,并赋予模型在需要时请求下一页的能力。我们也可以先进行过滤,例如只列出Python文件或特定目录,让模型只看到相关的内容。

代码智能体的核心工具

你的代码智能体需要特定的工具来完成编码任务,至少包括运行代码和访问文件系统。

关于运行代码,应遵循以下规则:

  • 任何由LLM生成的代码都应被视为不受信任
  • 应尽量避免直接在主机上运行代码操作。
  • 应选择与系统其余部分隔离的更安全环境(如沙箱、容器)。
  • 应强制执行严格的CPU、内存和执行时间限制,防止失控进程导致系统崩溃。
  • 除非任务明确需要,否则应记录并限制网络和文件系统访问。
  • 通过将模型指向我们选择的文本编辑器来保持其专注,否则它可能会开始臆想库名或使用你不希望的工具。

关于文件系统访问:
编码任务需要读取、编辑和写入文件,因此智能体需要访问文件系统。一个良好的实践是设置严格的权限,只允许智能体在特定的工作目录内操作。

为了搜索文件,我们可以为智能体配备基于正则表达式的文件搜索功能。为了在特定文件内搜索,我们可以使用模糊匹配。这样,即使搜索查询没有完全匹配,智能体也能找到相关的内容或模式。

智能体循环与错误处理

AI智能体可以被定义为一个使用工具在循环中执行操作以完成给定任务的LLM。这个循环就是两次用户消息之间发生的所有事情。大多数任务需要多轮推理,因此模型会反复调用工具,有时甚至会重复调用同一个工具。

但我们需要知道何时停止。必须有一个退出条件。智能体并不完美,它们可能会失败,或者在尝试从错误中恢复时失控。它们可能需要人类或另一个LLM来评估结果。你分配给智能体的任务越长、越复杂,它就越容易出错。

系统地跟踪错误至关重要,因为你的智能体可能会永远卡在尝试修复一个错误上。为了防止这种情况,我们可以:

  • 跟踪错误,例如设置一个最大重试计数器。
  • 请求用户提供输入以帮助解决错误。
  • 通过将错误聚类、移除旧的错误信息同时保持上下文清晰来帮助模型。

选择运行环境

你需要为代码智能体选择最佳的运行环境。Cursor使用本地执行,一些智能体使用Docker,而像GitHub Copilot、Perplexity等则使用沙箱。

在决定正确的环境时,我们应该考虑:

  • 安全性:例如攻击面,以及我们是否暴露了操作系统。
  • 开发者体验:我们希望避免代码变通方法,让用户满意。
  • 定制化:我们希望智能体拥有定制的环境,能够使用多种语言、工具链和特定版本的软件包。
  • 可维护性:需要良好的维护工具来减少智能体运行基础设施的停机时间,为最终用户提供可靠性和良好的支持。
  • 性能与扩展性:我们希望环境能够快速启动,并且需要能够大规模地完成所有这些工作,准备好为甚至数百万的最终用户提供服务。

总结

在本节课中,我们一起学习了代码智能体的核心机制。我们了解到,代码智能体是一个在循环中调用工具(特别是代码执行和文件访问)的LLM。我们探讨了如何为其选择合适的LLM模型、管理可能爆炸性增长的上下文、为其配备必要的工具并制定安全规则,以及如何处理循环中的错误和选择适当的运行环境。这些是构建一个实用、可靠代码智能体的基础。

在下一节课中,你将应用所有这些概念来构建你的第一个简单的代码智能体。我们课堂上见。

003:你的第一个代码智能体

在本节课中,我们将从零开始构建你的第一个代码智能体。你将赋予它真实的工具来执行代码、读写文件,并执行多步推理循环。现在,让我们开始编码。

概述

在本节课中,我们将学习如何构建一个基础的代码智能体。这个智能体能够理解用户指令,调用工具(如执行代码、读写文件)来完成任务,并通过循环进行多步推理。我们将从设置环境开始,逐步实现核心功能,最终创建一个可以交互的智能体。

环境设置与代码执行

首先,我们通过以下代码忽略警告。所有密钥已在深度学习AI环境中为你设置好,你无需担心。

import warnings
warnings.filterwarnings('ignore')

在构建我们的第一个智能体之前,让我们先预览一下基于本节课知识,你将在后续课程中构建的一个更复杂的数据分析智能体。这个智能体能够运行和执行代码。我们用一个简单的提示来演示它将能做什么。

# 示例提示:请求智能体创建一个绘图函数
prompt = "Can you create me a function that draws a smiley face and run it?"

在右侧,我们看到上下文在增长。我们使用了与幻灯片中相同的核心架构,高度与令牌大小成比例。智能体运行生成的代码并执行。它正在生成一些使用Matplotlib函数的Python代码。虽然它画的不是完美的笑脸,但结果很有趣。你可以自由地尝试更多提示并与数据世界互动。

构建第一个智能体:语言模型与工具

现在回到我们的第一个智能体。首先,我们需要一个语言模型。在本课程的所有课程中,我们将使用OpenAI的GPT模型。我们通过一个方便的run函数来调用模型。

import openai

def run(client, messages):
    """
    调用语言模型并返回回复。
    :param client: OpenAI客户端实例
    :param messages: 消息列表
    :return: 模型的回复
    """
    response = client.chat.completions.create(
        model="gpt-4",
        messages=messages
    )
    return response.choices[0].message.content

接下来,我们需要给智能体提供工具,即它可以调用的实际函数。这里我们定义一个类型字典来存储结果和可能的错误。

from typing import Dict, Any

def execute_code(code: str) -> Dict[str, Any]:
    """
    执行传入的Python代码字符串。
    :param code: 要执行的Python代码
    :return: 包含结果和错误的字典
    """
    import sys
    from io import StringIO

    old_stdout = sys.stdout
    redirected_output = sys.stdout = StringIO()
    result_dict = {'result': None, 'error': None}

    try:
        # 使用exec执行代码
        exec(code, globals())
        result_dict['result'] = redirected_output.getvalue()
    except Exception as e:
        result_dict['error'] = str(e)
    finally:
        sys.stdout = old_stdout

    return result_dict

我们使用Python内置的exec函数来执行代码,并重定向标准输出以便捕获并返回。让我们通过打印“Hello World”来测试它。

test_result = execute_code("print('Hello World')")
print(test_result)  # 输出: {'result': 'Hello World\n', 'error': None}

我们可以看到结果字典的result键中包含了“Hello World”。

定义工具模式

为了让智能体能够运行这个函数,我们需要将其描述为一个JSON模式。

execute_code_schema = {
    "name": "execute_code",
    "description": "Executes the provided Python code string.",
    "parameters": {
        "type": "object",
        "properties": {
            "code": {
                "type": "string",
                "description": "The Python code to execute."
            }
        },
        "required": ["code"]
    }
}

注意名称是execute_code,参数是一个属性code,类型为字符串。我们可以通过将其添加到工具字典中来注册这个工具。之后创建的每个工具也将放在这里。

tools_registry = {
    "execute_code": {
        "schema": execute_code_schema,
        "function": execute_code
    }
}

工具执行函数

接下来,我们定义一个名为execute_tool的函数。它接收工具名称,在字典中查找工具,传递参数,并返回结果。我们还添加了一些错误处理,以防智能体在解析参数或调用不存在的工具时出错。

def execute_tool(tool_name: str, arguments: Dict) -> Dict[str, Any]:
    """
    执行指定的工具。
    :param tool_name: 工具名称
    :param arguments: 工具参数
    :return: 执行结果字典
    """
    if tool_name not in tools_registry:
        return {"error": f"Tool '{tool_name}' not found."}

    tool_info = tools_registry[tool_name]
    tool_function = tool_info['function']

    try:
        result = tool_function(**arguments)
        return result
    except Exception as e:
        return {"error": f"Error executing tool '{tool_name}': {str(e)}"}

创建智能体函数

现在我们可以创建我们的智能体函数,这是智能体运行的核心。

def coding_agent(client, user_query, system_prompt, tools, max_steps=5):
    """
    代码智能体主函数。
    :param client: OpenAI客户端
    :param user_query: 用户查询
    :param system_prompt: 系统提示
    :param tools: 工具注册表
    :param max_steps: 最大执行步数
    :return: 最终消息列表
    """
    messages = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_query}
    ]
    steps = 0

    while steps < max_steps:
        # 调用语言模型
        response = run(client, messages)
        # 此处假设模型回复包含结构化内容(如函数调用)。
        # 实际实现中,你需要解析模型的响应。
        # 这是一个简化的示例逻辑。
        if "function_call" in response:
            # 解析函数调用并执行
            tool_name = ... # 解析得到工具名
            arguments = ... # 解析得到参数
            tool_result = execute_tool(tool_name, arguments)
            # 将工具执行结果作为消息追加
            messages.append({"role": "tool", "content": str(tool_result)})
        else:
            # 模型直接回复文本,任务可能完成
            print("Agent says:", response)
            break
        steps += 1
    return messages

我们传递OpenAI客户端、用户查询、系统提示、工具注册表和工具链。我们创建消息数组,使用所有参数调用LM函数,并遍历响应。如果类型是消息,意味着模型想对我们说话,我们就打印内容。如果是函数调用类型,我们就获取函数名,进行一些打印,并使用我们的execute_tool函数来获取工具的结果。

测试基础智能体

我们可以通过让智能体运行Python代码来测试它。这里我们有一个特定于任务的助手提示,告诉智能体它是一个Python程序员,并解释了如何使用工具。我们告诉它必须始终使用execute_code工具来运行代码。最后,我们告诉它如何收集用户输入,只需重用Python的input函数。

我们的任务很简单:我们想制作一个程序,询问我们今天喝了多少杯咖啡,然后将其转换为毫克咖啡因。

system_prompt = """
You are a helpful Python programming assistant.
You must always use the `execute_code` tool to run Python code.
To get user input, use the standard `input()` function within your code.
"""

user_query = """
Create a program that asks: 'How many cups of coffee did you have today?'.
Then, convert the number of cups to milligrams of caffeine (assuming 95mg per cup) and print the result.
"""

# 假设 client 已初始化
# messages = coding_agent(client, user_query, system_prompt, tools_registry)

运行后,智能体会生成并执行代码。假设我们输入5杯,它会计算出475毫克咖啡因并打印出来。这是一个完整的循环:智能体接收用户查询,进行推理,决定调用哪个工具,执行工具,并返回结果。

扩展功能:文件读写工具

真正的智能体通常需要使用文件系统来读取、写入和编辑文件。让我们实现两个工具来读写文件。

首先,我们需要read_file的模式。

read_file_schema = {
    "name": "read_file",
    "description": "Reads content from a file.",
    "parameters": {
        "type": "object",
        "properties": {
            "file_path": {
                "type": "string",
                "description": "Path to the file to read."
            },
            "limit": {
                "type": "integer",
                "description": "Maximum number of characters to read. Optional.",
                "default": 1000
            },
            "offset": {
                "type": "integer",
                "description": "Character position to start reading from. Optional.",
                "default": 0
            }
        },
        "required": ["file_path"]
    }
}

名称是read_file,它接收文件路径作为字符串,当然还有限制和偏移量参数,用于控制从文件中读取多少字符以及从哪里开始。这非常重要,因为我们可能处理非常大的文件,让模型能够选择读取多少字符可以保持上下文清洁。

write_file模式非常相似,我们传递一个内容字符串和一个文件路径。

write_file_schema = {
    "name": "write_file",
    "description": "Writes content to a file.",
    "parameters": {
        "type": "object",
        "properties": {
            "file_path": {
                "type": "string",
                "description": "Path to the file to write."
            },
            "content": {
                "type": "string",
                "description": "Content to write to the file."
            }
        },
        "required": ["file_path", "content"]
    }
}

实现文件工具函数

接下来,我们需要实现当智能体想要调用这些工具时实际运行的函数。让我们来实现read_file函数。注意我们创建了一个自定义异常。这很有帮助,因为我们可以避免传递长的错误堆栈跟踪,而只是向模型返回特定的错误反馈。例如,如果文件不存在,我们将引发一个新的工具错误,明确告诉模型文件不存在。

class ToolError(Exception):
    pass

def read_file(file_path: str, limit: int = 1000, offset: int = 0) -> Dict[str, Any]:
    """
    读取文件内容。
    """
    try:
        with open(file_path, 'r') as f:
            f.seek(offset)
            content = f.read(limit)
        return {"result": content, "error": None}
    except FileNotFoundError:
        raise ToolError(f"File '{file_path}' does not exist.")
    except Exception as e:
        raise ToolError(f"Error reading file: {str(e)}")

def write_file(file_path: str, content: str) -> Dict[str, Any]:
    """
    将内容写入文件。
    """
    try:
        with open(file_path, 'w') as f:
            bytes_written = f.write(content)
        return {"result": f"Successfully wrote {bytes_written} bytes to {file_path}", "error": None}
    except Exception as e:
        raise ToolError(f"Error writing file: {str(e)}")

最后,我们可以通过添加这两个新工具来更新我们的工具注册表。

tools_registry.update({
    "read_file": {
        "schema": read_file_schema,
        "function": read_file
    },
    "write_file": {
        "schema": write_file_schema,
        "function": write_file
    }
})

测试文件工具

让我们通过创建一个工作目录并给出新的用户查询来测试它。在查询中,我们要求智能体在工作目录中创建一个空的test.txt文件。

user_query = """
Create an empty text file named 'test.txt' in the current working directory.
"""
# 注意,我们将新的工具模式传递给智能体函数。
# messages = coding_agent(client, user_query, system_prompt, tools_registry)

我们可以看到智能体使用了write_file工具,并向该文件写入了0字节。现在让我们测试错误处理。我们可以要求智能体读取一个不存在的文件,看看会发生什么。

user_query = """
Read the content of a file named 'non_existent.txt'.
"""
# messages = coding_agent(client, user_query, system_prompt, tools_registry)

我们可以看到它再次使用了read_file工具,但这次我们得到了一个错误字典。这个结构化的错误信息将帮助智能体恢复并重试。

让我们尝试用稍微复杂一点的任务来测试智能体。这里我们希望智能体创建一个内容为“file1”的文件,然后将其读回给我们。

user_query = """
Create a file named 'file_one.txt' with the content 'file1' inside it.
Then, read the file back and tell me its content.
"""
# messages = coding_agent(client, user_query, system_prompt, tools_registry)

正如我们所料,智能体调用了write_file工具,向file_one.txt写入了5字节,然后调用了read_file工具。接着我们可以看到内容确实是“file1”。

实现多步推理循环

到目前为止,我们的智能体只能执行一步操作。但一个真正的智能体应该能够迭代地执行任务,并在完成时停止。我们需要创建一个循环和退出条件。决定何时停止的最简单方法是当智能体不再调用新函数或达到最大步数限制时。让我们看看实现:我们在coding_agent函数中处理一个新的循环,直到满足条件。参数max_steps默认为5。如果智能体不想进行新的函数调用,意味着它认为我们不需要使用可用工具,那么我们将任务标记为完成。我们还保留一个消息列表,并附加初始用户消息、每个消息部分以及工具的结果。

让我们来测试一下。这里我们希望模型创建一个凯撒密码函数,然后向我询问消息和移位值,接着运行它,打印加密后的消息,并将其保存到一个secret.txt文件中。步骤相当多,智能体将引导你完成。

user_query = """
1. Create a Python function that performs a Caesar cipher encryption.
   It should take a message (string) and a shift (integer) as input.
2. Ask the user for a message and a shift value using `input()`.
3. Use the function to encrypt the message.
4. Print the encrypted message.
5. Save the encrypted message to a file named 'secret.txt'.
"""
# messages = coding_agent(client, user_query, system_prompt, tools_registry, max_steps=10)

假设我输入我的名字“Francesco”作为消息,移位值设为3。模型告诉我它已将加密消息写入secret.txt文件。我没有看到任何write_file工具调用,所以它可能使用Python代码来写入文件。让我们检查这个文件,看看它是否真的在那里。

# 假设智能体已执行完毕,我们可以读取文件
# with open('secret.txt', 'r') as f:
#     print(f.read())

我们可以看到,智能体确实写入了正确的加密消息到当前文件夹。

创建交互界面

让我们创建一个简单的界面来与我们的代码智能体交互。

def interactive_agent(client, system_prompt, tools_registry):
    """
    与代码智能体交互的简单界面。
    """
    print("Welcome to the Coding Agent. Type 'exit' to quit.")
    while True:
        user_input = input("\nYou: ")
        if user_input.lower() == 'exit':
            break
        messages = coding_agent(client, user_input, system_prompt, tools_registry, max_steps=5)
        # 打印智能体的最后一条文本回复
        for msg in reversed(messages):
            if msg['role'] == 'assistant' and 'content' in msg and msg['content']:
                print(f"Agent: {msg['content']}")
                break

我们可以通过要求智能体创建一个ASCII艺术奶牛并将其返回给我们来测试它。

# 启动交互界面
# interactive_agent(client, system_prompt, tools_registry)
# 用户输入: "Create an ASCII art cow and print it back to me."

它成功了,我们可以看到我们的ASCII奶牛。你可以自由地继续与你的智能体对话。我们刚刚构建的这个智能体实验室在笔记本上本地运行。在下一课中,你将了解更多关于代码执行环境的知识,以及哪种环境最适合特定的使用场景。

总结

在本节课中,我们一起学习了如何构建一个具备工具执行能力的代码智能体。我们从设置环境开始,引入了语言模型作为核心。然后,我们定义了第一个工具——execute_code,并为其创建了JSON模式以便智能体调用。接着,我们实现了工具执行函数和智能体的主循环逻辑。为了扩展功能,我们增加了read_filewrite_file两个文件操作工具,并加入了健壮的错误处理。最后,我们实现了多步推理循环,使智能体能迭代完成复杂任务,并创建了一个简单的交互界面。你现在已经拥有了一个可以执行代码、读写文件并进行多步推理的基础代码智能体。在接下来的课程中,我们将探索更高级的功能和不同的代码执行环境。

004:工具执行环境 🛠️

在本节课中,我们将学习用于运行代码的不同环境,包括本地执行、容器和沙箱(如微虚拟机)。你将了解如何根据具体用例选择合适的环境。

概述

执行由大型语言模型生成的代码可以在多种环境中进行。你可以选择在本地运行代码,也可以使用Docker容器。另一种选择是使用沙箱环境。你可能会问,选择不同方法的标准是什么?这取决于你需要为你的智能体考虑的诸多因素。

安全挑战

其中一个核心挑战是安全。如果我们直接运行模型生成的操作,可能会出现什么问题?一个智能体可能会读取系统中的敏感数据,甚至注入恶意代码。模型可能意外暴露你的API密钥,恶意代码可能导致整个应用程序崩溃。此外,还存在扩展性问题以及无数其他安全漏洞。

本地执行

如果你使用云代码编辑器或Cursor,你可能熟悉在本地运行模型生成代码的方法。在使用编码助手时,你期望智能体在你的机器上运行代码和编辑文件。但是,本地执行存在重要的局限性,它无法提供强大的安全性。

主要问题在于缺乏完全隔离。如果模型生成了不期望的代码,它可能会运行并读取或修改你机器上的任何内容。除了安全问题,本地执行还存在其他限制:你的笔记本电脑资源会很快耗尽;如果安装的软件包存在冲突,环境可能会变得混乱;你无法轻松地让其他用户大规模使用你的智能体。

如果你想构建用于生产环境的应用程序,本地执行很快就会遇到瓶颈。像Lavable York或许多其他流行的智能体,从深度研究到编码代理,都利用沙箱解决方案来扩展到成千上万的用户。

容器环境

智能体环境的一个选择是Docker或广义上的容器。Docker通过使用命名空间在单独的进程中运行代码,提供了操作系统级别的隔离,这使其比本地代码执行更加隔离。

然而,所有容器仍然共享同一个主机内核。这意味着,即使其中一个容器被攻破,攻击者也有可能找到路径进行权限提升并访问主机。当同时运行多个不受信任的代码实例和多个容器时,这种风险变得更加相关。默认情况下,容器的系统调用会直接到达主机内核,这意味着攻击面更大。

为了使Docker更安全,一个选择是减少攻击面。为此,我们可以使用例如gVisor。来自Google的gVisor是一个用户空间内核,它将容器“监禁”起来,意味着我们对其可以访问的内容增加了限制。它自己实现了大部分Linux系统调用接口,实际上拦截了系统调用。它是一个中间人:容器首先与gVisor通信,而不是直接与主机内核通信。

gVisor也存在权衡:一些性能开销、操作系统兼容性差距、不同的调试或跟踪工具。即使gVisor也并非100%安全。它仍然可能被攻破,因为gVisor仍然依赖于主机内核。其复杂的实现中,漏洞仍然可能被利用。

沙箱环境

因此,我们有一个明确的目标:模型生成的代码绝不应该能够危及主机系统。一个很好的解决方案是沙箱。沙箱是一个用于运行代码的受限、隔离的环境。沙箱最大限度地减少了对主机机器的影响。

与容器相比,沙箱提供了真正的进程隔离,使其成为安全运行不受信任或模型生成工作负载的常见基础。沙箱的实现包括操作系统级隔离、WebAssembly或轻量级微虚拟机,如Firecracker、Gumo或Cloud Hypervisor。

一种非常适合编码智能体的沙箱解决方案实现是微虚拟机。这些是基于内核的轻量级虚拟机,剥离了不必要的硬件模拟,旨在快速启动和高效运行。每个微虚拟机运行自己的客户内核,这提供了更强的隔离性,同时保持了接近原生的性能。AWS Firecracker是微虚拟机实现的一个广泛使用的例子,它为AWS Lambda或Fargate等服务提供支持。

微虚拟机的权衡

那么,如果微虚拟机提供了更强的隔离性,为什么我们不随处使用它们呢?答案是存在重要的权衡。

首先,微虚拟机更难设置。与Docker不同,你不能只运行一条命令。它们需要更多的工程知识和手动配置。社区和生态系统也较小,你无法获得像Docker Hub那样的工具支持或公共镜像注册表。

可扩展性是另一个挑战。你需要自己管理基础设施,包括更新、编排以及解决技术栈中的安全漏洞。微虚拟机可以是一个很好的解决方案,但直接运行它们会产生一些操作开销,这使得大多数团队采用起来更加困难。

沙箱即服务提供商

因此,是的,沙箱作为一种技术有其优势。但随着AI智能体的兴起,我们也可以利用沙箱即服务提供商的优势。像E2B、Daytona或Model Context Protocol这样的公司提供沙箱环境,并在沙箱技术之上提供额外的好处。

与其自己设置微虚拟机或维护自己的可观测性和基础设施,沙箱提供商为你提供专门为AI智能体构建的生产就绪沙箱。它们可以提供诸如针对不同用例的预构建沙箱、长时间运行的会话、密钥和API管理等功能。当你需要服务数千甚至数百万用户时,沙箱提供商可以提供你所需的可扩展性。它们已经管理了底层编排,因此你可以专注于智能体,而不是基础设施。

使用沙箱来运行你的代码将变得非常简单,就像创建沙箱实例并将你的代码传递给它运行一样。

总结

在本节课中,我们一起学习了执行模型生成代码的不同环境。我们探讨了本地执行的局限性、容器(如Docker)提供的隔离性及其安全考量,以及沙箱(特别是微虚拟机)如何为实现更强的安全隔离提供了解决方案。我们还讨论了微虚拟机的权衡,并介绍了沙箱即服务提供商如何简化这一过程,让开发者能够专注于构建智能体本身。

在下一节课中,你将与France Francisco一起,实际使用E2B沙箱来运行你的智能体生成的代码。

005:在云端安全运行代码 🚀

在本节课中,我们将学习如何使用 HubB Sandbox 环境,在云端安全地运行智能体生成的代码。你将学会创建、管理并与一个远程沙盒环境进行交互,该环境能够安全地执行代码。

概述

上一节我们介绍了如何为智能体构建工具。本节中,我们来看看如何让智能体在云端沙盒中安全地执行代码。我们将使用 HubB 提供的沙盒环境,它通过简单的 API 管理,能有效隔离代码执行,确保安全性。

使用 HubB API 管理沙盒

HubB 提供了非常简单的 API 来管理沙盒。以下是核心操作步骤。

首先,从 HB Python 包中导入 Sandbox 类来创建沙盒。你可以设置超时时间(以秒为单位),例如,以下代码将创建一个存活一小时的沙盒。

from hb import Sandbox

sandbox = Sandbox(timeout=3600)  # 1小时超时

运行代码时,将代码字符串传递给沙盒的 run_code 方法。该方法会返回一个 Execution 对象,你可以将其视为一个笔记本单元格的输出。

execution = sandbox.run_code("print('Hello World')")

在这个例子中,代码只是打印“Hello World”,你可以在返回的 execution 对象的 logs 属性中看到输出。

print(execution.logs)  # 输出: Hello World

如果要返回一个变量,结果将位于 execution 对象的 results 数组中。

支持多种编程语言

HubB 默认支持 Python,但你也可以随时更改为其他语言,例如 JavaScript。

sandbox.language = "javascript"
execution_js = sandbox.run_code("console.log('Hello from JS')")
print(execution_js.logs)

我们还可以运行使用 Matplotlib 创建图表的 Python 代码。

code = """
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.plot(x, y)
plt.title('A Simple Plot')
"""
execution_plot = sandbox.run_code(code)

为了在单元格中显示图表,我们可以导入 display 函数。

from IPython.display import display
display(execution_plot.results[0])  # 假设图表在 results 中

列出与查询沙盒

有时,列出你正在使用的所有沙盒很有用。为此,你可以在 Sandbox 类上调用 list 方法。

sandboxes_page = Sandbox.list()
for sb in sandboxes_page:
    print(f"ID: {sb.id}, Status: {sb.status}")

你还可以打印沙盒的一些有用字段,例如元数据、创建时间和来源。

print(sandbox.meta)
print(sandbox.created_at)
print(sandbox.origin)

你可以在创建时附加一些元数据来查询沙盒。例如,我们设置 name"fine"

sandbox = Sandbox(meta={"name": "fine"})

当列出沙盒时,我们可以传递一个查询对象来过滤。

from hb import SandboxQuery
query = SandboxQuery(meta={"name": "fine"}, status="running")
found_sandboxes = Sandbox.list(query=query)

管理沙盒文件系统

HubB 沙盒拥有一个文件系统。我们可以在其中创建目录。

sandbox.files.mkdir("/home/user/data")

我们可以将一些内容写入文件。

sandbox.files.write("/home/user/data/hello.txt", "Hello from the sandbox")

我们可以使用 read 方法读回内容。

content = sandbox.files.read("/home/user/data/hello.txt")
print(content)  # 输出: Hello from the sandbox

最后,我们可以删除这个文件。

sandbox.files.remove("/home/user/data/hello.txt")

在沙盒中运行 Web 服务器

沙盒可以运行更复杂的应用,例如网站。这很简单,我们只需要创建一个 index.html 文件,并启动一个服务器来提供该文件。

首先,我们写入一个简单的 HTML 文件到沙盒中。

html_content = "<html><body><h1>Hello from Sandbox Web Server!</h1></body></html>"
sandbox.files.write("/home/user/index.html", html_content)

然后,我们可以使用 Python 的 http.server 模块在端口 3000 上启动一个服务器。

server_code = """
import http.server
import socketserver
import os
os.chdir('/home/user')
with socketserver.TCPServer(("", 3000), http.server.SimpleHTTPRequestHandler) as httpd:
    httpd.serve_forever()
"""
# 注意:`serve_forever` 会阻塞,在实际中可能需要以子进程方式运行。

为了可视化网站,我们可以在 Jupyter Notebook 中使用 IFrame 类。

from IPython.display import IFrame
display(IFrame(src=f"http://{sandbox.host}:3000", width=600, height=400))

为智能体集成代码执行能力

现在我们已经知道如何使用 HubB 沙盒,是时候为我们的智能体赋予在云端运行代码的能力了。

我们修改在之前实验中创建的 execute_code 函数。这次,我们将语言模型生成的代码传递给沙盒的 run_code 方法。

def execute_code_in_sandbox(code_str: str, sandbox: Sandbox):
    """在沙盒中安全执行代码"""
    try:
        execution = sandbox.run_code(code_str)
        # 处理执行结果,例如提取 logs 或 results
        output = execution.logs if execution.logs else str(execution.results)
        return output
    except Exception as e:
        return f"代码执行出错: {e}"

然后,我们重建工具注册表,将新的执行函数作为工具添加进去。

# 假设已有工具注册表构建逻辑
tool_registry.add_tool(
    name="execute_python",
    description="在安全沙盒中执行Python代码并返回结果",
    function=execute_code_in_sandbox,
    args_schema=... # 定义参数模式
)

为了在 Notebook 中友好地显示输出,我们可以使用 displayMarkdown 函数。

from IPython.display import display, Markdown

def log_output(message):
    """以友好格式记录输出"""
    display(Markdown(f"**输出:** {message}"))

让我们尝试运行智能体,观察它连接沙盒、执行代码并回复的过程。

# 初始化智能体客户端和沙盒
client = OpenAIClient(api_key="your_key")
sandbox = Sandbox()

# 向智能体提问
task = "编写一个模拟掷六面骰子的函数,并运行它。"
response = coding_agent(task, client, sandbox)
log_output(response)

智能体回复说:“我创建了一个名为 roll_die 的函数。” 我们实际上可以看到它在沙盒上创建和运行的代码,以及模拟掷骰子的结果。

测试文件系统操作能力

让我们给智能体分派一个与文件系统协作的任务。请注意,我们尚未为这个智能体专门添加文件系统工具,但通过执行 Python 代码,它实际上可以创建文件。

任务:创建一个包含“Hello World”的文本文件,读取它,并将其内容返回。

task2 = """
在沙盒中创建一个名为 `greeting.txt` 的文件,内容为 'Hello World'。
然后读取该文件并将其内容返回给我。
"""
response2 = coding_agent(task2, client, sandbox)
log_output(response2)

我们可以看到智能体运行代码、创建文件,然后将内容打印给我们。代码如下:

# 智能体可能生成的代码示例
with open('/home/user/greeting.txt', 'w') as f:
    f.write('Hello World')
with open('/home/user/greeting.txt', 'r') as f:
    content = f.read()
print(content)

仅仅凭借在沙盒中执行 Python 代码的能力,智能体已经相当强大。它可以创建、添加和删除文件。

创建网页游戏:综合能力测试

让我们通过要求智能体创建一个简单的贪吃蛇游戏网页来测试它的能力。我们给出一个更具体的查询。

game_task = """
创建一个简单的贪吃蛇游戏网页。
要求:
1. 只使用 HTML、CSS 和原生 JavaScript,不要用任何外部库。
2. 游戏区域是一个 20x20 的网格。
3. 使用箭头键控制蛇的移动。
4. 蛇吃到食物后会变长。
5. 蛇撞到墙壁或自身身体时游戏结束。
6. 游戏结束后可以按空格键重新开始。
请将代码写入沙盒文件系统,并启动一个Web服务器以便我试玩。
"""
response3 = coding_agent(game_task, client, sandbox)
log_output(response3)

智能体创建了文件。我们现在可以使用 IFrame 重新打开它,通过运行 Python HTTP 服务器来查看。

# 假设智能体已将游戏写入 /home/user/snake_game/index.html
display(IFrame(src=f"http://{sandbox.host}:3000/snake_game", width=800, height=600))

你可以用箭头键移动蛇,吃水果,蛇会随之生长。如果撞到墙或自己,游戏结束,按空格键可以重新开始。

由于我们使用 GPT 生成这个游戏,每次生成的游戏可能略有不同,但你得到的功能和玩法应该是相似的。

智能体创建的是 HTML 文件,我们可以读取它、存储它,然后在本地浏览器中打开。

# 从沙盒读取游戏文件
game_html = sandbox.files.read("/home/user/snake_game/index.html")
# 保存到本地
with open("local_snake_game.html", "w") as f:
    f.write(game_html)
# 现在你可以在本地浏览器中打开 `local_snake_game.html` 文件

总结

本节课中,我们一起学习了如何利用 HubB Sandbox 环境,让代码智能体获得在云端安全执行代码的能力。我们涵盖了从创建和管理沙盒、执行不同语言代码、操作文件系统,到运行 Web 服务器的全过程。最重要的是,我们将此能力集成到了智能体中,使其能够根据用户需求生成并安全地运行代码,甚至创建出交互式的网页应用。在下一实验中,我们将创建一个数据分析智能体,用于分析上传的 CSV 文件。

006:数据分析智能体 🧮

在本节课中,我们将学习如何将一个基础的代码智能体转变为数据分析师。这个智能体能够在沙盒环境中探索数据、回答问题并创建可视化图表。

概述

上一节我们介绍了智能体的基本框架,本节中我们来看看如何赋予它数据分析的能力。我们将引导智能体加载数据集、执行查询、进行数据聚合并生成图表,所有操作都在一个安全的沙盒环境中完成。

环境设置与数据加载

首先,我们需要设置环境并加载数据。以下是初始步骤:

  1. 导入必要的库并设置环境。
  2. 加载位于笔记本文件夹中的 Pokemon.csv 数据集。
  3. 将数据写入沙盒环境以供分析。
# 示例:导入库和加载数据
import pandas as pd
# 假设数据加载到沙盒的代码

配置智能体进行数据分析

配置完成后,我们可以使用智能体来探索数据。我们导入了所需的所有函数,并稍微修改了系统提示词。我们告诉智能体用户已更新了文件,并且它应该帮助我们理解数据并创建有趣的图表。

让我们从询问一个基本问题开始:“这些数据是关于什么的?” 智能体将创建一些 pandas 代码,在 Python 中运行它,并可能查看数据的前几行或为我们生成摘要。

深入数据探索

智能体初步分析后,我们可以进行更深入的探索。例如,我们可以要求智能体运行一个聚合操作,按类型查看宝可梦的数量。我们保留了消息数组,以便将先前的对话历史也传递给智能体。

以下是智能体可能执行的分析步骤:

  • 数据概览:查看数据集的列和基本统计信息。
  • 聚合分析:例如,按“类型”分组并计数。
  • 生成摘要:以 Markdown 表格等形式呈现结果。

通过这种方式,我们可以看到数据集中有“虫”、“暗”、“龙”、“电”等多种类型的宝可梦。

交互式用户界面

为了更自然地与智能体交互,我们引入了一个简单的无线电用户界面。我们可以通过导入 ui 模块并运行它来启动界面,同时传递通常传递给代码函数的参数。

在界面中:

  • 左侧是聊天历史记录和向模型输入的区域。
  • 右侧是内容堆栈,可以可视化当前的上下文。每条消息的高度与其令牌数成正比,颜色方案与我们之前使用的保持一致(蓝色代表系统提示,绿色代表用户,紫色代表工具调用和结果,黄色代表系统消息)。

执行具体查询与可视化

现在,让我们继续我们的分析。我们可以问智能体:“哪个宝可梦最重?” 在界面右侧,我们会看到一个新的用户消息,智能体调用了 exec_code 工具并给出了回复。结果显示,最重的宝可梦是 CosmosGastrodon,它们的重量接近一吨。

我们还可以要求智能体生成图表。例如,智能体可能会建议:“如果您想将教学信息可视化为图表,我可以帮忙。” 那么,我们就可以要求它创建一个显示前10名宝可梦的条形图。

智能体会运行一些 pandasmatplotlib 代码,并生成一个显示前10名宝可梦的条形图。

# 示例:生成条形图的代码框架
import matplotlib.pyplot as plt
# ... 数据处理 ...
plt.bar(x, height)
plt.show()

探索未知数据集

在本节文件夹中,实际上还有另一个名为 unknown.csv 的 CSV 文件。我们对其一无所知。我们可以将其加载到沙盒中,并使用交互界面让智能体去探索和发现更多信息。

您可以自由地探索这个数据集,使用聊天界面向智能体提问,以获取关于它的信息。

总结

本节课中,我们一起学习了如何将一个代码智能体转变为数据分析师。我们涵盖了从环境设置、数据加载、基础查询到执行数据聚合和生成可视化图表的全过程。我们还引入了交互式界面,使与智能体的对话更加自然流畅。在下一课中,我们将构建一个功能更全面的智能体,能够生成复杂的网络应用程序。

007:构建全栈协调器 🏗️

在本节课中,我们将学习如何构建一个能够编辑多个文件并生成完整Web应用的全栈协调器。我们还将探讨如何通过运行时摘要技术来高效管理长上下文。

概述

上一节我们介绍了智能体如何执行单个任务。本节中,我们将构建一个更复杂的智能体,它能够协调多个文件操作,完成创建Web应用这样的复合任务。这类任务通常涉及大量代码生成和文件编辑,智能体可能需要执行数十次循环才能完成。

这不可避免地会迅速填满我们的上下文窗口。因此,我们需要一种策略,在用户与智能体交互时动态压缩上下文。

管理长上下文:运行时摘要

为了压缩上下文,我们采用多种策略中的一种:运行时摘要。首先,我们需要定义在任何时候希望上下文窗口保留的固定令牌数量,例如40000个令牌。然后,我们将压缩一定百分比的令牌。在本例中,我们遵循GeeseI案例的方法,压缩从最旧消息开始的70%,同时保持最近的交互不变。

接着,我们将需要压缩的令牌发送给语言模型进行摘要,然后将摘要注入回上下文,替换旧消息。具体流程如下:如果智能体的上下文已满,我们取出从最旧消息开始的70%进行压缩。压缩完成后,我们用摘要替换旧消息,并继续处理后续消息。

我们将使用一个具有特定系统提示的智能体来创建对话快照。然后,我们构建一个包含快照的合成用户消息和一个用于确认的助手消息。这两条消息被插入到上下文中,替代最初被压缩的那些消息。

构建全栈智能体

现在,让我们进入实验环节,将所有这些概念整合起来,构建一个全栈智能体。

首先,我们忽略一些警告,并设置环境,这与我们在先前课程中所做的类似。由于我们希望创建一个全栈智能体,并且调用Web应用是一项复杂任务,我们需要为智能体添加更多工具。

具体来说,我们需要添加用于处理文件系统的工具。对于这个智能体,我们配备了以下工具。它们都位于live文件夹内,我们将看到如何使用它们。

我们赋予智能体列出目录、读取文件、写入文件、搜索文件内容、替换文件以及进行更通用搜索的能力。所有工具都在我们的Lib文件夹内的xb_tools.py文件中。这些工具将被复制到沙箱中,并在沙箱环境中直接运行。

让我们尝试使用search_file_content工具。我们需要传入一个模式。在这个例子中,我们只是查找包含单词Sx的文件,这个单词我们在代码中使用。前四个结果将给我们分页的JSON。我们可以看到总共有65个匹配项,并且可以查看具体内容。例如,在文件单元格中,我们找到了它。这只是智能体为了生成Web应用将使用的众多工具之一。我们不会深入探讨每个工具的实现细节,但它们都位于Lib文件中,鼓励你前往查看。

配置智能体与系统提示

我们离目标很近了。我们只需要给智能体一个系统提示来定义复杂任务。系统提示位于live文件夹的prompts文件中,名为system_prompt_web_app。我们可以打印它,你会发现它复杂得多。这里我们告诉智能体如何思考、项目是如何创建的、使用的技术栈,以及它应该如何调用工具等。这个系统提示很大程度上受到了Geminize系统提示的启发。

现在,让我们启动我们的新智能体。我们需要导入所有智能体函数和新的工具模式,并创建一个沙箱。这次我们传递一个模板ID,因为我们希望使用一个预装了Next.js运行时的沙箱。我们将使用Gradio UI来开发Web应用。create_sandbox函数还会在其中安装一些包,并将我们本地的工具复制到沙箱中,以便智能体使用。

现在我们的Gradio界面正在运行。我们添加了一个新元素——浏览器元素。我们可以在沙箱中看到网站是活的,并且能够实时看到模型对Web应用所做的更改。由于录屏宽度有限,你无法并排看到元素,但如果你在更大的屏幕上运行笔记本,就可以看到。

运行与测试智能体

沙箱中运行的应用是一个Xs模板,只包含一个页面。我们可以要求智能体将其更改为更有趣的东西。让我们创建一个待办事项列表,但要采用Windows 95的风格,放在主页面。

智能体将列出文件夹文件,读取当前文件,并生成代码。这需要几分钟,因为它可能需要生成几千个令牌来创建这个应用。在智能体生成代码时,你总能看到上下文在增长。我们可以看到智能体使用了read_file工具来读取当前的Yes文件。现在它正在执行一些代码以确保一切正确。我们可以看到它使用了相当多的令牌,但这是合理的。我们的应用在这里,让我们快速测试一下,一切似乎都正常。

我不太喜欢这些图标。颜色方案使得它们很难看清。让我们看看。智能体给了我们一个总结,然后我们问它:“你能让待办事项列表顶部的图标更显眼吗?它们目前是白色的,很难看清。”智能体可能会重新读取文件并更改图标的颜色。

智能体完成了任务。你可以看到它使用了replace_file工具。现在图标的颜色更好了,更容易看清。

工具使用分析

让我们具体看看模型为这个特定任务使用的所有工具。它使用了replace_file工具,该工具接受文件路径,然后用一个字符串替换另一个字符串。它还使用了execute_bar工具、read_file工具等等。为了节省时间,我们没有逐一实现所有工具,但它们都可以在Lib模块下的SBX_tools.py文件中找到。

这是工具文件,你可以看到这里也使用了我们在第一个实验中做的Q错误处理部分。我们有一个函数来处理结果。模型能够运行的所有函数都在这里记录:root_filewrite_file等等。请随时查看这些工具,并添加新的工具以使智能体更强大。你可以在这里实现一个工具并将其添加到注册表中,智能体将能够使用它。

总结

本节课中,我们一起学习了如何构建一个全栈协调器智能体。我们探讨了通过运行时摘要管理长上下文的方法,为智能体配备了文件系统操作工具,并通过一个复杂的系统提示引导其完成创建和修改Web应用的复合任务。最后,我们测试了智能体的功能,并分析了它在任务执行过程中使用的工具。鼓励你尝试用自己的提示来使用这个智能体,看看能构建出什么。

008:总结 🎯

在本课程中,我们学习了如何让代码智能体进行思考、行动,并在沙箱环境中安全地执行代码,其应用范围从简单的代码片段到完整的网络应用程序。

课程回顾

上一节我们探讨了智能体在复杂应用场景中的实践。现在,让我们对整个课程的核心内容进行总结。

以下是本课程涵盖的主要学习要点:

  • 思考与规划:智能体首先需要理解任务,并规划出实现目标的步骤序列。
  • 行动与工具调用:智能体根据规划,选择并调用合适的工具(如代码解释器、API)来执行具体操作。
  • 安全执行:所有代码的执行都在受控的沙箱环境中进行,以确保系统安全。核心安全机制可以概括为:执行环境.isolated == True
  • 从片段到应用:我们构建智能体的能力是递进的,从处理 print("Hello, World") 这样的单行代码,逐步扩展到能够构建交互式网络应用。

我们迫不及待想看到你接下来会构建出什么。🚀


在本节课中,我们一起学习了构建代码智能体的完整流程,掌握了其“思考-行动-安全执行”的核心工作模式。希望你能够运用这些知识,创造出强大而安全的AI驱动应用。

posted @ 2026-03-26 08:14  绝不原创的飞龙  阅读(3)  评论(0)    收藏  举报