技术总结|十分钟抓包逆向分析 `Claude-Code`

最近在尝试从 0 写类似 Claude-Code 的工具,实现长任务运行,由于 Claude-Code,想着探究一下 Claude-Code 底层实现。

1. 前置

实现一个 LLM Proxy,监听端口 8888

如下基于 openai 的模型转换为 Anthropic 的协议,具体代码在文章的最后(用 AI 实现)。

python 复制代码
# =====================================================================
# Anthropic (Claude) 兼容 --- POST /api/v1/messages
# 参考:https://docs.anthropic.com/en/api/messages
# =====================================================================

# ----- 请求模型 -----

class ClaudeContentBlock(BaseModel):
    """Claude content block(text / image / tool_use / tool_result)"""
    type: str
    text: Optional[str] = None
    # image
    source: Optional[dict] = None
    # tool_use
    id: Optional[str] = None
    name: Optional[str] = None
    input: Optional[Any] = None
    # tool_result
    tool_use_id: Optional[str] = None
    content: Optional[Any] = None# str 或 list[content block]
    is_error: Optional[bool] = None


class ClaudeMessage(BaseModel):
    """Claude 消息"""
    role: str
    content: str | list[ClaudeContentBlock]


class ClaudeToolInputSchema(BaseModel):
    """Claude tool input_schema"""
    type: str = "object"
    properties: Optional[dict] = None
    required: Optional[list[str]] = None


class ClaudeToolDef(BaseModel):
    """Claude tool 定义"""
    name: str
    description: Optional[str] = None
    input_schema: Optional[ClaudeToolInputSchema | dict] = None


class ClaudeMessagesRequest(BaseModel):
    """Anthropic Messages API 请求格式"""
    model: str
    messages: list[ClaudeMessage]
    max_tokens: int = 4096
    system: Optional[str | list[dict]] = None
    temperature: Optional[float] = None
    top_p: Optional[float] = None
    top_k: Optional[int] = None
    stop_sequences: Optional[list[str]] = None
    stream: Optional[bool] = False
    tools: Optional[list[ClaudeToolDef]] = None
    tool_choice: Optional[dict] = None
    metadata: Optional[dict] = None


# ----- 响应模型 -----

class ClaudeTextBlock(BaseModel):
    """Claude 响应 text block"""
    type: str = "text"
    text: str


class ClaudeToolUseBlock(BaseModel):
    """Claude 响应 tool_use block"""
    type: str = "tool_use"
    id: str
    name: str
    input: dict


class ClaudeUsage(BaseModel):
    """Claude token 用量"""
    input_tokens: int = 0
    output_tokens: int = 0


class ClaudeMessagesResponse(BaseModel):
    """Anthropic Messages API 响应格式"""
    id: str = Field(default_factory=lambda: f"msg_{uuid.uuid4().hex[:24]}")
    type: str = "message"
    role: str = "assistant"
    content: list[dict]
    model: str = ""
    stop_reason: Optional[str] = "end_turn"
    stop_sequence: Optional[str] = None
    usage: ClaudeUsage = ClaudeUsage()

...

安装 mitmproxy 启动抓包

perl 复制代码
mitmproxy --mode reverse:http://localhost:8888 -p 8889

将 proxy 设置 8889,大模型调用到 8889 端口即可

安装 cc-switch

bash 复制代码
https://github.com/farion1231/cc-switch

2. 开始 Claude-Code

先清空会话或者开始一个新项目开始对话

对话原文:

bash 复制代码
> 你是什么模型

⏺ 我是 Claude Sonnet 4.6,由 Anthropic 开发的人工智能模型。我是 Claude Code 的底层模型,这是一个专门用于软件工程任务的交互式代理。

  我现在正在帮助您处理 /Volumes/my/autosearch_agno 目录下的项目。从 git 状态来看,这是一个新项目,有很多未跟踪的文件(README.md、analysis.ipynb、prepare.py、program.md 等)。

  有什么可以帮助您的吗?

> 你有什么技能

⏺ 我是 Claude Code,Anthropic 官方的 Claude CLI 工具,专门用于软件工程任务。我的主要技能包括:

  代码相关技能

  - 代码编写和修改:创建、编辑、重构代码
  - 代码审查和优化:分析代码质量,发现并修复问题
  - 调试和故障排除:定位和解决代码中的问题
  - 代码解释:分析现有代码并解释其工作原理

  开发工具集成

  - 文件操作:读取、写入、搜索文件
  - Git 操作:查看状态、提交、分支管理等
  - Shell 命令执行:运行系统命令和终端操作
  - 包管理:处理依赖关系和项目配置

  项目管理和探索

  - 任务管理:使用 TaskCreate 工具规划和跟踪工作
  - 代码库探索:使用 Agent 工具进行深度代码库研究
  - 模式识别:发现代码中的模式和惯例

  自动化工具

  - 技能调用:使用 /simplify 等预定义技能
  - 配置管理:使用 /update-config 配置 Claude Code 设置
  - 循环任务:使用 /loop 设置定期执行的任务

  特定领域

  - Claude API 开发:构建使用 Claude API 或 Anthropic SDK 的应用
  - 安全测试:在授权范围内进行安全测试和CTF挑战
  - 教育指导:帮助学习编程概念和技术

  我可以帮助您处理当前项目中的各种任务,比如:
  - 分析代码结构和依赖关系
  - 实现新功能或修复 bug
  - 设置开发环境
  - 编写测试
  - 优化性能

  您希望我从什么开始帮助您?

> 分析当前 readme.md,用中文翻译一份 readme_zh.md

⏺ 我来分析当前的 README.md 文件并创建中文翻译版本。

  首先,让我读取当前的 README.md 文件。  

