# 拨开迷雾看企业技术栈

一门速成课，讲你在美国中型企业里构建 AI agent 时会遇到的那些系统，内容截至 2026 年 10 月。42 个概念——Azure resource hierarchy、Managed Identity、Key Vault、Private Endpoint、Microsoft Foundry、IdP 与 SSO、OIDC 与 SAML、Entra ID 的 app registration 与 service principal、delegated 与 application permissions、Conditional Access、Entra Agent ID、Okta、SCIM、Cross App Access、Snowflake 的 virtual warehouse 与 service user、Cortex、Databricks 的 Delta Lake 与 Unity Catalog、Agent Bricks、Microsoft Fabric 的 OneLake 与 capacity、data agent、least privilege、security review 等——每个都配有英文名称、一幅图和一张卡片：是什么 → 例子 → 陷阱 → 关联。结尾附一页速查表。

- **Author:** Fedor Chishchin
- **Level:** intermediate
- **Reading time:** ~55 分钟阅读
- **Published:** 2026-10-16
- **Source:** https://fedorthinks.com/zh/learn/enterprise-stack-express

---

## 1. 一张地图

六个产品名，三份工作。在看任何细节之前，先弄清每个系统属于哪一层，以及你的 AI 项目夹在它们中间的什么位置。

### 地图：八章共 42 个概念

> 一张地铁线路图：先看到整套线路，再一站一站地坐过去。

这门课按三层来走。先是**云**——Microsoft Azure，你的代码和公司的模型跑在这里。然后是**身份**——Microsoft Entra ID 和 Okta，它们决定谁能登录、能碰什么。再是**数据**——Snowflake、Databricks 和 Microsoft Fabric，公司把销售、财务和 CRM 的数字都放在这里。最后一章把一切串起来：AI agent 怎样拿到尽可能小的权限，怎样通过客户的 security review，怎样不必为一个角色干等三周。

_Diagram: 三层，六个系统——第 2–7 章依次讲它们。第 8 章讲你的 agent 怎样接入全部系统。_

### 怎么读这门课

> 一本护照：每个人的栏目都一样，所以容易比较。

每个概念都有它的**英文名称**（你在会上听到、在文档里读到的就是这个词）、一幅来自日常生活的画面、一张**图**，以及一张**卡片**：_是什么_、_例子_、_陷阱_，和*关联*概念。如果你熟悉 Google Cloud，会一次又一次看到对应关系：大多数企业系统都是熟悉的想法换了个名字。这个领域的产品名改得很勤——Azure AD 变成了 Entra ID，Azure AI Foundry 变成了 Microsoft Foundry，Vector Search 变成了 AI Search——所以课里也会给出旧名。在客户那里，两个名字你都会听到。

### Enterprise Stack——三层，一个项目在中间

> 一栋写字楼：房东拥有各个楼层（云），大堂的保安台查工牌（身份），地下室的档案馆存着每一份记录（数据）。你的新租户——AI 应用——三样都需要。

每家大公司都有同样的三层。**云**是代码运行的地方——在重度使用 Microsoft 的公司里，通常是 Azure。**身份**是公司的人员目录，加上为他们办理登录的前台——Entra ID 或 Okta。**数据**是公司记录存放的地方——Snowflake、Databricks 或 Fabric，通常是三者之一。你的 AI 应用就在中间：员工通过公司的 identity provider 登录，应用跑在公司的云里，用它所需的最小权限读取数据。一句话：云管*代码跑在哪里*，身份管*谁能进来*，数据管*他们读什么*。

_Diagram: 员工通过 Entra 或 Okta 登录；你的应用拿到 token，跑在客户的云里，从一个数据平台读数据。_

#### Enterprise Stack (云 · 身份 · 数据)

- **是什么:** 每家大公司都在运行的三层：一朵云、一个 identity provider、一个数据平台。
- **例子:** 一家物流公司：代码用 Azure，登录用 Entra ID，销售数据用 Snowflake——AI agent 三样都要碰。
- **陷阱:** 每一层都有自己的管理员、自己的工单和自己的安全规则。权限通常是整个项目里最慢的部分。
- **关联:** 各章按层展开：**Azure**、**Entra ID / Okta**、**Snowflake / Databricks / Fabric**；一切汇合于 **Least Privilege**。

> **框定问题:** 「我碰到的每个系统：它是云（我的代码跑在哪里）、身份层（谁被允许进来），还是数据层（我读什么）——第一天我需要找的管理员是谁？」

> 六个产品名其实是三份工作：云运行代码，身份决定谁能进来，数据平台保存公司的记录。你的 agent 就住在它们之间。

## 2. 云：Microsoft Azure

Azure 是 Microsoft 的云。如果你懂 AWS 或 Google Cloud，这些想法你已经会了——变的是名字、严格的文件夹结构，以及安全这件事有多大程度靠身份而不是密钥。

### Microsoft Azure——公司选它，「因为什么都是 Microsoft 的」

> 搬到一座新城市，商店都一样，只是名字不同：超市、药店、面包房都在——你只需要一张新地图。

**Microsoft Azure** 是 Microsoft 的云，就像 AWS（Amazon）或 Google Cloud：虚拟服务器、容器、数据库、文件存储、网络、队列。再加上通过 **Microsoft Foundry** 使用的模型——OpenAI、Anthropic 等等。为什么它在中型企业里无处不在？因为它们已经在为 Office 365、Teams 和 Outlook 付钱给 Microsoft，员工也已经用 Microsoft 工作账号登录。换第二朵云，就意味着新合同、新登录系统、新律师和新的 security review。对 AI 来说还有个更大的理由：很多公司不愿把数据发到公开的 `api.openai.com`，而 Azure 里的模型跑在*他们自己*的云里，受*他们*的合同和访问规则约束。

_Diagram: 三朵云里同样的积木。一个 Google Cloud project ≈ 一个 Azure subscription + resource group；一个 service account ≈ 一个 managed identity。_

#### Microsoft Azure (Microsoft 的云)

- **是什么:** Microsoft 的公有云：计算、存储、网络、数据库和 AI 模型。
- **例子:** 一家用 Office 365 的公司把新的 AI 助手跑在 Azure 上，走同一份 Microsoft 合同计费，用同样的工作账号登录。
- **陷阱:** 有两种权限：**Azure RBAC**（作用于资源）和 Entra 角色（作用于目录）是两回事。subscription 上的 Contributor 并不能让你创建 app registration。
- **关联:** 按 **Resource Hierarchy** 组织；登录来自 **Entra ID**；模型来自 **Microsoft Foundry**。

### Resource Hierarchy——tenant → subscription → resource group

> 俄罗斯套娃：公司里装着一个个合同文件夹，每份合同里装着项目箱子，每个箱子里装着真正的东西。打开大娃娃，里面的一切都跟着出来。

Azure 对结构要求很严，而你第一天就需要懂它，因为权限和账单都跟着它走。**tenant** 是 Microsoft 世界里的整个公司——一个人员和应用的目录，存放在 Entra ID 里。**management group** 是一组 subscription 的文件夹，带共享规则（**Azure Policy**，比如「资源只能放在 East US」）。**subscription** 是一份合同 + 一张账单 + 一道访问边界——通常 prod 和 dev 各一个。**resource group** 是一个文件夹，放那些同生共死的东西：应用、它的数据库、它的 vault。删掉这个 group，里面的一切都没了。关键规则：在较高层级授予的 **Azure RBAC** 角色（谁能做什么）会作用于其下的一切——所以要申请*一个* resource group 上的角色，而不是整个 subscription。

_Diagram: Tenant → management group → subscription → resource group → resource。在上层授予的角色会一路流到下面的一切。_

#### Resource Hierarchy (tenant · subscription · resource group)

- **是什么:** Azure 的层层嵌套文件夹：tenant → management group → subscription → resource group → resource。
- **例子:** 你作为外包人员的权限：他们 tenant 里的一个 guest 账号，以及仅限 `rg-ai-agent-dev` 的 Contributor 角色。
- **陷阱:** 新角色可能要大约 5 分钟（有时更久）才生效。刚授权完就遇到 403 是正常的——等一等。
- **关联:** 最顶层是 **Entra ID** tenant；角色授予给 **Managed Identity**、某个人或某个组。

### Where Code Runs——App Service、Container Apps、AKS、Functions

> 上班的几种方式：按时刻表开的公交（Functions），一叫就来的出租车（App Service），自己开但从不用保养的共享汽车（Container Apps），连同全部保养都归你的私家车（AKS）。

Azure 有四个常见的地方来安放你的代码。**App Service**——「把 web 应用传上去就行」；类似 Heroku，风格较老，能跑 Python、Node、.NET 或容器。**Container Apps**——不用操心服务器的容器，可以自动缩容到零；它最接近 Google Cloud Run，通常是 agent 后端的最佳选择。**AKS（Azure Kubernetes Service）**——完整的 Kubernetes，类似 GKE；只有当客户已经有 Kubernetes 平台时才选它。**Functions**——由事件（HTTP、定时器、队列）触发的代码；适合后台任务，比如「来了个文件——处理它」。

_Diagram: 从要管的更少，到控制得更多。对 agent 后端来说，Container Apps 通常是正确的默认选项。_

#### Container Apps (App Service · AKS · Functions)

- **是什么:** Azure 的 serverless 容器——AI agent 后端常见的家。
- **例子:** agent 的 API 作为一个 Container App 运行，带自己的 managed identity，夜里缩容到零。
- **陷阱:** 一个被遗忘的 AKS 集群或大虚拟机，每月要花几百甚至几千美元。给 resource group 设预算告警，并删掉 dev 资源。
- **关联:** 通过 **Managed Identity** 获得权限；通过 **Private Endpoint** 访问各项服务。

### Managed Identity——给应用的账号，根本没有密码

> 酒店的房卡，前台每天早上为你重新写卡：你从不拿着万能钥匙，它只能开你的房间，到时候自己就失效。

**managed identity** 是 Azure 为你的应用创建并亲自照管的账号。没有密码，也没有密钥文件：你的代码只需向 Azure 要一个 token。实践中你调用 `DefaultAzureCredential()`——在你的笔记本上它用你的 `az login`；在 Container App 里，它通过一个本地端点从 Entra ID 拿到一个短期 token（大约活一小时）。然后你只给这个身份它需要的角色，比如 Foundry 资源上的 **Cognitive Services OpenAI User**。它就是 Azure 版的 Google Cloud service account，只是没有 JSON 密钥。有两种：**system-assigned** 与某一个资源同生共死；**user-assigned** 是一个独立对象，可以挂到多个资源上。

_Diagram: Container App 向 Entra 要 token，拿到一个大约活一小时的 token，再用它调用模型。根本没有可以泄露的密钥。_

#### Managed Identity (system- or user-assigned)

- **是什么:** 由 Azure 管理的应用账号：它从 Entra ID 获取 token，从不持有密码。
- **例子:** agent 用来自 `DefaultAzureCredential()` 的 token 调用模型——代码和配置里到处都没有 API key。
- **陷阱:** 自 2025 年 10 月起，Microsoft 正在逐步要求：以用户身份通过 CLI、SDK 和 Terraform 做的变更必须使用 MFA。自动化请用 managed identity 或 workload identity，而不是你的个人账号。
- **关联:** 一种特殊的 **Service Principal**；从 **Key Vault** 读取秘密；CI 以同样的方式使用 workload identity federation（OIDC）。

### Key Vault——存放秘密的保险箱

