Skip to main content
Glama
get-dst

dst

Official
by get-dst

data serve tool (dst)

dst 把你的数据仓库服务给 AI,并为这件事的整个生命周期带来工程最佳实践。 AI 用自然语言提问;dst 根据你的团队写下的定义来回答,并附上产出答案的 SQL、一个置信度评级,以及一张收据。

问题

  • 把 AI 指向一个数据仓库,它就什么都会回答。它从不拒绝回答,而这正是失败模式:一个错误的答案比没有答案更糟。

  • 它去猜测“revenue”是什么意思,然后返回一个数字,看上去和正确答案别无二致。

  • 仅靠上下文无法解决这个问题的。AI 是不确定的,而其他团队、另一个模型,或另一个季度下有效的做法,在你这里未必有效。

  • 唯一知道答案的方法是:在你的数据上,持续地测试它。

解决方案

  • 声明:以版本控制中的文件形式声明你的数据如何被服务。

  • 测试:验证那些对你真正有效的做法。每一个被批准的答案都是一条回归测试,而管线中的每一个开关都可以翻转并重新测量。

  • 部署:只部署通过的内容,经过会大声失败的闸门。

  • 审计:审计所有已经被服务过的内容:谁问了、跑了什么、花了多少。每一个错误都会作为一个新测试自动回流。

由实践者构建,为实践者服务:文件、pull requests、CI 和退出码,而不是一套新工作流。


测试:引擎

生成过程的核心总是不确定的,因此 dst 把测试当成产品本身,而不是后补的环节。

  • 每一条被批准的答案,都是一条回归测试。 一条已认证的答案,就是一个被人担保的问题→SQL 配对。dst test 通过真实生成管线重新提出这个问题,然后把存储的 SQL 和生成的 SQL 都放到你的数据仓库中执行,并对结果。

  • 行为也被固定了。 evals/cases.yaml 里的用例断言响应的 形态:expect: clarify | refuse | answer。如果一个 lens 开始拒绝明明可回答的问题,或者干脆用猜测代替澄清,它的套件就会失败。

  • 每个结果都会被记录。 测试运行都会落入数据库,所以准确率是一个你能看着它在变化的数字,并且每个发布都以它作为门闸:任何比上次发布分数还低的变更,都被拒绝。

管线可以按 lens 调整(答案严格度、自我修复、逐条判断、门控硬度),而每次翻转都可测量:改一处,跑一次 dst test,结果立刻可知。而如果你想知道在你自己的问题集上,哪些部分真正值得保留,内置的试验场会把你的问题集整套跑过管线,每次只剥离掉一个 feature:

python -m services.benchmark --data -./data --strip <feature>。完整的可调面见配置参考。


Related MCP server: RunContext

部署:经过关卡

一个 lens 就是文件。改变一个答案的 diffuse;这就是一次部署,而不是一次编辑:

edit files → dst plan (dry run) → dst apply (gated, atomic)

dst plan 会显示一次变更会造成什么,如果 apply 会拒绝它,就以退出码 1 退出:plan 预测 apply 的结局。dst apply 就是一个单一事务:连接会被探测,每个过期的已认证答案都会重新对 live generation 运行,任何失败都意味着 什么都不用发布;之前的那个版本继续对外服务。

  • 版本:每次发布都会为一个 lens 版本做快照;dst lens log 会显示谁在什么时候改了什么(human:ana@corp / token:ci)。回滚就是 git revert + dst apply。

  • 环境是用来出错的。 每种环境都是一套独立的 dst:你的笔记本、共享查沙盒、生产。让沙盒指向不同的模型、不同的 temperature、不同的数据表,或一套重新撰写定义的解释,跑 dst rotate test,然后比较得分——沙盒只需要在你已有的服务器上 dst bootstrap 一次。上线就是往生产里应用同一个 git commit;环境之间不会复制任何东西,文件中也没有需要逐环境同步的条款。

  • CI 只用你自己用的那组命令。 exit code 就是接口:PR 任务跑 dst plan;merge 脚本跑 dst apply --require-gates;定时任务跑 dst test --all 和 dst drift。一份可直接使用的 GitHub Actions 示例在 环境与 CI 指南。


