<!-- title: Agent SDK实战：编程式AI代理开发 -->

# Agent SDK实战：编程式AI代理开发

## 概述

Claude Agent SDK 是 Anthropic 官方提供的编程式 AI 代理开发框架，它将 Claude Code 的完整能力——文件读写、命令执行、代码编辑、MCP 工具调用——封装为 TypeScript 和 Python SDK。本教程面向有一定编程基础的开发者，从零构建一个具备自定义工具、子代理编排和 Hook 拦截能力的生产级 AI 代理。学完可独立开发代码审查机器人、自动化运维代理、文档生成流水线等实际项目。

**适用人群**：有 TypeScript/Python 基础的后端开发者、DevOps 工程师、AI 应用开发者。

## 前置条件

| 项目 | 要求 |
|------|------|
| Node.js | v18+ |
| TypeScript | v5+ |
| Anthropic API Key | 需要有效的 API 密钥 |
| Claude Code | 已安装并配置（推荐最新版） |
| 基础知识 | 异步编程、JSON Schema、命令行操作 |

```mermaid
graph TD
    A[Agent SDK 核心架构] --> B[Agent Loop 代理循环]
    A --> C[Tool System 工具系统]
    A --> D[Hook System 钩子系统]
    A --> E[Subagent 子代理]

    B --> B1[消息发送]
    B --> B2[工具调用决策]
    B --> B3[结果处理]
    B --> B4[循环终止判断]

    C --> C1[内置工具: Read/Write/Bash]
    C --> C2[自定义工具: MCP In-Process]
    C --> C3[外部MCP服务器]

    D --> D1[PreToolUse: 工具调用前]
    D --> D2[PostToolUse: 工具调用后]
    D --> D3[Notification: 通知]

    E --> E1[Task Dispatch]
    E --> E2[Parallel Execution]
    E --> E3[Result Aggregation]
```

---

## 第一步：安装与初始化

### 1.1 创建项目

```bash
mkdir my-claude-agent && cd my-claude-agent
npm init -y
npm install @anthropic-ai/claude-agent-sdk
npm install -D typescript @types/node tsx
npx tsc --init
```

`tsconfig.json` 关键配置：

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true
  }
}
```

### 1.2 配置 API 密钥

```bash
export ANTHROPIC_API_KEY="sk-ant-xxxxx"
```

或在项目根目录创建 `.env` 文件（不要提交到版本控制）：

```
ANTHROPIC_API_KEY=sk-ant-xxxxx
```

### 1.3 验证安装

```typescript
// verify.ts
import { query } from "@anthropic-ai/claude-agent-sdk";

const result = await query({
  prompt: "回复'Agent SDK 连接成功'，不要执行其他操作",
});

console.log(result);
```

运行：

```bash
npx tsx verify.ts
```

**预期输出**：终端打印 Agent 的文本回复，包含"Agent SDK 连接成功"。

**常见问题**：

| 错误 | 原因 | 解决方案 |
|------|------|----------|
| `MODULE_NOT_FOUND` | SDK 未安装或路径错误 | 检查 `node_modules` 中是否存在包 |
| `401 Unauthorized` | API Key 无效 | 确认密钥正确且未过期 |
| `ECONNREFUSED` | 网络问题 | 检查代理设置或防火墙 |

---

## 第二步：核心 API 详解

### 2.1 `query()` —— 单次代理调用

`query()` 是 Agent SDK 的核心入口函数，它启动一个完整的代理循环：

```typescript
import { query } from "@anthropic-ai/claude-agent-sdk";

