Skip to content

沙盒方法详解

沙盒方法是 Nekro Agent 插件向 AI 提供功能和交互能力的主要途径。AI 在其沙盒环境中执行代码时,可以通过调用这些方法来获取信息、执行操作或请求进一步的处理。理解沙盒方法的机制、类型和编写规范对于开发有效的插件至关重要。

什么是沙盒方法?

当 AI 需要执行某些超出其自身能力范围的操作时(例如,访问外部 API、读写文件、执行复杂计算),它会生成一段代码,这段代码通常会调用插件提供的特定函数。这些被插件暴露给 AI 调用的函数,我们就称之为"沙盒方法"。

核心特点:RPC 执行机制

一个非常重要的概念是,尽管这些方法被 AI 在"沙盒"环境中调用,但它们的实际执行发生在 Nekro Agent 的主服务进程中,而不是在隔离的沙盒内。这种通信是通过 RPC(远程过程调用)实现的。

  • AI 在沙盒中发起调用请求。
  • Nekro Agent 核心系统接收请求,并在主进程中找到并执行对应的插件沙盒方法。
  • 方法的执行结果通过 RPC 返回给沙盒环境,供 AI 后续使用。

这种机制的优势在于插件方法可以访问主服务环境的所有资源(如数据库连接、核心服务 API 等),同时保持沙盒环境的隔离性和安全性。但也需要开发者注意两者环境的差异,尤其是在处理文件路径等问题时(详见文件交互章节)。

注册沙盒方法

沙盒方法通过 @plugin.mount_sandbox_method() 装饰器注册到插件实例上。

python
from nekro_agent.api.plugin import SandboxMethodType
from nekro_agent.api.schemas import AgentCtx

@plugin.mount_sandbox_method(
    method_type=SandboxMethodType.TOOL,
    name="calculate_sum",
    description="计算两个数字的和。"
)
async def my_sum_function(_ctx: AgentCtx, num1: int, num2: int) -> int:
    """计算并返回两个整数的和。

    Args:
        num1 (int): 第一个加数。
        num2 (int): 第二个加数。

    Returns:
        int: 两个数字的和。
    """
    return num1 + num2

装饰器参数:

  • method_type (SandboxMethodType): 指定沙盒方法的类型,这决定了 AI 如何使用该方法以及框架如何处理其返回值。详见下一节。
  • name (str): 用于在前端展示给用户的方法名称 命名应简洁并能准确描述方法功能。它不必与 Python 函数名相同。
  • description (str): 对该方法的详细描述,将在前端展示给用户。

沙盒方法类型 (SandboxMethodType)

SandboxMethodType 是一个枚举类型,定义了沙盒方法的不同行为模式。选择正确的类型对于插件与 AI 的顺畅交互至关重要。

1. SandboxMethodType.TOOL (工具方法)

  • 用途:提供特定的、可直接使用的工具或功能,AI 调用后可以直接利用其返回结果进行后续的思考或生成响应。这类方法通常执行计算、数据检索、简单操作等。

  • 返回值:可以是任何 Python 内置的可序列化类型(如 str, int, float, bool, list, dict 等),或者可以被 pickle 序列化的自定义对象。框架会将返回值直接传递给沙盒中调用该方法的代码。

  • AI 交互:AI 调用后,会等待方法执行完毕并获取返回值,然后基于返回值继续执行其任务。通常不直接触发 AI 的新一轮回复。

  • 示例:数学计算、文本翻译、单位转换、从特定数据源获取结构化信息、检查系统状态等。

    python
    @plugin.mount_sandbox_method(SandboxMethodType.TOOL, "get_current_time", "获取当前的日期和时间。")
    async def get_time(_ctx: AgentCtx) -> str:
        import datetime
        return datetime.datetime.now().isoformat()

