返回
RSS Snowflake Engineering (Medium) AI 逐段翻译 发布 2026-08-12 22:01 收录于 08-13

Cortex Analyst与Snowflake Agents:打造生产级分析服务指南

DataHot 速览

本文针对Cortex Analyst和Agent项目中常见的响应慢、结果不正确问题,指出根源往往在于语义模型、编排指令和工具描述等基础设置未受重视。内容包括如何精简现有语义视图、使用Cortex Search解析真实取值、用验证查询锚定SQL,以及通过工具描述和Agent指令路由问题,从而提升服务准确性与速度。

为什么值得关注:面向生产级Data Agent部署的深度实践指南,直击ChatBI项目落地中的性能和准确性问题,值得数据从业者参考。

本文目录 8 节
  1. 思维模型:两层,四个组件
  2. 1. 让语义模型高效
  3. 2. 使用 Cortex Search 在结构化列上解析混乱的值
  4. 3. 使用经过验证的查询锚定 SQL
  5. 4. 使用工具描述进行路由
  6. 5. 使用代理指令进行编排
  7. 综合起来——并确认它有效
  8. 总结

译文

AI 逐段翻译

在我参与的几乎每一个 Cortex Analyst 或智能体项目中,对话的开场白总是一样的:“响应时间太长”或“结果不正确”。而我的第一个问题也总是相同的。你究竟是如何设置你的分析服务的?你的语义模型长什么样?你的编排指令正确吗?你真的花时间编写工具描述了吗?

大多数时候,诚实的回答是否定的。不是因为团队能力不行——而是因为这些部分看起来像是一次性设置,设置完就可以继续前进。但它们并非如此。每一个都悄然影响着整个服务响应的速度和正确性,而当它们仓促完成时,性能会以难以追溯原因的方式下降。所以人们责怪模型或平台,而真正的问题在于基础从未得到应有的关注。

我观察过太多次这样的情形,以至于坚信一件事:Cortex Analyst 服务有一些关键部件,每个都必须正确,而只做对其中一个而忽略其余部分,正是许多部署显得迟缓或不可信的原因。让它们协同工作,曾经超时或回答错误的问题就会返回正确、格式化且快速的结果——就像资深分析师会回答的那样。

所以这是我希望在团队开始之前就能交给他们的指南。它涉及那些真正决定你的服务是否有效的部分:优化语义模型、使用 Cortex Search 解决杂乱值、使用已验证查询锚定 SQL以及使用工具描述和代理指令路由问题。没有“你好世界”,没有重新教如何创建语义视图——我假设你已经有一个了。接下来要做的就是让它达到生产级。

你将学到:

  • 如何在不重建的情况下让现有语义视图精简、快速且准确
  • 如何使用针对结构化列的 Cortex Search 服务,以便模型基于真实值进行过滤而不是猜测
  • 已验证查询如何作为地面实况锚点,教导模型你领域的 SQL
  • 工具描述和代理指令如何将每个问题路由到正确的位置——在多工具智能体中这是最大的杠杆
  • 如何判断它是否真的有效,以及如何迭代

思维模型:两层,四个组件

在动手之前,最好把整个系统记在心里,因为四个组件清楚地分为两层,各自解决不同的问题。

  • 准确性层——语义视图及其已验证查询——决定了当问题落在正确的语义模型上时 SQL 是否正确。这是值解析、连接逻辑、指标定义和领域词汇所在的地方。
  • 路由层——工具描述和代理指令——决定了问题首先落在哪个语义模型上,以及答案的措辞方式。

这种区别很重要,因为两层的失败方式不同,修复方式也不同。如果财务问题漏入销售模型,再多的已验证查询也救不了你——那是路由问题。如果选择了正确的语义模型但生成的 SQL 连接错误,工具描述也无济于事——那是准确性问题。保持层级清晰,你看到的每个症状都能对应到具体的修复。

本指南的其余部分按顺序讲解两层:首先让单个语义模型准确,然后让代理正确地路由到它。

1. 让语义模型高效