const result = await query({
  prompt: "读取当前目录的 package.json，列出所有依赖项",
  options: {
    model: "claude-sonnet-4-20250514",
    maxTurns: 10,
    allowedTools: ["Read", "Bash"],
    cwd: process.cwd(),
  },
});
```

**关键参数**：

| 参数 | 类型 | 说明 |
|------|------|------|
| `prompt` | `string` | 给代理的指令 |
| `options.model` | `string` | 模型选择，默认 `claude-sonnet-4-20250514` |
| `options.maxTurns` | `number` | 最大代理循环轮次，防止无限循环 |
| `options.allowedTools` | `string[]` | 白名单工具，未列出的工具不可用 |
| `options.cwd` | `string` | 工作目录，影响文件操作和命令执行的根路径 |
| `options.systemPrompt` | `string` | 自定义系统提示词 |
| `options.outputFormat` | `"text" \| "json"` | 输出格式 |

### 2.2 处理流式响应

对于长时间运行的任务，使用 `stream` 模式实时获取输出：

```typescript
import { query } from "@anthropic-ai/claude-agent-sdk";

const stream = query({
  prompt: "分析 src/ 目录下所有 TypeScript 文件的代码质量",
  options: {
    outputFormat: "stream-json",
    allowedTools: ["Read", "Glob", "Grep"],
  },
});

for await (const event of stream) {
  switch (event.type) {
    case "assistant_message":
      process.stdout.write(event.content);
      break;
    case "tool_use":
      console.log(`\n[工具调用] ${event.toolName}: ${JSON.stringify(event.input)}`);
      break;
    case "tool_result":
      console.log(`[工具结果] 长度: ${event.output.length} 字符`);
      break;
    case "result":
      console.log("\n--- 代理执行完成 ---");
      console.log(`总轮次: ${event.totalTurns}`);
      console.log(`总Token: ${event.totalTokenUsage}`);
      break;
  }
}
```

```mermaid
sequenceDiagram
    participant Dev as 开发者代码
    participant SDK as Agent SDK
    participant Claude as Claude API
    participant Tools as 工具层

    Dev->>SDK: query(prompt, options)
    SDK->>Claude: 发送消息 + 工具定义
    Claude->>SDK: 返回响应(含工具调用)
    
    loop Agent Loop
        SDK->>Tools: 执行工具调用
        Tools->>SDK: 返回工具结果
        SDK->>Claude: 发送工具结果
        Claude->>SDK: 继续响应或完成
    end
    
    SDK->>Dev: 返回最终结果 / 流式事件
```

### 2.3 内置工具清单

Agent SDK 继承了 Claude Code 的全部内置工具能力：

| 工具名 | 能力 | 典型用途 |
|--------|------|----------|
| `Read` | 读取文件内容 | 代码审查、配置检查 |
| `Write` | 写入/创建文件 | 代码生成、文档输出 |
| `Edit` | 精确编辑文件片段 | 代码修改、重构 |
| `Bash` | 执行Shell命令 | 构建、测试、部署 |
| `Glob` | 文件名模式匹配 | 项目结构扫描 |
| `Grep` | 内容搜索 | 依赖追踪、模式查找 |
| `WebSearch` | 网络搜索 | 资料查询 |
| `WebFetch` | 抓取网页内容 | 文档获取 |

---

## 第三步：自定义工具开发

这是 Agent SDK 最强大的能力——为 Claude 添加你自己的业务工具。

### 3.1 基础工具定义

```typescript
// tools/database-query.ts
import { defineTool } from "@anthropic-ai/claude-agent-sdk";

export const databaseQueryTool = defineTool({
  name: "database_query",
  description: "查询 PostgreSQL 数据库，执行只读 SQL 查询。仅支持 SELECT 语句。",
  inputSchema: {
    type: "object" as const,
    properties: {
      sql: {
        type: "string",
        description: "要执行的 SQL SELECT 查询语句",
      },
      database: {
        type: "string",
        description: "数据库名称，默认为 'main'",
        enum: ["main", "analytics", "logs"],
      },
      limit: {
        type: "number",
        description: "返回结果的最大行数，默认 100",
      },
    },
    required: ["sql"],
  },
  handler: async (input) => {
    // 安全校验：只允许 SELECT
    const normalizedSql = input.sql.trim().toUpperCase();
    if (!normalizedSql.startsWith("SELECT")) {
      return {
        isError: true,
        content: "安全限制：仅支持 SELECT 查询",
      };
    }

    const limit = input.limit ?? 100;
    const sqlWithLimit = normalizedSql.includes("LIMIT")
      ? input.sql
      : `${input.sql.replace(/;$/, "")} LIMIT ${limit}`;

    // 这里接入实际的数据库客户端
    const results = await executeQuery(input.database ?? "main", sqlWithLimit);
    
    return {
      content: JSON.stringify(results, null, 2),
    };
  },
});
```

### 3.2 带副作用的工具

```typescript
// tools/slack-notify.ts
import { defineTool } from "@anthropic-ai/claude-agent-sdk";