....

第一个请求

system提示词

  • x-anthropic-billing-header: cc_version=2.1.81.dd2; cc_entrypoint=cli; cch=6b09e;: 请求的元信息
    • 计费标识
    • 客户端版本标识
    • 入口来源
    • cch 应该是用于追踪会话的
  • Generate a concise, sentence-case title (3-7 words) ...: 中文翻译大概是 请为这次编程会话生成一个简洁的标题,长度 3 到 7 个词,概括本次会话的主要主题或目标。标题应该足够清晰,让用户在会话列表中一眼识别。使用 sentence case,也就是只有第一个词和专有名词首字母大写。,做这个的目的是重新进入 claude 可以看到你上次对话的标题,方便选择。

第一个回复

  • 对应的 SSE 流式输出,这里就是回复的信息,可以忽略。

3、加载 skills 和前置声明

system-reminder

  • <system-reminder>...</system-reminder> 这里表示本地有哪些 skill 可以使用,比如系统自带的:
    • update-config: Use this skill to configure the Claude Code harness via settings.json. Automated behaviors ("from now on when X", "each time X", "whenever X", "before/after X") require hooks configured in settings.json --- the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions ("allow X", "add permission", "move permission to"), env vars ("set X=Y"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: "allow npm commands", "add bash permission to global settings", "move permission to user settings", "set DEBUG=true", "when claude stops show X". For simple settings like theme/model, use Config tool.
    • simplify: Review changed code for reuse, quality, and efficiency, then fix any issues found.
    • loop: Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo, defaults to 10m). When the user wants to set up a recurring task, poll for status, or run something repeatedly on an interval (e.g. "check the deploy every 5 minutes", "keep running /babysit-prs"). Do NOT invoke for one-off tasks.
    • claude-api: Build apps with the Claude API or Anthropic SDK.\nTRIGGER when: code imports 'anthropic', '@anthropic-ai/sdk', or 'claude_agent_sdk', or user asks to use Claude API, Anthropic SDKs, or Agent SDK.\nDO NOT TRIGGER when: code imports 'openai', other AI SDK, general programming, or ML/data-science tasks.
  • 中文解释:
    • update-config: 通过 settings.json 自动修改 Claude Code 的运行配置,主要是用于权限,环境变量和Hooks 等
    • simplify: 用于代码 review 的,让代码更加简洁
    • loop: 循环执行 / 定时重复执行的任务,可以通过 Claude Code 选择当前 skill 完成非一次性的任务
    • claude-api: 主要用 Claude API 或 Anthropic SDK 来开发应用

IMPORTANT 和 最前置的声明

  • As you answer the user's questions, you can use the following context:\n# currentDate\nToday's date is 2026-03-24.\n\nThis context may or may not be relevant to your tasks. You should not respond to this context unless it is highly relevant to your task. 通过这段话来补充当前时间和背景。
  • IMPORTANT 主要是强调用户输入。

System Prompt

如上是 Claude Code 的系统提示词,通过这些指令,我们可以看到 Claude Code 的核心运行逻辑和行为边界,如果包括如下几个声明。

身份与安全边界

  • 你是 Claude Code,Anthropic 官方 CLI
  • 你是一个帮助用户完成软件工程任务的交互式代理
  • 只允许帮助处理:
    • 授权安全测试
    • 防御性安全任务
    • CTF
    • 教学场景
  • 明确拒绝:
    • 破坏性攻击
    • DoS
    • 大规模攻击
    • 供应链攻击
    • 恶意规避检测

URL 与外部资源

  • 不要随意编造 URL
  • 只有在确信 URL 有助于编程任务时,才能提供
  • 如果是用户提供的 URL,可以使用

与用户沟通

  • 所有非工具输出都会直接显示给用户
  • 可使用 Markdown
  • 如果工具被权限模式拦截,用户可能会批准或拒绝
  • 如果被拒绝,不要无脑重复同一个调用
  • 如果需要用户自己执行命令,可以建议他们输入 ! <command>

执行任务的原则

  • 用户主要是在让你处理软件工程问题
  • 回答要结合当前工作目录和代码库上下文
  • 不要在没读过代码的情况下就乱提修改建议
  • 除非必要,不要新建文件
  • 不要过度设计
  • 只做用户明确要求或明显必要的事
  • 不要顺手加功能、重构、注释、兼容层、特性开关等多余内容
  • 如果发现你写出了不安全代码,要立即修正

风险操作

  • 本地、可回退的小修改通常可以直接做
  • 对于以下行为要谨慎,通常要先确认:
    • 删除文件
    • 强制推送
    • 覆盖未提交修改
    • 修改共享基础设施
    • 往第三方平台发内容
    • 修改 CI/CD
  • 核心原则:
    • 先确认范围,再动手
    • 不要用破坏性手段偷懒

工具使用规则

  • 有专用工具时,不要乱用 Bash 替代
  • 例如:
    • 读文件用 Read
    • 改文件用 Edit
    • 新建文件用 Write
    • 查文件用 Glob
    • 搜内容用 Grep
  • 复杂任务要善用任务列表和子代理
  • 能并行的工具调用尽量并行

风格要求

  • 不要乱用 emoji
  • 回复要简短直接
  • 引用代码时尽量给出 file_path:line_number
  • 不要写很多铺垫废话

输出效率

  • 直奔主题
  • 优先给结果或动作
  • 只在这些情况多说一点:
    • 需要用户做决定
    • 自然进度更新
    • 出现阻塞或错误

自动记忆

  • Claude Code 有持久化 memory 目录
  • 需要把稳定模式、用户偏好、架构信息、已验证经验写进去
  • 不要写临时状态、未验证信息或当前会话的一次性细节