你有一个可行的语义视图。首要任务不是添加内容——而是让它精简。Cortex Analyst 在每一轮都将模型作为上下文发送给 LLM,因此模型的大小和形状是你在延迟和准确性上拥有的最大杠杆。臃肿的模型会消耗更多令牌,推理速度更慢,并给 LLM 更多机会选择错误的连接或错误的列。

保持每个模型只属于一个业务领域。一个覆盖销售和财务和支持的模型,有三次机会让 LLM 混淆重载的术语。单领域模型更易于路由,运行成本更低,也更准确。根据经验法则,如果模型超过约 1500 行或跨越多个明确的领域,那就是拆分模型的信号。销售管道放在一个模型中,总账和收入确认放在另一个模型中。

裁剪到实际查询触及的列。你暴露的每一列都是 LLM 必须阅读和推理的上下文。保留连接键、过滤器、时间和地理维度,以及真实问题中出现的列。删除内部代理 ID、ETL 时间戳、哈希键,以及 90 天查询历史中未被引用的任何内容。大型维度表(客户、产品主数据)应按模型裁剪——销售分析需要 customer_id、名称、区域、细分,而不是 50 个 CRM 属性。每个模型 — 销售分析需要customer_id、name、region、segment,而不需要CRM的50个属性。

将数据质量和范围过滤器放入基表,而不是由 LLM 处理。如果每个正确答案都排除测试记录并从当前财年开始,那么在源头上一次编码:

SELECT *
FROM sales.opportunities
WHERE record_status = 'ACTIVE'
  AND is_test        = FALSE
  AND close_date    >= '2024-01-01'

这里应用的过滤器不能被模型意外省略。你希望LLM 添加的过滤器最终会被遗忘——通常是在你的副总裁为董事会截图的查询上。

一次性定义指标并重用它们。一个命名指标——比如 total_bookings 定义为 SUM(amount) 并附有清晰描述——就是一个契约。它阻止 LLM 在五个问题中五种略微不同的方式重新推导计算,并给已验证查询一个稳定的参考。

编写描述,就像它们是 LLM 得到的唯一指南——因为它们就是。保持简短、业务准确且命令式。当同一指标存在于两种粒度时,命名和描述它们以便模型区分:weekly_bookings(“用于星期、同比或年周问题”)与 monthly_bookings(“用于月、季度或年初至今”)。反模式是三个几乎相同的指标带着相同的描述——LLM 会随机选一个。

关于格式的快速说明:本指南使用原生语义视图(CREATE SEMANTIC VIEW 对象),这是 Snowflake 正在引导新的 Cortex Analyst 工作的地方——它们原生支持 RBAC、共享和经过验证的查询。基于阶段的 YAML 语义模型文件仍然有效,本文中的每一条原则同样适用于它们。

2. 使用 Cortex Search 在结构化列上解析混乱的值

这是整个技术栈中最被低估的准确性杠杆——也是结果出错或查询运行缓慢的最常见原因之一。

每当用户在问题中提及某个名称——客户、产品、代表——Cortex Analyst 必须在 WHERE 子句中放入一个字面值。问题在于它不知道该值实际是如何存储的:"adidas" 是保存为 adidas、Adidas AG、ADIDAS,还是 adidas_na?如果让它猜测,它倾向于生成类似 WHERE customer_name ILIKE '%adidas%' 的内容——这既慢,又会匹配到像 "adidas reseller" 这样的误报,并且常常返回错误的数字而不报错提示您。只要您的用户通过名称而非 ID 来引用实体,这个问题就值得解决。

解决方法是让模型在编写 SQL 之前搜索您实际的列值。Cortex Analyst 对将Cortex Search 服务绑定到维度提供了一流支持。当它检测到需要的字面量时,它会查询搜索服务,获取真实存储的值,并基于精确匹配进行过滤。

-- 1. Index the distinct values of the high-cardinality dimension
CREATE OR REPLACE CORTEX SEARCH SERVICE sales.search.customer_name_search
  ON customer_name
  WAREHOUSE = wh_search
  TARGET_LAG = '1 hour'
  AS (SELECT DISTINCT customer_name FROM sales.dim_customer);

