Claude CLI 与 Agent SDK 在默认情况下会发送完全不同的系统提示,因此针对相同输入(如“挪威的首都是哪?”),两者无法产生完全相同的输出。CLI采用模块化的系统提示架构(约269基础token加上110多个条件加载片段),而SDK默认使用最小化提示。即使通过claude_code预设和settingSources配置来匹配CLI环境,并设置temperature=0,也无法保证输出的确定性,因为Claude API没有提供seed参数。

Claude Agent SDK vs CLI 系统提示对比:输出一致性与确定性分析

在开发基于Claude的应用时,理解Claude Agent SDK和CLI(即Claude Code)在底层行为上的区别至关重要。本文将深入对比两者系统提示的架构差异,分析输出不一致的根本原因,并提供配置指南,帮助你在SDK中尽可能模拟CLI的行为。

1. 核心架构差异:模块化提示 vs 最小化提示

理解两者输出不同的第一步,是明白它们发送给模型的系统提示(System Prompt)内容天差地别。

Claude CLI 的模块化系统提示

CLI拥有一个经过精心设计的、高度结构化的提示系统。其核心是一个约269个token的基础提示,随后会根据启用的功能,条件性地加载超过110个不同的提示片段。

这些组件共同构成了CLI的完整能力:

  • 基础提示:定义核心行为与指令。
  • 工具指令:详细说明18个以上内置工具(如Write, Read, Edit, Bash)的用法。
  • 编码规范与安全规则:代码风格、安全实践和防注入指令。
  • 环境上下文:自动包含当前工作目录、Git状态等信息。
  • 项目上下文:自动加载工作区下的CLAUDE.md文件内容。
  • 条件模块:例如当进入“计划模式”或使用Explore子代理时,会加载对应的专用提示。

这意味着,CLI在每次交互中都自动为模型装备了强大的“全副武装”,使其具备完整的软件工程能力。

Agent SDK 的最小默认提示

相比之下,Agent SDK的默认行为要精简得多。它只提供最基础的工具指令(如果确实提供了工具的话)和简短的安全规则。

SDK默认提示不包含:

  • 编码指南与风格偏好
  • CLI的完整安全规则集
  • 项目上下文(除非显式配置)
  • 详细的工具使用说明

这种设计赋予了开发者更高的灵活性,但也意味着SDK开箱即用的能力远不如CLI全面。

2. 实现最大输出一致性的配置方法

如果你在SDK应用中希望获得接近CLI的体验和行为,需要通过以下配置进行对齐。

步骤一:使用 claude_code 预设

这是匹配CLI提示架构最关键的一步。通过将systemPrompt的类型设置为preset,可以让SDK加载与CLI相同的核心模块化提示。

import { query } from "@anthropic-ai/agent-sdk";

const response = query({
  prompt: "分析当前目录的Git状态并提出建议",
  options: {
    systemPrompt: {
      type: "preset",
      preset: "claude_code" // 使用CLI的系统提示架构
    }
  }
});

步骤二:加载项目与用户设置

CLI会自动加载CLAUDE.md和用户级设置,SDK需要显式配置才能实现。

const response = query({
  prompt: "编写一个单元测试",
  options: {
    systemPrompt: {
      type: "preset",
      preset: "claude_code"
    },
    settingSources: ["project", "user"] // 加载项目级和用户级设置
  }
});
  • settingSources: ["project"]:会尝试加载项目根目录下的CLAUDE.md文件。
  • settingSources: ["user"]:会加载用户级的配置(如输出样式)。

步骤三:匹配模型与温度参数

确保使用相同的模型,并设置temperature: 0以追求最大确定性(虽然无法保证)。

const response = query({
  prompt: "什么是闭包?",
  options: {
    systemPrompt: {
      type: "preset",
      preset: "claude_code",
      append: "请用中文简洁解释。" // 在CLI提示基础上追加指令
    },
    settingSources: ["project"],
    model: "claude-sonnet-4-20250514",
    temperature: 0 // 使用贪心解码以追求一致性
  }
});

3. 输出一致性无法保证:根本原因分析

即使你完美复制了CLI的所有配置,SDK和CLI的输出也可能不同。更甚者,同一个接口在相同输入下多次运行的结果也可能存在差异。这是由多个不可控因素决定的。

无法实现完全确定性的技术因素

因素类别 具体原因 可否通过配置控制
API 架构 Claude Messages API 不提供 seed 参数,这是无法实现种子级复现的根本原因。 ❌ 不可控
硬件与算力 并行计算中的浮点运算细微差异、云基础设施的调度差异。 ❌ 不可控
模型架构 模型内部的专家混合(Mixture-of-Experts, MoE)路由可能因输入微小扰动而变化。 ❌ 不可控
提示差异 即使配置相同,加载的上下文(如动态变化的Git状态)也可能略有不同。 ✅ 可控制

重要结论:temperature=0 不等于确定性

temperature设置为0(贪心解码)会大幅降低随机性,但不能保证输出完全一致。已知的Claude CLI问题(如GitHub Issue #3370)也证实了这一点。系统内部的非确定性因素仍然会导致微小差异。

4. 实用建议与最佳实践

基于以上分析,在设计和构建应用时应遵循以下原则。

选择正确的接口

  • 选择CLI:当你需要进行交互式开发、调试,或希望模型拥有完整的本地项目上下文和工具链时。
  • 选择Agent SDK:当你需要将Claude能力集成到自定义应用、自动化管道或需要精细控制提示和输入输出格式时。

设计健壮的系统

  1. 不要依赖比特级一致:将模型输出视为概率性的。在应用逻辑层做好容错和校验。
  2. 拥抱结构化输出:使用JSON模式(JSON Mode)或请求结构化数据,这比依赖自然语言输出的字面一致性更可靠。
    const response = await client.messages.create({
      model: "claude-sonnet-4-20250514",
      max_tokens: 1024,
      system: "你是一个JSON输出助手。只返回JSON。",
      messages: [{role: "user", content: "列出北京的3个地标"}]
    });
  3. 缓存与重试:对于相同输入期望得到稳定结果的场景,考虑缓存首次成功的结果。对于关键任务,可实施重试机制。
  4. 明确你的目标:你需要的是“行为相似”还是“输出逐字相同”?前者通过claude_code预设已高度可实现,后者在当前技术架构下无法保证。

通过理解系统提示的底层差异并合理配置SDK,你可以在自定义应用中获得非常接近CLI的强大且一致的体验。关键在于为输出差异性设计系统,而非试图完全消除它。

FAQ

Q: 为什么我设置了temperature: 0,但CLI和SDK的输出还是不一样? A: temperature: 0只能消除采样随机性,无法消除模型固有的非确定性。两者系统提示的细微差异、加载的环境上下文不同,以及模型内部(如MoE路由)的计算差异都会导致输出不同。

Q: 如何让SDK的行为尽可能像CLI? A: 关键配置是:1) 使用systemPrompt: { type: "preset", preset: "claude_code" };2) 添加settingSources: ["project", "user"]以加载CLAUDE.md等上下文;3) 使用相同的模型和temperature: 0。但请注意,这仍不能保证输出完全相同。

Q: SDK的“最小默认提示”有什么优势? A: 优势在于轻量和灵活。它不会强加CLI的编码风格、安全规则等,为你留出更多的token预算来构建自己的系统提示,特别适合需要完全定制模型行为的特定领域应用。