环境信息

环境部分大意是:

  • 当前工作目录:/Volumes/my/autosearch_agno
  • 这是一个 Git 仓库
  • 平台:darwin
  • shell:zsh
  • 模型:Claude Sonnet 4.6
  • 当前分支:main
  • Git 状态里有若干未跟踪文件

4、tools

以上都是一些工具的使用声明和参数,这里有很多就没贴了,大体就是:

  • 每个工具是做什么的
  • 什么时候该用
  • 什么时候不该用
  • 参数结构是什么
  • 执行时有哪些限制和最佳实践

工具详解

(1) Agent --- 启动子代理

用途:启动新的代理(子进程)来自主处理复杂的多步骤任务。

可用代理类型

代理类型 说明 可用工具
general-purpose 通用代理,用于研究复杂问题、搜索代码、执行多步骤任务 全部工具 (*)
statusline-setup 配置用户的 Claude Code 状态行设置 Read, Edit
Explore 快速探索代码库,支持 quick/medium/very thorough 三级 除 Agent, ExitPlanMode, Edit, Write, NotebookEdit 外的所有工具
Plan 软件架构师代理,设计实现计划 除 Agent, ExitPlanMode, Edit, Write, NotebookEdit 外的所有工具
claude-code-guide 回答关于 Claude Code CLI / Agent SDK / Claude API 的问题 Glob, Grep, Read, WebFetch, WebSearch

参数(input_schema)

css 复制代码
{
  "properties": {
    "description": {
      "description": "A short (3-5 word) description of the task",
      "type": "string"
    },
    "prompt": {
      "description": "The task for the agent to perform",
      "type": "string"
    },
    "subagent_type": {
      "description": "The type of specialized agent to use for this task",
      "type": "string"
    },
    "model": {
      "description": "Optional model override (sonnet/opus/haiku)",
      "type": "string",
      "enum": ["sonnet", "opus", "haiku"]
    },
    "run_in_background": {
      "description": "Set to true to run this agent in the background",
      "type": "boolean"
    },
    "isolation": {
      "description": "Isolation mode. 'worktree' creates a temporary git worktree",
      "type": "string",
      "enum": ["worktree"]
    }
  }
}

关键使用规则

  • 尽量并发启动多个独立的 Agent(单条消息中包含多个 tool use)
  • 前台(默认):需要结果才能继续时使用;后台:有独立并行工作时使用
  • SendMessage 继续已有 Agent 的对话
  • Agent 的返回结果对用户不可见,需手动汇总转述

(2) TaskOutput --- 获取任务输出

用途:获取正在运行或已完成的后台任务(后台 shell、代理、远程会话)的输出。

参数(input_schema)

json 复制代码
{
  "properties": {
    "task_id": {
      "description": "The task ID to get output from",
      "type": "string"
    },
    "block": {
      "description": "Whether to wait for completion",
      "default": true,
      "type": "boolean"
    },
    "timeout": {
      "description": "Max wait time in ms",
      "default": 30000,
      "type": "number",
      "minimum": 0,
      "maximum": 600000
    }
  },
"required": ["task_id", "block", "timeout"]
}

关键使用规则

  • block=true(默认)等待任务完成
  • block=false 非阻塞检查当前状态
  • 任务 ID 可通过 /tasks 命令获取

(3) Bash --- 执行 Shell 命令

用途:执行 bash 命令并返回输出。工作目录在命令间持久化,但 shell 状态不持久化。

参数(input_schema)

json 复制代码
{
  "properties": {
    "command": {
      "description": "The command to execute",
      "type": "string"
    },
    "timeout": {
      "description": "Optional timeout in milliseconds (max 600000)",
      "type": "number"
    },
    "description": {
      "description": "Clear, concise description of what this command does",
      "type": "string"
    },
    "run_in_background": {
      "description": "Set to true to run in background. Use TaskOutput to read output later.",
      "type": "boolean"
    },
    "dangerouslyDisableSandbox": {
      "description": "Override sandbox mode and run without sandboxing",
      "type": "boolean"
    }
  },
"required": ["command"]
}

关键使用规则

  • ⚠️ 避免使用 find/grep/cat/head/tail/sed/awk/echo,应使用对应专用工具(Glob/Grep/Read/Edit/Write)
  • 独立命令并行调用(单条消息多个 Bash tool call);依赖命令用 && 串联
  • 默认超时 120s(2 分钟),最大 600s(10 分钟)
  • 尽量使用绝对路径,避免 cd
  • Git 安全协议:不更新 git config、不执行破坏性命令、不跳过 hooks、不 force push main/master
  • 创建 commit 时:先 git status + git diff + git log,再起草 commit message
  • 创建 PR 时:用 gh pr create,包含 Summary 和 Test plan

(4) Glob --- 文件模式匹配

用途:快速文件模式匹配工具,支持任意代码库大小,返回按修改时间排序的匹配文件路径。

参数(input_schema)

json 复制代码
{
  "properties": {
    "pattern": {
      "description": "The glob pattern to match files against",
      "type": "string"
    },
    "path": {
      "description": "The directory to search in (omit for current working directory)",
      "type": "string"
    }
  },
  "required": ["pattern"]
}

关键使用规则

  • 支持 **/*.jssrc/**/*.ts 等 glob 模式
  • 开放式搜索需要多轮时,改用 Agent 工具
  • 可并行发起多个 Glob 搜索

(5) Grep --- 内容搜索

用途:基于 ripgrep 的强大搜索工具,支持完整正则语法。

参数(input_schema)

