DeepSeek Harness: 沙箱与审批

最后更新:2026-08-31

Agent 的强大在于它能执行操作,但"能执行"不等于"应该执行"。审批策略是 Agent 行为的刹车,沙箱是 Agent 行为的围栏。两者结合,让 Agent 在安全边界内自由行动。

💡 提示:审批策略是"什么时候问用户",沙箱是"在哪执行"。审批决定决策权归属,沙箱决定执行环境边界。两者独立但互补。

📋 前置知识:已完成 13-effect.md22-capability.md

1. 你将学到


2. 审批策略(approval policy)

(1) 策略模式

DSH 提供四种审批模式:

模式 行为 适用
always 始终允许,不弹窗 安全操作(read、search)
ask 需要审批,弹窗确认 危险操作(create、edit、shell)
ask_with_confirm 二次确认弹窗 极危险操作(delete、sudo)
deny 自动拒绝 绝不允许的操作(rm -rf /)

(2) ▶ 示例 2

YAML
# dsh.config.yaml
approval:
  default: ask

  tools:
    file_edit:
      read: always
      create: ask
      edit: ask
      delete: ask_with_confirm

    shell:
      safe: always
      moderate: ask
      dangerous: ask_with_confirm
      forbidden: deny

    search:
      default: always

    sandbox:
      default: ask

(3) ▶ 示例 3

YAML
approval:
  tools:
    file_edit:
      # 路径白名单自动允许
      auto_allow_paths:
        - /tmp/**
        - /workspace/**
      # 路径黑名单自动拒绝
      auto_deny_paths:
        - /etc/**
        - /var/**

    shell:
      # 命令白名单
      auto_allow_commands:
        - ls
        - cat
        - grep
        - head
        - wc
        - git status
        - git log
      # 命令黑名单
      auto_deny_commands:
        - rm -rf /*
        - mkfs
        - dd if=*

(4) ▶ 示例 4

TYPESCRIPT
import { defineTool } from '@deepseek-ai/dsh'

export default defineTool({
  name: 'db_query',
  description: 'Execute SQL query',
  parameters: { /* ... */ },
  approval: {
    level: 'ask',
    rules: [
      { match: { query: /^SELECT/i }, level: 'always' },
      { match: { query: /^DROP/i }, level: 'deny' },
      { match: { query: /^INSERT|^UPDATE|^DELETE/i }, level: 'ask_with_confirm' }
    ]
  },
  async execute({ query }, ctx) {
    return await ctx.database.query(query)
  }
})

3. 权限预设(permission presets)

(1) 内置预设

DSH 提供三个权限预设:

预设 说明 典型操作
trusted 信任模式,大部分自动允许 个人开发环境
standard 标准模式,危险操作需审批 默认配置
restricted 受限模式,严格审批 生产环境

(2) 预设对比

YAML
# trusted — 信任模式
approval:
  tools:
    file_edit: always
    shell: always
    search: always

# standard — 标准模式
approval:
  tools:
    file_edit:
      read: always
      create: ask
      edit: ask
      delete: ask_with_confirm
    shell: ask

# restricted — 受限模式
approval:
  tools:
    file_edit: ask_with_confirm
    shell: deny
    search: ask

(3) 选择预设

BASH
# 启动时选择预设
pnpm dsh web --preset trusted
pnpm dsh web --preset standard
pnpm dsh web --preset restricted

(4) 自定义预设

YAML
# dsh.config.yaml
approval:
  presets:
    my-team:
      tools:
        file_edit:
          read: always
          create: ask
          edit: ask
          delete: deny
        shell: ask

4. 危险操作审批弹窗

(1) 弹窗机制

当工具调用需要审批时,DSH 暂停执行,弹出审批弹窗:

TEXT 📖 仅展示
⚠️ Approval Required: Execute shell command

  Command: npm install bcryptjs
  Working directory: /home/alice/project
  Risk level: MODERATE
  
  [Allow] [Always for npm] [Deny]

(2) 审批选项

选项 说明
Allow 允许本次操作
Always 允许此类操作(不再弹窗)
Always for X 允许特定规则的操作
Deny 拒绝本次操作

