文件交互
插件开发中经常需要处理文件,例如读取用户上传的文件、生成图片或文档返回给 AI、处理多媒体内容等。Nekro Agent 提供了全新的文件系统 API,通过 AgentCtx
的 fs
属性,让插件和 AI 沙盒之间的文件传递变得简单高效。
文件系统概述
在 Nekro Agent 中,AI 在隔离的沙盒环境中运行,而插件在主服务进程中执行。新的文件系统 API 通过 _ctx.fs
提供了两个核心方法来处理文件传递:
mixed_forward_file
: 插件向 AI 传递文件(URL、字节数据、本地文件等 → 沙盒路径)get_file
: AI 向插件传递文件(沙盒路径 → 宿主机真实路径)
核心概念
沙盒路径与宿主机路径
- 沙盒路径: AI 看到的文件路径,通常以
/app/uploads/
或/app/shared/
开头 - 宿主机路径: 插件实际操作的文件在宿主机上的真实路径
文件系统 API 自动处理这两种路径之间的转换,开发者无需关心底层实现细节。
插件向 AI 传递文件
mixed_forward_file
方法
当插件需要生成文件(图片、文档、数据等)并返回给 AI 时,使用此方法:
python
async def mixed_forward_file(
file_source: Union[str, bytes, Path, BinaryIO],
file_name: Optional[str] = None,
**kwargs
) -> str
支持的文件来源类型:
- HTTP/HTTPS URL 字符串
- 字节数据 (bytes)
- 本地文件路径 (Path)
- 文件对象 (BinaryIO)
使用示例
示例 1:从 URL 转发图片
python
from nekro_agent.api.plugin import SandboxMethodType
from nekro_agent.api.schemas import AgentCtx
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "get_logo", "获取项目 Logo")
async def get_logo(_ctx: AgentCtx) -> str:
"""从 URL 获取 Logo 图片并返回给 AI"""
# 从外部 URL 获取图片
image_url = "https://nekro-agent-dev.oss-cn-beijing.aliyuncs.com/images/NA_logo.png"
# 转换为 AI 可用的沙盒路径
sandbox_path = await _ctx.fs.mixed_forward_file(image_url, file_name="logo.png")
# 返回沙盒路径给 AI
return sandbox_path # "/app/uploads/logo.png"
示例 2:生成数据文件
python
import json
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "generate_data_file", "生成数据文件")
async def generate_data_file(_ctx: AgentCtx, data: dict) -> str:
"""生成 JSON 数据文件并返回给 AI"""
# 将数据序列化为 JSON 字节
json_data = json.dumps(data, ensure_ascii=False, indent=2).encode('utf-8')
# 转换为 AI 可用的沙盒路径
sandbox_path = await _ctx.fs.mixed_forward_file(json_data, file_name="data.json")
return sandbox_path # "/app/uploads/data.json"
示例 3:使用插件共享目录
python
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "create_temp_file", "创建临时文件")
async def create_temp_file(_ctx: AgentCtx, content: str) -> str:
"""在插件共享目录创建临时文件"""
# 在共享目录创建文件
temp_file = _ctx.fs.shared_path / "temp.txt"
with open(temp_file, "w", encoding="utf-8") as f:
f.write(content)
# 转换为 AI 可用的沙盒路径
sandbox_path = _ctx.fs.forward_file(temp_file)
return sandbox_path # "/app/shared/temp.txt"
AI 向插件传递文件
get_file
方法
当 AI 调用插件并传入文件路径参数时,插件使用此方法获取宿主机上的真实路径:
python
def get_file(sandbox_path: Union[str, Path]) -> Path
使用示例
示例 1:分析图片文件
python
import aiofiles
from PIL import Image
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "analyze_image", "分析图片文件")
async def analyze_image(_ctx: AgentCtx, image_path: str) -> str:
"""分析 AI 提供的图片文件"""
# AI 提供的沙盒路径: "/app/uploads/photo.jpg"
# 转换为宿主机可访问的真实路径
host_path = _ctx.fs.get_file(image_path)
try:
# 使用 PIL 打开并分析图片
with Image.open(host_path) as img:
width, height = img.size
format_name = img.format
mode = img.mode
return f"图片分析结果:尺寸 {width}x{height},格式 {format_name},模式 {mode}"
except Exception as e:
return f"图片分析失败:{e}"
示例 2:处理文本文件
python
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "process_text_file", "处理文本文件")
async def process_text_file(_ctx: AgentCtx, file_path: str) -> str:
"""处理 AI 提供的文本文件"""
# 转换为宿主机路径
host_path = _ctx.fs.get_file(file_path)
try:
# 异步读取文件内容
async with aiofiles.open(host_path, "r", encoding="utf-8") as f:
content = await f.read()
# 简单的文本分析
lines = len(content.splitlines())
words = len(content.split())
chars = len(content)
return f"文件分析:{lines} 行,{words} 个词,{chars} 个字符"
except Exception as e:
return f"文件处理失败:{e}"
结合消息发送的文件处理
使用便捷方法发送文件
AgentCtx
提供了便捷的消息发送方法,可以直接发送文件给用户:
python
@plugin.mount_sandbox_method(SandboxMethodType.BEHAVIOR, "send_report", "生成并发送报告")
async def send_report(_ctx: AgentCtx, title: str, content: str) -> str:
"""生成报告文件并发送给用户"""
# 创建报告内容
report_content = f"# {title}\n\n{content}\n\n生成时间:{datetime.now()}"
report_bytes = report_content.encode('utf-8')
# 转换为沙盒路径
sandbox_path = await _ctx.fs.mixed_forward_file(report_bytes, file_name=f"{title}.md")
# 发送文件给用户
await _ctx.send_file(sandbox_path)
return f"报告《{title}》已生成并发送"
发送处理后的图片
python
from PIL import Image, ImageFilter
@plugin.mount_sandbox_method(SandboxMethodType.BEHAVIOR, "apply_blur_filter", "应用模糊滤镜")
async def apply_blur_filter(_ctx: AgentCtx, image_path: str, radius: float = 2.0) -> str:
"""对图片应用模糊滤镜并发送结果"""
# 获取原始图片的宿主机路径
host_path = _ctx.fs.get_file(image_path)
try:
# 打开并处理图片
with Image.open(host_path) as img:
# 应用模糊滤镜
blurred = img.filter(ImageFilter.GaussianBlur(radius=radius))
# 保存到临时位置
output_path = _ctx.fs.shared_path / "blurred_image.jpg"
blurred.save(output_path, "JPEG")
# 转换为沙盒路径并发送
sandbox_path = _ctx.fs.forward_file(output_path)
await _ctx.send_image(sandbox_path)
return f"已应用模糊滤镜(半径 {radius})并发送结果图片"
except Exception as e:
return f"图片处理失败:{e}"
文件系统属性
常用路径属性
_ctx.fs
提供了一些有用的路径属性:
python
# 插件共享目录(用于临时文件)
shared_path = _ctx.fs.shared_path
# 用户上传目录
uploads_path = _ctx.fs.uploads_path
# 聊天频道标识
chat_key = _ctx.fs.chat_key
# 容器标识
container_key = _ctx.fs.container_key
实用方法
python
# 将宿主机文件转换为沙盒路径
sandbox_path = _ctx.fs.forward_file(host_file_path)
# 检查文件是否存在
if _ctx.fs.get_file(sandbox_path).exists():
# 文件存在
pass
最佳实践
1. 错误处理
始终对文件操作进行适当的错误处理:
python
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "safe_file_process", "安全的文件处理")
async def safe_file_process(_ctx: AgentCtx, file_path: str) -> str:
try:
host_path = _ctx.fs.get_file(file_path)
if not host_path.exists():
return "错误:指定的文件不存在"
# 文件处理逻辑...
return "处理成功"
except Exception as e:
return f"文件处理失败:{e}"
2. 文件类型验证
对于特定类型的文件,建议进行类型验证:
python
import mimetypes
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "process_image_only", "仅处理图片文件")
async def process_image_only(_ctx: AgentCtx, file_path: str) -> str:
host_path = _ctx.fs.get_file(file_path)
# 检查文件类型
mime_type, _ = mimetypes.guess_type(str(host_path))
if not mime_type or not mime_type.startswith('image/'):
return "错误:请提供图片文件"
# 图片处理逻辑...
return "图片处理完成"
3. 大文件处理
对于大文件,使用流式处理:
python
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "hash_large_file", "计算大文件哈希")
async def hash_large_file(_ctx: AgentCtx, file_path: str) -> str:
import hashlib
host_path = _ctx.fs.get_file(file_path)
sha256_hash = hashlib.sha256()
# 流式读取大文件
async with aiofiles.open(host_path, "rb") as f:
while chunk := await f.read(8192): # 8KB 块
sha256_hash.update(chunk)
return f"文件 SHA256: {sha256_hash.hexdigest()}"
4. 临时文件清理
使用上下文管理器或在适当时机清理临时文件:
python
import tempfile
import os
@plugin.mount_sandbox_method(SandboxMethodType.TOOL, "create_temp_processed_file", "创建临时处理文件")
async def create_temp_processed_file(_ctx: AgentCtx, input_data: str) -> str:
try:
# 创建临时文件
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as temp_file:
temp_file.write(f"处理结果:{input_data}")
temp_path = temp_file.name
# 转换为沙盒路径
sandbox_path = await _ctx.fs.mixed_forward_file(temp_path, file_name="processed.txt")
# 清理临时文件
os.unlink(temp_path)
return sandbox_path
except Exception as e:
return f"临时文件创建失败:{e}"
通过使用新的文件系统 API,插件开发者可以轻松实现复杂的文件处理功能,而无需关心底层的路径转换和环境差异问题。