☰
DatI:让AI Agent安全访问数据库的中间层工具设计与实践
2026/10/15 9:36:13 网站建设 项目流程

1. 为什么我要花大半年业余时间做 DatI 这件事

先说结论:DatI 是一个让 AI Agent 能够安全、结构化地访问数据库的中间层工具。它不是 ORM,不是数据库客户端,也不是又一个 MCP 协议的封装壳子。它解决的核心问题是——当你的 Agent 需要查数据、改数据、做分析的时候,怎么让它既能干活,又不会把生产库搞崩。

这个项目从去年冬天开始动手,到今年夏天基本能用,前后搭进去大半年的业余时间。工作日晚上写两小时代码,周末抽半天调试,中间还因为一次误操作把测试库的几张表结构改乱了,不得不从头搭环境。为什么要做这个?因为我在实际工作中反复遇到同一个场景:手头有一堆数据相关的重复劳动,想让 Agent 帮忙自动化,但每次一到数据库这一层就卡住。要么是 Agent 生成的 SQL 不敢直接跑,要么是权限控制太粗放,要么是返回的数据格式乱七八糟没法直接用。

市面上不是没有方案。MCP 协议出来之后,各种数据库连接器层出不穷,dbx 数据库工具也在往这个方向靠。但我试了一圈下来,发现几个共性问题:第一,大多数工具只解决了“连得上”的问题,没解决“管得住”的问题;第二,权限模型要么太简单(一个连接一把梭),要么太复杂(要写一堆配置文件);第三,对 Agent 的友好度不够,返回的数据结构没有针对 LLM 的消费习惯做优化。

DatI 的设计目标很明确:让 Agent 用最自然的方式访问数据库,同时让开发者用最省心的方式控制风险。它支持 MySQL、PostgreSQL、SQLite 这几种最常见的数据库,通过一套统一的接口暴露给 Agent,底层做连接池管理、查询审计、结果集裁剪和敏感字段脱敏。你可以把它理解成 Agent 和数据库之间的一个“智能网关”。

适合谁来参考?如果你正在做 AI Agent 相关的项目,尤其是涉及数据查询、报表生成、自动化运维这类场景,DatI 的思路和实现细节应该对你有用。如果你只是好奇 MCP 是什么、Agent 怎么接数据库,这篇文章也会从最基础的概念讲起,保证你能看懂。

2. 整体架构设计与技术选型背后的取舍

2.1 为什么不用现成的 MCP 数据库连接器

MCP 协议刚出来的时候,我第一时间试了官方和社区的几个数据库连接器。说实话,能用,但不够好用。问题出在几个地方:

第一,连接管理太粗糙。大多数实现是每次请求新建连接,或者用一个全局连接。前者在并发场景下直接把数据库连接数打满,后者在长时间运行后容易遇到连接超时的问题。Agent 的调用模式跟人类用户完全不一样,它可能在一分钟内发起几十次查询,也可能半小时不动。这种脉冲式的访问模式,需要一套专门的连接池策略。

第二,权限控制粒度不够。现成的连接器通常只支持数据库级别的权限,最多到表级别。但实际场景中,我需要的是列级别的控制。比如用户表里的手机号、邮箱这些字段,Agent 可以查,但返回的时候必须脱敏。再比如某些敏感表,Agent 只能读不能写,而且读的时候必须带特定的过滤条件。

第三,返回结果没有针对 LLM 优化。数据库查出来的原始结果集,列名可能是下划线命名,时间格式是数据库默认的,空值处理也不统一。Agent 拿到这些数据后,往往需要额外的处理步骤。DatI 在返回结果之前会做一层标准化:列名转成驼峰或自然语言描述,时间统一成 ISO 格式,空值转成空字符串或 null 的明确表示。

基于这些考虑,我决定自己写一套。不是重复造轮子,而是现有的轮子跟我的需求不匹配。

2.2 核心架构:三层分离

DatI 的架构分成三层,每一层职责清晰:

接入层负责跟 Agent 通信。这一层实现了 MCP 协议的服务端,同时也提供 HTTP API 作为备选。Agent 可以通过 MCP 的工具调用方式来使用 DatI,也可以直接发 HTTP 请求。接入层还负责请求的鉴权和限流,防止 Agent 因为 bug 或者提示词问题发起大量请求。

逻辑层是核心。它包含查询解析、权限校验、SQL 改写、结果处理这几个模块。查询解析模块会分析 Agent 发来的请求,判断是读操作还是写操作,涉及哪些表和列。权限校验模块根据配置的规则决定是否放行。SQL 改写模块会在原始 SQL 上追加安全限制,比如自动加上 LIMIT、自动注入租户 ID 过滤条件。结果处理模块负责脱敏、格式化和裁剪。

数据层管理数据库连接。每种数据库类型对应一个连接池实现,连接池的参数可以根据数据库的负载能力动态调整。数据层还负责事务管理,确保写操作的原子性。

这三层之间通过明确的接口通信,每一层都可以独立替换。比如你不想用 MCP,只想用 HTTP API,只需要替换接入层。你想支持一种新的数据库,只需要在数据层增加一个实现。

2.3 技术栈选择:Rust + TypeScript

后端核心用 Rust 写,接入层和部分工具用 TypeScript。这个选择当时纠结了很久。

Rust 的优势在于性能和内存安全。数据库连接池这种需要长时间运行、处理大量并发请求的组件,Rust 的零成本抽象和所有权模型能避免很多潜在问题。而且 Rust 的异步生态已经比较成熟,tokio 加 sqlx 的组合用起来很顺手。sqlx 支持编译期 SQL 检查,虽然 DatI 的场景下 SQL 是动态生成的,用不上这个特性,但它的连接池管理和类型映射做得很好。

TypeScript 用在接入层,主要是因为 MCP 的官方 SDK 是 TypeScript 的,用 TS 写接入层可以少踩很多协议兼容性的坑。另外,配置文件的解析和校验也用 TS 写,因为这部分逻辑经常变,TS 的开发效率更高。

两层之间通过 gRPC 通信。选 gRPC 而不是 REST,是因为接入层和逻辑层之间的调用频率很高,gRPC 的二进制协议和流式支持更合适。而且 protobuf 的接口定义文件本身就是很好的文档,改接口的时候不容易漏掉地方。

这个技术栈组合的代价是构建复杂度上去了。Rust 的编译时间不短,每次改完逻辑层都要等编译。TypeScript 那边倒是快,但类型检查在大型项目里也会变慢。我的解决办法是把逻辑层拆成多个 crate,只编译改动的部分。接入层用 esbuild 做打包,速度可以接受。

3. 核心细节解析:Agent 访问数据库的五个关键问题

3.1 连接池管理:让 Agent 的脉冲式访问不炸库

Agent 访问数据库的模式跟人类用户完全不同。人类用户是打开客户端,连着用一段时间,然后关掉。Agent 是来无影去无踪,可能突然发起一波查询,然后沉寂很久。这种模式下,传统的连接池策略会出问题。

如果连接池设得太小,Agent 并发查询的时候会排队等待,响应变慢。如果设得太大,数据库那边可能承受不住。而且 Agent 的查询往往比较重,一个不带索引的查询可能跑几十秒,占着连接不放。

DatI 的连接池策略是这样的:每个数据库实例维护一个最小连接数和最大连接数。最小连接数保持常驻,确保低延迟。最大连接数根据数据库的负载能力设置,MySQL 一般不超过 50,PostgreSQL 可以到 100。关键是加了一个“查询超时”机制:任何查询超过配置的时间(默认 30 秒)就会被强制中断,连接归还到池子里。

还有一个细节:Agent 的查询往往有重复性。比如它可能在一次对话中反复查同一张表的同一批数据。DatI 在逻辑层加了一个短期的查询缓存,同样的 SQL 和参数在 5 秒内重复请求,直接返回缓存结果。这个缓存不是为了提高性能,而是为了防止 Agent 因为提示词问题陷入循环查询。

