6.4 System Prompt 架构

模型: claude-opus-4-6 (anthropic/claude-opus-4-6) 生成日期: 2026-02-17


在 Agent 系统中,System Prompt 是 LLM 的"操作手册"——它定义了 Agent 的身份、行为规范、工具使用策略和输出风格。OpenCode 的 System Prompt 架构并非简单的"一段文本",而是一个精心设计的分层拼接系统,需要根据不同的 LLM Provider、Agent 配置和会话状态动态组装最终的 Prompt。

衍生解释:什么是 System Prompt?

在 LLM 的对话接口中,消息分为三种角色:system(系统指令)、user(用户输入)和 assistant(AI 回复)。System Prompt 是最先发送给 LLM 的指令文本,它设定了 AI 在整个对话过程中的行为基调。与 User 消息不同,System Prompt 通常由应用开发者而非终端用户编写,用于控制 AI 的"人格"和行为边界。

在 OpenCode 中,System Prompt 可能长达数千个 token,包含详细的工具使用说明、安全规范、输出格式要求等。

6.4.1 SystemPrompt.provider() —— 按模型选择 Prompt 模板

不同的 LLM 有不同的"理解能力"和"指令遵循"特征。Claude 擅长遵循复杂的结构化指令,GPT 系列对某些指令格式的响应不同,Gemini 又有自己的特点。OpenCode 通过 SystemPrompt.provider() 函数为每种模型系列选择最适合的 Prompt 模板:

// session/system.ts
export namespace SystemPrompt {
  export function provider(model: Provider.Model) {
    if (model.api.id.includes("gpt-5"))      return [PROMPT_CODEX]
    if (model.api.id.includes("gpt-") || 
        model.api.id.includes("o1") || 
        model.api.id.includes("o3"))          return [PROMPT_BEAST]
    if (model.api.id.includes("gemini-"))     return [PROMPT_GEMINI]
    if (model.api.id.includes("claude"))      return [PROMPT_ANTHROPIC]
    if (model.api.id.toLowerCase()
        .includes("trinity"))                 return [PROMPT_TRINITY]
    return [PROMPT_ANTHROPIC_WITHOUT_TODO]
  }
}

模型到 Prompt 的映射关系

模型系列
Prompt 文件
核心特征

Claude 系列

anthropic.txt

完整的任务管理指令(Todo 工具集成)、专业客观风格

GPT-4/O1/O3

beast.txt

强调自主执行、网络研究、步骤规划

GPT-5

codex_header.txt

精简的编码指令、前端设计指南

Gemini

gemini.txt

结构化工作流、详细的安全规范

Trinity

trinity.txt

精简的 CLI 工具指令

其他模型

qwen.txt

基础指令集(无 Todo 支持)

这种映射策略的匹配是通过简单的字符串包含检查includes())实现的,而非精确匹配。这意味着 claude-3-opus-20240229claude-sonnet-4-20250514 都会匹配到 anthropic.txt。这种宽松的匹配方式确保了即使新版模型发布(如 claude-4-xxx),只要命名中包含 "claude",就能自动使用正确的 Prompt。

各 Prompt 模板的设计差异

让我们对比几个关键 Prompt 模板的设计哲学:

anthropic.txt(Claude 专用)

Claude 版本的 Prompt 强调:

  • Todo 工具的频繁使用——Claude 被认为擅长遵循结构化任务管理指令。

  • Task 委托——鼓励 Claude 主动将搜索任务委托给子 Agent,减少主 Agent 的上下文消耗。

  • 专业客观——明确要求"技术准确性优先于验证用户信念"。

beast.txt(GPT 系列专用)

GPT 版本的 Prompt 风格截然不同:

  • 强调持续执行——GPT 系列模型有时会过早停止,因此 Prompt 反复强调"keep going"。

  • 网络研究优先——假设 GPT 的知识可能过时,要求大量使用 webfetch 工具验证信息。

  • Memory 机制——引入了 .github/instructions/memory.instruction.md 文件来持久化用户偏好。

  • Casual 语气——使用更口语化的表达("Whelp - I see we have some problems")。

gemini.txt(Gemini 专用)

Gemini 版本的 Prompt 特点:

  • 严格的约定遵循——Gemini 被认为有时会引入不在项目中的库或框架。

  • 结构化工作流——定义了完整的五步工作流(Understand → Plan → Implement → Verify Tests → Verify Standards)。

  • 极简输出——"aim for fewer than 3 lines of text output per response"。

  • 新应用开发流程——Gemini 版本特别包含了详细的"从零创建应用"流程指南。

