deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
English | 中文
给 DeepSeek Harness 的地图与路径规划原生工具:驾车 / 公交 / 步行 / 骑行路线、地理编码、逆地理编码、POI 搜索。模型直接调用,不需要 MCP;算出来的路线会画成真地图卡片,出现在最终回答的下方。
一句话:把「路怎么走」变成模型能直接调用的 7 个工具,并把结果画成看得见的地图。
map_*):路线规划(驾车 / 公交 / 步行 / 骑行)、地理编码、逆地理编码、POI 搜索。走 DSH 的 ctx.tools 注册,不是 MCP,没有额外进程。设置 → 插件 → dsh-map-tools。)
实机截图:收尾正文下方的路线地图卡(驾车 10.8 公里 / 约 20 分钟 / 共 12 步,蓝色折线是高德绘制的真实路线)。
一次路线问答里,界面会出现三处内容:
| 位置 | 内容 | 说明 |
|---|---|---|
| 过程区(「思考」块) | 紧凑摘要 + 可折叠路书 | 不渲染地图——过程区会随思考反复重绘,地图只在最终结果处出现一次(也省掉一次取图) |
| 收尾正文 | 距离、耗时、分段指引、「在高德打开 ↗」 | 模型可见文本,模型据此作答 |
| 收尾正文之后(回合尾部) | 路线地图卡:真地图 + 起终点 + 距离/耗时/步数 + 深链 | 本轮每条路线一张,多条时标注「第 k/N 条」;超过 3 条只显示前 3 条并注明总数 |
卡片的四种形态(都不会「空白」):
回合尾部是 DSH 客户端的单选席位:本轮有路线时地图卡占用这一行,官方的「本轮产出文件」行本轮不渲染——卡片里会注明「本轮另有 N 个产出文件(地图卡占用此行,文件见过程区)」。
dsh plugin --profile web add dsh-map-tools
dsh plugin --profile web add github:HorusJiang/dsh-map-tools
发布包不含
prepare/postinstall构建脚本,pnpm ≥10 安装时无需放行构建脚本,也不必配置allowBuilds。
版本要求
@deepseek-ai/dsh-settings、@deepseek-ai/dsh-tools、@deepseek-ai/cordis ^4.0.2)。更早的发布线因 @deepseek-ai/dsh-settings 移除旧 API 而不再支持。package.json 的 dsh.compatibility.dshReleases。conversation.chat.turnTail 在 0.1.6-alpha.2 由链式(chain,要 select)改成列表式(list,要 id),插件运行时读 slots.spec() 决定注册形状,因此同一份构建在两条发布线上都能让回合尾部的路线卡片正常出现(详见 docs/dsh-map-tools-0.1.6兼容性说明.md)。dsh.client.platform: web)。TUI / headless 下 7 个工具照常可用,只是没有图形卡片。安装后重启 dsh web(或在启动器里按 R),在会话中即可使用 map_* 工具。
dsh plugin --profile web list # 查看已装版本
dsh plugin --profile web add dsh-map-tools@^0.7.0 # 升级
dsh plugin --profile web remove dsh-map-tools # 卸载
0.x 的坑:
^0.6.1这类 caret 在 0.x 阶段只允许同 minor,装不到 0.7.x。升级时请写明目标版本(如dsh-map-tools@^0.7.0)。
dsh plugin --profile web add /absolute/path/to/dsh-map-tools
以 link: 方式挂载。注意:改动源码后需要 pnpm run build,且要让宿主热替换(不用重启)还得额外配置 hmr 与 junction——完整配方见 docs/开发-热插拔-HMR.md。
配置高德 key(约 2 分钟):
amap,保存(立即生效)。从北京南站到首都机场T3,规划驾车路线
把「西湖区文三路478号」转成经纬度
116.397428,39.90923 附近 1 公里内有什么加油站?
| 工具 | 作用 | 参数 | 免费 OSM | 高德 |
|---|---|---|---|---|
map_driving_route |
驾车路线规划 | origin、destination(必填,地址或 "lng,lat");waypoints("lng,lat;lng,lat",仅高德);alternatives(备选方案,默认 false) |
✅ | ✅ |
map_walking_route |
步行路线规划 | origin、destination |
✅ | ✅ |
map_bicycling_route |
骑行路线规划 | origin、destination |
✅ | ✅ |
map_transit_route |
公交 / 地铁换乘 | origin、destination |
— | ✅ |
map_geocode |
地址 → 经纬度 | address |
中文不可靠 | ✅ |
map_reverse_geocode |
经纬度 → 结构化地址 | location("lng,lat") |
中文不可靠 | ✅ |
map_poi_search |
兴趣点搜索 | keywords;location(周边搜索中心)、radiusM(默认 1000,最大 50000)、region(限定城市)、types(POI 类型编码,如 060000 餐饮) |
— | ✅ |
起点 / 终点统一接受 地址文本 或 "lng,lat" 坐标两种形式,插件自动归一化。
模型可见文本里有什么
起终点:北京南站 → 北京首都国际机场(坐标反查)。卡片专用数据不进模型上下文:路线几何(抽稀后 ≤ 200 点)与起终点名走 output.presentationMeta 持久化到会话日志,只用于渲染卡片,不占 token。
DSH 的侧边栏「插件」页 → dsh-map-tools 打开 bundle 详情页,描述与行列表之间就是本插件的配置区:顶部一行状态(数据源 · key 状态)、数据源选择、高德 key 输入(脱敏,留空表示保持不变)、请求超时,并内置「如何获取高德 Key?」申请链接和「打开配置文件」。保存后插件重建工具实例,立即生效。
这一区渲染在宿主的 plugins.bundle.config 座位上(key = 包名 dsh-map-tools),页面骨架(标题、包名、描述、开关、卸载)由宿主自己画,插件只画内容。更老的宿主(DSH ≤ 0.1.5,没有这个座位)自动回落到 设置 → 插件 → dsh-map-tools,卡片内容完全一样。
配置区的行为约定与官方配置页一致:改动先暂存、点保存才落盘(屏幕上看到的就是保存会写下的),离开页面即丢弃(所以没有「取消」按钮),非法超时会拦住保存而不是被悄悄改写成默认值,保存后以宿主返回的值重新播种(key 输入框随之为空)。
配置实际存储在 ~/.dsh-map-tools/config.json(权限 0600),与 DSH 设置文档解耦、跨 profile 共享。路径可用 DSH_MAP_TOOLS_CONFIG 环境变量覆盖。
// ~/.dsh-map-tools/config.json
{
"provider": "amap", // "amap" | "osm"
"amapKey": "……", // 高德「Web 服务」key(secret,永不回显)
"timeoutMs": 15000,
"maxQps": 2, // 高德每秒请求上限
"defaultMode": "driving",
"language": "zh"
}
| 键 | 默认 | 配置页可改 | 说明 |
|---|---|---|---|
provider |
amap |
✅ | amap = 高德(推荐,国内数据最全);osm = 免费 OSM 兜底(无需 key,能力有限) |
amapKey |
— | ✅ | 高德 Web 服务 类型 key,免费申请 |
timeoutMs |
15000 |
✅ | 单次请求超时(毫秒) |
maxQps |
2 |
手改 JSON | 高德每秒最大请求数(默认 2 低于常见上限 3,防触发 10021 配额错误) |
defaultMode |
driving |
手改 JSON | 默认路线模式(driving / transit / walking / bicycling) |
language |
zh |
手改 JSON | 返回语言(zh / en) |
key 只以布尔标记
hasAmapKey呈现给前端,永远不会回显到页面或日志。
也可以在 profile 的 cordis.yml 里给默认值:
- id: map-tools
name: dsh-map-tools
config:
provider: amap
优先级:配置文件(配置页写入)> cordis.yml 默认值。
配置卡片走插件的同源回环路由,不直接写 DSH 设置:
| 路由 | 用途 |
|---|---|
GET /dsh-map-tools/config |
返回非敏感摘要(provider、hasAmapKey、timeoutMs …) |
GET /dsh-map-tools/config?open=true |
在编辑器中打开配置文件 |
POST /dsh-map-tools/config |
应用 provider / amapKey / timeoutMs 补丁并重建工具 |
GET /dsh-map-tools/staticmap?w=&h=&line= |
取静态地图 PNG(同源校验、按参数缓存、连接关闭即取消上游请求) |
静态地图由宿主取回,浏览器只拿到一张本地图片——这也是不把图片塞进工具结果的原因:既不进模型上下文,也不必往附件仓库写字节。
| 数据源 | 覆盖能力 | Key | 备注 |
|---|---|---|---|
| 高德(amap) | 全部 7 个工具 | 免费申请 | 国内数据最全:公交换乘、POI、稳定的中文地理编码 |
| OSRM | 驾车 / 步行 / 骑行 | 无 | 免费公共实例,有频率限制 |
| Photon / Nominatim | 地理编码 | 无 | 免费公共实例;中文地址解析不可靠,部分国内网络不可达 |
降级规则(都是明说,不静默失败、不伪造结果):
"lng,lat" 坐标或配置 key。uri.amap.com 协议,尚未逐项实机验证;如遇异常欢迎提 Issue。┌─────────────────────────────────────────────────────────────┐
│ 模型(任意 DSH 支持的模型 / 提供方) │
│ map_driving_route / map_transit_route / ... 7 个原生工具 │
└─────────────────┬───────────────────────────────────────────┘
│ ctx.tools(defineTool:参数校验 + output.render)
┌─────────────────▼───────────────────────────────────────────┐
│ 宿主半边(TypeScript -> lib/) │
│ src/tools/ 工具定义与模型可见文本 │
│ src/clients/ 数据源客户端 │
│ amap.ts 高德 Web 服务(推荐) │
│ osrm.ts OSRM 免费路线(兜底) │
│ photon.ts / nominatim.ts 免费地理编码(兜底) │
│ src/geo.ts 几何解码 / 去重 / 抽稀 / 编码 │
│ src/config-file.ts ~/.dsh-map-tools/config.json 0600 │
│ src/config-route.ts GET/POST /dsh-map-tools/config │
│ src/staticmap-route.ts GET /dsh-map-tools/staticmap │
│ src/settings-ns.ts 老宿主设置页 namespace 注册 │
└─────────────────┬───────────────────────────────────────────┘
│ presentationMeta(不进模型上下文)
┌─────────────────▼───────────────────────────────────────────┐
│ 浏览器半边 client/client.js(手写 lazy-CJS,零构建) │
│ - 插件页 bundle 配置卡(plugins.bundle.config,key=包名) │
│ 老宿主回落到 设置 -> 插件(settings.plugin.item) │
│ - 回合尾部路线卡(conversation.chat.turnTail) │
│ 真地图(宿主静态图)-> 取不到时退回自绘 SVG 示意图 │
└─────────────────────────────────────────────────────────────┘
cordis.yml 默认值。Q:配置了高德 key,路线还是走 OSM?
A:检查配置文件的 provider 是否为 amap(不是 osm)、amapKey 是否非空。配置卡顶部的状态行会显示当前数据源与 key 状态。
Q:为什么公交换乘 / POI 搜索提示需要 key? A:免费 OSM 源不提供公交换乘与 POI 数据,这两项能力必须用高德 key。
Q:高德 key 被拒 / 报错怎么办?
A:确认申请的是 「Web 服务」 类型 key(不是 JS API / Web 端 key),并在高德控制台确认对应服务已启用。报 10021 说明触发 QPS 配额,可调低 maxQps(默认 2)或降低请求频率。
Q:中文地址解析报「免费数据源不可用」?
A:免费源(Photon / Nominatim)对中文支持差,且部分国内网络不可达。这是设计行为——直接传 "lng,lat" 坐标,或配置高德 key。
Q:路线卡片没出现地图? A:卡片会依次尝试:真地图 → 自绘示意图 → 文本行。看不到地图通常是①未配 key ②高德配额超限 ③不在 Web 客户端。此时仍会有自绘示意图或文本结果,不是失败。
Q:卡片画的是另一条路线,和正文对不上? A:见已知限制第 1 条——卡片只镜像本回合的路线调用。让模型在本回合用相同起终点再算一次即可;工具的 description 已内置这条契约。
Q:为什么过程区(「思考」块)里没有地图? A:刻意如此。过程区会随思考反复重绘,地图只在回合尾部(最终结果处)出现一次,避免闪烁并省掉重复取图。
Q:地图卡把「本轮产出文件」那行挤掉了? A:回合尾部是单选席位。卡片里会注明「本轮另有 N 个产出文件」,文件本身在过程区。
Q:卡片会消耗 token 吗?
A:不会。卡片数据走 presentationMeta 持久化,不进入模型上下文。
Q:需要装 MCP 服务器吗? A:不需要。7 个工具都是 DSH 原生工具。
Q:支持哪些 DSH 版本? A:DSH ≥ 0.1.2-rc.1;已在 0.1.6-alpha.2 与 0.1.6-alpha.1 实测(回合尾部槽位语义在这两版之间变过,插件按槽位声明自适应,无需换版本)。详见安装。
pnpm install
pnpm run build # tsc → lib/
pnpm test # vitest 单元测试(mock 网络,135 个用例)
node scripts/smoke.mjs # 冒烟:宿主注册 7 个工具
node scripts/config-e2e.mjs # 配置回环:卡片数据通路端到端
node scripts/integration.mjs # 真实网络:免费源(OSRM / Nominatim)
node scripts/amap-e2e.mjs # 真实网络:高德(需 AMAP_API_KEY)
node scripts/call-driving-route.mjs # 单次调用示例
node scripts/check-secrets.mjs # 密钥守卫
pnpm hooks # 安装 pre-commit 密钥守卫
约定要点:
tests/ 里不发真实请求);需要真实 key 的脚本只从 process.env.AMAP_API_KEY 读取,禁止把 key 写进任何文件。client/client.js 必须 node --check client/client.js;client 测试直接加载随包发布的同一份字节。docs/开发-热插拔-HMR.md。完整约定见 CONTRIBUTING.md 与 AGENTS.md。
发布由 CI 执行(.github/workflows/release.yml),而且是两段式——第二段必须由人来做:
# 1. 打 tag 触发:CI 跑 CI 同款闸门,然后 `npm stage publish` 只 staging(此时谁都装不到)
git tag -a vX.Y.Z -m "dsh-map-tools X.Y.Z" && git push origin vX.Y.Z
# 2. 维护者用 2FA 批准,再把 CI 留下的草稿 Release 转正(命令会打印在 run summary 里)
npm stage list dsh-map-tools
npm stage approve <stage-id>
gh release edit vX.Y.Z --draft=false
为什么两段式:npm 的 trusted publisher 只授权 staged publishing(npm publish 不在允许动作里),所以工作流自己没有能力把包推到所有人面前——tag 表示"这是候选发布",2FA 那一下才表示"这就是发布"。一次性配置(npm 包的 Trusted publishing 加一条 GitHub Actions 连接、allowed actions 不勾 npm publish)见 release.yml 顶部注释。
没有 CI、或要手动发布时,同一套动作用本地脚本:
node scripts/publish.mjs # 直发:构建 → 测试 → 打包检查 → publish → 验证可安装
node scripts/publish.mjs --stage # 两段式:只 staging,等 2FA 批准
node scripts/publish.mjs --verify-only # 只验证某个版本"真的能装上"(含 sha1 对照)
两条路都用同一条判据判断"发布成功":用全新缓存 + --prefer-online 把 tarball 真拉下来,并与本地构建产物对照 sha1。npm 的读路径是几份独立传播的缓存(完整 packument、安装用的 corgi packument、tarball),npmjs.com 上的 Published 和 npm view 都比 tarball 传播得早,不能当"能装上"的证据。
版本语义遵循 SemVer,变更记录见 CHANGELOG.md。发布包只含 lib/、client/、cordis.patch.yml 与 LICENSE(外加 npm 必带的 package.json / README)。
API key 的存储方式(~/.dsh-map-tools/config.json,0600,永不回显)与漏洞报告流程见 SECURITY.md。
欢迎 Issue 与 PR!请先阅读 CONTRIBUTING.md 了解开发约定与提交规范。
MIT © HorusJiang
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。