json 复制代码
{
  "properties": {
    "pattern": {
      "description": "The regex pattern to search for in files",
      "type": "string"
    },
    "path": {
      "description": "The directory to search in (omit for current working directory)",
      "type": "string"
    },
    "include": {
      "description": "Glob pattern(s) to filter files (e.g., '*.js', '**/*.tsx')",
      "type": "array",
      "items": { "type": "string" }
    },
    "type": {
      "description": "Filter files by language/type (e.g., 'js', 'py', 'rust')",
      "type": "array",
      "items": { "type": "string" }
    },
    "output": {
      "description": "Output mode: 'content' | 'files_with_matches' (default) | 'count'",
      "type": "string",
      "enum": ["content", "files_with_matches", "count"]
    },
    "multiline": {
      "description": "Enable multiline matching (default false)",
      "type": "boolean"
    }
  },
"required": ["pattern"]
}

关键使用规则

  • ⚠️ 永远不要 通过 Bash 调用 greprg,始终使用 Grep 工具
  • 使用 ripgrep 语法(非传统 grep),字面量花括号需转义
  • 默认输出模式为 files_with_matches(仅文件路径)
  • 跨行匹配需设置 multiline: true

(6) Read --- 读取文件

用途:从本地文件系统读取文件,支持文本、图片、PDF、Jupyter Notebook。

参数(input_schema)

json 复制代码
{
  "properties": {
    "file_path": {
      "description": "The absolute path to the file to read",
      "type": "string"
    },
    "offset": {
      "description": "The line number to start reading from (for large files)",
      "type": "number"
    },
    "limit": {
      "description": "The number of lines to read (for large files)",
      "type": "number"
    },
    "pages": {
      "description": "Page range for PDF files (e.g., '1-5', '3', '10-20'). Max 20 pages/request.",
      "type": "string"
    }
  },
"required": ["file_path"]
}

关键使用规则

  • 路径必须是绝对路径
  • 默认读取前 2000 行
  • 支持读取图片(PNG/JPG 等,多模态展示)
  • PDF 超过 10 页必须 指定 pages 参数
  • 只能读文件不能读目录(读目录用 Bash 的 ls
  • 可并行读取多个文件

(7) Edit --- 编辑文件

用途:在文件中执行精确的字符串替换。

参数(input_schema)

json 复制代码
{
  "properties": {
    "file_path": {
      "description": "The absolute path to the file to modify",
      "type": "string"
    },
    "old_string": {
      "description": "The text to replace",
      "type": "string"
    },
    "new_string": {
      "description": "The text to replace it with (must be different from old_string)",
      "type": "string"
    },
    "replace_all": {
      "description": "Replace all occurrences of old_string (default false)",
      "default": false,
      "type": "boolean"
    }
  },
"required": ["file_path", "old_string", "new_string"]
}

关键使用规则

  • ⚠️ 编辑前必须先用 Read 工具读取过该文件
  • 如果 old_string 在文件中不唯一,编辑会失败(需提供更多上下文或用 replace_all
  • 保持精确的缩进(tab/空格)
  • 优先编辑现有文件,不要随意创建新文件
  • 不要添加 emoji(除非用户明确要求)

(8) Write --- 写入文件

用途:向本地文件系统写入文件(会覆盖已有文件)。

参数(input_schema)

css 复制代码
{
  "properties": {
    "file_path": {
      "description": "The absolute path to the file to write (must be absolute)",
      "type": "string"
    },
    "content": {
      "description": "The content to write to the file",
      "type": "string"
    }
  },
  "required": ["file_path", "content"]
}

关键使用规则

  • 已有文件必须先 Read 再 Write
  • 优先用 Edit 修改现有文件(只发送 diff),Write 仅用于创建新文件或完全重写
  • 不要 创建 *.md 或 README 文件(除非用户明确要求)
  • 不要添加 emoji(除非用户明确要求)

(9) NotebookEdit --- 编辑 Jupyter Notebook

用途 :替换/插入/删除 Jupyter Notebook(.ipynb)中特定单元格的内容。

参数(input_schema)

json 复制代码
{
  "properties": {
    "notebook_path": {
      "description": "The absolute path to the Jupyter notebook file (must be absolute)",
      "type": "string"
    },
    "cell_number": {
      "description": "The 0-indexed number of the cell to edit/insert/delete",
      "type": "integer"
    },
    "new_source": {
      "description": "The new source content (required for replace, ignored for delete)",
      "type": "string"
    },
    "edit_mode": {
      "description": "Operation mode: 'replace' (default), 'insert', or 'delete'",
      "type": "string",
      "enum": ["replace", "insert", "delete"]
    }
  },
"required": ["notebook_path", "cell_number", "edit_mode"]
}

(10) WebFetch --- 抓取网页内容

用途:从 URL 获取内容,转换 HTML 为 markdown,并用小型快速模型处理内容。

参数(input_schema)

json 复制代码
{
  "properties": {
    "url": {
      "description": "The URL to fetch content from",
      "type": "string",
      "format": "uri"
    },
    "prompt": {
      "description": "The prompt to run on the fetched content",
      "type": "string"
    }
  },
"required": ["url", "prompt"]
}

关键使用规则

  • ⚠️ 无法访问需要认证的 URL(Google Docs、Confluence、Jira、GitHub 等),应使用对应 MCP 工具
  • HTTP 自动升级为 HTTPS
  • 有 15 分钟缓存
  • GitHub URL 优先使用 gh CLI
  • 如有 MCP 提供的 web fetch 工具,优先使用 MCP 版本

(11) WebSearch --- 网页搜索

用途:搜索网页并使用结果来辅助回答,提供超出知识截止日期的最新信息。

参数(input_schema)

json 复制代码
{
  "properties": {
    "query": {
      "description": "The search query",
      "type": "string"
    },
    "domain_filter": {
      "description": "Domain filtering to include or block specific websites",
      "type": "array",
      "items": { "type": "string" }
    }
  },
"required": ["query"]
}

关键使用规则

  • ⚠️ 强制要求 :回答后必须附带 Sources: 部分,列出所有相关 URL
  • 支持域名过滤
  • 搜索查询中使用正确的年份

(12) AskUserQuestion --- 向用户提问

用途:在执行过程中向用户提问,收集偏好、澄清指令、获取实现选择的决策。

参数(input_schema)

css 复制代码
{
  "properties": {
    "questions": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "text": {
            "description": "The question text to show the user",
            "type": "string"
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label": { "type": "string" },
                "value": { "type": "string" },
                "preview": {
                  "description": "Optional markdown preview for visual comparison",
                  "type": "string"
                }
              },
              "required": ["label", "value"]
            }
          },
          "multiSelect": {
            "description": "Allow multiple answers (default false)",
            "type": "boolean"
          }
        },
        "required": ["id", "text", "options"]
      }
    }
  },
