不信任 Agent,也能开放数据问答:Noisegate 用差分隐私把查询权限关进可审计边界
让 AI Agent 查询业务数据,最危险的误解是:只要模型“遵守提示词”,就能把数据库访问权交出去。提示词注入、错误推理和蓄意的多轮试探都会打破这个前提。即使每次只允许它执行看似无害的聚合查询,攻击者也可以把两次仅相差一条记录的结果相减,恢复某个人的敏感值;反复提问还可能把随机噪声平均掉。
Noisegate 是 Yash Mahajan 开发的 Apache-2.0 开源项目,定位为面向不可信 AI Agent 的差分隐私数据分析网关。它以 Python 3.13+ 实现,组合 DuckDB、FastAPI、Streamlit 和 MCP SDK。核心立场很明确:LLM 只负责把自然语言转换为受限的查询提议,不能自行扩大权限;真正执行查询、检查策略、添加噪声和扣减隐私预算的逻辑位于模型之外的确定性信任边界中。
这使它适合需要让 Claude Desktop 或其他 MCP 客户端查看统计趋势、但又不能读取原始记录的场景:例如在测试数据或已授权的数据集上询问分布、均值和汇总,而不是把一张可任意查询的 SQL 表暴露给 Agent。
还要区分两类目标:访问控制回答“谁可以发起请求”,差分隐私回答“即使请求被允许,单个样本能从结果中获得多少保护”。前者仍不可缺少——没有认证、授权和网络边界,任何人都可能消耗预算或试探策略;后者则补上了仅靠 RBAC 无法覆盖的统计泄露问题。两层结合时,身份系统负责把用户、服务账户或工作流绑定到明确的额度,数据策略负责缩小可见列与运算,差分隐私机制负责限制每次及多次回答累计释放的信息。
对 AI Agent 而言,这种分层尤其有意义。Agent 的目标会在长会话中变化,工具调用参数也可能来自网页、工单或其他不可信输入。与其试图在每个提示词里预先列举“不能问什么”,不如让 Agent 即使偏离预期,也只能提交受 schema 约束的统计请求;越过字段、筛选范围或预算的请求由模型外的程序拒绝。这样并没有消除业务侧的数据分级责任,却把一部分安全结论从概率性的模型服从,转成了可以单元测试和审计的执行规则。
威胁不只来自一条危险 SQL
普通的只读权限不足以解决隐私问题。假设 Agent 可以请求“部门 X 所有人的收入总和”,再请求“部门 X 中除 Alice 外所有人的收入总和”。两条请求都只是聚合,二者的差却可能正好是 Alice 的收入。这类差分攻击不依赖 DELETE、UPDATE 或裸露的行级查询。
Noisegate 因此没有让模型生成自由 SQL。README 描述的路径可概括为三层:不可信的 LLM 编译器产出受约束的查询 AST;验证与策略层检查公开列、聚合操作和取值范围;最后由差分隐私引擎在受跟踪的预算内执行,并返回带置信区间的扰动结果。模型可以建议问题,却无法通过输出改变后两层的规则。
项目还将攻击示例纳入测试:差分攻击、成员推断和重识别攻击都会分别在关闭隐私保护时复现、在开启保护时被压制。这里重要的不是把“安全”写进 README,而是把会失败的攻击路径变成 CI 可以回归检查的行为。
MCP 接口为什么只暴露少量结构化工具
Noisegate 可作为 stdio MCP server 接入客户端。其工具面不是通用数据库接口,而是 count、sum、average、histogram 和 get_budget 五类结构化操作;参数 schema 根据数据集策略生成,只允许策略中公布的列和与列类型相符的聚合。
本地安装入口时,官方文档使用的是可编辑安装:
python3 -m venv venv source venv/bin/activate pip install -e .
安装后,noisegate 是 MCP server 的命令行入口,dp-mcp 保留为兼容别名。以下是官方 macOS Claude Desktop 配置结构的缩略示例;路径必须替换为本机的绝对路径:
{
"mcpServers": {
"noisegate": {
"command": "/absolute/path/to/venv/bin/noisegate",
"cwd": "/absolute/path/to/llm-dp",
"env": {
"DP_IDENTITY": "demo"
}
}
}
}
这个例子没有配置 ANTHROPIC_API_KEY,因为连接的 Agent 本身就是发起问题的一方。若使用自然语言 Web/API 界面,才需要该 key 作为“不可信自然语言到查询”的编译器凭据。两种入口都不改变执行边界:MCP 客户端获得的是受限工具,而不是数据文件、DuckDB shell 或任意 SQL 执行器。
策略、身份和预算要一起设计
差分隐私不是简单给数字加一点随机数。Noisegate 的策略文件决定可查询的数据集和公开字段;预算则按 identity、epoch 与数据集组合持久化。默认预算库位于 data/budget.db,审计日志默认写入 data/audit.log。服务重启不会恢复已经消费的预算;若确实需要新一轮额度,文档建议有意识地提高 DP_EPOCH,而非删除状态文件。
项目默认的总 ε 为 5.0、单次查询 ε 为 0.05,可通过 DP_TOTAL_EPSILON 与 DP_PER_QUERY_EPSILON 调整。ε 越小,单次答案通常越模糊、保护越强;但不能只看单次精度。预算机制还阻止同一身份无限重复提问来稀释噪声,余额不足时应返回明确的拒绝结果,而不是悄悄给出更不可靠的数据。
一个实用的部署准则是:不要把 DP_IDENTITY=demo 原样搬到多人或对外环境。身份必须由可信的上层认证系统绑定;否则不同使用者可能共享预算,也无法可靠追责。README 也明确将随项目提供的 HTTP + Streamlit 界面定义为本机、单租户 demo:它使用可伪造的 X-Identity 请求头,不能被视为公网多租户认证边界。
用患者样例验证“拒绝”是否真实发生
在接入真实数据前,可以先运行仓库的攻击样例。20 行的 synthetic patients 数据随仓库提供,无须下载外部数据:
python -m attacks.patients_alice
该演示用 5.0 的总预算和每次 0.25 的 ε,展示一个典型陷阱:将年龄限制到过窄区间的查询会被过滤器拒绝,且拒绝发生在执行前,不消耗预算。对于两个宽泛但相减后可能指向单人的直方图查询,系统仍给出经噪声处理的结果;而在连续请求耗尽预算后,下一次调用返回 kind: "refusal",不是鼓励客户端重试的普通错误。
这也是上线验收应检查的内容。不要只验证 Agent 能不能看到图表,还要记录:不在策略中的列是否无法出现;窄筛选是否在引擎前被拦截;同一身份重连后预算是否仍被保留;预算耗尽时客户端是否正确处理拒绝。只有这些负向路径被实际验证,MCP 配置才称得上安全边界,而不只是一个更漂亮的数据库代理。
本地服务适合演示,不等于可直接公网部署
如果需要完整的自然语言 UI,官方给出的本地启动方式是:
export ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" docker compose up
该 compose 组合会启动 API 与 UI,并持久化数据集和预算存储;UI 默认在 8501 端口。不过,容器化解决的是可重复启动,不会自动补齐身份认证、TLS、网络隔离、密钥管理、审计留存策略或针对真实数据的策略审查。特别是当数据包含个人、医疗、财务或内部经营信息时,应由数据所有者定义字段范围、取值界限和预算;不要让 Agent 开发者自行决定哪些列“看起来安全”。
Noisegate 最有价值的地方,不是让 Agent 获得更多数据权限,而是把“它能问什么、会泄露多少、何时必须停止”从模型行为中剥离出来,落到可测试的工程组件上。对已有 MCP 工作流的团队而言,先在合成数据上复现攻击和拒绝,再把最小策略接入一个只读分析场景,比直接将生产库包装成自然语言问答更稳健。