export const slackNotifyTool = defineTool({
  name: "slack_notify",
  description: "向指定 Slack 频道发送通知消息。用于在关键操作完成后通知团队。",
  inputSchema: {
    type: "object" as const,
    properties: {
      channel: {
        type: "string",
        description: "Slack 频道名称，如 '#deployments'",
      },
      message: {
        type: "string",
        description: "要发送的消息内容",
      },
      severity: {
        type: "string",
        description: "消息严重级别",
        enum: ["info", "warning", "critical"],
      },
    },
    required: ["channel", "message"],
  },
  handler: async (input) => {
    const colorMap = { info: "#36a64f", warning: "#f2c744", critical: "#e01e5a" };
    
    const payload = {
      channel: input.channel,
      attachments: [{
        color: colorMap[input.severity ?? "info"],
        text: input.message,
        ts: Math.floor(Date.now() / 1000).toString(),
      }],
    };

    const response = await fetch(process.env.SLACK_WEBHOOK_URL!, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    });

    return {
      content: response.ok
        ? `消息已发送到 ${input.channel}`
        : `发送失败: ${response.status} ${response.statusText}`,
    };
  },
});
```

### 3.3 组合工具到代理

```typescript
// agent.ts
import { query } from "@anthropic-ai/claude-agent-sdk";
import { databaseQueryTool } from "./tools/database-query";
import { slackNotifyTool } from "./tools/slack-notify";

const result = await query({
  prompt: "检查过去 24 小时的错误日志数量，如果超过 100 条则通知 #ops 频道",
  options: {
    model: "claude-sonnet-4-20250514",
    maxTurns: 15,
    customTools: [databaseQueryTool, slackNotifyTool],
    allowedTools: ["Read", "Bash", "database_query", "slack_notify"],
    systemPrompt: "你是一个运维监控助手。先查询数据，再根据结果决定是否需要告警。",
  },
});

console.log(result);
```

```mermaid
flowchart LR
    subgraph 自定义工具层
        A[database_query<br/>SQL只读查询]
        B[slack_notify<br/>Slack消息推送]
        C[jira_create<br/>Jira工单创建]
        D[metric_fetch<br/>监控指标拉取]
    end

    subgraph Agent SDK 核心
        E[Agent Loop<br/>推理-决策-执行]
    end

    subgraph 内置工具层
        F[Read/Write/Edit]
        G[Bash]
        H[Glob/Grep]
    end

    I[用户 Prompt] --> E
    E --> A & B & C & D & F & G & H
    A & B & C & D & F & G & H --> E

    style A fill:#4a9eff,color:#fff
    style B fill:#e01e5a,color:#fff
    style C fill:#0052cc,color:#fff
    style D fill:#f4a261,color:#000
```

---

## 第四步：Hook 系统——拦截与增强

Hook 允许你在代理执行的关键节点注入自定义逻辑，实现审计、限流、安全校验等功能。

### 4.1 Pre-Tool-Use Hook：工具调用前拦截

```typescript
// hooks/pre-tool-security.ts
import { Hook } from "@anthropic-ai/claude-agent-sdk";

