deepseek-harness
tiwe0/GeoChat会话隔离的 GeoGebra 画布,提供 20 个原生工具用于作图、场景读回、文字排版检查与动画采样,并内置 55 个数学技能和离线 Applet 资源。
在网页或对话里编写 Python 工具,保存后免重启注册成 DSH 工具;危险工具每次执行都走内置审批,工具可分组成包,还可选「解锁模式」按对话逐步放出。需 PATH 中有 Python 3。
在 DeepSeek Harness(DSH)里用 Python 写工具,保存即生效。
两条入口都能用:
custom_tool_manage 工具。保存后的工具会立刻注册成真正的 DSH 工具(不需要重启 DSH),模型下一轮就能调用。所有数据只存在本机 %DSH_HOME%\custom-tools,不会上传。
custom_tool_manage能力 · 说明 创建工具 · 起一个模型可见的工具名,写描述、参数 JSON Schema、Python 代码 即时注册 · 保存/启用/改名/删除后,host 立即对账并注册或注销,无需重启 DSH 试运行 · 页面「测试」或 AI 的 custom_tool_manage → action="test",直接传参数跑一次 危险工具 · 标记后每次执行前都走 DSH 内置审批,和内置的自动审查兼容 工具包 · 把几个工具归到一个有名称、有描述的分组里,一键启停整包;工具仍各自注册 AI 主动解锁 · 开启后每个对话只看到管理/解锁两个工具,AI 解锁工具包后才出现包内工具;支持「始终解锁」与未分组工具单独解锁 统一存储 · 所有工具存在一个 tools.json,可整体备份、迁移、删除 一键恢复 · restore.ps1 把插件从 profile 里干净卸载,可选清空工具数据
不是 Skill、不是 MCP:工具是同进程注册的真正 DSH 工具,执行时由 host 启动本机 Python 进程运行你的脚本。
安装并重启 DSH 后,左侧导航栏会出现「自定义工具」(与「插件」「自动化任务」同一排)。点进去是工具列表页:
Python、危险、已禁用 徽标;N 个工具 · 启用 M,右侧是整包开关和 编辑/删除;没有归包的工具放在最后一张「未分组工具」卡片里;测试、编辑、删除,以及该工具自己的启用/停用开关;设置、刷新、新建工具包、新建工具。新建工具 打开编辑对话框:名称、标题、描述、所属工具包、参数 JSON Schema、Python 代码、危险工具开关、启用开关;def run(args): ... 约定;「测试」对话框里填 args JSON(就是传给 run(args) 的对象),点「运行」后显示成功/失败、结果、stdout、stderr 和耗时。测试失败不会弄坏已保存的工具。
def run(args):
text = args["text"]
return {
"chars": len(text),
"upper": text.upper(),
}args 是模型按参数 JSON Schema 传进来的参数对象(普通 Python dict);dict / list / str / 数字 / bool / None)。host 会把它 json.dumps(..., ensure_ascii=False) 后作为工具结果给模型看;print() 是日志通道,两者互不干扰:print() / sys.stdout 的内容作为 stdout 展示(最多保留末尾 8,000 字符);sys.stderr 的内容进 stderr;抛出的异常被抓成 error(含 traceback),不会混进 stderr;DSH_CUSTOM_TOOL_TIMEOUT_MS 调整(毫秒),改完重启 DSH;runs 下建一个临时目录(tool.py、args.json、result.json、stdout.txt、stderr.txt),只保留最新 30 个;DSH_CUSTOM_TOOL_PYTHON(可指定完整路径)→ python → python3 → py -3。参数根节点必须是 JSON 对象。常用写法:
{
"type": "object",
"properties": {
"text": { "type": "string", "description": "要统计的文本" },
"limit": { "type": "integer", "description": "最多返回几行" }
},
"required": ["text"]
}description 写给模型看,说明每个参数的含义;required 里列出必填参数;{"type": "object", "properties": {}};^[a-z][a-z0-9_]{1,47}$(小写字母开头,只含小写字母/数字/下划线,长度 2–48);run_code 是 DSH 保留名,不能使用;read_file),冲突时保存会报「已被现有工具占用」;description 是模型选工具的主要依据,写清「做什么 / 什么时候用 / 返回什么」;title)只给人看,可以写中文;不填时默认等于工具名。dangerous: true,会把工具标记为危险;ask 审批决定,由 DSH 内置审批界面问你「是否运行危险自定义工具 xxx?」,点拒绝就不会执行;AI 通过 custom_tool_manage 的 action="test" 试跑危险工具同样要审批;@deepseek-ai/dsh-experimental-auto-review)兼容:自动审查策略接管 ask 的时候同样按它的判断走,不需要额外配置;除「危险工具执行」之外,还可以让 AI 的管理操作也要你确认:
开关 · 默认 · 作用 创建工具需要审批 · 关 · AI 用 custom_tool_manage 的 create 新建工具时先弹审批 修改工具需要审批 · 关 · AI 用 update 改名称/描述/参数/代码/危险与启用状态时先弹审批 删除工具需要审批 · 开 · AI 用 delete 删除工具时先弹审批(删除不可恢复,所以默认开)
工具包的操作复用同样三个开关:
AI action · 走哪个开关 pack_create · 创建工具需要审批 pack_update(改描述/改成员/改名) · 修改工具需要审批 pack_delete · 删除工具需要审批 pack_set_enabled · 不拦(和 set_enabled 一样,只是批量改启用状态)
%DSH_HOME%\custom-tools\settings.json,点「保存设置」写入;custom_tool_manage;你在页面上点按钮 / 用表单操作不会被自己拦;test(试跑)不在三个开关里:它按「危险工具」规则单独判定;危险开关只是「每次都问」,不是沙箱。脚本仍以当前用户权限运行,写代码时要自己注意边界。
工具包(pack)是分组,不是新的工具类型:
新建工具包,填名称和描述,在「包里的工具」里勾选要放进去的工具;包和工具是「一个工具最多属于一个包」的关系(成员关系记在工具记录上),所以改包名不会影响任何工具。
文本处理、数据处理、运维脚本……;通用叫法是 deferred tool loading(工具延迟加载)/ progressive tool disclosure(渐进式工具披露);Anthropic 侧叫 Tool Search / defer_loading,OpenAI Responses 侧叫 deferred tools。插件用的是 DSH 内核自带的「会话中途增加工具」通道:解锁只改变当前 agent 的可见性,不重新注册工具。
开启方式:页面顶部「设置」→ 打开「AI 主动解锁模式」。
开启后:
custom_tool_manage(管理工具/工具包)custom_tool_unlock(解锁工具包)custom_tool_unlock 的 action="unlock"、pack="包名" 后,包内启用的工具在下一轮请求出现。unlocks.json;新对话 / 子代理不会继承。DSH 重启或插件重挂载后会按文件恢复。custom_tool_manage 的 pack_update 传 alwaysUnlocked=true。custom_tool_unlock 的 tool="工具名" 单独解锁;关闭这个开关时,未分组工具不能单独解锁,只能放进工具包。unlocks.json 里的记录保留,下次开启继续生效。ctx.systemPrompt.context),把当前工具包、成员名与未分组工具写进每个模型步骤的提示里,AI 不用先调 action="list" 就知道有什么可解锁。文本每次组装时重建,改包/改开关下一轮即生效;解锁模式关闭时不注入,避免无意义 token。注意:
pack_set_enabled。unlocks.json 重建,不会出现「解锁了却仍不可见」的叠加。custom_tool_manage模型可以直接调用 custom_tool_manage 完成全部管理操作,和网页共用同一套逻辑。
action · 作用 · 必填参数 list · 列出所有自定义工具(含启用/危险状态,以及工具包名) · – get · 查看某个工具的完整定义(含 Python 代码、所属工具包) · name create · 新建并注册一个工具 · name、description、code update · 修改已有工具(只改传入的字段) · name + 至少一个字段 delete · 删除工具(同时从 DSH 注销) · name set_enabled · 启用 / 停用 · name、enabled test · 用给定参数试运行一次 · name(args 可选) pack_list · 列出工具包及各自成员 · – pack_get · 查看一个工具包的成员与描述 · pack(或 name) pack_create · 新建工具包,可同时指定成员 · name(description/tools/alwaysUnlocked 可选) pack_update · 改包描述 / 改成员 / 改名 / 改始终解锁 · pack + 至少一项(description/tools/alwaysUnlocked/name) pack_delete · 删除工具包(工具保留,变未分组) · pack pack_set_enabled · 一键启停整包 · pack、enabled
参数 · 类型 · 说明 action · string · 上面 13 个之一(必填) name · string · 工具名;create 必填,其余工具类 action 用它定位工具。pack_create 时是包名,pack_update 时是包的新名字 title · string · 给人看的标题,可空 description · string · 给模型看的说明;create 必填。工具包类 action 里是包的描述 parameters · object · 参数 JSON Schema 对象;省略则无参数 code · string · Python 源码,必须含 def run(args):;create 必填 dangerous · boolean · 是否危险(每次执行需审批),默认 false enabled · boolean · 是否启用,默认 true;停用的工具不注册 pack · string · 包名或包 id:pack_get/update/delete/set_enabled 用它定位包;create/update 用它把这个工具放进该包,传空字符串表示移出 tools · string[] · 包成员(工具名数组):pack_create 可选;pack_update 给了就整体替换成员(没列出的工具移出该包但保留) alwaysUnlocked · boolean · 工具包是否「始终解锁」:true 时任何对话都无需 custom_tool_unlock 即可见。仅在解锁模式开启时有意义 args · object · 仅 test 使用:传给 run(args) 的参数对象
帮我新建一个自定义工具 python_word_count,统计文本字符数。
参数:text(字符串,必填)。代码用 def run(args): return {"chars": len(args["text"])}模型会调用 custom_tool_manage:action="create",然后建议 action="test" 验证。
也可以直接说:
列出我现在所有的自定义工具,并测试 python_word_count,参数 text="hello"工具包相关:
把 python_word_count 和 python_clean_text 放进一个叫「文本处理」的工具包,
描述写「处理文本统计与清洗」。然后把整个包停用。模型会依次调用 pack_create(name="文本处理", description=..., tools=["python_word_count","python_clean_text"])和 pack_set_enabled(pack="文本处理", enabled=false)。
custom_tool_unlock只在开启「AI 主动解锁模式」后有意义。参数:
参数 · 类型 · 说明 action · string · list / unlock / lock(必填) pack · string · 工具包名称或 id;与 tool 二选一 tool · string · 未分组工具名;与 pack 二选一,需要设置开启「允许单独解锁未分组工具」
action 行为:
action · 作用 list · 列出所有工具包、未分组工具及各自状态(未解锁 / 当前对话已解锁 / 始终解锁 / 已停用),并说明当前设置 unlock · 解锁整个包(pack=...)或一个未分组工具(tool=...);下一轮请求生效 lock · 反向收起;alwaysUnlocked 的包不能单独锁定,要去页面关闭「始终解锁」
示例:
我需要用 mcdev-docs 里的工具查文档。模型会先 custom_tool_unlock action="list",再 action="unlock"、pack="mcdev-docs";包内工具在下一轮出现。属于某个包的工具不能用 tool=... 绕过包,必须整包解锁。
所有数据都在:
%DSH_HOME%\custom-tools本机默认是:
C:\Users\acer\.dsh\custom-tools如果设置过DSH_HOME,以它为准;插件也支持用DSH_CUSTOM_TOOLS_DIR指向别的目录(需重启 DSH)。
路径 · 内容 · 说明 tools.json · 工具记录(唯一真源) · 名称、标题、描述、参数、代码、dangerous、enabled、packId(所属工具包,空=未分组)、时间戳 packs.json · 工具包记录 · id、name、description、alwaysUnlocked(是否始终解锁)、时间戳;成员关系不在这个文件里,而在各工具的 packId 上 settings.json · 全局开关 · unlockMode(AI 主动解锁模式)、allowUngroupedUnlock(允许单独解锁未分组工具)、approveCreate / approveUpdate / approveDelete;文件不存在时用默认值(解锁模式关、删除审批开) unlocks.json · 按对话的解锁状态 · sessions..packs(已解锁的工具包 id)+ .tools(已单独解锁的未分组工具 id);最多保留最近 200 个对话 scripts.py · 每个工具的代码副本 · 方便直接阅读/备份;直接改这里不会生效,执行用的是 tools.json 里的 code runs\r_ · 每次执行的临时目录 · 含 tool.py、args.json、result.json、stdout.txt、stderr.txt;自动只留最新 30 个 runner.py · 自动生成的 Python 执行器 · 不要手改,会被覆盖
备份/迁移:直接复制整个 custom-tools 目录即可(tools.json + packs.json + settings.json + unlocks.json 一起复制才算完整)。恢复时把目录放回原位(或设 DSH_CUSTOM_TOOLS_DIR),重启 DSH。