DeepSeek Harness 工具卡片怎么渲染?DSH plugin 的 presentCall 与纯函数契约

插件开发发布于 2026-10-07作者: DeepSeek Plugin 插件市场
DSH pluginDeepSeek Harness工具卡片presentCall渲染意图
DSH plugin 的工具输出为什么还要单独写卡片?本文按官方工具编写参考,讲清 presentCall 与 presentResult 返回的渲染意图、generic/terminal/diff/search/web 五种卡片标签的选用,以及纯函数、UI 格式不进模型结果、展示路径软校验三条硬性规则。

DSH plugin 的工具输出分两条路:output.render 返回模型可见的内容,而「卡片」是另一项独立关注点,通过纯展示投影与可选的 presentCall/presentResult 声明(来源)。 本文面向 DSH插件 / DeepSeek插件 作者,讲清两个方法各自返回什么、五种 card 标签怎么选,以及三条违反就出问题的硬性规则;defineTool 与 output.schema 的契约见 工具 execute 契约,设置页卡片(Host 半 + 浏览器半)见 插件 UI 开发,这里不重复。

DSH plugin 的 presentCall:待执行卡片能返回哪三种 card

presentCall(args) 返回一个待执行阶段的 ToolCallView(PENDING 卡片),覆盖工具还没产出结果的那段时间(来源)。 可用类型有三种:

  1. generic(默认) — { card: 'generic', title, kind?, rawInput?, content?, locations? }。设置 kind 获取图标(read/search/…);设置 locations: [{ path, line? }] 标注工具涉及的文件,使有能力的编辑器跟随或跳转。
  2. terminal — { card: 'terminal', title, description?, cwd? }。你的调用本身就是 shell 命令:title 是命令,description 渲染在终端卡片上方(tool-bash 就是这么做的)。
  3. diff — { card: 'diff', title, diffs, locations? }。diffs: [{ path, oldText, newText }] 渲染为内联 diff 卡片(新文件时 oldText: null,tool-fs 的 write/edit 如此)。

注意没有 search:发现型结果要等 execute 之后才存在,所以发现型调用的 pending 状态只能保持 generic 卡片。

DSH plugin 的 presentResult:完成卡片与五种 card 的细节

presentResult(args, { content, isError, meta? }) 返回完成后的卡片,其中 search 与 web 的两处细节最容易忽略(来源)。

  1. generic — 提供可选的标题和内容。
  2. terminal — 提供原始输出和可选的退出元数据;各 UI 按自身能力渲染对应视图或回退视图。
  3. diff — 提供已应用的 hunk,通常由 output.presentationMeta 派生并通过持久化的 result.meta 携带,使回放能重现它们。变更类工具保留 diff 结果,因为完成后的视图会替换 pending 卡片。
  4. search — 从持久化 result.meta 重建的发现型结果:按文件分组的匹配(shape: 'matches',grep)或扁平路径列表(shape: 'paths',glob),外加 truncated/total,使 UI 永不把被截断的结果当作完整结果呈现。该视图不携带结果文本(没有 search 卡片能力的 UI 回退到原始结果内容),也没有 search 调用视图(tool-fs-search 的 grep/glob)。
  5. web — 已完成的 web 检索,以 kind: 'search' | 'fetch' 区分(结构化的搜索来源或抓取摘要),由 result.meta 派生;它不携带正文副本,因此不具备 web 能力的 UI 回退到原始结果内容(tool-web 的 web_search/web_fetch)。

DSH plugin 工具卡片的三条硬性规则

官方把「违反会出问题」的规则单列出来,三条都要守住(来源)。

  1. 纯函数 — 这些方法在实时流式输出和会话日志回放时都会运行,因此必须是 args(加 result)的纯函数:不做 I/O、不读会话状态、不用时钟或随机数。diff 从 args 派生(write 使用 oldText: null,因为调用时的展示器没有文件先前内容);会话上下文由 UI 适配器而非工具提供。如果你发现自己在 presentCall 内获取文件旧内容或工作目录,请停下:那属于持久结果元数据或适配器,不属于展示器。
  2. UI 格式不进入模型结果 — 围栏 console 块、diff、相对化路径均不应仅为服务 UI 而进入规范值或 Native 内容。output.render 负责模型可见的自然语言;presentationMeta 和卡片展示器负责可回放的 UI 状态;terminal 结果视图携带原始输出,由适配器按需添加回退格式。
  3. defineTool 对展示路径做软校验 — 格式错误或旧版日志中的参数会使包装器返回 undefined(通用回退)而非抛异常,因为展示绝不能导致回放崩溃。