然后将其绑定到语义模型中的逻辑维度:

dimensions:
  - name: customer_name
    expr: customer_name
    cortex_search_service:
      service: customer_name_search
      literal_column: customer_name   # optional; defaults to the search column

您不必手动编写这个绑定——Snowsight 中的语义视图编辑器允许您直接在 UI 中将 Cortex Search 服务附加到维度,并为您生成绑定。

收益是叠加的:

  • 正确的过滤器—— 使用 WHERE customer_name = 'Adidas AG' 而不是模糊的 ILIKE,后者会静默地多算或漏算。
  • 更少的澄清往返—— 模型预先解析实体,而不是询问用户,或猜测并重试。
  • 更精简的模型—— 您可以停止在 YAML 中为高基数列填充庞大的 sample_values 数组;搜索服务现在是有效值的真相来源。

何时使用哪种:对于低基数维度(大致 1-10 个稳定值,如 deal_stage 或 region),模型中的少量 sample_values 就足够了,无需额外服务。对于高基数或频繁变化的列(客户、产品、代表、账户),请使用 Cortex Search 服务。当前预览版的一个注意事项:一个逻辑维度支持单个 Cortex Search 服务,因此请对模糊匹配实际影响最大的列建立索引。

大多数团队将 Cortex Search 视为非结构化文档工具。将其指向您的结构化维度值,它就成为 Cortex Analyst 的准确性倍增器。

3. 使用经过验证的查询锚定 SQL

优化的模型告诉 LLM 存在什么。经过验证的查询 (VQR)告诉它好的样子。经过验证的查询是一个自然语言问题与已知正确的 SQL 答案配对,并标记为已验证——在原生语义视图中,您可以直接使用 AI_VERIFIED_QUERIES 子句附加它们。

它们同时做三件事:

  1. 少样本教学。在查询时,Cortex Analyst 检索与用户问题最相似的 VQR,并将其用作工作示例。这就是它学习您的连接模式、过滤措辞和 NULL 处理约定的方式——然后将其适应到新问题。VQR 是可推广的模式,而非要重播的模板。
  2. 信任信号。当问题与 VQR 高度匹配时,Analyst 可以直接返回该经过验证的 SQL,并带有验证标记,完全跳过生成。
  3. 领域词汇。VQR 是编码“当用户说bookings时,我们指的是 amount 其中 stage = 'Closed Won',并过滤到财年”的最佳位置——这是模式本身无法表达的棘手业务规则。

数量多少?目标是每个模型20-50 个高质量的 VQR——对于热门的、频繁查询的模型 20-30 个,冷模型更少。这种情况下并非越多越好:太多重叠的 VQR 会降低检索质量,因为模型开始拉取近乎重复的示例而不是多样化的示例。覆盖面胜过数量。健康的一组应涵盖不同的意图——聚合、时间比较(同比、环比、年初至今)、地理细分、Top-N 排名、比率计算和多表连接——而不是“总销售额”的十五种改写。

-- One VQR: a ranking pattern the model will generalize to other dimensions/periods
SELECT
    COALESCE(region, 'Unknown')            AS region,
    ROUND(SUM(bookings_amount), 2)         AS total_bookings
FROM sales.opportunities
WHERE stage = 'Closed Won'
  AND YEAR(close_date) = YEAR(CURRENT_DATE())
GROUP BY 1
ORDER BY total_bookings DESC
LIMIT 10;

注意这个单一示例教了什么:COALESCE 处理 NULL 维度,ROUND 处理金额,时间过滤,以及清晰的 Top-N 形状。接下来问“本季度前 10 名产品”,模型会复用所有这些习惯。

VQR 从何而来?不要坐在办公桌前凭空捏造。挖掘您现有的仪表板(每个 KPI 图块都是 VQR 候选),采访五到十位真实用户,了解他们每天早上问的第一个问题,并从您的 BI 日志中提取最常见的查询模式。从 5-10 个最高价值的问题开始,发布,然后根据人们实际提出的问题扩展集合。