注意:查询缓存只对读操作生效,写操作永远不走缓存。而且缓存键包含了数据库连接信息和用户上下文,避免不同权限的 Agent 拿到不该看的数据。

3.2 权限模型:列级别的精细控制

权限控制是 DatI 最花心思的部分。我设计了一套基于“策略”的权限模型,每条策略包含这几个要素:

  • 主体:哪个 Agent 或哪个 API Key
  • 资源:哪个数据库、哪张表、哪些列
  • 操作:读、写、删、改
  • 条件:额外的过滤条件,比如只能查自己创建的数据
  • 效果:允许或拒绝

这套模型参考了 AWS IAM 的思路,但做了简化。配置用 YAML 写,比如:

policies: - name: "agent-readonly-users" subject: "agent:report-bot" resource: "mysql://prod/users" actions: ["read"] columns: ["id", "name", "created_at"] effect: "allow" - name: "agent-deny-sensitive" subject: "agent:*" resource: "mysql://prod/users" columns: ["phone", "email", "id_card"] effect: "deny"

这个配置的意思是:report-bot 这个 Agent 可以读 users 表的 id、name、created_at 三列,但所有 Agent 都不能读 phone、email、id_card 这三列。deny 策略的优先级高于 allow,所以即使某个 Agent 有读整张表的权限,敏感列依然会被拦住。

权限校验发生在 SQL 执行之前。DatI 会解析 Agent 发来的 SQL,提取出涉及的表和列,然后逐条匹配策略。如果发现请求了被 deny 的列,直接返回错误,不会去查数据库。如果请求了没被 allow 的列,也会被拒绝。

这个模型的优点是灵活。你可以给不同的 Agent 配置不同的权限,也可以给同一 Agent 在不同数据库上配置不同权限。缺点是配置起来有点繁琐,尤其是表很多的时候。我后来加了一个“权限模板”功能,可以把一组策略保存为模板,应用到多个 Agent 上。

3.3 SQL 改写:自动加 LIMIT 和租户过滤

Agent 生成的 SQL 有时候很危险。比如它可能生成一个不带 WHERE 条件的 DELETE,或者一个不带 LIMIT 的 SELECT。DatI 在 SQL 执行之前会做一层改写,加上必要的安全限制。

对于 SELECT 语句,如果没带 LIMIT,自动加上 LIMIT 1000。这个数字可以配置,默认是 1000。为什么是 1000?因为 Agent 处理大量数据的能力有限,返回太多数据反而会拖慢它的响应。而且大多数 Agent 场景下,它只需要看几条样本数据就能做出判断。

对于 UPDATE 和 DELETE 语句,如果没带 WHERE 条件,直接拒绝执行。这个规则很硬,没有例外。我踩过的坑是:有一次测试的时候,Agent 生成了一个没有 WHERE 的 DELETE,把测试表清空了。虽然只是测试库,但那种感觉很不爽。从那以后,这条规则就写死在代码里了。

租户过滤是另一个重要的改写。如果你的系统是多租户的,Agent 查询的时候必须带上租户 ID。DatI 可以从请求的上下文里提取租户 ID,然后自动注入到 SQL 的 WHERE 条件里。这样即使 Agent 生成的 SQL 忘了加租户过滤,也不会查到别的租户的数据。

SQL 改写用了一个轻量级的 SQL 解析器。没有用完整的 SQL 解析库,因为那些库太重了,而且对某些数据库的方言支持不好。我自己写了一个基于正则和状态机的解析器,能识别常见的 SQL 结构。虽然不够完美,但覆盖了 95% 的场景。

3.4 结果集处理:脱敏、格式化和裁剪

Agent 拿到数据库返回的原始结果集后,往往需要额外的处理。DatI 在返回结果之前做三件事:

脱敏:根据权限策略,对敏感列进行脱敏。手机号保留前三位和后四位,中间用星号代替。邮箱保留 @ 前面的前两个字符,后面用星号代替。身份证号只保留前六位和后四位。脱敏规则可以配置,也支持自定义函数。