export const securityHook: Hook = {
  name: "security-gate",
  events: ["PreToolUse"],
  handler: async (event) => {
    // 阻止对生产环境文件的写入
    if (event.toolName === "Write" || event.toolName === "Edit") {
      const filePath = event.input.file_path ?? "";
      const blockedPaths = [
        "/etc/",
        "/var/www/production/",
        "/opt/deploy/",
      ];
      
      if (blockedPaths.some((p) => filePath.startsWith(p))) {
        return {
          decision: "block" as const,
          reason: `安全策略禁止修改生产路径: ${filePath}`,
        };
      }
    }

    // 审计所有 Bash 命令
    if (event.toolName === "Bash") {
      const command = event.input.command ?? "";
      const dangerousPatterns = [
        /rm\s+-rf\s+\//,
        /DROP\s+TABLE/i,
        /DELETE\s+FROM/i,
        />\s*\/dev\//,
        /curl.*\|\s*sh/,
      ];

      for (const pattern of dangerousPatterns) {
        if (pattern.test(command)) {
          return {
            decision: "block" as const,
            reason: `危险命令被安全策略拦截: ${command.substring(0, 80)}...`,
          };
        }
      }

      // 记录审计日志
      await logAudit({
        tool: event.toolName,
        input: event.input,
        timestamp: new Date().toISOString(),
      });
    }

    return { decision: "allow" as const };
  },
};
```

### 4.2 Post-Tool-Use Hook：结果后处理

```typescript
// hooks/post-tool-metrics.ts
import { Hook } from "@anthropic-ai/claude-agent-sdk";

export const metricsHook: Hook = {
  name: "execution-metrics",
  events: ["PostToolUse"],
  handler: async (event) => {
    const metrics = {
      tool: event.toolName,
      duration: event.duration,
      success: !event.isError,
      tokenCost: event.tokenUsage,
      timestamp: new Date().toISOString(),
    };

    // 推送到监控系统
    await pushToMetrics(metrics);

    // 如果执行失败超过阈值，自动告警
    if (event.isError) {
      console.error(`[工具失败] ${event.toolName}: ${event.error}`);
    }

    return { decision: "allow" as const };
  },
};
```

### 4.3 注册 Hook 到代理

```typescript
import { query } from "@anthropic-ai/claude-agent-sdk";
import { securityHook } from "./hooks/pre-tool-security";
import { metricsHook } from "./hooks/post-tool-metrics";

const result = await query({
  prompt: "重构 src/utils/ 下的所有工具函数，添加 JSDoc 注释",
  options: {
    hooks: [securityHook, metricsHook],
    maxTurns: 30,
  },
});
```

```mermaid
flowchart TD
    A[Claude 决定调用工具] --> B{PreToolUse Hook}
    B -->|block| C[返回拒绝原因<br/>代理重新规划]
    B -->|allow| D[执行工具]
    D --> E{PostToolUse Hook}
    E -->|block| F[丢弃结果<br/>代理重新规划]
    E -->|allow| G[结果返回代理<br/>继续推理]

    H[代理启动] --> I{Notification Hook}
    I --> J[记录启动事件]

    style B fill:#ff6b6b,color:#fff
    style E fill:#4ecdc4,color:#fff
    style C fill:#ffe66d,color:#000
    style F fill:#ffe66d,color:#000
```

---

## 第五步：子代理编排——并行处理复杂任务

当单一代理处理不过来时，可以派生子代理并行工作。Agent SDK 提供了子代理调度能力。

### 5.1 并行文件处理

```typescript
// multi-file-review.ts
import { query } from "@anthropic-ai/claude-agent-sdk";
import * as path from "path";
import * as fs from "fs/promises";

async function reviewFile(filePath: string) {
  return query({
    prompt: `审查以下文件的代码质量，关注：安全性、性能、可维护性。
    
文件路径: ${filePath}

请输出结构化的审查报告，包含问题列表和改进建议。`,
    options: {
      model: "claude-sonnet-4-20250514",
      maxTurns: 5,
      allowedTools: ["Read"],
      outputFormat: "json",
      systemPrompt: "你是一位高级代码审查工程师。输出 JSON 格式的审查报告。",
    },
  });
}