保持 VQR 健康的一条规则:切勿使用 VQR 来掩盖缺失的指标或过滤器。如果您发现自己一遍又一遍地将相同的计算写入 VQR,请将其提升为模型中的命名指标或过滤器,以便在任何地方可复用。VQR 教授模式;它们不应该是属于模型的逻辑的倾倒场。

4. 使用工具描述进行路由

现在进入路由层。一旦您有多个语义模型——比如销售和财务——代理必须决定哪个模型来回答每个问题。它几乎完全根据您附加到每个 Cortex Analyst 工具的工具描述来做决定。将描述视为工具的文档字符串;它是整个设置中 ROI 最高的文本。

一个强大的工具描述回答五件事:它涵盖哪些领域和实体,粒度是什么,时间范围是什么,哪些问题属于这里(三到五个具体示例),以及——关键的是——哪些问题不属于这里,而是由哪个模型负责,并加以说明。

Tool: sales_analyst
Description: Use for sales pipeline, opportunities, accounts, reps, quotas,
bookings, and win/loss. Covers FY22-present at daily grain.
Examples: "top 10 reps by bookings this quarter", "pipeline coverage by region".
Do NOT use for GL, revenue recognition, or headcount -> use finance_analyst.

Tool: finance_analyst
Description: Use for GL, revenue recognition, P&L, budget vs actual, and expense
questions. Monthly grain, FY20-present.
Examples: "YTD opex by cost center", "revenue vs budget variance".
Do NOT use for sales pipeline or CRM questions -> use sales_analyst.

那些相互的“不要使用”交叉引用是你能添加的最高杠杆的东西。它们阻止“收入”问题被错误地路由到错误的语义模型,并阻止代理调用两个并合并不应该合并的结果。如果两个语义模型共享一个术语(两者都有某种“收入”概念),那么否定排除加上不同的实体名称(“预订管道”与“已确认收入”)就是消除歧义的关键。

保持描述具体且最新。过于宽泛的描述(“所有业务数据”)会吸引无法回答的问题;过时的描述(在你添加FY26后仍然写着“FY22”)会在时间过滤上误导路由。

5. 使用代理指令进行编排

工具描述决定问题去哪里。代理指令管理围绕该决策的行为——代理如何规划,以及如何措辞答案。它们有两种形式,值得将它们与准确性层中的所有内容区分开来:代理指令决定调用哪个工具以及如何回应;它们并不改善给定模型生成的SQL。那是VQR的工作。

编排指令是简短的跨领域规则,规划者应用于每个问题:

  • 始终调用工具——绝不要凭记忆回答。“不要估算或基于一般知识回答。你必须调用工具来检索真实数据。”这一条规则就消除了整类自信但错误的幻觉回答。
  • 在猜测之前先消除歧义。“如果问题可以由任一分析师回答(例如‘收入’),在调用工具前询问他们是指预订收入(销售)还是已确认收入(财务)。”
  • 不要扩散。“除非用户明确要求跨域比较,否则不要调用多个分析师并合并结果。”
  • 回退到提问。“如果没有明确适用的工具,请提出澄清问题而不是选择一个。”

回答指令塑造答案本身:格式化数字并添加千位分隔符和单位,一致地表达增量(▲上升,▼下降),首次使用缩写时展开,使用表格呈现多行结果,并以数据新鲜度说明和几个建议的后续问题结束。这些是原始结果集与副总裁真正信任的东西之间的区别。

一个值得深思熟虑的操作细节是:编排模型。默认情况下,Snowflake推荐自动——Cortex为你的账户选择最高质量的可用模型,并在更好的模型发布时自动升级。这对大多数团队来说是正确的起点。权衡是可复现性:路由行为对规划器模型敏感,因此如果你需要明天的行为与今天相同——受控评估、变更管理、严格审计——固定一个特定模型(例如claude-4-sonnet),并自己承担管理升级的工作。从自动开始;当你有了具体原因时再固定。