2. SandboxMethodType.AGENT (代理方法)

  • 用途:用于执行需要 AI 进一步处理或解释的操作,或者提供需要 AI 进行迭代思考的信息。这类方法通常涉及到与外部世界的主动交互(如网络搜索)、复杂信息的生成或需要 AI 根据结果生成新一轮对话的场景。

  • 返回值必须是字符串 (str) 类型。这个字符串通常是对所执行操作的结果的描述,或者需要 AI 理解并据此生成回复的信息。

  • AI 交互:方法返回的字符串会被添加到当前的对话上下文中,并立即触发 AI 进行新一轮的思考和回复。AI 会将这个返回的字符串视为一个新的观察结果或信息输入。

  • 示例:执行网络搜索并返回摘要、调用外部知识库、生成一段复杂的文本内容供 AI 润色、需要用户进一步确认的操作等。

    python
    @plugin.mount_sandbox_method(SandboxMethodType.AGENT, "search_knowledge_base", "根据关键词搜索内部知识库并返回相关信息。")
    async def search_kb(_ctx: AgentCtx, query: str) -> str:
        # 假设 search_internal_db 是一个执行实际搜索的函数
        results = await search_internal_db(query)
        if not results:
            return f"在知识库中没有找到与 '{query}' 相关的信息。"
        return f"关于 '{query}' 的知识库搜索结果如下:\n{results}"

3. SandboxMethodType.BEHAVIOR (行为方法)

  • 用途:用于执行某些操作或修改系统状态,AI 需要知道操作的结果,但这个结果本身不应该直接触发 AI 的新一轮回复。通常用于执行一些"副作用"操作。

  • 返回值必须是字符串 (str) 类型。这个字符串是对行为执行结果的描述。

  • AI 交互:方法返回的字符串会被作为一条系统消息添加到当前的聊天记录中,供 AI 和用户参考。但是,与 AGENT 类型不同,它不会立即触发 AI 的新一轮回复。AI 可能会在后续的思考中参考这条信息。

  • 示例:发送消息/邮件(返回发送状态)、设置定时器(返回设置成功的消息)、修改某个配置项(返回修改结果)、在用户无感知的情况下执行一些后台任务并记录结果。

    python
    from nekro_agent.api import message
    
    @plugin.mount_sandbox_method(SandboxMethodType.BEHAVIOR, "send_channel_message", "向当前会话发送一条消息。")
    async def send_chat_message(_ctx: AgentCtx, text_to_send: str) -> str:
        try:
            await message.send_text(_ctx.from_chat_key, text_to_send, _ctx)
            return f"消息 '{text_to_send[:30]}...' 已成功发送。"
        except Exception as e:
            return f"发送消息失败: {e}"

4. SandboxMethodType.MULTIMODAL_AGENT (多模态代理方法)

  • 用途:与 AGENT 类型类似,但专门用于处理和返回多模态内容(如图像、音频等),并需要 AI 对这些多模态内容进行理解和回应。

  • 返回值:必须是符合特定结构的多模态消息段(通常是 OpenAI 定义的消息格式,例如包含图像 URL 或 base64 数据的消息对象)。

  • AI 交互:返回的多模态消息段会被添加到当前的对话上下文中,并立即触发 AI 进行新一轮的思考和回复,AI 应能处理和理解这些多模态内容。

  • 示例:生成一张图片并返回给 AI、将文本转换为语音并让 AI 播放、分析一张用户上传的图片并返回分析结果的图文结合消息。

    python
    # 伪代码示例,实际结构取决于 Nekro Agent 对多模态消息的处理
    @plugin.mount_sandbox_method(SandboxMethodType.MULTIMODAL_AGENT, "generate_image_and_comment", "生成一张关于特定主题的图片,并附带一句评论。")
    async def generate_image_with_comment(_ctx: AgentCtx, topic: str) -> List[Dict[str, Any]]: # 返回值类型可能是一个dict
        image_url = await generate_image_service(topic) # 假设这是一个生成图片的服务
        comment = f"这是一张关于 '{topic}' 的图片。"
        # 返回符合 OpenAI 或 Nekro Agent 内部规范的多模态消息结构
        return [
            {
                "type": "text",
                "text": comment
            },
            {
                "type": "image_url",
                "image_url": {
                    "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." # (图片的Base64编码数据)
                }
            },
        ]

