Bundle
dsh-agency-market
Expert market inside DeepSeek Harness — browse expert cards, install one as a user agent preset, then chat in a session on that preset. · DSH 专家市场:逛一逛,点一下,装好,开聊。
- Source
- pbwheel
- License
- MIT
- Updated
- Updated 4 days ago
Readme
<div align="center">
# DSH Agency Market
**装进 DeepSeek Harness 的专家市场:273 位专家,一键安装,随时召唤**
[English](README.en.md) · [许可证](LICENSE)
[](LICENSE)
[](#目录数据)
[](#安装)
[](#开发)
</div>
> dsh-agency-market 是社区维护的 DeepSeek Harness(DSH)插件,不是 DeepSeek 官方产品。
> 273 位专家人格全部导入自开源项目 [agency-agents](https://github.com/msitarzewski/agency-agents)(MIT)。
## 它是什么
一个装在 DSH Web 里的「专家」市场:浏览专家卡片,一键把专家安装为**用户级 agent preset**。装好的专家有两种用法——新建会话时选预设直接开聊;或在**任意已有会话**里让模型召唤(`market_experts` / `summon_market_expert` 两个模型工具),随时换专家。
```
设置 → 专家市场 → 挑一张卡片 → 安装 ─┬→ 新建会话选预设 → 与专家开聊
├→ 任意会话里让模型召唤(market_experts / summon_market_expert)
└→ 输入框「召唤专家」按钮 / @ 菜单 → 生成召唤指令草稿(不自动发送)
```
主会话始终保留任务上下文、判断与最终答复;被召唤的专家子代理只提供专业视角,且不能再召唤其他专家(防递归)。
## 特性
- **市场页**(设置 → 专家市场):registry 式名录——全宽搜索、领域分类 chips、超大领域的二级分组 tab、已装过滤、行内移除确认、骨架加载,卡片中英文双语,搜索跨语言命中。
- **一键安装**:安装即走 roster 认可的预设编写路径(复制底座 → 改写元数据 → 换 persona → 挂载校验),绝不写部署自带的 preset;卸载只删用户级条目。
- **会话中途召唤**:`market_experts()` 列出可召唤专家,`summon_market_expert(expert, task)` 把一个自包含任务委派给专家——一次性子代理带着完整专家人格跑完任务并返回答复,同一会话可以给不同任务换不同专家。
- **三个召唤入口**:模型自己调工具、输入框「召唤专家」按钮、`@` 菜单触发源;后两者只起草指令草稿,**不自动发送**。
- **273 位专家 × 18 个领域**:学术、设计、工程、财务、游戏开发、GIS、医疗、营销、付费媒体、产品、项目管理、研究、销售、安全、空间计算、专业垂直、支持、测试。
- **零构建、零运行时依赖、不执行任何安装脚本**:纯 ESM JavaScript + JSON 数据,`node scripts/smoke.mjs` 一条命令离线自检。
## 前置条件
- 一套可用的 DeepSeek Harness Web 安装,`dsh` 命令可用(Node.js 22+;`dsh` 不在 PATH 时可用 `npx --yes @deepseek-ai/dsh` 代替)。
- 示例均使用 `web` profile,按需替换为目标 profile。
## 安装
### 方式一:让另一个 agent 帮你装
本插件运行在 DeepSeek Harness Web 里。把下面这句话粘给 DSH(或 Codex、WorkBuddy 等会装 npm 包的 agent),让它替你装进本地 `web` profile:
```text
从源码安装 DSH 插件 https://github.com/pbwheel/dsh-agency-market 到我的 web profile:clone 该仓库,进入目录运行 dsh plugin --profile web add .,然后运行 dsh --profile web --dump-config 确认 dsh-agency-market 已挂载,并提醒我重启 DSH Web 和强刷浏览器。
```
### 方式二:从源码安装(调试或未发布改动)
```sh
git clone https://github.com/pbwheel/dsh-agency-market.git
cd dsh-agency-market
dsh plugin --profile web add .
dsh --profile web --dump-config
```
`dsh plugin ... add` 会读取包元数据与 `cordis.patch.yml`,把 host 半边接入组合层(`dsh.profile.bundles` 自动登记,无需手改)。配置输出里应能看到 `dsh-agency-market`。
然后**重启 `dsh web`**(bundles 列表只在启动时读)并**强刷浏览器**。本插件无构建步骤、无运行时依赖、不执行任何安装脚本,克隆目录就是插件源码目录,不要只拷贝 `src`。
### 安装排障
- 市场页 404 / 只有客户端按钮不工作:确认 `~/.dsh/profiles/web/package.json` 的 `dsh.profile.bundles` 数组里有 `"dsh-agency-market"`,改完**重启 `dsh web`**(bundles 只在启动时加载);客户端半边由 loader 自动扫描,刷新页面即可。
- `@` 菜单里没有「专家市场」分组:`package.json` 的 `dsh.client.inject` 组合变更由 loader 在**启动时**扫描,重启 `dsh web` 即可(按钮与市场页不受影响,判空降级不报错)。
## 使用
### 1. 挑专家安装
打开 **设置 → 专家市场**:按分类浏览或搜索(中英文关键词均可命中),点卡片安装。已装行盖 ✓ 印章,行内确认即可移除。
### 2. 新会话直接开聊(preset 模式)
新建会话时,预设选择器里选该专家(`expert-<id>`,显示专家名与简介),即可以专家人格开一场会话。
### 3. 任意会话中途召唤(tool 模式)
在任意进行中的会话里,直接让模型召唤,或点输入框左侧的「召唤专家」按钮 / 输入 `@` 选专家——按钮与 `@` 只把指令写进草稿,**不会自动发送**:
```text
召唤专家「Code Reviewer」(engineering-code-reviewer)处理以下任务:
审查当前工作区的改动,按严重程度列出可复现的问题。
```
模型侧的两个工具:
- **`market_experts(category?)`** —— 列出当前可召唤(= 已安装)的专家:id、名称、描述、分类;不知道精确 id 时先调它。
- **`summon_market_expert(expert, task)`** —— 把一个自包含任务委派给某专家;`expert` 支持 id(推荐)或名称(含中文名,模糊匹配,歧义时报候选列表),`task` 需完整自包含、上限 8000 Unicode 码点。调用等待专家跑完并返回答复。
未安装的专家召唤会报错并提示「请先在 设置 → 专家市场 安装」。
### 一个专家是什么
一个专家 = 一个用户级 agent preset(`expert-<id>`,装在 `${DSH_HOME:-~/.dsh}/.agent-presets/` 下):
| 卡片字段 | 落到哪里 |
|---|---|
| `name` / `description` | preset 的 `preset.yml`(预设选择器里显示的名字与简介;写入的是目录基字段/英文) |
| `persona` | 组合里 `dsh-persona` 行的 `text`(专家的系统提示词) |
| `base` | 被复制的底座预设(默认 `standard`,完整工具链) |
| `avatar` / `tags` / `category` | 市场页自己的展示字段(只在目录数据里) |
| 中文 `name` / `description` / 分类名 | **不进专家文件**:放在 locale 覆盖层 `data/i18n.d/zh.json`,仅用于展示(见[多语言](#多语言locale-覆盖层)) |
安装流程(宿主侧,全部走 roster 服务):
1. `agentPresets.copy(base, 'expert-<id>')` —— 复制底座预设到用户根;
2. 改写副本的 `preset.yml`(名字/简介);
3. 用锚定替换把副本 persona 行的 `text` 换成专家 persona(锚不中则降级为底座 persona 并在 UI 提示,不会装出坏预设);
4. `agentPresets.standingKeyFor(id)` 真实挂载校验,通过才算装好。
**绝不写部署自带的 preset**;卸载只删 `trust: "user"` 的条目。
### 行为与安全细节
- 变更路由只接受**同源 POST**、请求体上限 4 KiB、同时只允许一个安装/卸载在进行;安装只是预设复制加两处文件改写,**不执行任何构建/安装脚本**。
- 防递归:子代理的 `toolFilter` deny 掉两个召唤工具名;系统提示词 section 在子会话(header 带 `parentSession`)返回空串,被召唤的专家学不会再召唤。
- 专家子代理以 `stopReason !== 'completed'` 结束时报错(携带 diagnostic 与部分输出),`run.dispose()` 在 finally 里必定执行。
- persona 里 `{{model}}` / `{{cwd}}` / `{{provider}}` 是宿主注册的提示词变量,原样透传、不做 sanitize;导入时会把其余完整 `{{…}}` 组拆开(`{{` → `{ {`),未注册变量会让子代理启动失败,因此按字面文本处理。
- 所有副作用(路由、工具、提示词 section、插槽、样式)收集 disposer 统一释放,插件卸载即全部撤销。
## 目录数据
目录分三层,加载逐文件合并、逐文件隔离:某个文件 JSON 损坏或校验不过(id 不合法 / 缺 persona / id 重复)只跳过该文件并把原因放进 `/api/state` 返回的 `warnings[]`,不会拖垮整个目录;头文件损坏也一样降级,只有全部不可用时才报错。
| 文件 | 作用 |
|---|---|
| [`data/experts.json`](data/experts.json) | 头文件:`version` + `categories`(18 个领域) |
| [`data/experts.d/`](data/experts.d/) | 每位专家一个 JSON,按领域分子目录(273 位 × 18 领域) |
| [`data/subgroups.json`](data/subgroups.json) | 二级分组覆盖层:超大领域的子分组(见下) |
| [`data/i18n.d/`](data/i18n.d/) | locale 覆盖层:展示层翻译(见[多语言](#多语言locale-覆盖层)) |
一位专家的完整定义:
```jsonc
// data/experts.d/engineering/engineering-code-reviewer.json
{
"id": "engineering-code-reviewer", // [a-z0-9-],安装后 preset id 为 expert-engineering-code-reviewer
"name": "Code Reviewer", // 卡片标题 + preset.yml name
"avatar": "👁️", // ← agency-agents frontmatter emoji
"category": "engineering", // 头文件 categories[].id(= 领域)
"tags": ["Engineering", "agency-agents"],
"description": "卡片一句话简介 + preset.yml description",
"base": "standard", // 底座预设,缺省 standard
"persona": "# Code Reviewer Agent\n\nYou are **Code Reviewer**…" // = md 正文
}
```
**二级分组(subgroups)**:engineering 一个领域装了 59 位专家,单层 tab 挑不动。二级分组刻意做成独立覆盖层而不是写进专家文件——上游 agency-agents 没有分组信息,`--replace` 重导入会清空重写 `experts.d/`,而覆盖层与 i18n 一样原样幸存:
```jsonc
// data/subgroups.json
{
"categories": {
"engineering": [
{ "id": "ai-ml", "label": "AI & Machine Learning", // label 为基字段(英文)
"experts": ["engineering-ai-engineer", /* … */] }, // 数组顺序即子 tab 顺序
// …frontend-client / backend-architecture / data-database /
// ops-reliability / cms-platform / practices-quality,共 7 组覆盖全部 59 位
]
}
}
```
加载分两步、逐条降级:先按形状校验(坏 id / 空 label / 畸形 experts / 重复组跳过并告警),再与目录对账(目录里已不存在的过期 id、挂错分类的 id、被两个组重复认领的专家、成员掉光的空组,逐条跳过并告警)。当前覆盖四个超载领域:engineering(59 × 7 组)、specialized(58 × 6)、marketing(36 × 4)、game-development(21 × 6);其余领域规模小(≤13 位),保持单层。给新领域加二级分组只需照抄该文件 + zh 覆盖层,代码零改动。
**重新导入**:agency-agents 上游更新后重跑 [`scripts/import-agency-agents.mjs`](scripts/import-agency-agents.mjs):
```sh
node scripts/import-agency-agents.mjs --replace # --repo <path> 指定仓库位置,--dry-run 只校验
```
## 多语言(locale 覆盖层)
页面静态文案走客户端 locale 服务(zh/en 双语字典)。专家**名称/描述/领域与二级分组标签**的多语言走独立的 locale 覆盖层 `data/i18n.d/`:每种语言一个 `<locale>.json`,纯展示元数据叠加在目录之上——`experts.d/` 里的专家文件(含 persona)一个字节都不改,`--replace` 重导入也不会碰覆盖层:
```jsonc
// data/i18n.d/zh.json(en 不需要文件:目录基字段本身就是英文,缺失即回退)
{
"categories": { "engineering": "工程", /* …18 个领域 */ },
"subcategories": { "engineering/ai-ml": "AI 与机器学习", /* …与 subgroups.json 一一对应 */ },
"experts": {
"engineering-frontend-developer": {
"name": "前端开发工程师",
"description": "专注现代 Web 技术、React/Vue/Angular 框架、UI 实现与性能优化的前端专家"
}
// …273 位专家
}
}
```
生效链路:市场页把当前界面语言作为 `?locale=zh|en` 传给宿主(切换语言自动重拉);宿主用覆盖层逐字段覆盖、缺啥回退基字段;召唤侧 `market_experts` 的输出、专家名称匹配(粘贴中文卡片标题也能解析到)同样吃覆盖层。市场页搜索是**跨语言**的——API 为每位专家附另一种语言的关键词,zh 界面输入英文照样命中。冒烟测试会强制校验 zh.json 覆盖全部目录专家,重导入新增专家后跑一次即可发现漏译;批量重译可用 `scripts/merge-zh-overlay.mjs` 合并片段,零星修订直接编辑 zh.json。
## 架构
```
浏览器 DSH 宿主进程
────── ───────────
client/client.js src/index.js (host 入口,双 inject)
settings.section「专家市场」 ├─ inject webServer+agentPresets:
名录行/分组/搜索 ──同源 fetch──────→ │ src/routes.js /dsh-agency-market/api/*
│ src/experts.js catalog + 安装/卸载/状态
conversation.input.left ├─ inject tools+subagents+systemPrompt+agentPresets:
「召唤专家」按钮 ──同源 fetch──────→ │ src/summon.js market_experts / summon_market_expert
(菜单选中 → setDraft 草稿) │ + 提示词 section(子会话为空)
inputTriggers「@」触发源 ├─ agentPresets 服务(copy / remove / standingKeyFor)
(candidates → /api/state) └─ subagents 服务(spawn provider,按次 persona)
data/experts.json + data/experts.d/ ~/.dsh/.agent-presets/expert-<id>/
data/subgroups.json + data/i18n.d/<locale>.json(展示层翻译)
```
- **host 半边**(`src/`):纯 ESM JavaScript,无依赖无构建。三个路由:`GET /api/state`(目录+已装标记+告警)、`POST /api/install`、`POST /api/uninstall`。`src/summon.js` 注册两个模型工具 + 一个提示词 section;工具名为 `market` 命名域,避免与其他专家插件(如 dsh-agency-agents 的 `list_experts` / `summon_expert`)在同 profile 重名。两段 inject 相互独立:市场页不依赖工具 seam,召唤也不依赖 web server。
- **client 半边**(`client/client.js`):手写的 harness 客户端 bundle 格式(`window.__ModuleLoader__.load({ id, factory })`,唯一 external 是 `react`),注册两个插槽条目——`settings.section` 渲染市场页、`conversation.input.left` 渲染「召唤专家」按钮——并在 `inputTriggers` 服务可用时注册一个 `@` 触发源(服务不在组合里则判空跳过)。样式走主题 token(`--dsw-alias-*` 等)并带回退,明暗主题自适应,焦点环与 `prefers-reduced-motion` 降级齐备;`<style data-plugin>` 标签随插件卸载被 loader 清掉。
- **安装边界**:预设编写只发生在宿主 `agentPresets` 服务;路由层只解析请求、调服务、序列化 JSON。
## 已知边界
- 安装写入 `preset.yml` 的是目录基字段(英文名/描述),预设选择器因此显示英文名;中文译名只出现在市场卡片、召唤菜单与 `market_experts` 输出里(按安装时语言写死元数据会随语言切换过期,故刻意不写)。
- 召唤是一次性委派(one-shot subagent):专家答复返回主会话,不保留可续聊的专家子会话——「直开专家会话」是路线图项。
- persona 替换依赖底座组合里 persona 行的锚定形状;底座漂移时降级为底座 persona 并在安装结果里给出 warning(不会装出坏预设)。
## 路线图
1. **远端 registry**:catalog 从远端 JSON 拉取,支持市场内更新;
2. **直开专家会话**:卡片按钮直接开一个可续聊的专家子会话,主会话随时 @ 它;
3. **富卡片**:使用次数 / 作者 / 版本 / 预览对话示例;卡片排序(热门/最新)。
## 开发
```sh
node scripts/smoke.mjs # 离线冒烟:目录与覆盖层加载 + 安装/卸载/状态 + 召唤工具 + client bundle 加载与注册
```
无构建、无依赖、无 install 脚本。改完 host(`src/`)或 `package.json` 的 `dsh.client.inject` 后需**重启 `dsh web`** 才生效(bundles 与注入组合只在启动时加载);只改客户端半边(`client/client.js`)刷新页面即可。目录与脚本:
- `src/index.js` —— host 入口(双 inject 编排);
- `src/experts.js` —— 目录加载(头文件 + 分文件 + 覆盖层)与安装/卸载/状态;
- `src/routes.js` —— 三个同源 HTTP 路由;
- `src/summon.js` —— 两个召唤工具 + 提示词 section;
- `client/client.js` —— 市场页 + 输入框按钮 + `@` 触发源;
- `scripts/import-agency-agents.mjs` —— 从 agency-agents 导入/重导入目录;
- `scripts/merge-zh-overlay.mjs` —— 合并 zh 翻译片段;
- `scripts/smoke.mjs` —— 离线冒烟。
## 许可证与致谢
- 本插件的源码与文档以 [MIT License](LICENSE) 发布(Copyright (c) 2025 pbwheel)。
- `data/experts.d/` 下的 273 位专家人格导入自 [agency-agents](https://github.com/msitarzewski/agency-agents)(MIT,Copyright (c) 2025 AgentLand Contributors),许可见 [`data/experts.d/LICENSE`](data/experts.d/LICENSE);二级分组与中文翻译为本项目新增。
Install
dsh plugin --profile web add github:pbwheel/dsh-agency-market
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-agency-market from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.