格式化:把数据库的原始格式转成对 LLM 友好的格式。时间统一成 ISO 8601 格式,比如 2025-06-15T14:30:00Z。布尔值统一成 true/false。空值统一成 null。列名从下划线命名转成驼峰命名,或者保留原始列名但附加一个描述字段。

裁剪:如果结果集的行数超过配置的最大值,只返回前 N 行,并在响应里标注“结果已截断”。如果某一列的内容特别长(比如 TEXT 类型的字段),超过配置的长度就截断,并标注“内容已截断”。

这些处理看起来简单,但实际做的时候有很多细节。比如时间格式化,不同数据库返回的时间格式不一样,MySQL 返回的是字符串,PostgreSQL 返回的是 timestamp 对象,SQLite 返回的是 Unix 时间戳。需要针对每种数据库做适配。

3.5 审计日志:每一次查询都要留痕

Agent 的查询必须留痕,这是底线。DatI 的审计日志记录了每一次查询的完整信息:谁发起的、什么时候发起的、查了哪个数据库、执行了什么 SQL、返回了多少行、耗时多少、有没有被权限拦截。

日志的存储用了 SQLite,因为部署简单,不需要额外的服务。日志表的结构设计得比较紧凑,每条记录大概 500 字节。按每天 10 万次查询算,一年的日志大概 18GB,完全可以接受。

审计日志的用途有几个:排查问题的时候可以回溯 Agent 到底做了什么;分析 Agent 的行为模式,优化权限策略;满足合规要求,有些场景下必须保留数据访问记录。

日志的查询接口也做了权限控制,只有管理员才能查。而且日志本身也做了脱敏,SQL 里的敏感参数会被替换成占位符。

4. 实操过程:从零搭建一个 DatI 实例

4.1 环境准备与依赖安装

DatI 的部署比较简单,核心是一个二进制文件加一个配置文件。二进制文件可以从源码编译,也可以用预编译的版本。我建议从源码编译,因为这样可以根据自己的需求调整编译参数。

编译需要 Rust 工具链和 Node.js。Rust 用 rustup 安装,Node.js 用 nvm 管理。具体的版本要求:Rust 1.75 以上,Node.js 18 以上。

# 安装 Rust curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # 安装 Node.js(用 nvm) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash nvm install 18 nvm use 18

编译过程分两步:先编译 Rust 核心,再编译 TypeScript 接入层。

# 编译 Rust 核心 cd dati-core cargo build --release # 编译 TypeScript 接入层 cd ../dati-gateway npm install npm run build

编译完成后,核心二进制在dati-core/target/release/dati-core,接入层在dati-gateway/dist/。

4.2 配置文件详解

DatI 的配置文件是 YAML 格式,分成几个部分:数据库连接、权限策略、审计日志、服务端口。

数据库连接的配置:

databases: - name: "prod-mysql" type: "mysql" url: "mysql://user:password@localhost:3306/prod" pool: min_connections: 5 max_connections: 50 idle_timeout: 300 query_timeout: 30 - name: "analytics-pg" type: "postgres" url: "postgres://user:password@localhost:5432/analytics" pool: min_connections: 2 max_connections: 20 idle_timeout: 600 query_timeout: 60

pool下面的参数需要根据数据库的实际情况调整。min_connections是常驻连接数,设得太小会导致冷启动慢,设得太大浪费资源。max_connections是上限,MySQL 一般不超过数据库的max_connections的 10%。query_timeout是查询超时时间,单位秒,超过这个时间的查询会被强制中断。

权限策略的配置前面已经举例了,这里补充一点:策略的匹配顺序是从上到下,第一条匹配的策略决定结果。所以要把 deny 策略放在 allow 策略前面。

审计日志的配置:

audit: enabled: true storage: "sqlite" path: "./data/audit.db" retention_days: 90 log_sql: true log_params: false