> 银行的保管箱：贵重物品不放在办公室抽屉里，只有名单上的人能打开箱子——而且每一次来访都会被记下。

有时你确实需要一个秘密——比如客户 CRM 的 API key，或者 Snowflake 的私钥。**Key Vault** 是 Azure 存放秘密、密钥和证书的保险箱，类似 Google Cloud 里的 Secret Manager。应用用自己的 managed identity 读取秘密，这个身份在那个 vault 上持有 **Key Vault Secrets User** 角色。`config.yaml` 里没有，git 里的 `.env` 文件里也没有。对于「你们的秘密放在哪里？」，这正是安全团队想听到的答案。

_Diagram: 之前：密码写在配置文件里。之后：agent 用自己的身份和一个窄角色，从 Key Vault 读取每个秘密。_

#### Key Vault (secrets · keys · certificates)

- **是什么:** Azure 托管的秘密、密钥和证书存储。
- **例子:** agent 启动时从 Key Vault 取出 CRM key 和 Snowflake 私钥。
- **陷阱:** 用 private endpoint 封闭的 vault，从你的笔记本上是访问不到的——要提前规划好开发路径。
- **关联:** 通过 **Managed Identity** 读取；常常藏在 **Private Endpoint** 后面。

### Private Endpoint——不开向互联网的门

> 一栋写字楼，档案馆根本没有临街的入口——只能在大堂放你进来之后，从内部走廊过去。

**VNet**（Virtual Network）是客户在 Azure 里的私有网络，类似 Google Cloud 里的 VPC。**private endpoint** 给一项服务——Key Vault、Foundry、Postgres——在这个 VNet 里分配一个私有 IP 地址。之后，这项服务就可以完全对互联网关闭（`publicNetworkAccess: Disabled`）。企业安全团队很喜欢这个，在中型企业里它常常是硬性要求。代价落在你这边：你本地的代码再也连不上被关闭的服务了。你需要 VPN、跳板机，或者 VNet 内的开发环境——还要正确配置的 **private DNS**，这是「为什么这个名字解析不了？」的经典来源。

_Diagram: 在 VNet 内，agent 通过私有 IP 访问每项服务。互联网——以及你的笔记本——进不来。_

#### Private Endpoint (VNet · private link)

- **是什么:** 云服务在客户网络内的私有 IP，这样它就可以对互联网关闭。
- **例子:** Foundry、Key Vault 和 Postgres 各拿到一个像 `10.0.1.4` 这样的地址；公网访问被禁用。
- **陷阱:** 它会切断你笔记本上的访问，还需要 private DNS。在第一次部署之前规划好开发路径。
- **关联:** **Security Review** 最先问的问题之一；在 Google Cloud 里对应的是 Private Service Connect。

### Microsoft Foundry——模型就在客户的 tenant 里

> 由外部餐饮公司经营的员工食堂：饭菜和这家公司餐厅里的一样，但在你的楼里做，按你的规矩，走你的合同。

**Microsoft Foundry** 是 Azure 的 AI 平台。它的名字改了好几次：Azure AI Studio（2023）→ Azure AI Foundry（2024）→ **Microsoft Foundry**（2025 年底起），Azure OpenAI 现在是「Foundry Models」的一部分。你在一个地方创建 **model deployment**——你给某个模型起了名字的副本（比如 `gpt-4o-contracts`），带有自己的 **quota**：**TPM**（每分钟 token 数）或 **PTU**（预先购买的容量）。你在 **Foundry Agent Service** 里用 tools 和 MCP 构建 agent，还能用上评估、内容过滤和 trace。旧的 Azure OpenAI 端点和密钥大多还能用。当前的 token scope 是 `https://ai.azure.com/.default`；较老的示例用的是 `https://cognitiveservices.azure.com/.default`。

_Diagram: 你的代码按名字调用 deployment；Foundry 还托管 agent、评估和内容安全。三年里换了三个名字。_

#### Microsoft Foundry (formerly Azure AI Foundry · Azure OpenAI)

- **是什么:** Azure 的模型（包括 Azure OpenAI）、agent 和评估平台。
- **例子:** `model="gpt-4o-contracts"`——这个字段填的是 deployment 名，而不是模型名。
- **陷阱:** 你需要的模型可能在客户的 region 里不存在，或者它的 TPM quota 太小。开工前就检查，别等到演示那天。404 常常意味着 deployment 名写错了。
- **关联:** 用 **Managed Identity** 的 token、经由 **Private Endpoint** 调用；在 Google Cloud 里对应的是 Vertex AI，在 AWS 里是 Bedrock。

> **云的问题:** 「我的 agent 是否用 managed identity 运行，只在一个 resource group 上有角色，秘密放在 Key Vault 里，服务在 private endpoint 后面——我查过他们 region 里的模型 quota 了吗？」

> Azure 是一朵熟悉的云，只是文件夹更严格。申请一个 resource group，给应用 managed identity 而不是密钥，把秘密放在 Key Vault 里，第一天就检查模型 quota。

## 3. 身份：SSO 与 Entra ID

你的应用不该自己核对密码。公司已经有一个认识每位员工的前台。这一章先讲通用的词——IdP、SSO、OIDC——再讲 Microsoft 的前台 Entra ID，大多数困惑都出在这里。

### Identity Provider 与 SSO——所有应用共用一个前台

> 商务中心的保安台：保安凭工牌认识每一个人。楼上的办公室不自己查护照——它们信任大堂发的工牌。

**identity provider**（IdP）是确认「这是 Anna，她在销售部」的系统。Entra ID、Okta 和 Google 都是 IdP。**SSO**（Single Sign-On）是人们的直接感受：用工作账号登录一次，之后就能进入每个应用——Outlook、Salesforce、Jira、你的 agent——不用新密码。在这之前，每个应用都有自己的登录：每人十五个密码，一半贴在便利贴上；新员工要等一周才拿到账号；离职的人六个月后还能打开 CRM。有了 IdP，IT 关掉一个账号，所有地方的权限就都没了——而且谁在哪里登录过，只有一份日志。

_Diagram: 员工在 identity provider 登录一次；每个应用——包括你的——都信任这次登录。_

#### Identity Provider (IdP · SSO)

- **是什么:** 公司的登录服务；有了它，一个工作账号就能打开所有应用（SSO）。
- **例子:** 内部 agent 没有登录表单——「Sign in」把人送到客户的 Entra ID 或 Okta。
- **陷阱:** 它不归你控制。你的应用登录方式的每一处改动，都要经过客户的 IT。
- **关联:** 说的是 **OIDC 或 SAML**；两大巨头是 **Entra ID** 和 **Okta**。

### OIDC 与 SAML——应用怎样询问前台

> 证明你是客人的两种方式：手机上的二维码，门口一秒就扫完（OIDC）；或者一封主人盖了章的纸质信，保安一行一行地读（SAML）。

有两套标准让你的应用能「询问前台」。**OAuth 2.0** 是你获取 access token 来调用 API 的方式。**OIDC**（OpenID Connect）建在 OAuth 之上，用于登录：它加了一个 **ID token**——一个说明这个人是谁的 JWT。对新的 web 应用来说，OIDC 是默认选项。**SAML** 是更老的 XML 标准，在企业 SaaS 里仍很常见；客户坚持时才用它。常见的 OIDC 流程是 **authorization code + PKCE**：重定向到 IdP，这个人登录（带 MFA），IdP 送回一个一次性 code，你的后端用它换 token。然后你检查 ID token：签名（密钥来自 JWKS）、`iss`、`aud`、过期时间、`nonce`。对于没有人参与的后台工作，有 **client credentials** 流程；当你的 API 代表用户调用另一个 API 时，有 **on-behalf-of（OBO）**。

_Diagram: Authorization code + PKCE：浏览器去 IdP 再带着 code 回来，后端用 code 换 token 并检查它们。_

#### OIDC and SAML (OAuth 2.0 · auth code + PKCE)

- **是什么:** 你的应用通过 IdP 让人登录所用的标准。OIDC 是现代的 JSON/JWT；SAML 是较老的 XML。
- **例子:** 就是你在「Sign in with Google」里熟悉的那套 code 加 PKCE 流程，只是指向 `login.microsoftonline.com/<tenant-id>` 或 `acme.okta.com`。
- **陷阱:** 永远别用 email 作为用户主键——它会变。用稳定的 ID：Entra 里是 `oid` + `tid`，Okta 里是 `sub`。
- **关联:** 背后的 IdP 是 **Entra ID** 或 **Okta**；它对接的应用是一个 **App Registration**。

### Microsoft Entra ID——公司在云上的目录

> 公司的员工通讯录和钥匙柜合二为一：它列出每个人、每个组，并负责发钥匙。

**Microsoft Entra ID**——2023 年之前叫 **Azure AD**——是 Microsoft 存放公司人员和应用的云目录，外加它的登录服务器。**tenant** 是一个组织的目录：它有一个 ID（GUID），还有像 `contoso.com` 这样的域名。里面有**用户**（员工和 **guest**——你作为外包人员，通常以 B2B guest 的身份、用自己的 email 加入）、**组**（用于授权的 security group；组可以是静态的，也可以是动态的，比如「department = Sales 的所有人」），以及**应用**。很多公司还在运行老的本地 **Active Directory**，并用 **Entra Connect** 同步到 Entra。给你的应用的规则：把权限给组，而不是给人。IT 把某人加进组——他就有了权限，你根本不用碰代码。

_Diagram: 一个 tenant 里有用户（包括 guest）、组和应用。老的本地 AD 常常会同步进来。_

#### Microsoft Entra ID (formerly Azure AD)

- **是什么:** Microsoft 面向员工、组和应用的云目录与 identity provider。
- **例子:** 合同 agent 只放行 `contoso.com` tenant 里 `AI-Agent-Users` 组的成员。
- **陷阱:** 内部应用要用客户的 tenant ID——而不是 `/common`，它会放行任何组织和个人账号——并检查 token 里的 `tid`。
- **关联:** 应用在这里以 **App Registration** 和 **Service Principal** 的形式存在；访问由 **App Roles** 和 **Conditional Access** 塑造。

### App Registration vs Service Principal——护照与签证

> 护照与签证：护照（registration）说明你是谁，只办一次。签证（service principal）由你进入的每个国家分别签发——而正是签证规定了你在那里能做什么。

这是人人都会搞混的一对。**app registration** 是应用的护照：它的 **client ID**（公开的，不是秘密）、它的 **redirect URI**（登录后把人送到哪里——必须完全一致）、它的凭据、它申请的权限、它声明的角色。**service principal** 是应用在某个具体 tenant 里的实例——在 portal 里叫 **Enterprise application**。权限授予给 service principal，人被分配给它，Conditional Access 也作用于它。一个 registration 可以在许多 tenant 里拥有 service principal——SaaS 产品就是这么运作的。**managed identity** 也是一个 service principal，只是没有需要你管理的护照。关于凭据：**client secret** 会过期（最长两年，按策略常常是 6–12 个月）；证书更好；**federated credential** 最好——没有东西会过期。

_Diagram: registration 是蓝图，只做一次。每个使用该应用的 tenant 都有自己的 service principal，权限就挂在它上面。_

#### App Registration vs Service Principal (Enterprise application)

