ClickHouse 发布 SQL 内置 AI Functions(Beta)
DataHot 速览
ClickHouse 推出内置 AI Functions,可在 SQL 引擎中直接调用 LLM 或 embedding 服务,把模型调用变成类似 SUM() 的 SQL 函数。相关功能处于 Beta,按版本逐步加入:26.4 引入 aiGenerate/aiClassify/aiExtract/aiTranslate,26.6 加入 aiEmbed,26.8 加入 aiFilter/aiRedact/aiSimilarity。官方称其目标是“把模型搬到数据旁”,减少将数据抽到外部 RAG、向量库和编排框架的复杂度。ClickHouse Cloud 中无需额外配置,目前 AI Functions 处于 private preview。
为什么值得关注:数据从业者可关注在数仓/OLAP 内直接完成分类、抽取、翻译、embedding 和语义搜索,减少数据搬运与多栈编排;这也体现数据库平台 AI 化趋势。
本文目录 13 节
译文
AI 逐段翻译ClickHouse 现在有一系列内置的 AI Functions,它们直接从 SQL 引擎调用 LLM 或嵌入提供程序。模型变成了你可以从 SQL 中调用的东西,就像 lower() 或 sum() 一样,并且在你数据已经所在的地方运行。
这些函数目前处于 Beta 阶段,功能正在不断添加和改进。它们在三个版本中陆续推出:aiGenerate()、aiClassify()、aiExtract() 和 aiTranslate() 在 26.4 中推出,aiEmbed() 在 26.6 中推出,然后 aiFilter()、aiRedact() 和 aiSimilarity() 在 26.8 中推出。
在 ClickHouse Cloud 中试用 AI Functions
在 ClickHouse Cloud 中,无需下文提到的任何额外配置——这些函数开箱即用。目前,AI Functions 在 ClickHouse Cloud 中处于私有预览阶段。
我们为什么开发这个
你的所有数据都已经存放在 ClickHouse 中:日志、产品评论、描述和支持工单。传统的 LLM 工作流(如 RAG)将数据从数据库拉取到单独的栈中运行分类或嵌入任务,然后再将结果推回。这种方法慢、容易出错,并且增加了运维复杂性。
AI Functions 背后的核心思想是将模型移动到数据,而不是将数据移动到模型。由于 ClickHouse 已经存储并可以 搜索向量,完整的 RAG 循环可以在一个系统中运行,而不是在向量数据库、编排框架和单独的 LLM API 之间拼凑起来。
一个简单的例子:
1SELECT aiClassify('I love this product!', ['positive', 'negative', 'neutral']);响应:
1positive一个查询中的 AI Functions
简而言之,AI Functions 将复杂的工作流转变为简单的 `SELECT` 查询。
Beta 中包含什么
以下 AI Functions 在 26.8 中可供你使用。文本函数:
- aiClassify - 使用 LLM 提供程序将给定文本分类为所提供的类别之一。
- aiExtract - 使用 LLM 提供程序从非结构化文本中提取结构化信息。
- aiGenerate - 使用 LLM 提供程序根据提示生成自由形式的文本内容。
- aiTranslate - 使用 LLM 提供程序将给定文本翻译为指定的目标语言。
- aiFilter - 使用 LLM 提供程序对给定文本评估自然语言条件,并返回适合用于
UInt8的布尔值(WHERE、PREWHERE和JOIN ... ON)。 - aiRedact - 使用 LLM 提供程序检测并脱敏给定文本中的个人身份信息(PII)。
嵌入函数:
- aiEmbed - 使用配置的 AI 提供程序为给定文本生成嵌入向量。
- aiSimilarity - 使用配置的嵌入提供程序计算两个文本的语义相似度。
AI Functions 的工作原理是对每个输入(或嵌入函数的一批输入)向配置的提供程序 API 发起远程 HTTP 调用,并将解析后的响应作为原生 ClickHouse 值返回。
准备设置(仅 OSS)
要开始使用 AI Functions,你必须配置两个 命名集合(分别用于文本和嵌入),以存储你的提供程序凭据和配置。
我们建议配置设置 ai_function_text_default_credentials 和 ai_function_embedding_default_credentials,值为命名集合的名称。然后,所有 AI Functions 将自动选取正确的端点。也可以在执行期间覆盖这些设置。
创建带 OpenAI 提供程序凭据的命名集合的示例语句:你需要一个来自 OpenAI 的 API 密钥,一个用于 chat 端点,另一个用于 embedding 端点:
1CREATE NAMED COLLECTION ai_text_credentials AS2 provider ='openai',3 endpoint ='https://api.openai.com/v1/chat/completions',4 model ='gpt-5.6-terra',5 api_key ='sk-...';67-- The embedding functions (`aiEmbed`, `aiSimilarity`) do not read `model` from the named collection.8CREATE NAMED COLLECTION ai_embedding_credentials AS9 provider ='openai',10 endpoint ='https://api.openai.com/v1/embeddings',11 api_key ='sk-...'; 注意:任何 OpenAI 兼容的 API(例如 Ollama、LiteLLM)都可以通过设置 provider = 'openai' 并将端点指向你的服务来使用。因此,这也可以与本地模型一起使用。接下来,配置默认凭据设置:
1SET ai_function_text_default_credentials ='ai_text_credentials';2SET ai_function_embedding_default_credentials ='ai_embedding_credentials';现在你已准备好运行这些函数。让我们回顾一些示例。
理解你的数据:分类、过滤
以下示例使用 Hacker News 数据集,包含 2800 万行故事和评论,按照该指南中的 Parquet 模式加载。我们关注的列是 title、comment、author、score、type 和 timestamp。确保你遵循上述设置步骤。
aiClassify 接受一个字符串和一个常量标签列表,并准确返回其中一个标签。模型被要求选择一个类别,因此在这里它的创造力有限。
首页标题是一个很好的起点,因为它们很短,可以保持 token 使用量低:
1SELECT2 title,3 aiClassify(title, ['space', 'security', 'databases', 'startups', 'programming', 'other']) AS topic4FROM hackernews5WHERE (type ='story') AND (score <3000) AND (title !='')6ORDERBY score DESC7LIMIT 3 FORMAT Vertical;响应:
1Row1:2──────3title: SpaceX’s Falcon Heavy successfully launches4topic: space56Row2:7──────8title: Twitter Will Allow Employees to Work at Home Forever9topic: other1011Row3:12──────13title: No Cookie for You14topic: security由于输出是 String,它可以无缝地与标准 SQL 集成。你可以在子查询中使用 aiClassify,然后对结果进行聚合。对于任何你计划查询多次的内容,将其分类到列中,而不是重新计算。
用 SQL 分析,而不是 Python
这是对高分 Hacker News 故事的主题分解,没有使用一行 Python 计算得出。在旧工作流中,这需要导出、分类任务,然后再加载回 ClickHouse。
aiFilter 返回 UInt8,这意味着它可以直接放入 WHERE,并像任何其他布尔条件一样表现。这个函数最清楚地做了 SQL 以前无法做到的事情。Token 搜索查找包含单词 "database" 的行。aiFilter 查找有人抱怨数据库的行:
1SELECT author, substring(comment, 1, 100) AS snippet 2FROM (3SELECT author, comment 4FROM hackernews5WHERE type ='comment'AND ilike(comment, '%database%') 6 LIMIT 5007)8WHERE aiFilter(comment, 'the author is describing a production incident or outage they experienced') LIMIT 3 FORMAT Vertical;响应:
1Row1:2──────3author: 0x04snippet: Wow, the database connection ip and dbname were taken from http cookies!56Row2:7──────8author: AccountCreated9snippet: > The primary MCP database is comprised of9 MongoDB shards10enough said.1112Row3:13──────14author: tomazzi15snippet: The link goes to "Database Error - Error establishing a database connection" which is kind of intere注意:由于 AI Function 调用处理速度可能较慢,最好先在子查询中应用便宜的谓词,然后在外部查询中调用 LLM 谓词。
重塑你的数据:生成、翻译
前面的分类示例将文本放入桶中;接下来的两个函数生成新文本。我们使用相同的数据集和与上面相同的设置。
aiGenerate 接受一个提示并返回模型写回的任何内容。在 SQL 上下文中,提示通常使用 concat 从列构建。
Hacker News 评论是一个很好的目标,因为其中许多很长,这是摘要的绝佳候选。在此示例中,我们还使用 params 映射,带有 system_prompt 和 temperature 参数,以保持各行输出形状一致:
1SELECT2 author,3 length(comment) AS original_chars,4 aiGenerate(5 concat('Summarize this Hacker News comment in one sentence: ', comment),6 map('system_prompt', 'You are terse. Reply with one sentence and no preamble.',7'temperature', '0.3',8'max_tokens', '2000'9)10 ) AS summary11FROM hackernews12WHERE type ='comment'AND length(comment) >100013LIMIT 3 FORMAT Vertical;响应:
1Row1:2──────3author: 0-_-04original_chars: 10765summary: Some VPN providers have been court-verified to keep no logs, and given their financial incentive to protect their reputation plus mandatory data retention laws in many countries, using a reputable VPN is likely more private than relying on your local ISP.67Row2:8──────9author: 0-_-010original_chars: 142811summary: The comment shares Our World in Data links comparing COVID-19 confirmed cases, deaths, andcase fatality rates across several countries using7-day rolling averages.1213Row3:14──────15author: 0-_-016original_chars: 133717summary: The commenter is drawing a parallel to Wim Hof, a man famous for extreme cold endurance feats and the ability to consciously control his immune system through a method combining cold exposure, breathing, and meditation.你还可以使用一些有趣的模式,例如按组生成一次而不是按行生成一次,类似这样:
1SELECT aiGenerate(2 concat(3'Write a three-bullet digest of what Hacker News was discussing. Titles:\n',4 arrayStringConcat(groupArray(title), '\n')5 )6) AS digest;aiTranslate接收文本和目标语言,目标语言可以是语言名称或 BCP-47 代码。值得了解的参数是 instructions,它向模型传递风格或方言方面的指导:
1SELECT title, aiTranslate(title, 'Spanish', map('instructions', 'Use polite form. Keep technical terms and product names in English.')) AS title_es 2FROM hackernews3WHERE type ='story'AND score <3000AND title !=''4ORDERBY score DESC LIMIT 3 FORMAT Vertical;响应:
1Row1:2──────3title: SpaceX’s Falcon Heavy successfully launches4title_es: El Falcon Heavy de SpaceX se lanza con éxito56Row2:7──────8title: Twitter Will Allow Employees to Work at Home Forever9title_es: Twitter Permitirá a sus Empleados Trabajar desde Casa para Siempre1011Row3:12──────13title: No Cookie for You14title_es: Sin Cookie para Ti由于这两个函数都返回 String,它们可以嵌套使用。例如,先总结一条长评论,再翻译该总结:
1aiTranslate(2 aiGenerate( concat('Summarize in one sentence: ', comment),3 map('system_prompt', 'Reply with one sentence, no preamble.') ),4'es-MX' ) AS resumen;完整的 RAG 循环,在数据库中
由 aiEmbed 和向量搜索驱动的检索增强生成

