Skip to content
Leo的技术分享
Go back

通过 OpenRouter 使用 Claude Agent SDK

概述

Claude Agent SDK 是 Anthropic 官方推出的 Python/TypeScript 库,让开发者能够以编程方式构建可自主执行任务的 AI Agent。它底层复用了 Claude Code CLI 的全部能力,包括工具调用、多轮对话循环、上下文管理等,开发者无需自己实现工具执行逻辑,Agent 可以开箱即用地读取文件、运行命令、搜索代码库、编辑代码等。

与直接调用 Anthropic Messages API 相比,Agent SDK 的核心差异在于:Messages API 需要开发者自己实现工具调用循环(发请求 -> 解析工具调用 -> 执行工具 -> 把结果塞回去 -> 再发请求),而 Agent SDK 把这个循环内置了,开发者只需要描述任务,Agent 自主决定调用哪些工具、何时停止。

本文介绍如何通过 OpenRouter 路由 API 请求来使用 Claude Agent SDK,从而在不直接使用 Anthropic API Key 的情况下,接入 Anthropic 及其他厂商的模型。

环境准备

1. 安装依赖

uv add claude-agent-sdk python-dotenv

或使用 pip:

pip install claude-agent-sdk python-dotenv

Claude Code CLI 已自动打包在 claude-agent-sdk 内,无需单独安装。

2. 获取 OpenRouter API Key

前往 OpenRouter 注册账号,在控制台创建 API Key。

3. 配置 .env

在项目根目录创建 .env 文件:

# OpenRouter 模型标识符,格式为 provider/model-name
MODEL=deepseek/deepseek-v3.2

# Anthropic SDK 通过以下三个变量识别 API 地址和鉴权信息
# 将 ANTHROPIC_BASE_URL 指向 OpenRouter,ANTHROPIC_AUTH_TOKEN 填入 OpenRouter API Key
# ANTHROPIC_API_KEY 必须存在但设为空字符串,否则 SDK 会尝试直连 Anthropic
ANTHROPIC_BASE_URL=https://openrouter.ai/api
ANTHROPIC_AUTH_TOKEN=sk-or-v1-your-key-here
ANTHROPIC_API_KEY=

# 禁止 Claude Code CLI 发送 Anthropic 专有 beta header(如 context-management),OpenRouter 不支持这些 header
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1
# 禁用 prompt 缓存,OpenRouter 部分模型不支持 Anthropic 缓存机制
DISABLE_PROMPT_CACHING=1
# 限制每次模型回复的最大 token 数
CLAUDE_CODE_MAX_OUTPUT_TOKENS=4096

代码实现

完整代码如下:

import asyncio
import os
import sys
from dotenv import load_dotenv

from claude_agent_sdk import (
    AssistantMessage,
    ClaudeAgentOptions,
    PermissionResultAllow,
    TextBlock,
    ToolPermissionContext,
    query,
)

load_dotenv()


def stderr_printer(line: str) -> None:
    print(line, file=sys.stderr)


async def allow_all_tools(
    tool_name: str, tool_input: dict, context: ToolPermissionContext
) -> PermissionResultAllow:
    return PermissionResultAllow()


async def prompts():
    yield {
        "type": "user",
        "message": {
            "role": "user",
            "content": "请先用 ls 查看当前目录,然后用 Read 读取 ./pyproject.toml 并简要分析。",
        },
    }


async def main() -> None:
    async for message in query(
        prompt=prompts(),
        options=ClaudeAgentOptions(
            system_prompt=(
                "You are running in /Users/leo/Code/ai/claude/coding-agent. "
                "Use paths relative to the current working directory."
            ),
            allowed_tools=["Read", "Edit", "Bash"],
            model=os.environ.get("MODEL"),
            stderr=stderr_printer,
            can_use_tool=allow_all_tools,
            cwd=os.getcwd(),
            add_dirs=[os.getcwd()],
            extra_args={},
        ),
    ):
        print(f"[{type(message).__name__}]")
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, TextBlock) and block.text:
                    print(block.text)


if __name__ == "__main__":
    asyncio.run(main())

下面逐段讲解。

导入与初始化

from claude_agent_sdk import (
    AssistantMessage,      # agent 的文字回复消息
    ClaudeAgentOptions,    # agent 启动选项
    PermissionResultAllow, # 工具调用权限:允许
    TextBlock,             # 消息内容块:纯文本
    ToolPermissionContext, # 工具调用的上下文信息
    query,                 # 驱动 agent 对话循环的核心函数
)

