日期:2026-11-05 GitHub Trending 日榜第 1 位;榜单采集时间:16:00:25(北京时间)。
"本月收入是多少?"看起来只需要一个 SUM。接到真实业务数据库,问题很快就变了:订单金额还是实收金额,取消订单算不算,时间按创建还是支付,金额存的是元还是分?SQL 能运行,与回答了正确的问题,是两件事。
NVIDIA 的 Auto Ontology 把一部分工作放到提问之前。它读取数据目录,生成映射到物理列的业务术语、关系和 SQL 属性,让人修订这些定义,再让查询 Agent 从这份语义上下文中选材料、写 SQL、检查并执行。值得看的是这些材料怎样进入执行链,以及检查失败以后系统究竟怎么做。
📋 项目概览
| 项目 | 内容 |
|---|---|
| 主仓库 | NVIDIA/auto-ontology |
| 定位 | 面向结构化数据问答的语义目录、业务本体与 NL-to-SQL 系统 |
| 核心实现 | Python 后端、LangGraph 查询编排、Next.js 前端、Postgres 与 pgvector |
| 后端清单版本 | 1.1.0,Python 要求为 3.12 至 3.13 |
| 对外入口 | Web、经过认证的 REST API、单独发行的 MCP 服务 |
| 部署方式 | Docker Compose 或 Kubernetes Helm |
| 许可证 | Apache-2.0;第三方组件另见仓库的许可说明 |
| 快照热度 | 5,898 Stars、461 Forks、46 个 open issues |
这里的本体可以先理解成"有物理落点的业务词汇表"。Customer 这样的 Term 有描述、同义词和对应列;ColumnAttribute 把术语连接到列;SqlAttribute 保存业务概念下可复用的 SQL 表达式。表之间的关系给查询提供连接路径。
它不是要求企业先建一个独立图数据库。目录、本体和向量检索所需的数据保存在 Postgres 体系里,业务数据仍由各自的数据源连接器访问。
🔥 为什么值得关注
直接把建表语句塞进提示词,能让模型知道某张表有 amount 列,却不能稳定解释这个列在业务上代表什么。复杂查询还会遇到更隐蔽的问题:两张表都含 customer_id,连接到哪一张才符合统计粒度;中间桥接表没进入上下文,生成器可能写出能跑但重复计数的 JOIN。
Auto Ontology 试图把这些信息变成共享资产。术语说明、列映射、连接关系和审核过的表达式可以反复进入不同问题的上下文,而不必每次让模型重新猜一遍。
这条路线的成本也很明确:接入数据库只是开始,还要处理术语质量、采样权限、模型调用、索引和业务人员的修订。数据库里没有的业务定义,换一个模型也不会凭空变得可信。项目更适合愿意维护语义层的团队,而不是只想加一个聊天输入框的团队。
另一个值得读源码的原因是,它并没有把"检查"做成一个神秘黑盒。静态引用解析、模型意图判断、可选数据库探测、执行错误修复、空结果处理,各自有自己的失败路径。理解这些边界,比记住一个准确率数字更有用。
🏗️ 核心特性
从数据目录生成语义初稿
摄取入口 ingest_catalog 调用目录提取和写入逻辑,返回带有存储标识的表、列 DataFrame。语义编译再按表提取术语,组合声明外键、推测关系和列画像,保存业务节点及其向量表示。
"自动"主要解决初稿生产,不等于自动完成业务认证。一个叫 revenue 的表达式是否符合财务口径,仍需要负责人确认。Web 界面允许修改术语、描述和 SQL 属性,并对定义进行认证。
查询上下文同时包含语义与结构
候选检索按对象类型工作:实体找 ColumnAttribute,问题找 SqlAttribute,主题可找 Term;候选准备还会选择连接锚点、查找 join 路径,补回连接所需的桥接表。生成器拿到的不是一串相似文本,而是带表列落点、关系路径和表达式的上下文。
不过,最终 SQL 仍由模型生成。结构化输出限定了返回形状,并不证明模型选择了正确的聚合粒度或过滤条件。
API 与 MCP 共用后端问答能力
REST 的 POST /api/chat/completions 返回 SSE 流。MCP 的 ask_question 包装这条流,把回答、SQL、行数据和步骤拆成字段;其他工具提供术语检索、部署就绪检查和问题覆盖检查。
MCP 服务是独立 Python 包,通过 HTTP 访问部署,不需要把整套数据库驱动和模型依赖安装到每个 Agent 客户端。它也不是一套独立运行的查询引擎。
有权限入口,但部署网络仍是边界
Next.js 网关负责解析用户身份和权限,再向 FastAPI 转发内部身份头。后端信任这个内部身份;摄取服务还有自身不带认证的管理端点。因此,对外发布 Web 入口,不意味着可以把内部服务端口一并暴露。
🔬 技术架构深度解析
1. 数据先进入目录,业务含义再进入语义图
下面把准备阶段和提问阶段分开。两者使用同一份目录与本体,但成本、更新频率和失败语义不同。
text
源数据库
│ schema、键关系、列画像、有限样本
▼
目录摄取:extract → populate_tabular_data
│
▼
按表语义编译:采样 → 外键建议 → 术语/列属性生成
│ │
│ 人工修订、认证 SQL 属性
▼ ▼
Postgres:目录 + Term / ColumnAttribute / SqlAttribute / 关系
│
└── pgvector:对应语义对象的检索表示
│
用户问题 ▼
│ 分类与实体抽取 → 候选检索 → 连接路径/桥接表准备
│ │
└────────────────────────────────────────────▼
模型生成 SQL
│
可选值验证 → 静态/意图检查
│
可选数据库前置探测
│
连接器执行 → 结果检查
│
回答 / SQL / 行数据
检查或执行失败 ──→ 重构 SQL ──→ 重新验证
耗尽预算或缺少数据 ──→ 无法构造回答,部分路径保留候选 SQL
源码中的语义编译入口 compile_semantic_layer 只取尚未关联 Term 的表,并按库、schema、表名等组成的规范键排序。它使用三个 worker 并行执行昂贵的表处理,但把术语持久化安排在规范顺序的 commit slot 中。
这解决一个容易被忽略的问题:两张表提议了相同的无前缀术语名,如果谁先得到名字取决于模型网络延迟,同一批输入的命名就会漂移。并行提取、顺序提交让这种名称归属更稳定。它保证的是提交顺序,不是模型每次生成完全相同的词。
队列还记录提前放弃的 slot。后续表取消或失败时,不用占着 worker 等前序表结束;当前 slot 推进时会消费连续的跳过位置。已经进入模型调用的表不能因此立即中断。停止编译意味着不再继续积压任务,不能当作对外部请求的强制撤销。
这段实现可以直接在 semantic/pipeline.py 中找到。
2. 采样降级会影响语义质量,不一定让编译整体失败
process_table 先尝试列画像,再提议外键、生成非外键列的属性规格,然后让模型提出术语与显示名称。没有任何可落到真实列上的分配时,它会跳过术语创建,而不是保存一个空泛概念。
画像路径使用最多 1,000 行的样本,支持超时能力的连接器会收到画像查询的时间限制。分类值的 DISTINCT 探测受单独开关控制;关闭它,并不会同时关闭普通行样本。
系统还有按数据源记录的采样熔断器:连续三次超时或连接类失败后,本轮剩余采样可以跳过,继续编译时缺少样本信息。普通权限或类型错误不会一概算成仓库不可达。新一轮编译会重置对应数据源的熔断状态。
这是一种可用性取舍。它避免对失联数据源逐表耗尽等待时间,却也意味着"编译完成"可能包含质量下降的对象。术语持久化后,内联 embedding 失败会记录警告;这同样不能被理解为术语已经可以通过向量检索完整召回。运维上应分别观察目录写入、术语覆盖和检索索引,而不是只看一个完成提示。
3. 候选准备决定了模型有机会答对什么
NL-to-SQL 的正常计算路径依次经过意图分类、问题实体抽取、候选检索与候选准备。information 类型问题会走解释信息的旁路,不进入 SQL 生成;预测节点则需要相应 Kumo 配置,不是普通分析问题的必经流程。
候选准备比较有工程价值。它围绕主 ColumnAttribute 建立 join 路径,再做表相关性筛选,随后由代码补回必要的 junction 和 bridge 表。后面这一步防止语言模型在"删掉不相关表"的同时,把连接两端唯一的中间表删掉。
例如,客户与商品可能通过订单、订单明细关联。问题只提到客户和商品,并不代表查询上下文可以省掉订单明细。向量相似度很难独自表达这种连接必要性,图关系提供的是另一种约束。
但关系信息也有来源质量。声明外键与推测关系不能不加区别地当成业务事实;路径存在,只能说明系统找到了可用连接线索,不能证明它符合某次统计的粒度。
4. "验证通过"包含几种强度不同的判断
生成器优先使用 reasoning client,把 schema、关系路径、SQL 属性、evidence、值锚和参考查询结构等放入提示词。之后的统一验证先做 catalog/static 处理,再让模型审查 SQL 是否符合问题意图。
catalog 解析会关联已知表列,完全找不到目录中的表会成为错误。它不是数据库优化器,也不是一个覆盖全部业务语义的证明器。某些静态检查还受功能开关控制,不能把文件里存在一个检查函数当成所有部署都启用了它。
更值得注意的是意图判断的降级:调用意图模型抛异常,或返回空结果时,代码会返回 valid_sql;明确发现连接、聚合、投影或 evidence 问题时,才进入重构。这个标签表达的是流程路由决定,不能直接翻译成"答案正确"。
可选数据库探测又是另一层。值、join 路径、JSONB 路径的前置探测在默认配置下并非全部开启。有些值验证由带预算的模型工具循环完成;它们可以提供纠错证据,但不等于穷尽检查所有输入。
具体降级条件见 sql_parse_validation.py。把这些分支保留在接入评估里,比只验证正常查询更重要。
5. 重试有三层,返回 SQL 也未必代表执行成功
| 层次 | 实际控制 | 接入时的含义 |
|---|---|---|
| 首轮 SQL writer | 生成返回空值等情况下,局部最多尝试三轮 | 不能据此估算整个请求的调用次数 |
| SQL 重构 | 硬失败依据失败历史计数,阈值为七 | 还受图执行深度及提前退出影响 |
| 软检查 | 失败计数超过五后可不再阻塞 | 探测失败并不总会禁止继续执行 |
| 完整流程 | 特定无法构造、无 SQL 的终态最多运行两次流程 | 可能重新做意图分类和候选检索 |
普通数据库语句错误可以触发重写;连接失效等基础设施问题会结束当前内层链。但是外层重启逻辑没有因此自动排除所有基础设施故障,不能概括成"连接错误绝不重试"。
还有一个需要调用方特别处理的 fallback:第二次完整流程失败后,如果保存着此前被意图审查拒绝的 SQL,系统可以把它放进最终 sql_code 返回。这个分支直接复制候选,不会再执行那段 SQL。仓库对应测试也明确覆盖了返回这种候选的情形。
因此,接业务系统时应分别判断自然语言回答、sql_code、数据库行数据和错误状态。不能用"SQL 字段非空"作为成功记账、自动导出或触发下游流程的条件。main.py 里的完整流程恢复与 fallback 是理解这点的入口。
6. 主查询执行没有因为 MCP 的"只读工具"定位而获得沙箱
主 SQL 最终经 execute_chat_sql 交给选中的 connector。多个数据库连接同时存在时,代码根据相关表的 database_name 路由;跨库集合、找不到对应库或多连接却没有库元数据等情况会拒绝,而不是随意选择第一个连接。
数据库 probe 有自己的 SELECT/WITH 形态检查、调用预算和返回行限制,但这套 guard 不是主查询的通用隔离层。语句超时也依赖 connector 是否声明支持,不是整个自然语言请求的总截止时间。
MCP 工具面向读取目录与查询数据,不提供目录编辑工具,这是接口设计。它不代替数据库侧的最小权限。生产数据源仍应使用受限账号、合适的视图和数据库资源限制,内部服务放在可信网络,采样数据和模型请求的去向也要符合企业要求。
📖 README 核心内容摘要
README 对使用流程的描述是"生成、认证、回答":先摄取目录并生成语义初稿,再由人修订定义,最后通过聊天或 Agent 访问。业务 SQL 属性的复用是这套方案的重要收益,不过已有定义并不会自动变成不可违反的形式化约束。
部署包含 Web 网关、FastAPI 后端、摄取服务,以及保存目录和本体的 Postgres/pgvector。Web 默认端口是 3000,内部后端与摄取服务分别为 3001、3002。Compose 文件包含数据库迁移任务,启动顺序要求迁移成功后再启动相关服务。绕开这些任务直接运行容器,可能得到界面可访问、目录接口却报缺表的半成品部署。
模型按角色设置 endpoint、API key 和 model 三元组。未设置的字段回落到共享默认值。一个实际容易踩的坑是:.env.example 各角色已经写了示例 key,只修改共享默认 key,不会覆盖仍然存在的角色示例值。应替换它们,或删除对应示例行让回落生效。Embedding 的接入还要求摄取与查询使用相同模型,不能仅因为维度碰巧相同就随意替换。
README 和环境模板在具体模型名称上可能不同,配置应以所用版本的环境模板及实际端点可用性为准。换 endpoint 也不能只改聊天模型,还需要确认 embedding 和 rerank 的协议、模型名称及认证方式。
Agent 接入有两个不同的前置检查:check_readiness 判断部署是否具备回答能力,check_answerable 判断本体是否覆盖某个问题。后者说"能覆盖"不保证查询必定成功,也不能替代业务口径审查。
许可证允许按 Apache-2.0 的条款使用与分发,仓库同时声明当前不接收外部贡献。开放源码、允许复用和维护者是否接受 PR,是不同的问题。
🚀 快速上手
先从已有的小型、无敏感数据源入手。以下使用清单版本 1.1.0 的 Compose 服务名和接口,前提是安装了 Git、Docker 与 Compose v2,并准备了可用的模型端点。模型调用可能计费。
1. 准备仓库与环境
bash
git clone https://github.com/NVIDIA/auto-ontology.git
cd auto-ontology
cp .env.example .env
编辑 .env,更换数据库、pgAdmin、管理员的默认密码及 AUTH_SECRET,填写模型配置。不要把真实 key 提交到 Git。演示数据使用下面的连接,密码与 POSTGRES_PASSWORD 一致;若密码含 URI 特殊字符,需要做 URL 编码。
dotenv
APP_URL=http://localhost:3000
CONNECTION_STRINGS=postgresql://postgres:YOUR_URL_ENCODED_PASSWORD@postgres:5432/report_demo
这里 postgres 是 Compose 网络里的服务名,不是本机地址;report_demo 是随后创建的演示数据源,不要把它与保存应用目录的 POSTGRES_DATABASE 混为一谈。
Compose 默认还会映射数据库、pgAdmin 和内部服务端口。演示环境也应限制主机入站访问;共享服务器部署前,先调整绑定地址或网络规则,不要将原配置直接作为公网部署方案。
2. 建立一个能人工对账的示例
先启动存储,再在同一实例里创建独立演示库。创建命令用于首次初始化,已有同名数据库时应检查现有内容,不要为了重跑示例直接删库。
bash
docker compose up -d --wait postgres
docker compose exec postgres sh -lc \
'psql -U "$POSTGRES_USER" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE report_demo;"'
docker compose exec -T postgres sh -lc \
'psql -U "$POSTGRES_USER" -d report_demo -v ON_ERROR_STOP=1' <<'SQL'
CREATE TABLE demo_orders (
id INTEGER PRIMARY KEY,
status TEXT NOT NULL,
paid_at DATE NOT NULL,
amount_cents INTEGER NOT NULL
);
INSERT INTO demo_orders VALUES
(1, 'paid', '2026-11-01', 10000),
(2, 'paid', '2026-11-02', 15000),
(3, 'cancelled', '2026-11-03', 20000),
(4, 'paid', '2026-12-01', 7000);
SQL
docker compose up -d --build
打开本机 3000 端口的 Web 页面,使用所配置的管理员登录。确认数据源已摄取,在 Settings → Semantic Compilation 开启语义编译,等待可用。检查生成的术语是否保留了 amount_cents 的分单位,以及 paid_at 的时间含义,必要时修订描述。
可以问:"按 paid_at 统计 2026 年 11 月 status 为 paid 的订单总金额,保留分单位,不包含 cancelled 订单。"这段明确口径也适合放入 API 的 evidence。固定样例的对账 SQL 是:
sql
SELECT SUM(amount_cents) AS revenue_cents,
COUNT(*) AS paid_orders
FROM demo_orders
WHERE status = 'paid'
AND paid_at >= '2026-11-01'
AND paid_at < '2026-12-01';
这份数据的参考值是 revenue_cents=25000、paid_orders=2。它是样例数据的确定结果,不是承诺模型每次生成相同 SQL。故意换成"净收入""已退款金额"等没有定义的口径,再观察系统怎样澄清或拒绝,会比只测一句顺利的问题更有价值。
3. 用 SSE 客户端取回独立字段
在 Web 用户菜单的 API Tokens 中创建自己的 token,通过环境变量提供给客户端。API 请求走 3000 端口网关,不直接绕过它调用内部 FastAPI。下面脚本使用 Python 3.12 标准库,不需要安装 HTTP SDK。
python
import json
import os
import urllib.request
payload = {
"question": "2026年11月已支付订单的总金额和订单数是多少?",
"evidence": "仅统计status='paid';时间按paid_at;金额保留分单位。",
}
request = urllib.request.Request(
"http://localhost:3000/api/chat/completions",
data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
headers={
"Content-Type": "application/json",
"Accept": "text/event-stream",
"x-api-key": os.environ["AUTO_ONTOLOGY_API_TOKEN"],
},
method="POST",
)
with urllib.request.urlopen(request, timeout=900) as response:
for raw_line in response:
line = raw_line.decode("utf-8").strip()
if not line.startswith("data:"):
continue
data = line.removeprefix("data:").strip()
if not data:
continue
if data == "[DONE]":
break
event = json.loads(data)
if event.get("type") == "error":
raise RuntimeError(event.get("message", "query failed"))
if event.get("type") == "result":
answer = event["answer"]
print(answer.get("response"))
print(answer.get("sql_code"))
print(answer.get("sql_response_from_db"))
sql_response_from_db 可能带有 JSON 字符串包装,业务客户端应按约定解析,不要从回答正文正则提取数字。SSE 的 [DONE] 不是 JSON;直接把整个响应交给 .json() 会用错协议。HTTP 成功也不代表流里没有 error 事件。
需要连续对话时,传入并复用一个 UUID 格式的 conversation_id,等待前一轮流结束再发下一轮。同一会话重叠运行会返回 409。只做上面的单次查询,可以省略会话标识,不必为了拿一个统计值创建聊天历史。
接口字段与流格式见 Conversation API。
4. Agent 通过独立 MCP 包连接
已有完整部署后,在另一个终端进入同一仓库目录,安装本地 MCP 子项目并启动:
bash
cd mcp
uv sync
AUTO_ONTOLOGY_API_URL=http://localhost:3000 uv run auto-ontology-mcp
这条命令要求另行安装 uv。服务使用 HTTP transport,客户端连接本机 3003 端口的 /mcp,并按登录流程以用户身份授权。MCP 不代替前面的数据库接入和语义编译。
ask_question 的返回层最多提供 100 行,同时带 row_count 和 truncated。这是传给 Agent 的响应截断,不是保证数据库最多计算或读取 100 行。详细分析或大规模导出,不应依赖一个受上下文预算约束的工具返回值。
📊 增长速度与社区热度
2026-11-05 的日榜快照里,Auto Ontology 位于第 1,仓库累计 5,898 Stars、461 Forks。进入 Trending 表明它在这个时间窗口受到关注,但这些累计数不能推导出单日新增量,更不能当成真实部署数量。
对准备接入的团队,更有判断价值的是边界问题能否得到持续维护:连接错误会不会无限重构,认证定义怎样进入生成上下文,模型异常时哪些检查会降级,以及数据库迁移是否与应用启动保持一致。星数可以帮助发现项目,不能代替这些评估。
当日日榜如下:
🎯 适用场景
有共享指标口径的数据问答
如果团队里不同工具都在回答收入、库存、客户等问题,维护一份能映射到列和 SQL 表达式的语义层,有机会减少重复解释。前提是有负责人管理定义,数据表和关系也足够稳定。
初次接入可以从十几个关键指标、少量视图和受限账号开始。把标准问题与已知 SQL 结果做成回归集,覆盖取消、退款、跨月边界、空结果和连接失败,不要只收集看起来流畅的聊天截图。
编码 Agent 的结构化数据入口
需要让 Agent 先查业务术语,再提问取数时,MCP 的分工比较清楚。先区分部署未就绪与问题超范围,能减少把配置故障反复当成提示词问题修复的浪费。
但这仍是有成本的数据查询。多次串行模型调用、数据库探测和完整流程重试都会增加延迟与端点费用。应按自己的请求分布看调用次数、失败分类和数据库负载,不能只看一次首轮 SQL 生成的时间。
不适合直接承担不可逆业务决策
需要严格证明口径、自动执行资金动作或零容忍错误的流程,不应直接消费聊天回答。模型意图验证存在放行降级,fallback 还可能返回未执行的候选 SQL。必要的审批、对账和只读数据库权限,需要由业务系统单独建立。
只有几张结构固定的表、几个固定看板时,写好 SQL 和视图往往更省维护成本。引入本体的收益应来自多入口复用和复杂语义,而不是为了把一个固定查询改成自然语言。
💡 总结
Auto Ontology 的可取之处,是把业务术语、列映射、连接路径和 SQL 属性放进一条可观察的查询链,而不是指望模型仅凭 schema 就理解企业。
它也清楚地暴露了这条路线的难题:语义初稿需要人维护,检索与目录可能分步成功,验证有降级,返回候选不等于完成执行。把它作为需要治理的语义数据服务来接入,比把它当成开箱即用、永不出错的 SQL 助手更合适。