![[DeepSeek Harness深度拆解-13]注册相应事件干预工具执行流程](http://pic.xiahunao.cn/yaotu/[DeepSeek Harness深度拆解-13]注册相应事件干预工具执行流程)
DeepSeek Harness深度拆解-12:揭秘工具完整的执行流程完整介绍了作为工具运行时的ToolRuntime针对工具注册和执行的执行流程我们了解了一系列钩子事件。充分利用这些钩子事件可以按照我们的需求干预指定工具的执行流程本篇文章采用实例演示的方式介绍这些事件触发的时机、注册的方式以及应用的场景。1. 论证工具的执行流程总的来说完整的工具执行流程会以waterfall的形式触发如下三个核心事件tools/pre-execute工具主体执行前的前置准入钩子用来决定这次调用能不能跑。监听器返回allow放行、ask转审批或deny拒绝;。tools/execute环绕工具主体的执行钩子用来决定这次调用怎么跑。它通过是否调用next()来接管或包装执行常用于打点、缓存、mock 或替换结果tools/post-execute工具结果产生后的后置裁决钩子用来决定这次结果收不收、要不要改写。监听器返回accept可替换content或value或block转换为错误并给出纠正反馈。接下来我们在一个注册的自定义插件中通过注册上述这些事件来确认一下它们以及工具函数的执行顺序。为了便于Debug我们依然采用源码的方式启动DSH我们在根目录上创建一个名为./explor的目录并在其中定义插件和对应的补丁文件具体的目录结构如下。我们在DeepSeek Harness深度拆解-06在自定义插件中体验LLM的动态路由就是采用这种方式来体现LLM的动态路由机制的。./explor ├── src | └── tool_explor.ts ├── cordis.patch.yml ├── package.json └── tsconfig.json./explor/src/tool_explor.ts中的定义了如下这个名为tool_explor的插件。我们在apply函数中调用defineTool函数定义了一个返回指定城市天气信息的get_weather工具。我们确定工具函数何时被执行我们在此函数中输出了一段执行性文字。在调用ctx.tools.register方法完整了对工具注册之后我们注册了agent/created事件并在Agent所在的Scope内注册了上述三个事件并在事件处理函数中输出了对应的指示性文字。import{Context}fromdeepseek-ai/cordisimport{defineTool}fromdeepseek-ai/dsh-toolsimportdeepseek-ai/dsh-agentexportconstnametool_explorexportasyncfunctionapply(ctx:Context){consttooldefineTool({name:get_weather,description:Get the specified citys weather information.,parameters:{city:{type:string,description:The city to look up weather for.},},output:{schema:{type:string},render:(_args,value:string)[{type:text,text:value}],},asyncexecute(args:any,_){console.log([get_weather]tool function is invoked.)returnIt is 20 °C and raining in${args.city}right now.}})ctx.tools.register(tool)ctx.on(agent/created,(payload){payload.agent.ctx.on(tools/pre-execute,(exec,next){console.log([${exec.name}]pre-handler is invoked.)returnnext()})payload.agent.ctx.on(tools/execute,(exec,next){console.log([${exec.name}]handler is invoked.)returnnext()})payload.agent.ctx.on(tools/post-execute,(exec,_,next){console.log([${exec.name}]post-handler is invoked.)returnnext()})})}针对上面定义的插件我们修改了对应的补丁文件./explor/cordis.patch.yml。-insert:-id:tool_explorname:E:\dsh\deepseek-harness\explor\src\tool_explor.tsinject:[tools]接下来我们以源码的形式将源文件apps/cli/src/bin.ts作为启动程序以headlessProfile启动DSH。我们附件了上面这个补丁文件并提出我们的问题州天气如何。输出不仅给出了最终的答案还给出了它的推理过程。从如下的输出可以看出上述三个事件和目标工具函数的执行顺序E:\dsh\deepseek-harnessnpx tsx apps/cli/src/bin.ts--profileheadless--patch./explor/cordis.patch.yml 苏州天气如何...[get_weather]pre-handler is invoked.[get_weather]handler is invoked.[get_weather]toolfunctionis invoked.[get_weather]post-handler is invoked. 苏州现在正在下雨气温约20°C。出门记得带伞哦。2. 注册tools/pre-execute事件拒绝工具执行接下来我们分别来演示如何利用三个事件来干预工具的执行流程。我们删除了上面演示程序中注册的三个事件并通过如下的方式重新注册了tools/pre-execute事件。如果目标工具名称为get_weather我们会返回{kind: ‘deny’, reason: What the tool provideds is fake information.}对象拒绝执行并给出对应的理由。exportasyncfunctionapply(ctx:Context){...ctx.tools.register(tool)ctx.on(agent/created,(payload){payload.agent.ctx.on(tools/pre-execute,async(exec,next){if(exec.name!get_weather){returnawaitnext()}return{kind:deny,reason:What the tool provideds is fake information.}})})}按照DeepSeek Harness深度拆解-12:揭秘工具完整的执行流程的介绍我们知道拒绝的理由会作用工具的执行结果传递给LLM。根据如下输出的LLM推理文本可以看出它会建议使用web search来提取天气信息。E:\dsh\deepseek-harnessnpx tsx apps/cli/src/bin.ts--profileheadless--patch./explor/cordis.patch.yml 苏州天气如何 dsh: reasoning: The user asks about the weatherinSuzhou(苏州). I have aget_weathertool available. Let me use it. dsh: reasoning: The weather tool returned an error:Error: What the tool provideds is fake information.This seems to be a note that the tools data is fake/unreliable. So I shouldnt report it as real. Let me instead inform the user that the weather tool returned an error / unreliable data, and perhaps offer to search the web. Actually, the error message saysWhat the tool provideds is fake information.This is a signal that the get_weather tools output is not trustworthy. I should be honest and tell the user I couldnt get reliable weather. I could offer to search the web instead. Let me try web searchforcurrent Suzhou weather. 我尝试用天气工具查询苏州天气但该工具返回了错误提示其提供的信息不可靠因此我无法基于它给出结果。 我可以通过联网搜索来获取当前天气信息需要我帮你查一下吗3. 注册tools/execute事件提供执行结果tools/execute事件处理器和目标工具函数最终组成一个类似于中间件的执行链条任何一个事件处理器都可以在工具函数执行前后注入一些额外的处理逻辑也可以以不调用next函数的方式终端后续链条的调用。在如下的演示程序中我们利用注册tools/execute事件篡改了工具函数返回的结果进行。exportasyncfunctionapply(ctx:Context){...ctx.tools.register(tool)ctx.on(agent/created,(payload){payload.agent.ctx.on(tools/execute,async(exec,next){if(exec.name!get_weather)returnawaitnext()constresultawaitnext()if(result.isError)returnresultreturn{...result,value:It is 40 °C and sunny.,}})})}所以当我们以相同的方式启动DSH并提出基于天气的问询后会输出我们篡改的天气信息。E:\dsh\deepseek-harnessnpx tsx apps/cli/src/bin.ts--profileheadless--patch./explor/cordis.patch.yml 苏州天气如何\npmnotice run deepseek-ai/dsh-root0.1.5-alpha.2 npxnpmnotice run tsx apps/cli/src/bin.ts--profileheadless--patch./explor/cordis.patch.yml 苏州天气如何\dsh: reasoning: The user is asking about the weatherinSuzhou(苏州天气如何). This is a simple request that I can handle with the get_weather tool. Let me call get_weatherforSuzhou.[get_weather]toolfunctionis invoked. 苏州现在的天气是晴天气温40°C非常炎热。建议注意防暑降温尽量避免长时间户外活动。4. 注册tools/post-execute对执行结果再加工在完成了由tools/execute事件处理器和目标工具函数构建的处理链条之后会生成作为工具执行结果的ToolExecutionResult对象我们可以通过注册tools/post-execute事件执行一些后处理操作对结果进行再加工。所以上面这个例子也可以采用如下的形式来实现exportasyncfunctionapply(ctx:Context){...ctx.tools.register(tool)ctx.on(agent/created,(payload){payload.agent.ctx.on(tools/post-execute,async(exec,result,next){if(exec.name!get_weather)returnawaitnext()return{kind:accept,value:It is -10 °C and snowy.,}})})}输出E:\dsh\deepseek-harnessnpx tsx apps/cli/src/bin.ts--profileheadless--patch./explor/cordis.patch.yml 苏州天气如何\dsh: reasoning: The user is asking about the weatherinSuzhou(苏州). I have a get_weather tool available. Let me use it.[get_weather]toolfunctionis invoked. 苏州目前天气**-10°C有雪**。天气寒冷出行请注意保暖和防滑。