(3) 批量审批

多个操作可以批量审批:

TEXT 📖 仅展示
⚠️ Batch Approval Required: 3 operations

  1. file_edit: create src/utils.ts
  2. file_edit: edit src/app.ts
  3. shell: npm install bcryptjs

  [Allow All] [Review Each] [Deny All]

(4) 审批日志

所有审批决策记录到日志:

TEXT 📖 仅展示
[approval] ALLOWED: file_edit(read, src/config.ts) — policy: always
[approval] ASKED: file_edit(create, src/utils.ts) — user: allowed
[approval] DENIED: shell(rm -rf /tmp/test) — policy: deny

5. 沙箱后端注册

工具执行与权限流水线

(1) 沙箱概念

沙箱是工具执行的隔离环境——Agent 的操作在沙箱内执行,不影响宿主系统:

100%
graph LR
    AGENT[Agent] -->|调用工具| SANDBOX[沙箱环境]
    SANDBOX -->|隔离执行| FS[沙箱文件系统]
    SANDBOX -->|隔离执行| SHELL[沙箱 Shell]
    SANDBOX -->|隔离网络| NET[沙箱网络]
    SANDBOX -.->|不允许| HOST[宿主系统]

(2) 沙箱后端接口

TYPESCRIPT
interface SandboxBackend {
  name: string
  execute(command: string, options: ShellOptions): Promise<ShellResult>
  readFile(path: string): Promise<string>
  writeFile(path: string, content: string): Promise<void>
  stat(path: string): Promise<FileStat>
  readdir(path: string): Promise<DirEntry[]>
}

(3) 注册沙箱后端

TYPESCRIPT
import { Service, Context } from '@deepseek-ai/cordis'

export default class DockerSandboxBackend extends Service {
  constructor(ctx: Context) {
    super(ctx, 'sandbox')
    
    ctx.implement(SandboxCapability, {
      name: 'docker-sandbox',
      
      async execute(command, options) {
        const container = await this.getContainer()
        const result = await container.exec(command, options)
        return result
      },

      async readFile(path) {
        const container = await this.getContainer()
        return await container.readFile(path)
      },

      async writeFile(path, content) {
        const container = await this.getContainer()
        await container.writeFile(path, content)
      },

      // ...
    })
  }
}

(4) 配置沙箱

YAML
# dsh.config.yaml
sandbox:
  backend: docker
  config:
    image: dsh-sandbox:latest
    workdir: /workspace
    memory: 512m
    cpus: 1
    timeout: 30000
    network: none

6. ctx.sandbox 与 ctx.shell

(1) ctx.sandbox

ctx.sandbox 提供沙箱化的文件操作:

TYPESCRIPT
export const inject = ['sandbox']

export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
    name: 'sandbox_read',
    description: 'Read file in sandbox',
    parameters: {
      type: 'object',
      properties: {
        path: { type: 'string', description: 'File path in sandbox' }
      },
      required: ['path']
    },
    async execute({ path }, ctx) {
      const content = await ctx.sandbox.readFile(path)
      return { content }
    }
  }))
}

(2) ctx.shell

ctx.shell 在沙箱内执行命令:

TYPESCRIPT
export const inject = ['shell']

export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
    name: 'sandbox_exec',
    description: 'Execute command in sandbox',
    parameters: {
      type: 'object',
      properties: {
        command: { type: 'string', description: 'Command to execute' }
      },
      required: ['command']
    },
    async execute({ command }, ctx) {
      const result = await ctx.shell.execute(command, {
        cwd: '/workspace',
        timeout: 30000
      })
      return {
        stdout: result.stdout,
        stderr: result.stderr,
        exitCode: result.exitCode
      }
    }
  }))
}

(3) sandbox vs 直接 fs

操作 直接 fs ctx.sandbox
文件路径 宿主系统路径 沙箱内路径
权限 宿主用户权限 沙箱用户权限
隔离 完全隔离
性能 稍慢(经过沙箱层)

(4) 安全使用原则