async function parallelReview(directory: string) {
  // 1. 扫描目标文件
  const files = await fs.readdir(directory, { recursive: true });
  const tsFiles = files
    .filter((f) => f.toString().endsWith(".ts"))
    .map((f) => path.join(directory, f.toString()));

  console.log(`发现 ${tsFiles.length} 个 TypeScript 文件`);

  // 2. 并行启动子代理（限制并发数）
  const CONCURRENCY = 5;
  const results: Map<string, any> = new Map();

  for (let i = 0; i < tsFiles.length; i += CONCURRENCY) {
    const batch = tsFiles.slice(i, i + CONCURRENCY);
    const batchResults = await Promise.all(
      batch.map(async (file) => {
        const result = await reviewFile(file);
        return { file, result };
      })
    );
    batchResults.forEach((r) => results.set(r.file, r.result));
    console.log(`已完成 ${Math.min(i + CONCURRENCY, tsFiles.length)}/${tsFiles.length}`);
  }

  // 3. 汇总报告
  return Object.fromEntries(results);
}

// 执行
const report = await parallelReview("./src");
console.log(JSON.stringify(report, null, 2));
```

### 5.2 管道式多阶段代理

```typescript
// pipeline.ts — 代码迁移流水线
import { query } from "@anthropic-ai/claude-agent-sdk";

// 阶段1: 分析旧代码结构
const analysis = await query({
  prompt: "分析 src/ 目录的代码结构，识别所有需要从 JavaScript 迁移到 TypeScript 的文件",
  options: {
    allowedTools: ["Read", "Glob", "Grep"],
    maxTurns: 10,
    outputFormat: "json",
  },
});

// 阶段2: 生成迁移计划
const migrationPlan = await query({
  prompt: `基于以下分析结果，生成迁移计划。按依赖顺序排列文件。
  
分析结果:
${JSON.stringify(analysis)}

输出 JSON 数组，每个元素包含: file, priority, dependencies, estimated_complexity`,
  options: {
    maxTurns: 3,
    outputFormat: "json",
  },
});

// 阶段3: 逐文件执行迁移
const plan = JSON.parse(migrationPlan);
for (const item of plan) {
  console.log(`迁移: ${item.file} (优先级: ${item.priority})`);
  
  await query({
    prompt: `将 ${item.file} 从 JavaScript 迁移到 TypeScript。
要求：
1. 添加完整的类型注解
2. 修复所有类型错误
3. 保持原有功能不变
4. 添加必要的 interface 和 type 定义`,
    options: {
      allowedTools: ["Read", "Write", "Edit", "Bash"],
      maxTurns: 20,
      systemPrompt: "你是一个 TypeScript 迁移专家。严格保持功能等价。",
    },
  });
}

// 阶段4: 验证
const validation = await query({
  prompt: "运行 TypeScript 编译检查，确认所有文件无类型错误。如果有错误，修复它们。",
  options: {
    allowedTools: ["Bash", "Read", "Edit"],
    maxTurns: 15,
  },
});
```

```mermaid
flowchart TD
    A[主代理: 分析阶段] -->|JSON 输出| B[主代理: 规划阶段]
    B -->|迁移计划| C{文件列表}

    C --> D1[子代理: 迁移 file1.ts]
    C --> D2[子代理: 迁移 file2.ts]
    C --> D3[子代理: 迁移 file3.ts]
    C --> D4[子代理: 迁移 fileN.ts]

    D1 --> E[主代理: 验证阶段]
    D2 --> E
    D3 --> E
    D4 --> E

    E -->|编译检查| F{有错误?}
    F -->|是| G[子代理: 修复错误]
    G --> E
    F -->|否| H[迁移完成 ✓]

    style A fill:#6c5ce7,color:#fff
    style B fill:#0984e3,color:#fff
    style E fill:#00b894,color:#fff
    style H fill:#00b894,color:#fff
