# dsh-tool-ths 架构文档 本文档说明插件的整体设计、加载机制与代码结构,帮助理解插件如何接入 DeepSeek Harness(DSH),以及如何扩展。 ## 1. 插件定位 `dsh-tool-ths` 是一个标准的 DSH(Cordis)插件包:它不修改 Harness 本体, 通过 Cordis 的「服务注入 + 工具注册 + 设置项注册」机制把同花顺数据能力挂进 agent 的工具集。插件同时支持**两个数据通道**,对外暴露同一组 `ths_*` 工具, 由配置项 `channel` 决定实际走哪个通道。 ``` ┌────────────────────────────── agent (LLM) ──────────────────────────────┐ │ ths_status / ths_quote / ths_kline / ths_basic / ths_trade_dates / │ │ ths_announcement / ths_code │ └───────────────┬─────────────────────────────────────────────────────────┘ │ ctx.tools.register(...) ┌───────────────▼─────────────────────────────────────────────────────────┐ │ index.js — 插件入口(Config / apply / 工具定义 / 设置页 / 提示词) │ │ ThsGateway — 通道分发器(按配置变更懒重建客户端,缓存 access_token) │ ├──────────────────────────────┬──────────────────────────────────────────┤ │ lib/public.js │ lib/ifind.js │ │ 同花顺公开行情(免登录) │ 官方 iFinD HTTP API │ │ d.10jqka.com.cn │ quantapi.51ifind.com │ └──────────────────────────────┴──────────────────────────────────────────┘ │ │ 实时行情 / 日周月K线 实时行情+估值指标 / 任意周期K线+复权 / 财务指标 / 交易日历 / 公告 / 代码转换 ``` ## 2. 通道对比 | 维度 | public(默认) | ifind | | --- | --- | --- | | 数据源 | 同花顺网页公开行情接口 `d.10jqka.com.cn` | 官方 iFinD HTTP API `quantapi.51ifind.com` | | 账号 | 不需要 | iFinD 账号 `refresh_token` | | 实时行情 | 最新价/涨跌/涨跌幅/当日OHLC/均价/全日量额 | 上述 + 总市值/流通市值/PE/PB/量比/振幅/停牌状态等 | | K线 | 日/周/月(约140/1260根窗口) | 日/周/月/季/半年/年 + 复权(前/后/分红再投) | | 财务/公告/交易日历/代码转换 | 不支持 | 支持 | | 可靠性 | 非官方承诺,偶发 502/限流(已带重试) | 官方 SLA,按数据量计费 | | 适用 | 个人研究、快速行情 | 生产/合规、深度数据 | ## 3. 加载机制(DSH 侧) 插件通过 DSH 的 Cordis Loader 加载,加载路径有两种,效果等价: ### 3.1 Bundle 方式(`dsh plugin add`) 包内 `package.json` 声明: ```json "dsh": { "bundle": { "patch": "./cordis.patch.yml" } } ``` `cordis.patch.yml` 是一个补丁列表,向 profile 的加载树**插入**一行: ```yaml - insert: - id: tool-ths name: dsh-tool-ths # 由 pnpm 装入 profile node_modules 后按包名解析 config: channel: public ... ``` `dsh plugin --profile web add <包路径>` 执行后,`dsh` 会检测到该包声明了 `dsh.bundle`,自动把它加入 `dsh.profile.bundles` 列表,其补丁随 profile 一起 生效。适用于希望用 pnpm 管理依赖、可移植发布的场景。 ### 3.2 手动补丁方式(当前已安装方式) 把包复制到 profile 目录下(如 `~/.dsh/profiles/web/plugins/dsh-tool-ths/`), 在 profile 的用户补丁层 `~/.dsh/profiles/web/cordis.patch.yml` 中插入同一行, `name` 用相对路径: ```yaml - insert: - id: tool-ths name: ./plugins/dsh-tool-ths/index.js config: channel: public ... ``` `name` 相对 profile 根目录解析;插件内部的 `@deepseek-ai/*` 裸导入通过 profile 的 node_modules(含 DSH 安装的扁平回退目录)逐级向上解析。 ### 3.3 热加载 profile 的 `cordis.patch.yml` 被 DSH 的 HMR 机制监听(`watchUserPatches`): 修改文件后,运行中的 GUI 会把补丁重新应用到根 Include,**无需重启**即可 加载/卸载/修改插件。若 HMR 未生效(例如以非 `dsh --profile web` 方式启动), 重启 GUI 即可。 ## 4. 插件内部结构 ``` dsh-tool-ths/ ├── package.json # 包元信息 + dsh.bundle 声明(bundle 方式安装用) ├── cordis.patch.yml # bundle 补丁(插入 tool-ths 加载项) ├── index.js # 插件入口:Config 校验、ThsGateway、7 个工具、 │ # systemPrompt 段落、GUI 设置项注册 ├── lib/ │ ├── util.js # 代码规范化(normalizeCode)、HTTP 重试、Markdown 表格、 │ │ # 日期/数字格式化(无通道逻辑) │ ├── public.js # PublicClient:10jqka 公开行情客户端 │ └── ifind.js # IFindClient:iFinD HTTP API 客户端 + 指标标签表 ├── scripts/ │ ├── smoke.mjs # 通道冒烟测试(免登录即可跑) │ ├── harness.mjs # stub 注册表加载插件并逐个调用工具 │ └── loader-activation-test.mjs# 真实 Cordis Loader 激活测试(生产 boot 路径) ├── README.md / README.zh.md # 快速上手 └── docs/ # 本文档集 ``` ### 关键对象 - **`Config`**(index.js):schemastery 配置模式,同时用于 Loader 配置校验与 GUI 设置页表单。字段见《工具与配置参考》。 - **`ThsGateway`**:按 `channel` 分发到 `IFindClient` 或 `PublicClient`; 仅在连接相关配置(refreshToken/baseUrl/timeoutMs/retries)变化时重建 iFinD 客户端,从而保住内存中的 access_token 缓存(7 天有效期内不重复登录)。 所有工具调用经 `gateway.run()` 包装,失败会记录到 `lastError` 供 `ths_status` 展示。 - **`IFindClient`**:封装官方鉴权(refresh_token → access_token)与数据端点; token 失效错误码(-1010/-1003/-1300/-1301/-1302)自动重登一次后重试。 - **`PublicClient`**:封装 10jqka JSONP 接口;实时行情由「分时数据(当日 OHLC/均价/量额求和)+ 昨收」合成(日线接口当日 K 收盘后才落库,不能作当日 OHLC 来源),详见《协议与数据格式》。 ## 5. 配置流向 ``` cordis.patch.yml (config) ──┐ GUI 设置页 (dsh-tool-ths 段) ──┴─► current() thunk ──► ThsGateway ──► 工具执行 ``` Loader 的 `config` 作为初始值;GUI 设置页通过 `ctx.settings.installSection(ctx, ns, Config, config, hooks)` 注册同名设置段, `setSource` 把设置值替换为实时来源。工具执行时读 `current()`,因此设置页改动 即时生效,无需重载。 ## 6. 依赖注入 插件声明 `inject: ["tools", "systemPrompt", "settings"]`: - `tools`:`@deepseek-ai/dsh-tools` 提供的工具注册表(`ctx.tools.register`); - `systemPrompt`:`@deepseek-ai/dsh-system-prompt`(`ctx.systemPrompt.section` 注册提示词段落,指引模型何时使用 ths_* 工具); - `settings`:`@deepseek-ai/dsh-settings` 提供的设置注册表 (`ctx.settings.installSection` 把组合配置注册为基线层,并让 GUI 设置页 的改动实时生效);由 `dsh-settings-file` 行提供,随 dsh-base 默认装载。 Loader 按服务可用性驱动激活顺序,插件缺服务时保持 pending,服务就绪后自动 激活。 ## 7. 扩展指南 - **新增 iFinD 端点**:在 `lib/ifind.js` 的 `IFindClient` 加一个 `request()` 包装方法;在 `index.js` 按现有模板 `ctx.tools.register(defineTool({...}))` 注册工具;补充 `docs/tool-reference.md`。 - **新增 public 端点**:在 `lib/public.js` 用 `jsonp()` 拉取并解析;注意 浏览器伪装请求头(UA/Referer/Cookie)与 502 重试。 - **新增通道**:实现同 `PublicClient` 的方法签名(`quote`/`kline`),在 `ThsGateway.sync()` 分发,`Config.channel` 加一个 `z.const` 分支。 - **测试**:`node scripts/smoke.mjs <代码>`(免登录)、 `node scripts/harness.mjs <代码>`(stub 注册表全工具调用)、 `node scripts/loader-activation-test.mjs`(真实 Loader 激活,需工作区 node_modules 指向 DSH 安装的软链)。