审计:每条答案都有价格和签名

  • 回执。 每条数据答案都携带一份可移植、HMAC 签名的回执:请求 id、lens、certification、确切 SQL 的哈希。可在之后通过这些 API 在 verify_receipt MCP 工具中验证。拒绝里不带回执:因为拒绝声明明确不做任何数据主张。

  • 台账。 dst observe 的回答是“谁一直在用它,用它干什么”:每一个调用的题目、SQL、结果、在云计算的两种计价表(AI 和数据仓库)上的成本。已答复、已拒绝、出错分开计数;一次受管控的拒绝是一种结果,把一个错误。

  • 访问是被拒绝的。 每一个 allow * 和每一个 deny 都会落入 append-only 审计日志,包含调用者、lens 和原因。

  • 数据仓库的每个角落都在被监视。 dst drift 把现在的实况 schema 和已提交的基线做对比,而且把每次变更与读取被改表的全部定义、全体实体相互关联。之后是 exit code:0 干净、2 无变化,1 变更破坏已声明、4 还没有基线。

  • 的怀疑是一等输入。 Anyone 可以发一个问题来 review;低置信度的答案可以自己用 auto_review 标记。AI 法官会先做 triage 三角分类,人再裁判;repair 修复就变成一个文件加一个新测试,把整个闭环完成:

---
config:
  theme: base
  themeVariables:
    fontFamily: "ui-monospace, Menlo, monospace"
    fontSize: "14px"
    primaryColor: "#faf6ee"
    primaryBorderColor: "#b45309"
    primaryTextColor: "#292524"
    lineColor: "#b45309"
    edgeLabelBackground: "#faf6ee"
---
flowchart TD
    serve(["an answer is served,<br/>with its receipt"])
    doubt["someone doubts it"]
    test["it becomes a test:<br/>an approved answer + its check"]
    gate["it gates every deploy"]
    serve -- "flagged by the asker,<br/>or by dst itself" --> doubt
    doubt -- "AI drafts the fix,<br/>a person approves it" --> test
    test -- "commit + dst apply" --> gate
    gate -- "the corrected answer serves<br/>from then on, and the AI learns from it" --> serve

一次被修正的错误,就永远被修正,而且你能证明这一点:被纠正的问题从那个时刻起就被已被认证的 SQL 提供,并且它的测试在 release 每次都会被重跑。注意重点任务请看修正环。


模型

Lens——服务的最小单位。一个使用场景(如流失、销售提成、董事会指标模式)会被描述成你共享的语义资产上的 selects,以及访问规则和自己的时钟。一个 agent 用 natural language 向一个 lens 提问,然后得到一份 grounded 且可引用的答复。

语义文件——实体 and 定义,是一次性写的文件,,每个选择它的 lens 共享。一个指标,一个定义;agent 使用的词是可以变化的,而含义从来不非遗。

在丰富上下文——“在上下文中”被 review 过的回答句就会被每个 lens 传播。entry 条目是刻意的、可追溯的,并且随 lens 一起版本化。

认证的回答——经 review 的 question-to-SQL 对,在匹配时逐句地原样,再由 dst test 当作回归测试重跑,并在每次 dst apply 中重跑。一次被修正确的错误就会成为到这样一个词,这样它就一直是 fixed 的。

治理——访问控制完全用你选择的方式:逐 lens 的 person/group 白名单、逐 caller 的 API key(dst_…)、rate limit、以及在数据库层面强制执行的 tenant 隔离(PostgreSQL RLS)。存储的 warehouse 凭据在 rest 时会被加密。

观测——每次调用都会自上而下被 trace。那个 lens,那个 caller,那个 question,那个 SQL,AI 一致性 + 直接的 warehouse 还分,并又被answered / declined / errored 分开数。答案还能被送去 review:AI 法官审查 reasoning trace,并在需要时快速汇报给人类。