```

---

## 第六步：实战项目——自动化代码审查机器人

将以上所有知识整合为一个可运行的代码审查机器人。

### 6.1 项目结构

```
code-review-bot/
├── src/
│   ├── index.ts          # 入口文件
│   ├── tools/
│   │   ├── git-diff.ts   # Git 差异工具
│   │   └── comment.ts    # PR 评论工具
│   ├── hooks/
│   │   └── audit.ts      # 审计 Hook
│   └── prompts/
│       └── review.ts     # 审查提示词模板
├── package.json
└── tsconfig.json
```

### 6.2 自定义工具：获取 Git 差异

```typescript
// src/tools/git-diff.ts
import { defineTool } from "@anthropic-ai/claude-agent-sdk";

export const gitDiffTool = defineTool({
  name: "git_diff",
  description: "获取 Git 仓库中指定提交范围的代码差异。用于代码审查。",
  inputSchema: {
    type: "object" as const,
    properties: {
      base: {
        type: "string",
        description: "基准分支或提交，默认为 'main'",
      },
      head: {
        type: "string",
        description: "目标分支或提交，默认为 'HEAD'",
      },
      stat: {
        type: "boolean",
        description: "是否只返回统计信息而非完整差异",
      },
    },
    required: [],
  },
  handler: async (input) => {
    const { execSync } = await import("child_process");
    
    const base = input.base ?? "main";
    const head = input.head ?? "HEAD";
    
    try {
      let command: string;
      if (input.stat) {
        command = `git diff --stat ${base}...${head}`;
      } else {
        command = `git diff ${base}...${head} --diff-filter=ACMR`;
      }
      
      const output = execSync(command, {
        maxBuffer: 10 * 1024 * 1024, // 10MB
        encoding: "utf-8",
      });
      
      return { content: output || "没有发现差异" };
    } catch (error: any) {
      return {
        isError: true,
        content: `Git 命令执行失败: ${error.message}`,
      };
    }
  },
});
```

### 6.3 自定义工具：发布审查评论

```typescript
// src/tools/comment.ts
import { defineTool } from "@anthropic-ai/claude-agent-sdk";

interface ReviewComment {
  file: string;
  line: number;
  severity: "info" | "warning" | "critical";
  message: string;
  suggestion?: string;
}

export const postCommentTool = defineTool({
  name: "post_review_comment",
  description: "发布代码审查评论。支持对特定文件行添加审查意见。",
  inputSchema: {
    type: "object" as const,
    properties: {
      summary: {
        type: "string",
        description: "整体审查摘要",
      },
      comments: {
        type: "array",
        items: {
          type: "object",
          properties: {
            file: { type: "string" },
            line: { type: "number" },
            severity: { type: "string", enum: ["info", "warning", "critical"] },
            message: { type: "string" },
            suggestion: { type: "string" },
          },
          required: ["file", "line", "severity", "message"],
        },
        description: "具体审查评论列表",
      },
      approved: {
        type: "boolean",
        description: "是否批准此 PR",
      },
    },
    required: ["summary", "comments", "approved"],
  },
  handler: async (input) => {
    const { summary, comments, approved } = input;
    
    // 构建审查报告
    const emoji = { info: "ℹ️", warning: "⚠️", critical: "🔴" };
    let report = `## 代码审查报告\n\n`;
    report += `**结论**: ${approved ? "✅ 批准合并" : "❌ 需要修改"}\n\n`;
    report += `### 摘要\n${summary}\n\n`;
    report += `### 详细评论 (${comments.length} 条)\n\n`;
    
    for (const comment of comments as ReviewComment[]) {
      report += `${emoji[comment.severity]} **${comment.file}:${comment.line}**\n`;
      report += `> ${comment.message}\n`;
      if (comment.suggestion) {
        report += `建议: \`${comment.suggestion}\`\n`;
      }
      report += "\n";
    }
    
    // 如果配置了 GitHub，可以自动发布到 PR
    if (process.env.GITHUB_TOKEN && process.env.PR_NUMBER) {
      await postToGitHub(report);
    }
    
    // 同时输出到控制台
    console.log(report);
    
    return {
      content: `审查报告已生成。${comments.length} 条评论。结论: ${approved ? "批准" : "需修改"}。`,
    };
  },
});