还有一条架构纪律:中性词汇定义在 dsh-tools 中,工具绝不导入 UI 或传输类型;host/client 运行时把每个 card 映射到各自的视图。想对照参考实现,可在 DSH Plugin Hub 找工具类插件(dsh-tool-fs 是 generic/diff、dsh-tool-bash 是 terminal);output.render 与规范值的契约见 工具 execute 契约,工具在 UI 插件里如何被消费见 插件 UI 开发。

常见问题

DSH plugin 工具已经有 output.render 了,为什么还要写卡片?

DSH plugin 的 output.render 返回模型可见的内容,而工具卡片是另一项独立关注点:卡片通过纯展示投影与可选的 presentCall/presentResult 声明,只服务人看的界面。没有 UI 展示方法的工具会回退到通用卡片(标题等于工具名、原始 args 作为输入),所以两者要一起设计。

DSH plugin 的 presentCall 和 presentResult 分别返回什么?

DSH plugin 里 presentCall(args) 返回待执行阶段的 ToolCallView(PENDING 卡片),presentResult(args, { content, isError, meta? }) 返回完成后的卡片。两个方法都返回一个带 card 标签的渲染意图,card 取值在 generic、terminal、diff、search、web 之间选,要与工具行为匹配。

DSH plugin 的 search 卡片为什么没有 pending 状态?

DSH plugin 的发现型结果只在 execute 之后才存在,所以 search 卡片没有 pending 状态:它从持久化的 result.meta 重建,grep 用 shape: 'matches' 按文件分组、glob 用 shape: 'paths' 列扁平路径,并带 truncated/total 让 UI 永不把被截断的结果当作完整结果。发现型调用的 pending 状态因此保持 generic 卡片,不存在 search 调用视图。

DSH plugin 的卡片展示器为什么必须是纯函数?

DSH plugin 的卡片方法必须是 args(加 result)的纯函数,因为它在实时流式输出和会话日志回放时都会运行——不做 I/O、不读会话状态、不用时钟或随机数。diff 只能从 args 派生(write 用 oldText: null,因为调用时的展示器拿不到文件先前内容);会话上下文由 UI 适配器提供。如果你想在 presentCall 内获取文件旧内容或工作目录,就说明那属于持久结果元数据或适配器,不属于展示器。

DSH plugin 卡片渲染出错会拖垮回放吗?

不会。DSH plugin 的 defineTool 对展示路径做软校验:格式错误或旧版日志中的参数会使包装器返回 undefined(通用回退)而不是抛异常,因为展示绝不能导致回放崩溃。反过来,工具的 UI 格式(围栏 console 块、diff、相对化路径)也不应仅为服务 UI 而进入规范值或 Native 内容。

相关术语

工具卡片(tool card)
工具卡片是 DeepSeek Harness 中工具调用的纯展示投影,通过可选的 presentCall/presentResult 方法与规范值一并设计,与负责模型可见内容的 output.render 相互独立,缺少展示方法的工具回退到通用卡片。— https://deepseek-harness.github.io/deepseek-harness/reference/cookbook/adding-a-tool
渲染意图(render intent)
渲染意图是 presentCall/presentResult 返回的带 card 标签的对象,用 generic、terminal、diff、search、web 描述这次调用应渲染成哪类卡片,host 与 client 运行时再把每个 card 映射到各自视图。— https://deepseek-harness.github.io/deepseek-harness/reference/cookbook/adding-a-tool
PENDING 卡片(ToolCallView)
PENDING 卡片是 presentCall 返回的待执行视图,覆盖工具尚未产出结果的时间段;它只能用 generic、terminal 或 diff,因为发现型结果要等 execute 之后才存在,所以不存在 search 调用视图。— https://deepseek-harness.github.io/deepseek-harness/reference/cookbook/adding-a-tool
result.meta(持久化结果元数据)
result.meta 是核心持久化在 tool/result 上的可回放元数据,通常由 output.presentationMeta 派生;search 与 web 卡片从它重建发现型结果,diff 卡片也用它在回放中重现已应用 hunk。— https://deepseek-harness.github.io/deepseek-harness/reference/cookbook/adding-a-tool

来源