用 Plano 的 Bearer 授权能力接入 Spotify API:Agent 应用调用第三方接口的实战指南
【免费下载链接】planoPlano is an AI-native proxy server and data plane for agentic apps. Smart LLM routing, observability, agent orchestration, and guardrails so you stay focused on your agents core logic.项目地址: https://gitcode.com/GitHub_Trending/ar/plano
在构建 Agent 应用时,接入带鉴权的第三方 API 往往需要额外编写令牌管理、请求头注入等样板代码。本指南以 Plano 开源仓库中的 Spotify Bearer 授权演示为例,讲解如何通过 Plano 的 Prompt Target 与http_headers配置,让 Agent 在零业务代码的前提下安全调用 Spotify Web API 获取新专辑与艺人热门单曲,并支持连续多轮的自然语言交互。读完本文,你将掌握 Bearer 令牌注入的配置原理、完整参数说明以及从启动到联调的端到端操作流程。
演示场景概述
该演示位于 demos/integrations/spotify_bearer_auth,目标非常聚焦:让用户通过自然语言与两个 Spotify 官方接口交互:
/v1/browse/new-releases:获取 Spotify 精选的最新专辑列表(对应“Browse”标签页场景);/v1/artists/{artist_id}/top-tracks:获取指定艺人的热门单曲列表。
典型的交互流程是用户先问“Show me the latest releases in the US”,紧接着追问“Show me top tracks from Taylor Swift”——后者无需用户重新描述任何上下文,Plano 会自动完成意图识别、参数抽取与路径拼接。从实现角度看,整个过程中不需要你编写任何后端代理代码:授权头、路径参数、HTTP 调用、结果格式化全部由 Plano 配置驱动完成。
核心原理:Prompt Target + 请求头注入
Bearer 授权的本质,是在每一次对上游 API 的 HTTP 请求中携带Authorization: Bearer <token>请求头。在 Plano 中,这个能力被收敛为 Prompt Target 的endpoint.http_headers配置项,配合环境变量替换机制实现“密钥不出配置文件、令牌不落盘”的安全实践。
源码层面的证据
在 crates/common/src/configuration.rs 中,EndpointDetails结构体明确定义了端点所需的三个核心字段:
pub struct EndpointDetails { pub name: String, pub path: Option<String>, #[serde(rename = "http_method")] pub method: Option<HttpMethod>, pub http_headers: Option<HashMap<String, String>>, }而PromptTarget(对应配置文件里的prompt_targets数组项)则在其上叠加了parameters、system_prompt、auto_llm_dispatch_on_response等属性,其中parameters会被转换为 OpenAI 兼容的 Function Calling 工具定义(见同一文件中的impl From<&PromptTarget> for ChatCompletionTool),这正是“LLM 抽取参数 → 组装 HTTP 请求”的桥梁。
请求头真正注入上游请求的位置在 crates/prompt_gateway/src/stream_context.rs:Plano 先构造包含请求 ID、traceparent 等内部头的默认请求头集合,然后读取endpoint_details.http_headers并将其中的键值逐一覆盖/追加到实际发出的请求中:
// override http headers that are set in the prompt target let http_headers = endpoint_details.http_headers.clone().unwrap_or_default(); for (key, value) in http_headers.iter() { headers.insert(key.as_str(), value.as_str()); }这意味着Authorization: "Bearer $SPOTIFY_CLIENT_KEY"会在请求发出前完成环境变量替换,并以标准 HTTP 头的形式透传给 Spotify。
环境变量替换机制
根据 skills/rules/config-secrets.md 中的约定,Plano 支持在配置值中使用$VAR_NAME语法引用环境变量,该能力适用于access_key、状态存储的connection_string,以及 Prompt Target 与端点中的http_headers。planoai up启动时会自动加载当前目录下的.env文件(也接受 shell 中直接导出的环境变量);若配置引用的变量缺失,启动会失败并明确列出缺失的键名。因此:
- 配置文件里只出现
$SPOTIFY_CLIENT_KEY占位符,真正的令牌保存在.env中; .env应加入.gitignore,避免密钥进入版本库;- 配置校验器 config/plano_config_schema.yaml 将
http_headers定义为additionalProperties: string的对象,即任意自定义请求头(Authorization、X-Api-Key 等)均可按此模式声明。
配置详解:逐段拆解 config.yaml
演示的完整配置位于 demos/integrations/spotify_bearer_auth/config.yaml,下面按区块讲解每个字段的作用与可调项。
版本与监听器
version: v0.3.0 listeners: - type: prompt name: prompt_listener port: 10000 overrides: optimize_context_window: trueversion:声明配置结构版本,Plano 会据此选择配置解析与行为路径;listeners:声明对外服务的监听器。type: prompt表示 OpenAI 兼容的 Prompt 网关(Chat Completions 协议),port: 10000即接入端口。schema(config/plano_config_schema.yaml)中还支持model、agent类型、address、timeout、router、max_retries等扩展项;overrides.optimize_context_window:开启上下文窗口优化,在多轮会话中控制上下文增长(schema 定义见 config/plano_config_schema.yaml)。
上游端点声明
endpoints: spotify: endpoint: api.spotify.com protocol: httpsendpoints是一组命名上游集群(键名即后续prompt_targets.endpoint.name的引用名)。此处spotify集群指向api.spotify.com且强制https协议。schema(config/plano_config_schema.yaml)允许为每个端点配置connect_timeout、http_host、prefix_affinity(自托管多副本后端按前缀一致性哈希路由)等更多属性。
结果格式化:system_prompt
system_prompt: | I have the following JSON data representing a list of albums from Spotify: ... Please convert this JSON into Markdown with the following layout for each album: ...该区块并非普通指令,而是针对回调响应的后处理提示:Spotify 返回的 JSON 会被注入该提示,指示 LLM 将每条专辑渲染为「专辑封面图 → 标题/艺人/发行日期 → Spotify 收听链接」的 Markdown 卡片,并要求输出合法的 Markdown。这正是截图中聊天界面呈现结构化专辑卡片的原因——数据格式化逻辑同样由配置完成,而非业务代码。
模型提供商
model_providers: - access_key: $OPENAI_API_KEY model: openai/gpt-4o default: true声明承载意图理解、参数抽取与结果格式化的 LLM:access_key通过环境变量注入 OpenAI 密钥,default: true标记默认模型。模型名使用openai/gpt-4o的“provider/model”格式,Plano 的模型路由层支持按此标识进行分发与替换。
Prompt Targets:授权与参数抽取的核心
prompt_targets: - name: get_new_releases description: Get a list of new album releases featured in Spotify (shown, for example, on a Spotify player's "Browse" tab). parameters: - name: country description: the country where the album is released required: true type: str in_path: true - name: limit type: integer description: The maximum number of results to return default: "5" endpoint: name: spotify path: /v1/browse/new-releases http_headers: Authorization: "Bearer $SPOTIFY_CLIENT_KEY" - name: get_artist_top_tracks description: Get information about an artist's top tracks parameters: - name: artist_id description: The ID of the artist. required: true type: str in_path: true endpoint: name: spotify path: /v1/artists/{artist_id}/top-tracks http_headers: Authorization: "Bearer $SPOTIFY_CLIENT_KEY"两个 Prompt Target 完整展示了该能力的关键要点:
| 元素 | 说明 | 本演示中的取值 |
|---|---|---|
name | 目标唯一标识,同时作为 LLM 可见的工具名 | get_new_releases/get_artist_top_tracks |
description | 意图匹配与工具选择的依据,描述越具体命中越准 | “Get a list of new album releases featured in Spotify…” |
parameters[].name | 参数名,须与路径模板中的占位符对应 | country、artist_id |
parameters[].type | 数据类型(str、integer 等,见下节) | str/integer |
parameters[].required | 是否必填,缺失时 LLM 会向用户追问 | true |
parameters[].in_path | 参数是否嵌入 URL 路径 | true(拼接进 path) |
parameters[].default | 用户未提及时使用的默认值 | limit: "5" |
endpoint.name | 引用endpoints中声明的上游 | spotify |
endpoint.path | 请求路径,{param}语法与in_path: true参数按名替换 | /v1/browse/new-releases、/v1/artists/{artist_id}/top-tracks |
endpoint.http_headers | 随请求注入的静态请求头(支持$ENV替换) | Authorization: "Bearer $SPOTIFY_CLIENT_KEY" |
参数属性在 docs/source/concepts/prompt_target.rst 中有完整官方说明:type支持int、str、float、bool、list、set、dict、tuple;in_path决定参数是拼入 URL 路径还是作为查询串;此外还支持enum(允许值列表)、format(如日期2019-12-31)、items(复合类型元素声明)。schema 层面 config/plano_config_schema.yaml 要求每个 target 至少包含name与description,端点至少包含name与path,并限定http_method仅可为GET/POST。
值得注意的细节:artist_id与country均声明in_path: true,说明 LLM 抽取出的值会直接替换路径模板{artist_id}/{country};而limit未声明in_path,会被作为查询参数附加。演示中 Spotify 的新专辑接口把country作为路径段(/v1/browse/{country}/new-releases变体),这里则直接拼接在/v1/browse/new-releases之后,具体以你的目标 API 路径设计为准。
可观测性
tracing: random_sampling: 100random_sampling: 100表示以 100% 概率对请求采样并上报链路追踪(取值范围 0–100)。schema(config/plano_config_schema.yaml)还支持trace_arch_internal、opentracing_grpc_endpoint、span_attributes(自定义静态属性与头前缀)以及exporters(如 PostHog)等高级配置。
启动演示:从密钥到第一个问题
前置准备
按仓库根目录 README.md 中的 Prerequisites 安装好 Plano 运行环境(含planoaiCLI)与 Docker(可选 UI 服务需要)。
获取 Spotify 令牌
- 在 Spotify 开发者后台创建应用,获得 Client Key / Client Secret;
- 调用 Spotify 的
https://accounts.spotify.com/api/token令牌端点(用curl或类似工具),以client_credentials等流程换取访问令牌; - 该令牌即下文
.env中的SPOTIFY_CLIENT_KEY。
创建 .env 并启动
在 demos/integrations/spotify_bearer_auth 目录下创建.env:
OPENAI_API_KEY=your_openai_api_key SPOTIFY_CLIENT_KEY=your_spotify_api_token然后一键启动:
sh run_demo.sh查看 run_demo.sh 可知脚本行为:
- 若
.env不存在,且 shell 中已导出OPENAI_API_KEY,脚本会自动生成.env(不会覆盖已存在的文件); - 支持
sh run_demo.sh --with-ui附加启动 UI 服务:先用docker compose up -d拉起 AnythingLLM 与 Jaeger(Jaeger 必须先于 Plano 启动,以便抢占 OTEL 端口 4317),再执行planoai up config.yaml; sh run_demo.sh down会依次执行docker compose down与planoai down完成清理。
启动成功后,浏览器访问http://localhost:18080(AnythingLLM 的 Chat 界面,已通过 docker-compose.yaml 中的GENERIC_OPEN_AI_BASE_PATH=http://host.docker.internal:10000/v1指向 Plano 的 10000 端口监听器)。
发起对话
在聊天框输入:
show me new album releases in the USPlano 的完整处理链路如下:
- 意图匹配:LLM 依据
get_new_releases的描述识别意图,并抽取参数(country=US,limit缺省时用默认值"5"); - 参数组装:
country因in_path: true嵌入路径,构建出面向api.spotify.com的 HTTPS GET 请求; - Bearer 注入:请求发出前,
Authorization: Bearer <SPOTIFY_CLIENT_KEY>由 crates/prompt_gateway/src/stream_context.rs 注入请求头; - 结果格式化:Spotify 返回的专辑 JSON 交由
system_prompt定义的后处理流程转为 Markdown 卡片返回给用户。
继续追问:
Show me top tracks from Taylor Swift由于是两个独立 Prompt Target,用户无需重复任何上下文——Plano 会在此前对话基础上识别新意图get_artist_top_tracks,抽取artist_id(如06HL4z0CvFAxyc27GXpf02)并替换进/v1/artists/{artist_id}/top-tracks。这正是多轮 Agent 交互中“先浏览、再下钻”的典型范式。
扩展与安全建议
- 多目标与多 API:每个第三方 API 对应一个
endpoints条目,每个操作对应一个 Prompt Target,即可在一个 Plano 实例中统一接入多家服务,鉴权头各自独立配置; - 密钥生命周期:Spotify 令牌过期后仅需更新
.env并重启 Plano,业务层无感知;若令牌支持刷新,可在获取新令牌后替换SPOTIFY_CLIENT_KEY的值; - 配置即文档:
description与system_prompt是决定意图识别与输出质量的关键杠杆,建议对每个参数写清取值范围与示例,与 docs/source/concepts/prompt_target.rst 中的参数约定保持一致; - 排障入口:开启
tracing.random_sampling后可在 Jaeger(http://localhost:16686)中观察每次 Spotify 调用 span,确认请求头、路径与响应是否符合预期。
小结
通过demos/integrations/spotify_bearer_auth这一个演示,可以看到 Plano 把“第三方 API 鉴权接入”压缩成了纯配置动作:endpoints声明上游,prompt_targets声明可调用的操作、参数与http_headers,环境变量负责密钥隔离,LLM 负责意图理解与参数抽取,system_prompt负责结果美化。从 crates/common/src/configuration.rs 的数据结构到 crates/prompt_gateway/src/stream_context.rs 的请求头覆盖逻辑,再到配置 schema 的严格校验,整条链路都有源码可查、有示例可跑。理解了这一模式,你就可以将同样的手法扩展到任何需要 Bearer Token 或自定义请求头的第三方服务上,为 Agent 应用快速补齐外部数据接入能力。
【免费下载链接】planoPlano is an AI-native proxy server and data plane for agentic apps. Smart LLM routing, observability, agent orchestration, and guardrails so you stay focused on your agents core logic.项目地址: https://gitcode.com/GitHub_Trending/ar/plano
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考