返回

把Cortex Agent当代码管:从沙箱到生产的CI/CD实践

RSS Snowflake Engineering (Medium) AI 逐段翻译 发布 2026-11-11 23:01

有效阅读仅保存在当前浏览器,用于「为你」推荐;暂停或清除记录。

DataHot 速览

作者用一周搭好面向财务域的Snowflake Cortex Agent,却花了更长时间把它从沙箱安全推到生产。文章指出Agent的真实行为由工具描述、编排指令、语义视图与验证查询、Cortex Search服务及授权共同决定,手工在UI改动会带来无diff、无评审、无法回滚的风险。作者主张用CREATE AGENT ... FROM SPECIFICATION的YAML规格将Agent纳入Git与Azure DevOps,并与Terraform管理的仓库、角色、授权保持同一套纪律。

原收录理由(待核验):这是Data Agent落地生产中少见的工程化实践,讲清了Agent配置漂移、环境晋级与可回滚性等数据团队真实痛点。

本文目录 9 节
  1. 构建这个智能体只用了一周。安全地把它从沙箱推进到生产花了更长时间,也教会了我更多东西。
  2. 1. 误区:“智能体不过是 UI 里的一个配置”
  3. 2. 智能体所需的一切都放进仓库
  4. 3. 智能体是简单的部分。语义视图才是依赖项。
  5. 4. 流水线:与其他所有内容相同的晋升路径
  6. 5. “它部署了”不等于“它能用”
  7. 6. 授权是发布的一部分
  8. 我会再次以同样方式做的事
  9. TL;DR

译文

AI 逐段翻译

构建这个智能体只用了一周。安全地把它从沙箱推进到生产花了更长时间,也教会了我更多东西。

我读过的关于 Cortex Agent 的内容大多都止步于同一个地方:Snowsight 里一个能跑的演示、一个巧妙的问题、一个正确的答案。

而我的真正工作正是从那里开始的。

在我最近的项目中,我为财务领域构建了一个 Cortex Agent。智能体本身很快就搭好了。真正需要认真思考的是没人做演示的那部分:用我们已经应用到平台上其他所有 Snowflake 对象上的同等严谨态度,把它推进到 sbx、dev、uat 和 prod 环境。

在构建数据平台大约十年之后,我学会了怀疑任何只存在于一个环境中的东西。以下就是我把智能体当作代码来对待时学到的东西,以及我会再次照做的做法。

一个无法从 Git 重新构建的智能体就只是一个原型,无论它的回答有多好。

1. 误区:“智能体不过是 UI 里的一个配置”

很容易把 Cortex Agent 看作表单里的几项设置:选一个语义视图、加一个搜索服务、写一些指令,就完成了。

但看看究竟是什么决定了智能体能否给出好的回答:

  • 工具描述,它告诉编排器何时使用哪个工具
  • 编排与响应指令
  • 语义视图,即 Cortex Analyst 背后的语义视图,包括其已验证查询
  • Cortex Search 服务 以及它暴露的列
  • 授权,它决定谁可以调用该智能体以及它能触达什么

以上每一项都是行为变更。如果有人手工在 prod 中修改了一处工具描述,智能体就可能开始以不同方式路由问题,而你没有 diff、没有评审,也没有回退的途径。

在我们的平台上,仓库、角色、schema 和授权早已通过 Terraform 和 Azure DevOps 管理。让智能体成为人们唯一靠点击拼出来的东西,这说不通。

那么,如果智能体不在 UI 里,它实际存在于哪里?

2. 智能体所需的一切都放进仓库

好消息是,Snowflake 提供了一种清晰的方式把智能体表达为代码。CREATE AGENT ... FROM SPECIFICATION 接受一个 YAML 规格,涵盖模型、编排、指令、工具和工具资源。

我使用的模式经过精简后大致如下:

CREATE OR REPLACE AGENT <% db %>.AGENTS.FINANCE_AGENT
  COPY GRANTS
  COMMENT = 'Finance domain agent. Managed by CI/CD, do not edit by hand.'
  FROM SPECIFICATION
  $$
  models:
    orchestration: auto
  orchestration:
    budget:
      seconds: 30
      tokens: 16000
  instructions:
    orchestration: >
      Use FinanceAnalyst for questions about balances, flows and
      period-over-period numbers. Use PolicySearch for questions about
      definitions, methodology or policy documents.
    response: "Answer concisely. State the period and source for every number."
    sample_questions:
      - question: "What were net contributions last quarter?"
  tools:
    - tool_spec:
        type: "cortex_analyst_text_to_sql"
        name: "FinanceAnalyst"
        description: "Structured finance metrics from the curated finance semantic view."
    - tool_spec:
        type: "cortex_search"
        name: "PolicySearch"
        description: "Finance policy and methodology documents."
  tool_resources:
    FinanceAnalyst:
      semantic_view: "<% db %>.SEMANTIC.FINANCE_SV"
    PolicySearch:
      search_service: "<% db %>.SEARCH.FINANCE_POLICY_SEARCH"
      max_results: "5"
  $$;

这里有几个选择比我预想的更重要:

  • 注释写着“请勿手工编辑”。 小事一桩,但它能阻止 prod 中善意的修复。
  • COPY GRANTS 与 OR REPLACE 搭配使用。 没有它,每次重新部署都会清空授权,某人的智能体第二天早上就“停止工作”了。
  • 环境是变量,不是副本。 一份规格文件,数据库名在部署时注入。不会有 finance_agent_dev.sql 和 finance_agent_prod.sql 逐渐偏离的问题。

仓库最终形成了简单的布局:智能体规格、语义视图定义、搜索服务 DDL、授权,以及一组评估问题。

