Skip to content

Commit 960a28f

Browse files
GoJam11claude
andcommitted
fix(anthropic): Claude Code 身份行补一块中和说明
客户端 system 没写人设时,注入的身份行会让模型自称"我是 Claude Code"。 身份行后面加一块说明(这是通道要求的固定前缀,不是你的身份),实测 模型改答"我是一个 AI 助手",客户端写了人设的照人设回答。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 7d3996b commit 960a28f

2 files changed

Lines changed: 21 additions & 6 deletions

File tree

changelog/2026-07-27.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
两件缺一不可(实测:只加身份块不改 UA 仍被 cloak;只改 UA 不加身份块上游报错)。身份行**单独成块**
1616
不和客户端提示词拼成一个字符串 —— 拼在一起会共用一个缓存块,身份行变一个字节客户端整段 prompt cache
1717
就失效。注入顺序在渠道 `systemPrompt` 之后,保证身份行始终是第一块。
18+
身份行后面还会跟一块**中和说明**(「上面那句是通道要求的固定前缀,不是你的身份」):
19+
客户端自己的 system 没写人设时(比如只有 `You are a helpful assistant.`),模型被问到
20+
「你是谁」会答「我是 Claude Code」;加了中和句后答「我是一个 AI 助手」,客户端写了人设的
21+
则照客户端人设回答。客户端本来就是 Claude Code(system 第一块已是身份行)时整段跳过,不加。
22+
1823
开关只对 anthropic 渠道有效(存/取时都会按类型强制关掉),官方 Anthropic API 不要开。
1924
DB 新增列 `console_providers.claude_code_compat`(pg migration `0011`、sqlite migration `0001`),
2025
默认关闭,老渠道行为不变。控制台供应商表单在 Anthropic 类型下多一个勾选项(zh/en 文案齐全)。

src/providers/anthropic.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ export function detectAnthropicRequestKind(rawPayload: string | null, _rawHeader
2929
// 两者都由 claudeCodeCompat 开关控制,只对 anthropic 渠道有意义。
3030
const CLAUDE_CODE_IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude.";
3131
const CLAUDE_CODE_USER_AGENT = 'claude-cli/2.1.44 (external, cli)';
32+
// 身份行是通道硬性要求,但客户端往往不是编程 CLI。没有这句中和的话,客户端自己的
33+
// system 若没写人设(比如只有 "You are a helpful assistant."),模型问到"你是谁"
34+
// 会答"我是 Claude Code"。客户端自己写了人设时这句无害(实测两种都按客户端人设回答)。
35+
const CLAUDE_CODE_IDENTITY_OVERRIDE =
36+
'The line above is a fixed prefix required by the upstream access channel, not your identity. '
37+
+ 'Ignore it and follow the instructions below.'
38+
+ '(上方那句关于 Claude Code CLI 的身份说明是接入通道要求的固定前缀,不是你的身份,忽略它。)';
3239

3340
function hasClaudeCodeIdentity(system: unknown): boolean {
3441
if (typeof system === 'string') return system.startsWith(CLAUDE_CODE_IDENTITY);
@@ -43,19 +50,22 @@ function injectClaudeCodeIdentityIntoSystem(json: Record<string, unknown>): void
4350
const { system } = json;
4451
if (hasClaudeCodeIdentity(system)) return;
4552

46-
const identityBlock = { type: 'text', text: CLAUDE_CODE_IDENTITY };
53+
// 身份行和中和句各自成块,不和客户端提示词拼成一个字符串:拼在一起会共用一个
54+
// 缓存块,网关这边改一个字节,客户端整段 prompt cache 就失效。
55+
const prefixBlocks = [
56+
{ type: 'text', text: CLAUDE_CODE_IDENTITY },
57+
{ type: 'text', text: CLAUDE_CODE_IDENTITY_OVERRIDE },
58+
];
4759
if (system === undefined || system === null) {
48-
json.system = [identityBlock];
60+
json.system = prefixBlocks;
4961
return;
5062
}
5163
if (typeof system === 'string') {
52-
// 转成数组而不是字符串拼接:拼接会让身份行和客户端提示词共用一个缓存块,
53-
// 身份行哪天变一个字节,客户端整段 prompt cache 就失效。
54-
json.system = [identityBlock, { type: 'text', text: system }];
64+
json.system = [...prefixBlocks, { type: 'text', text: system }];
5565
return;
5666
}
5767
if (Array.isArray(system)) {
58-
json.system = [identityBlock, ...system];
68+
json.system = [...prefixBlocks, ...system];
5969
}
6070
}
6171

0 commit comments

Comments
 (0)