"required": ["questions"]
}

关键使用规则

  • 用户始终可以选择 "Other" 提供自定义输入
  • 推荐选项放第一个,末尾加 (Recommended)
  • preview 字段仅支持单选(非 multiSelect),用于展示 ASCII 布局、代码片段等
  • 在 Plan 模式中,不要 用此工具询问"计划是否 OK",应使用 ExitPlanMode

(13) Skill --- 调用技能

用途:调用已注册的技能(Skills)来扩展能力。

可用技能

技能名称 触发场景
update-config 配置 settings.json(hooks、权限、环境变量等)
simplify 审查已修改代码的复用性、质量、效率
loop 在循环间隔上运行提示或斜杠命令(如 /loop 5m /foo
claude-api 构建使用 Claude API / Anthropic SDK 的应用

(14) TaskCreate --- 创建任务

用途 :创建后台任务(如后台 shell 命令、异步代理等)。通常与 TaskOutput 配合使用。


(15) TaskUpdate --- 更新任务

用途:更新已有任务的状态或参数。


(16) TaskList --- 列出任务

用途 :列出所有当前任务及其状态。等同于 /tasks 命令。


(17) EnterPlanMode --- 进入计划模式

用途 :进入计划模式,开始为代码实现任务编写计划。与 ExitPlanMode 配对使用。

相关工具 --- ExitPlanMode

json 复制代码
{
  "properties": {
    "allowedPrompts": {
      "description": "Prompt-based permissions needed to implement the plan",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "tool": {
            "description": "The tool this prompt applies to",
            "type": "string",
            "enum": ["Bash"]
          },
          "prompt": {
            "description": "Semantic description of the action (e.g., 'run tests')",
            "type": "string"
          }
        },
        "required": ["tool", "prompt"]
      }
    }
  },
"required": ["allowedPrompts"]
}

关键使用规则

  • 仅在需要编写代码的实现计划时使用(研究任务不用)
  • 计划内容写入计划文件,ExitPlanMode 从文件读取
  • 不要用 AskUserQuestion 代替 ExitPlanMode 来请求审批

(18) EnterWorktree --- 进入工作树

用途:创建临时 git worktree,在隔离的仓库副本中工作。


(19) CronCreate --- 创建定时任务

用途:创建定时/循环执行的任务。


(20) CronDelete --- 删除定时任务

用途:删除已创建的定时任务。


(21) CronList --- 列出定时任务

用途:列出所有已创建的定时任务。


(22) TaskStop --- 停止任务

用途:停止正在运行的后台任务。

参数(input_schema)

json 复制代码
{
  "properties": {
    "task_id": {
      "description": "The ID of the background task to stop",
      "type": "string"
    },
    "shell_id": {
      "description": "Deprecated: use task_id instead",
      "type": "string"
    }
  }
}

工具使用优先级速查

需求 ❌ 不要用 ✅ 应该用
搜索文件 find, ls (Bash) Glob
搜索内容 grep, rg (Bash) Grep
读取文件 cat, head, tail (Bash) Read
编辑文件 sed, awk (Bash) Edit
写入文件 echo >, cat <<EOF (Bash) Write
输出文本 echo, printf (Bash) 直接输出文本

5、Claude-Code 识别用户意图

在整个任务完成以后,Claude-Code 会预测用户下一步的行为,所以会多发一条请求,大体的就是:


【建议模式:建议用户接下来可能会自然地在 Claude Code 里输入什么。】

首先: 查看用户最近的消息和最初的请求。

你的工作是预测用户自己会输入什么,而不是你觉得他们"应该"做什么。

判断标准:

用户看到后会不会觉得: "对,我刚刚正准备输入这个。"

示例

  • 用户说"修复这个 bug 并运行测试",bug 已修好 → 运行测试
  • 代码写完后 → 试一下
  • Claude 提供了几个选项 → 根据上下文,建议用户最可能会选的那个
  • Claude 询问是否继续 → / 继续
  • 任务完成后,明显的下一步 → 提交这个 / 推送上去
  • 出现错误或误解之后 → 保持沉默(让用户自己评估或纠正)

信息要具体

  • "运行测试""继续" 更好。

绝对不要建议

  • 评价性的话(如"看起来不错"、"谢谢")
  • 提问(如"那......怎么办?")
  • Claude 口吻的话(如"让我来......"、"我会......"、"这是......")
  • 用户没有提过的新想法
  • 多句话

如果根据用户的话,下一步并不明显,就保持沉默

格式要求

  • 2 到 12 个词
  • 匹配用户的说话风格
  • 或者什么都不输出

回复时只输出建议内容,不要加引号,也不要解释。


