Claude Agent SDK 配置渗漏与隔离 (Claude Agent SDK Config Bleed and settingSources Isolation)
一句话
用 Claude Agent SDK 自建 agent 时,即使把系统提示词整个替换成自己的字符串,宿主的 CLAUDE.md、~/.claude/settings.json 和自己配置的 MCP server 仍然会被注入。除非显式设置 settingSources: []。
不报错、不警告,只是 agent "有点怪" —— 属于最难查的一类问题。
发生条件
- 用
@anthropic-ai/claude-agent-sdk(query()API) - 通过
systemPrompt: "<纯字符串>"替换了默认提示词 - 没有设置
settingSources - 运行环境的用户/工程目录下存在着 Claude Code 的配置文件(几乎所有用过 Claude Code 的机器都满足)
症状
搭建 mini-agent 时实测(同一句提问,只差一个 settingSources 字段):
| 观察点 | 未设置(默认) | 设为 [] |
|---|---|---|
system/init 消息里的 tools.length | 30 | 26 |
mcp_servers 列表 | 出现 bing-search: failed、google-search: connected(用户自己的 MCP 配置) | 只剩自己注册的 local: connected |
模型 thinking 块的语言与内容 | 复述宿主全局 ~/.claude/CLAUDE.md 的规则(英文、关于"回答后必须带工具调用") | 干净,只遵循自定义提示词 |
| 最终回答风格 | 偏离自定义提示词 | 符合自定义提示词("用中文回答,简短") |
关键点:第三行是判定依据。工具数变化可能是系统版本差异,但模型念出宿主 CLAUDE.md 的内容,只可能是渗漏。
先说清楚:三条独立的通道
渗漏之所以难查,是因为"给模型的指令"其实走三条互不相干的通道,改了一条不代表改了别的。
| 通道 | 是什么 | 怎么控制 |
|---|---|---|
| 系统提示词 | Claude Code 内置那一大段(身份、工具用法、安全规则、环境信息) | Options.systemPrompt |
| CLAUDE.md | 文件系统里的记忆文件(~/.claude/CLAUDE.md、工程 CLAUDE.md) | Options.settingSources 里含 'project' 才加载 |
| settings.json | ~/.claude/settings.json、.claude/settings.json 等 | 同上 |
关键:systemPrompt 传纯字符串只换第一条,不会关掉另外两条。
所以会出现"提示词明明整个换掉了,模型还在念宿主 CLAUDE.md 的规矩"这种怪事 ——
两条通道各管各的,你只拨了一个开关。
完整的三层可配项(以及 plugins 这条绕过 settingSources 的路)另见
Claude Code 插件与 Skill 加载机制 (Claude Code Plugin and Skill Loading)。
关于"能不能修改内置提示词"
分四种情况,别混:
| 想干什么 | 能不能 | 怎么做 |
|---|---|---|
| 整个换掉 | ✅ | systemPrompt: '<纯字符串>' |
| 在它基础上追加 | ✅ | systemPrompt: { type: 'preset', preset: 'claude_code', append: '...' } |
| 拿到它的原文 | ⚠️ | 官方闭源,在打包的二进制里;要看得 MITM 抓(见 Claude Code 系统提示词抓取 (Capturing Claude Code System Prompt via MITM) ) |
| 改它本身 | ❌ | 那是产品的一部分,只能用或不用 |
"换掉"不等于"修改"。 传字符串是把内置的整个丢掉,换成自己的 —— 对模型来说内置那套压根不存在。
但换说明书 ≠ 换手脚:
系统提示词 = 岗位说明书 ← 可以整个换
工具集 = 手上的家伙 ← 换不掉(除非改 allowedTools / disallowedTools)
hooks/权限 = 门禁 ← 换不掉(除非自己配 hooks)推论:换了提示词之后,必须自己在提示词里交代"你有哪些工具、该怎么用" ——
自带的那部分说明跟着原提示词一起消失了。这是换提示词最容易漏的一条。
根因
Claude Agent SDK 本质是启动一个 Claude Code CLI 子进程。这个 CLI 沿用命令行默认行为加载"文件系统配置",来源有三层:
user→~/.claude/settings.jsonproject→ 工程.claude/settings.jsonlocal→ 工程.claude/settings.local.json
且 CLAUDE.md 的记忆加载挂在 project 源下。
SDK 文档原文:
When omitted, all sources are loaded (matches CLI defaults).
Pass[]to disable filesystem settings (SDK isolation mode).
Must include'project'to load CLAUDE.md files.
所以"替换 system prompt"只换了提示词本身,没有换掉配置加载。两条路径独立,各管各的。
这与直觉冲突的地方在于:systemPrompt 传纯字符串的语义确实是"整个替换,不追加"(已由 SDK 类型定义确认:只有 {type:'preset', preset:'claude_code'} 才包含默认提示词)。提示词替换本身是对的,渗漏来自另一个开关。
最终解决
query({
prompt,
options: {
systemPrompt: myPromptString, // 换提示词(这个本来就是对的)
settingSources: [], // 关键:关掉配置加载
// ...其它 options
}
})需要读工程自己的 CLAUDE.md 时再改成 ['project']。
配套的两项加固(同源问题,建议一起做):
- 剥掉宿主会话变量再传给子进程:
CLAUDECODE、CLAUDE_CODE_SESSION_ID、CLAUDE_CODE_ENTRYPOINT、CLAUDE_CODE_CHILD_SESSION、CLAUDE_CODE_EXECPATH、CLAUDE_PID。不剥的话,被启动的 CLI 会误判自己处在嵌套会话中。 - 显式覆盖
ANTHROPIC_BASE_URL:从 Claude Code 进程内运行时该变量会被继承成宿主的中转地址,不是使用者自己配置的端点。
验证方法
npm start -- --show-config # 看继承到的 ANTHROPIC_* 环境
npm start -- "随便问一句" # 看启动日志里的 tools.length 和 mcp_servers对照表见上文。只要 mcp_servers 里出现了自己没注册的名字,就是渗漏。
下次如何识别
- 自建 agent "不听话"、"答非所问"、风格忽变,但提示词明明是对的
- 启动日志里
tools数量比预期多 - 模型复述了任何你没有写进自定义提示词里的规则
- 从 Claude Code 内部(或任何装有 Claude Code 的机器)启动自建 agent
以上任一条命中,先查 settingSources,再查环境变量继承。
相关
- AI 编码代理提示词架构对比 (Prompt Architecture pi vs Claude Code) —— 两家提示词分层设计的差异
- MCP (Model Context Protocol) ——
mcpServers注册机制与工具命名mcp__<server>__<tool> - Claude Code 系统提示词抓取 (Capturing Claude Code System Prompt via MITM) —— 默认提示词到底包含什么
出现场景
C:\Users\18086\.workspace\git-workspace\mini-agent —— 一个"自加工具 + 自换提示词"的小 agent 壳子,2026-09-11 搭建。该问题在首次实跑时暴露。
本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/AI apps