AI 是确定性房子中的非确定性访客:停止构建聊天机器人,开始构建沙盒

发布: (2026年5月4日 GMT+8 09:16)
7 分钟阅读
原文: Dev.to

Source: Dev.to

AI 是确定性房子里的非确定性客人:停止构建聊天机器人,开始构建沙盒

Kowshik Jallipalli

信号:具有法律约束力的幻觉

最近,一家大型航空公司的客户支持聊天机器人“幻觉”出了一条关于丧葬票价政策的错误信息。客户要求退款,航空公司拒绝,仲裁庭裁定支持客户。该聊天机器人被认定为公司的法律代理人。

问题不在于大型语言模型产生了幻觉,而在于它被允许 直接 与客户以及数据库对话,且没有任何监管。当你让一个非确定性的访客在没有约束的情况下进入你的确定性系统时,你将对由此引发的灾难承担法律和财务责任。

我们必须停止把 AI 当作开放式的“聊天”界面来使用,而应把它视为 不可信、极易波动的代码执行

第 1 阶段:架构赌注

  • 供应商陷阱Chat Completion API
    它鼓励你构建开放的文本框,让用户可以询问任何内容,AI 也会返回任何内容。它依赖 system prompts 来强制行为——就像让小偷在离开时请把门锁上。

  • 所有权路径Isolate Sandbox
    我们不需要对话者;我们需要一个函数,接受输入,在加密和内存硬化的环境中运行,并输出严格类型的负载,我们会在它触及主线程之前进行验证。

第2阶段:安全审计(为何您当前的沙箱是风险)

上周我提议使用原生 Node.js vm 模块 来对代理输出进行沙箱隔离。我们的首席 QA 与安全测试员把该 Pull Request 撕得粉碎。以下是迫使我们进行架构重写的审计报告:

高级测试员审计报告

严重性问题详情
严重沙箱逃逸原生 Node.js vm 模块 不是 安全边界。官方文档明确指出:“不要用它来运行不可信的代码。”LLM 可以轻易产生原型污染攻击,遍历原型链,并在宿主机器上执行远程代码执行(RCE)。
严重事件循环拒绝服务vm.runInContext 在主线程上运行。如果 LLM 生成一个简单的 while(true){} 循环,它将完全阻塞 Node.js 事件循环。您的服务器会立即丢失所有活跃的用户连接。
状态损坏将活对象(例如数据库连接)传入 VM 上下文会允许代理全局修改它们。

结论: 我们不能使用原生 Node.js 工具。必须降级到 C++ V8 引擎 级别。

第三阶段:生产实现(V8 隔离)

为了构建真正的 “Boss Battle” 竞技场,我们使用 isolated‑vm。它会创建一个完全独立的 V8 JavaScript 引擎实例,拥有自己的内存堆。如果 AI 触发无限循环或尝试逃逸,我们可以直接终止该 isolate 线程,而不会影响主 Node.js 服务器。

const ivm = require('isolated-vm');
const { trace } = require('@opentelemetry/api');

const tracer = trace.getTracer('ai.hardened_sandbox');

class FortressSandbox {
  constructor(memoryLimitMB = 64, timeoutMs = 1500) {
    this.memoryLimitMB = memoryLimitMB;
    this.timeoutMs = timeoutMs;
  }

  async executeUntrustedAgent(aiGeneratedLogic, safeInputPayload) {
    return tracer.startActiveSpan('v8_isolate_execution', async (span) => {
      // 1. Hard Boundary – create a separate V8 heap
      const isolate = new ivm.Isolate({ memoryLimit: this.memoryLimitMB });
      const context = isolate.createContextSync();
      const jail = context.global;

      try {
        // 2. State Management – pass data as deeply cloned strings, NEVER by reference
        jail.setSync('global', jail.derefInto());
        jail.setSync('_inputData', JSON.stringify(safeInputPayload));

        // 3. Compile the Agent's logic
        const script = isolate.compileScriptSync(`
          // Agent must parse input, do its logic, and return a stringified result
          const input = JSON.parse(_inputData);
          let output = {};

          ${aiGeneratedLogic}

          JSON.stringify(output);
        `);

        // 4. Dead‑Man’s Switch – run with strict timeout
        // If it loops infinitely, the isolate is terminated. Main thread survives.
        const resultStr = script.runSync(context, { timeout: this.timeoutMs });

        span.setAttribute('sandbox.status', 'success');
        return JSON.parse(resultStr);
      } catch (error) {
        span.recordException(error);
        span.setAttribute('sandbox.status', 'terminated');
        // The guest tried to burn the house down. The house won.
        return {
          error: `GUARD INTERVENTION: Agent execution terminated. Reason: ${error.message}`
        };
      } finally {
        // 5. Memory Cleanup – destroy the arena
        isolate.dispose();
        span.end();
      }
    });
  }
}

// Example Usage:
// const fortress = new FortressSandbox();
// const output = await fortress.executeUntrustedAgent(
//   "output.action = 'refund'; output.amount = input.amount;",
//   { amount: 500 }
// );

已清理的 Markdown 结束。

第四阶段:检查清单(接下来要构建的内容)

  • 实现 Zod 出口过滤
    FortressSandbox 的输出在代码执行层面是安全的,但数据仍然不可信。将输出直接传入 Zod 模式验证器。如果验证失败,丢弃请求。

  • 基于尾部的 OTel 采样
    沙箱会经常失败(设计如此)。配置你的 OpenTelemetry 收集器,仅对 sandbox.status === 'terminated' 保存完整的追踪跨度,以降低 Datadog/Honeycomb 成本。

  • 多代理防火墙
    如果 Agent A 将数据传递给 Agent B,必须在中间进行模式检查。绝不让两个代理共享同一个 V8 隔离内存空间。

结论:
将 LLM 输出视为 1999 年公共互联网的用户输入。对其进行清理、隔离,并默认假设它们是恶意的。构建防护。限制来客。

0 浏览
Back to Blog

相关文章

阅读更多 »

让客户交接轻松的文件夹结构

每家机构都有这样一个版本的故事:团队成员离职、客户升级,或者你在替病假的同事顶班——于是你花了20分钟去搜索……

2026年 ATS 筛选软件实际检查的内容

概述:大多数你在网上找到的“ATS‑friendly CV”建议都可以追溯到2017年。2026年的现代 applicant tracking systems(ATS)远不止简单的关键词匹配……