第 4 课:SDK 集成详解
这一课的重点
前面几个教程已经讲过 query() 的基本用法。这一课重点讲:在 Electron 桌面应用里,query() 是怎么被调用和管理的。
代码在哪?
SDK 集成的核心代码在 src/main/main.ts 里——Electron 的主进程。
query() 在桌面应用里的调用
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/ 子目录
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'] 会自动读取这个文件。
效果一样,但文件方式更方便维护。
消息流处理
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
桌面应用有一个新需求:用户可能想中途取消正在执行的任务。
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 生成了什么新文件。
// 任务开始前,记录已有的文件
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),
})));
}
这样前端就知道该显示哪些文件的下载按钮了。
完整的生命周期
把上面所有内容串起来:
用户点击[发送]
│
▼
前端 → 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 让用户可以随时取消任务
- 文件检测机制让前端知道什么时候显示下载按钮
课后练习
- 在
main.ts的for await循环里加一个计数器,看看一个 Excel 任务通常要多少轮 - 试着把
maxTurns改成 10,看看复杂任务是不是会失败 - 思考:如果你想支持多个用户同时使用(多窗口),代码需要怎么改?