- **是什么:** Registration = 应用的定义（client ID、redirect URI、权限、角色）。Service principal = 它在某个 tenant 里获得权限的副本。
- **例子:** 「Contract Assistant」：single-tenant registration，redirect URI `https://agent.contoso.com/auth/callback`，用 federated credential 代替 secret。
- **陷阱:** 你多半没法自己创建 registration，也没法自己授予 admin consent——那是 Application Administrator 的事。提前把精确清单发过去，能省下好几周。
- **关联:** 申请 **Delegated 或 Application Permissions**；声明 **App Roles**；**Managed Identity** 也是一个 service principal。

### Delegated vs Application Permissions——以用户身份，还是以它自己

> 拿着你办公室钥匙的私人助理，只能开你的门。拿着万能钥匙的夜班保安，能开楼里的每一扇门——巡逻时很有用，落到坏人手里很危险。

调用 Microsoft API 有两种权限。**Delegated permissions**（scopes）——应用*代表已登录的用户*行事，只能看到这个用户能看到的东西：`User.Read`、`Files.Read`。在 token 里它们出现在 `scp` claim 中。**Application permissions**——应用*以它自己*的身份行事，没有用户参与，用于后台工作：`Sites.Read.All` 能读取每一个 SharePoint 站点。这非常强大，并且总是需要 **admin consent**。**Consent** 就是批准：「好，这个应用可以读我的资料」。如果公司允许，用户可以批准普通权限；强权限只有管理员能批准。很多公司干脆完全关掉用户 consent——于是有了首次登录时最常见的那个画面：**「Need admin approval」**。

_Diagram: Delegated：agent 只能看到 Anna 的文件。Application：它能看到每一个站点——并且需要管理员批准。_

#### Delegated vs Application Permissions (scopes · consent · admin consent)

- **是什么:** Delegated = 以已登录用户的身份、在其权限范围内行事。Application = 以应用自己的身份、在所授予的全部范围内行事。
- **例子:** 一个 SharePoint 问答 agent 使用 delegated 的 `Sites.Read.All`，于是 SharePoint 自动套用每个用户自己的权限。
- **陷阱:** Application permissions 会绕过人的权限。用 application 的 `Sites.Read.All`，一个回答员工问题的 agent 很容易把某人不该看的文档给他看。
- **关联:** **As the User vs As Itself** 的核心；**Security Review** 的第一个问题。

### App Roles——你自己的角色，由他们的 IT 分配

> 音乐节上的彩色手环：主办方决定蓝色手环能进哪里，票务台发手环，入口只看颜色。

**app roles** 是你在 app registration 里声明的角色——比如 `Agent.User` 和 `Agent.Admin`。客户的 IT 把它们分配给组。有人登录时，他的角色会出现在 token 的 `roles` claim 里，你的后端检查它们：没有 `Agent.User` → 403。打开 **「Assignment required」**，没被分配的人连登录都登不进来。为什么不直接从 token 里读组？因为 **groups overage**：如果一个用户所在的组太多（JWT 里超过 200 个），组就根本不会出现在 token 里——你只会拿到一个指向 Graph 的链接。app roles 避开了这个问题，还把控制权留在客户手里：IT 在组之间调动人员，你根本不用碰代码。

_Diagram: 你声明角色，IT 把它们分配给组，角色随 token 到达，你的后端检查它们。_

#### App Roles (roles claim · assignment required)

- **是什么:** 由你的应用声明、由客户 IT 分配给组、随 token 送达的角色。
- **例子:** `if "Agent.User" not in claims.get("roles", []): abort(403)`——财务部通过自己的组拿到 `Agent.Admin`。
- **陷阱:** 改用 groups claim 的话，对属于 200 多个组的人就会失效——组根本不在 token 里。
- **关联:** 在 **App Registration** 里声明；Okta 里对应的是经过过滤的 groups claim。

### Conditional Access——门口的规则

> 夜店门口的保安拿着一张规则单：会员直接进，客人出示证件，穿运动鞋的谁都不让进——每一次都在门口检查。

**Conditional Access** 是一组「如果——那么」规则，Entra 在每次登录时都会检查：*如果*财务组的某人从一台未受管理的设备打开应用 X，*那么*要求 MFA——或者直接拦下。条件可以包括用户或组、应用、设备、位置和登录风险。**MFA** 是第二因素：Microsoft Authenticator 里的推送、FIDO2 密钥、passkey。Conditional Access 需要 **Entra ID P1** 或更高的许可证；没有 P1 的小 tenant 可能只有 **security defaults**（所有人都要 MFA）。基于风险的访问和 PIM 需要 P2，而针对 service principal 的 Conditional Access 需要单独的 Workload Identities 许可证。「我们没有那个功能」常常意味着「我们没有那个许可证」。

_Diagram: 每次登录时，Entra 检查是谁、哪个应用、哪台设备、在哪里、风险多大——然后放行、要求 MFA 或拦下。_

#### Conditional Access (MFA · security defaults)

- **是什么:** Entra ID 里的登录规则：条件（谁、应用、设备、地点、风险）→ 动作（放行、MFA、拦下）。
- **例子:** 合同 agent 自动落入公司的「MFA 加受管设备」策略之下。
- **陷阱:** 「仅限受管设备」可能会挡住你的测试、你的 CI 和你自己的笔记本。尽早商定例外，并确认开发环境没被锁在外面。
- **关联:** 作用于 **Service Principal**；Okta 里的对应物是 authentication policies。

### Microsoft Graph——通往整个 Microsoft 365 的一个 API

> 一栋楼的总机：拨一个号码就能接通任何一个办公室——收发室、日程台、档案室——而不是每处各拉一条电话线。

**Microsoft Graph** 是通往 Microsoft 365 里一切的一个大 API：用户、组、邮件、日历、Teams、OneDrive 和 SharePoint 里的文件。地址是 `https://graph.microsoft.com/v1.0/…`。对 AI agent 来说，它是进入客户文档的正门：「在 SharePoint 里找到第 42 号合同」「订个会议」。用 delegated token 时，Graph 只返回已登录用户能看的内容——所以 agent 的搜索工具白白继承了 SharePoint 的权限。老的 Azure AD Graph API 已经关停，只剩 Microsoft Graph。用于登录和 token 的官方库是 **MSAL**（Python、JS、.NET 等）。

_Diagram: agent 调用一个端点 graph.microsoft.com，就能触达用户、邮件、日历、Teams 和文件——在它的 scopes 范围内。_

#### Microsoft Graph (graph.microsoft.com · MSAL)

- **是什么:** 通往 Microsoft 365 数据的单一 REST API。
- **例子:** agent 的搜索工具用用户的 access token 调用 `POST /v1.0/search/query`，只拿到该用户能打开的文档。
- **陷阱:** Graph 返回什么，受 token 里的 scopes 限制。要得太少，调用会失败；要得太多，security review 会卡住。
- **关联:** 用 **Delegated 或 Application Permissions** 调用；token 来自 **OIDC** 登录。

### Entra Agent ID——专为 AI agent 打造的身份

> 给机器人的员工工牌：它有自己的照片、自己的通行清单，会出现在访客记录里，保安也能像对待其他人一样在前台把它停用。

2026 年，Entra 为 AI agent 推出了一种单独的身份：**Agent ID**（自 2026 年 4 月起正式可用）。agent 拥有自己的身份，由一个模板——**agent identity blueprint**——创建，IT 像管理员工一样管理它：权限、Conditional Access、登录日志、停用。它回答了安全团队如今常问的一个问题：「我们怎么知道有哪些 agent、它们能做什么？」。它很新，所以值得记住这个词，并问问客户是否在用。

_Diagram: agent 由 blueprint 创建，每个都有自己的身份，IT 像管理员工账号一样控制它们。_

#### Entra Agent ID (agent identity blueprint)

- **是什么:** Entra 为 AI agent 提供的一等身份类型，由 blueprint 创建，像用户一样受治理。
- **例子:** 合同 agent 和销售 agent 各自拥有 Agent ID，权限和日志相互分开。
- **陷阱:** 2026 年的新东西——很多中型企业的 tenant 还没用上。要问，别想当然。
- **关联:** Okta 的对应物是 **Cross App Access** 加 Agent SSO；两者都服务于 **Least Privilege**。

> **身份的问题:** 「我的应用是否通过客户的 tenant、用 OIDC 让人登录，使用稳定的用户 ID，检查 app roles——并且在回答某个人时，用的是 delegated permissions？」

> Entra ID 是公司的目录和前台。注册应用，让 IT 把 app roles 分配给组，优先用 delegated permissions，再给 IT 发一份精确清单，好让 admin consent 来得快。

## 4. 身份：Okta

Okta 做的事和 Entra ID 一样，但它是一家独立的公司。当客户用着一堆五花八门的工具、Microsoft 又不是中心时，它就胜出。标准是一样的；不同的是用词和管理后台。

### Okta Workforce Identity——中立的前台

> 一个由独立安保公司而不是房东经营的前台：它和楼里每个租户都能合作，不管各家办公室用的是什么牌子的门锁。

**Okta Workforce Identity** 是 Okta 面向员工的产品：它认识公司里的每个人，决定谁能登录哪里。人们登录一次，就能进入 Slack、Salesforce、Jira、AWS 和你的应用。当公司有一个「动物园」——Google Workspace、AWS、Slack、Salesforce、Mac——而 Microsoft 又不是主要供应商时，就会选它；它的目录 **Okta Integration Network**（OIN）里有 8,000 多个现成集成。每家公司有一个 **org**，地址像 `acme.okta.com`——这是 Okta 版的 tenant。通常有一个生产 org 和一个 **preview org**（在 `oktapreview.com` 上）：先申请 preview 那个的权限。**Universal Directory** 存放人员（带部门、经理等字段）、组，如今还有 AI agent；数据通常从 HR 系统流入——「HR 是唯一可信来源」。别把 Okta 和 **Auth0** 搞混：同一家公司，但 Auth0 是给公司的*客户*登录它的产品用的；Okta Workforce 是给*员工*用的。

_Diagram: 人员从 HR 和 AD 流入；Okta org 存放目录、策略、token 服务器和生命周期；各个应用——以及你的 agent——信任它。_

#### Okta (Workforce Identity · org · Universal Directory)

- **是什么:** 一个独立的、面向员工的云 identity provider：目录、SSO、MFA、provisioning。
- **例子:** agent 是 `acme.okta.com` 里的一个 OIDC app integration，分配给 `AI-Agent-Users` 组。
- **陷阱:** 默认情况下，组不会放进 token。你需要一个带过滤器的 groups claim（比如「以 AI-Agent- 开头」）——「所有组」会让 JWT 膨胀，撑爆 header 和 cookie。
- **关联:** 与 **Entra ID** 标准相同（**OIDC 与 SAML**）；用 **SCIM** 做 provisioning；token 来自 **Authorization Servers**。

### SCIM——入职、调岗、离职，全自动

> 新员工的名字一上 HR 名单，第一天工牌、工位和储物柜就都备好了；他一离职，工牌当晚就失效——不用任何人记着去办。

**SCIM** 是一个用于在应用里创建、更新和停用用户的标准 REST API。IdP——Okta 或 Entra——是 SCIM 客户端；你的应用是 SCIM 服务器，提供 `/Users` 和 `/Groups` 端点。**Lifecycle management** 覆盖三个时刻：**joiner**（有人被分配了这个应用 → `POST /scim/v2/Users`）、**mover**（他换了团队 → 组更新）、**leaver**（他离职了 → 带 `active=false` 的 `PATCH`：账号被停用，而不是删除）。没有 SCIM 的话，用户只会在第一次登录时出现在你的数据库里（**just-in-time**），也没人会删掉离职的人——那些带着聊天记录和设置的「死魂灵」。

