第 47 章
Claude Code SDK 模式:--print / JSON output / 程序化调用与自动化集成
第四十七章:Claude Code SDK:以编程方式驱动 AI 编程 Agent
47.1 SDK 的定位:嵌入你的应用程序
Claude Code 本身是一个命令行工具,但 Anthropic 提供了 @anthropic-ai/claude-code npm 包,让你可以在自己的 Node.js 或 TypeScript 应用程序中以编程方式调用 Claude Code 的全部功能。
这解锁了一类全新的使用场景:将 AI 编程 Agent 嵌入到现有工具、平台和自动化系统中。
使用 SDK 你可以:
- 在 Web 应用中提供"AI 代码助手"功能,用户上传代码,AI 分析并返回改进建议
- 构建批量代码处理流水线(例如:批量为遗留代码添加类型注解)
- 创建自定义的 AI 编程工作流,集成到你的内部开发工具中
- 构建 Claude Code 的 GUI 前端
与直接调用 Anthropic Messages API 相比,Claude Code SDK 的核心优势是:它内置了完整的工具调用能力(读文件、写文件、运行命令)和 Agent 编排逻辑,你不需要从零开始构建这些基础设施。
47.2 安装与基础配置
# 安装 SDK
npm install @anthropic-ai/claude-code
# 或使用 pnpm
pnpm add @anthropic-ai/claude-code
SDK 需要 Node.js 18+,并且需要系统中安装有 Claude Code CLI(SDK 在底层调用 CLI):
# 确保 Claude Code CLI 已安装
npm install -g @anthropic-ai/claude-code
# 验证安装
claude --version
设置 API 密钥:
# 方式一:环境变量(推荐)
export ANTHROPIC_API_KEY="your-api-key-here"
# 方式二:在代码中设置(不推荐在生产环境使用)
process.env.ANTHROPIC_API_KEY = "your-api-key-here";
47.3 ClaudeCode 类:核心 API
SDK 的核心是 ClaudeCode 类,它提供了与 Claude Code 交互的主要接口:
import { ClaudeCode } from '@anthropic-ai/claude-code';
// 创建实例
const claude = new ClaudeCode({
// 可选配置
apiKey: process.env.ANTHROPIC_API_KEY, // 默认从环境变量读取
model: 'claude-opus-4-5', // 使用的模型
maxTurns: 10, // 最大对话轮次
cwd: '/path/to/project', // 工作目录
});
基础对话:query 方法
最基础的使用方式是 query 方法,它发送一个提示词并返回完整响应:
import { ClaudeCode } from '@anthropic-ai/claude-code';
async function analyzeCode() {
const claude = new ClaudeCode({
cwd: '/path/to/my-project',
});
const result = await claude.query(
'请分析 src/utils/date.ts 文件,找出潜在的 bug 和改进点'
);
console.log(result.response);
console.log(`使用了 ${result.usage.inputTokens} 个输入 token`);
console.log(`使用了 ${result.usage.outputTokens} 个输出 token`);
}
analyzeCode().catch(console.error);
流式响应:stream 方法
对于长时间运行的任务,使用 stream 方法获取实时流式输出:
import { ClaudeCode } from '@anthropic-ai/claude-code';
async function streamedQuery() {
const claude = new ClaudeCode({
cwd: '/path/to/project',
});
const stream = await claude.stream(
'重构 src/api/ 目录下的所有文件,统一错误处理方式'
);
// 监听不同类型的事件
for await (const event of stream) {
switch (event.type) {
case 'text':
// Claude 的文本输出
process.stdout.write(event.content);
break;
case 'tool_use':
// Claude 正在使用工具(读/写文件等)
console.log(`\n[工具调用] ${event.tool}: ${event.input}`);
break;
case 'tool_result':
// 工具执行结果
console.log(`[工具结果] ${event.result.substring(0, 100)}...`);
break;
case 'done':
// 任务完成
console.log('\n\n任务完成');
console.log(`总共使用 ${event.usage.totalTokens} tokens`);
break;
}
}
}
47.4 完整示例:批量代码迁移工具
以下是一个完整的实际应用示例:批量将 JavaScript 文件迁移到 TypeScript。
// scripts/migrate-to-typescript.ts
import { ClaudeCode } from '@anthropic-ai/claude-code';
import * as fs from 'fs';
import * as path from 'path';
import * as readline from 'readline';
interface MigrationResult {
file: string;
success: boolean;
tokensUsed: number;
error?: string;
}
async function migrateFile(
claude: ClaudeCode,
filePath: string
): Promise<MigrationResult> {
console.log(`\n迁移文件: ${filePath}`);
try {
const result = await claude.query(`
将以下 JavaScript 文件迁移到 TypeScript:
文件路径:${filePath}
要求:
1. 读取文件内容
2. 添加 TypeScript 类型注解(推断 + 显式声明)
3. 将文件重命名为 .ts(保留 .js 作为备份)
4. 确保没有 TypeScript 编译错误
5. 不要修改任何逻辑
`);
return {
file: filePath,
success: true,
tokensUsed: result.usage.totalTokens,
};
} catch (error) {
return {
file: filePath,
success: false,
tokensUsed: 0,
error: error instanceof Error ? error.message : String(error),
};
}
}
async function batchMigrate(projectDir: string, targetFiles: string[]) {
console.log(`\n开始批量迁移 ${targetFiles.length} 个文件...`);
const claude = new ClaudeCode({
cwd: projectDir,
maxTurns: 15, // 迁移单个文件可能需要多轮
});
const results: MigrationResult[] = [];
let totalTokens = 0;
for (let i = 0; i < targetFiles.length; i++) {
const file = targetFiles[i];
console.log(`\n进度: ${i + 1}/${targetFiles.length}`);
const result = await migrateFile(claude, file);
results.push(result);
totalTokens += result.tokensUsed;
if (result.success) {
console.log(`✓ 成功迁移: ${file}`);
} else {
console.log(`✗ 迁移失败: ${file}`);
console.log(` 原因: ${result.error}`);
}
// 避免速率限制:每个文件后等待 1 秒
if (i < targetFiles.length - 1) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
// 生成迁移报告
const successful = results.filter(r => r.success).length;
const failed = results.filter(r => !r.success).length;
console.log('\n========== 迁移报告 ==========');
console.log(`成功: ${successful}/${targetFiles.length}`);
console.log(`失败: ${failed}/${targetFiles.length}`);
console.log(`总计 token 使用: ${totalTokens.toLocaleString()}`);
console.log(`估计费用: $${(totalTokens * 0.000003).toFixed(4)}`);
if (failed > 0) {
console.log('\n失败的文件:');
results
.filter(r => !r.success)
.forEach(r => console.log(` - ${r.file}: ${r.error}`));
}
// 将报告写入文件
const report = {
timestamp: new Date().toISOString(),
projectDir,
results,
summary: { successful, failed, totalTokens },
};
fs.writeFileSync('migration-report.json', JSON.stringify(report, null, 2));
console.log('\n详细报告已写入 migration-report.json');
}
// 主函数
async function main() {
const projectDir = process.argv[2] || process.cwd();
// 查找所有 .js 文件(排除 node_modules 和测试文件)
const jsFiles = fs
.readdirSync(path.join(projectDir, 'src'), { recursive: true })
.filter((f): f is string => typeof f === 'string')
.filter(f => f.endsWith('.js') && !f.includes('.test.'))
.map(f => `src/${f}`);
if (jsFiles.length === 0) {
console.log('没有找到需要迁移的 .js 文件');
return;
}
console.log(`找到 ${jsFiles.length} 个文件:`);
jsFiles.forEach(f => console.log(` - ${f}`));
// 预估费用
const estimatedTokens = jsFiles.length * 2000; // 粗略估计
const estimatedCost = (estimatedTokens * 0.000003).toFixed(4);
console.log(`\n预估 token 消耗: ~${estimatedTokens.toLocaleString()}`);
console.log(`预估费用: ~$${estimatedCost}`);
// 确认执行
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const answer = await new Promise<string>(resolve =>
rl.question('\n是否继续?(y/n) ', resolve)
);
rl.close();
if (answer.toLowerCase() !== 'y') {
console.log('已取消');
return;
}
await batchMigrate(projectDir, jsFiles);
}
main().catch(console.error);
47.5 高级功能:会话管理与上下文复用
SDK 支持多轮对话,可以维护上下文状态:
import { ClaudeCode } from '@anthropic-ai/claude-code';
async function multiTurnSession() {
const claude = new ClaudeCode({
cwd: '/path/to/project',
});
// 第一轮:分析代码库
console.log('第一轮:分析代码库...');
const analysis = await claude.query(
'分析 src/ 目录的整体架构,识别主要模块和它们之间的依赖关系'
);
console.log('架构分析完成');
// 第二轮:基于分析提出改进方案(继承上一轮的上下文)
console.log('\n第二轮:提出改进方案...');
const improvements = await claude.query(
'基于刚才的分析,提出 3 个最重要的架构改进建议,并估计每个改进的工作量'
);
console.log(improvements.response);
// 第三轮:实施第一个改进
console.log('\n第三轮:实施改进...');
const implementation = await claude.query(
'开始实施第一个改进建议,先写测试,再实现'
);
}
管理会话 ID
如果需要跨进程恢复会话,可以保存和恢复会话 ID:
import { ClaudeCode } from '@anthropic-ai/claude-code';
import * as fs from 'fs';
const SESSION_FILE = '.claude-session-id';
async function continueSession() {
const claude = new ClaudeCode({ cwd: process.cwd() });
// 尝试恢复上次的会话
let sessionId: string | undefined;
if (fs.existsSync(SESSION_FILE)) {
sessionId = fs.readFileSync(SESSION_FILE, 'utf8').trim();
console.log(`恢复会话: ${sessionId}`);
}
const result = await claude.query(
'继续上次未完成的重构任务',
{ sessionId } // 传入 session ID 恢复上下文
);
// 保存新的会话 ID
if (result.sessionId) {
fs.writeFileSync(SESSION_FILE, result.sessionId);
}
console.log(result.response);
}
47.6 工具权限控制
在生产应用中,你可能需要限制 Claude 可以使用的工具:
import { ClaudeCode } from '@anthropic-ai/claude-code';
// 只读模式:只允许读取文件,不允许修改
const readOnlyClaude = new ClaudeCode({
cwd: '/path/to/project',
allowedTools: ['Read', 'Grep', 'Glob'], // 只允许这些工具
});
// 分析模式:允许读取和运行只读命令
const analysisClaude = new ClaudeCode({
cwd: '/path/to/project',
allowedTools: ['Read', 'Grep', 'Glob', 'Bash'],
// 还可以设置 Bash 工具的允许命令列表
bashAllowedCommands: ['npm test', 'tsc --noEmit', 'eslint'],
});
// 完整权限(默认)
const fullClaude = new ClaudeCode({
cwd: '/path/to/project',
// 不设置 allowedTools,则默认使用所有工具
});
47.7 错误处理与重试策略
import { ClaudeCode, ClaudeCodeError, RateLimitError } from '@anthropic-ai/claude-code';
async function robustQuery(
claude: ClaudeCode,
prompt: string,
maxRetries = 3
): Promise<string> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const result = await claude.query(prompt);
return result.response;
} catch (error) {
if (error instanceof RateLimitError) {
// 速率限制:等待后重试
const waitTime = Math.pow(2, attempt) * 1000; // 指数退避
console.log(`速率限制,等待 ${waitTime/1000}s 后重试 (${attempt}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, waitTime));
continue;
}
if (error instanceof ClaudeCodeError) {
// Claude Code 特定错误
console.error(`Claude Code 错误: ${error.message}`);
if (error.code === 'CONTEXT_TOO_LONG') {
// 上下文太长,需要压缩
console.log('上下文过长,尝试压缩...');
// 可以在这里调用 /compact 或减少输入
}
throw error; // 非速率限制错误,直接抛出
}
throw error; // 未知错误,直接抛出
}
}
throw new Error(`经过 ${maxRetries} 次重试后仍然失败`);
}
47.8 构建 Web 服务:Claude Code API Server
你可以将 Claude Code SDK 包装成一个 HTTP API,为 Web 前端提供 AI 编程功能:
// server.ts
import express from 'express';
import { ClaudeCode } from '@anthropic-ai/claude-code';
import * as path from 'path';
const app = express();
app.use(express.json());
// POST /analyze — 分析代码
app.post('/analyze', async (req, res) => {
const { projectPath, prompt } = req.body;
if (!projectPath || !prompt) {
return res.status(400).json({ error: 'projectPath and prompt are required' });
}
// 安全检查:确保路径在允许的范围内
const allowedBasePath = '/workspace/projects';
const resolvedPath = path.resolve(projectPath);
if (!resolvedPath.startsWith(allowedBasePath)) {
return res.status(403).json({ error: 'Access denied' });
}
try {
const claude = new ClaudeCode({
cwd: resolvedPath,
allowedTools: ['Read', 'Grep', 'Glob'], // 只读模式
});
const result = await claude.query(prompt);
res.json({
response: result.response,
usage: result.usage,
});
} catch (error) {
console.error('Analysis error:', error);
res.status(500).json({
error: error instanceof Error ? error.message : 'Unknown error',
});
}
});
// POST /stream — 流式分析
app.post('/stream', async (req, res) => {
const { projectPath, prompt } = req.body;
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
try {
const claude = new ClaudeCode({ cwd: projectPath });
const stream = await claude.stream(prompt);
for await (const event of stream) {
// 发送 Server-Sent Events
res.write(`data: ${JSON.stringify(event)}\n\n`);
}
res.write('data: [DONE]\n\n');
res.end();
} catch (error) {
res.write(`data: ${JSON.stringify({ type: 'error', message: String(error) })}\n\n`);
res.end();
}
});
app.listen(3000, () => {
console.log('Claude Code API Server running on port 3000');
});
47.9 SDK 与直接 API 调用的选择
| 场景 | 推荐方案 |
|---|---|
| 需要文件系统操作(读写代码) | ClaudeCode SDK |
| 只需要文本生成(聊天、摘要) | Anthropic Messages API |
| 需要运行 Shell 命令 | ClaudeCode SDK |
| 需要精细控制工具调用 | 手动实现 Tool Use API |
| 需要多 Agent 协作 | ClaudeCode SDK(利用 Agent 工具) |
| 简单的单次文本问答 | Anthropic Messages API(更轻量) |
小结
Claude Code SDK 将 AI 编程 Agent 的能力从命令行工具扩展到了可编程接口。
关键要点:
@anthropic-ai/claude-codenpm 包提供ClaudeCode类,可在 Node.js/TypeScript 应用中编程调用 Claude Codequery方法适合单次查询,stream方法适合长时间任务的实时输出- SDK 内置工具调用(读文件、写文件、运行命令)和 Agent 编排,无需从零构建
- 通过
allowedTools配置限制 Claude 可用的工具,实现权限控制 - 使用会话 ID 可以跨请求维护对话上下文
- 适合构建批量代码处理流水线、Web 版 AI 编程助手、内部自动化工具