Agent 数据模型:工具 / 工具包 / 研判方案 的表关系
适用范围:ai-server 的 module/agent 域 + sql/agent_tool_pack.sql(含内置种子)。
结论先行:工具 ↔ 工具包 ↔ 研判方案 是两级多对多,都用 JSON 数组做松引用,
完整性由「保存时校验 + 删除时清理 + 装配时跳过」三道防线保证(见文末规则表)。
关系图
erDiagram
agent_tool ||--o{ agent_tool_pack_item : "tool_names[] 按名引用"
agent_tool_pack ||--o{ agent_tool_pack_item : "包内工具集合"
agent_tool_pack ||--o{ agent_pack_bind : "被方案勾选"
agent ||--o{ agent_pack_bind : "方案勾选的包"
agent_tool {
bigint id PK "雪花 ID"
varchar tool_name UK "工具名(@Tool name)"
varchar group_code "call/trans/track/graph/person/file/base"
varchar status "ACTIVE | DELETED"
}
agent_tool_pack {
bigint id PK "雪花 ID(内置包固定 1~6)"
bigint user_id "创建者;内置包 = 1"
varchar name "包名(user_id 内唯一)"
text tool_names "JSON 数组:tool_name 列表"
smallint is_builtin "1=内置(全局可见、不可改删)"
}
agent {
bigint row_id PK "identity 自增"
bigint user_id "创建者;内置方案 = 1"
smallint is_builtin "1=内置(不可改删)"
text tools_allow_json "JSON 数组:工具组编码白名单"
text tool_packs_json "JSON 数组:工具包 id 白名单"
text skills_allow_json "JSON 数组:技能名白名单"
}
图中 agent_tool_pack_item / agent_pack_bind 是逻辑关系(多对多的语义实体),
物理上分别落在 agent_tool_pack.tool_names 与 agent.tool_packs_json 两个 JSON 数组列里,
不建物理中间表:数据量级为个位数包 × 十位数工具,JSON 松引用 + 服务层校验更贴合本域。
多对多怎么成立
| 关系 |
基数 |
存储 |
说明 |
| 工具 ↔ 工具包 |
一个工具可出现在多个包;一个包含多个工具 |
agent_tool_pack.tool_names[](工具名数组) |
同一个 tool_name 可以出现在任意多个包的数组里,互不影响 |
| 工具包 ↔ 研判方案 |
一个包可被多个方案勾选;一个方案可勾多个包 |
agent.tool_packs_json[](包 id 数组) |
同一个包 id 可以出现在任意多个方案的数组里;改包内容对所有引用它的方案即时生效(下次装配) |
| 工具 ↔ 研判方案 |
传递多对多(经包)+ 直接的工具组白名单 |
agent.tools_allow_json[](组编码) |
方案最终可见的业务工具 = 所选工具组的全部工具 ∪ 所选工具包的工具;全不勾 = 不限制 |
ID 约定
agent.row_id:PostgreSQL identity 自增(存量默认方案 = 1「数刃」);
agent_tool.id / agent_tool_pack.id:应用侧雪花 ID(MyBatis-Plus ASSIGN_ID);
内置工具包固定占用 1~6(雪花 ID 远大于此,不会碰撞),供脚本直接引用;
- 前端传输一律字符串 ID(避免 JS 精度丢失);JSON 数组里也是字符串(
["1","2"])。
完整性规则(三道防线)
| 时机 |
校验/清理 |
实现位置 |
| 保存工具包 |
工具名逐个对照 AgentToolRegistry.registeredToolNames()(业务工具;base 基础工具不进包),非法 400 |
ToolPackService.validateToolNames |
| 保存研判方案 |
引用的包必须存在且「内置或属于当前用户」,否则 400/404 |
ToolPackService.requireBindable(由 AgentService.validateExpertConfig 调用) |
| 删除工具包 |
内置拒绝;同时从所有方案的 tool_packs_json 摘掉该 id,并驱逐受影响的 agent 实例池 |
AgentToolPackController.deletePack → AgentService.removePackBinding |
| 装配对话(buildAgent) |
解析包 id:坏 id / 已删除的包跳过(双保险);组白名单 ∪ 包内工具 = 工具级白名单,白名单外业务工具 removeTool 真实注销 |
ToolPackService.resolveToolNames + AgentToolRegistry.registerBusinessToolsForWhitelist |
| 改方案配置 |
保存后驱逐该方案的池化实例(否则旧工具面继续生效) |
AgentService.updateExpert → evictAgentPool |
删除语义速查
| 删除对象 |
对工具包 |
对研判方案 |
对对话实例 |
| 工具(代码下线) |
包里残留该名字 → 装配时自然无此工具(惰性失效),不报错 |
— |
— |
| 工具包 |
— |
tool_packs_json 被摘掉该 id(不留悬空引用) |
受影响方案实例池被驱逐 |
| 研判方案 |
不影响(包独立存在) |
— |
该方案池清空;已建会话再对话报「智能体不存在」 |
内置 vs 自建
|
内置(is_builtin=1,user_id=1) |
自建(is_builtin=0) |
| 工具包 |
全局可见、不可编辑/删除(服务端双重拒绝);id 固定 1~6 |
仅创建者可见/改删 |
| 研判方案 |
全局可见、不可编辑/删除(如默认方案「数刃」) |
仅创建者可见/改删 |
| 绑定 |
任何方案可勾选内置包 |
只能勾选自己的包(保存时校验) |
默认方案(agent.row_id=1)绑定全部 6 个内置工具包(sql/agent_tool_pack.sql 维护;
内置方案 UI 不可编辑,其绑定关系只能由该脚本写入)。