跳到主要内容

第 4 章 · 资源、工具与沙箱

前几章讲的是 Agent 的"脑子"(循环、记忆)。本章讲"手脚"——模型说要查某张表、跑某段代码,这句话怎么精确落到真实的数据库连接、真实的函数、真实的容器上。

4.1 它要解决的小问题

模型输出的是文本:"我要查 orders 表"、"我要跑这段 Python"。但数据库连接、Python 函数、知识库检索器是完全不同的东西。需要一个统一抽象,让 Agent 不管挂的是哪种能力,都用同一套接口去用;同时,AI 生成的代码不能直接在主机上跑——得隔离

4.2 思路/直觉:Resource 是统一工具箱

DB-GPT 用 Resource 抽象把所有"外部能力"统一起来(dbgpt/agent/resource/base.py:90-288)。不管是数据库、工具、知识库还是沙箱,都是一个 Resource,都实现同一组方法:

Resource (抽象,resource/base.py)
┌──────────┬──────────┬──────────┬───────────┐
│ get_prompt│ execute │ is_async │sub_resources│ ← 统一接口
└──────────┴──────────┴──────────┴───────────┘
│ │ │ │
┌────┴───┐ ┌───┴────┐ ┌───┴────┐ ┌───┴─────┐
│database│ │ tool │ │knowledge│ │ pack │
│DBResource│ │BaseTool│ │ 知识检索 │ │多资源打包 │
└────────┘ └────────┘ └─────────┘ └─────────┘

几个关键方法(resource/base.py):

  • type() / type_alias():这是什么类型的资源(ResourceType 枚举,base.py:40-)。
  • get_prompt(...)(base.py:173-):把资源描述成给模型看的 prompt——比如数据库资源会把表结构渲染成文字,塞进 system prompt 让模型知道有哪些表/字段。
  • execute(...) / async_execute(...)(base.py:227-241):真正执行。
  • sub_resources / is_pack(base.py:243-248):支持把多个资源打包(ResourcePack),Agent 可以一次挂一组。

资源怎么被 Agent 用上?在第 1 章 think 之前,_load_thinking_messages 会调 load_resource(base_agent.py:1323-1332),把资源的 get_prompt 结果注入上下文。这就是为什么 DataScientist 能"看到"表结构。

4.3 把普通函数变成工具:FunctionTool 与 @tool

最常用的资源是工具。DB-GPT 让你用装饰器把任意 Python 函数变成工具,核心是 FunctionTool(dbgpt/agent/resource/tool/base.py:140-229)和 @tool 装饰器(tool/base.py:232-)。

# 示意,基于真实 @tool API:把一个普通函数登记成工具
@tool(description="查询某城市天气")
def get_weather(city: str) -> str:
return weather_api(city)
# 之后把它作为 resource 绑到 Agent 上,模型就能"调用"它

@tool 内部做了什么(tool/base.py:240-249):把函数包成 FunctionTool,自动从函数签名和 docstring 解析出参数 schema 和描述(tool/base.py:156-162_parse_args / _parse_docstring)。所以你写好类型注解和 docstring,工具的"说明书"就自动生成了。

执行时区分同步/异步(tool/base.py:195-229):

# tool/base.py:209-211 真实片段
if self._is_async:
raise ValueError("The function is asynchronous")
return self._func(*args, **kwargs)

is_async 在构造时就用 asyncio.iscoroutinefunction(func) 判好(tool/base.py:164),调错通道直接报错——避免在同步路径里 await 一个协程这类隐蔽 bug。

4.4 一个具体资源:数据库

DBResource(dbgpt/agent/resource/database.py)是 Text-to-SQL 的关键资源。第 1 章 DataScientist 的 correctness_check 里那句 await self.database.query(sql=..., db=...)(data_scientist_agent.py:134-137),self.database 就是从挂载的资源里取出来的 DBResource(data_scientist_agent.py:92-100)。它对外暴露 dialect(方言,拼进 prompt 让模型写对方言的 SQL)和 query(执行 SQL)。

这串起了整个 Text-to-SQL 闭环:资源把表结构喂进 prompt → 模型写 SQL → 资源执行 SQL → 结果回到 verify。资源既是"信息来源"又是"执行器"。

4.5 安全执行:dbgpt-sandbox

AI 写的代码必须隔离跑。dbgpt-sandbox 包用容器做隔离,分了清晰的层(packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/):

干什么
control_layer接任务、分发(connect/configure/execute/disconnect…)
execution_layer真正的运行时:docker / podman / nerdctl / local
display_layer把执行结果/产物展示出来
user_layer对外服务接口

隔离靠容器的限额参数,创建会话时设上(execution_layer/docker_runtime.py:50-54):

# docker_runtime.py:50-54 真实片段
"mem_limit": self.config.max_memory,
"cpuset_cpus": str(self.config.max_cpus),
...
"network_disabled": self.config.network_disabled,

默认限额相当保守(sandbox/config.py:29-35):内存 256MB、CPU 50%、单次执行 30 秒、文件 10MB、最多 10 个进程。每种语言用固定的官方 slim 镜像(config.py:3-11),工作目录固定 /workspace(config.py:13)。

运行时选择是 fail-closed 的:SANDBOX_RUNTIME 不设时自动探测容器运行时,探测不到就失败关闭(config.py:45-48 注释),而不是悄悄退回"直接在主机跑"——除非你显式 SANDBOX_ALLOW_LOCAL_RUNTIME=1。这个默认值体现了"宁可不跑,也不裸跑"的安全取向。

4.6 边界与诚实

  • 隔离是"限额 + 容器"级别,不是形式化沙箱:代码里见到的是 mem/cpu/network/进程数限额和容器隔离(config.pydocker_runtime.py),没见到 seccomp profile、cap_drop、只读根文件系统这类更硬的内核级裁剪配置。对抗强对手时,这层隔离的强度取决于底层容器运行时的默认安全策略,而非 DB-GPT 额外加固。
  • local runtime 是逃生门:SANDBOX_ALLOW_LOCAL_RUNTIME 打开后代码会在主机本地跑,失去隔离——生产环境别开。
  • 工具说明书依赖 docstring:@tool 没给 description、函数又没 docstring,会直接 raise ValueError("The description is required")(tool/base.py:156-159)——逼你写说明,也意味着模型看到的工具质量取决于你的注释质量。

4.7 代码地图

主题文件路径关键符号
资源抽象dbgpt-core/.../agent/resource/base.pyResource, ResourceType, AgentResource
工具基类与装饰器dbgpt-core/.../agent/resource/tool/base.pyBaseTool, FunctionTool, tool
数据库资源dbgpt-core/.../agent/resource/database.pyDBResource
资源加载进 promptdbgpt-core/.../agent/core/base_agent.pyload_resource, generate_resource_variables
沙箱控制层dbgpt-sandbox/.../sandbox/control_layer/control_layer.pyControlLayer.handle_task
沙箱 docker 运行时dbgpt-sandbox/.../sandbox/execution_layer/docker_runtime.pyDockerSandboxSession, DockerRuntime
沙箱安全默认值dbgpt-sandbox/.../sandbox/config.pyMAX_MEMORY, SANDBOX_ALLOW_LOCAL_RUNTIME