系统提示不是一段作者手写的固定字符串。它在每个模型 step 由 SystemPrompt 服务(packages/core/system-prompt/src/index.ts,暴露为 ctx.systemPrompt)组装:把注册表贡献的节、动态上下文、工具 schema 与提示变量合成一个 PromptAssembly,再渲染成文本。组装发生在 agent/pre-step——一个 turn 的第一次请求之前,而不是 step 内部的每次重试。
| 包 | 负责 |
|---|---|
@deepseek-ai/dsh-system-prompt | SystemPrompt 服务、PromptSection、PromptContext、变量、工具排序、renderPrompt |
@deepseek-ai/dsh-llm | 提示所消费的 ToolSchema、ContextSnapshotSection 类型 |
@deepseek-ai/dsh-agent-instructions | 注入工作区指令的运行时上下文贡献者 |
输入:节、上下文、工具、变量
注册表接受四种贡献,每种都在调用上下文的某个作用域里注册(全局或通过 agent.ctx 作用域化)。两种静态文本输入:
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 构造函数从配置安装常开的节:
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 ?? '' })
...
}这两个常量被导出,正因组合层刻意瞄准它们:
export const PERSONA_SECTION = 'deployment:persona' // persona 槽位,order 0
export const PERSONA_ORDER = 0deployment:persona 是作用域可见的 persona 机制:一个作用域节(例如智能体预设的 persona)持有相同的名字,于是遮蔽会为该作用域替换部署的全局 persona,而不是复制一份。
组装流程
assemble(context) 收集全局与作用域链上的贡献、解绑工具参数、应用规范排序,并运行真实钩子:
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:
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> 处:
export const TOOL_ORDER_REST = '<unlisted-tools>'每个提供者的 ToolProviderResult 还可以上报一个 knownNames 全集——即限制前的名称集合,用来区分"配置文件写错了名字"与"已知但在该作用域被刻意隐藏"。工具限制过滤发生在工具注册表而非这里(见 tool-presentation.md)。
渲染
renderPrompt 插值严格的 引用,丢弃空节,并用空行拼接剩余内容:
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 以注入 provider 与 model 两个提示变量,并挂钩 agent/request 把 provider/model 对强加到请求配置上。详见 model-selection.md。
延伸阅读
- 智能体循环 ——
systemPrompt.assemble在每个被提议 step 处被调用的位置。 - 会话管理 —— 渲染后的提示如何记入
request/header。 - 作用域系统 —— 作用域节/变量(含 persona)如何遮蔽全局项。
- 模型选择 ——
provider/model提示变量。 - 仓库源码:
packages/core/system-prompt/src/index.ts、packages/context/agent-instructions/src/index.ts。 - 官方脚手架:
docs/subsystems/system-prompt.md、docs/config-catalog.md(system-prompt 的persona/toolOrder键)。