现在每个阶段都可以在 SQL 中执行:
- 在写入时使用物化视图中的
aiEmbed()进行嵌入,这样向量会随着数据写入而填充。 - 将数据和嵌入向量存储在一起
- 使用向量相似度索引建立索引
- 使用
cosineDistance针对嵌入数据进行检索。 - 使用
aiGenerate基于检索到的上下文生成答案
请注意,aiSimilarity() 是用于临时工作的便捷路径:它在一次调用中嵌入两侧并返回余弦相似度,非常适合语义去重或对几千行数据进行排序。
为花费设置上限
与大多数 ClickHouse 函数不同,AI 函数除了 CPU 周期和内存使用之外,每次调用还有 token 和美元成本。在部署此类函数时,限制每次查询的 token 成本和使用量很可能是首要考虑的问题。为帮助避免失控查询和令人咋舌的 AI 使用账单,我们实现了一组配额设置,可用于限制每次查询的 AI 函数使用量。
以下会话设置用于控制这些配额:
- ai_function_max_input_tokens_per_query(默认 1000000)
- ai_function_max_output_tokens_per_query(默认 500000)
- ai_function_max_api_calls_per_query(默认 1000)
以及一个用于控制达到配额限制时错误行为的设置:ai_function_throw_on_quota_exceeded(默认 1 - 抛出错误)。
上述设置可以这样使用:
1SELECT2 title,3 aiClassify(title, ['space', 'security', 'databases', 'startups', 'programming', 'other']) AS topic4FROM hackernews5WHERE type ='story'AND score >100AND title !=''6LIMIT 50007SETTINGS ai_function_max_api_calls_per_query =100;文本函数每行发出一个请求,因此调用预算实际上就是行预算。此查询想要 5000 行,但只允许 100 个请求,因此它会停止:
1Code: 290. DB::Exception: AI API call limit reached: 100 calls made, maximum: 100.2This is controlled by the 'ai_function_max_api_calls_per_query' setting. (LIMIT_EXCEEDED)计数是精确的,因为配额在每次请求分发之前都会检查,所以查询永远不会超出其调用预算。将其设置为 0 会禁用该限制。
Token 配额的工作方式相同,但跟踪的是提供商实际报告的内容,这更接近你实际被计费的内容。总结长 Hacker News 评论是成本高昂的情况,因为整条评论都会进入提示词:
1SELECT2 author,3 aiGenerate(4 concat('Summarize this Hacker News comment in one sentence: ', comment),5 map('system_prompt', 'You are terse. Reply with one sentence and no preamble.',6'temperature', '0.3')7 ) AS summary8FROM hackernews9WHERE type ='comment'AND length(comment) >100010LIMIT 200011SETTINGS12 ai_function_max_input_tokens_per_query =500000,13 ai_function_max_output_tokens_per_query =50000;1415Code: 290. DB::Exception: AI input token limit reached or exceeded: 500642 tokens consumed,16maximum: 500000. This is controlled by the 'ai_function_max_input_tokens_per_query' setting.注意"达到或超过"。一次调用的 token 成本要等到其响应返回后才知道,因此总量在每个线程上最多可能超出一个在途请求的量。请留出余量,而不是将限制设置为你能承受的精确数字。
中止是正确的默认行为,但并不总是你想要的。在 99% 时停止长时间的分类运行并返回空结果,比返回大部分结果更糟糕。ai_function_throw_on_quota_exceeded = 0 将配额转变为软停止:
1SELECT2 topic,3count() AS stories4FROM (5SELECT aiClassify(title, ['space', 'security', 'databases', 'startups', 'programming', 'other']) AS topic6FROM hackernews7WHERE type ='story'AND score >100AND title !=''8 LIMIT 50009)10WHERE topic !=''11GROUPBY topic12ORDERBY stories DESC13SETTINGS14 ai_function_max_api_calls_per_query =1000,15 ai_function_throw_on_quota_exceeded =0;超出配额的行会接收该列的默认值,对于 String 来说是空字符串,查询成功。使用 WHERE topic != '' 将它们过滤掉,这样你得到的是部分但真实的聚合结果,而不是异常。
要查看查询实际花费了多少,请从 system.query_log 中读取 profile events:
1SELECT2 ProfileEvents['AIAPICalls'] AS api_calls,3 ProfileEvents['AIInputTokens'] AS input_tokens,4 ProfileEvents['AIOutputTokens'] AS output_tokens,5 ProfileEvents['AIRowsProcessed'] AS rows_processed,6 ProfileEvents['AIRowsSkipped'] AS rows_skipped7FROM system.query_log8WHERE query_id ='hn_classify'AND type ='QueryFinish'9ORDERBY event_time DESC10LIMIT 1 FORMAT Vertical;1112Row1:13──────14api_calls: 100015input_tokens: 2431016output_tokens: 312217rows_processed: 100018rows_skipped: 4000AIRowsSkipped 是每当 ai_function_throw_on_quota_exceeded = 0 时需要关注的指标,因为它统计的是悄然获得默认值的行数,这些行可能来自配额削减或错误。确定配额大小最便宜的方法是先对 LIMIT 100 样本运行此查询,然后乘以相应倍数。
配置配额时需要记住的几点:
- 在顶层查询中设置它们。 子查询上的
SETTINGS子句对于配额设置会被忽略。 - 它们是每服务器、每查询片段的。在一个执行上下文中,上限是精确的,并且由每个 AI 函数、块和线程共享。分布式查询最多可以在每个分片上分发到该限制,在每个分片上,因此请除以你的分片数量。
- Token 配额需要提供商报告使用量。OpenAI、Anthropic 和 vLLM 会报告。省略
usage对象的提供商会使 token 计数器保持为0,因此这些限制永远不会触发,你应该改用ai_function_max_api_calls_per_query来限制它们。 - 嵌入函数从不产生补全 token,因此输出 token 限制不适用于
aiEmbed或aiSimilarity。 - 重试会计入调用配额。
ai_function_max_retries默认为1,因此如果提供商返回瞬时错误,1000 的预算覆盖的是 1000 次尝试尝试,而不是 1000 行。
发布前需要了解的事项
在将此功能投入生产时,请牢记这些顾虑和限制:
- 提示词注入:输入文本会引导模型,因此请将输出视为不可信,绝不要将其输入到生成的 SQL 或 shell 命令中。
- 非确定性:相同的行输入到 LLM 会得到不同的答案,因此请使用 temperature = 0,并考虑物化结果而不是重新计算它们。
- 成本: 成本和延迟随行数增长,因此请始终先用 LIMIT 测试,并利用上述配额设置
- 安全性:将
remote_url_allow_hosts限制为你的提供商,保持端点在 HTTPS 上,并记住提供商在 TLS 终止后以明文看到你的数据。
结论
AI Functions 将分类、翻译、嵌入和生成放到了 ClickHouse 世界中的 SQL 地图上。完整的 RAG 循环现在可以在数据已经所在的地方运行。期待听到你如何基于此功能构建你的应用。
在 ClickHouse Cloud 中尝试 AI Functions
AI Functions 在 ClickHouse Cloud 中处于私有预览阶段。
这篇内容对你有用吗?
反馈只用于改善内容筛选,不等同于收藏