_Diagram: HR 有变动，IdP 就调用你的 SCIM 端点：入职时创建，调岗时更新，离职时停用。_

#### SCIM (provisioning · joiner–mover–leaver)

- **是什么:** IdP 用来在你的应用里创建、更新和停用用户的标准 API。
- **例子:** 有人周五离职；Okta 发出 `PATCH active=false`，他的 agent 账号当天就关掉了。
- **陷阱:** 只有当你的应用保存自己的用户记录时才重要。如果保存了却跳过 SCIM，你就会有比主人活得还久的账号。
- **关联:** 由 **Okta** 或 **Entra ID** 驱动；和短期 token 搭配，可以快速撤销权限。

### Authorization Servers——org 还是 custom

> 一栋楼有一种能开大门的标准通行证，但规矩特殊的实验室会印自己的通行证、盖自己的章。在实验室门口出示标准通行证是不管用的。

Okta 里负责签发 token 的部分叫 **authorization server**，有两种。**org authorization server** 是内置的，issuer 是 `https://acme.okta.com`；它适合普通登录和调用 Okta 自己的 API，但你不能加自己的 scope。**custom authorization server**（issuer 像 `https://acme.okta.com/oauth2/default`）保护的是*你的* API：你的 scope，比如 `agent.read` 和 `agent.tickets.write`，你的 claim，你的策略，machine-to-machine。它需要付费的 **API Access Management** 订阅——客户可能没有。经典错误：在你自己的 API 里，把 org server 的 token 当成你的 access token 去校验。如果你要做 API，就需要 custom server。先弄清 issuer。

_Diagram: org server 负责登录和 Okta 自己的 API；custom server 为你的 API 签发带你的 scope 的 token。_

#### Authorization Server (org vs custom · issuer)

- **是什么:** Okta 的 token 签发方。Org = 内置，用于 SSO；custom = 你的 scope 和 claim，用于你的 API。
- **例子:** 后端按 issuer `…/oauth2/default`、audience `api://ai-agent`、来自 `/v1/keys` 的密钥来校验 token。
- **陷阱:** 把两者搞混，`aud` 检查就会失败，或者你加不了 scope。调用 Okta 的 API 时，用 OAuth service app——而不是静态的 SSWS token，它会在创建它的管理员离职时失效。
- **关联:** 签发 **OIDC** 的 token；agent 可以通过 **Cross App Access** 触达其他应用。

### Cross App Access 与 Agent SSO——不囤 token 的 agent

> 不再让每个助理揣着一串复制来的、能开每间办公室的钥匙，而是由前台为每次来访签发一张短期访客证——并把每次来访都记在本子上。

新问题来了：应用 A 里的 agent 想代表用户从应用 B 拿数据。老办法是每个用户在每个应用里点「连接你的 Jira」，agent 把这些 token 存起来——而 IT 看不到其中任何一条连接。**Cross App Access**（XAA）是一个开放协议，建立在 IETF 草案 **ID-JAG**（Identity Assertion JWT Authorization Grant）之上：agent 向 Okta 申请，Okta 检查策略，为应用 B 签发一个短期 token，并且看到、记录下每一条这样的连接。XAA 已被接纳为 MCP 的 Enterprise-Managed Authorization 扩展；合作方包括 Slack、Atlassian、Zoom、Asana、Datadog、Cloudflare 和 Claude。**Agent SSO**（自 2026 年 8 月 24 日起正式可用，包含在标准 Okta SSO 中，不额外收费）把支持 XAA 的 agent 作为独立身份注册进 Universal Directory，和人并列，用短期 token 代替静态 API key。除此之外，**Okta for AI Agents** 是单独的付费订阅，用于发现和治理。在面向客户的一侧，**Auth0 for AI Agents**（自 2025 年 11 月起正式可用）提供 Token Vault、由人异步审批，以及面向 RAG 的细粒度访问控制。

_Diagram: 老办法：用户逐个连接应用，agent 保管 token。XAA：agent 向 Okta 申请，以用户身份拿到短期 token，Okta 记录在案。_

#### Cross App Access (XAA · ID-JAG · Agent SSO)

- **是什么:** 一种由 Okta 居中的方式：一个应用里的 agent 以用户身份，为另一个应用拿到一个短期、有记录的 token。
- **例子:** 销售 agent 通过 XAA 创建 Jira 工单——什么都不用存，IT 在 Okta 里能看到这条连接。
- **陷阱:** 只有 agent 所在的应用和资源应用都支持时才行。很多中型企业客户不会开启它——把带最小 scope 的普通 OAuth 作为 B 计划。
- **关联:** Microsoft 的对应物是 **Entra Agent ID**；它就是集中化实现的 **As the User**。

> **Okta 的问题:** 「我知道 issuer 吗，知道它是哪种 authorization server 吗，groups claim 过滤了吗，我需要 SCIM 吗——我的 agent 能不能用 Cross App Access 代替存 token？」

> Okta 是中立的前台：还是你已经熟悉的那套 OIDC，加上处理入职离职的 SCIM、两种 token 服务器，以及如今让 agent 不再囤 token 的 Cross App Access。

## 5. 数据：Snowflake

公司把它的数字放在哪里？先看数据存储可以有的三种形态。然后是 Snowflake——一个存储与计算分离、按秒付费的 SQL 数据仓库。

### Warehouse、Lake、Lakehouse——保存公司数据的三种方式

> 一座图书馆、一个车库和一座现代档案馆。图书馆有严格的书架和目录，但只收成品书。车库什么都收，但要找东西就是噩梦。档案馆把一切都放在便宜的箱子里——上面再加一份目录。

**data warehouse** 是为分析而建的数据库，而不是为运行应用：又大又严格的表，以及像「三年来各地区的销售额」这样的重型查询，用 SQL 写。又快又可靠，但更贵，而且只有干净的那部分数据能进去。**data lake** 不过是云存储里的一堆文件夹——S3、ADLS、GCS：日志、CRM 导出、CSV、JSON、图片。便宜又海量，但毫无秩序：没有事务，并发写入时文件会坏，没人知道哪个版本是对的。多年来，公司两样都用，数据来回拷贝——两份副本、两套权限，报表和模型的结论对不上。**lakehouse**（lake + warehouse）把数据只在便宜的存储里保存一份，用一种表格式加上事务、schema 和历史，让 SQL、Spark 和 ML 都在同一批表上工作。

_Diagram: Warehouse：严格而干净。Lake：便宜而凌乱。Lakehouse：文件只存一份，上面加表的规则。Snowflake 起步于第一种；Databricks 和 Fabric 属于第三种。_

#### Warehouse · Lake · Lakehouse (data warehouse · data lake)

- **是什么:** 三种存储形态：严格的 SQL 表，任意类型的原始文件，或者上面加了表格式的文件。
- **例子:** 财务报表放在 warehouse 里；应用日志落进 lake；lakehouse 用一份副本同时服务两者。
- **陷阱:** 如今 Snowflake、Databricks 和 Fabric 几乎什么都能做，竞争激烈——这些标签描述的是它们各自的起点。
- **关联:** **Snowflake**（warehouse 出身），**Databricks** 和 **Microsoft Fabric**（基于 **Delta Lake** 的 lakehouse）。

### Snowflake——存储在一处，计算按需

> 一座巨大的货仓，配着几支独立的搬运队，按分钟雇用。要快一点——叫一支更大的队。没人干活——就没人拿钱。

**Snowflake** 是一个用于分析的云数据库。公司把数据倒进去——销售、CRM、库存、财务——每个部门都用普通 SQL 查询。服务器、磁盘、索引、备份都是 Snowflake 的事。核心想法是**存储与计算分离**：数据只有一份，每个团队拿到自己的计算集群，互不拖慢。共有三层：**cloud services**（大脑——登录与角色、元数据、查询优化器、结果缓存）、**compute**（virtual warehouse，按 credit 付费）和 **storage**（存放在 micro-partition 里的列式数据，按每月每 TB 付费）。Snowflake 跑在 AWS、Azure 或 GCP 之上——由客户选择云和 region。你的 **account** 有一个像 `myorg-myaccount` 这样的标识符；web 控制台叫 **Snowsight**。附赠功能：**Time Travel** 可以读取表「昨天的样子」（默认 1 天，Enterprise 版最多 90 天），**zero-copy clone** 能在几秒内从 prod 复制出一个 dev 数据库。

_Diagram: 三层：最上面是大脑，中间是各团队独立的计算集群，最下面是共享的一份数据。_

#### Snowflake (cloud data warehouse)

- **是什么:** 一个存储与计算分离、计算按用量计费的 SQL 分析平台。
- **例子:** agent 的查询跑在它自己的小集群上，所以 BI 团队的仪表盘不会变慢。
- **陷阱:** 不加引号的名字会变成大写：带引号的 `"orders"` 和 `orders` 是两张不同的表。还有，要用新的 `org-account` 标识符，而不是旧的 locator 格式。
- **关联:** 计算是 **Virtual Warehouse**；访问控制是 **Roles and RBAC**；AI 是 **Cortex**。

### Virtual Warehouse——是计算集群，不是仓库

> 一辆打表的出租车：叫了就来，只在你坐车时计价，用完就开走。车越大，每分钟越贵。

这个词把所有人都搞糊涂了：**virtual warehouse** 什么都不存。它是运行你查询的计算集群。它有尺寸——X-Small、Small、Medium、Large，一直到 6X-Large——每升一级，算力翻倍，价格也翻倍。价格以 **credit** 计，这是 Snowflake 的内部货币：X-Small 每小时烧 1 个 credit，Small 2 个，Medium 4 个，Large 8 个，X-Large 16 个（Gen1）。一个 credit 值多少美元，取决于版本、云、region 和合同——没有统一的数字。按秒计费，每次启动最少计 60 秒。**auto-suspend** 在空闲时让集群休眠（设成 60 秒）；**auto-resume** 在下一次查询时唤醒它；**multi-cluster**（Enterprise 版）在负载高时加副本。**resource monitor** 给 credit 封顶：到 80% 时通知，到 100% 时暂停。没有活跃的 warehouse，SQL 就跑不起来：「No active warehouse selected」。

_Diagram: 每升一档，每小时的 credit 翻倍。查询时唤醒，按秒计费，安静 60 秒后休眠——再用 resource monitor 封顶。_

#### Virtual Warehouse (credits · auto-suspend · resource monitor)

- **是什么:** Snowflake 的计算集群，有尺寸和按秒计算的 credit 价格；它不存任何数据。
- **例子:** `AI_AGENT_WH`：X-Small，`AUTO_SUSPEND = 60`，50 个 credit 的 resource monitor，再加一个 `QUERY_TAG` 来追踪 agent 的成本。
- **陷阱:** 没有 auto-suspend 或尺寸太大 = 每月几千美元。一个每个问题发 20 条查询的 agent，会让 warehouse 一整天都醒着。
- **关联:** **Snowflake** 的计算层；通过 **Role** 使用。

### Roles and RBAC——权限给角色，角色给用户

> 挂钥匙的板子按岗位贴标签，而不是按人名：「收银员」「夜班经理」。新员工拿到自己岗位的那个钩子；没人会被塞一把散钥匙。

