Skip to content

系统提示不是一段作者手写的固定字符串。它在每个模型 stepSystemPrompt 服务(packages/core/system-prompt/src/index.ts,暴露为 ctx.systemPrompt组装:把注册表贡献的节、动态上下文、工具 schema 与提示变量合成一个 PromptAssembly,再渲染成文本。组装发生在 agent/pre-step——一个 turn 的第一次请求之前,而不是 step 内部的每次重试。

负责
@deepseek-ai/dsh-system-promptSystemPrompt 服务、PromptSectionPromptContext、变量、工具排序、renderPrompt
@deepseek-ai/dsh-llm提示所消费的 ToolSchemaContextSnapshotSection 类型
@deepseek-ai/dsh-agent-instructions注入工作区指令的运行时上下文贡献者

输入:节、上下文、工具、变量

注册表接受四种贡献,每种都在调用上下文的某个作用域里注册(全局或通过 agent.ctx 作用域化)。两种静态文本输入:

ts
interface PromptSection {
  readonly name: string
  readonly order: number           // 节按升序拼接;-100 身份,0 persona,100–199 工具指引
  readonly text: string | ((context: AssembleContext) => string)
  readonly complete?: boolean      // 成为唯一的系统提示节
}

interface PromptContext {
  readonly name: string
  readonly order: number           // 上下文按升序拼接
  readonly text: string | ((context: AssembleContext) => string)
}

systemPrompt.section())贡献固定提示文案。上下文systemPrompt.context())贡献动态、对模型可见、被物化为持久 user-role 快照的状态(见会话一章)——两者排序不同,且都支持 插值。工具来自 systemPrompt.tools(provider),变量来自 systemPrompt.variable(name, provider)

固定节

SystemPrompt 构造函数从配置安装常开的节:

ts
constructor(ctx, config) {
  if (config.includeHarnessIdentity ?? true) {
    this.section({ name: 'harness:identity', order: -100, text: 'You are an AI agent powered by DeepSeek Harness.' })
  }
  this.section({ name: PERSONA_SECTION, order: PERSONA_ORDER, text: config.persona ?? '' })
  ...
}

这两个常量被导出,正因组合层刻意瞄准它们:

ts
export const PERSONA_SECTION = 'deployment:persona'   // persona 槽位,order 0
export const PERSONA_ORDER = 0

deployment:persona作用域可见的 persona 机制:一个作用域节(例如智能体预设的 persona)持有相同的名字,于是遮蔽会为该作用域替换部署的全局 persona,而不是复制一份。

组装流程

assemble(context) 收集全局与作用域链上的贡献、解绑工具参数、应用规范排序,并运行真实钩子:

ts
async assemble(context: AssembleContext = {}): Promise<PromptAssembly> {
  const scope = context.scope
  const scopeLayers = this.layers.chainLayers(scope)
  const runtimeContextSuppressed = !this.layers.global.runtimeContextSuppressors.isEmpty()
    || scopeLayers.some(layer => !layer.runtimeContextSuppressors.isEmpty())
  // 作用域变量遮蔽全局...
  // 作用域节在稳定排序前遮蔽全局...
  const sectionByName = this.layers.merge(scope, layer => layer.sections)
  const contextByName = this.layers.merge(scope, layer => layer.contexts)
  ...
  const assembly: PromptAssembly = { sections, contexts, tools, variables }
  const transformed = await this.ctx.waterfall(
    scopeTarget(this, scope), 'system-prompt/assemble', assembly, context,
    () => Promise.resolve(assembly),
  )
  if (completeSection === undefined && !runtimeContextSuppressed) return transformed
  return { ...transformed, sections: ..., contexts: ... }
}

返回的 PromptAssembly

ts
interface PromptAssembly {
  sections: AssembledSection[]
  contexts: AssembledContext[]
  tools: ToolSchema[]
  variables: Record<string, string | undefined>   // 渲染时才求值
}

节文本与上下文文本在组装对象里保持未插值;只有 renderPrompt/renderContextSections 会解析 。组装对象会经过 system-prompt/assemble 专家 waterfall(按作用域过滤),因此扩展插件可以权威地改写它;被标记 complete 的节随后被恢复为唯一的系统提示节(而其它多个活跃 complete 节会让组装失败)。

工具 schema 组装与排序

工具 schema 从全局与作用域链的 toolProviders 收集,每个都通过 structuredClone 解绑参数。它们按规范排序——默认按字典序;若配置了 toolOrder,则 orderTools 会把未列出的工具插入保留标记 <unlisted-tools> 处:

ts
export const TOOL_ORDER_REST = '<unlisted-tools>'

每个提供者的 ToolProviderResult 还可以上报一个 knownNames 全集——即限制前的名称集合,用来区分"配置文件写错了名字"与"已知但在该作用域被刻意隐藏"。工具限制过滤发生在工具注册表而非这里(见 tool-presentation.md)。

渲染

renderPrompt 插值严格的 引用,丢弃空节,并用空行拼接剩余内容:

ts
export function renderPrompt(assembly: PromptAssembly): string {
  return assembly.sections
    .map(section => interpolate(section, assembly.variables, 'section'))
    .filter(text => text.length > 0)
    .join('\n\n')
}

变量名必须匹配 [a-z][a-z0-9_]*;畸形、未知或取到空值的引用会抛出。joinContextSections 用固定开头 "Current runtime context. This snapshot supersedes earlier runtime-context snapshots." 包裹运行时上下文快照——这正是 harness 自身运行时暴露的那个开头。

组装何时发生

在循环的 preStep 处(见 agent-loop.md),每个被提议的 step 都会用 assembleContextFor(agent, signal)(设置 scope: agent 与当前 turn 的信号)运行一次组装,然后把渲染后的节经 agent/pre-step waterfall 呈现出来。工具 schema 与渲染后的系统文本会成为记录的 request/header 的一部分,因此过去某 step 的精确提示可以从会话日志重建。

模型选择变量

通过 installModelSelection 安装的模型选择会挂钩 system-prompt/assemble 以注入 providermodel 两个提示变量,并挂钩 agent/request 把 provider/model 对强加到请求配置上。详见 model-selection.md

延伸阅读

  • 智能体循环 —— systemPrompt.assemble 在每个被提议 step 处被调用的位置。
  • 会话管理 —— 渲染后的提示如何记入 request/header
  • 作用域系统 —— 作用域节/变量(含 persona)如何遮蔽全局项。
  • 模型选择 —— provider/model 提示变量。
  • 仓库源码:packages/core/system-prompt/src/index.tspackages/context/agent-instructions/src/index.ts
  • 官方脚手架:docs/subsystems/system-prompt.mddocs/config-catalog.md(system-prompt 的 persona/toolOrder 键)。