qwen.txt(回退版本)

对于不在上述模型系列中的其他模型(如 Qwen、LLaMA 等),OpenCode 使用了一个基础版本。与 anthropic.txt 相比,它移除了 Todo 工具相关的指令——因为并非所有模型都能可靠地使用结构化的 Todo 管理。

6.4.2 SystemPrompt.environment() —— 运行环境信息注入

除了行为指令外,System Prompt 还需要告诉 LLM 当前的运行环境信息:

这段代码注入了以下运行时信息:

信息
说明
用途

Model ID

当前使用的模型标识

让 Agent 知道自己"是谁",有时会影响自我评估行为

Working directory

项目工作目录

Agent 需要知道项目根目录来构建正确的文件路径

Git repo

是否为 Git 仓库

影响 Agent 是否建议使用 Git 命令

Platform

操作系统

影响 shell 命令的选择(如 ls vs dir

Date

当前日期

LLM 的训练数据有截止日期,提供当前日期帮助它判断信息时效性

值得注意的是,<directories> 部分当前被禁用(false 条件),原先计划使用 Ripgrep.tree() 注入项目目录结构。这可能是出于性能或 token 消耗的考虑——大型项目的目录树可能占用大量 token。

6.4.3 Prompt 的分层拼接逻辑

System Prompt 的最终组装发生在 LLM.stream() 函数中。让我们追踪完整的拼接流程:

Prompt 选择的优先级

  1. 如果 Agent 有自定义 Prompt(如 explorecompaction 等),直接使用该 Prompt。

  2. 如果 Agent 没有自定义 Prompt(如 build),使用 SystemPrompt.provider() 根据模型选择对应的 Prompt 模板。

  3. Codex 模式特例:如果是 Codex 模式(OpenAI OAuth),Provider Prompt 通过 options.instructions 发送而非作为 system 消息。

Plugin 变换层

这是 Plugin 系统的关键钩子之一。Plugin 可以在 Prompt 发送给 LLM 之前对其进行修改——添加额外的指令、替换某些部分或完全重写。oh-my-opencode 就大量使用这个钩子来注入其复杂的 Agent 行为指令。

系统设计了一个安全回退:如果 Plugin 意外地将 system 数组清空,会恢复为原始的 Prompt。这种防御性编程确保了即使 Plugin 出错,Agent 也不会在没有任何 System Prompt 的情况下运行(那将导致不可预测的行为)。

Prompt 缓存优化

衍生解释:Prompt Caching

Anthropic 等 Provider 提供了 Prompt Caching 功能——如果连续多次调用使用了相同的 System Prompt 前缀,API 可以缓存已处理的部分,减少延迟和成本。OpenCode 通过将 System Prompt 保持为固定的两部分结构 [header, rest] 来最大化缓存命中率:

  • header(第一部分)通常是不变的基础 Prompt(如 anthropic.txt),在整个会话中保持一致。

  • rest(第二部分)可能包含动态内容(如环境信息、Plugin 注入的指令)。

这样,只要 header 部分不变,API 就可以缓存对该部分的处理结果,显著降低后续请求的延迟。

完整的 System Prompt 结构

综合以上分析,一次 LLM 调用的完整 System Prompt 结构如下:

6.4.4 Provider 特定选项的注入

在 Prompt 之外,LLM 调用还涉及 Provider 特定选项的注入:

参数的优先级链是:

例如,如果 title Agent 设置了 temperature: 0.5,这个值会覆盖模型和 Provider 的默认 temperature 设置。这种层级化的参数管理确保了每个 Agent 可以精确控制 LLM 的行为。

6.4.5 工具解析与权限过滤

在 LLM 调用前的最后一步,系统会根据 Agent 的权限规则过滤可用工具:

这段代码执行了两层过滤:

  1. 权限规则过滤:根据 Agent 的 permission 字段,移除被 deny 的工具。

  2. 用户消息级过滤:用户可以在单条消息中临时禁用某些工具(input.user.tools)。

过滤后的工具列表被传递给 streamText()——LLM 只能"看到"并调用这些被允许的工具。这确保了即使 LLM 尝试调用被禁止的工具,请求也会被拒绝。


本节完整分析了 System Prompt 的架构设计——从模型选择到分层拼接,从 Plugin 变换到缓存优化。下一节,我们将转向自定义 Agent 的配置方式,了解用户如何通过配置文件和目录结构创建自己的 Agent。

Last updated