6、Claude-Code 的设计哲学给我们什么启示?

可以看到 Claude-Code 的提示词中没有用什么 RAG 或者复杂的工具,就是日常的工具的组合,搜索就 Glob + Grep,极简的哲学和 Unix 的 一切皆为文件 的思考方式感觉一样。

底层(模型)做的更强大,应用层应用要做更薄。对应到 Harness Engineering,就是所有的实现交给模型+工具,工程师只要设计目标,约束和验证方案即可。

参考

  1. www.cnblogs.com/noonafter/p...
  2. juejin.cn/post/761420...
  3. OpenAI 的模型兼容 Anthropic 的 Proxy 代码:
python 复制代码
# =====================================================================
# Anthropic (Claude) 兼容 --- POST /api/v1/messages
# 参考:https://docs.anthropic.com/en/api/messages
# =====================================================================

# ----- 请求模型 -----

class ClaudeContentBlock(BaseModel):
    """Claude content block(text / image / tool_use / tool_result)"""
    type: str
    text: Optional[str] = None
    # image
    source: Optional[dict] = None
    # tool_use
    id: Optional[str] = None
    name: Optional[str] = None
    input: Optional[Any] = None
    # tool_result
    tool_use_id: Optional[str] = None
    content: Optional[Any] = None# str 或 list[content block]
    is_error: Optional[bool] = None


class ClaudeMessage(BaseModel):
    """Claude 消息"""
    role: str
    content: str | list[ClaudeContentBlock]


class ClaudeToolInputSchema(BaseModel):
    """Claude tool input_schema"""
    type: str = "object"
    properties: Optional[dict] = None
    required: Optional[list[str]] = None


class ClaudeToolDef(BaseModel):
    """Claude tool 定义"""
    name: str
    description: Optional[str] = None
    input_schema: Optional[ClaudeToolInputSchema | dict] = None


class ClaudeMessagesRequest(BaseModel):
    """Anthropic Messages API 请求格式"""
    model: str
    messages: list[ClaudeMessage]
    max_tokens: int = 4096
    system: Optional[str | list[dict]] = None
    temperature: Optional[float] = None
    top_p: Optional[float] = None
    top_k: Optional[int] = None
    stop_sequences: Optional[list[str]] = None
    stream: Optional[bool] = False
    tools: Optional[list[ClaudeToolDef]] = None
    tool_choice: Optional[dict] = None
    metadata: Optional[dict] = None


# ----- 响应模型 -----

class ClaudeTextBlock(BaseModel):
    """Claude 响应 text block"""
    type: str = "text"
    text: str


class ClaudeToolUseBlock(BaseModel):
    """Claude 响应 tool_use block"""
    type: str = "tool_use"
    id: str
    name: str
    input: dict


class ClaudeUsage(BaseModel):
    """Claude token 用量"""
    input_tokens: int = 0
    output_tokens: int = 0


class ClaudeMessagesResponse(BaseModel):
    """Anthropic Messages API 响应格式"""
    id: str = Field(default_factory=lambda: f"msg_{uuid.uuid4().hex[:24]}")
    type: str = "message"
    role: str = "assistant"
    content: list[dict]
    model: str = ""
    stop_reason: Optional[str] = "end_turn"
    stop_sequence: Optional[str] = None
    usage: ClaudeUsage = ClaudeUsage()

...

# ----- 格式转换 -----

def _claude_messages_to_openai(
    messages: list[ClaudeMessage],
    system: str | list[dict] | None = None,
) -> list[dict]:
    """将 Claude 格式的 messages + system 转为 OpenAI 格式的 messages。"""
    result: list[dict] = []

    # system 提升为首条 system message
    if system:
        if isinstance(system, str):
            result.append({"role": "system", "content": system})
        elif isinstance(system, list):
            # Claude 的 system 可以是 content blocks
            text_parts = [b.get("text", "") for b in system if b.get("type") == "text"]
            if text_parts:
                result.append({"role": "system", "content": "\n".join(text_parts)})

    for m in messages:
        role = m.role

        # content 为纯字符串
        if isinstance(m.content, str):
            if role == "user":
                result.append({"role": "user", "content": m.content})
            elif role == "assistant":
                result.append({"role": "assistant", "content": m.content})
            continue

        # content 为 block 列表
        if role == "user":
            # 合并 text blocks;tool_result 转为 OpenAI tool message
            text_parts: list[str] = []
            for block in m.content:
                if block.type == "text":
                    text_parts.append(block.text or"")
                elif block.type == "tool_result":
                    # 先 flush 累积的 text
                    if text_parts:
                        result.append({"role": "user", "content": "\n".join(text_parts)})
                        text_parts = []
                    tool_content = block.content
                    if isinstance(tool_content, list):
                        tool_content = "\n".join(
                            b.get("text", "") if isinstance(b, dict) else str(b)
                            for b in tool_content
                        )
                    elifnot isinstance(tool_content, str):
                        tool_content = json.dumps(tool_content, ensure_ascii=False) if tool_content else""
                    result.append({
                        "role": "tool",
                        "tool_call_id": block.tool_use_id or"",
                        "content": tool_content,
                    })
            if text_parts:
                result.append({"role": "user", "content": "\n".join(text_parts)})

        elif role == "assistant":
            # 提取 text + tool_use
            text_parts = []
            tool_calls: list[dict] = []
            for block in m.content:
                if block.type == "text":
                    text_parts.append(block.text or"")
                elif block.type == "tool_use":
                    tool_calls.append({
                        "id": block.id orf"call_{uuid.uuid4().hex[:8]}",
                        "type": "function",
                        "function": {
                            "name": block.name or"",
                            "arguments": json.dumps(block.input, ensure_ascii=False) if isinstance(block.input, dict) else str(block.input or"{}"),
                        },
                    })
            msg: dict = {"role": "assistant", "content": "\n".join(text_parts) orNone}
            if tool_calls:
                msg["tool_calls"] = tool_calls
            result.append(msg)

    return result


