前途科技前途科技
  • 洞察
  • 服务
  • 关于
  • AI 资讯
    • 快讯
    • 产品
    • 技术
    • 商业
    • 政策
    • 初创
  • 洞察
  • 资源中心
    • 深度研究
      • AI 前沿
      • 案例研究
      • AI 知识库
    • 行业报告
      • 白皮书
      • 行业报告
      • 研究报告
      • 技术分享
      • 专题报告
    • 精选案例
      • 金融行业
      • 医疗行业
      • 教育行业
      • 零售行业
      • 制造行业
  • 服务
  • 关于
联系我们
AI 前沿/08.25 · 09:03/5 MIN/0 阅读

DeepSeek Harness 源码深潜(中)——工具、提示词与插件内核(附DSH源码)

系列:DeepSeek Harness(dsh)源码教程 · 中篇(第 4-7 章)上篇:从 API 到 Agent 心脏(第 0-3 章)下篇:模型适配、多 Agent 与 Python 实

                    <div class="detail-content-box has-mask large">
                        <blockquote style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.55);line-height: 1.8;margin-bottom: 24px;"><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>系列:DeepSeek Harness(dsh)源码教程 · 中篇(第 4-7 章)</span><span><br/></span><span>上篇:从 API 到 Agent 心脏(第 0-3 章)</span><span><br/></span><span>下篇:模型适配、多 Agent 与 Python 实战(第 8-10 章)</span></p></blockquote><hr/><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>上篇我们拆完了 Agent 的心脏:事件溯源的日志系统、turn/step 状态机、工具调度器。</span></p><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>但心脏只是泵血。中篇进入让 Agent 真正&quot;能干活、可替换、可扩展&quot;的部分:</span></p><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span>第 4 章:<span style="font-weight: 500;">工具系统</span>——function calling 的工业级实现</span></section></li><li style="margin-bottom: 0px;"><section><span>第 5 章:<span style="font-weight: 500;">提示词组装</span>——模型看到世界的&quot;总装车间&quot;</span></section></li><li style="margin-bottom: 0px;"><section><span>第 6 章:<span style="font-weight: 500;">Cordis 插件内核</span>——dsh 的立身之本</span></section></li><li style="margin-bottom: 0px;"><section><span>第 7 章:<span style="font-weight: 500;">能力缝 seam</span>——可替换的能力接口</span></section></li></ul><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>如果说上篇是骨架,中篇就是肌肉与神经。</span></p><section style="text-align: center;"><img src="https://pic.imgdb.cn/i/034D0AyxwUgaPrHT6N2ob8.jpg" class="rich_pages wxw-img"/></section><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>第 4 章 工具系统:FC 的工业级实现</span></h2><blockquote style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.55);line-height: 1.8;margin-bottom: 24px;"><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>核心机制:<span style="font-weight: 500;">schema 驱动的声明式校验、五段执行流水线、TOOL_RUNTIME_SCHEDULER 调度器、执行模式(并行/串行)、作用域隔离</span>。</span></p></blockquote><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>4.1 从手写 dispatch 到工业工具系统</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">tools&nbsp;=&nbsp;[{”type”:&nbsp;”function”,&nbsp;”function”:&nbsp;{”name”:&nbsp;”get_weather”,&nbsp;”parameters”:&nbsp;{...}}}]&nbsp;def&nbsp;dispatch(name,&nbsp;args):&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;name&nbsp;==&nbsp;”get_weather”:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;get_weather(**args)</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>这个写法的问题:</span></p><ol style="list-style-type: decimal;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">校验靠手写</span>:</span><code><span>get_weather(city=123)</span></code><span>&nbsp;这种类型错要自己 if/else</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">没有把关</span>:任何人都能调任何工具——</span><code><span>delete_all()</span></code><span>&nbsp;被误调谁拦?</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">没有流水线</span>:遥测、限流、权限检查要自己塞进 dispatch</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">作用域不分</span>:主 agent 和子 agent 共用同一批工具</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">结果不可追溯</span>:调用结果怎么和日志关联?</span></section></li></ol><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>dsh 的答案就是本章内容。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>4.2 defineTool:声明式定义 + 自动校验</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">export&nbsp;function&nbsp;defineTool(&nbsp;&nbsp;options:&nbsp;DefineToolOptions,):&nbsp;ToolDefinition&nbsp;{&nbsp;&nbsp;const&nbsp;parameters&nbsp;=&nbsp;parameterSchemaSpecToJsonSchema(options.parameters)&nbsp;&nbsp;const&nbsp;validate&nbsp;=&nbsp;(args:&nbsp;unknown):&nbsp;string[]&nbsp;=&gt;&nbsp;&nbsp;&nbsp;&nbsp;validateJsonSchemaValue(parameters,&nbsp;args,&nbsp;&#39;&#39;)&nbsp;&nbsp;return&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;name:&nbsp;options.name,&nbsp;&nbsp;&nbsp;&nbsp;description:&nbsp;options.description,&nbsp;&nbsp;&nbsp;&nbsp;parameters,&nbsp;&nbsp;&nbsp;&nbsp;async&nbsp;execute(args,&nbsp;exec)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;violations&nbsp;=&nbsp;validate(args)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(violations.length&nbsp;&gt;&nbsp;0)&nbsp;throw&nbsp;new&nbsp;ToolArgsError(violations)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;userExecute(args&nbsp;as&nbsp;InferArgs,&nbsp;exec)&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;&nbsp;}}</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">关键点:ToolDefinition里execute是被包装过的</span>——你写的 userExecute 永远在参数校验通过之后才被调用。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>4.3 真实例子:tool-bash 的三层防御</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">层次 1:参数 schema</span></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">interface&nbsp;BashToolArgs&nbsp;{&nbsp;&nbsp;command:&nbsp;string&nbsp;&nbsp;description:&nbsp;string&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;必填——”为什么执行”(可审计)&nbsp;&nbsp;timeoutMs?:&nbsp;number&nbsp;&nbsp;workdir?:&nbsp;string&nbsp;&nbsp;run_in_background?:&nbsp;boolean}</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">层次 2:业务校验</span></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">function&nbsp;validateBashArgs(args:&nbsp;BashToolArgs):&nbsp;void&nbsp;{&nbsp;&nbsp;if&nbsp;(args.command.trim().length&nbsp;===&nbsp;0)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;Error(&#39;invalid&nbsp;command:&nbsp;expected&nbsp;a&nbsp;non-empty&nbsp;string&#39;)&nbsp;&nbsp;}&nbsp;&nbsp;//&nbsp;timeoutMs&nbsp;必须是正有限数&nbsp;&nbsp;//&nbsp;sandbox_permissions&nbsp;⇔&nbsp;justification&nbsp;必须配对}</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">为什么 schema 之外还要手写校验?</span>JSON Schema 表达不了&quot;两个字段必须配对出现&quot;这种跨字段约束。</span></p><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">层次 3:动态生成的工具描述</span></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">function&nbsp;bashDescription(backgroundEnabled:&nbsp;boolean,&nbsp;escalationModes:&nbsp;readonly&nbsp;SandboxMode[]):&nbsp;string&nbsp;{&nbsp;&nbsp;//&nbsp;把当前沙箱模式、后台执行可用性写进描述}</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">工具描述不是静态字符串,是运行时生成的</span>——环境变了,模型看到的工具说明就变了。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>4.4 五段执行流水线</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">tools/pre-execute&nbsp;&nbsp;&nbsp;(瀑布事件:允许&nbsp;/&nbsp;拒绝&nbsp;/&nbsp;询问)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;↓tools/execute&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(调度器分发)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;↓tools/post-execute&nbsp;&nbsp;(结果后处理)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;↓tools/result&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(结果观测)</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>每一个阶段都是一个<span style="font-weight: 500;">Cordis 事件</span>,任何插件都能在流水线上插一脚:</span></p><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span>安全插件监听&nbsp;</span><code><span>pre-execute</span></code><span>:</span><code><span>rm -rf /</span></code><span>&nbsp;→ 拒绝</span></section></li><li style="margin-bottom: 0px;"><section><span>遥测插件监听&nbsp;</span><code><span>result</span></code><span>:统计每次调用的耗时/token</span></section></li><li style="margin-bottom: 0px;"><section><span>沙箱插件监听&nbsp;</span><code><span>pre-execute</span></code><span>:检查参数是否越权</span></section></li></ul><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">设计精髓:工具自己不管&quot;能不能调&quot;,只管&quot;怎么干活&quot;。安全策略在流水线上。</span></span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>4.5 TOOL_RUNTIME_SCHEDULER:决策与执行分离</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>第 3 章的 tool-calls.ts 里出现了一个常量 TOOL_RUNTIME_SCHEDULER:</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">ctx.provide(TOOL_RUNTIME_SCHEDULER,&nbsp;{&nbsp;&nbsp;prepare(exec)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;进入流水线(跑&nbsp;pre-execute),返回&nbsp;dispatch&nbsp;/&nbsp;post-result&nbsp;/&nbsp;final-result&nbsp;&nbsp;dispatch(prepared)&nbsp;&nbsp;&nbsp;//&nbsp;真正执行工具函数&nbsp;&nbsp;finalize(exec,&nbsp;result)&nbsp;&nbsp;//&nbsp;结果后处理&nbsp;&nbsp;finish(exec,&nbsp;result)&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;直接收尾})</pre></section><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">const&nbsp;prepared&nbsp;=&nbsp;await&nbsp;ctx.tools[TOOL_RUNTIME_SCHEDULER].prepare(call.exec)switch&nbsp;(prepared.kind)&nbsp;{&nbsp;&nbsp;case&nbsp;&#39;dispatch&#39;:&nbsp;&nbsp;&nbsp;&nbsp;promise&nbsp;=&nbsp;ctx.tools[TOOL_RUNTIME_SCHEDULER].dispatch(prepared.exec)&nbsp;&nbsp;case&nbsp;&#39;post-result&#39;:&nbsp;&nbsp;case&nbsp;&#39;final-result&#39;:}</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">为什么要套这一层?</span>prepare() 里跑了 pre-execute 瀑布——监听器可能直接给出结果(&quot;被策略拦下了&quot;),此时根本不需要执行工具。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>4.6 执行模式:并行还是串行?</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">defineTool({&nbsp;&nbsp;...,&nbsp;&nbsp;isConcurrencySafe:&nbsp;true,&nbsp;&nbsp;&nbsp;//&nbsp;如&nbsp;read_file:可并行&nbsp;&nbsp;//&nbsp;默认&nbsp;false:如&nbsp;bash:必须串行})</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">为什么&quot;可并行&quot;要工具自己声明?</span></span></p><ol style="list-style-type: decimal;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span>读文件可以并行,写文件不能——只有工具作者知道语义</span></section></li><li style="margin-bottom: 0px;"><section><span>猜错了后果严重:两个并行 bash 都改环境变量,结果不可预测</span></section></li><li style="margin-bottom: 0px;"><section><span>声明是&quot;契约&quot;,调度器按契约执行</span></section></li></ol><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>4.7 作用域隔离</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">ctx.tools.register(tool,&nbsp;{&nbsp;scope:&nbsp;agentId&nbsp;})&nbsp;&nbsp;&nbsp;//&nbsp;只给这个&nbsp;agent&nbsp;注册ctx.tools.register(tool)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;全局注册</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>主 agent 能调&quot;创建子任务&quot;,子 agent 只能调&quot;读写文件&quot;——<span style="font-weight: 500;">最小权限在 agent 世界落地</span>。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>4.8 Python 对照:带流水线和并发声明的工具系统</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">class&nbsp;ToolRuntime:&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;__init__(self):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.tools&nbsp;=&nbsp;{}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.pre_execute_hooks&nbsp;=&nbsp;[]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;register(self,&nbsp;tool_def:&nbsp;dict,&nbsp;scope:&nbsp;str&nbsp;=&nbsp;”*”):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.tools[(scope,&nbsp;tool_def[”name”])]&nbsp;=&nbsp;tool_def&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;get(self,&nbsp;scope:&nbsp;str,&nbsp;name:&nbsp;str):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;self.tools.get((scope,&nbsp;name))&nbsp;or&nbsp;self.tools.get((”*”,&nbsp;name))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;add_pre_execute_hook(self,&nbsp;hook):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.pre_execute_hooks.append(hook)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;async&nbsp;def&nbsp;prepare(self,&nbsp;scope:&nbsp;str,&nbsp;name:&nbsp;str,&nbsp;args:&nbsp;dict):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tool&nbsp;=&nbsp;self.get(scope,&nbsp;name)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;not&nbsp;tool:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;{”kind”:&nbsp;”rejected”,&nbsp;”reason”:&nbsp;”tool&nbsp;not&nbsp;found”}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;hook&nbsp;in&nbsp;self.pre_execute_hooks:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;decision&nbsp;=&nbsp;hook(name,&nbsp;args)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;decision:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;{”kind”:&nbsp;”rejected”,&nbsp;”reason”:&nbsp;decision}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;{”kind”:&nbsp;”dispatch”,&nbsp;”tool”:&nbsp;tool}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;async&nbsp;def&nbsp;dispatch(self,&nbsp;prepared,&nbsp;args:&nbsp;dict):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;prepared[”tool”][”execute”](args)&nbsp;def&nbsp;define_tool(runtime:&nbsp;ToolRuntime,&nbsp;name:&nbsp;str,&nbsp;description:&nbsp;str,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parameters:&nbsp;dict,&nbsp;concurrency_safe:&nbsp;bool&nbsp;=&nbsp;False,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;scope:&nbsp;str&nbsp;=&nbsp;”*”):&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;decorator(func):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;execute(args:&nbsp;dict):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;key,&nbsp;spec&nbsp;in&nbsp;parameters.get(”properties”,&nbsp;{}).items():&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;spec.get(”required”)&nbsp;and&nbsp;key&nbsp;not&nbsp;in&nbsp;args:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise&nbsp;ValueError(f”缺少参数:&nbsp;{key}”)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;func(**args)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;runtime.register({&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;”name”:&nbsp;name,&nbsp;”description”:&nbsp;description,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;”parameters”:&nbsp;parameters,&nbsp;”execute”:&nbsp;execute,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;”concurrency_safe”:&nbsp;concurrency_safe,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;scope)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;func&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;decorator&nbsp;rt&nbsp;=&nbsp;ToolRuntime()&nbsp;@define_tool(rt,&nbsp;”read_file”,&nbsp;”读取文件(可并行)”,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{”type”:&nbsp;”object”,&nbsp;”properties”:&nbsp;{”path”:&nbsp;{”type”:&nbsp;”string”,&nbsp;”required”:&nbsp;True}}},&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;concurrency_safe=True)def&nbsp;read_file(path:&nbsp;str):&nbsp;return&nbsp;f”[内容]&nbsp;{path}”&nbsp;@define_tool(rt,&nbsp;”delete_file”,&nbsp;”删除文件(危险)”,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{”type”:&nbsp;”object”,&nbsp;”properties”:&nbsp;{”path”:&nbsp;{”type”:&nbsp;”string”,&nbsp;”required”:&nbsp;True}}})def&nbsp;delete_file(path:&nbsp;str):&nbsp;return&nbsp;f”[已删除]&nbsp;{path}”&nbsp;rt.add_pre_execute_hook(lambda&nbsp;name,&nbsp;args:&nbsp;f”禁止执行&nbsp;{name}”&nbsp;if&nbsp;name&nbsp;==&nbsp;”delete_file”&nbsp;else&nbsp;None)&nbsp;import&nbsp;asyncioasync&nbsp;def&nbsp;main():&nbsp;&nbsp;&nbsp;&nbsp;print(”决策:”,&nbsp;await&nbsp;rt.prepare(”*”,&nbsp;”delete_file”,&nbsp;{”path”:&nbsp;”/etc/passwd”}))&nbsp;&nbsp;&nbsp;&nbsp;prepared&nbsp;=&nbsp;await&nbsp;rt.prepare(”*”,&nbsp;”read_file”,&nbsp;{”path”:&nbsp;”a.py”})&nbsp;&nbsp;&nbsp;&nbsp;print(”执行:”,&nbsp;await&nbsp;rt.dispatch(prepared,&nbsp;{”path”:&nbsp;”a.py”}))&nbsp;asyncio.run(main())</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">对照 dsh 的差距:</span>dsh 的瀑布是带 next() 委托语义的 Cordis 事件;校验是完整 JSON Schema 引擎;scope 是分层作用域。<span style="font-weight: 500;">但&quot;决策与执行分离 + 瀑布把关 + 并发声明&quot;三个核心已实现。</span></span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>4.9 本章小结</span></h3><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span>defineTool = 声明式 schema + 自动校验 + 动态描述</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">五段流水线</span>是事件驱动的,安全策略是插件不是代码</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">TOOL_RUNTIME_SCHEDULER</span>&nbsp;分离&quot;决策&quot;与&quot;执行&quot;</span></section></li><li style="margin-bottom: 0px;"><section><span>并发安全是<span style="font-weight: 500;">工具的声明</span>,调度器不猜</span></section></li><li style="margin-bottom: 0px;"><section><span>作用域隔离实现 agent 级最小权限</span></section></li></ul><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>第 5 章 提示词组装:从字符串拼接到&quot;总装车间&quot;</span></h2><blockquote style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.55);line-height: 1.8;margin-bottom: 24px;"><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>核心机制:<span style="font-weight: 500;">组装/渲染分离、变量后插值、complete 语义、组装瀑布</span>。</span></p></blockquote><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>5.1 你现在的做法,和它的三个致命伤</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">system_prompt&nbsp;=&nbsp;f”””你是一个智能助手。当前工作目录:{cwd}可用工具:{”,&nbsp;”.join(tool_names)}规则:{rules}”””</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>三个问题:</span></p><ol style="list-style-type: decimal;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">顺序靠手排</span>:新增一段提示词要手工决定插在哪</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">没有&quot;来源&quot;概念</span>:拼出来的字符串不知道每段来自哪个插件</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">改动是整体性的</span>:想换某一段等于重拼整个字符串</span></section></li></ol><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>dsh 的答案:<span style="font-weight: 500;">把&quot;提示词&quot;变成注册表 + 总装线</span>。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>5.2 section 注册表</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">export&nbsp;interface&nbsp;PromptSection&nbsp;{&nbsp;&nbsp;readonly&nbsp;name:&nbsp;string&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;唯一名——重名注册直接抛错&nbsp;&nbsp;readonly&nbsp;order:&nbsp;number&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;排序权重&nbsp;&nbsp;readonly&nbsp;text:&nbsp;string&nbsp;|&nbsp;((context)&nbsp;=&gt;&nbsp;string)&nbsp;&nbsp;readonly&nbsp;complete?:&nbsp;boolean&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;”我就是整个系统提示词”(独占模式)}</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">规则:每个插件只声明自己的片段,不知道也不关心别人。</span>组装时框架负责排序、拼接、冲突检测。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>5.3 组装产物:PromptAssembly——四路输入的总装</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">export&nbsp;interface&nbsp;PromptAssembly&nbsp;{&nbsp;&nbsp;sections:&nbsp;AssembledSection[]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;静态/半静态规则&nbsp;&nbsp;contexts:&nbsp;AssembledContext[]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;动态上下文&nbsp;&nbsp;tools:&nbsp;ToolSchema[]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;工具&nbsp;schema&nbsp;&nbsp;variables:&nbsp;Record&nbsp;&nbsp;&nbsp;//&nbsp;模板变量}</pre></section><table><thead><tr class="firstRow"><th style="text-align: left;"><section><span>路</span></section></th><th style="text-align: left;"><section><span>内容</span></section></th><th style="text-align: left;"><section><span>生命周期</span></section></th></tr></thead><tbody><tr><td style="text-align: left;"><section><span>sections</span></section></td><td style="text-align: left;"><section><span>规则性文本</span></section></td><td style="text-align: left;"><section><span>基本静态,配置时注册</span></section></td></tr><tr><td style="text-align: left;"><section><span>contexts</span></section></td><td style="text-align: left;"><section><span>动态信息</span></section></td><td style="text-align: left;"><section><span><span style="font-weight: 500;">每次请求现算</span></span></section></td></tr><tr><td style="text-align: left;"><section><span>tools</span></section></td><td style="text-align: left;"><section><span>工具 schema</span></section></td><td style="text-align: left;"><section><span>注册时收集,组装时排序</span></section></td></tr><tr><td style="text-align: left;"><section><span>variables</span></section></td><td style="text-align: left;"><code><span>{{date}}</span></code><section><span>&nbsp;类占位</span></section></td><td style="text-align: left;"><section><span>渲染时才插值</span></section></td></tr></tbody></table><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">组装(assemble)和渲染(render)是两步。</span>组装产生结构化的 PromptAssembly,渲染才把它变成字符串。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>5.4 变量插值为什么放最后</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>PromptSection.text 里可以写 {{variable}},但<span style="font-weight: 500;">插值是渲染阶段的事</span>:</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">//&nbsp;组装时:只是把文本解析出来,保留&nbsp;{{var}}&nbsp;原样//&nbsp;渲染时:renderPrompt(assembly)&nbsp;才把&nbsp;{{var}}&nbsp;替换成&nbsp;assembly.variables&nbsp;里的值</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">为什么?</span>sections/contexts/tools 三个阶段都可能贡献变量,如果组装时就插值,顺序耦合就出现了。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>5.5 complete 语义</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">readonly&nbsp;complete?:&nbsp;boolean//&nbsp;若某&nbsp;section&nbsp;标记&nbsp;complete=true://&nbsp;&nbsp;&nbsp;组装仍跑&nbsp;waterfall//&nbsp;&nbsp;&nbsp;但最终只保留这一个&nbsp;section&nbsp;作为系统提示词//&nbsp;多个&nbsp;complete&nbsp;同时生效&nbsp;→&nbsp;组装失败</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">为什么需要它?</span>有些场景要求&quot;整个系统提示词是我说了算&quot;。complete 是<span style="font-weight: 500;">显式的整体替换开关</span>,冲突直接报错,不会静默覆盖。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>5.6 Python 对照:带组装/渲染两阶段的提示词系统</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">from&nbsp;dataclasses&nbsp;import&nbsp;dataclass,&nbsp;fieldimport&nbsp;re&nbsp;@dataclassclass&nbsp;Section:&nbsp;&nbsp;&nbsp;&nbsp;name:&nbsp;str&nbsp;&nbsp;&nbsp;&nbsp;order:&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;text:&nbsp;str&nbsp;|&nbsp;callable&nbsp;&nbsp;&nbsp;&nbsp;complete:&nbsp;bool&nbsp;=&nbsp;False&nbsp;@dataclassclass&nbsp;Assembly:&nbsp;&nbsp;&nbsp;&nbsp;sections:&nbsp;list[dict]&nbsp;=&nbsp;field(default_factory=list)&nbsp;&nbsp;&nbsp;&nbsp;contexts:&nbsp;list[str]&nbsp;=&nbsp;field(default_factory=list)&nbsp;&nbsp;&nbsp;&nbsp;tools:&nbsp;list[dict]&nbsp;=&nbsp;field(default_factory=list)&nbsp;&nbsp;&nbsp;&nbsp;variables:&nbsp;dict&nbsp;=&nbsp;field(default_factory=dict)&nbsp;class&nbsp;SystemPrompt:&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;__init__(self):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self._sections:&nbsp;list[Section]&nbsp;=&nbsp;[]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;add(self,&nbsp;s:&nbsp;Section):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;any(x.name&nbsp;==&nbsp;s.name&nbsp;for&nbsp;x&nbsp;in&nbsp;self._sections):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise&nbsp;ValueError(f”重复&nbsp;section:&nbsp;{s.name}”)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;s.complete&nbsp;and&nbsp;any(x.complete&nbsp;for&nbsp;x&nbsp;in&nbsp;self._sections):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise&nbsp;ValueError(”多个&nbsp;complete&nbsp;section&nbsp;冲突”)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self._sections.append(s)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;assemble(self,&nbsp;context:&nbsp;dict,&nbsp;tools:&nbsp;list[dict])&nbsp;-&gt;&nbsp;Assembly:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sections&nbsp;=&nbsp;[]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;s&nbsp;in&nbsp;sorted(self._sections,&nbsp;key=lambda&nbsp;x:&nbsp;x.order):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text&nbsp;=&nbsp;s.text(context)&nbsp;if&nbsp;callable(s.text)&nbsp;else&nbsp;s.text&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sections.append({”name”:&nbsp;s.name,&nbsp;”text”:&nbsp;text})&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;completes&nbsp;=&nbsp;[x&nbsp;for&nbsp;x&nbsp;in&nbsp;sections&nbsp;if&nbsp;any(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.name&nbsp;==&nbsp;x[”name”]&nbsp;and&nbsp;s.complete&nbsp;for&nbsp;s&nbsp;in&nbsp;self._sections)]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;completes:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sections&nbsp;=&nbsp;completes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;Assembly(sections=sections,&nbsp;tools=tools,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;variables=context.get(”variables”,&nbsp;{}))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;render(self,&nbsp;assembly:&nbsp;Assembly)&nbsp;-&gt;&nbsp;str:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parts&nbsp;=&nbsp;[s[”text”]&nbsp;for&nbsp;s&nbsp;in&nbsp;assembly.sections]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;assembly.tools:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parts.append(”可用工具:&nbsp;”&nbsp;+&nbsp;”,&nbsp;”.join(t[”name”]&nbsp;for&nbsp;t&nbsp;in&nbsp;assembly.tools))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text&nbsp;=&nbsp;”\n\n”.join(parts)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;key,&nbsp;value&nbsp;in&nbsp;assembly.variables.items():&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text&nbsp;=&nbsp;re.sub(r”\{\{\s*”&nbsp;+&nbsp;key&nbsp;+&nbsp;r”\s*\}\}”,&nbsp;str(value),&nbsp;text)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;text&nbsp;sp&nbsp;=&nbsp;SystemPrompt()sp.add(Section(”identity”,&nbsp;-100,&nbsp;”你是自动化&nbsp;agent。”))sp.add(Section(”persona”,&nbsp;0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lambda&nbsp;ctx:&nbsp;f”你是{ctx[&#39;deployment&#39;]}的助手,今天是{{{{date}}}}。”))sp.add(Section(”rules”,&nbsp;150,&nbsp;”调用工具前必须说明目的。”))&nbsp;asm&nbsp;=&nbsp;sp.assemble({”deployment”:&nbsp;”工厂质检”,&nbsp;”variables”:&nbsp;{”date”:&nbsp;”2026-08-14”}},&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[{”name”:&nbsp;”read_file”},&nbsp;{”name”:&nbsp;”search”}])print(sp.render(asm))</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">对照 dsh 的差距:</span>dsh 的 text 函数接收 AssembleContext(带 scope/signal),支持 agent 级隔离;工具 schema 是完整 JSON Schema。<span style="font-weight: 500;">但&quot;组装/渲染两阶段 + 变量后插值 + complete 独占 + 冲突显性化&quot;四个核心已实现。</span></span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>5.7 本章小结</span></h3><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span>系统提示词 =&nbsp;<span style="font-weight: 500;">sections + contexts + tools + variables</span>&nbsp;四路输入的总装</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">组装与渲染分离</span>:结构化中间产物可被插件检查改写</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">变量后插值</span>:解耦&quot;谁提供值&quot;与&quot;谁使用值&quot;</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">text 可以是函数</span>:每次请求现场生成</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">complete</span>:显式整体替换,冲突直接报错</span></section></li></ul><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>第 6 章 一切皆插件:Cordis 微内核</span></h2><blockquote style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.55);line-height: 1.8;margin-bottom: 24px;"><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>核心机制:<span style="font-weight: 500;">三类事件语义(waterfall/serial/emit)</span>、<span style="font-weight: 500;">作用域(scope)</span>、<span style="font-weight: 500;">服务生命周期</span>。</span></p></blockquote><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>6.1 Cordis 只做三件事</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">加载(依赖解析、拓扑排序)卸载(副作用逆序回滚)事件(waterfall&nbsp;/&nbsp;serial&nbsp;/&nbsp;emit&nbsp;三种语义)</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">&quot;连 agent loop 都是插件&quot;</span>意味着:dsh 里没有&quot;内核&quot;——所有能力都是平级插件。想换主循环?写个插件替换 ctx.agentLoop。想换模型?换个适配器插件。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>6.2 插件三要素</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">export&nbsp;const&nbsp;name&nbsp;=&nbsp;&#39;tool-bash&#39;export&nbsp;const&nbsp;inject&nbsp;=&nbsp;[&#39;tools&#39;,&nbsp;&#39;shell&#39;,&nbsp;&#39;systemPrompt&#39;,&nbsp;&#39;shellEnv&#39;]&nbsp;export&nbsp;function&nbsp;apply(ctx:&nbsp;Context):&nbsp;void&nbsp;{&nbsp;&nbsp;ctx.tools.register(bashTool)}</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">inject 不是装饰,是契约</span>:Cordis 加载插件前会解析依赖图,缺依赖的插件<span style="font-weight: 500;">根本不加载</span>。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>6.3 三类事件语义</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">//&nbsp;①&nbsp;waterfall:监听器必须调用&nbsp;next()&nbsp;才能放行ctx.emit(&#39;tools/pre-execute&#39;,&nbsp;data,&nbsp;(decision)&nbsp;=&gt;&nbsp;{&nbsp;})//&nbsp;权力:可以拦截、可以修改&nbsp;//&nbsp;②&nbsp;serial:按注册顺序执行,但不能改写结果ctx.emit(&#39;agent/turn-stopping&#39;,&nbsp;{&nbsp;turn,&nbsp;signal&nbsp;})//&nbsp;权力:可以感知、可以追加副作用&nbsp;//&nbsp;③&nbsp;emit:异步通知,监听器互不干扰ctx.emit(&#39;session/event&#39;,&nbsp;event)//&nbsp;权力:只能旁观</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">Cordis 用事件模式把&quot;权力&quot;显式化:要拦截用 waterfall,要感知用 emit。</span></span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>6.4 作用域(scope)</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">ctx.on(&#39;tools/pre-execute&#39;,&nbsp;handler,&nbsp;{&nbsp;scope:&nbsp;agentId&nbsp;})ctx.provide(&#39;llm&#39;,&nbsp;impl,&nbsp;{&nbsp;scope:&nbsp;agentId&nbsp;})</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">scope 是&quot;多 agent 世界的防火墙&quot;</span>——每个 agent 有自己独立的插件视角。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>6.5 卸载回滚</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">ctx.provide(&#39;llm&#39;,&nbsp;impl)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;→&nbsp;记录:&nbsp;卸载时删除&nbsp;&#39;llm&#39;ctx.on(&#39;tools/pre-execute&#39;,&nbsp;fn)&nbsp;&nbsp;&nbsp;//&nbsp;→&nbsp;记录:&nbsp;卸载时移除监听器ctx.tools.register(tool)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;→&nbsp;记录:&nbsp;卸载时注销工具&nbsp;//&nbsp;卸载时:逆序执行回滚栈</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">为什么逆序?</span>后注册的往往依赖先注册的。逆序回滚保证依赖关系不被破坏。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>6.6 Python 对照:带三类事件和回滚的插件容器</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">import&nbsp;asyncio&nbsp;class&nbsp;Cordis:&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;__init__(self):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self._services&nbsp;=&nbsp;{}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self._listeners&nbsp;=&nbsp;{}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self._rollbacks&nbsp;=&nbsp;[]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;provide(self,&nbsp;name,&nbsp;impl,&nbsp;scope=”*”):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;key&nbsp;=&nbsp;(name,&nbsp;scope)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self._services[key]&nbsp;=&nbsp;impl&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self._rollbacks.append(lambda:&nbsp;self._services.pop(key,&nbsp;None))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;get(self,&nbsp;name,&nbsp;scope=”*”):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;self._services.get((name,&nbsp;scope))&nbsp;or&nbsp;self._services.get((name,&nbsp;”*”))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;on(self,&nbsp;event,&nbsp;handler,&nbsp;scope=”*”):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self._listeners.setdefault((event,&nbsp;scope),&nbsp;[]).append(handler)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self._rollbacks.append(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lambda:&nbsp;self._listeners[(event,&nbsp;scope)].remove(handler))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;_collect(self,&nbsp;event,&nbsp;scope):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(self._listeners.get((event,&nbsp;scope),&nbsp;[])&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;self._listeners.get((event,&nbsp;”*”),&nbsp;[]))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;async&nbsp;def&nbsp;waterfall(self,&nbsp;event,&nbsp;data,&nbsp;scope=”*”,&nbsp;default=None):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;handler&nbsp;in&nbsp;self._collect(event,&nbsp;scope):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;await&nbsp;handler(data)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;result&nbsp;is&nbsp;not&nbsp;None:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;result&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;default&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;async&nbsp;def&nbsp;serial(self,&nbsp;event,&nbsp;data,&nbsp;scope=”*”):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;handler&nbsp;in&nbsp;self._collect(event,&nbsp;scope):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;await&nbsp;handler(data)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;emit(self,&nbsp;event,&nbsp;data,&nbsp;scope=”*”):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;handler&nbsp;in&nbsp;self._collect(event,&nbsp;scope):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;asyncio.create_task(handler(data))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;load(self,&nbsp;plugin):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;dep&nbsp;in&nbsp;plugin.get(”inject”,&nbsp;[]):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;self.get(dep)&nbsp;is&nbsp;None:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise&nbsp;RuntimeError(f”{plugin[&#39;name&#39;]}&nbsp;缺少依赖&nbsp;{dep}”)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;plugin[”apply”](self)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self._rollbacks.append(lambda:&nbsp;print(f”[卸载]&nbsp;{plugin[&#39;name&#39;]}”))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;unload_all(self):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;fn&nbsp;in&nbsp;reversed(self._rollbacks):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fn()</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">对照真实 Cordis 的差距:</span>真 Cordis 有完整的异步生命周期、依赖图拓扑排序、next() 委托链、作用域的正式分层。<span style="font-weight: 500;">但三类事件语义、作用域回退、逆序回滚三个骨架已实现。</span></span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>6.7 本章小结</span></h3><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span>Cordis = 加载 + 卸载 + 事件</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">三类事件语义 = 三种权力</span>:waterfall 把关、serial 收尾、emit 旁观</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">scope</span>&nbsp;= 多 agent 世界的防火墙</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">卸载逆序回滚</span>&nbsp;= 无孤儿状态</span></section></li></ul><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>第 7 章 能力缝(seam)——可替换的能力</span></h2><blockquote style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.55);line-height: 1.8;margin-bottom: 24px;"><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>核心机制:<span style="font-weight: 500;">seam 三角色</span>、</span><code><span><span style="font-weight: 500;">import type</span></span></code><span><span style="font-weight: 500;">&nbsp;强制解耦</span>、<span style="font-weight: 500;">&quot;换 Provider = 搬家&quot;</span>、<span style="font-weight: 500;">isolate realm</span>。</span></p></blockquote><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>7.1 什么是 seam:衣服的接缝</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>一件衣服换袖子,不会把整件衣服重做——因为**接缝(seam)**把袖子和其他部分解耦了。</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">Service&nbsp;Definition(接口声明)&nbsp;&nbsp;&nbsp;←&nbsp;接缝本身&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;↑&nbsp;实现&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;↑&nbsp;使用Service&nbsp;Provider(实现)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Consumer(消费者)</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>以 ctx.fs 为例:</span></p><table><thead><tr class="firstRow"><th style="text-align: left;"><section><span>角色</span></section></th><th style="text-align: left;"><section><span>是什么</span></section></th><th style="text-align: left;"><section><span>真实代码</span></section></th></tr></thead><tbody><tr><td style="text-align: left;"><section><span>Definition</span></section></td><td style="text-align: left;"><code><span>ctx.fs</span></code><section><span>&nbsp;接口</span></section></td><td style="text-align: left;"><code><span>packages/fs/fs/src/index.ts</span></code></td></tr><tr><td style="text-align: left;"><section><span>Provider</span></section></td><td style="text-align: left;"><section><span>具体实现</span></section></td><td style="text-align: left;"><code><span>fs-local</span></code><section><span>、</span><code><span>fs-sandbox</span></code><span>、</span><code><span>fs-e2b</span></code></section></td></tr><tr><td style="text-align: left;"><section><span>Consumer</span></section></td><td style="text-align: left;"><section><span>模型调用的工具</span></section></td><td style="text-align: left;"><code><span>tool-fs</span></code></td></tr></tbody></table><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">Consumer 只依赖接口</span>——这是 seam 的全部秘密。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>7.2 import type 强制解耦</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">import&nbsp;type&nbsp;{&nbsp;}&nbsp;from&nbsp;&#39;@deepseek-ai/dsh-fs&#39;&nbsp;&nbsp;&nbsp;//&nbsp;←&nbsp;只&nbsp;import&nbsp;接口(type-only!)&nbsp;export&nbsp;const&nbsp;inject&nbsp;=&nbsp;[&#39;fs&#39;,&nbsp;&#39;tools&#39;,&nbsp;&#39;systemPrompt&#39;]&nbsp;export&nbsp;function&nbsp;apply(ctx:&nbsp;Context):&nbsp;void&nbsp;{&nbsp;&nbsp;const&nbsp;readTool&nbsp;=&nbsp;defineTool({&nbsp;&nbsp;&nbsp;&nbsp;name:&nbsp;&#39;read_file&#39;,&nbsp;&nbsp;&nbsp;&nbsp;async&nbsp;execute(args,&nbsp;exec)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;ctx.fs.read(args.path,&nbsp;exec)&nbsp;&nbsp;&nbsp;//&nbsp;调用接口,不知道背后是谁&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;&nbsp;})&nbsp;&nbsp;ctx.tools.register(readTool)}</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">import type是关键词</span>——tool-fs 只引入类型,不引入任何 Provider 实现。<span style="font-weight: 500;">编译期就保证了 Consumer 与 Provider 解耦。</span></span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>7.3 换 Provider = 搬家</span></h3><blockquote style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.55);line-height: 1.8;margin-bottom: 24px;"><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>文件系统与进程提供方共享同一个执行世界,因此把它们指向远程沙箱,也就把 Bash、PTY 和 LSP 一并搬了过去。</span></p></blockquote><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>拆开看:</span></p><ol style="list-style-type: decimal;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><code><span>ctx.fs</span></code><section><span>&nbsp;有多个 Provider(local/sandbox/e2b)</span></section></li><li style="margin-bottom: 0px;"><code><span>ctx.subprocess</span></code><section><span>&nbsp;有多个 Provider(local/e2b)</span></section></li><li style="margin-bottom: 0px;"><code><span>ctx.shell</span></code><section><span>&nbsp;通过&nbsp;</span><code><span>ctx.subprocess</span></code><span>&nbsp;执行</span></section></li><li style="margin-bottom: 0px;"><code><span>ctx.lsp</span></code><section><span>&nbsp;也通过&nbsp;</span><code><span>ctx.subprocess</span></code><span>&nbsp;启动</span></section></li></ol><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">所以:把subprocess和fs的 Provider 从 local 换成 e2b,shell、terminal、lsp全部自动跟着去远程。</span></span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>7.4 isolate realm</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">realm 是比 scope 更严格的服务隔离</span>:scope 是&quot;按 agent 划分视角&quot;,realm 是&quot;一个 agent 完全拥有自己的服务实例&quot;。</span></p><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>主 agent 的 ctx.llm 配置 A 模型,子 agent 的 ctx.llm 配置 B 模型——<span style="font-weight: 500;">同名的服务,不同的 realm,各自独立</span>。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>7.5 Python 对照:带 seam 的架构升级</span></h3><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"></ul><pre class="code-snippet__js">from&nbsp;abc&nbsp;import&nbsp;ABC,&nbsp;abstractmethod&nbsp;class&nbsp;Shell(ABC):&nbsp;&nbsp;&nbsp;&nbsp;@abstractmethod&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;run(self,&nbsp;cmd:&nbsp;str)&nbsp;-&gt;&nbsp;str:&nbsp;...&nbsp;class&nbsp;BashLocal(Shell):&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;run(self,&nbsp;cmd:&nbsp;str)&nbsp;-&gt;&nbsp;str:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;import&nbsp;subprocess&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;subprocess.run(cmd,&nbsp;shell=True,&nbsp;capture_output=True,&nbsp;text=True).stdout&nbsp;class&nbsp;BashSandbox(Shell):&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;run(self,&nbsp;cmd:&nbsp;str)&nbsp;-&gt;&nbsp;str:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;”rm”&nbsp;in&nbsp;cmd:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise&nbsp;PermissionError(f”[沙箱]&nbsp;拒绝危险命令:&nbsp;{cmd}”)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;f”[沙箱执行]&nbsp;{cmd}&nbsp;→&nbsp;ok”&nbsp;class&nbsp;BashRemote(Shell):&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;run(self,&nbsp;cmd:&nbsp;str)&nbsp;-&gt;&nbsp;str:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;f”[远程执行]&nbsp;{cmd}&nbsp;→&nbsp;ok”&nbsp;class&nbsp;ToolBash:&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;__init__(self,&nbsp;shell:&nbsp;Shell):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self._shell&nbsp;=&nbsp;shell&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;execute(self,&nbsp;cmd:&nbsp;str)&nbsp;-&gt;&nbsp;str:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;self._shell.run(cmd)&nbsp;CONFIG&nbsp;=&nbsp;{”provider”:&nbsp;”sandbox”}&nbsp;def&nbsp;make_tool()&nbsp;-&gt;&nbsp;ToolBash:&nbsp;&nbsp;&nbsp;&nbsp;provider&nbsp;=&nbsp;CONFIG[”provider”]&nbsp;&nbsp;&nbsp;&nbsp;shell&nbsp;=&nbsp;{”local”:&nbsp;BashLocal,&nbsp;”sandbox”:&nbsp;BashSandbox,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;”remote”:&nbsp;BashRemote}[provider]()&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;ToolBash(shell)&nbsp;CONFIG[”provider”]&nbsp;=&nbsp;”local”print(make_tool().execute(”echo&nbsp;hi”))&nbsp;CONFIG[”provider”]&nbsp;=&nbsp;”sandbox”print(make_tool().execute(”echo&nbsp;hi”))try:&nbsp;&nbsp;&nbsp;&nbsp;make_tool().execute(”rm&nbsp;-rf&nbsp;/”)except&nbsp;PermissionError&nbsp;as&nbsp;e:&nbsp;&nbsp;&nbsp;&nbsp;print(”被拦截:”,&nbsp;e)&nbsp;CONFIG[”provider”]&nbsp;=&nbsp;”remote”print(make_tool().execute(”echo&nbsp;hi”))</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">对照 dsh 的差距:</span>dsh 的 Provider 是插件(通过 cordis 配置加载,可热插拔),realm/scope 提供运行时隔离。<span style="font-weight: 500;">但&quot;接口定义 → Provider 注册 → 配置切换 → 业务不变&quot;这条链已跑通。</span></span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>7.6 本章小结</span></h3><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span>seam =&nbsp;<span style="font-weight: 500;">接口声明 + Provider + Consumer</span>&nbsp;三角色</span></section></li><li style="margin-bottom: 0px;"><code><span>import type</span></code><section><span>&nbsp;在编译期强制解耦</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">换 Provider = 搬家</span>:fs/subprocess 换 Provider,shell/lsp/terminal 全部跟随</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">isolate realm</span>:agent 拥有自己的服务实例</span></section></li></ul><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>中篇小结</span></h2><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>四章下来,你已经掌握了让 Agent 能干活、可替换、可扩展的机制:</span></p><ol style="list-style-type: decimal;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">工具系统</span>:schema 驱动、五段流水线、决策与执行分离、并发声明、作用域隔离</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">提示词组装</span>:四路输入、组装/渲染分离、变量后插值、complete 独占</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">Cordis 插件内核</span>:三类事件语义、scope、卸载回滚</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">能力缝 seam</span>:接口定义 + Provider + Consumer,换 Provider = 搬家</span></section></li></ol>                            <div class="content-mask">
                            <a href="javascript:void (0);" class="mask-text login-trigger">
                                <i class="iconfont icon-suo"></i>
                                登录查看剩余 70% 内容
                                <i class="iconfont icon-arrow-right-o"></i>
                            </a>
                        </div>
                    </div>
                                        </div>