在 Snowflake 里，权限从不直接给用户：它们给**角色**，角色再给用户。这就是 **RBAC（role-based access control）**。内置角色：**ACCOUNTADMIN**——账号里的上帝（没人会给你，你也不需要）；**SECURITYADMIN** 和 **USERADMIN** 管理用户和授权；**SYSADMIN** 创建数据库和 warehouse；**PUBLIC** 人人都有。角色可以嵌套。给 agent 你要创建它自己的角色，比如 `AI_AGENT_RO`，带它的 warehouse、数据库和 schema 上的 `USAGE`，以及它需要的那些 view 上的 `SELECT`——如果它调用 Cortex，再加上 `SNOWFLAKE.CORTEX_USER`。在此之上，**row access policies** 决定某人能看到哪些行，**masking policies** 隐藏像 email 或 SSN 这样的值。对象命名为 `DATABASE.SCHEMA.OBJECT`，比如 `SALES.MART.ORDERS_V`。

_Diagram: service user 拿到一个只读角色；授权都挂在这个角色上。内置角色在上方，从 ACCOUNTADMIN 一直到 PUBLIC。_

#### Roles and RBAC (AI_AGENT_RO · row access · masking)

- **是什么:** Snowflake 的访问模型：授权给角色，角色给用户，也给其他角色。
- **例子:** `GRANT SELECT ON ALL VIEWS IN SCHEMA SALES.MART TO ROLE AI_AGENT_RO`——只给 view，绝不给带原始 PII 的表。
- **陷阱:** 「Object does not exist」或「no warehouse」几乎从来不是拼写错误——而是少了一个 `USAGE` 授权。角色看不到某个对象时，Snowflake 就说它「不存在」。
- **关联:** 授予给 **Service User**；**Least Privilege** 的教科书案例。

### Service User——给程序用的 Snowflake 账号，不准用密码

> 送货机器人凭一枚写好程序的芯片进楼，而不是输入门禁密码——而且这栋楼干脆不再允许机器人用密码了。

**service user**（`TYPE = SERVICE`）是给程序而不是给人用的 Snowflake 用户。它被禁止使用密码：它用 **key pair**（JWT）、OAuth、programmatic access token（PAT）或 workload identity federation 登录。而且现在执行得很严：2026 年 8 月到 10 月间，Snowflake 彻底封禁了 service user 的密码登录，`LEGACY_SERVICE` 用户正在被迁移成 `SERVICE`。所以网上那些带 `password=` 的老示例都不管用了——别照抄。用 **key-pair auth** 时，你生成一对 RSA 密钥，把公钥交给管理员（`RSA_PUBLIC_KEY`），私钥放在 Key Vault 或 Secret Manager 里。你的连接器用它签一个 JWT（`authenticator="SNOWFLAKE_JWT"`）。第二个槽位 `RSA_PUBLIC_KEY_2` 让你轮换密钥而不停机；**network policy** 限制哪些 IP 可以连接。

_Diagram: 没有密码。vault 里的私钥签一个 JWT；Snowflake 用公钥、network policy 和角色来核对它。_

#### Service User (TYPE = SERVICE · key-pair auth)

- **是什么:** 给软件用的 Snowflake 用户，必须用密钥、OAuth、PAT 或 workload identity 登录。
- **例子:** `SVC_AI_AGENT`，带 `DEFAULT_ROLE = AI_AGENT_RO`、`DEFAULT_WAREHOUSE = AI_AGENT_WH`，只有一个 RSA 公钥。
- **陷阱:** 如果 agent 的所有用户都走同一个 service user，人人看到的数据都一样。尽早决定：用 row policy，还是通过 Okta 或 Entra 的 OAuth 以用户身份登录。
- **关联:** 持有一个 **Role**；密钥放在 **Key Vault** 里；设计上的选择是 **As the User vs As Itself**。

### Cortex——AI 就在数据旁边

> 不是把档案馆的箱子搬去给外面的专家看，而是把专家请进档案馆——文件从不离开这栋楼。

**Cortex AI** 是 Snowflake 的 AI 层，来自 Anthropic、OpenAI、Meta、Mistral 等家的模型都跑在 Snowflake 的安全边界之内。**Cortex AI Functions**（以前叫「AISQL」）把 LLM 直接放进 SQL：`AI_COMPLETE`、`AI_CLASSIFY`、`AI_EXTRACT`、`AI_SENTIMENT`——一条查询就能给 100,000 条评论分类。**Cortex Analyst** 借助定义业务术语的 **semantic view**（「revenue = SUM(amount) where status = 'paid'」）把英文问题变成 SQL。**Cortex Search** 是现成的文本 RAG 搜索：向量 + 关键词 + 重排。**Cortex Agents**（自 2025 年 11 月起正式可用）会规划步骤，调用 Analyst、Search、你的存储过程和 MCP 连接器；2026 年 8 月 28 日，Snowflake 建议从单独的 Analyst API 迁移到 Cortex Agents。**Snowflake Intelligence** 是建在上面的现成聊天界面。**Snowflake-managed MCP server**（`CREATE MCP SERVER`，最多 50 个 tools）把这一切开放给外部 agent，比如 Claude 或 Cursor，使用 Snowflake OAuth 或来自 Okta、Entra 的外部 OAuth。

_Diagram: Functions、Analyst、Search、Agents、Intelligence——全都在 Snowflake 内部。托管的 MCP server 把它们开放给外部 agent。_

#### Cortex (AI Functions · Analyst · Search · Agents)

- **是什么:** Snowflake 内置的 AI：SQL 里的 LLM 函数、text-to-SQL、RAG 搜索和 agent，就在数据旁边。
- **例子:** 一个「为什么德州的利润率下降了？」agent 的 B 方案：一个 semantic view + 一个 Cortex Agent，从你的应用调用——数据从不离开 Snowflake。
- **陷阱:** Cortex 调用按 token 消耗 credit——账单上单独一行；先试 1,000 行，再上一百万行。不是每个模型在每个 region 都有。还有，没有语义层的 text-to-SQL 会胡说八道：没人知道 `AMT_NET_2` 是什么意思。
- **关联:** agent 通过 **MCP** 触达它；Databricks 里对应的是 **Agent Bricks**，Fabric 里是 **Data Agent**。

> **Snowflake 的问题:** 「我的 agent 是否有自己带密钥的 service user、一个只能读 view 的角色、一个闲置一分钟就休眠的小 warehouse、一个 resource monitor——并且在做任何 text-to-SQL 之前先有语义层？」

> Snowflake 把存储和计算分开，计算按秒计费。给 agent 一个用密钥签名的专属 service user、一个只读角色、一个 X-Small warehouse 和一个 credit 上限——再给管理员发一份现成的 SQL 脚本。

## 6. 数据：Databricks

Databricks 起步于文件和 Spark，而不是 SQL 表。它就是 lakehouse：数据在客户自己的云存储里只存一份，所有访问只有一个守卫，AI 平台也建在同一批表上。

### Databricks——仓库、车间和实验室在同一个园区

> 一座工厂园区：放原料的便宜仓库，清洗加工的车间，摆成品的橱窗，做实验的实验室——还有大门口一个查验每个人通行证的守卫。

**Databricks** 是由 Apache Spark 的创造者打造的数据平台。公司把所有数据放在便宜的云文件存储里，用 **Spark**（一个分布式引擎，处理一台机器装不下的数据）来处理，用 SQL 回答问题，在同一批数据上构建 ML 模型和 AI agent——全都在一套权限体系 **Unity Catalog** 之下。它跑在客户的云之上：AWS、Azure（叫 Azure Databricks）或 GCP。人们在 **notebook**（类似 Jupyter，在浏览器里，总是挂着计算资源）和 **job** 里工作；分析师用 SQL 和 **Genie**；AI 工作放在 **Agent Bricks** 里。计算——给 Spark 用的 **cluster**、给 SQL 用的 **SQL warehouse**、**serverless**——以 **DBU**（Databricks Units）计费。还有 **Lakebase**，一个托管的 Postgres，给需要快速读写的应用用。

_Diagram: 人和 agent 在最上面，计算在中间，Unity Catalog 守着一切，数据在客户自己的存储里。_

#### Databricks (lakehouse platform · Azure Databricks)

- **是什么:** 一个 lakehouse 平台：文件在客户的云存储里，上面是 Spark 和 SQL，AI 用同一批表。
- **例子:** 一家分销商把销售、库存和 CRM 放在 Azure Databricks 上的 gold 表 `main.sales_gold.*` 里。
- **陷阱:** 三朵云，三个略有不同的产品：在 Azure 上，身份通过 Entra 而来；功能是否可用（Lakebase、serverless）取决于云和 region。
- **关联:** 表是 **Delta Lake**；权限是 **Unity Catalog**；agent 查询的是 **SQL Warehouse**。

### Delta Lake——一个表现得像表的文件夹

> 仓库的登记簿：架子上的箱子就只是箱子，但门口的登记簿写着哪些箱子构成今天的库存。一笔改掉登记簿——库存就一下子全变了。

**Delta Lake** 是一种开放的表格式。底层是普通的 **Parquet** 文件（一种列式文件格式），外加一个 `_delta_log` 文件夹——一本记录此刻哪些文件构成这张表的日志。多亏这本日志，一个普通的文件夹就获得了真正数据库的能力：**ACID 事务**（写入要么完整落地，要么根本不发生；读者永远看不到一半）、**schema**（你没法往数字列里写字符串）、**time travel**（读取表「昨天的样子」），以及 `MERGE`、`UPDATE`、`DELETE`。如果你懂 Postgres：它就像 WAL，只不过日志就放在 S3 里的文件旁边，而不在数据库服务器里面。Fabric 用的也是同一种格式。

_Diagram: 普通的 Parquet 文件加一份事务日志。日志说明哪些文件构成这张表——于是一个文件夹变成了一张数据库表。_

#### Delta Lake (Parquet + transaction log)

- **是什么:** 一种开放的表格式：Parquet 文件加上一本 `_delta_log` 日志，带来事务和历史。
- **例子:** 每晚的任务把新订单 MERGE 进 `orders`；分析师永远看不到写了一半的表。
- **陷阱:** 它是一种格式，不是服务器：仍然得有东西运行计算来读它——而你付钱买的就是这份计算（DBU）。
- **关联:** **Lakehouse** 的基础；**Databricks** 和 **Microsoft Fabric** 共用。

### Unity Catalog——一个守卫，一本登记簿

> 唯一的大门口站着一个拿登记簿的守卫：不管你是步行、开车还是作为送货机器人来，他都查你的通行证，并记下你拿走了什么。

**Unity Catalog** 是 Databricks 里覆盖所有数据和 AI 的单一权限与目录层；2023 年 11 月 8 日之后创建的每个 workspace 都默认开启它。名字有三级——`catalog.schema.table`，比如 `main.sales_gold.orders`——同一套方案也覆盖 view、**volume**（放 PDF 之类文件的文件夹）、函数和模型。权限用 `GRANT` 授予给人、组和 **service principal**。你可以隐藏行（**row filters**）和列（**column masks**）。**Lineage** 画出数据从哪里来、到哪里去，每个操作都在 **system tables** 里留下审计。不管请求来自 notebook、SQL 还是你的 agent，都要过同一道检查。要读一张表，主体需要同时拥有三项授权：`USE CATALOG` + `USE SCHEMA` + `SELECT`。

_Diagram: notebook、SQL 和 agent 都过同一道检查。Unity Catalog 授予对表、volume 和模型的访问——并记录下来。_

#### Unity Catalog (catalog.schema.table · GRANT · lineage)