def _claude_tools_to_openai(tools: list[ClaudeToolDef] | None) -> list[dict] | None:
    """将 Claude tools(input_schema)转为 OpenAI tools(parameters)。"""
    ifnot tools:
        returnNone
    return [
        {
            "type": "function",
            "function": {
                "name": t.name,
                "description": t.description or"",
                "parameters": (
                    t.input_schema.model_dump(exclude_none=True)
                    if isinstance(t.input_schema, ClaudeToolInputSchema)
                    else t.input_schema or {"type": "object"}
                ),
            },
        }
        for t in tools
    ]


def _claude_tool_choice_to_openai(tool_choice: dict | None) -> str | dict | None:
    """将 Claude tool_choice 转为 OpenAI tool_choice。"""
    ifnot tool_choice:
        returnNone
    tc_type = tool_choice.get("type", "auto")
    if tc_type == "auto":
        return"auto"
    if tc_type == "any":
        return"required"
    if tc_type == "tool":
        return {"type": "function", "function": {"name": tool_choice.get("name", "")}}
    if tc_type == "none":
        return"none"
    return"auto"


def _openai_finish_reason_to_claude(finish_reason: str) -> str:
    """将 OpenAI finish_reason 映射为 Claude stop_reason。"""
    mapping = {
        "stop": "end_turn",
        "length": "max_tokens",
        "tool_calls": "tool_use",
        "content_filter": "end_turn",
    }
    return mapping.get(finish_reason, "end_turn")


# ----- 接口 -----

asyncdef _claude_stream_generator(req: ClaudeMessagesRequest):
    """Anthropic SSE 流式生成器 --- 遵循 Anthropic streaming events 协议。

    事件序列:
      message_start → content_block_start → content_block_delta* → content_block_stop
      → ... (更多 block) → message_delta → message_stop
    """
    provider = _get_provider()
    model = T_OPENAI_MODEL

    messages = _claude_messages_to_openai(req.messages, req.system)
    tools = _claude_tools_to_openai(req.tools)
    tool_choice = _claude_tool_choice_to_openai(req.tool_choice)

    msg_id = f"msg_{uuid.uuid4().hex[:24]}"

    kwargs: dict[str, Any] = {
        "model": model,
        "messages": provider._sanitize_empty_content(messages),
        "max_tokens": max(1, req.max_tokens),
        "temperature": req.temperature or0.7,
        "stream": True,
    }
    if tools:
        kwargs["tools"] = tools
        kwargs["tool_choice"] = tool_choice if tool_choice isnotNoneelse"auto"

    def _sse(event: str, data: dict) -> str:
        returnf"event: {event}\ndata: {json.dumps(data, ensure_ascii=False)}\n\n"

    try:
        # message_start
        yield _sse("message_start", {
            "type": "message_start",
            "message": {
                "id": msg_id,
                "type": "message",
                "role": "assistant",
                "content": [],
                "model": req.model or model,
                "stop_reason": None,
                "stop_sequence": None,
                "usage": {"input_tokens": 0, "output_tokens": 0},
            },
        })

        # 跟踪当前 content block 状态
        block_index = 0
        block_open = False# 是否已经 open 了一个 text block
        has_tool_call_blocks: list[dict] = []  # 已开启的 tool_use blocks
        tool_args_buffers: dict[int, str] = {}  # 缓存各 tool_call 的 arguments 片段

        stream = await provider._client.chat.completions.create(**kwargs)
        finish_reason = "stop"

        asyncfor chunk in stream:
            ifnot chunk.choices:
                continue
            delta = chunk.choices[0].delta
            chunk_finish = chunk.choices[0].finish_reason
            if chunk_finish:
                finish_reason = chunk_finish

            # ── 文本 delta ──
            if delta.content isnotNoneand delta.content != "":
                ifnot block_open:
                    yield _sse("content_block_start", {
                        "type": "content_block_start",
                        "index": block_index,
                        "content_block": {"type": "text", "text": ""},
                    })
                    block_open = True
                yield _sse("content_block_delta", {
                    "type": "content_block_delta",
                    "index": block_index,
                    "delta": {"type": "text_delta", "text": delta.content},
                })

            # ── tool_calls delta ──
            if delta.tool_calls:
                # 先关闭 text block
                if block_open:
                    yield _sse("content_block_stop", {
                        "type": "content_block_stop",
                        "index": block_index,
                    })
                    block_index += 1
                    block_open = False

                for tc in delta.tool_calls:
                    tc_idx = tc.index
                    if tc_idx notin tool_args_buffers:
                        # 新 tool_use block
                        tool_args_buffers[tc_idx] = ""
                        tool_id = tc.id orf"toolu_{uuid.uuid4().hex[:12]}"
                        tool_name = tc.function.name if tc.function and tc.function.name else""
                        yield _sse("content_block_start", {
                            "type": "content_block_start",
                            "index": block_index + tc_idx,
                            "content_block": {
                                "type": "tool_use",
                                "id": tool_id,
                                "name": tool_name,
                                "input": {},
                            },
                        })
                        has_tool_call_blocks.append({"index": block_index + tc_idx, "id": tool_id, "name": tool_name})
                    # 累积 arguments
                    if tc.function and tc.function.arguments:
                        tool_args_buffers[tc_idx] += tc.function.arguments
                        yield _sse("content_block_delta", {
                            "type": "content_block_delta",
                            "index": block_index + tc_idx,
                            "delta": {
                                "type": "input_json_delta",
                                "partial_json": tc.function.arguments,
                            },
                        })

        # ── 关闭所有未关闭的 blocks ──
        if block_open:
            yield _sse("content_block_stop", {
                "type": "content_block_stop",
                "index": block_index,
            })
        for tb in has_tool_call_blocks:
            yield _sse("content_block_stop", {
                "type": "content_block_stop",
                "index": tb["index"],
            })

        # message_delta + message_stop
        stop_reason = _openai_finish_reason_to_claude(finish_reason)
        if has_tool_call_blocks:
            stop_reason = "tool_use"
        yield _sse("message_delta", {
            "type": "message_delta",
            "delta": {"stop_reason": stop_reason, "stop_sequence": None},
            "usage": {"output_tokens": 0},
        })
        yield _sse("message_stop", {"type": "message_stop"})

    except Exception as e:
        logger.error(f"Claude 流式输出异常: {e}", exc_info=True)
        yield _sse("error", {
            "type": "error",
            "error": {"type": "api_error", "message": f"AI 处理失败: {str(e)}"},
        })