log_params设为 false 是为了避免把敏感参数写进日志。如果确实需要记录参数,可以设为 true,但要确保日志文件的访问权限足够严格。

服务端口的配置:

server: mcp_port: 8080 http_port: 8081 auth: type: "api_key" keys: - "sk-xxxxxxxx"

MCP 和 HTTP 用不同的端口,避免冲突。API Key 的鉴权方式比较简单,适合内部使用。如果需要更复杂的鉴权,可以接 OAuth 或者 JWT。

4.3 启动与验证

配置写好后,启动服务:

# 启动核心 ./dati-core --config ./config.yaml # 启动接入层 node ./dati-gateway/dist/index.js --config ./config.yaml

启动后,可以用 curl 测试 HTTP 接口:

curl -X POST http://localhost:8081/query \ -H "Authorization: Bearer sk-xxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "database": "prod-mysql", "sql": "SELECT id, name FROM users LIMIT 10" }'

如果返回了数据,说明基本链路通了。如果返回权限错误,检查权限策略配置。如果返回连接错误,检查数据库连接配置。

MCP 接口的测试需要用一个 MCP 客户端。我一般用 Claude Desktop 或者自己写的一个小脚本。MCP 的工具定义里,DatI 暴露了这几个工具:query、execute、list_databases、describe_table。Agent 可以通过这些工具来操作数据库。

4.4 接入 Agent 的实操步骤

把 DatI 接入 Agent 的过程分几步:

第一步,在 Agent 的配置里注册 DatI 的 MCP 服务。不同的 Agent 框架配置方式不一样,但核心都是提供 MCP 服务的地址和鉴权信息。

第二步,给 Agent 写提示词,告诉它什么时候用 DatI。提示词里要说明 DatI 的能力边界,比如“你可以查询数据库,但只能读,不能写”、“查询结果最多返回 1000 行”。

第三步,测试。先让 Agent 做一个简单的查询,比如“查一下 users 表有多少条记录”。观察 Agent 生成的 SQL 和 DatI 的返回结果。如果 Agent 生成的 SQL 有问题,调整提示词。

第四步,配置权限策略。根据 Agent 的实际需求,收紧或放宽权限。一开始可以给宽一点的权限,观察一段时间后再收紧。

实操心得:Agent 的提示词里一定要强调“先查表结构再查数据”。很多 Agent 会直接生成 SQL,结果因为列名不对而失败。让 Agent 先调describe_table拿到表结构,再生成 SQL,成功率会高很多。

5. 常见问题与排查技巧实录

5.1 连接池耗尽怎么办

这是最常见的问题。表现是 Agent 的查询请求一直排队,响应时间越来越长,最后超时。

排查思路:先看 DatI 的日志,确认是不是连接池满了。日志里会记录当前活跃连接数和等待队列长度。如果活跃连接数等于max_connections,说明池子满了。

解决方法有几个:临时调大max_connections,但要注意数据库那边的承受能力。检查是否有慢查询占着连接不放,调小query_timeout。检查 Agent 是不是在短时间内发起了大量并发请求,如果是,在接入层加限流。

我遇到过一次比较隐蔽的情况:Agent 在一个循环里反复查询,每次查询都很快,但频率太高,连接池来不及回收。后来在接入层加了一个“同一 Agent 的并发请求数限制”,默认是 10,超过就排队。

5.2 权限策略不生效

表现是 Agent 明明配置了 deny 策略,但还是能查到敏感数据。

排查思路:先确认策略的匹配顺序。deny 策略必须放在 allow 策略前面。然后确认策略的subject和resource是否匹配。subject支持通配符,比如agent:*匹配所有 Agent。resource的格式是数据库类型://数据库名/表名,注意不要写错。

还有一个容易忽略的点:如果 Agent 的请求里用了表的别名,比如SELECT u.phone FROM users u,权限校验的时候需要正确解析出phone列属于users表。DatI 的 SQL 解析器会处理别名,但如果 SQL 写得太复杂,可能会解析失败。这种情况下,DatI 会拒绝请求,而不是放行。

