前面几个教程已经讲过 query() 的基本用法。这一课重点讲:在 Electron 桌面应用里,query() 是怎么被调用和管理的。

第 4 课:SDK 集成详解


这一课的重点

前面几个教程已经讲过 query() 的基本用法。这一课重点讲:在 Electron 桌面应用里,query() 是怎么被调用和管理的

代码在哪?

SDK 集成的核心代码在 src/main/main.ts 里——Electron 的主进程。

query() 在桌面应用里的调用

python
import { query, type SDKMessage } from '@anthropic-ai/claude-agent-sdk';

// 当收到前端发来的消息时
ipcMain.on('claude-code:query', async (event, { content, files }) => {

  // 1. 处理上传的文件(如果有的话)
  // ...

  // 2. 构造提示词
  const prompt = content;  // 用户输入的文字

  // 3. 调用 query()
  const queryIterator = query({
    prompt,
    options: {
      cwd: path.join(process.cwd(), 'agent'),  // AI 在 agent/ 目录干活
      abortController,    // 用来取消正在进行的任务
      maxTurns: 100,      // 比 resume-generator 的 30 大很多!
      settingSources: ['local', 'project'],
      allowedTools: [
        'Bash',           // 执行 Python 代码
        'Create', 'Edit', // 创建和编辑文件
        'Read', 'Write',  // 读写文件
        'WebSearch',      // 搜索网页
        'GrepTool',       // 搜索文件内容
        'Skill',          // 读取技能文件
        'TodoWrite',      // 创建任务列表
        'TodoEdit',       // 编辑任务列表
      ],
    },
  });

  // 4. 流式处理消息
  for await (const message of queryIterator) {
    messages.push(message);
    event.reply('claude-code:response', message);  // 实时发给前端
  }
});

和 resume-generator 的对比

配置项 resume-generator excel-demo 为什么不同
maxTurns 30 100 Excel 任务更复杂,可能需要多次调试
cwd process.cwd() ./agent 让 AI 在 agent 子目录工作,隔离文件
settingSources ['project'] ['local', 'project'] 多了 local,可以读全局配置
systemPrompt 直接传入 不传(用 CLAUDE.MD) 通过文件来配置系统提示词
allowedTools 7 个 10 个 多了 Create、Edit、GrepTool、TodoWrite/Edit

几个关键区别详解

maxTurns: 100

Excel 任务比简历生成复杂得多。AI 可能需要: - 先读取上传的文件理解结构 - 写代码生成 Excel - 执行代码发现有公式错误 - 修改代码重新执行 - 再次验证……

100 轮给了充足的试错空间。

cwd 设为 agent/ 子目录

code
demos/excel-demo/
├── src/            ← Electron 代码在这里
└── agent/          ← AI 在这个目录干活
    ├── CLAUDE.MD   ← AI 能读到系统提示词
    ├── .claude/    ← AI 能读到技能文件
    ├── problems/   ← 上传的文件在这里
    └── *.xlsx      ← AI 生成的文件也在这里

把 AI 限制在 agent/ 目录,它就不会误操作 Electron 的源代码。

systemPrompt 没有直接传

resume-generator 是把 SYSTEM_PROMPT 变量直接传进 query()。excel-demo 不传这个参数,而是在 agent/CLAUDE.MD 文件里写系统提示词。SDK 通过 settingSources: ['project'] 会自动读取这个文件。

效果一样,但文件方式更方便维护。

消息流处理

code
const messages: SDKMessage[] = [];

for await (const message of queryIterator) {
  // 把每条消息存起来
  messages.push(message);

  // 实时转发给前端显示
  event.reply('claude-code:response', message);
}

和 resume-generator 的区别: - resume-generator 自己在终端打印消息 - excel-demo 把消息通过 IPC 转发给 React,由 React 来渲染

取消任务:AbortController

桌面应用有一个新需求:用户可能想中途取消正在执行的任务。

code
let abortController: AbortController | null = null;

// 开始任务时
ipcMain.on('claude-code:query', async (event, data) => {
  abortController = new AbortController();

  const queryIterator = query({
    prompt,
    options: {
      abortController,  // 传给 SDK
      // ...
    },
  });

  // ...
});

// 用户点击"取消"按钮时
ipcMain.on('claude-code:abort', () => {
  if (abortController) {
    abortController.abort();  // 终止!
    abortController = null;
  }
});

AbortController 是 Web API 标准的"取消器"。调用 abort() 后,SDK 会停止当前的 AI 任务。

文件检测机制

主进程还有一个重要职责:检测 AI 生成了什么新文件

code
// 任务开始前,记录已有的文件
const outputDir = path.join(process.cwd(), 'agent');
const initialFiles = fs.readdirSync(outputDir)
  .filter(f => f.endsWith('.xlsx') || f.endsWith('.csv'));

// ... AI 工作 ...

// 任务结束后,检测新文件
const finalFiles = fs.readdirSync(outputDir)
  .filter(f => f.endsWith('.xlsx') || f.endsWith('.csv'));

const newFiles = finalFiles.filter(f => !initialFiles.includes(f));

if (newFiles.length > 0) {
  // 告诉前端:"有新文件了!"
  event.reply('claude-code:output-files', newFiles.map(f => ({
    name: f,
    path: path.join(outputDir, f),
  })));
}

这样前端就知道该显示哪些文件的下载按钮了。

完整的生命周期

把上面所有内容串起来:

code
用户点击[发送]
    │
    ▼
前端 → IPC('claude-code:query', { content, files })
    │
    ▼
主进程收到消息
    ├── 保存上传文件到 agent/problems/
    ├── 记录初始文件列表
    ├── 创建 AbortController
    └── 调用 query()
          │
          ├── 消息 1 → IPC('response') → 前端更新
          ├── 消息 2 → IPC('response') → 前端更新
          ├── 消息 3 → IPC('response') → 前端更新
          └── ...
          │
    query() 完成
    ├── 检测新文件
    └── IPC('output-files') → 前端显示下载按钮

本课小结

  • SDK 的 query() 在 Electron 主进程里调用,通过 IPC 和前端通信
  • maxTurns=100 给了 AI 充足的操作空间
  • cwd 设为 agent/ 子目录,隔离 AI 的工作区
  • AbortController 让用户可以随时取消任务
  • 文件检测机制让前端知道什么时候显示下载按钮

课后练习

  1. main.tsfor await 循环里加一个计数器,看看一个 Excel 任务通常要多少轮
  2. 试着把 maxTurns 改成 10,看看复杂任务是不是会失败
  3. 思考:如果你想支持多个用户同时使用(多窗口),代码需要怎么改?

沿着当前专题继续,或返回课程目录重新整理阅读顺序。

返回课程目录