goose 接入 Azure AI Foundry:三种端点类型、协议路由与部署元数据解析实战
【免费下载链接】goosean open source, extensible AI agent that goes beyond code suggestions - install, execute, edit, and test with any LLM项目地址: https://gitcode.com/GitHub_Trending/goose3/goose
本文以 goose 的azure_foundry提供商为核心,完整讲解如何把 goose 会话接入 Azure AI Foundry 的 Project / Resource / MaaS 三类端点:包括全部配置环境变量与取值前提、认证优先级(Entra 令牌 → API Key → Azure CLI)、基于modelPublisher的协议路由机制、部署元数据对上下文窗口的解析,以及常见 401/403 与"列不出部署"问题的排查方法。读完后,你可以直接在自己的 Foundry 项目中配置 goose 会话,并理解每次请求在底层被路由到哪条推理协议、能力参数从哪里来。
一、三种端点类型与对应的推理接口
azure_foundry提供商把 goose 连接到 Azure AI Foundry 的模型部署。它区分三种端点形态,每种形态的推理接口(inference surface)不同:
| 端点类型 | 端点形态 | 推理接口 |
|---|---|---|
| Foundry 项目(Project) | https://<resource>.services.ai.azure.com/api/projects/<project> | 通过部署发现 + 按发布者(publisher)感知路由 |
| Foundry 资源(Resource) | https://<resource>.services.ai.azure.com | OpenAI 模型走 Responses,Claude 走 Anthropic Messages,伙伴模型走 Chat Completions |
| MaaS / Serverless | https://<deployment>.<region>.models.ai.azure.com | 对该端点绑定的模型使用 Chat Completions |
端点类型的识别并不依赖任何额外配置,而是由 goose 在解析AZURE_FOUNDRY_ENDPOINT时自动完成。实现位于 endpoint_kind:
- URL 中包含
/api/projects/子串 →Project端点; - 否则主机名以
.services.ai.azure.com结尾 →Resource端点; - 其余情况 →MaaS端点。
这一点在测试endpoint_type_is_detected中有直接验证(crates/goose-providers/src/azure_foundry.rs#L791-L809),三类 URL 各归其位。
对 Project 端点,goose 会用GET /deployments发现项目内的全部模型部署;部署名可以被自定义,goose 依据返回的modelPublisher字段选择协议,并依据modelName解析上下文窗口等模型元数据。Resource 端点不提供项目级部署发现接口,goose 只能按模型名/部署名前缀识别模型族:gpt-5*和支持的 o 系列走 Responses、claude-*走 Anthropic Messages、其余走 Chat Completions。如果你的部署是自定义别名且无法从名字判断模型族,建议使用 Project 端点。
二、配置:环境变量与 goose configure
| 变量 | 是否必填 | 说明 |
|---|---|---|
AZURE_FOUNDRY_ENDPOINT | 是 | 完整的 Foundry 项目或 MaaS 端点 |
AZURE_FOUNDRY_API_KEY | 否 | API Key;省略时使用 Azure CLI 凭证 |
AZURE_FOUNDRY_MODEL | 仅 MaaS 必填 | 绑定到所配置 MaaS 端点的模型名 |
AZURE_FOUNDRY_AD_TOKEN | 否 | 预取的 Microsoft Entra 访问令牌;优先级高于 API Key |
AZURE_FOUNDRY_API_VERSION | 否 | 部署发现 API 版本;Project 端点默认v1 |
运行goose configure,选择Configure Providers,再选择Azure AI Foundry,即可交互式配置;也可以在启动 goose 之前直接设置环境变量:
export AZURE_FOUNDRY_ENDPOINT="https://my-resource.services.ai.azure.com/api/projects/my-project" export AZURE_FOUNDRY_API_KEY="<key>" goose session对于 MaaS 端点:
export AZURE_FOUNDRY_ENDPOINT="https://my-deployment.eastus.models.ai.azure.com" export AZURE_FOUNDRY_API_KEY="<key>" export AZURE_FOUNDRY_MODEL="<model-bound-to-this-endpoint>" goose sessionMaaS 端点只暴露一个已部署模型,因此AZURE_FOUNDRY_MODEL对这类端点是硬性要求。这一点在构造阶段就会强制校验:AzureFoundryProvider::create 中,当端点被识别为 MaaS 而maas_model为空时,直接返回错误AZURE_FOUNDRY_MODEL is required for MaaS endpoints。
配置项在源码中的定义
这些变量在 ProviderDescriptor::metadata 中注册为ConfigKey,默认模型为Phi-4,并内置一份AZURE_FOUNDRY_KNOWN_MODELS已知模型清单(Phi-4、Meta-Llama-3.3-70B-Instruct、Mistral-large-2411、Cohere-command-r-plus、DeepSeek-R1/V3、glm-4.7、Kimi-K2-Instruct、claude-sonnet-4-6、gpt-5、o3 等)。当端点不是 Project 端点时(即 Resource 或 MaaS 未显式配置模型),fetch_supported_models返回的正是这份清单(crates/goose-providers/src/azure_foundry.rs#L458-L478)。
"已配置"的判定逻辑在 azure_foundry_configured_values:端点非空,且(非 MaaS 端点,或 MaaS 端点同时提供了模型名)才视为该提供商可用。
三、认证优先级与资源标识
认证按如下顺序选择:
AZURE_FOUNDRY_AD_TOKEN(预取的 Entra 令牌)AZURE_FOUNDRY_API_KEY- Azure CLI 默认凭证链
当两者都未配置时,先执行az login再启动 goose 即可:
az login这条优先级链直接对应 AzureAuth::new_with_resource 中的模式匹配:(Some(token), _) => BearerToken,(None, Some(key)) => ApiKey,(None, None) => DefaultCredential。
Project 与 Resource 端点为https://ai.azure.com申请令牌,MaaS 端点则申请https://ml.azure.com的令牌。两个资源标识符定义在 azure_foundry_def.rs(AZURE_PROJECT_ENTRA_RESOURCE/AZURE_MAAS_ENTRA_RESOURCE),并在from_env中按端点类型选择。
认证头的实现细节
不同端点/凭证组合使用不同的请求头策略,见 from_env:
- API Key 用于 Project/Resource:以
api-key头发送,并启用"同源重定向"限制; - API Key 用于 MaaS:改用标准
Authorization: Bearer头; - API Key 用于 Anthropic 面(
/anthropic):使用x-api-key头; - Entra 令牌:以
Authorization: Bearer <token>发送。
关于重定向限制,值得单独说明:当认证方式是 API Key 时,客户端会限制重定向只在同源内跟随(configured_client 中的with_same_origin_redirects)。测试 foundry_api_keys_do_not_follow_cross_origin_redirects 验证了这一点:若源服务器把带api-key/x-api-key头的请求 307 重定向到其他域,客户端会直接报错,且目标域不会收到任何请求——这是为了防止 API Key 通过跨域重定向泄漏。同域重定向则正常跟随(foundry_api_keys_follow_same_origin_redirects测试)。
四、协议路由:publisher 感知与降级策略
Project 端点的三路分发
对 Project 端点,goose 依据 Azure 返回的部署元数据为每个部署选择协议:
- 发布者
OpenAI且为 Responses 兼容模型(gpt-5*与支持的 o 系列)→POST /openai/v1/responses - 发布者
Anthropic→POST /anthropic/v1/messages - 更老的 OpenAI 模型与其他所有发布者 →
POST /openai/v1/chat/completions
核心路由函数是 inference_route。测试 routing_matrix_uses_endpoint_publisher_and_underlying_model 给出了完整的判定矩阵:
| 端点 | 发布者 | 底层模型 | 路由结果 |
|---|---|---|---|
| MaaS | OpenAI | gpt-5 | MaasChatCompletions |
| MaaS | Anthropic | claude-sonnet-4-6 | MaasChatCompletions |
| Project | OpenAI | gpt-5 | ProjectResponses |
| Project | OpenAI | o3-mini | ProjectResponses |
| Project | OpenAI | gpt-4o | ProjectChatCompletions |
| Project | Partner | gpt-5 | ProjectChatCompletions |
| Project | Anthropic | 任意 | AnthropicMessages |
其中"Responses 兼容模型"的判定来自 is_openai_responses_model,正则(?i)(?:^|[-/])(?:o\d+(?:$|-)|gpt-5(?:$|[-.]))同时匹配gpt-5、gpt-5.x(如gpt-5.6-sol)以及o1/o3-mini这类 o 系列命名。
注意路由的语义:路由由发布者和底层模型共同决定,与部署的显示名无关。比如 Partner 发布者的gpt-5部署仍然走 Chat Completions(因为协议选择以 publisher 为准),而 Anthropic 发布者下的任何部署都走 Messages 面。
三个推理客户端的构建
Provider 构造时会同时准备最多四个 HTTP 客户端(AzureFoundryProvider::create):
- Chat 客户端:OpenAI 兼容协议。Project/Resource 端点的路径前缀是
openai/v1/,MaaS 端点是v1/——这与文档中"MaaS 端点始终走/v1/chat/completions"一致; - Responses 客户端:仅非 MaaS 端点构建,base path 为
openai/v1/responses,并跳过规范化过滤(skip_canonical_filtering); - Anthropic 客户端:仅非 MaaS 端点构建,请求发往
https://<resource>.services.ai.azure.com/anthropic(代码中取端点的/api/projects/之前的部分作为 hub),并附加anthropic-version头; - Deployments 客户端:用于调用
GET /deployments做部署发现。
部署发现不可用时的降级
当部署发现暂时不可用时,gpt-5*/o 系列这类可识别的 Responses 兼容名与claude-*名仍会落到各自的 native 协议,其余名字走 Chat Completions。
从源码结构看,这套降级由两层缓存机制支撑(DeploymentCache 与 deployment_for):
- 部署元数据带 60 秒 TTL(
DEPLOYMENT_METADATA_TTL_SECS),单次拉取有 5 秒超时(DEPLOYMENT_METADATA_TIMEOUT_SECS),避免元数据接口抖动阻塞推理请求; - 缓存区分两种用途:上下文发现(
ContextDiscovery)和推理路由(InferenceRouting)。即使上次拉取失败,空缓存也允许用于上下文发现(避免每次推理都重试拖慢响应),但推理路由会在 TTL 内重试拉取; - 测试 routing_retries_cached_deployment_failures 精确验证了这个不对称行为。
部署列表拉取:分页与 api-version
fetch_deployments 实现了一个标准的 OData 风格分页循环:
- 首次请求
deployments?api-version=<version>,其中 version 取AZURE_FOUNDRY_API_VERSION,Project 端点缺省为v1; - 响应体的
value数组中每项含name(部署名)、modelName(底层模型)、modelPublisher(发布者),缺失modelName时回退为部署名,缺失modelPublisher时用模型名前缀推断(ModelPublisher::from_model_name); - 跟随
nextLink翻页,并用 with_api_version 保证分页链接上携带同一api-version(若链接已带该参数则不覆盖)。测试pagination_link_keeps_api_version覆盖了两种情形。
测试 deployment_discovery_is_paginated_and_preserves_underlying_model 用 wiremock 模拟了两页部署数据,验证翻页与name → modelName/publisher映射的完整性。
五、模型元数据:上下文窗口、能力与定价
部署 API 返回name、modelName、modelVersion、modelPublisher。goose 用底层模型名(modelName)在内置模型目录中查找上下文窗口,部署显示名只作为ModelInfo.name保留。核心逻辑在 model_info_for_deployment:
- 先按原始名、小写名、以及剥离
-high/-low等推理强度后缀后的基名,三次尝试在 canonical 目录中查找(配合extract_reasoning_effort解析 effort 后缀); - 查到后取其
limit.context作为context_limit,reasoning取目录值、否则按模型名启发式判定; - 显式的
GOOSE_CONTEXT_LIMIT或会话级覆盖始终优先。
测试给出的具体数值可以作为验证依据:
- deployment_metadata_enriches_context_without_pricing:部署名
production-chat、底层gpt-5,解析出上下文窗口400_000,且input_token_cost/output_token_cost均为None; - gpt_5_6_sol_uses_its_full_context_window:
gpt-5.6-sol的上下文窗口为1_050_000且标记为推理模型; - custom_deployment_context_uses_underlying_model:通过
get_context_limit("production-chat")得到的 400K 窗口完全来自底层modelName的目录查表; - caller_override_precedes_deployment_metadata:调用方显式传入
Some(64_000)时直接返回 64K,且完全不会发起部署发现请求——即覆盖值优先于元数据。
关于定价:Azure 的计费取决于区域、SKU、合同与部署类型,deployments API 不提供可靠的每 token 价格,因此该提供商不会为发现的部署附加任何价格信息(对应上面input_token_cost: None等字段)。
六、关键实战细节:自定义部署名的"别名保留"
这是该提供商最有价值的行为之一:你在会话中使用的模型名始终是你的部署名(别名),goose 不会把它重写成底层模型名;底层模型名仅用于内部的能力判断(上下文窗口、max_tokens、是否推理模型等)。
以 Project 端点的stream实现为例(crates/goose-providers/src/azure_foundry.rs#L534-L609):
wire_model:发往 Azure 的请求体中的model字段,即部署别名;capability_model:取自部署元数据的modelName,用于套用 canonical 能力上限;- 三个路由分支都同时携带两者:
stream_for_model(&capability_config, &wire_model, capability_model, ...)。
测试 custom_openai_deployment_uses_alias_and_underlying_capabilities 验证了部署名production-chat(底层gpt-5):请求确实命中/openai/v1/responses,且请求体model == "production-chat"。suffixed_deployment_without_metadata_is_preserved_on_the_wire 则验证了另一种边缘场景:部署发现返回空列表时,名为gpt-5-high的别名既保留在线上报文中,又通过名字前缀解析出 400K 上下文——即"无元数据时按可识别名字前缀降级"的完整闭环。
对 Anthropic 面,anthropic_alias_uses_underlying_output_limit_and_preserves_override 展示了 max_tokens 的解析:部署claude-prod(底层claude-sonnet-4-6)默认输出上限取底层模型的 canonical 值128_000,而用户显式设置max_tokens: 12_345时则原样透传。
此外,在会话恢复(session resume)路径上,deserialize_session_model_config 对azure_foundry做了特殊处理:恢复model_name与request_params(例如thinking_effort),确保带后缀的部署别名(如gpt-5-high)在跨会话恢复后不被规范化改写;crates/goose/src/model_config.rs#L42-L48 也对该提供商跳过了常规的 canonical 上限套用以保持部署别名原样。
七、排障手册
401 / 403
- 确认 API Key 属于你所配置的端点;
- 使用 Entra 认证时,重新执行
az login,并确认你的身份对该 Foundry 项目有访问权限; - 不要混用:Project 端点的 Key 不能用于 MaaS 端点,反之亦然(两者的资源标识与认证头策略都不同,见上文第三节的
api-key/Bearer差异)。
另外,401 也可能出在部署发现接口上:fetch_supported_models对 Project 端点会把发现接口的认证失败原样向上抛出(测试 project_inventory_failure_is_propagated),表现为模型列表拉取直接失败——此时先检查端点 Key 是否有效。
列不出部署
- 确认端点 URL 中包含
/api/projects/<project>后缀(缺少它会让你落到 Resource 端点分支,从而返回内置的已知模型清单而非项目部署列表); - 确认项目中确实存在模型部署;
- 如果项目使用了非默认部署发现 API 版本,设置
AZURE_FOUNDRY_API_VERSION(注意该参数只影响GET /deployments的查询,不影响推理请求)。
自定义部署名路由到了错误的协议
刷新提供商的模型列表,让 goose 重新拉取部署元数据(元数据缓存 60 秒过期,且推理路由用途在拉取失败后 TTL 内会重试)。没有部署元数据时,路由只能依据可识别的模型名前缀——所以给部署起gpt-5*/o\d+/claude-*之外的自定义名字时,务必确保发现接口可用。
八、小结:一张表看清请求走向
把上面各节的规则合并,任一goose session请求的实际走向如下:
| 场景 | 请求路径(相对端点) | 请求体model字段 | 协议选择依据 |
|---|---|---|---|
Project + OpenAI 发布者 +gpt-5*/o 系列 | <endpoint>/openai/v1/responses | 部署别名 | modelPublisher+ 底层模型名 |
| Project + Anthropic 发布者 | <hub>/anthropic/v1/messages | 部署别名 | modelPublisher |
| Project + 其他(含老版 OpenAI、伙伴模型) | <endpoint>/openai/v1/chat/completions | 部署别名 | modelPublisher |
| Resource(无部署发现) | 与 Project 相同的三个接口 | 模型/部署名 | 仅模型名前缀 |
| MaaS | <endpoint>/v1/chat/completions | AZURE_FOUNDRY_MODEL绑定的模型 | 固定 Chat Completions |
理解这套"别名保留、能力按底层模型解析、协议按发布者路由"的设计后,你可以在 Foundry 中随意重命名部署(例如按环境区分的production-chat、gpt-5-high),而不必改动 goose 侧的任何模型声明;上下文窗口、输出上限等能力仍会正确解析。相关实现集中在 crates/goose-providers/src/azure_foundry.rs(端点识别、路由、部署缓存与全套测试)、crates/goose/src/providers/azure_foundry_def.rs(环境变量装配与认证头策略)和 crates/goose/src/providers/azureauth.rs(凭证优先级与令牌获取)。
【免费下载链接】goosean open source, extensible AI agent that goes beyond code suggestions - install, execute, edit, and test with any LLM项目地址: https://gitcode.com/GitHub_Trending/goose3/goose
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考