综合起来——并确认它有效

这里是整个系统端到端回答开头问题的示例。副总裁问“本季度按区域的管道覆盖率”。代理读取工具描述,路由到sales_analyst(而不是财务——否定排除起了作用)。Cortex Analyst读取优化的模型,将“区域”与样本值解析,检索类似的已验证查询作为模式,并生成干净的分组聚合。回答指令将其格式化为表格,带有新鲜度标记和两个后续问题。一句话进去,一个可信的答案出来。

配置为代理规范,形状很紧凑:

models:
  orchestration: claude-4-sonnet     # optional; omit to let Snowflake auto-select (its default)
orchestration:
  budget:
    seconds: 30
    tokens: 16000
instructions:
  orchestration: |
    Always call a tool for data; never answer from memory.
    Ask to clarify ambiguous terms like "revenue" before routing.
    Do not merge results across tools unless asked to compare.
  response: |
    Use thousand separators and units. Show deltas as up/down.
    End with data freshness and 2 suggested follow-ups.
tools:
  - tool_spec:
      type: cortex_analyst_text_to_sql
      name: sales_analyst
      description: "Sales pipeline, bookings, quotas... Do NOT use for finance -> finance_analyst"
  - tool_spec:
      type: cortex_analyst_text_to_sql
      name: finance_analyst
      description: "GL, revenue recognition, P&L... Do NOT use for pipeline -> sales_analyst"
tool_resources:
  sales_analyst:
    semantic_view: "sales.analytics.sales_sv"
  finance_analyst:
    semantic_view: "finance.analytics.finance_sv"

一个容易遗漏的细节:models.orchestration键设置规划器模型,而顶层编排块用于预算(时间和令牌上限)。而且每个Cortex Analyst工具都需要在tool_resources中有一个匹配的条目指向其语义视图——没有资源绑定的工具是无法工作的。

不要凭空调优——要测量。收集30-50个真实问题,并标记每个问题应该由哪个工具回答(销售、财务、任一、都不是)。通过代理运行它们,查看两个数字:是否路由到正确的工具,生成的SQL是否返回了正确的答案?错误路由指向工具描述和编排规则;在正确的语义模型上错误的SQL指向已验证查询和语义模型本身。编辑,重新运行同一组,并跟踪准确率直到稳定。发布后,继续从实际使用中学习:SNOWFLAKE.ACCOUNT_USAGE.CORTEX_AGENT_USAGE_HISTORY(和CORTEX_ANALYST_USAGE_HISTORY)为你提供每个请求的信号——令牌、积分、哪个用户、哪个代理——但不提供原始问题文本。从你的应用层或Agent/Analyst API响应中捕获用户实际提出的问题,将它们与那些使用信号配对,并将失败反馈回去。这个循环——不是一次性的构建——是区分演示与服务的关键。

总结

一个生产级的Cortex Analyst服务是跨两个层的四个组件,每个组件都有明确的工作:

大多数团队陷入的陷阱是只调优其中一个,然后奇怪为什么服务仍然感觉不可靠。只有当四个组件协同工作时,收益才会复合:优化模型使其快速且无歧义,添加Cortex Search使其在真实值上过滤,用一组紧凑的已验证查询锚定它,然后用尖锐的工具描述和几条坚定的代理指令包装它。做到这一点,下次你的副总裁用纯英语提问时,她会得到高级分析师会给出的答案——只需几秒钟,而且正确。

然后保持评估循环运行。四个组件让你进入生产;循环让你留在那里。

Cortex Analyst + Snowflake Agents:生产级分析师服务的权威指南 ❄️最初发布在Snowflake Builders Blog:数据工程师、应用开发者、AI与数据科学在Medium上,人们在那里通过突出显示和回应这个故事继续对话。

这篇内容对你有用吗?

反馈只用于改善内容筛选,不等同于收藏

分享这条资讯
分享海报
保存图片
iOS 也可以长按图片保存