load_dotenv()

load_dotenv() 在程序启动时读取 .env 文件,将其中的变量注入 os.environ。由于 SDK 底层的 Claude Code CLI 子进程会继承父进程的环境变量,.env 里配置的 ANTHROPIC_BASE_URLANTHROPIC_AUTH_TOKEN 等变量会自动传递给 CLI,无需额外处理。

工具调用权限回调

async def allow_all_tools(
    tool_name: str, tool_input: dict, context: ToolPermissionContext
) -> PermissionResultAllow:
    return PermissionResultAllow()

每次 agent 想调用一个工具前,SDK 都会调用 can_use_tool 回调询问是否允许。这里直接返回 PermissionResultAllow() 表示无条件放行。

如果需要限制某些工具,可以按 tool_name 判断:

async def allow_all_tools(tool_name, tool_input, context):
    if tool_name == "Bash":
        return PermissionResultDeny(reason="不允许执行 shell 命令")
    return PermissionResultAllow()

注入用户消息

async def prompts():
    yield {
        "type": "user",
        "message": {"role": "user", "content": "..."},
    }

prompts 是一个异步生成器,用于向 agent 注入用户消息。单轮对话直接传字符串也可以:

await query(prompt="请分析当前项目结构", options=...)

使用异步生成器的优势在于支持多轮对话,可以在 agent 回复后继续 yield 下一条消息,甚至根据 agent 的回复动态决定下一步指令。

启动 agent 与处理消息流

async for message in query(
    prompt=prompts(),
    options=ClaudeAgentOptions(
        system_prompt="...",
        allowed_tools=["Read", "Edit", "Bash"],
        model=os.environ.get("MODEL"),
        stderr=stderr_printer,
        can_use_tool=allow_all_tools,
        cwd=os.getcwd(),
        add_dirs=[os.getcwd()],
    ),
):

query() 是核心函数,返回一个异步生成器。agent 每完成一个步骤就 yield 一条消息,不会等全部完成再返回,因此输出是流式的。主要参数说明:

参数说明
system_prompt给 agent 的全局指令,限定行为范围
allowed_tools工具白名单,不在列表中的工具会被拒绝
modelOpenRouter 模型标识符,格式为 provider/model-name
stderr接收 CLI 进程 stderr 输出的回调,用于调试
can_use_tool工具调用权限回调
cwdagent 的工作目录
add_dirs额外允许 agent 访问的目录列表

提取文本输出

if isinstance(message, AssistantMessage):
    for block in message.content:
        if isinstance(block, TextBlock) and block.text:
            print(block.text)

query() 会 yield 多种类型的消息,完整的消息流大致如下:

SystemMessage     # CLI 启动后发出的初始化信息(工作目录、工具列表、模型等)
AssistantMessage  # agent 的文字回复(可能穿插多条)
ToolUseMessage    # agent 调用工具及工具返回结果
AssistantMessage  # 最终总结回复
ResultMessage     # 对话结束标志

AssistantMessage.content 是内容块列表,类型有 TextBlock(文本)和 ThinkingBlock(推理模型的思维链)。这里只打印 TextBlock,跳过其他类型。

运行 python openrouter_agent.py 后,输出大致如下:

[SystemMessage]
[AssistantMessage]
[AssistantMessage]
根据`ls`和`pyproject.toml`的分析:

**目录结构**:
- 项目使用 `uv` 管理依赖(存在 `uv.lock`)
- 包含两个主脚本:`openrouter_agent.py` 和 `openrouter_anthropic.py`
- 有 `scripts/` 目录存放环境变量脚本

**pyproject.toml 分析**:
- 项目名称:`coding-agent`,版本 `0.1.0`
- 主要依赖:`anthropic`、`claude-agent-sdk`、`python-dotenv`
[ResultMessage]

其中 [SystemMessage][AssistantMessage] 等是我们打印的消息类型标记,实际回复内容由模型生成,每次运行可能略有不同。

参考资料

  1. https://platform.claude.com/docs/en/agent-sdk/overview
  2. https://github.com/anthropics/claude-agent-sdk-python
  3. https://openrouter.ai/docs/guides/community/anthropic-agent-sdk
  4. https://openrouter.ai/deepseek/deepseek-v3.2
  5. https://github.com/anthropics/claude-code/issues/11960

Share this post on:

评论


Next Post
Antigravity Skills 使用指南