@_avichawla: 另一个疯狂的 Jev 使用场景!传统数据库过滤需要精确的、预先定义好的条件,但许多问题是语义性的:
摘要
pg-jev 是一个开源的 Postgres 扩展,通过 TypeSafe 的 Jev 模型,让你用自然语言条件对数据行进行过滤、分类和排序,无需向量列或 embedding 索引。
查看缓存全文
缓存时间: 2026/10/03 15:02
又一个疯狂的 Jev 使用场景!
传统的数据库过滤需要精确、预定义的条件。但很多问题是语义性的:
- 这篇文章主要是关于软件工程的吗?
- 哪个主题最适合描述它?
- 它的技术深度看起来如何?
这些通常需要把数据行取出到应用代码中,调用模型,解析输出,再把结果写回。pg-jev 是一个开源的 Postgres 扩展,它通过 SQL 函数暴露了 Jev:
jev()可以在WHERE中使用jev_prob()返回概率jev_choice()选择标签jev_score()在有序层级上对行进行排名
它不需要向量列或嵌入索引。在底层,pg-jev 会批量打包数据行,发送给 Jev,然后返回类型化的答案,SQL 可以对这些答案进行过滤、排序、分组,并与精确谓词组合使用。
下面的录制视频展示了它对存储在 Postgres 中的真实 Hacker News 故事运行的结果。视频首先展示数据行,然后让 Jev 找出软件工程相关的故事、按主题分类,并按技术深度排序。最后的查询会从数据库会话中报告请求数、token 数、成本和缓存命中情况。
GitHub 仓库:https://github.com/realZachi/pg-jev …(别忘了给它点个 Star ⭐)
Postgres 负责保存数据并控制查询,而 Jev 负责处理 SQL 无法表达为确定性条件的部分。如果你想了解 Jev 底层的工作原理,我还写了一份实战指南,教你用开源模型在本地完全离线地构建一个类似 Jev 的模型。请继续往下阅读。
realZachi/pg-jev
来源:https://github.com/realZachi/pg-jev
jev — 用自然语言向你的 Postgres 表提问
CI(https://github.com/realZachi/pg-jev/actions/workflows/ci.yml) PGXN(https://pgxn.org/dist/jev/) License Website(https://pgjev.com)
用你想表达的方式写条件,剩下的交给 Postgres。
jev 让你用自然语言条件来过滤、排序和分类数据行。每一行都由 TypeSafe 的 Jev(https://docs.typesafe.ai)进行判定,Jev 是一个 System One 模型,返回校准过的概率而不是生成的文本。无需索引、无需嵌入、无需向量列。
网站:pgjev.com(https://pgjev.com)
CREATE EXTENSION jev CASCADE;
SELECT * FROM people WHERE jev(people, 'the name is European');
SELECT subject, jev_prob(tickets, 'the customer is angry') AS p
FROM tickets
ORDER BY p DESC
LIMIT 20;
SELECT jev_choice(tickets, 'which team should handle this?',
ARRAY['billing', 'technical', 'security', 'sales']) AS team,
count(*)
FROM tickets
GROUP BY 1;
SELECT name,
jev_score(products, 'how luxurious is this product?',
ARRAY['budget', 'mid-range', 'premium', 'luxury']) AS luxury
FROM products
ORDER BY luxury DESC;
jev() 是一个普通的布尔函数,因此它可以与 SQL 中的其他一切组合使用:AND age > 40、JOIN、GROUP BY、LIMIT、ORDER BY jev_prob(...)。
工作原理
-
jev(table, 'condition')将数据行作为复合值接收。对于某个表 + 条件的首次调用会启动一个预读(read-ahead),按物理顺序流式读取表(TID 范围扫描;视图使用OFFSET分页),因此无论表多大,内存占用都保持恒定。 -
数据行按
jev.batch_size(默认 20)每请求打包成一个共享的 state({"condition": ..., "rows": [...]}),每行对应一个 Yes/No 的 Noul(https://docs.typesafe.ai/primitives/noul)问题。Jev 在一个 state 上并行评估所有问题,这摊薄了约 270 token 的请求开销(单行单独请求约需 435 token,而批量为 20 时每行仅需约 175 token)。 -
最多同时有 2 ×
jev.concurrency个请求通过持久 HTTPS 连接处于飞行中(in-flight),并且每一批返回后就立即回答对应的行。因此LIMIT会在飞行窗口之外停止预读,而在jev()执行前就被更便宜的谓词过滤掉的行(如WHERE age > 60 AND jev(...))会被跳过,而不会送去判定。 -
答案会按行内容在会话内缓存,因此重新执行、更改阈值或按概率排序都是免费的。来自子查询或 CTE(匿名
record类型)的行无法预读,只能每次请求一个请求地判定;尽可能把jev()用在基表或视图上。
在一个来自欧洲的 2,000 行表上的实测(到 API 约 190 ms):首次运行 ≈ 3.5 秒,共发出 100 个请求,约 296k 输入 token,约 $0.012;第二次运行 ≈ 50 ms;对新条件执行 LIMIT 3 ≈ 0.6 秒。在会话中仍持有池化连接(空闲时间少于 jev.keepalive)时使用新条件 ≈ 2.3 秒:每条新连接上的首个请求是最慢的。版本 0.1.0 完整查询需要 8.5 秒(338k token),LIMIT 需要 8.4 秒。
为什么是每请求 20 行?
Jev 需要在数组中按位置找到 rows[i],而在长数组中这变得不可靠。以结构化列作为基准真相(职位名称、欧盟成员身份、自由文本字段中的某个短语;各 400 行)进行测试,1–20 行的批次准确率为 100%,40 行的批次为 92–98%,80 行的批次为 77–94%。即使行更宽(1,000 字符),在 20 行时也没有差异。给行命名而不是用索引引用也没有帮助。20 行的批次比 40 行的批次多花 4% 的 token,但速度一样快,因为请求的延迟几乎不受大小影响。
安装
要求:PostgreSQL 14–17,带 plpython3u(Debian/Ubuntu 上为包 postgresql-plpython3-NN,EDB 和 Postgres.app 构建中已包含),一个超级用户,以及来自 https://console.typesafe.ai 的 TypeSafe API 密钥。不提供超级用户权限或 plpython3u 的托管主机(Supabase、Neon、RDS 等)无法运行它;参见 Where it runs(https://pgjev.com/docs/getting-started/where-it-runs)。
通过 AI 代理安装(最简单)
仓库自带一个 agent skill,可通过 skills.sh(https://skills.sh)安装。将它安装到你的项目中,然后让 Claude Code、Codex、Cursor 或任何其他支持 skill 的代理来完成工作:
npx skills add realZachi/pg-jev
在这台服务器上安装 pgjev 并完成配置。
代理会运行预检(PostgreSQL 版本、plpython3u、超级用户权限),针对正确的 pg_config 执行 pgxn install jev 或 make install,执行 CREATE EXTENSION jev CASCADE,放置 API 密钥并运行冒烟测试。之后它还知道如何编写注重成本的 jev() 查询(“找出客户威胁要取消订阅的工单”),以及如何解释 pgjev 能做什么。文档也可以作为 Markdown 供代理阅读:在 https://pgjev.com/docs 下的任何页面后追加 .md 即可(参见 For agents(https://pgjev.com/docs/for-agents))。
通过 PGXN 安装
pip install pgxnclient # 仅需一次;Debian/Ubuntu 和 Homebrew 中也可用 `pgxn-client`
pgxn install jev # 从 pgxn.org 下载发行版,并对 PATH 上的 pg_config 运行 `make install`
psql -c "CREATE EXTENSION jev CASCADE"
当服务器上的 pg_config 不在 PATH 上,或扩展目录不可写时,使用 pgxn install jev --pg_config=/path/to/pg_config(或 sudo pgxn install jev)。
从源码编译(PGXS)
git clone https://github.com/realZachi/pg-jev.git && cd pg-jev
make install # 使用 PATH 上的 pg_config;或者:make install PG_CONFIG=/path/to/pg_config
psql -c "CREATE EXTENSION jev CASCADE" # 需要超级用户(plpython3u 是不信任语言);CASCADE 会创建 plpython3u
Docker
docker build -t pg-jev . # 如需其他大版本,可加 --build-arg PG_MAJOR=17
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=pw -e TYPESAFE_API_KEY=your-key pg-jev
psql postgres://postgres:pw@localhost/postgres -c "CREATE EXTENSION jev CASCADE"
API 密钥
可以导出 PostgreSQL 服务器进程环境中的 TYPESAFE_API_KEY,也可以按会话或按角色设置:
SET jev.api_key = 'your-key';
ALTER ROLE analyst SET jev.api_key = 'your-key'; -- 持久化,按角色设置
函数
| 函数 | 返回 | 用途 |
|---|---|---|
jev(row, condition [, threshold]) | boolean | WHERE 谓词。阈值:参数 → jev.threshold → 0.5 |
jev_prob(row, condition) | float8 | 该行满足条件的概率,范围 0..1 |
jev_score(row, question, levels text[]) | float8 | 在有序层级上的概率加权位置(0 .. n-1) |
jev_score_norm(row, question, levels) | float8 | 同上,归一化到 0..1 |
jev_choice(row, question, options text[]) | text | 该行最可能的选项 |
jev_confidence(row, question, kind, options) | float8 | score/choice 答案的置信度 |
jev_eval(row, question, kind, options) | jsonb | 完整的原始答案(概率、图例、置信度) |
jev_stats() | jsonb | 本会话的请求数、token 数、预估成本、缓存命中数、飞行中请求数和池化连接数 |
jev_cache_clear() | void | 清除缓存的判定结果 |
jev_version() | text | 扩展版本 |
row 是表别名本身(jev(people, ...))或子查询别名。
配置
所有配置都是普通的 GUC:SET jev.<setting> = ...、ALTER ROLE ... SET、ALTER DATABASE ... SET,或直接写在 postgresql.conf 中。
| 配置项 | 默认值 | 含义 |
|---|---|---|
jev.api_key | 环境变量 TYPESAFE_API_KEY | TypeSafe API 密钥 |
jev.model | jev-latest | 模型名称或固定版本,如 jev-1.13.0 |
jev.threshold | 0.5 | jev() 返回 true 的概率阈值 |
jev.batch_size | 20 | 每个 API 请求的数据行数。超过约 20–25 后准确率会显著下降(见上文) |
jev.concurrency | 16 | 并行 API 请求数;最多有其两倍数量的请求在执行器前方排队 |
jev.max_prefetch_rows | 5000 | 缓存未命中时,预读会向后扫描多远以找到请求的行,以及为后续请求保留多少被跳过的行(内存上限) |
jev.notices | on | 每个完成的请求输出一条进度 NOTICE,每个表输出一条摘要,包含请求数、token 数、预估成本和耗时 |
jev.api_url | https://api.typesafe.ai/v1/systemone | API 端点(可配置代理、模拟服务) |
jev.timeout | 30 | 每个 API 请求的超时秒数。等待是可中断的:statement_timeout 和取消请求会在 250 ms 内生效 |
jev.keepalive | 600 | 池化的 API 连接可空闲多少秒才需要重新连接。新连接上的首个请求需要一次 TLS 握手,实测还有最多 1.5 秒的服务器端初始化,因此请让连接跨查询保持活跃;TCP keepalive 探测会发现被静默断开的连接 |
jev.max_rows_per_statement | 0(关闭) | 中止会向 API 发送超过此行数数据的语句。用于共享部署的费用护栏 |
jev.max_chars_per_statement | 0(关闭) | 同上,针对行数据的字符数 |
编写良好的条件
Jev 会按字面意思回答你写的问题。以下几点会有帮助(更多内容参见 TypeSafe 文档(https://docs.typesafe.ai/model-jaggedness/jev-1.13)):
- 明确表述条件:
'the customer threatens to leave, dispute a charge, or take legal action'(客户威胁要离开、对收费提出异议或采取法律行动)比'churn risk'(流失风险)更好。 - 把算术、日期和精确匹配留给 SQL 处理;让模型来判断语义。
- 在确定阈值之前,先用
jev_prob()看看分布。模糊的情况确实会落在 0.5 附近。 - 只发送判定所需的列:创建一个包含相关列(以及任何预过滤条件)的视图,然后在视图上调用
jev(view_alias, ...)。视图与表一样会被预读和批量处理。
注意事项
- 这是一个设计上的全表扫描:执行器询问的每一行都会发送到 API。
WHERE中更便宜的谓词会先运行,它们过滤掉的行会被跳过;LIMIT会提前停止;jev.max_rows_per_statement可以限制支出。 - 行内容会发送到第三方 API。不要在你不能共享的数据上使用它。
- 缓存存在于后端会话中(PL/Python
GD)。连接池中每个会话都会各自维护自己的缓存。 plpython3u是不信任语言:只有超级用户才能创建扩展,并且函数以服务器的 OS 权限运行。
开发
make docker-test # 构建 test/Dockerfile 并运行回归测试套件(默认 PG_MAJOR=16)
make docker-test PG_MAJOR=17
在本地使用运行中的服务器和 PATH 上的 pg_config:
make install
python3 test/mock_api.py & # TypeSafe API 的确定性替身
make installcheck # pg_regress,测试位于 test/sql,预期输出位于 test/expected
回归测试从不调用真实 API。要尝试真实效果,设置 jev.api_key 并运行任意查询即可。
参见 CONTRIBUTING.md 和 docs/PUBLISHING.md 了解发布流程。
许可证
PostgreSQL License。Jev 和 TypeSafe 是其各自所有者的商标;本项目与 TypeSafe 无隶属关系。
相似文章
@BohuTANG: 我也在做类似的事情。Jev 在这种场景下仍然太慢了。
关于使用 Jev 进行查询优化的讨论,一位用户分享了在 Postgres 查询中实现 12% 速度提升的经验,另一位用户则评论了 Jev 的性能问题。
@yan5xu:Jev 特别适合处理数据量大且对缺失数据有容忍度的场景。每个人都值得一试。
Jev 定位为面向高吞吐量、容忍缺失数据场景的工具,同时推出了一款名为 jevable 的新 CLI,它让智能体通过将自然语言规则与 TypesafeAI 的 Jev 判断相结合,按语义过滤任意数据流,将常驻运行的智能体成本最多降低 86%。
JevGPT
JevGPT 是一款基于 TypeSafe 的 Jev 模型构建的聊天应用。Jev 是一款专为决策而非文本生成而设计的模型,其每条回复都是通过多项选择逐词构建而成。它是一款开源项目,被视为一次有趣但并不完美的实验。
又一个疯狂的Jev用例!Jev让评估代理内部实际发生的事情变得极其便宜…
Beacon是一个开源的AI编码代理记忆层,使用Jev评估代理运行,并将有用的工作流程、纠正和调试模式转化为跨多个工具的可重用技能。
SelfJev
SelfJev 是一个可自行部署的开源 4B 决策模型,支持对任意文本提出有类型的提问(是/否、单选、多选、评分),并给出经过校准的概率;同时通过 TypeSafe 的 SDK 实现 Jev 兼容,并可用自有数据进行微调。