选择合适的类型

需求场景推荐类型
AI 需要直接使用方法返回的数据进行后续处理TOOL
AI 需要根据方法的文本输出进行新一轮对话AGENT
AI 执行操作后,只需记录结果,不立即对话BEHAVIOR
AI 需要根据方法返回的多模态内容进行新一轮对话MULTIMODAL_AGENT

编写沙盒方法:规范与最佳实践

为了确保 AI 能够正确、高效地使用你的沙盒方法,请遵循以下规范:

  1. 清晰的函数签名与类型注解

    • 所有沙盒方法都必须是异步函数 (async def)。
    • 第一个参数必须_ctx: AgentCtx,它提供了会话上下文信息。
    • 为所有参数和返回值添加明确的 Python 类型注解。这不仅有助于代码维护,也是 AI 理解参数类型的重要依据。
  2. 详细且结构化的文档字符串 (Docstring): 这是 AI 理解和使用你的沙盒方法的最重要信息来源之一!文档字符串应该遵循一定的格式(如 Google 风格、Numpy 风格),并至少包含:

    • 简洁的摘要行:清晰说明方法的功能。
    • 详细描述 (可选):如果需要,可以进一步阐述方法的工作方式、限制或注意事项。
    • Args: 部分:列出所有参数(除了 _ctx),说明每个参数的名称、类型和含义。
    • Returns: 部分:说明返回值的类型和含义。
    • Example: 部分 (强烈推荐):提供一个或多个 AI 可以理解和模仿的 Python 调用示例。这对于复杂方法尤其有用。
    python
    @plugin.mount_sandbox_method(SandboxMethodType.TOOL, "get_user_preference", "获取指定用户的特定偏好设置。")
    async def get_user_preference(_ctx: AgentCtx, user_id: str, preference_key: str) -> Optional[str]:
        """获取用户的偏好设置值。
    
        根据用户ID和偏好键名,从插件存储中检索对应的偏好设置值。
    
        Args:
            user_id (str): 用户的唯一标识符。
            preference_key (str): 需要查询的偏好设置的键名 (例如:"theme", "language")。
    
        Returns:
            Optional[str]: 如果找到偏好设置,则返回其字符串值;如果未找到,则返回 None。
        
        Example:
            ```python
            # 获取用户 'user_123' 的 'theme' 偏好设置
            theme = get_user_preference(user_id="user_123", preference_key="theme")
            if theme:
                print(f"用户的主题是: {theme}")
            else:
                print("未找到用户的主题偏好设置。")
            ```
        """
        # ... 实现逻辑 ...
        stored_value = await plugin.store.get(user_key=user_id, store_key=preference_key)
        return stored_value
  3. 适当的命名

    • 沙盒方法的 name 参数(暴露给 AI 的名称)应使用清晰、简洁、符合 AI 调用习惯的命名方式(通常是下划线 snake_case 或驼峰 camelCase,具体看 AI 的偏好,但保持插件内一致性)。
    • Python 函数名可以更具描述性。
  4. 错误处理

    • 在方法内部妥善处理可能发生的异常(如 API 调用失败、文件未找到、数据格式错误等)。
    • 尽量不要让未捕获的异常直接抛给 AI。如果可以,将错误信息以友好的方式包含在返回值中(特别是对于 TOOL 类型),或者记录详细日志。
    • 对于 AGENTBEHAVIOR 类型,如果发生严重错误,可以在返回的描述字符串中说明错误情况。
  5. 返回值处理

    • 严格遵守所选 SandboxMethodType 对返回值的类型要求。
    • 确保返回的数据是 AI 可理解和处理的。
  6. 上下文 (AgentCtx) 的正确使用AgentCtx 对象 (_ctx) 包含了当前会话的关键信息,如 _ctx.from_chat_key (会话标识)、ctx.from_user_id (用户标识)等。按需使用这些信息,例如用于区分不同会话的数据存储、获取消息发送者等。

通过遵循这些规范,你可以创建出 AI 更易于理解、更乐于使用、也更不容易出错的强大沙盒方法。