资讯详情

让数据先会说话:数据字典、查询契约与接口语义化

📅 2026/10/14 3:22:34 | 华诺云谱 👁 阅读
让数据先会说话:数据字典、查询契约与接口语义化
系列《VeapAI 自然语言操作实战》第 2 篇核心篇 相关源码veap-cloud/veap-modules/veap-gen/在线开发与代码生成含 src/main/resources/vm/ 模板、veap-cloud/veap-modules/veap-system/数据表字典与字段清单端点、veap-cloud/veap-modules/veap-blockgen/接口扫描、veap-cloud/veap-common/veap-common-view/结构化查询协议## 一、模型猜字段是它最擅长的事也是最危险的事做自然语言操作第一件让人睡不着的不是模型不够聪明而是它太会说话了。你问「查一下状态为启用的用户」模型完全能编出一个 SELECT * FROM user WHERE status enabled。语法没错逻辑也对但在这套系统里 status 存的是 1 和 0enabled 这个值根本不存在。如果这条 SQL 真的执行了结果只会是空——用户以为系统里没有启用的账号。更麻烦的情况是写操作。表名猜错一个前缀可能改到另一张表的数据。所以这套链路里有一条不动摇的约束**模型不许猜表名、不许猜字段名、不许猜接口地址、不许猜字典值只能从我们给它的清单里挑。**这条约束要成立得先让数据自己会说话。下面三层就是我们为此做的准备。![语义基座三层链路字典 → 生成 → 扫描 → AI 消费]## 二、第一层数据表字典平台里有一套数据表字典veap_dict_table 存表veap_dict_field 存字段。这套东西本来服务于在线开发的页面生成后来发现它天然就是一份表语义清单。表上跟语义直接相关的字段| 字段 | 注释原文 | 用途 || --- | --- | --- || table_code | 表名称默认自动生成 | 表编码所有引用的起点 || title | 表标题数据表标题 | 「用户」「角色」这类人话与表编码的对齐 || pk_field | 主键字段 | 写操作定位记录 || pid_field | 父级字段 | 识别树形结构 || state_field | 状态字段名称 | 状态类条件 || del_field | 删除字段名称 | 逻辑删除口径 || sort_field | 排序字段名称 | 「最近创建的」这类命令 || module_code | 表所属模块默认 sys 系统模块通过数据字典配置 | 功能引导时归类 || status / del_state / sort | 有效状态 / 删除状态 / 显示次序 | 目录资源口径status1 且 del_state0按 sort 排序 |字段表里我在排查问题时最常看这几个| 字段 | 注释原文 | 说明 || --- | --- | --- || field_code | 字段名称 | 计划里的字段引用必须命中它 || db_type | 数据库类型 | 判断能不能承载非数字值——文本值落到数值列上必须挡住 || is_indexed | 是否索引 | 慢查询排查的第一手线索 || parent_id | 上级字段主键支持多层嵌套 | 结构化字段的层级 || json_path | JSON/ARRAY字段路径如address.city、items[].name | 非平铺字段的取值路径 || schema_json | JSON/ARRAY字段结构描述JSON Schema | 驱动前端动态表单 |不过 AI 侧不会把整张字段表塞给模型——那是一份很长的清单。我们做了一个最小集端点textPOST /system/veapDictTable/listFieldMetas参数tableCode返回fieldCode / title / desp / fieldType / dbType / isQuery / queryType / javaField八个键每一个都有明确用途。前六个来自字典表描述「这个字段是什么」。后两个不是字典表给的- queryType这个字段在列表接口里有没有过滤条件是哪种过滤方式- javaField这个字段在列表接口请求体里的 Java 属性名。这两个键来自**在线开发的字段快照** veap_generate_field端点会左关联该表最新一次生成记录的字段配置。没匹配到就返回空值或 0。为什么要分开取因为「字段是什么」和「字段能怎么用」是两件事。字典是人工维护的快照是生成器产出的。把它们混在一起就会出现「字典里写了字段但生成的接口查不出这个条件」这种无法解释的现象。## 三、第二层查询能力契约上面那个 queryType值得单独讲一节因为它背后有一次重构。在线开发里原本有两个字段一个叫「是否查询」一个叫「查询方式」。最开始的理解是前者控制要不要生成查询条件后者控制用哪种查询方式。听起来合理实际跑起来是两个问题- 清空「查询方式」后渲染期会做一次推断把值补回来于是「不想生成条件」这件事做不到模板里还有一层兜底推断不出来就按等值处理- 写了非法值不报错静默按等值处理。配置写错了没有任何提示只在运行期表现为「条件不生效」。后来我们把职责彻底拆开- **「是否查询」只控制前端查询项显示**不参与过滤能力判定- **「查询方式」是过滤能力的唯一开关**非空即生成过滤条件为空即不生成。同时补了两件事模板里那层兜底分支被删掉生成期新增契约校验规则是「配了是否查询就必须配查询方式、取值必须在集合内、范围查询只能落在数值或日期字段」。取值集合是七个加一个textEQ等值 NE不等 GT大于 GTE大于等于 LT小于 LTE小于等于 LIKE模糊 BETWEEN范围非法值的处理方式也变了生成期直接失败带上字段定位和修复建议。宁可生成时拦住也不要让一个错误的配置跑到运行期才发现。这套变化对 AI 链路的直接好处是计划校验脚本可以拿 queryType 当唯一依据做断言。脚本里有一张操作符到查询方式的映射表| 计划里的操作符 | 要求字段的查询方式 || --- | --- || eq | EQ || ne | NE || like | LIKE || gt / ge | GT / GTE || lt / le | LT / LTE |对不上就报错文案是可读的告诉用户该字段要求的查询方式是什么、实际是什么、建议调整条件或改用复杂查询。其中两种组合被明确拒绝范围查询BETWEEN不支持单值条件in 操作符不支持——因为生成的列表接口请求体是一个 Java 对象没有数组语义。这类情况会提示改用复杂查询。三个开关、生成期与运行期两层拦截的关系画在一张图上就是![查询能力契约三个开关与两层拦截]落到界面上这个开关在在线开发的字段配置里![在线开发字段配置是否查询与查询方式]## 四、第三层接口语义表能定位了字段能过滤了还差最后一环模型要知道调哪个接口。我们没用接口标题做匹配。标题是给人看的改标题不该影响功能。用的是积木库的全量 Java 扫描结果。扫描与刷新逻辑在 veap-cloud/veap-modules/veap-blockgen/ 里。刷新支持三种粒度按文件、按模块、全量。控制器层的接口被收录进积木库表族。AI 侧拿清单的端点是textPOST /blockgen/veapBlockFunc/listGeneratedApis参数tableCode返回id / title / reqUrl / reqType / apiType五个键其中 apiType 是决定性的取值只有四种——**新增、编辑、删除、自定义**。「自定义」即列表查询接口。![接口清单接口类型决定了 AI 能挑到哪一条]计划校验脚本按 apiType 精确匹配挑接口硬性规定是这样几条- 新增操作接口的 apiType 必须是「新增」- 修改操作必须命中「编辑」删除必须命中「删除」- 简单查询必须命中「自定义」- 修改和删除还要再选一个列表接口用于执行前的补全查询。匹配口径也可查表编码先转成驼峰形式的类名再关联 veap_block_class 找到控制器类。所以表编码只要规范接口清单就能自动对上不需要人工维护一张映射表。## 五、结构化的那一路只开放读能力简单查询走列表接口就够了但「统计用户总数」「按部门统计用户数」这类命令列表接口做不了。这一路我们接的是平台既有的结构化查询能力一套 ExeDTO 协议打到 POST /dict/view/execute。协议本身支持增删改但**这套链路只使用其中的查询类型**写操作一律走在线生成的接口靠既有的权限体系兜住。统计能力是后补的。第一版跑「统计用户总数」时报的是「统计聚合能力基座未开启」因为当时协议里的查询项没有聚合表达式。补的方式是一次受控扩展- 聚合函数取值收敛到五个COUNT、SUM、AVG、MAX、MIN白名单精确匹配不在集合内直接拒绝- COUNT 且未指定字段时生成 COUNT(*) AS total- 非 COUNT 聚合必须指定字段否则拒绝- 聚合列显式起别名返回结果的列名与别名对齐——这一点很重要否则前端拿到的是 COUNT(*) 这种不能直接用的列名。脚本侧同步加固分组列前置于选择项这样在严格分组模式下语句依然合法输出也更可读非 COUNT 聚合缺字段的检查前置到组装阶段。结构化查询本身有一套可视化设计器复杂查询与统计的取数规则都在这里配置![视图设计器结构化查询的取数配置]## 六、三个真实踩过的坑### 6.1 前缀近似表一个字母引发的整表错位这个坑是测试案例暴露的。删除命令的目标表是角色表链路却把 veap_role_user 的字段清单拿了过来。根因在字段清单端点内部它复用了管理页的模糊列表查询去查表取了第一条。管理页那条查询用的是模糊匹配veap_role 会命中 veap_role_user而后者恰好排在前面。后果不只是一条命令失败。字段清单错位意味着模型看到的字段和真实表不一致它按错误清单输出的条件——如果没被后续校验拦住——就可能造成全量误操作。修法是把表码解析收敛为精确匹配按表编码精确查有效表del_state0查不到就返回空数组由归一化脚本判定为「目标表不在数据表目录或未登记字段」。管理页的模糊搜索语义保持不变两处用不同的查询不再复用。顺带还补了一条修改和删除操作如果定位条件为空直接拦下。空条件意味着全量补全查询存在误操作整表的风险这种通道不该存在。### 6.2 「查询方式」被静默兜底的那些年前一节讲过重构结论这里补一句当时的感受。我们一直以为「清空查询方式 不生成条件」直到有人要做一个「只在接口层过滤、页面上不显示查询项」的配置才发现做不到清空之后会被推断回来。这类问题的讨厌之处在于它不报错。配置看起来是空的行为却是等值的日志里没有任何异常只有结果不符合预期。等到定位到「渲染期有一次推断」和「模板里有一层兜底」时已经绕了几个小时。结论写进了契约**配置即事实不做推断不留兜底**。要表达「不生成条件」就是查询方式为空要表达「条件生成但页面不显示」就是查询方式非空加查询项关闭。### 6.3 一个名称里的两件事第三个坑比较轻但值得记一笔。同一个「是否查询」字段我们一度同时拿它判断两件事前端要不要显示这个查询项后端要不要生成过滤条件。结果是运营把查询项关掉以后接口层的过滤条件也一起消失了——用户明明还能通过自然语言按这个字段查。拆开之后职责清楚多了「是否查询」管显示「查询方式」管能力。两个开关各管一件事配置项的意思也就写在名字里了。## 七、这一层怎么验证想确认这套语义是不是真的对上了可以做三个小检查一是打开数据表字典看目标表的表编码、表标题、主键字段、状态字段、排序字段填全了没有。这五项缺一项对应的命令类别就会不灵。二是在线开发里看目标表字段的查询方式配置。把 AI 侧字段清单端点返回的八个键和页面上的配置对一遍queryType 与 javaField 应当一一对得上。三是调一次接口清单端点看目标表的接口有没有完整收录特别是四类接口新增、编辑、删除、自定义是否齐全。缺了哪一类对应的操作就会在计划阶段被拦下。三件事做完再跑一条查询命令如果字段清单和接口清单都能正常返回这一层基本就通了。到这里表能定位、字段能过滤、接口能挑出来了。剩下最后一块是值用户说的「启用」怎么变成存储里的 1。这块单独占一篇因为它是这套链路里唯一需要提前「备料」的环节。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