- **是什么:** Databricks 针对数据和 AI 对象的集中式权限、目录、lineage 和审计。
- **例子:** agent 的 service principal 拿到 schema `main.sales_gold` 上的 `SELECT` 和 `main.docs.contracts` 上的 `READ VOLUME`——别的什么都没有。
- **陷阱:** 三项授权少一项，看起来就像「table not found」。老 workspace 可能还把表放在遗留的 `hive_metastore` 里，沿用旧规则——查一查哪些已经迁移了。
- **关联:** 守护 **Delta Lake** 表；每次 **SQL Warehouse** 查询都要过它的检查。

### Medallion——bronze、silver、gold

> 矿山里的矿石，熔炉里的金属，橱窗里的成品戒指。珠宝商只卖戒指——但留着矿石，以防熔炉得重新开一次。

**Medallion** 不是产品，而是按质量分层摆放数据的通行做法。**Bronze**——原样保存的原始数据，和来源一模一样：重复、垃圾、格式混杂。保留它是为了一切都能重建。**Silver**——清洗过的：类型正确、没有重复、表之间用键关联。**Gold**——聚合表和业务表：每日收入、活跃客户。报表和 agent 读的是 gold。在各层之间搬运数据的流水线是 **Lakeflow Jobs**（以前叫 Workflows）和 **Lakeflow Declarative Pipelines**（以前叫 Delta Live Tables，DLT）。AI 项目的规则：agent 读 gold，有时读 silver，永远不读 bronze。

_Diagram: 来源 → bronze（原样）→ silver（清洗后）→ gold（可供业务使用）。agent 读 gold。_

#### Medallion (bronze · silver · gold)

- **是什么:** 按质量给数据分层：原始（bronze）、清洗后（silver）、可供业务使用（gold）。
- **例子:** agent 读 `main.sales_gold.orders`；原始的 CRM 导出留在 bronze 里，它够不着。
- **陷阱:** gold 每晚或每小时刷新一次。agent 应该说明它的数字截至哪一天。
- **关联:** 建在 **Delta Lake** 之上；通过 **Unity Catalog** 只给 agent 授予 gold。

### SQL Warehouse 与 OAuth M2M——agent 怎样提问

> 一个带取号机的自助柜台：你出示一次证件，拿到一张一小时内有效的号，工作人员在递给你任何东西之前先核对名单。

**SQL warehouse** 是 Databricks 里专门给 SQL 用的计算；BI 工具和你的应用通过 **server hostname** 和 **HTTP path**（两者都在 warehouse 的 Connection details 里）连接它。有三种类型：**serverless**（推荐，通常 2–6 秒启动）、**pro** 和 **classic**（约 4 分钟）。agent 以 **service principal** 的身份用 **OAuth M2M**（machine-to-machine）登录：它用 `client_id` 和 `client_secret` 换一个短期 token，然后发送 SQL。Unity Catalog 检查授权和过滤规则；引擎只读需要的文件和列；行被返回；每条查询都会进入审计表（`system.access.audit`）。用查询参数（`:since`）传值，别拼接字符串——即使 SQL 是 LLM 写的——并且总要加上 `LIMIT`。

_Diagram: agent 用 client ID 和 secret 换一个短期 token，把 SQL 发给 warehouse，Unity Catalog 检查，行被返回，全程有审计。_

#### SQL Warehouse (serverless · OAuth M2M · DBU)

- **是什么:** Databricks 只跑 SQL 的计算，应用和 agent 用 service principal 的 token 来查询它。
- **例子:** 一个只给 agent 用的小 serverless warehouse（`CAN USE` 权限）——它的成本一目了然，还能有自己的上限。
- **陷阱:** classic warehouse 启动要约 4 分钟，所以 agent 的第一条查询会超时——而 API 默认创建的就是 classic。个人访问令牌（PAT）常常被禁用；请用 OAuth M2M。
- **关联:** 由 **Unity Catalog** 检查；身份是 **Service Principal**；设计上的选择是 **As the User vs As Itself**（U2M / on-behalf-of）。

### Agent Bricks——Databricks 的 AI 总招牌

> 一座购物中心，所有 AI 店铺都搬到了同一个屋顶、同一块招牌下——还是原来那些店，入口上方换了个新名字。

这个角落里名字改得最多。**Mosaic AI** 是以前的总称（来自 Databricks 收购的 MosaicML）；自 2026 年 3 月起，所有 AI 的总招牌是 **Agent Bricks**——一个构建和运行 agent 的平台。它支持前沿模型和开源模型（OpenAI、Anthropic、Gemini 等），支持 LangGraph、CrewAI 和 Claude Agent SDK 等框架，通过 **MLflow** 做 tracing，提供托管的 agent 记忆（基于 Lakebase）、运行代码的沙箱，以及文档解析（`ai_parse_document`、`ai_extract`、`ai_classify`）。围绕它的还有：**Model Serving**（把模型变成 REST 端点，LLM 兼容 OpenAI 接口）、**Unity AI Gateway**（模型和 MCP 调用的统一关口：权限、预算、限额、PII 保护、tracing）、**Databricks Apps**（在 Databricks 里托管你的 agent）、**Genie**（用英文向数据提问，带 Genie MCP server）。Vector search 在 2026 年 6 月 1 日改名为 **AI Search**，现在还支持全文检索、混合检索和重排；索引可以和 Delta 表保持同步。

_Diagram: 模型、框架、tracing、serving、AI gateway、搜索、Genie 和应用托管——全都在 Agent Bricks 之下。外加你仍会听到的那些旧名字。_

#### Agent Bricks (formerly Mosaic AI · AI Search · Genie)

- **是什么:** Databricks 在数据旁边构建、运行和治理 agent 的平台。
- **例子:** 合同用 `ai_parse_document` 解析 → 一张 chunks 表 → 一个与之保持同步的 AI Search 索引；agent 的模型调用都经过 Unity AI Gateway。
- **陷阱:** 到处都在改名：DLT → Declarative Pipelines，Workflows → Lakeflow Jobs，Vector Search → AI Search，Delta Sharing → OpenSharing（2026 年 6 月）。客户的文档用的是旧名——两个都要认得。
- **关联:** Snowflake 的对应物是 **Cortex**；Fabric 的是 **Data Agent**；Azure 的是 **Microsoft Foundry**。

> **Databricks 的问题:** 「我的 agent 是否只读 gold 表，通过一个小的 serverless SQL warehouse，以 service principal 身份用 OAuth M2M，在 Unity Catalog 里只有只读授权——并且每条查询都在审计表里？」

> Databricks 就是 lakehouse：Delta 文件放在客户的存储里，Unity Catalog 是唯一的守卫，gold 表给 agent 读，再配一个用 OAuth 的小 serverless warehouse——而不是个人令牌。

## 7. 数据：Microsoft Fabric

Fabric 是 Microsoft 对 Snowflake 和 Databricks 的回应：一个产品里包含存储、数据导入、处理、Power BI 和 AI。已经生活在 Microsoft 世界里的公司会选它。这里的新想法是：全公司一个存储，以及一份人人都在花的共享 capacity。

### Microsoft Fabric 与 OneLake——「数据界的 Office」

> 五栋楼里的五间储藏室，每间一把钥匙，换成一栋大楼、一张通行证、一张电费单。

**Microsoft Fabric** 是一个一体化的 SaaS 数据平台：一个共享存储（**OneLake**）、数据导入（**Data Factory**）、处理（Spark、SQL）、报表（**Power BI**）和 AI（Copilot、data agent）——都在一个浏览器门户里，通过 Entra ID 登录，作为一份 **capacity** 付费。在 Fabric 之前，一家 Microsoft 公司要把 Azure Data Factory、ADLS、Azure Synapse 和 Power BI 粘在一起，每样都有自己的账单、权限和网络，数据要拷贝三四次。**OneLake** 是整个 tenant 的一个逻辑存储，自动创建——Microsoft 称它为「数据界的 OneDrive」。里面按 **workspace** 和 **item** 分文件夹，表是 Delta 格式（和 Databricks 同一种开放格式）；从外面看，它就像 ADLS Gen2。workspace 的角色有 **Admin**、**Member**、**Contributor**、**Viewer**——你也可以单独共享某个 item。在 Power BI 里，**semantic model**（以前叫「dataset」）是由表、关系和 DAX 度量组成的业务层——公司常常已经在这里商定了「收入」是什么意思。

_Diagram: 一个 Entra tenant，一份付费 capacity，装满 item 的 workspace——而在这一切之下，是 OneLake。外部数据通过 shortcut 或 mirroring 进来。_

#### Microsoft Fabric (OneLake · workspace · item)

- **是什么:** Microsoft 的一体化数据平台，一个存储（OneLake），一张账单（capacity）。
- **例子:** 一家诊所连锁把它的 EHR 和账单数据从 Azure SQL 镜像进 Fabric，在 F64 上用 Power BI 出报表。
- **陷阱:** 比 Databricks 简单，但控制力更弱。而且名字在变：dataset → semantic model，「license mode」→「workspace type」，P SKU → F SKU。
- **关联:** 通过 **Capacity** 付费；通过 **SQL Analytics Endpoint** 读取；由 **Shortcuts and Mirroring** 供数。

### Capacity 与 F SKU——一张共享的电费单

> 整栋办公楼共用一份电力合同：如果你的新电暖器开到最大，CFO 那层楼的灯也会跟着闪。

**capacity** 是公司买下的一池计算资源。每个 workspace 挂在一份 capacity 上，它做的一切——SQL、Spark、报表刷新、Copilot——都在花这份 capacity 的 **CU（capacity units）**。尺寸叫 **F SKU**：F2、F4、F8 … F64 … F2048；数字就是 CU 数，F64 对应老的 Power BI Premium P1。它在 Azure 里购买，按秒计费（最少一分钟），或按年预留享受折扣；价格取决于 region，所以要按客户的 region 查 Azure 计算器。capacity 可以暂停——那时里面什么都不能用。**F64 是一道门槛**：从 F64 起，拥有免费许可证的人也能查看 Power BI 报表；低于它，每个查看者都需要 Pro 或 PPU。Fabric 允许重任务短时间用超过你买的量（**bursting**），并把成本摊到一段时间里（**smoothing**：交互式工作至少摊 5 分钟，后台工作摊 24 小时）。用过了头，就开始 **throttling**：「欠账」10 分钟后交互式查询要等 20 秒，60 分钟后被拒绝，24 小时后一切都被拒绝。

_Diagram: 所有人都从一份 capacity 里花。超出它的短时爆发会被摊平；欠账太多就意味着延迟，然后是拒绝。_

#### Capacity (F SKU · CU · throttling)

- **是什么:** 共享的、预先买好的计算池，挂在它上面的每个 Fabric workspace 都在花它。
- **例子:** 一份 F64 服务整个公司；agent 的查询保持小巧，并在 Capacity Metrics 应用里盯着。
- **陷阱:** throttling 不是「稍微慢一点」——而是所有人都收到 `CapacityLimitExceeded` 错误。还有，如果客户为了省钱在夜里暂停 capacity，你的 agent 夜里就不工作。
- **关联:** **Microsoft Fabric** 的账单；Snowflake 按集群计费的 **Virtual Warehouse** 是相反的模式。

### SQL Analytics Endpoint——从外面看，Fabric 就像 SQL Server

> 博物馆的展柜：你可以从各个角度随便看多久都行，但前面隔着玻璃——什么都不能挪动，也不能添加。

