首页 / 资讯中心 / 文章详情

[DeepSeek Harness深度拆解-16]系统提示词的组装流程

[DeepSeek Harness深度拆解-16]系统提示词的组装流程 ★ FEATURED ARTICLE
DeepSeek Harness深度拆解-15:体验系统提示词的构建从编程的角度利用提供的一系列实例演示如何使用SystemPrompt这个基础服务这篇文章将会完整介绍该服务针对相关注册方法的处理和最终对系统提示词的组装流程。1. 不仅仅提供单纯系统提示词文本SystemPrompt并不是单纯地为我们生成调用LLM提供的系统提示词对应GenerateOptions的system字段它最终构建的产物体现在其assemble方法构建的PromptAssembly对象上只有sections部分的内容才会参与存储系统提示的渲染。contexts部分会转换成消息并最终添加到GenerateOptions的messages列表中而tools提供的工具Schema自然就对应GenerateOptions的同名字段。exportclassSystemPromptextendsService{asyncassemble(context:AssembleContext{}):PromisePromptAssembly}exportinterfaceAssembleContext{scope?:ScopeKey signal?:AbortSignal}exportinterfacePromptAssembly{sections:AssembledSection[]contexts:AssembledContext[]tools:ToolSchema[]variables:Recordstring,string|undefined}定义在PromptAssembly四个字段说明如下sections系统提示词的正文片段已求值但未插值按order排序最终由renderPrompt插值、过滤、拼接成提示词字符串contexts动态运行时上下文快照已求值但未插值按order排序最终渲染成用户消息插入模型历史tools面向模型的工具Schema列表已结构化、已按toolOrder排序不做文本渲染直接作为LLM API的tools参数传入variables变量名到值的扁平映射已求值仅作为sections和contexts插值时的查表来源。DSH中的大部分资源都需要与具有某个Scope绑定,SystemPrompt提供的PromptAssembly自然也是如此这一点从作为assembly方法的签名就可以看出来。该方法的参数context对应的AssembleContext接口定义了ScopeKey类型的scope字段意味着可以针对指定的Scope动态提供对应的PromptAssembly对象。如果将Scope映射为Agent那么就意味着可以针对性地为具体的Agent注册和提供系统提示词。2. SystemPrompt插件配置SystemPrompt即使服务也是插件即以插件的方式将自己作为服务注册到Context上作为插件的配置对应于如下这个Config。exportinterfaceConfig{includeHarnessIdentity?:booleanincludeRuntimeContext?:booleanpersonaPrefix?:stringpersonaSuffix?:stringtoolOrder?:string[]}Config接口定义的配置选项包括includeHarnessIdentity是否注入Harness身份声明默认true即You are an AI agent powered by DeepSeek Harness.;includeRuntimeContext是否包含PromptContext默认true如当前工作目录、可用工具等动态信息每轮请求重新求;personaPrefix作为persona的前缀出现在第一个PromptSection之前用于定义部署特有的角色描述支持严格变量插值;personaSuffix部署级人格后缀出现在最后一个PromptSection之后用于追加全局补充约束;toolOrder显式指定工具呈现顺序。3. 基于Scope的分层注册表SystemPrompt采用标准的Builder模式来构建最终的PromptAssembly它内部提供了一个基于Scope的分层注册表我们针对section、context、tools和variable方法的调用都是在注册表中添加相应的条目最终利用assembly方法根据指定的Scope提取匹配的注册条目进行合并最终将PromptAssembly创建出来。在DSH中但凡涉及基于Scope的分层注册表概念必然会使用ScopedLayers对象并通过实现ScopeLayer接口提供基于某个Scope的单层注册表。如下这个PromptLayer类型就是SystemPrompt内部使用的单层注册表。classPromptLayerimplementsScopeLayer{readonlysections:NamedEntriesPromptSectionreadonlycontexts:NamedEntriesPromptContextreadonlyruntimeContextSuppressorsnewAnonymousEntriestrue()readonlytoolProvidersnewAnonymousEntriesToolProvider()readonlyvariables:NamedEntriesVariableProviderisEmpty():boolean{returnthis.sections.isEmpty()this.contexts.isEmpty()this.runtimeContextSuppressors.isEmpty()this.toolProviders.isEmpty()this.variables.isEmpty()}}exportinterfacePromptSection{readonlyname:stringreadonlyorder:numberreadonlytext:string|((context:AssembleContext)string)readonlycomplete?:boolean}exportinterfacePromptContext{readonlyname:stringreadonlyorder:numberreadonlytext:string|((context:AssembleContext)string)}typeToolProvider(context:AssembleContext)ToolProviderResultexportinterfaceToolProviderResult{readonlyschemas:readonlyToolSchema[]readonlyknownNames?:readonlystring[]}typeVariableProvider(context:AssembleContext)string|undefined如上面的代码所示PromptLayer内部定义了五个字段来存储与某个Scope绑定或者全局系统提示词注册条目其中包括三个类型为NamedEntriesT的字段sections、contexts和variables用来存储注册的PromptSection、PromptContext和VariableProvider(用来提供模板渲染时替换占位符的变量值)。至于两个AnonymousEntriesT类型的字段toolProviders用来保存用于提供工具Schema的ToolProvider对象runtimeContextSuppressors字段则用来存储当前Scope是否开启动态运行时上下文的开关。4. 注册请求的处理SystemPrompt定义了如下这个ScopedLayers类型的字段layers它内部使用正是上面定义的这个PromptLayer。PromptLayer中定义的五个注册表分别通过五个对应的方法来维护。exportclassSystemPromptextendsService{privatereadonlylayersnewScopedLayers(scopenewPromptLayer(scope),(){this.ctx.emit(system-prompt/change)})section(section:PromptSection):()voidcontext(context:PromptContext):()voidsuppressRuntimeContext():()voidtools(provider:(context:AssembleContext)ToolProviderResult):()voidvariable(name:string,provider:(context:AssembleContext)string|undefined):()void}SystemPrompt还定义了getSectionOrder和getContextOrder方法为DSH原生提供的PrompSection和PromptContext提供对应的order值。如下面的代码所示deepseek-ai/dsh-system-prompt定义了常量SECTION_ORDERS和CONTEXT_ORDERS为为这些PrompSection和PromptContext提供的名称和order值得映射关系并以PromptSectionOrderName和PromptContextOrderName类型名称将它们的keyof typeof导出。exportclassSystemPromptextendsService{getSectionOrder(name:PromptSectionOrderName):number{returnSECTION_ORDERS[name]}getContextOrder(name:PromptContextOrderName):number{returnCONTEXT_ORDERS[name]}}constSECTION_ORDERS{HARNESS_IDENTITY:-1000,DEPLOYMENT_PERSONA_PREFIX:0,PLAN_POLICY:500,TEAM_POLICY:600,PTC_ONLY:800,FILE_REFERENCE:900,TOOL_BASH:1000,TOOL_PWSH:1010,TOOL_READ:1100,TOOL_WRITE:1200,TOOL_EDIT:1300,TOOL_GLOB:1400,TOOL_GREP:1500,TOOL_JOBS:1600,TOOL_PTY:1700,TOOL_WEB_SEARCH:2000,TOOL_WEB_FETCH:2100,TOOL_LSP:2200,TOOL_SESSION_QUERY:2300,TOOL_GOAL:2400,TOOL_CORDIS:2500,TOOL_WORKFLOW:2600,TOOL_RALPH:2700,TOOL_SUBAGENT:2800,TOOL_REPORT:2900,TOOLS_SDK:5000,DELIVERABLE_FILE_REFERENCES:9000,STRUCTURED_OUTPUT:9900,HARNESS_SOURCE:10000,WEB_SURFACE:10100,DEPLOYMENT_PERSONA_SUFFIX:10200,}asconstconstCONTEXT_ORDERS{SANDBOX_POLICY:110,APPROVAL_POLICY:115,SUBAGENT_DELEGATION:120,}asconstexporttypePromptSectionOrderNamekeyoftypeofSECTION_ORDERSexporttypePromptContextOrderNamekeyoftypeofCONTEXT_ORDERS5. PromptAssembly的组装在assemble方法中会提取AssembleContext提供的Scope然后利用它从layers字段表示的ScopedLayers对象中提取注册的条目并生成最终的PromptAssembly对象。exportclassSystemPromptextendsService{asyncassemble(context:AssembleContext{}):PromisePromptAssembly}具体会按照如下的规则生成PromptAssembly的四个成员variables将Scope作为参数调用layers的chainLayers方法得到按照Scope路径排序的PromptLayer不包含全局层按照这个顺序提取变量进行合并如果变量名称相同与Scope越近的层具有更高遇优先级sectionscontexts将Scope作为参数调用layers的merge方法合并每个PromptLayer提供的PromptSection和PromptContexttools: 将Scope作为参数调用layers的chainLayers方法得到按照Scope路径排序的PromptLayer并使用全局层与之合并然后按照此顺序提取每个PromptLayer的ToolProvider。将AssembleContext作为参数调用每个ToolProvider函数得到一组ToolSchema和工具名称列表然后执行一系列验证和排序生成最终的ToolSchema列表。在返回最终生成的PromptAssembly对象之前assemble方法会以waterfall形式触发具有如下签名的system-prompt/assemble事件对外发送通知。当然此事件肯定是以Scope绑定的范围进行分发的本着事件向上传播的原则之后当前以及上级Scope才会监听到此通知。整个事件流返回的PromptAssembly将会作为assemble方法的返回值意味着我们还可以通过注册此事件的形式对PromptAssembly实施再加工。declaremoduledeepseek-ai/cordis{interfaceEvents{system-prompt/assemble(this:ScopedSystemPrompt,assembly:PromptAssembly,context:AssembleContext,next:()PromisePromptAssembly):PromisePromptAssembly}}6. 提示词和上下文快照渲染如下所示的是两个针对PromptAssembly进行文本渲染的renderPrompt和renderContextSnapshot方法的定义。它们分别从PromptAssembly提取sections和contexts将它们的内容作为模板调用interpolate函数使用variables提供的变量执行插值操作生成最终的文本。exportfunctionrenderPrompt(assembly:PromptAssembly):string{returnassembly.sections.map(sectioninterpolate(section,assembly.variables,section)).filter(texttext.length0).join(\n\n)}exportfunctionrenderContextSnapshot(assembly:PromptAssembly):string{returnjoinContextSections(renderContextSections(assembly))}exportfunctionrenderContextSections(assembly:PromptAssembly):ContextSnapshotSection[]{returnassembly.contexts.map(context({name:context.name,text:interpolate(context,assembly.variables,context)})).filter(sectionsection.text.length0)}exportfunctionjoinContextSections(sections:readonlyContextSnapshotSection[]):string{constbodysections.map(sectionsection.text).join(\n\n)if(body.length0)returnreturnCurrent runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\n${body}}exportinterfaceContextSnapshotSection{readonlyname:stringreadonlytext:string}functioninterpolate(input:AssembledSection|AssembledContext,variables:Recordstring,string|undefined,kind:section|context,):stringrenderContextSection和joinContextSections函数作为renderContextSnapshot的辅助函数同时可以直接导入使用。前者生成一个ContextSnapshotSection列表后者将这个列表转换成字符串并前置一段指示性文本。
阅读完成 · 觉得有帮助?
咨询建站