Agents 才是 interface

没有 query UI。真正用你喂的是“agent”:把你的任意 MCP client(Claude Desktop、Claude Code、Cursor,或者你自己产品里面内置的 agent)连到指向 /mcp 的 guard 上的 /mcp,只需要一个 URL 和一个作用域受限的 dst_… key(见services/mcp/README.md)。每个问题都走同一条受治理的管线,所以无论哪个 agent 问,它得到答案都一样:

agent (Claude Desktop · Claude Code · Cursor · your product's agent)
        │  MCP — one scoped dst_… key per person
        ▼
   dst  ── lens: semantic model + context + access
        │   ground → SQL guard → execute → compose (cited)
        ▼
   your warehouse (BigQuery · Snowflake · Postgres · MySQL · DuckDB)
        │
   trace + cost + review  →  Observe

人类留在站守,而不是 query 处理路径里:dashboard 才是治理和观察那些 file 所“服务”的驾驶座:review queue,drift 审计、访问、成本。Lens 就是 files 写出来的(dst init → 编辑 → plan/apply),绝不会在 UI 里写。(如果想接 rest API 到 dst 里,也有 REST 门:API reference。)


项目长什么样

项目是这样一组文件:版本化、在 PR 里 review,像 infrastructure 一样地 apply。以下内容是从 dst init 真正生成的 scaffold 里精简略的(scaffold 会把演示语义资源放到 examples/ 下面,所以你可以整体删掉、整那个文件夹也别担忧;那个演示的 lens 本身在 lenses/customer_value/):

name: orders
description: One row per order.
source:
  connection: jaffle
  table: orders
default_time_field: order_date
primary_key: [order_id]
fields:
  - {name: order_id,    type: integer}
  - {name: customer_id, type: integer}
  - {name: order_date,  type: date}
  - {name: status,      type: string}
  - {name: amount,      type: number, description: Order total (USD).}
metrics:
  - {name: revenue,     agg: sum,   expr: orders.amount, format: currency}
  - {name: order_count, agg: count, expr: orders.order_id}
  - name: average_order_value
    type: ratio
    numerator: revenue
    denominator: order_count
    format: currency
joins:
  - {right: customers, on: customers.customer_id = orders.customer_id,
     type: left, relationship: many_to_one}

一次定义把业务术语绑定给 SQL:

---
metric: repeat_customer
sql: customers.number_of_orders > 1
---

A repeat customer has number_of_orders > 1.

而一个 模糊 的定义则会让 dst 去澄清,而不是猜测:

---
metric: value
status: ambiguous
possible_mappings:
  - lifetime value — customers.customer_lifetime_value
  - order amount — orders.amount
---

一下这些 mapping 就足够了:dst 在生成任何东西之前,会在代码里把它们直接长出的澄清,不需要更多配置。

name: customer_value
description: Customer lifetime value and order activity.
connections: [jaffle]
select:
  entities:
    - name: customers
    - name: orders
  definitions: [lifetime_value, repeat_customer, value]
model:
  temperature: 0.0
  answer_mode: balanced
instructions: Select explicit columns.
access:
  allow:
    - caller: alex        # deny-by-default; or `- group: everyone`
eval_gate: block          # a failing eval suite blocks the apply
auto_review: unverified   # low-confidence answers open review tickets
# Served VERBATIM on a match — and each one is a regression test:
# `dst test` re-asks the question and compares against this SQL's result.
- question: How many customers are repeat customers?
  sql: SELECT count(*) AS n FROM customers WHERE number_of_orders > 1
- question: What was total revenue?
  sql: SELECT sum(amount) AS revenue FROM orders
name: analytics

providers:
  anthropic:
    type: anthropic
    api_key_env: DST_API_KEY_ANTHROPIC
  # any openai-compatible endpoint works: deepseek, ollama, vllm, groq …

connections:
  jaffle:
    type: duckdb
    config: {path: fixtures/jaffle_shop.duckdb}
  wh:
    type: bigquery                  # or snowflake, postgres, mysql
    config: {project: my-gcp-project}
    secret_env: DST_API_KEY_WH      # an inline key is a parse error
$ dst query customer_value "How many customers are repeat customers?"
19 of the 100 customers are repeat customers.

sql: SELECT count(*) AS n FROM customers WHERE number_of_orders > 1
basis: A repeat customer has number_of_orders > 1.
confidence: verified · definition: repeat_customer

$ dst query customer_value "What is the average value of a customer?"
clarify: 'value' is ambiguous in this dataset — lifetime value (total
historical revenue per customer) or order amount (a single order's total)?
  - lifetime value — customers.customer_lifetime_value
  - order amount — orders.amount

拒绝和澄清都算是一种 result 而非 error:dst 更多是要发问,而不是猜。


快速开始

安装包。先决条件:Python 3.12+、Docker(dst 用它自己的 PostgreSQL 运行),以及至少一个 model provider 的 API key:Anthropic 或任何支持 openai 兼容端点的供应商(DeepSeek、Ollama、vLLM、Groq,以及大部分网关)。

pip install dst-core            # the CLI is `dst`
dst init analytics --warehouse demo --yes
cd analytics                    # put your provider key in the generated .env
dst dev                         # Postgres up + migrate + serve, one command

# in a second terminal, same directory
dst bootstrap --org me --email you@example.com
dst apply
dst query customer_value "How many customers are repeat customers?"

dst init 会在一个捆绑的 jaffle DuckDB warehouse 上搭建一整个项目,并且同时保留( apply 和 query 都可以在你连接真实仓库之前就能工作。这个 release wheel 已经带着 migrations 和 dashboard,所以你可以自己 run dst dev,两个都不动,都挂着后端 http://localhost:8000。quickstart 是主路线,并会把这同一套路线引申到您自己的 warehouse。

从源码运行

贡献者的路径:一份源码检出(checkout)、仓库自带的 Makefile,以及由 Vite 构建(而非打包)的仪表盘。前提条件:以上内容,外加 uv、Node 22+ 和 pnpm。

# 1. Backend deps
make install                 # uv sync

# 2. Configure — create .env in the repo root (see "Configuration" below)
echo 'DST_PROVIDERS={"anthropic": {"type": "anthropic", "api_key": "sk-ant-..."}}' > .env
# openai-compatible works the same:
#   {"ollama": {"type": "openai-compatible", "base_url": "http://localhost:11434/v1", "api_key": "unused"}}

# 3. Start Postgres (pgvector), run migrations, seed an org + admin token
make up
make migrate
make seed                    # prints a dstadm_… admin token — copy it

# 4. Run the API (http://localhost:8000)
make dev

然后是仪表盘:

cd apps/web
pnpm install
pnpm dev                     # http://localhost:5173

打开仪表盘,将右上角的 dstadm_… 管理员令牌粘贴进去,即可治理该组织:审核队列、漂移审计、调用方、成本。Lens 本身以文件形式编写:uv run dst init 会在随附的 jaffle DuckDB 数据仓库上生成一个 Lens,因此在连接真实数据仓库之前,uv run dst apply 和 uv run dst query 就能正常工作。源码检出不会把 dst 放到你的 PATH 上——每条命令都以 uv run dst … 的形式运行,Makefile 目标正是这样实现的。

如果要让某个 agent 查询某个 Lens:在 Settings 中签发一个 caller key,把它加入 lens.yaml 中该 Lens 的 allow-list,然后运行 uv run dst apply,最后通过 MCP 连接该 agent。


配置

设置从 .env 加载(见 services/config.py)。常用键:

变量

是否必填

用途

DST_PROVIDERS

是,至少一个条目

模型提供方,按名称组织 JSON(BYOK;不要使用以厂商命名的键变量)。类型:anthropic、openai-compatible、local。声明顺序就是层级/成本偏好顺序;grounding、组合、审核判定和路由都会通过它解析。

embedding provider

上下文功能需要

DST_PROVIDERS 中提供 embedding 模型的条目:任何 openai-compatible 端点,或免密钥的进程内 local 类型(uv sync --extra local-embed)。认证匹配就是基于该 embedding 的余弦匹配。没有可用的 embedder 时完全无法触发,这也是每个响应的 degraded 列表和 /ready 的 certified_matching 所反映的。

FASTEMBED_CACHE_PATH

可选

local 层存放其 ONNX 权重的位置。默认为 ~/.cache/dst/fastembed(也支持 $XDG_CACHE_HOME),这是刻意不用 fastembed 自己的默认目录——它默认的是操作系统中 macOS 会从底下直接清掉的临时目录。在容器中请将它指向已挂载的卷。

DST_SECRET_KEY

需要存储凭据时

用于加密已存储的数据仓库/上下文凭据的 Fernet 密钥。dst init 会生成一个;也可手动生成:python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"。

DATABASE_URL / DATABASE_ADMIN_URL

本地默认即可

应用(强制 RLS、非超级用户)和管理(迁移/种子)连接。

CLERK_SECRET_KEY / CLERK_PUBLISHABLE_KEY

可选

托管仪表盘的身份认证;没有它也不影响本地登录和管理员令牌。

数据仓库和检索源凭据不是环境变量:请在 dst.yaml 中用 secret_env 引用声明连接(见 quickstart)。.env.example 由 services/config.py 生成:它是一份完整、永不过时的配置表面。


项目布局

services/          FastAPI app (services.app:app)
  api/             control plane (/mgmt/*) + data plane (/v1/*)
  contracts/       lens config, semantic model, protocols
  connectors/      warehouse connectors
  context/         embedding providers + the serving error surface
  runtime/         the query pipeline (ground → guard → execute → compose)
  reviews/         AI-judge + human review queue
  governance/      access policy, credentials, rate limits, audit
  mcp/             remote + stdio MCP server (see its README)
apps/web/          React + Vite dashboard
migrations/        Alembic migrations
fixtures/          built-in jaffle DuckDB warehouse

开发

命令

作用

make up / make down

启动 / 停止本地 Postgres(pgvector)

make migrate

应用数据库迁移(dst migrate:schema + 应用角色密码同步)

make seed

填充一个开发组织和管理员令牌

make dev

在 :8000 上热重载运行 API

make lint

ruff + 格式检查 + mypy + UI 样式门禁

make fmt

自动格式化 + 修复

make test

后端测试(pytest)

pnpm --dir apps/web dev

在 :5173 上运行仪表盘

架构:FastAPI 后端(lens 配置、context 向量存储、请求 trace、审核)架构基于 Postgres + pgvector,并从你的数据仓库读取数据;结果集本身不会被持久化。trace 保留的是问题、SQL、组合后的答案及其引用;只有 lens 通过 logging.log_samples 开启时,才会保存前几行结果。数据仓库 profiling 同样会存储每列的统计信息和低值列表;exclude_columns 中提到的列只读取其结构,值不会被收集。dst 不会自动分类或脱敏个人数据:不要暴露你不希望值离开你网络的列。仪表盘是一个同源或拆分的 React SPA。

用户文档位于 docs/(quickstart、概念、指南、参考),并发布在 https://www.dataservetool.com;贡献者子系统地图请见 ARCHITECTURE.md。


许可证

Apache-2.0。贡献:CONTRIBUTING.md · 安全漏洞:SECURITY.md · 问题与反馈:github.com/get-dst/dst/issues。

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    Not graded
    maintenance
    Enables natural language querying of Microsoft Fabric Data Warehouses with intelligent SQL generation, metadata exploration, and business-friendly result summarization. Features two-layer architecture with MCP-compliant server and agentic AI reasoning for production-ready enterprise data access.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to understand and query your database safely by providing a semantic layer of metadata, with tools to search, explain, validate, and generate safe SQL.
    2
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enforces safety and governance for SQL queries executed by AI agents, providing read-only enforcement, cost estimation, and audit trails.
    Apache 2.0