Fabric 的表有两个家。**lakehouse** 给数据工程师用：Delta 表（`Tables` 文件夹）加上任意文件（`Files` 文件夹），用 Spark、notebook 和流水线写入。**warehouse** 给写 SQL 的人用：只有表，带完整的 **T-SQL**（Microsoft 的 SQL 方言）——`INSERT`、`UPDATE`、`MERGE`。两者都是 OneLake 里的 Delta，你可以在一条查询里把它们 join 起来。每个 lakehouse（以及镜像数据库）都会自动得到一个 **SQL analytics endpoint**：在它的 Delta 表上提供只读的 T-SQL。你可以 `SELECT`，创建 view 和函数，设置行级安全——但不能修改数据。从外面看，它就像 SQL Server：你的 agent 用 ODBC Driver 18、通过 1433 端口（TDS 协议）连接到像 `xxxx.datawarehouse.fabric.microsoft.com` 这样的地址，带着 scope 为 `https://database.windows.net/.default` 的 Entra token。对 agent 来说，只读是件好事。

_Diagram: lakehouse 与 warehouse 一览。agent 通过 lakehouse 的只读 SQL endpoint 读它，就像读任何 SQL Server 一样。_

#### SQL Analytics Endpoint (lakehouse vs warehouse · T-SQL)

- **是什么:** Fabric lakehouse 或镜像数据库上自动生成的、只读的、SQL Server 风格的端点。
- **例子:** `pyodbc` + 一个 Entra token → 带参数的 `SELECT TOP (100) clinic, COUNT(*) … FROM dbo.appointments`。
- **陷阱:** 只有 `/Tables` 里的表会显示出来，新表要等元数据同步之后（有时会延迟）。你不能在那里写 agent 日志或缓存——请用 warehouse、Fabric 里的 SQL database，或者你自己的数据库。
- **关联:** 花的是 **Capacity**；访问需要给 **Service Principal** 一个 workspace 角色或 item 权限。

### Shortcuts 与 Mirroring——不用搭 ETL 就有数据

> 桌面上的快捷方式打开的是放在别处的文件。Mirroring 则是那个文件的副本，原件每次变化，它都自动更新。

这是 Fabric 避免搭拷贝流水线的办法。**shortcut** 就像 symlink：lakehouse 里的一个文件夹，实际指向 S3、ADLS、Google Cloud Storage、Dataverse、SharePoint/OneDrive 或另一个 OneLake item。不拷贝任何数据；删掉 shortcut，源头毫发无损。对于内部 shortcut，访问权限按读取者的身份检查。**Mirroring** 是持续的副本：Fabric 把一个数据库以 Delta 格式复制进 OneLake，接近实时。来源包括 Azure SQL、SQL Server、Azure Database for PostgreSQL、Cosmos DB、Snowflake、Google BigQuery、Oracle 和 SAP。对 Azure Databricks 有元数据镜像：只拷贝目录，数据通过 shortcut 读取。复制所用的计算是免费的，存储副本在每 CU capacity 1 TB 以内也免费。

_Diagram: shortcut 指向数据原本所在的地方。Mirroring 在 OneLake 里保存一份接近实时的 Delta 副本。_

#### Shortcuts and Mirroring (symlink · live copy)

- **是什么:** 把外部数据带进 OneLake 的两种方式：指向它（shortcut），或保存一份实时副本（mirroring）。
- **例子:** Azure SQL 的账单表被镜像进 Fabric；agent 通过镜像数据库的 SQL endpoint 读取它们。
- **陷阱:** 暂停 capacity 也会让 mirroring 停下。shortcut 的名字不能有空格或非拉丁字符。
- **关联:** 把数据落进 **Microsoft Fabric** 的 OneLake；镜像数据库会得到一个 **SQL Analytics Endpoint**。

### Fabric Data Agent——现成的「和数据聊天」

> 一位把馆藏目录烂熟于心的参考馆员，替你把答案找来——但只从你的借书证能打开的书架上找，而且绝不让你在书上写字。

**Fabric data agent** 是一个可配置的「和数据聊天」。你挑选最多五个来源——lakehouse、warehouse、semantic model、KQL 数据库、镜像数据库、ontology——写好说明和「问题 → 查询」示例，agent 就会把问题变成 SQL、DAX 或 KQL 并执行。它已经正式可用。它是**只读的**，并且**以用户身份**工作：只能看到那个人能看的东西。你可以从 Microsoft 365 Copilot、Copilot Studio、Teams、Microsoft Foundry 调用它，或者把它当作 MCP server 调用。**Fabric IQ** 是更新的一层「业务含义」（ontology、semantic model、data agent），供外部 agent 使用；在 Foundry Agent Service 里，旧的「Fabric data agent」工具将于 2026 年 11 月 6 日退役，由 Fabric IQ 取代。**Copilot in Fabric** 这个内置助手，现在在任何从 F2 起的付费 capacity 上都能用（F64 的要求在 2025 年 4 月取消了）——但在试用 capacity 上不行。

_Diagram: 可从 Teams、Copilot、Foundry 或 MCP 调用；在最多五个来源上把问题变成 SQL、DAX 或 KQL——只读，以用户身份。_

#### Fabric Data Agent (Fabric IQ · Copilot)

- **是什么:** Fabric 内置的、只读的「问题变查询」agent，作用于选定的来源，以用户身份行事。
- **例子:** 一个「为什么奥斯汀的爽约率在上升？」agent 的 A 方案：一个基于 semantic model 的 data agent，发布到 Teams——用户权限自动生效。
- **陷阱:** 只支持英文，不能选 LLM，答案上限 25 行 × 25 列，不支持 PDF，而且来源和 agent 必须在同一个 capacity region。需要更多控制？在 SQL endpoint 上自建 agent（B 方案）。
- **关联:** **Cortex** 和 **Agent Bricks** 在 Fabric 里的同类；B 方案使用 **SQL Analytics Endpoint**。

> **Fabric 的问题:** 「Fabric 管理员是否允许了 service principal，我的主体是否只在一个 item 上有最小的角色，agent 的查询是否小到适合共享 capacity——而基于他们 semantic model 的 data agent 是不是已经够用了？」

> Fabric 是装在一个盒子里的 Microsoft 数据平台：OneLake 管存储，一份共享 capacity 管计算，一个只读 SQL endpoint 给你的 agent——简单路线够用时，还有内置的 data agent。

## 8. 把它们串起来

现在是真正的工作：在客户公司里跑一个 AI agent。上面每个系统问的都是同样几个问题——agent 是谁，它能读什么，它以谁的身份行事，安全团队能不能签字放行。早点回答，项目就往前走；回答晚了，项目就得等。

### 蓝图：一个 agent，三层

> 作为新租户搬进一栋写字楼：前台给你工牌，房东给你一个单元，档案馆给你一把只开你那层架子的钥匙——全都在搬家卡车到来之前安排妥当。

用大白话说一个典型项目。客户的数据在 Snowflake 里（或 Databricks，或 Fabric）。员工通过 Entra ID（或 Okta）登录。应用跑在客户的云里——常常是 Azure。你的 agent 只通过客户自己的登录放行「他们的人」，用的是他们的 IT 分配给组的 app roles。它作为一个带 managed identity 的 Container App 运行，调用 Foundry 里的 model deployment，从 Key Vault 读取秘密，如果安全团队要求，就藏在 private endpoint 后面。它用最小的权限读数据：一个带密钥和只读角色的 Snowflake service user，一个用 OAuth M2M、只有 gold 授权的 Databricks service principal，或者一个在单个 item 上是 Viewer 的 Fabric service principal。日志进 Application Insights——prompt 里不带个人数据。部署从 GitHub 用 OIDC 进行，仓库里没有秘密。

_Diagram: 通过客户的 IdP 登录，agent 带着 managed identity 跑在他们的 Azure 里，再用一条窄连接接到数据平台。_

### Least Privilege——只给工作需要的

> 酒店保洁员的房卡在她当班时能开她那层的房间——开不了保险柜，开不了经理办公室，也开不了别的楼层。

**least privilege** 的意思是只给所需的最小权限，作用于最小的范围，默认只读。每个系统都有自己的版本：一个 resource group 上的 Contributor，而不是整个 subscription；像 `AI_AGENT_RO` 这样只在 view 上有 `SELECT` 的角色，而不是任何接近 `ACCOUNTADMIN` 的东西；delegated 的 `Files.Read.All`，而不是 application 的 `Sites.Read.All`；单个 Fabric item 上的 Viewer，而不是 workspace 上的 Admin；在 Unity Catalog 里只给 gold schema。对 agent 来说，这比对人更重要：LLM 可能被 prompt injection 骗到，而一旦被骗，损失恰好止步于它被允许做的事。

_Diagram: 同样的四个系统：左边给得太多，右边刚刚好。_

#### Least Privilege (minimal rights · smallest scope)

- **是什么:** 只授予工作所需的权限，范围尽可能窄，时间尽可能短。
- **例子:** agent 的 Snowflake 角色能读 `SALES.MART` 里的三个 view，别的什么都不行——连它们背后的原始表都不行。
- **陷阱:** 给得太窄，agent 会以奇怪的方式失败（「table not found」）。从最小开始，一项一项加权限，并写下每一项为什么存在。
- **关联:** 塑造 **Resource Hierarchy** 的角色、**Roles and RBAC**、**Unity Catalog** 的授权；**Security Review** 最先检查的东西。

### As the User vs As Itself——agent 用谁的眼睛看

> 派助理拿着你自己的借书证去档案馆，还是拿着整栋楼的万能卡去。用你的卡，她只能带回你能拿的东西。用万能卡，她什么都能带回来——而你得决定她把什么给谁看。

这是最大的设计选择，每次 security review 都会问。**As the user**：agent 用已登录者的身份调用数据源——Entra 里的 delegated permissions、on-behalf-of、Databricks 里的 OAuth U2M、通过 Okta 或 Entra 登录 Snowflake、Fabric data agent。数据源套用每个人自己的权限，于是 Anna 看到德州，Ben 看到俄亥俄，不费吹灰之力。**As itself**：agent 用一个 **service account**——service principal、managed identity、Snowflake service user，「给程序而不是给人用的账号」。每个和 agent 对话的人看到的数据都一样，所以*你*必须做过滤：Snowflake 里的 row access policies、Unity Catalog 里的 row filters、Fabric 里的 RLS，或者你自己的代码。经验法则：回答人的问题 → 以用户身份；像夜间索引这样的后台任务 → 一个小的服务身份。

_Diagram: 以用户身份：套用每个人自己的权限。以它自己：一个身份看到一切，过滤的活儿归你。_

#### As the User vs As Itself (delegated / OBO vs service account)

- **是什么:** 在两者之间做选择：agent 用每个用户的身份行事，还是用它自己的服务身份行事。
- **例子:** 合同助手以用户身份搜索 SharePoint；另一个后台索引器则以服务身份运行，只带一小组 application permissions。
- **陷阱:** 对医疗或其他敏感数据来说，这是审查的关键问题。如果用一个共享身份，就要准备好展示行到底是怎么过滤的。
- **关联:** 由 **Delegated vs Application Permissions**、**Cross App Access**、**Service User** 构成；**Security Review** 的核心问题。

### Security Review——上线前的考试

> 开业前的建筑验收：检查员拿着清单走一遍，最后一个勾打完，大门才打开。