标签:AI

想了解 AI 如何助力您的企业?

免费获取企业 AI 成熟度诊断报告,发现转型机会

置顶文章

会打字,就能"拍"电影:ScriptTask 开放限量内测
置顶

会打字,就能"拍"电影:ScriptTask 开放限量内测

//

24小时热榜

TOP1

为什么DeepSeek Harness选择了Cordis作为Agent的内核?

DeepSeek Harness 源码深潜(上)——从 API 到 Agent 心脏(附源码)
前途科技前途科技
服务关于快讯技术商业报告
前途科技微信公众号

微信公众号

扫码关注

Copyright © 2026 AccessPath.com, 前途国际科技咨询(北京)有限公司,版权所有。|京ICP备17045010号-1|京公网安备 11010502033860号|隐私政策|服务条款
TOP2

DeepSeek Harness 源码深潜(上)——从 API 到 Agent 心脏(附源码)

3

DeepSeek Harness 源码深潜(中)——工具、提示词与插件内核(附DSH源码)

1小时前
DeepSeek Harness 源码深潜(中)——工具、提示词与插件内核(附DSH源码)
4

DeepSeek Harness只需装这一个插件即可

1小时前
DeepSeek Harness只需装这一个插件即可
5

Huashu-Excel正式发布!可能、也许、大概是最好用的Excel数据处理和分析skill

1小时前
Huashu-Excel正式发布!可能、也许、大概是最好用的Excel数据处理和分析skill
6

DeepSeek Harness规模化踩坑实录:耗时、成本、失败到底该怎么查

1小时前
DeepSeek Harness规模化踩坑实录:耗时、成本、失败到底该怎么查
7

19 万 Star 之后,我给 DeepSeek Harness 接入了Milvus

1小时前
19 万 Star 之后,我给 DeepSeek Harness 接入了Milvus
8

RAG处理表格数据——Table RAG让我把Excel变成了知识库

1小时前
热门标签
大模型AgentRAG微调私有化部署Prompt EngineeringChatGPTClaudeDeepSeek智能客服知识管理内容生成代码辅助数据分析金融零售制造医疗教育AI 战略数字化转型ROI 分析OpenAIAnthropicGoogle

关注公众号

前途科技微信公众号

扫码关注,获取最新 AI 资讯

免费获取 AI 落地指南

3 步完成企业诊断,获取专属转型建议

已有 200+ 企业完成诊断