Spike S1 · Claude Agent SDK 集成验证(skills + 鉴权)
状态:✅ 通过(2026-05-18)
本文档原本只覆盖”skills 加载”,spike 过程中追加了 SDK OAuth fallback 调研——两组发现一起记下。
验证目标
@anthropic-ai/claude-agent-sdk 在容器内的预期使用方式(cwd 指向 git clone 后的仓库 + settingSources + skills:"all")能否识别本仓库的 4 个 .claude/skills/openspec-*。
验证策略 · 零成本
关键发现:SDKSystemMessage 的 init 子类型携带 skills: string[] 字段——SDK spawn Claude Code 子进程时就枚举所有 discovered skills 并通过 init message 返回。
拿到 init 立即 process.exit():
- 不进 turn → 不调 Anthropic API → 零 token 消耗
- 不需要
ANTHROPIC_API_KEY(apiKeySource: 'none'也能拿到 init)
验证脚本
/tmp/openspec-spike-s1/spike.mjs(独立 npm 隔离环境,未污染主仓库依赖):
import { query } from '@anthropic-ai/claude-agent-sdk';
const REPO = '/Users/tanghehui/ExploreProject/openspec-agent-platform';
for await (const msg of query({
prompt: 'unused',
options: {
cwd: REPO,
settingSources: ['project'],
skills: 'all',
allowedTools: [],
permissionMode: 'bypassPermissions',
maxTurns: 0,
},
})) {
if (msg.type === 'system' && msg.subtype === 'init') {
const openspec = (msg.skills ?? []).filter((s) => s.startsWith('openspec-'));
console.log(`匹配 openspec-* 的 skill 数: ${openspec.length}`);
process.exit(openspec.length === 4 ? 0 : 1);
}
}实际输出
=== INIT MESSAGE ===
apiKeySource: none
model: claude-opus-4-7[1m]
claude_code_version: 2.1.143
cwd: /Users/tanghehui/ExploreProject/openspec-agent-platform
--- skills 字段 ---
total: 12
list:
- openspec-apply-change ← project skill
- openspec-explore ← project skill
- openspec-archive-change ← project skill
- openspec-propose ← project skill
- update-config ← user-level
- debug
- simplify
- batch
- fewer-permission-prompts
- loop
- schedule
- claude-api
匹配 openspec-* 的 skill 数: 4
✓ S1 通过结论
| 验证项 | 结果 |
|---|---|
SDK 在 cwd: <repo> + settingSources: ['project'] + skills: 'all' 配置下能加载仓库 skills | ✅ |
4 个 .claude/skills/openspec-* 全部被识别 | ✅ |
| SKILL.md frontmatter 格式(已离线验证 + 现已运行时验证)符合 SDK 期望 | ✅ |
| 整个 spike 零成本 | ✅ |
下一阶段(运行时实际触发 skill)
POC 实施期跑通真实容器任务时(§7 任务 12 / 16)会进入 turn,那时才能验证 Claude 实际能不能在 propose 这个 skill 已加载的前提下,按需触发 /opsx:propose ...。但根据 SDK 文档:skill 加载机制 = 描述匹配 + on-demand,frontmatter description 字段已离线验证齐全,运行时触发风险极低。
走偏的 attempt(仅作记录)
2026-05-18 第一次尝试用 claude --print --output-format stream-json --max-budget-usd 0.05 跑 spike——方向错了:
- CLI 走 OAuth(Claude.ai 订阅),与 SDK 在容器内用
ANTHROPIC_API_KEY鉴权路径不同 - 没禁用 SessionStart hooks,注入 ~40K Vercel context tokens,cache_creation 触发 $0.27 实际计费(在 $0.05 budget cap 检查前)
教训:涉及真实费用的 spike,必须先确认”零成本路径”(如 init 拿了就退)再跑。
追加调研 · SDK 鉴权 fallback 链路(2026-05-18)
触发问题
hi minimal turn 跑通后,result.total_cost_usd: 0.10 引发疑问:
- env 没设
ANTHROPIC_API_KEY(已显式delete) - init message 报告
apiKeySource: 'none' - 但 turn 跑通了 → SDK 走了某条 fallback 鉴权
需要弄清:$0.10 是真实 API token 计费、还是 Max 订阅额度池吸收。
SDK 源码层面的事实
@anthropic-ai/claude-agent-sdk@0.3.143 内嵌的鉴权 endpoint:
| Endpoint | 用途 |
|---|---|
https://platform.claude.com/oauth/authorize | OAuth 授权流程入口 |
https://platform.claude.com/v1/oauth/token | OAuth refresh |
https://api.anthropic.com/api/oauth/claude_cli/create_api_key | 关键 · OAuth → 临时 API key 换取 |
https://api.anthropic.com/api/oauth/claude_cli/roles | 角色查询 |
SDK 内嵌字符串:"find-generic-password" + "security" → SDK 用 security find-generic-password -s "Claude Code-credentials" 命令读 macOS keychain。
完整鉴权 fallback 链路
SDK query() 启动
↓
1. 检查 process.env.ANTHROPIC_API_KEY
↓ (未设)
2. 调 `security find-generic-password` 读 macOS keychain
service: "Claude Code-credentials"
↓ (找到 claudeAiOauth.accessToken)
3. 用 OAuth access token POST
/api/oauth/claude_cli/create_api_key
↓ (拿到临时 API key)
4. 用临时 API key 调 /v1/messages 真正做 inference
↓
init message 报告 apiKeySource: 'none' ← 是"user 没显式传",不是"无鉴权"”apiKeySource: ‘none’” 实测含义
ApiKeySource TypeScript 类型声明:'user' | 'project' | 'org' | 'temporary' | 'oauth' —— 不含 'none'。
但 SDK 实际运行时返回 'none' —— 类型声明与实现脱节。
正确理解:“none 表示 user 没显式提供 API key,SDK 内部用 OAuth fallback 链路自动换取了临时 key”。
$0.10 实际走哪条账户
结论:Max 订阅额度池吸收,不另扣信用卡。
实测验证(用户在 console.anthropic.com/settings/usage 查看):
- spike 前后今天 API spend 不增加
- 即便 SDK 用了临时 API key 调 inference,Anthropic 后端识别这是”OAuth 派生的 key” → reimburse 到订阅额度池
result.total_cost_usd: 0.10 是 SDK 计算的”等价 API 价格”,对订阅用户来说是参考数字,不进信用卡账单。
对 POC 架构的实际影响
| 场景 | 鉴权方式 | 合规性 |
|---|---|---|
| 本地 dev(开发者自己跑 spike / 调容器镜像) | SDK 自动复用 keychain OAuth | ✅(自用) |
| POC 容器(生产 / 多用户) | 必须注入 ANTHROPIC_API_KEY | ✅ |
| 容器复用 OAuth token | ❌ Anthropic ToS 禁止 + user-level 凭据安全风险 | ❌ |
Anthropic 官方表述(Agent SDK overview ):
“Unless previously approved, Anthropic does not allow third party developers to offer claude.ai login or rate limits for their products, including agents built on the Claude Agent SDK. Please use the API key authentication methods described in this document instead.”
对 design.md 的补充
→ D11 · 双轨鉴权策略(已写入 design.md):
- 容器内:
ANTHROPIC_API_KEYenv 严格注入 - 本地 dev:SDK 自动 fallback OAuth(无需配置)
- 镜像构建禁止读 keychain / 打包 OAuth refresh token
对 specs/task-runner/spec.md 的补充
→ 新增 Requirement: 容器内鉴权策略(已写入),定义 3 个 scenario:
- API key 注入(business 提供,
sk-ant-api*格式) - 禁止 OAuth 复用(镜像不打包 keychain 读取逻辑)
- API key 缺失或无效时快速失败(退出码 2)