5.3 查询结果被截断

表现是 Agent 拿到的数据不完整,比如明明有 10000 条记录,只返回了 1000 条。

这是设计行为,不是 bug。DatI 默认对 SELECT 语句加 LIMIT 1000。如果 Agent 确实需要更多数据,可以在 SQL 里显式指定更大的 LIMIT,但前提是权限策略允许。

如果 Agent 需要做聚合查询,比如 COUNT、SUM,不受 LIMIT 影响。DatI 会识别聚合函数,不加 LIMIT。

5.4 时间格式不对

表现是 Agent 拿到的时间字符串格式混乱,有时候是2025-06-15 14:30:00,有时候是2025-06-15T14:30:00Z。

这是因为不同数据库返回的时间格式不一样。DatI 的结果处理模块会统一格式化,但前提是正确识别了时间类型的列。如果数据库里的时间字段是字符串类型,DatI 无法自动识别,需要手动配置。

配置方式是在权限策略里指定列的类型:

columns: - name: "created_at" type: "datetime" format: "iso8601"

5.5 常见问题速查表

问题现象可能原因排查方法解决方案
查询超时连接池满或慢查询查看日志中的活跃连接数调大连接池或优化查询
权限拒绝策略配置错误检查策略匹配顺序和资源路径调整策略配置
结果截断默认 LIMIT 生效确认 SQL 是否带 LIMIT显式指定更大的 LIMIT
时间格式乱列类型未识别检查列类型配置手动指定列类型
连接失败数据库地址或凭证错误测试数据库连通性修正连接配置
Agent 循环查询提示词问题查看审计日志中的重复查询优化提示词或加缓存

6. AI 编程体验:这大半年我踩过的坑和学到的经验

6.1 AI 编程工具在项目中的实际表现

这大半年里,我用了不少 AI 编程工具来辅助开发。说实话,体验是喜忧参半。

代码生成方面,AI 确实能省不少事。比如写一些模板化的代码,像数据库连接的初始化、配置文件的解析、日志的记录,这些代码结构固定,AI 生成的质量很高。我只需要检查一下边界条件,基本就能用。

但在核心逻辑上,AI 的表现就不太稳定了。比如 SQL 解析器那部分,我让 AI 帮我写一个解析 SELECT 语句的函数,它生成的代码能处理简单情况,但遇到子查询、JOIN、UNION 这些复杂结构就出错了。最后还是我自己重写了核心部分,AI 只用来生成测试用例。

调试方面,AI 的帮助有限。遇到一些奇怪的 bug,比如连接池在特定情况下死锁,AI 给出的建议往往是泛泛而谈,没有针对性。这种问题还是得靠自己的经验和日志分析。

6.2 提示词工程在 AI 编程中的重要性

用 AI 编程工具,提示词的质量直接决定输出质量。我总结了几条经验:

第一,给 AI 足够的上下文。不要只给一个函数名让它实现,要把相关的类型定义、接口约定、调用示例都给它。这样它生成的代码才能融入现有项目。

第二,明确约束条件。比如“不要用 unwrap”、“错误处理用 thiserror”、“异步函数用 tokio”。这些约束能避免 AI 生成不符合项目风格的代码。

第三,分步骤生成。不要一次性让 AI 生成一个大模块,而是拆成小函数,逐个生成。这样每个函数的逻辑更清晰,也更容易检查。

第四,让 AI 写测试。AI 生成的测试用例往往能覆盖一些我没想到的边界情况。虽然测试本身也可能有 bug,但至少能提供一个检查的思路。

6.3 Agent 开发中的并发问题

Agent 的并发访问是 DatI 面临的最大挑战之一。一个 Agent 可能同时发起多个查询,多个 Agent 可能同时访问同一个数据库。如果处理不好,轻则响应变慢,重则数据库崩溃。

DatI 的并发控制分几个层面:

