自学教程

DeepSeek Harness 开发之defineTool 入门

DeepSeek Harness 开发第一个工具:defineTool 完全入门

在 DeepSeek Harness 中,所有智能体能力的最小单元是 Tool(工具)。无论是文件读写、终端执行、代码运行、网络请求,全部基于 defineTool 标准化定义。

defineTool 是 Harness 官方提供的工具定义原语:它统一描述工具名称、用途、参数结构、执行逻辑、输出规范,让大模型可以「读懂工具、自动决策调用、自动入参校验」。

本文带你从零手写第一个可被AI自动调用的自定义工具,完整覆盖工程结构、字段释义、参数规范、编译加载、Web 调试、常见报错,是所有高级插件开发的基础。

一、为什么必须用 defineTool

普通 Agent 框架中,工具只是一段函数;但在 Harness 架构中,工具是可被模型理解、可被内核托管、可生命周期回收、可配置校验的标准化能力。

使用 defineTool 拥有四大核心能力:

  • 模型自调度:通过 description 让 LLM 自主判断何时调用该工具
  • 自动参数校验:内核校验参数类型、必填项,非法参数直接拦截
  • 生命周期托管:随插件加载注册、随插件卸载销毁,无残留
  • 标准化协议:兼容 Function Call、PTC 代码模式、多模态串联任务

结论:所有自定义工具,必须通过 defineTool 注册,才能被 Harness 智能体识别和调用。

二、前置环境准备

沿用标准插件工程结构(前文统一规范):

  • Node.js 20+ / 22+
  • 已安装最新版 dsh:npm install -g @deepseek-ai/dsh@latest
  • 工程具备 tsconfig.json + cordis.yml

三、最简完整工程:第一个自定义工具

我们开发一个实用工具:时间时区查询工具,支持自定义时区获取系统时间,全程可直接复刻。

1. 工程目录

dsh-plugin-first-tool/
├── src/index.ts      # 工具主逻辑
├── cordis.yml        # 插件清单
├── package.json
└── tsconfig.json

2. 核心代码:defineTool 完整写法

src/index.ts

import { Context, defineTool } from "@deepseek-ai/dsh-core";// 插件标准入口
export function apply(ctx: Context) {
// 注册自定义工具
ctx.tools.register(defineTool({
// 工具唯一标识(全局唯一,小写+下划线)
name: "query_current_time", // 【最重要】模型调用依据,必须清晰、场景明确 description: "用于查询当前系统时间,支持指定时区。用户询问现在时间、北京时间、海外时区时间时调用。", // 入参结构定义(JSON Schema) parameters: { timezone: { type: "string", required: false, description: "目标时区,默认 Asia/Shanghai,例如:America/New_York、Europe/London" } }, // 工具执行逻辑 async execute(args) { const { timezone = "Asia/Shanghai" } = args; const now = new Date().toLocaleString("zh-CN", { timeZone: timezone, hour12: false }); return { timezone, datetime: now, timestamp: Date.now() }; } }));
}

3. cordis.yml 插件清单

name: dsh-plugin-first-tool
version: 0.1.0
description: 我的第一个 defineTool 自定义工具插件
main: dist/index.js
dependencies:
  "@deepseek-ai/dsh-core": ">=0.20.0"

4. 编译命令

tsc

四、defineTool 字段逐字详解(必背)

掌握以下字段,即可开发 99% 的自定义工具。

1. name(工具名)

  • 全局唯一,不可重复
  • 规范:小写、下划线分隔、语义清晰
  • 禁止中文、大写、特殊符号
  • 示例:query_current_time、file_batch_rename

2. description(工具描述,核心)

决定模型会不会调用你的工具。

书写公式:能力是什么 + 什么时候调用 + 适用场景

❌ 错误写法:获取时间(太简短,模型无法决策)

✅ 正确写法:用户询问当前时间、各时区时间时调用,可返回指定时区标准时间戳

3. parameters(参数结构)

标准 JSON Schema,内核自动校验:

  • type:string / number / boolean / object
  • required:是否必填
  • description:参数用途说明,辅助模型填参

4. execute(执行函数)

  • 必须 async 异步函数
  • args 为模型自动填充的参数对象
  • 返回结构化数据,便于模型总结输出
  • 支持文件操作、网络请求、计算逻辑

五、本地加载与实测调用

1. 加载本地插件

dsh plugin --profile web add ./当前插件目录

2. 查看是否加载成功

dsh plugin --profile web list

3. Web 实测调用

启动服务:dsh web,新建会话,输入自然语言指令:

  • 现在北京时间几点
  • 查询纽约当前时间
  • 获取当前时间戳

模型会自动识别、自动调用、自动传参你的自定义工具,无需手动触发。

六、工具生命周期与副作用规范

通过 ctx.tools.register 注册的工具,自动纳入插件生命周期管理:

  • 插件加载 → 工具注册生效
  • 插件卸载 → 工具自动注销,模型无法再调用
  • 热重载 → 旧工具销毁、新工具替换

如果工具内部存在定时器、长连接、监听,必须用 ctx.effect 托管(参考前文生命周期文章)。

七、进阶:带必填参数 + 数值校验工具示例

再写一个更强的结构化工具:数字加法计算器,带必填校验。

ctx.tools.register(defineTool({
  name: "math_add_calc",
  description: "高精度数字加法计算,用户需要计算两个数字相加时调用,支持小数运算",
  parameters: {
    a: {
      type: "number",
      required: true,
      description: "第一个加数"
    },
    b: {
      type: "number",
      required: true,
      description: "第二个加数"
    }
  },
  async execute(args) {
    const { a, b } = args;
    return {
      result: a + b,
      formula: `${a} + ${b} = ${a + b}`
    };
  }
}));

八、新手高频问题与排错

1. 插件加载成功,但模型不调用工具

99%原因:description 描述不清晰,模型不知道该在什么场景触发。

解决方案:补全场景、用途、触发条件,不要写极简描述。

2. 参数报错、参数类型不匹配

内核强校验 Schema,模型传参错误会直接拦截。优化参数 description,引导模型正确传参。

3. 旧会话不生效

工具注册仅对新建会话生效,测试必须新开对话。

4. 工具重复注册

name 全局唯一,修改重名工具名即可解决冲突。

九、defineTool 开发黄金规范

  1. 命名语义化:工具名见名知意,统一下划线小写风格
  2. 描述场景化:写清触发场景、用途、能力边界
  3. 参数精细化:必填/选填明确,每个参数带详细说明
  4. 返回结构化:不要返回纯文本,返回 JSON 便于模型二次推理
  5. 逻辑纯净化:单一工具只做一件事,职责单一
  6. 资源可回收:异步副作用全部 effect 托管

本篇小结

defineTool 是 DeepSeek Harness 插件开发的入门基石与核心原语。所有高级能力、工作流、智能体任务,底层都是无数标准化 Tool 的组合。

掌握 defineTool 的 命名规范、场景描述、参数 Schema、执行逻辑、生命周期托管,你就掌握了 Harness 自定义智能体能力的根本:让模型可控、可调度、可执行、可回收。

0 条笔记