把 L12 的 scratch-plugin/src/my-plugin.ts 整个替换为:
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet someone by name.',
parameters: {
name: { type: 'string', required: true, description: 'The name to greet' },
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
return `Hello, ${args.name}!`
},
}))
}
重启(或热重载)后,在 Web UI 里对模型说:"用 greet 工具向 Ada 问好"——模型会调用 greet,并把收到的 Hello, Ada! 作为工具结果继续对话。
| 字段 | 作用 | 通俗理解 |
|---|---|---|
name | 工具调用名 | 工具的"工号",模型按它发起调用 |
description | 何时该用这个工具 | 写给模型看的说明书——模型全靠它判断"现在该不该用你"。写不好等于工具不存在 |
parameters | 参数 schema | 点菜单:收什么参数、什么类型、哪个必填 |
output.schema | 返回值 schema | 上菜的标准盘子形状 |
output.render | 值 → 模型可见内容 | 把菜摆盘端给模型 |
execute(args, exec) | 执行逻辑 | 后厨。args 已按 schema 校验过类型 |
parameters 自动变成模型可见的 JSON Schema,汇入每次请求的工具清单(提示词组装的一部分)——不需要任何额外接线。// 一个更真实的例子:参数带取消信号的文件读取
async execute(args, exec) {
// args 类型自动推断为 { path: string; limit?: number }
return readFile(args.path, { encoding: 'utf8', signal: exec.signal })
}
工具结果的 UI 展示是设计的一部分(仓库明文规定"写工具时就要决定"),通过纯展示函数声明卡片类型:
| 卡片 | 适合 | 例子 |
|---|---|---|
generic | 普通结果,可加图标/文件位置 | read |
terminal | 你的调用就是一条命令 | bash |
diff | 创建/修改了文件 | write / edit |
search | 发现型结果(分组匹配/路径清单) | grep / glob |
web | 完成的网络检索 | web_search / web_fetch |
两条硬规则:① 纯函数——展示函数在直播和回放时都会跑,禁止 I/O、读会话状态、时钟/随机;② UI 专用格式不进模型结果——代码块装饰、相对路径美化是 UI 的事,模型内容保持干净。
长任务不堵会话:配置里开启后台门控 → execute 里走 ctx.jobs.start({ kind, label, owner: exec.agent, run }),成功返回类型化的后台句柄(如 { kind: 'background', jobId }),模型稍后用 job_* 控制工具查询/停止。Code Mode 绝不能从人类可读文字里解析 jobId——句柄必须是结构化的。
Code Mode(模型写代码调工具的模式)里,每个已注册工具自动变成 await tools.greet({ name: 'Ada' })——参数和返回类型从同一套 schema 推导,调用走正常执行管线,你什么都不用做。因此 output.schema 要当真正的程序 API 设计:直接返回句柄和字段,人类解释留在 render。
style: 'formal' | 'casual'(union 类型),两种风格返回不同问候语;验证模型会正确传参。{ greeting: string, length: number },render 里拼文本——体会"规范值给程序、render 给模型"的分工。throw new Error('boom'),观察 UI 如何展示 isError 结果。