async function postToGitHub(body: string) {
  const repo = process.env.GITHUB_REPOSITORY!;
  const prNumber = process.env.PR_NUMBER!;
  
  await fetch(`https://api.github.com/repos/${repo}/issues/${prNumber}/comments`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ body }),
  });
}
```

### 6.4 审查提示词模板

```typescript
// src/prompts/review.ts
export function buildReviewPrompt(diffStat: string): string {
  return `你是一位资深代码审查工程师。请审查以下代码变更。

## 审查维度
1. **安全性**: SQL 注入、XSS、路径遍历、硬编码密钥
2. **性能**: N+1 查询、内存泄漏、不必要的循环
3. **可维护性**: 命名规范、函数复杂度、重复代码
4. **正确性**: 逻辑错误、边界条件、错误处理

## 变更统计
${diffStat}

## 审查流程
1. 使用 git_diff 工具获取完整差异（不带 stat 参数）
2. 逐文件分析变更内容
3. 使用 post_review_comment 工具发布审查结果

## 输出要求
- summary: 用中文写一段总体评价
- comments: 每个问题一条评论，必须包含文件名和行号
- approved: 只有 0 个 critical 问题时才批准`;
}
```

### 6.5 主入口

```typescript
// src/index.ts
import { query } from "@anthropic-ai/claude-agent-sdk";
import { gitDiffTool } from "./tools/git-diff";
import { postCommentTool } from "./tools/comment";
import { securityHook } from "./hooks/audit";
import { buildReviewPrompt } from "./prompts/review";

async function main() {
  const targetBranch = process.env.TARGET_BRANCH ?? "main";
  const sourceBranch = process.env.SOURCE_BRANCH ?? "HEAD";

  // 获取差异统计（用 Bash 工具）
  const statResult = await query({
    prompt: `执行 git diff --stat ${targetBranch}...${sourceBranch}，返回输出`,
    options: {
      allowedTools: ["Bash"],
      maxTurns: 2,
    },
  });

  // 启动审查代理
  const result = await query({
    prompt: buildReviewPrompt(statResult),
    options: {
      model: "claude-sonnet-4-20250514",
      maxTurns: 25,
      customTools: [gitDiffTool, postCommentTool],
      allowedTools: ["Read", "Grep", "git_diff", "post_review_comment"],
      hooks: [securityHook],
      systemPrompt: `你是一个代码审查机器人。
规则：
- 必须使用工具获取实际代码，不要猜测
- 每个问题必须有具体的文件名和行号
- 安全问题标记为 critical，性能标记为 warning，其他标记为 info
- 审查完毕后必须调用 post_review_comment 发布结果`,
    },
  });

  console.log("\n=== 审查代理执行完毕 ===");
  console.log(result);
}

main().catch(console.error);
```

### 6.6 运行方式

```bash
# 命令行运行
TARGET_BRANCH=main SOURCE_BRANCH=feature/login npx tsx src/index.ts

# CI/CD 中运行（GitHub Actions）
env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  GITHUB_REPOSITORY: ${{ github.repository }}
  PR_NUMBER: ${{ github.event.pull_request.number }}
  TARGET_BRANCH: main
  SOURCE_BRANCH: ${{ github.head_ref }}
```

---

## 第七步：进阶模式

### 7.1 会话持久化与恢复

```typescript
import { query, saveSession, loadSession } from "@anthropic-ai/claude-agent-sdk";

// 第一次运行，保存会话
const result = await query({
  prompt: "开始分析项目架构",
  options: { sessionId: "arch-analysis-001" },
});