@router.post("/messages")
asyncdef claude_messages(req: ClaudeMessagesRequest):
    """Anthropic Claude 兼容的 Messages 接口

    接收 Claude /v1/messages 格式的请求,内部转换为 OpenAI 格式调用 Provider,
    再将响应转换回 Claude Messages 格式返回。
    支持 stream=true 的 SSE 流式输出。
    """
    ifnot req.messages:
        raise HTTPException(status_code=400, detail={
            "type": "invalid_request_error",
            "message": "messages 不能为空",
        })

    # ── 流式输出 ──
    if req.stream:
        return StreamingResponse(
            _claude_stream_generator(req),
            media_type="text/event-stream",
            headers={
                "Cache-Control": "no-cache",
                "Connection": "keep-alive",
                "X-Accel-Buffering": "no",
            },
        )

    # ── 非流式输出 ──
    try:
        provider = _get_provider()
        model = T_OPENAI_MODEL

        # 转换请求格式:Claude → OpenAI
        messages = _claude_messages_to_openai(req.messages, req.system)
        tools = _claude_tools_to_openai(req.tools)
        tool_choice = _claude_tool_choice_to_openai(req.tool_choice)

        response = await provider.chat(
            messages=messages,
            model=model,
            max_tokens=req.max_tokens,
            temperature=req.temperature or0.7,
            tools=tools,
            tool_choice=tool_choice,
        )

        logger.info(f"Claude Messages 接口请求: model={req.model}, messages_count={len(req.messages)}")
        logger.info(f"Claude Messages 接口响应: finish_reason={response.finish_reason}")

        if response.finish_reason == "error":
            raise HTTPException(status_code=502, detail={
                "type": "api_error",
                "message": response.content or"AI 服务返回错误",
            })

        # 构建 Claude 格式响应 content blocks
        content_blocks: list[dict] = []

        # 文本内容
        if response.content:
            content_blocks.append({"type": "text", "text": response.content})

        # tool_use blocks
        if response.tool_calls:
            for tc in response.tool_calls:
                content_blocks.append({
                    "type": "tool_use",
                    "id": tc.id,
                    "name": tc.name,
                    "input": tc.arguments if isinstance(tc.arguments, dict) else {},
                })

        # 如果既没有文本也没有 tool_use,补一个空 text block
        ifnot content_blocks:
            content_blocks.append({"type": "text", "text": ""})

        # 确定 stop_reason
        stop_reason = _openai_finish_reason_to_claude(response.finish_reason or"stop")
        # 如果有 tool_calls 则 stop_reason 应为 tool_use
        if response.tool_calls:
            stop_reason = "tool_use"

        usage_data = response.usage if response.usage else {}

        return ClaudeMessagesResponse(
            model=req.model or model,
            content=content_blocks,
            stop_reason=stop_reason,
            usage=ClaudeUsage(
                input_tokens=usage_data.get("prompt_tokens", 0) if isinstance(usage_data, dict) else0,
                output_tokens=usage_data.get("completion_tokens", 0) if isinstance(usage_data, dict) else0,
            ),
        )
    except HTTPException:
        raise
    except RuntimeError as e:
        raise HTTPException(status_code=500, detail={
            "type": "api_error",
            "message": str(e),
        })
    except Exception as e:
        logger.error(f"Claude Messages 接口异常: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail={
            "type": "api_error",
            "message": f"AI 处理失败: {str(e)}",
        })
相关推荐
Theodore_10222 小时前
深度学习(11):偏差与方差诊断、学习曲线
人工智能·笔记·深度学习·神经网络·机器学习·计算机视觉
weixin_436182422 小时前
PLC 与 DCS 国产化报告获取:工控产业情报查找指南
大数据·人工智能·国产plc
金智维科技官方2 小时前
制造业如何用Ki-AgentS智能体平台实现设备巡检自动化?
大数据·运维·人工智能
stereohomology2 小时前
大模型看大模型:推理Token的能耗用电量比对
人工智能
Hello world.Joey2 小时前
Transformer解读
人工智能·深度学习·神经网络·自然语言处理·nlp·aigc·transformer
机器之心2 小时前
Sand.ai开源发布MagiCompiler:突破局部编译界限,定义训推性能上限
人工智能·openai
KieranYin2 小时前
AI编程 | 概念
人工智能
飞Link2 小时前
LangChain Core 架构深度剖析与 LCEL 高阶实战
人工智能·架构·langchain
liangdabiao2 小时前
Seedance 2.0 Skill 一键写好剧本上线了coze的技能商店了,免费
人工智能