接入层用令牌桶算法做限流。每个 Agent 有一个令牌桶,初始容量是 100,每秒补充 10 个令牌。每次请求消耗一个令牌,令牌不够就排队。这个策略能防止单个 Agent 占用过多资源。

逻辑层用读写锁保护共享状态。权限策略的加载、审计日志的写入这些操作需要加锁。但查询本身是无状态的,不需要加锁,可以并行执行。

数据层的连接池本身就是并发安全的。sqlx 的连接池用了异步信号量来控制并发访问,不需要额外的同步机制。

实测下来,在 4 核 8G 的机器上,DatI 能支撑每秒 500 次左右的简单查询。复杂查询的吞吐量会低一些,但也能到每秒 100 次左右。这个性能对于大多数 Agent 场景已经够用了。

6.4 数据库同步与结构变更的处理

开发过程中,数据库的结构变更是个麻烦事。测试库和生产库的结构可能不一致,Agent 生成的 SQL 在测试库能跑,到生产库就报错。

DatI 的做法是在启动时读取数据库的 schema 信息,缓存起来。Agent 调describe_table的时候,返回的是缓存的信息。如果数据库结构变了,需要重启 DatI 或者调一个刷新接口。

更好的做法是接一个数据库迁移工具,比如 Flyway 或 Liquibase。每次结构变更都通过迁移脚本执行,DatI 在启动时检查迁移版本,确保 schema 是最新的。这个我还没集成进去,是下一步的计划。

6.5 安全方面的额外考虑

Agent 访问数据库的安全问题,比人类用户访问要复杂。因为 Agent 的行为不完全可控,它可能因为提示词注入或者模型幻觉,生成危险的 SQL。

DatI 在安全方面做了几层防护:SQL 改写层拦截没有 WHERE 的写操作;权限层做列级别的访问控制;结果处理层做敏感数据脱敏;审计层记录所有操作。

但这些都是事后防护。更好的做法是在 Agent 的提示词层面就做好约束,告诉它什么能做、什么不能做。提示词里要明确写出禁止的操作,比如“不要执行 DROP、TRUNCATE、ALTER 语句”。

还有一个容易被忽略的点:Agent 的 API Key 管理。每个 Agent 应该有独立的 API Key,方便追踪和撤销。API Key 要定期轮换,避免泄露。DatI 支持多个 API Key,每个 Key 可以绑定不同的权限策略。

7. 这个项目后续还能怎么扩展

DatI 目前支持 MySQL、PostgreSQL、SQLite 三种数据库。下一步计划支持更多类型,比如 TDengine 这种时序数据库,还有向量数据库。Agent 做数据分析的时候,经常需要查时序数据或者做向量检索,支持这些数据库能扩展 DatI 的使用场景。

另一个方向是增强 Agent 的交互能力。现在 Agent 只能通过 SQL 来操作数据库,但有些场景下,Agent 可能更擅长用自然语言描述需求。DatI 可以加一个“自然语言转 SQL”的接口,Agent 直接说“帮我查一下上个月的销售总额”,DatI 负责生成 SQL 并执行。这个功能已经在实验阶段了,用的是本地的轻量级模型,不依赖外部服务。

还有一个想法是做一个 DatI 的 Web 管理界面。现在配置权限策略、查看审计日志都要手动改文件或者调 API,不太方便。做一个界面,让开发者可以可视化地管理数据库连接、配置权限、查看查询统计。这个工作量不小,但价值很高。

最后,我还在考虑把 DatI 的核心逻辑抽成一个库,让其他项目可以嵌入使用。现在 DatI 是一个独立的服务,部署起来虽然简单,但对于一些轻量级场景来说还是太重了。如果做成库,开发者可以直接在自己的 Agent 项目里引入,不需要额外部署服务。

这个项目从冬天写到夏天,中间经历了很多次重构和调整。最大的体会是:做 Agent 相关的基础设施,不能只考虑功能,还要考虑安全、性能和可维护性。Agent 的行为模式跟人类用户完全不同,需要专门的设计。如果你也在做类似的事情,希望这篇文章能帮你少踩一些坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询