**security review** 是客户的安全团队在你的方案上线前对它做的检查。问题是可以预料的，所以提前准备好答案——最好是一份列出每条连接的简短文档。数据去哪里，模型跑在哪个 region，用的是哪个 LLM、走哪份合同（data residency）？有没有 API key？正确答案是「没有——managed identity，秘密在 Key Vault 里」。服务对互联网关闭了吗？角色是最小的吗？agent 是以用户身份还是以它自己行事，谁能看到什么？它自己能*做*什么——写入、删除、发邮件——有没有人来确认？日志里记什么、保存多久，prompt 里有没有个人数据？怎么撤销访问——停用一个用户、删除 enterprise app——是不是立即生效？防御 prompt injection 和内容过滤也会被问到。

_Diagram: 每个安全团队都会问的八个问题。在他们开口之前，就把答案写好。_

#### Security Review (客户的签字放行)

- **是什么:** 客户安全团队在上线前对数据流、身份、权限、日志和撤销机制所做的检查。
- **例子:** 你带去一页纸的地图：每条连接、它的身份、它的角色，以及为什么需要它——再加上能显示每条查询的审计表。
- **陷阱:** 应用范围的读权限（`*.Read.All`）最让审查者害怕。准备好为每一项给出理由，或者改用 delegated 访问。
- **关联:** 检查 **Least Privilege**、**As the User vs As Itself**、**Private Endpoint**、**Key Vault**。

### Day-One Access——先启动慢的部分

> 为婚礼定做一套西装：量体裁衣要好几周，所以收到请柬那天就去订，而不是婚礼前一周。

在中型企业里，权限通常是整个项目里最慢的部分。一个 guest 账号、一个 resource group 上的角色、一个带 admin consent 的 app registration、一个 Snowflake service user、Unity Catalog 授权、Fabric 的 tenant 设置「Service principals can call Fabric public APIs」——每一项都是发给不同管理员的一张工单，加在一起可能要一到三周。你不是管理员，也不会成为管理员。所以第一天就发一条包含一切的消息：redirect URI、scope、app roles、组、授权——以及每一项为什么需要。对 Snowflake，发一份管理员直接运行就行的现成 SQL 脚本。先申请 preview/测试环境。并且在开工之前，就在正确的 region 里检查模型 quota，别等到演示那天。

_Diagram: 第一天发出完整清单，开发完成时权限也就绪了。第三周才申请，演示就得等一个角色。_

#### Day-One Access (权限清单)

- **是什么:** 在最开始就用一份精确清单申请所有账号、角色和设置的习惯。
- **例子:** 第 1 天：guest + `rg-ai-agent-dev` 上的 Contributor、app registration 的细节、`SVC_AI_AGENT` 脚本、Fabric 管理员的联系方式、quota 检查。
- **如何避免:** 尽早找到每一位管理员——Fabric 管理员常常不是 Azure 管理员。像跟踪任务一样跟踪各项申请，写明负责人和日期。
- **关联:** **Least Privilege** 的实践一面；为 **Security Review** 提供素材。

### 一切如何相连

> 一幅拼图：每一块单独看意义不大；拼在一起就成了整幅画。

这门课里的概念是一个整体。**云**这一支——Azure 的 **resource hierarchy**、**managed identity**、**Key Vault**、**private endpoint** 和 **Foundry**——回答*agent 跑在哪里、怎样做到不持有秘密*。**身份**这一支——**IdP 与 SSO**、**OIDC**、带 **app registration**、**permissions**、**app roles** 和 **Conditional Access** 的 **Entra ID**、带 **SCIM** 和 **Cross App Access** 的 **Okta**，以及 **Agent ID**——回答*agent 和它的用户是谁*。**数据**这一支——从 **lake** 到 **lakehouse**，**Snowflake**、**Databricks**、**Fabric**——回答*agent 能读什么*。三支汇合在你的 agent 里，由 **least privilege**、**以用户身份还是以它自己**的选择，以及 **security review** 绑在一起。

_Diagram: 三个分支——云、身份、数据——汇合在你的 agent 里，由 least privilege 和 security review 绑在一起。_

#### 在客户那里启动 AI 项目之前

- **管理员都是谁**——Azure、Entra 或 Okta、数据平台、Fabric——我给每一位都发了精确清单吗？
- **agent 跑在哪里**——一个带 managed identity 的 Container App，只在一个 resource group 上有角色？
- **人们怎么登录**——通过客户 IdP 的 OIDC，app roles 或过滤过的组，稳定的用户 ID？
- **以用户身份还是以它自己**——如果是以它自己，行怎么过滤？
- **它读什么**——只读 gold 表或 view，只读权限，通过一个会休眠的小计算？
- **安全团队会问什么**——密钥、网络、角色、日志、data residency、撤销——我的答案写下来了吗？

#### 你现在掌握的词

- **Tenant、subscription、resource group**——以及为什么一个 RG 上的角色胜过 subscription 上的角色。
- **Managed identity、Key Vault、private endpoint**——一个没有密钥、也没有公网大门的应用。
- **IdP、SSO、OIDC、SAML**——以及为什么用 `oid` + `tid` 或 `sub`、而不是 email 来标识用户。
- **App registration vs service principal**、**delegated vs application permissions**、**admin consent**。
- **App roles、Conditional Access、Microsoft Graph、Entra Agent ID**。
- **Okta org、Universal Directory、SCIM、org vs custom authorization server、Cross App Access**。
- **Data warehouse、data lake、lakehouse、Delta Lake、medallion**。
- **Virtual warehouse、credits、service user、Cortex**；**Unity Catalog、SQL warehouse、OAuth M2M、Agent Bricks**。
- **OneLake、capacity、F SKU、SQL analytics endpoint、shortcut、mirroring、data agent**。
- **Least privilege、service account、security review**。

## 速查表：全部 42 个概念

### 地图

- **Enterprise Stack** (云 · 身份 · 数据): 每家大公司都有的三层；你的 agent 夹在它们中间。

### 云：Azure

- **Microsoft Azure** (Microsoft 的云): 熟悉的云概念，不同的名字；被选中是因为「什么都是 Microsoft 的」。
- **Resource Hierarchy** (tenant · subscription · RG): 层层嵌套的文件夹；在上层授予的角色作用于下面的一切。
- **Container Apps** (App Service · AKS · Functions): serverless 容器——agent 后端常见的家。
- **Managed Identity** (没有密码): 从 Entra 获取 token 的应用账号；没有可泄露的东西。
- **Key Vault** (秘密保险箱): 为数不多的真正秘密住在这里，用一个窄角色读取。
- **Private Endpoint** (VNet): 给服务一个私有 IP，这样它就能对互联网关闭。
- **Microsoft Foundry** (formerly Azure AI Foundry): 客户 tenant 内的 model deployment、agent 和评估。

### 身份：Entra ID

- **Identity Provider** (IdP · SSO): 公司的前台；一个工作账号打开所有应用。
- **OIDC and SAML** (auth code + PKCE): 应用通过 IdP 让人登录的方式；新代码用 OIDC。
- **Microsoft Entra ID** (formerly Azure AD): Microsoft 存放人员、组和应用的云目录。
- **App Registration vs Service Principal** (Enterprise app): 应用的护照，以及它在某个 tenant 里持有权限的副本。
- **Delegated vs Application Permissions** (consent): 以用户身份行事，或以应用自己的身份、经 admin consent 行事。
- **App Roles** (roles claim): 你的角色，由他们的 IT 分配给组，由你的后端检查。
- **Conditional Access** (MFA 规则): 每次登录时的「如果——那么」规则；需要 Entra ID P1。
- **Microsoft Graph** (graph.microsoft.com): 通往 Microsoft 365 数据的一个 API，受 token 的 scopes 限制。
- **Entra Agent ID** (agent blueprint): 为 AI agent 准备的身份类型，像员工一样受治理。

### 身份：Okta

- **Okta** (Workforce Identity): 给多供应商公司用的中立 IdP；每家公司一个 org。
- **SCIM** (joiner–mover–leaver): IdP 自动在你的应用里创建和停用用户。
- **Authorization Server** (org vs custom): org 用于登录；custom 用于你自己 API 的 scope。
- **Cross App Access** (XAA · Agent SSO): agent 通过 Okta 拿到通往其他应用的短期、有记录的 token。

### 数据：Snowflake

- **Warehouse · Lake · Lakehouse** (三种形态): 严格的表、原始文件，或上面加了表规则的文件。
- **Snowflake** (cloud data warehouse): 存储与计算分离的 SQL 分析。
- **Virtual Warehouse** (credits): 按秒计费的计算集群；把 auto-suspend 设成 60。
- **Roles and RBAC** (AI_AGENT_RO): 授权给角色，角色给用户；agent 只能读 view。
- **Service User** (TYPE = SERVICE): 程序的账号；用 key pair 或 OAuth，密码被封禁。
- **Cortex** (数据旁边的 AI): Snowflake 内部的 LLM 函数、text-to-SQL、搜索和 agent。

### 数据：Databricks

- **Databricks** (lakehouse platform): 文件在客户的存储里；Spark、SQL 和 AI 用同一批表。
- **Delta Lake** (Parquet + log): 一个表现得像事务表的文件夹。
- **Unity Catalog** (唯一的守卫): 对每条入口都适用的授权、遮蔽、lineage 和审计。
- **Medallion** (bronze · silver · gold): 质量分层；agent 读 gold。
- **SQL Warehouse** (OAuth M2M): SQL 计算；serverless 几秒启动，classic 要几分钟。
- **Agent Bricks** (formerly Mosaic AI): Databricks 的 AI 总招牌：模型、serving、gateway、AI Search、Genie。

### 数据：Fabric

- **Microsoft Fabric** (OneLake): Microsoft 所有数据工具共用一个存储、一张账单。
- **Capacity** (F SKU · CU): 共享的计算池；超支会导致 throttling。
- **SQL Analytics Endpoint** (只读 T-SQL): 把 lakehouse 看作 SQL Server；非常适合 agent 读取。
- **Shortcuts and Mirroring** (不用 ETL): 指向外部数据，或在 OneLake 里保存一份实时副本。
- **Fabric Data Agent** (Fabric IQ): 内置的、只读的「和数据聊天」，以用户身份行事。

### 串起来

- **Least Privilege** (最小权限): 只给工作需要的，范围尽可能小。
- **As the User vs As Itself** (OBO vs service account): 用每个用户的权限，或者用一个身份、再由你过滤输出。
- **Security Review** (客户的签字放行): 问题可以预料；把答案写下来。
- **Day-One Access** (权限清单): 第一天就用一条精确的消息申请一切。

> 企业里的 AI agent，主要是一个权限项目。让它跑在他们的云里、不带密钥，通过他们的 IdP 让人登录，只读最小的一块数据，并且第一天就把一切都申请好。

速成课结束 · 8 章 · 42 个概念

接下来是实践：注册一个免费的 Azure 账号，部署一个小小的 Container App，给它一个 user-assigned managed identity，让它从 Key Vault 读取一个秘密——任何地方都没有密钥。在一个 Entra 测试 tenant（或 Okta developer org）里注册一个应用，用 OIDC 登录，再亲手检查一遍 ID token。然后开一个 Snowflake 试用账号，创建一个带 key pair 和只读角色的 `TYPE = SERVICE` 用户，从 Python 查询一个 view。把你需要的每一项授权及其理由都写下来——这份清单就是你的第一份 security review 文档。有一个想法要置于其余之上：企业技术栈不过是三个问题——它跑在哪里，它是谁，它能读什么——而最好的答案永远是最小的那个。