// 后续运行，恢复上下文
const continued = await query({
  prompt: "继续上次的架构分析，现在重点关注数据库层",
  options: { sessionId: "arch-analysis-001" },
});
```

### 7.2 与 `claude -p` 的协同

Agent SDK 和 `claude -p` 可以协同使用——CLI 用于快速脚本，SDK 用于复杂逻辑：

```bash
# CLI: 快速批量操作
for dir in packages/*/; do
  claude -p --output-format json \
    "检查 $dir 的 package.json 是否有过时的依赖" \
    --allowedTools "Read" \
    --max-turns 3
done

# SDK: 复杂的多步骤代理
npx tsx complex-agent.ts
```

### 7.3 成本控制策略

```typescript
const result = await query({
  prompt: "简要总结这个文件的功能",
  options: {
    model: "claude-haiku-4-5-20251001", // 简单任务用 Haiku
    maxTurns: 3,                         // 限制轮次
    allowedTools: ["Read"],              // 最小工具集
  },
});

// 复杂任务才用 Sonnet
const complexResult = await query({
  prompt: "重构整个模块的架构",
  options: {
    model: "claude-sonnet-4-20250514",
    maxTurns: 30,
    allowedTools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
  },
});
```

---

## 常见陷阱与解决方案

| 问题 | 原因 | 解决方案 |
|------|------|----------|
| 代理陷入无限循环 | 任务目标不明确，代理反复尝试 | 设置 `maxTurns`，优化 prompt |
| 工具调用权限不足 | `allowedTools` 未包含所需工具 | 检查白名单，添加缺失工具名 |
| Token 消耗过大 | 大文件读取 + 多轮推理 | 用 `Glob`/`Grep` 精确定位，避免读取整个项目 |
| 自定义工具未生效 | 工具名不匹配或 Schema 错误 | 确保 `defineTool` 的 name 与 `allowedTools` 一致 |
| Hook 阻塞了正常操作 | Hook 规则过严 | 先在测试环境调试 Hook 逻辑 |
| 并行子代理 API 限流 | 并发请求超过速率限制 | 控制并发数（建议 3-5），加入重试逻辑 |
| 流式输出解析错误 | 事件类型判断不完整 | 使用 switch 语句覆盖所有事件类型 |

---

## 架构总览

```mermaid
graph TB
    subgraph 用户应用
        A[TypeScript 代码]
    end

    subgraph "Agent SDK 层"
        B[query 入口函数]
        C[Agent Loop<br/>推理→决策→执行]
        D[Hook Engine<br/>PreToolUse / PostToolUse]
    end

    subgraph "工具层"
        E[内置工具<br/>Read Write Edit Bash<br/>Glob Grep WebSearch]
        F[自定义工具<br/>defineTool 注册<br/>MCP In-Process Server]
        G[外部 MCP<br/>第三方服务集成]
    end

    subgraph "Claude API"
        H[消息发送/接收]
        I[工具调用解析]
        J[流式响应]
    end

    A --> B
    B --> C
    C --> D
    D --> E & F & G
    E & F & G --> D
    D --> C
    C <--> H
    H <--> I
    H <--> J

    style B fill:#6c5ce7,color:#fff
    style C fill:#0984e3,color:#fff
    style D fill:#fdcb6e,color:#000
    style F fill:#00b894,color:#fff
```

---

## 参考来源

1. [Agent SDK overview - Claude Code Docs](https://code.claude.com/docs/en/agent-sdk/overview) — Agent SDK 官方概述与核心概念
2. [anthropics/claude-agent-sdk-typescript - GitHub](https://github.com/anthropics/claude-agent-sdk-typescript) — TypeScript SDK 源码与示例
3. [Give Claude custom tools - Claude Code Docs](https://code.claude.com/docs/en/agent-sdk/custom-tools) — 自定义工具定义文档
4. [Run Claude Code programmatically - Claude Code Docs](https://code.claude.com/docs/en/headless) — Headless 模式与编程式调用
5. [Build Production AI Agents with Claude Agent SDK and TypeScript](https://noqta.tn/en/tutorials/claude-agent-sdk-typescript-ai-agents-2026) — 生产级 Agent 构建教程

---

**训练营适用性评估**：本教程适合「AI 工程化训练营」和「DevOps 自动化训练营」，建议教学时长 4-5 小时（含上机实操）。学员需具备 TypeScript 基础和异步编程经验。建议分三个阶段教学：SDK 基础（1.5h）→ 自定义工具与 Hook（1.5h）→ 实战项目（1.5-2h）。

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