TYPESCRIPT
// ❌ 直接操作宿主文件系统
import { readFileSync } from 'fs'
const content = readFileSync('/etc/passwd')

// ✅ 通过沙箱操作
const content = await ctx.sandbox.readFile('/etc/passwd')
// → 如果沙箱配置了文件系统隔离,这个调用会被限制

7. 远程沙箱配置

(1) 远程沙箱架构

100%
graph TB
    DSH[DSH Agent] -->|HTTP API| API[Sandbox API Server]
    API -->|管理| CONTAINER1[容器 1<br/>Agent A]
    API -->|管理| CONTAINER2[容器 2<br/>Agent B]
    CONTAINER1 --> FS1[隔离文件系统 1]
    CONTAINER2 --> FS2[隔离文件系统 2]

(2) 配置远程沙箱

YAML
# dsh.config.yaml
sandbox:
  backend: remote
  config:
    endpoint: http://sandbox-server:8080
    apiKey: sk-sandbox-xxx
    defaultImage: dsh-sandbox:latest
    maxContainers: 10
    containerTimeout: 3600
    allowedImages:
      - dsh-sandbox:latest
      - dsh-sandbox-python:latest

(3) 远程沙箱后端实现

TYPESCRIPT
export default class RemoteSandboxBackend extends Service {
  private endpoint: string
  private apiKey: string

  constructor(ctx: Context) {
    super(ctx, 'sandbox')
    this.endpoint = ctx.config.endpoint
    this.apiKey = ctx.config.apiKey
  }

  async execute(command: string, options: ShellOptions): Promise<ShellResult> {
    const response = await fetch(`${this.endpoint}/execute`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${this.apiKey}`
      },
      body: JSON.stringify({ command, ...options })
    })
    return await response.json()
  }

  async readFile(path: string): Promise<string> {
    const response = await fetch(`${this.endpoint}/read`, {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${this.apiKey}` },
      body: JSON.stringify({ path })
    })
    const data = await response.json()
    return data.content
  }

  // ...
}

(4) 沙箱生命周期

TEXT 📖 仅展示
1. Agent 会话创建 → 请求沙箱容器
2. 沙箱 API 创建容器 → 返回容器 ID
3. Agent 操作在容器内执行
4. Agent 会话销毁 → 请求销毁容器
5. 沙箱 API 销毁容器 → 释放资源

❓ 常见问题

Q 审批弹窗会阻塞 Agent 吗?
A 会。Agent 等待用户审批后才会继续执行。这是有意设计——防止 Agent 在用户不知情时执行危险操作。
Q 可以跳过审批弹窗吗?
A 使用 --preset trusted 可以自动允许大部分操作。但不建议在生产环境使用。
Q 沙箱和 Docker 是什么关系?
A Docker 是沙箱的一种实现方式。DSH 的沙箱后端接口是通用的,可以用 Docker、gVisor、远程服务器等任何实现。
Q 没有配置沙箱时会怎样?
A 工具直接在宿主系统上执行。相当于"无沙箱"模式——Agent 有完整的宿主权限。
Q 远程沙箱的延迟大吗?
A 取决于网络和沙箱实现。通常文件操作 10-50ms,命令执行 100-500ms(含启动开销)。
Q 如何审计审批决策?
A 查看审批日志(approval log),记录了每次审批决策的时间、操作、策略和用户选择。 ---

📖 小节


📝 作业

1. ⭐ 基础题:配置 DSH 使用 standard 预设,尝试让 Agent 执行 ls(自动允许)和 rm(需审批),观察审批弹窗行为。

2. ⭐⭐ 进阶题:为自定义工具添加审批规则——SELECT 查询自动允许,INSERT/UPDATE/DELETE 需审批,DROP 自动拒绝。测试每种 SQL 类型的审批行为。

3. ⭐⭐⭐ 挑战题:实现一个简单的沙箱后端(用子进程隔离),注册到 DSH 中。让 Agent 在沙箱内执行命令,验证:1) 文件操作限制在沙箱目录内;2) 网络请求被阻断;3) 沙箱销毁后文件被清理。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