3. 智能体是简单的部分。语义视图才是依赖项。

这是我想要着重强调两遍的教训。

当智能体在 uat 中给出糟糕回答时,智能体规格很少是原因。通常问题出在 语义视图 上:缺少同义词、指标定义与财务实际谈论方式略有不同,或者某个已验证查询没有覆盖一个常见问题。

这改变了流水线设计。语义视图必须在同一发布中先于智能体部署,而且它需要有自己的检查。如果智能体和其语义视图按不同的节奏发布,你测试的就是一个在 prod 中永远不会存在的组合。代理之前在同一版本中,而且它需要自己的检查。如果代理及其语义视图以不同的时间表发布,你就在测试一种在生产环境中永远不会存在的组合。

这也改变了归属。智能体规格是我的。语义视图需要了解财务定义的人来评审。把两者放在同一个 pull request 中,让这种评审变得顺理成章,而不是非得开个会。

4. 流水线:与其他所有内容相同的晋升路径

我们没有为智能体发明新的发布流程。它走的是与平台其他部分相同的 Azure DevOps 路径:基于分支的部署,在 uat 和 prod 之前有带门禁的审批。

精简之后,部署阶段大致如下:

- stage: deploy_uat
  dependsOn: deploy_dev
  jobs:
    - deployment: agent
      environment: snowflake-uat   # approval gate lives on this environment
      strategy:
        runOnce:
          deploy:
            steps:
              - checkout: self
              - script: |
                  snow sql -f semantic/finance_sv.sql   -D "db=FIN_UAT" -c uat
                  snow sql -f search/policy_search.sql  -D "db=FIN_UAT" -c uat
                  snow sql -f agents/finance_agent.sql  -D "db=FIN_UAT" -c uat
                  snow sql -f grants/finance_agent.sql  -D "db=FIN_UAT" -c uat
                displayName: Deploy semantic view, search, agent, grants
              - script: python evals/run_questions.py --env uat
                displayName: Smoke test with known questions

有两点值得特别指出:

  • 顺序是明确的。 先语义视图,再搜索,再智能体,最后授权。在一个脚本中按此顺序部署,可以避免“智能体指向一个尚不存在的视图”这样的失败。
  • 部署智能体的服务账户不是人类账户。 它在目标 schema 上拥有 CREATE AGENT 权限,除此之外没有多余权限。这与我们在整个平台上应用的最小权限思路一致。

5. “它部署了”不等于“它能用”

陷阱就在这里。CREATE AGENT 即使某个工具配置在运行时实际不可用,也可能成功。Snowflake 自己的文档也这么说:创建智能体后要测试它。

那么,对于一个非确定性的东西,测试究竟该是什么样子?

对我有帮助的做法是保持无聊:

  • 每个领域一组固定问题。 财务实际会问的问题,附带预期数字或预期来源。
  • 检查路由,而不只是答案。 政策类问题是否走了搜索,指标类问题是否走了 Analyst?错误的路由往往是工具描述退化的第一个迹象。
  • 部署后在每个环境中运行它。 相同的问题,相同的预期。

Snowflake 后来发布了一份开发者指南,将这一点妥善接入 CI:先针对已验证查询评估语义视图的 SQL 正确性,再评估智能体的答案正确性和工具选择准确率,只有两者都通过才晋升。如果你今天刚开始做,值得一看。在依赖这些功能之前,请查看你账户中智能体评估和版本管理功能的当前发布状态。

6. 授权是发布的一部分

最后一件坑了大家(包括我)的事是访问权限。

智能体需要对搜索服务有 USAGE 权限,并能访问语义视图背后的每一个对象,而调用它的人需要对智能体有 USAGE 权限。如果这条链路中任何一环缺失,用户看到的就只是一个无法回答的智能体。

对我有效的做法是把智能体授权完全当作我们其他的角色自动化来对待:在代码中定义,随智能体一起部署,并映射到我们已为财务数据使用的相同领域角色。如果你能看到财务数据,你就能使用财务智能体。如果你不能,智能体也不应该能把它展示给你。

我会再次以同样方式做的事

  • 将 agent、语义视图、搜索服务和授权放在一个仓库和一次发布中
  • 注入环境名称;绝不为每个环境保留规范副本
  • 像对待任何其他生产变更一样,用审批来把关 uat 和 prod
  • 每次部署后运行固定问题集,并检查工具路由,而不仅仅是答案
  • 将 agent 标记为 CI 管理,这样没人会在生产环境中“快速修复”它

TL;DR

  1. Agent 不是 UI 配置。它的行为存在于工具描述、指令、语义视图和授权中,而所有这些都需要版本控制。
  2. CREATE AGENT ... FROM SPECIFICATION 让 agent 成为代码。保留一份规范,并在部署时注入环境。
  3. 语义视图才是真正的依赖项。首先在同一发布中部署它,并与定义的所有者一起审查它。
  4. 使用你已有的晋升路径。相同的分支、相同的关卡、相同的服务账户。
  5. 已部署不等于能正常工作。用固定问题集进行测试并检查路由。
  6. 授权随 agent 一同发布。将 agent 访问权限映射到治理数据的相同角色。

如果你正在跨环境运行 Cortex Agents,我很想听听你如何测试它们。这是我仍在完善的部分。

Agent 也是代码:我通过 Azure DevOps 交付 Cortex Agent 所学到的最初发布于Snowflake Builders Blog: Data Engineers, App Developers, AI, & Data Science在 Medium 上,人们通过高亮并回应这个故事来继续对话。

这篇内容对你有用吗?

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

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