fix(codex-models): 非 GPT 模型的 Codex 提示词模板去掉 GPT 身份声明,避免 Antigravity 429

模型目录对所有模型都用 openai.CodexBaseInstructionsForModel 填 instructions_template,
gemini / claude 等因此下发 "You are Codex, a coding agent based on GPT-5."。Codex 把它作为
instructions 发出,Antigravity 对「Codex 提示词 + GPT-5 身份」返回 429 RESOURCE_EXHAUSTED,
并让账号进入限流冷却。

OpenAI GPT 模型保持原模板;其他模型族(含 gpt-oss)去掉 "based on GPT-N" /
"You are GPT-N" 身份声明,提示词其余部分不变。

Refs #7307
This commit is contained in:
HuntercodeT
2026-09-18 14:21:21 +08:00
parent efe9aab1e4
commit cfd2fc779b
2 changed files with 99 additions and 1 deletions
@@ -11,6 +11,7 @@ import (
"net"
"net/http"
"net/url"
"regexp"
"sort"
"strings"
"sync"
@@ -443,7 +444,7 @@ func newConfiguredCodexModelDescriptor(modelID string) configuredCodexModelDescr
Priority: configuredCodexModelPriority,
AdditionalSpeedTiers: []string{},
ServiceTiers: []configuredCodexServiceTier{},
ModelMessages: configuredCodexModelMessages{InstructionsTemplate: openai.CodexBaseInstructionsForModel(modelID)},
ModelMessages: configuredCodexModelMessages{InstructionsTemplate: codexInstructionsTemplateForModel(modelID)},
SupportsReasoningSummaryParameter: true,
DefaultReasoningSummary: "auto",
WebSearchToolType: "text",
@@ -636,6 +637,47 @@ func configuredCodexGPTReasoningLevels(modelID string) []configuredCodexReasonin
return levels
}
// codexGPTIdentityPatterns match the GPT self-identification that the bundled
// Codex prompts open with:
// - "You are Codex, a coding agent based on GPT-5." / "…an agent based on GPT-6." / "You are Codex, based on GPT-5."
// - "You are GPT-5.1 running in the Codex CLI, …"
var codexGPTIdentityPatterns = []struct {
re *regexp.Regexp
repl string
}{
{regexp.MustCompile(`,?\s*based on GPT-\d+(?:\.\d+)?(?:-[A-Za-z0-9]+)*`), ""},
{regexp.MustCompile(`\bYou are GPT-\d+(?:\.\d+)?(?:-[A-Za-z0-9]+)*`), "You are Codex"},
}
// codexInstructionsTemplateForModel returns the Codex base prompt advertised for
// modelID in the models manifest.
//
// OpenAI GPT models keep the bundled prompt verbatim. Every other family the
// manifest can list (Gemini, Claude, DeepSeek, Grok, gpt-oss, custom aliases)
// gets the same prompt without the GPT self-identification: the claim is false
// for those models, and Google's Antigravity backend answers requests whose
// system prompt is the Codex prompt carrying a "GPT-5" identity with
// 429 RESOURCE_EXHAUSTED — which also puts the account into rate-limit
// cooldown. Codex sends this template as `instructions` for the selected model.
func codexInstructionsTemplateForModel(modelID string) string {
base := openai.CodexBaseInstructionsForModel(modelID)
if codexModelKeepsGPTIdentity(modelID) {
return base
}
for _, p := range codexGPTIdentityPatterns {
base = p.re.ReplaceAllString(base, p.repl)
}
return base
}
// codexModelKeepsGPTIdentity reports whether modelID is an OpenAI GPT model
// whose Codex prompt may claim a GPT identity. gpt-oss models are open-weight
// and typically served by non-OpenAI providers, so they do not.
func codexModelKeepsGPTIdentity(modelID string) bool {
return isOpenAICodexGPTModel(modelID) &&
!strings.HasPrefix(canonicalizeOpenAIModelAliasSpelling(modelID), "gpt-oss")
}
func isOpenAICodexGPTModel(modelID string) bool {
normalized := canonicalizeOpenAIModelAliasSpelling(modelID)
if normalized == "" || strings.HasPrefix(normalized, "gpt-image") {
@@ -3703,3 +3703,59 @@ func TestFetchCodexModelsManifestOAuthSharedAcrossGroupsWithIndependentFiltering
require.Equal(t, []string{"model-b"}, got[92])
require.EqualValues(t, 1, calls.Load(), "同一账号两个分组同时请求时只发一次上游请求")
}
// Scenario: 非 OpenAI GPT 模型的 Codex 提示词不声称自己是 GPT。
// Antigravity(Google) 对「Codex 提示词 + GPT-5 身份」直接回 429 RESOURCE_EXHAUSTED。
func TestBuildCodexModelsManifestStripsGPTIdentityForNonGPTModels(t *testing.T) {
t.Parallel()
nonGPT := []string{
"gemini-3-flash", "claude-sonnet-4-6", "deepseek-v4-flash", "grok-4.3",
"gpt-oss-120b-medium", "company-coding-model", "company-codex-alias",
}
body, err := BuildCodexModelsManifest(nonGPT)
require.NoError(t, err)
var manifest struct {
Models []struct {
Slug string `json:"slug"`
ModelMessages struct {
InstructionsTemplate string `json:"instructions_template"`
} `json:"model_messages"`
} `json:"models"`
}
require.NoError(t, json.Unmarshal(body, &manifest))
require.Len(t, manifest.Models, len(nonGPT))
for _, m := range manifest.Models {
tmpl := m.ModelMessages.InstructionsTemplate
require.NotContains(t, tmpl, "GPT-", m.Slug)
require.True(t, strings.HasPrefix(tmpl, "You are Codex"), "%s: %.60q", m.Slug, tmpl)
// Only the identity clause is removed; the rest of the bundled prompt is kept.
base := openai.CodexBaseInstructionsForModel(m.Slug)
require.Less(t, len(base)-len(tmpl), 32, m.Slug)
}
}
// Scenario: OpenAI GPT 模型的 Codex 提示词保持原样。
func TestBuildCodexModelsManifestKeepsGPTIdentityForGPTModels(t *testing.T) {
t.Parallel()
for _, model := range []string{"gpt-5.5", "gpt-6-astra", "gpt-5.2", "gpt-5.3-codex-spark"} {
require.Equal(t, openai.CodexBaseInstructionsForModel(model), codexInstructionsTemplateForModel(model), model)
}
}
// Scenario: 每份内置 Codex 提示词的身份声明都能被识别并去掉。
func TestCodexGPTIdentityPatternsCoverBundledPrompts(t *testing.T) {
t.Parallel()
for _, model := range []string{"gpt-5.1", "gpt-5.2", "gpt-5.5", "gpt-6-astra", "gpt-5-codex"} {
tmpl := openai.CodexBaseInstructionsForModel(model)
require.Contains(t, tmpl, "GPT-", "bundled prompt for %s changed; update this test", model)
for _, p := range codexGPTIdentityPatterns {
tmpl = p.re.ReplaceAllString(tmpl, p.repl)
}
require.NotContains(t, tmpl, "GPT-", model)
require.True(t, strings.HasPrefix(tmpl, "You are Codex"), "%s: %.60q", model, tmpl)
}
}