dsh-plugin-show-me-data 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +27 -0
- package/README.md +96 -0
- package/cordis.patch.yml +40 -0
- package/docs/01-product-effect.md +178 -0
- package/docs/02-architecture.md +275 -0
- package/docs/03-data-contracts.md +291 -0
- package/docs/04-sources.md +342 -0
- package/docs/05-ui-spec.md +167 -0
- package/docs/06-ai-layer.md +194 -0
- package/docs/07-implementation-plan.md +399 -0
- package/docs/08-test-plan.md +133 -0
- package/docs/09-packaging-install.md +249 -0
- package/docs/10-kickoff-prompt.md +94 -0
- package/docs/11-decisions.md +203 -0
- package/docs/12-runtime-verified.md +115 -0
- package/docs/13-acceptance.md +153 -0
- package/docs/14-progress.md +150 -0
- package/docs/15-publish.md +185 -0
- package/lib/app/ai-deterministic.js +327 -0
- package/lib/app/ai-validate.js +284 -0
- package/lib/app/ai.js +440 -0
- package/lib/app/health.js +77 -0
- package/lib/app/overview.js +349 -0
- package/lib/app/propose-indicator.js +122 -0
- package/lib/app/refresh.js +251 -0
- package/lib/app/series-view.js +195 -0
- package/lib/app/watchlist.js +102 -0
- package/lib/client.js +4322 -0
- package/lib/core/ai/prompts.js +213 -0
- package/lib/core/chart/axis.js +133 -0
- package/lib/core/chart/bar.js +58 -0
- package/lib/core/chart/candle.js +216 -0
- package/lib/core/chart/line.js +186 -0
- package/lib/core/chart/scale.js +132 -0
- package/lib/core/format.js +143 -0
- package/lib/core/indicators/catalog.js +1011 -0
- package/lib/core/indicators/resolve.js +196 -0
- package/lib/core/insight/digest.js +250 -0
- package/lib/core/insight/rank.js +115 -0
- package/lib/core/insight/related.js +90 -0
- package/lib/core/insight/rules.js +417 -0
- package/lib/core/stats/derive.js +123 -0
- package/lib/core/stats/series.js +465 -0
- package/lib/core/time/range.js +242 -0
- package/lib/core/types.js +478 -0
- package/lib/host/ai/discussion.js +559 -0
- package/lib/host/ai/dsh-llm-gateway.js +333 -0
- package/lib/host/config.js +194 -0
- package/lib/host/http/respond.js +165 -0
- package/lib/host/http/routes.js +689 -0
- package/lib/host/index.js +293 -0
- package/lib/host/infra/fs-repos.js +179 -0
- package/lib/host/infra/memory-fallback.js +64 -0
- package/lib/host/tools/define-tool.js +295 -0
- package/lib/host/tools/register.js +431 -0
- package/lib/host.js +7 -0
- package/lib/ports/clock.js +57 -0
- package/lib/ports/snapshot-repo.js +48 -0
- package/lib/sources/eastmoney-macro.js +197 -0
- package/lib/sources/eastmoney-quote.js +201 -0
- package/lib/sources/ecb.js +179 -0
- package/lib/sources/fred.js +207 -0
- package/lib/sources/http.js +136 -0
- package/lib/sources/ohlc.js +36 -0
- package/lib/sources/quote-cascade.js +177 -0
- package/lib/sources/registry.js +153 -0
- package/lib/sources/sina-cn.js +197 -0
- package/lib/sources/sina-us.js +187 -0
- package/lib/sources/tencent.js +158 -0
- package/lib/sources/us-treasury-rates.js +275 -0
- package/lib/sources/us-treasury.js +196 -0
- package/lib/sources/worldbank.js +170 -0
- package/package.json +69 -0
- package/src/app/ai-deterministic.js +327 -0
- package/src/app/ai-validate.js +284 -0
- package/src/app/ai.js +440 -0
- package/src/app/health.js +77 -0
- package/src/app/overview.js +349 -0
- package/src/app/propose-indicator.js +122 -0
- package/src/app/refresh.js +251 -0
- package/src/app/series-view.js +195 -0
- package/src/app/watchlist.js +102 -0
- package/src/client/api.js +323 -0
- package/src/client/components.js +1877 -0
- package/src/client/copy.js +368 -0
- package/src/client/index.js +169 -0
- package/src/client/store.js +219 -0
- package/src/core/ai/prompts.js +213 -0
- package/src/core/chart/axis.js +133 -0
- package/src/core/chart/bar.js +58 -0
- package/src/core/chart/candle.js +216 -0
- package/src/core/chart/line.js +186 -0
- package/src/core/chart/scale.js +132 -0
- package/src/core/format.js +143 -0
- package/src/core/indicators/catalog.js +1011 -0
- package/src/core/indicators/resolve.js +196 -0
- package/src/core/insight/digest.js +250 -0
- package/src/core/insight/rank.js +115 -0
- package/src/core/insight/related.js +90 -0
- package/src/core/insight/rules.js +417 -0
- package/src/core/stats/derive.js +123 -0
- package/src/core/stats/series.js +465 -0
- package/src/core/time/range.js +242 -0
- package/src/core/types.js +478 -0
- package/src/host/ai/discussion.js +559 -0
- package/src/host/ai/dsh-llm-gateway.js +333 -0
- package/src/host/config.js +194 -0
- package/src/host/http/respond.js +165 -0
- package/src/host/http/routes.js +689 -0
- package/src/host/index.js +293 -0
- package/src/host/infra/fs-repos.js +179 -0
- package/src/host/infra/memory-fallback.js +64 -0
- package/src/host/tools/define-tool.js +295 -0
- package/src/host/tools/register.js +431 -0
- package/src/ports/clock.js +57 -0
- package/src/ports/snapshot-repo.js +48 -0
- package/src/sources/eastmoney-macro.js +197 -0
- package/src/sources/eastmoney-quote.js +201 -0
- package/src/sources/ecb.js +179 -0
- package/src/sources/fred.js +207 -0
- package/src/sources/http.js +136 -0
- package/src/sources/ohlc.js +36 -0
- package/src/sources/quote-cascade.js +177 -0
- package/src/sources/registry.js +153 -0
- package/src/sources/sina-cn.js +197 -0
- package/src/sources/sina-us.js +187 -0
- package/src/sources/tencent.js +158 -0
- package/src/sources/us-treasury-rates.js +275 -0
- package/src/sources/us-treasury.js +196 -0
- package/src/sources/worldbank.js +170 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 hhhcbw
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
数据来源说明:`test/fixtures/` 中的快照取自 FRED、美国财政部、东方财富、新浪财经、
|
|
26
|
+
腾讯行情、世界银行、欧洲央行等公开接口,版权归各上游所有,仅供研究与测试参考;
|
|
27
|
+
本插件不存储也不再分发上游数据,面板中的数值与来源链接均指向原始出处。
|
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Show Me Data · 全球数据指标雷达(DSH 插件)
|
|
2
|
+
|
|
3
|
+
一套**可直接开工**的方案与实施文档,用于在 DeepSeek Harness 上开发插件
|
|
4
|
+
`dsh-plugin-show-me-data`(本机安装时曾用名 `@local/dsh-plugin-show-me-data`):采集全球宏观/利率/股市/债市指标 → 规则化筛出「值得关注」→
|
|
5
|
+
可溯源的图表看板 → AI 解析 / 时段总结 / 数据问答 → 目录与数据源均可扩展。
|
|
6
|
+
|
|
7
|
+
**本插件已实现并安装完成**(2026-09-12)。下面是真实的运行状态与用法;`docs/` 里的设计文档保留原文,
|
|
8
|
+
`docs/12–14` 记录了实现过程中的全部运行时校准、验收结果与偏差修复。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 一句话结论(先看这个)
|
|
13
|
+
|
|
14
|
+
| 问题 | 答案 |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| 最终是什么形态? | GUI 里一个**悬浮式「数据雷达」面板**(frame-wide overlay):今日值得关注 + 中美核心指标卡片 + SVG 图表 + 可点开原始数据源链接 + AI 解析/总结/问答 + 自定义指标。同时把数据能力**注册成模型工具**,所以「和 AI 对话加指标」是原生能力。 |
|
|
17
|
+
| 数据从哪来? | host 半(Node 进程)直接 `fetch`。**已实测可用**:FRED CSV(无需 API key)、东方财富 `push2his`/`datacenter-web`、美国财政部 fiscaldata、World Bank、ECB SDW。**已实测不可用**:BLS API(403)、Yahoo(403)、Stooq(JS 墙)、新浪 hq(403)。 |
|
|
18
|
+
| 为什么不用动态 Cordis 包(`cordis_define`)做产品? | 动态 host 半跑在 `node:vm` 沙箱里,**`fetch` 是被封装的 trap,直接抛错**;而本部署**没有挂载 fetch provider**(`web.fetch` 不可用)。所以动态包只能做 UI 原型,不能做取数。产品必须是**真正安装的插件包**。 |
|
|
19
|
+
| 怎么保证可扩展? | 三层端口:`SourceAdapter`(数据源)/ `IndicatorCatalog`(指标定义)/ `AiGateway`(AI)。**加一个新指标 = 改 3 行目录数据,0 行代码**;**加一个新数据源 = 1 个文件 + 1 行注册 + 1 个 fixture 测试**。 |
|
|
20
|
+
| 怎么保证 TDD? | `core/` 与 `sources/` 全部是**无 IO 纯逻辑 / 纯适配器**,用 `node:test`(零依赖)跑;网络调用在测试中一律走**录制好的 fixtures**,只有 `RUN_NET=1` 时才打真网。图表是手写 SVG 的纯几何函数,因此**图也能被单测**。 |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 文档索引
|
|
25
|
+
|
|
26
|
+
| 文档 | 内容 | 谁看 |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| [`docs/01-product-effect.md`](docs/01-product-effect.md) | **最终表现效果**:完整使用叙事、面板线框、交互清单、降级表现 | 你(评审效果) |
|
|
29
|
+
| [`docs/02-architecture.md`](docs/02-architecture.md) | 分层与端口契约、数据流、扩展点、目录结构、运行时事实 | 实现 agent |
|
|
30
|
+
| [`docs/03-data-contracts.md`](docs/03-data-contracts.md) | 全部数据结构、归一化/派生规则、种子指标目录(实测可用系列 ID) | 实现 agent |
|
|
31
|
+
| [`docs/04-sources.md`](docs/04-sources.md) | **实测**数据源矩阵、每个适配器规格、字段陷阱、降级策略 | 实现 agent |
|
|
32
|
+
| [`docs/05-ui-spec.md`](docs/05-ui-spec.md) | 槽位选型、视图与组件、图表规格、状态机、主题与 i18n | 实现 agent |
|
|
33
|
+
| [`docs/06-ai-layer.md`](docs/06-ai-layer.md) | AI 端口与适配器、提示词、结构化输出、防幻觉、可溯源、离线降级 | 实现 agent |
|
|
34
|
+
| [`docs/07-implementation-plan.md`](docs/07-implementation-plan.md) | **实施计划**:M0–M10 里程碑 + 每步的 TDD 卡片与验收标准 | 实现 agent |
|
|
35
|
+
| [`docs/08-test-plan.md`](docs/08-test-plan.md) | 测试清单、fixtures 策略、门禁与覆盖率门槛 | 实现 agent |
|
|
36
|
+
| [`docs/09-packaging-install.md`](docs/09-packaging-install.md) | 包结构、client bundle 手写格式、安装/挂载/回滚/调试命令 | 实现 agent + 你 |
|
|
37
|
+
| [`docs/10-kickoff-prompt.md`](docs/10-kickoff-prompt.md) | **可直接粘贴到创造模式的启动提示词** + 完成定义 DoD | 你 |
|
|
38
|
+
| [`docs/11-decisions.md`](docs/11-decisions.md) | 决策记录 ADR:选定方案与被否决方案的理由 | 所有人 |
|
|
39
|
+
| [`docs/12-runtime-verified.md`](docs/12-runtime-verified.md) | 运行时校准记录(M0 填写模板) | 实现 agent |
|
|
40
|
+
| [`docs/13-acceptance.md`](docs/13-acceptance.md) | 验收记录(M8/M10 填写模板) | 实现 agent + 你 |
|
|
41
|
+
| [`docs/14-progress.md`](docs/14-progress.md) | 进度记录(每里程碑更新,含偏差日志) | 实现 agent |
|
|
42
|
+
| [`docs/15-publish.md`](docs/15-publish.md) | **打包与发布**:能不能发 npm / GitHub、还差什么、命令清单 | 你(发布时) |
|
|
43
|
+
| [`scripts/probe-sources.mjs`](scripts/probe-sources.mjs) | **现在就能跑**的数据源巡检脚本(本文档所有源结论由它复现) | 所有人 |
|
|
44
|
+
| [`cordis.patch.yml`](cordis.patch.yml) | 挂载片段:追加到 `/data/profiles/web/cordis.patch.yml` | 实现 agent |
|
|
45
|
+
|
|
46
|
+
> 除 `scripts/probe-sources.mjs` 外,文档里提到的 `build-client.mjs`、`record-fixtures.mjs`、
|
|
47
|
+
> `smoke.mjs` 都是 **M0–M10 的交付物**,目前尚不存在。
|
|
48
|
+
|
|
49
|
+
## 现在怎么用
|
|
50
|
+
|
|
51
|
+
插件已装进 web profile(`/data/profiles/web`),GUI 右下角有一个「数据雷达」浮层触发器。
|
|
52
|
+
后端的路由与模型工具同时可用:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
curl -s http://127.0.0.1:38080/api/show-me-data/health # 各源可用性
|
|
56
|
+
curl -s 'http://127.0.0.1:38080/api/show-me-data/overview?range=1Y' # 面板数据(52 个指标)
|
|
57
|
+
curl -s 'http://127.0.0.1:38080/api/show-me-data/series?indicator=us.dgs10&range=1Y'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
对话里也可以直接用:`data_overview` / `data_series` / `data_search` / `data_watchlist` /
|
|
61
|
+
`data_explain` / `data_digest` / `data_refresh` / `data_health` 八个工具。
|
|
62
|
+
|
|
63
|
+
**日常命令**
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
node --test "test/**/*.test.js" # 全部门禁(当前 598 通过 / 0 失败)
|
|
67
|
+
RUN_NET=1 node --test "test/net/*.test.js" # 真网冒烟(每个适配器至少一个序列可返回)
|
|
68
|
+
RUN_BOOT=1 node --test "test/net/boot.test.js" # 真实启动门禁(把插件挂到 profile 副本并启动)
|
|
69
|
+
node scripts/build-client.mjs && node scripts/build-host.mjs # 重新构建 lib/(改动 src/ 后必须执行)
|
|
70
|
+
RUN_NET=1 node scripts/record-fixtures.mjs # 重新录制上游 fixtures
|
|
71
|
+
node scripts/probe-sources.mjs # 数据源可达性巡检
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**数据源现状(实测)**:`fred`、`eastmoney-macro`、`us-treasury`、`us-treasury-rates`(财政部官方收益率曲线)、
|
|
75
|
+
`worldbank`、`ecb`、`sina-us`(美股指数)、`sina-cn`(A 股与债券指数)可用;`eastmoney-quote` 与 `tencent`
|
|
76
|
+
在本网络被上游限流(已由 `quote` 多源级联兜住,见 `docs/13-acceptance.md` E 段)。
|
|
77
|
+
无可用源的 12 个指标标为 `unsupported`,调查过程记录在 `docs/03 §3.6` 与代码内 `UNSUPPORTED` 常量。
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 开工方式(最短路径)
|
|
82
|
+
|
|
83
|
+
1. 新建一个会话,预设选 **`cordis`(创造模式)**,工作目录 `/root/show_me_data`。
|
|
84
|
+
2. 把 [`docs/10-kickoff-prompt.md`](docs/10-kickoff-prompt.md) 的提示词整段贴进去。
|
|
85
|
+
3. 让 agent 先做 **M0**:它必须先用 `cordis_inspect` 查真实槽位/服务,再把本目录的假设校准成
|
|
86
|
+
`docs/12-runtime-verified.md`(见 `02` 文档「运行时校准」一节),然后才开始写测试。
|
|
87
|
+
|
|
88
|
+
## 三条硬约束(别在实施时忘掉)
|
|
89
|
+
|
|
90
|
+
1. **取数只能在 host 半做**(真实插件进程里的 `fetch`)。浏览器侧受 CORS 限制:FRED 不返回
|
|
91
|
+
可用的 CORS 头(实测为 `null` 或缺失),浏览器直连会失败。
|
|
92
|
+
2. **不要依赖 `ctx.web.fetch`**:本部署 `tool-web` 配置为 `fetch: false` 且没有 fetch provider。
|
|
93
|
+
3. **安装要写 `/data/profiles/**`(会话工作区之外)**:取决于当时会话的文件策略——
|
|
94
|
+
若是 `workspace-write`,写 profile 会被拒,此时按 `09` 文档对**同一条命令**就地提权重试一次;
|
|
95
|
+
若是全访问策略则直接写。**绝不要**改 `/data/profiles/web/cordis.yml`(它是空 root,由 bundle 组合),
|
|
96
|
+
要改的是 `cordis.patch.yml`。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# 挂载片段:把它追加到 /data/profiles/web/cordis.patch.yml
|
|
2
|
+
#
|
|
3
|
+
# 语义提醒(实测自 dsh-base/cordis.patch.yml):一个 patch 会**替换**目标行的整份 config,
|
|
4
|
+
# 不是合并。所以下面把希望生效的配置项写全。
|
|
5
|
+
#
|
|
6
|
+
# 前置:先执行 `dsh plugin --profile web add dsh-plugin-show-me-data` 让包可被解析
|
|
7
|
+
# (本机开发时该名字是指向 /root/show_me_data 的 link,见 docs/09)。
|
|
8
|
+
# 之后重启 web profile 进程并刷新浏览器页面。
|
|
9
|
+
|
|
10
|
+
- insert:
|
|
11
|
+
- id: show-me-data
|
|
12
|
+
name: 'dsh-plugin-show-me-data'
|
|
13
|
+
config:
|
|
14
|
+
# 缓存与刷新
|
|
15
|
+
refreshMinutes: 30
|
|
16
|
+
cacheTtl: # 缺省按频率;这里可按需覆盖
|
|
17
|
+
daily: 15
|
|
18
|
+
weekly: 180
|
|
19
|
+
monthly: 360
|
|
20
|
+
quarterly: 1440
|
|
21
|
+
# 首页展示
|
|
22
|
+
groups: [US, CN, GLOBAL, CUSTOM]
|
|
23
|
+
noteworthyLimit: 5
|
|
24
|
+
# AI
|
|
25
|
+
ai:
|
|
26
|
+
enabled: true
|
|
27
|
+
mode: auto # auto | llm | deterministic | relay
|
|
28
|
+
maxChars: 1200
|
|
29
|
+
cacheMinutes: 60
|
|
30
|
+
# 数据源开关(默认全开;排查时可单独关掉)
|
|
31
|
+
sources:
|
|
32
|
+
fred: true
|
|
33
|
+
eastmoney-quote: true
|
|
34
|
+
eastmoney-macro: true
|
|
35
|
+
us-treasury: true
|
|
36
|
+
us-treasury-rates: true
|
|
37
|
+
worldbank: true
|
|
38
|
+
ecb: true
|
|
39
|
+
tencent: true
|
|
40
|
+
sina-us: true
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# 01 · 最终表现效果(产品形态)
|
|
2
|
+
|
|
3
|
+
这一篇只回答一个问题:**做出来之后,你在 GUI 里会看到什么、能做什么。**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. 一次完整使用(叙事)
|
|
8
|
+
|
|
9
|
+
> 早上打开 DSH Web GUI(`http://127.0.0.1:38080`)。画面右下角多了一个常驻小徽标
|
|
10
|
+
> `📊 数据雷达 · 2`——那个 `2` 表示:**自你上次查看后,有 2 条新发布/异常的数据**。
|
|
11
|
+
|
|
12
|
+
点开徽标,弹出占屏幕约 78% 的浮层面板(不遮挡输入框,可拖动、可 Esc 关闭、可钉住)。
|
|
13
|
+
|
|
14
|
+
**顶部:今日值得关注(3 张卡)**
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
┌─ 值得关注 ──────────────────────────── 数据截至 09-11 21:30 · 2 条新增 ─┐
|
|
18
|
+
│ ⚡ 美国 8月非农就业 +16.2万 ▲ 高于前值 13.1万 │
|
|
19
|
+
│ [sparkline 12 个月] 发布 09-05 · 来源 FRED PAYEMS ↗ │
|
|
20
|
+
│ 规则命中:新发布 · 超预期幅度 1.8σ · 3 个月新高 │
|
|
21
|
+
│ [ AI 解析 ] [ 看图表 ] [ 加入我的关注 ] │
|
|
22
|
+
├──────────────────────────────────────────────────────────────────────┤
|
|
23
|
+
│ ⚠ 美国 10Y-2Y 利差 +0.31pp ▼ 较上周 -0.09pp │
|
|
24
|
+
│ [sparkline] 来源 FRED T10Y2Y ↗ 命中:区间波动异常 │
|
|
25
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**中部:核心指标(中美双栏,可切换 1M / 3M / 6M / YTD / 1Y / 3Y / 5Y / 自定义)**
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
┌─ 美国 ──────────────────────────┐ ┌─ 中国 ──────────────────────────┐
|
|
32
|
+
│ CPI 同比 3.4% ▲0.1 [∿] │ │ CPI 同比 0.6% ▲0.2 [∿] │
|
|
33
|
+
│ 核心CPI 同比 3.1% ─0.0 [∿] │ │ PPI 同比 -1.8% ▲0.4 [∿] │
|
|
34
|
+
│ 失业率 4.1% ─0.0 [∿] │ │ 制造业PMI 49.8 ▲0.8 [∿] │
|
|
35
|
+
│ 非农就业 +16.2万 [∿] │ │ GDP 同比 5.2% ▲0.1 [∿] │
|
|
36
|
+
│ 联邦基金利率 3.63% ─ [∿] │ │ M2 同比 8.1% ▲0.3 [∿] │
|
|
37
|
+
│ 10Y 国债 4.47% ▼0.03 [∿] │ │ 沪深300 4123.5 ▲1.2% [∿] │
|
|
38
|
+
│ 10Y-2Y 利差 +0.31pp [∿] │ │ 上证国债指数 231.4 ▲0.1% [∿] │
|
|
39
|
+
│ 标普500 6480.2 ▲0.4% [∿] │ │ 恒生指数 25328 ▼0.6% [∿] │
|
|
40
|
+
└─────────────────────────────────┘ └─────────────────────────────────┘
|
|
41
|
+
每张卡右下角都有「来源徽章」:FRED / 东方财富 / 美国财政部 —— 点击直达原始 URL
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**点击任意卡片 → 详情视图**
|
|
45
|
+
|
|
46
|
+
- 主图:多序列切换(原始值 / 环比 / 同比 / 4 周均值),线图 / 柱图 / 面积图,可叠对比序列(例如
|
|
47
|
+
「美国 CPI 同比」叠「核心 CPI 同比」,或「中美国债 10Y 利差」= 两条序列相减)
|
|
48
|
+
- 统计带:区间均值 / 最大 / 最小 / 标准差 / 变化幅度 / 趋势斜率(回归)/ 缺失点数
|
|
49
|
+
- 数据表:逐点时间与数值(可复制 CSV)
|
|
50
|
+
- 右侧 **AI 解析**(流式):这个指标是什么 → 本期读数意味着什么 → 与历史同期对比 → 对哪些资产/政策有影响 → 风险与反例 → **引用列表(每条引用都带指标 ID、日期、数值、原始 URL)**
|
|
51
|
+
|
|
52
|
+
**底部:AI 问答**
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
┌ 就当前面板数据提问 ─────────────────────────────────────────────┐
|
|
56
|
+
│ 非农超预期、失业率却没动,这对降息预期意味着什么? [发送] │
|
|
57
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
回答流式出现,末尾固定附「本次回答使用的数据点」清单(可点开逐条跳到图表对应位置)与
|
|
61
|
+
「数据可信度」标记(哪些指标是 fresh / stale / 缺失)。
|
|
62
|
+
|
|
63
|
+
**右上:+ 添加指标**
|
|
64
|
+
|
|
65
|
+
两种方式都被支持:
|
|
66
|
+
|
|
67
|
+
1. **目录检索**:输入「国债」「PMI」「通胀」→ 命中内置目录(种子约 50 个指标,覆盖中美与全球,
|
|
68
|
+
可随目录数据继续扩充)→ 勾选加入。
|
|
69
|
+
2. **自然语言(AI 对话,同一入口)**:输入
|
|
70
|
+
`帮我加上中国10年期国债收益率,还有德国 Ifo 商业景气指数`
|
|
71
|
+
→ AI 把它解析成结构化指标定义 → 面板显示「预览:我将添加 2 项,数据源分别是 ……」
|
|
72
|
+
→ 你确认 → 落盘到自定义关注清单 → 立刻出现在核心区。
|
|
73
|
+
若某个指标**现有数据源无法覆盖**(如 Ifo 指数当前适配器没有),面板会明确说
|
|
74
|
+
「当前无可用数据源,原因:…;可选方案:新增适配器 / 用代理指标替代」,**绝不编造数值**。
|
|
75
|
+
|
|
76
|
+
**也可以在对话里直接用**:
|
|
77
|
+
|
|
78
|
+
> 你:看看今天有什么值得关注的宏观数据
|
|
79
|
+
> AI:(调用 `data_overview`)+ 一段解读,并在会话里内联渲染一张图卡片
|
|
80
|
+
> 你:把上证国债指数加进我的看板,时间范围拉到 3 年
|
|
81
|
+
> AI:(调用 `data_watchlist add` + `data_overview range=3Y`)→ 面板同步更新
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 2. 界面线框(浮层面板整体)
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
╔════════════════════════════════════════════════════════════════════════════╗
|
|
89
|
+
║ 📊 数据雷达 [今日] [核心] [我的] [设置] 范围:[1Y▾] ⟳ 6分钟前 ⚙ ✕ ║
|
|
90
|
+
╠════════════════════════════════════════════════════════════════════════════╣
|
|
91
|
+
║ ▍值得关注 2 条新增 · 规则+AI 排序║
|
|
92
|
+
║ ┌──────────────────────────┐ ┌──────────────────────────┐ ┌────────────┐ ║
|
|
93
|
+
║ │ ⚡ 美国 8月非农就业 │ │ ⚠ 10Y-2Y 利差 │ │ + 更多 5 条│ ║
|
|
94
|
+
║ │ +16.2万 ▲ 1.8σ │ │ +0.31pp ▼ -0.09pp │ │ │ ║
|
|
95
|
+
║ │ ∿∿∿∿∿∿∿ chart │ │ ∿∿∿∿∿∿∿ chart │ │ │ ║
|
|
96
|
+
║ │ 来源 FRED ↗ [AI 解析] │ │ 来源 FRED ↗ [AI 解析] │ │ │ ║
|
|
97
|
+
║ └──────────────────────────┘ └──────────────────────────┘ └────────────┘ ║
|
|
98
|
+
╠════════════════════════════════════════════════════════════════════════════╣
|
|
99
|
+
║ ▍核心指标(美国 / 中国 / 全球 / 我的) [网格|列表] ║
|
|
100
|
+
║ <指标卡片网格,每卡:名称 · 最新值 · 变化 · sparkline · 来源徽章 · 状态点> ║
|
|
101
|
+
╠════════════════════════════════════════════════════════════════════════════╣
|
|
102
|
+
║ ▍时段 AI 总结 [对当前范围生成总结] ║
|
|
103
|
+
║ · 关键变化 · 相互印证 · 背离与矛盾 · 下一个该盯的数据(含预计发布窗口) ║
|
|
104
|
+
╠════════════════════════════════════════════════════════════════════════════╣
|
|
105
|
+
║ 💬 就当前数据提问 … [发送] ║
|
|
106
|
+
╚════════════════════════════════════════════════════════════════════════════╝
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
面板之外还有三处入口(都已纳入实施计划):
|
|
110
|
+
|
|
111
|
+
- **设置页**(`settings.section`):「数据看板」分区——数据源开关、缓存 TTL、刷新频率、
|
|
112
|
+
自定义指标管理、AI 开关与模型选择、单位与季节调整偏好。
|
|
113
|
+
- **会话内图表卡**(工具调用卡):模型调用 `data_overview` / `data_query` 时,对话里直接渲染
|
|
114
|
+
图表 + 来源,而不是只有一段文字。
|
|
115
|
+
- **常驻徽标**(`shell.overlay` 上的小触发器):带「新增条数」角标。
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 3. 交互清单(验收时逐条核对)
|
|
120
|
+
|
|
121
|
+
| # | 交互 | 期望 |
|
|
122
|
+
| --- | --- | --- |
|
|
123
|
+
| 1 | 打开面板 | ≤ 300ms 出首屏骨架;有缓存时立即显示旧值并标注「6 分钟前」,随后静默刷新 |
|
|
124
|
+
| 2 | 切换时间范围 | 所有卡片与图表重算,URL/状态保留;切回不重新打网(命中缓存) |
|
|
125
|
+
| 3 | 点击来源徽章 | 新标签打开**原始数据源 URL**(如 `fred.stlouisfed.org/series/PAYEMS`) |
|
|
126
|
+
| 4 | 点击卡片 | 进详情:主图 + 统计带 + 数据表 + AI 解析 |
|
|
127
|
+
| 5 | AI 解析 | 流式输出;回答里每条结论可回溯到具体数据点;无 LLM 时退化为确定性摘要并明确标注 |
|
|
128
|
+
| 6 | 时段 AI 总结 | 结构化 4 段(关键变化/互相印证/背离/下一个看点),引用当前范围内的真实数据点 |
|
|
129
|
+
| 7 | AI 问答 | 仅用面板内数据回答;超出数据范围时**明确说"数据不足"**而不是编造 |
|
|
130
|
+
| 8 | 添加指标(目录) | 检索 → 加入 → 落盘 → 立即出现在「我的」栏 |
|
|
131
|
+
| 9 | 添加指标(对话) | 自然语言 → 结构化预览 → 确认 → 落盘;无法覆盖时给出原因与替代方案,不编造 |
|
|
132
|
+
| 10 | 移除/排序自定义项 | 立即生效并持久化,重启后仍在 |
|
|
133
|
+
| 11 | 单个数据源失败 | 该卡片显示「数据源暂不可用(最后成功 12:03)」+ 重试按钮;**其余卡片不受影响** |
|
|
134
|
+
| 12 | 全部数据源失败 | 面板显示降级态:上次快照 + 明确横幅,绝不显示空白或伪造数值 |
|
|
135
|
+
| 13 | 数据陈旧 | 按指标频率判断陈旧(日频 >2 交易日、月频 >40 天)→ 黄色状态点 |
|
|
136
|
+
| 14 | 单位与口径 | 每张卡都有单位(%、pp、万人、点)与季节调整标注(SA/NSA) |
|
|
137
|
+
| 15 | 无网络环境 | 面板读本地快照;AI 功能自动切到确定性摘要模式 |
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 4. 「值得关注」是怎么算出来的(不是全靠 AI)
|
|
142
|
+
|
|
143
|
+
这是产品可信度的关键:**排序与命中理由必须可复现**。
|
|
144
|
+
|
|
145
|
+
`core/insight/rules.js` 是一组**纯函数规则**,对每个指标在给定范围上打分:
|
|
146
|
+
|
|
147
|
+
| 规则 | 说明 | 典型触发 |
|
|
148
|
+
| --- | --- | --- |
|
|
149
|
+
| `fresh-release` | 最新观测日落在近 N 天(N 按频率:日频 3、月频 10、季频 45) | 今日公布非农 |
|
|
150
|
+
| `surprise-sigma` | 最新变化量 / 历史变化标准差 \|z\| ≥ 1.5 | 非农超预期 |
|
|
151
|
+
| `extreme` | 近 3 年新高/新低(或分位数 ≥ 95% / ≤ 5%) | CPI 创 3 年新低 |
|
|
152
|
+
| `trend-break` | 趋势斜率符号翻转且幅度显著 | 利差由陡转平 |
|
|
153
|
+
| `threshold-cross` | 跨越业务阈值(政策利率变动、CPI 同比跨 3%、失业率跨 4.5%) | 降息落地 |
|
|
154
|
+
| `spread-signal` | 派生序列(利差/比值)出现倒挂、快速收敛 | 10Y-2Y 转正 |
|
|
155
|
+
| `divergence` | 两个相关指标方向背离 | 非农强但失业率不动 |
|
|
156
|
+
| `stale-gap` | 应有新数据但没来(发布节奏推测) | 数据源故障 |
|
|
157
|
+
|
|
158
|
+
每条命中都产出 `{ ruleId, score, reason, evidence }`,UI 直接显示理由。AI 只做两件事:
|
|
159
|
+
**① 对这些理由做自然语言排序与合并**;**② 生成解读文本**。AI 不可用时,规则本身已能给出可读列表。
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 5. 数据可溯源的三个层级
|
|
164
|
+
|
|
165
|
+
1. **卡片级**:来源徽章 = 提供方(FRED / 东方财富 / 美国财政部 / World Bank / ECB)+ 原始 URL。
|
|
166
|
+
2. **观测级**:数据表每行可显示 `fetched_at` 与源端系列 ID(如 `PAYEMS`、`secid=1.000001`)。
|
|
167
|
+
3. **AI 级**:AI 回答强制携带 `usedPoints[]`(`indicatorId` + 日期 + 数值 + `sourceUrl`),
|
|
168
|
+
UI 渲染为可点击引用;**引用不到数据点的断言不允许输出**(提示词 + 输出校验双重约束,见 `06`)。
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 6. 明确不做(避免范围蔓延)
|
|
173
|
+
|
|
174
|
+
- 不做实时逐笔行情 / 交易下单;日频与月频为主,最高到分钟级快照(腾讯行情)仅作可选。
|
|
175
|
+
- 不做预测模型(不训练、不外推点位);AI 只解释已有数据。
|
|
176
|
+
- 不做付费数据源接入(Wind / Bloomberg / 同花顺 iFinD);适配器接口预留但不实现。
|
|
177
|
+
- 不做多用户/权限;单进程本地使用。
|
|
178
|
+
- 不做新闻聚合(可后续作为 `SourceAdapter` 的兄弟类型扩展,本方案不承诺)。
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# 02 · 架构与端口契约
|
|
2
|
+
|
|
3
|
+
目标:**可扩展、低耦合、可单测**。约束来自 DSH 的真实运行时,不是设计偏好。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. 运行时事实(已实测,实施时按此假设,不要重新猜)
|
|
8
|
+
|
|
9
|
+
### 1.1 产物形态
|
|
10
|
+
|
|
11
|
+
| 事实 | 依据 | 对设计的影响 |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| DSH = Cordis 插件树;能力 = `cordis.yml` 里的一行 `insert` | `dsh-base/cordis.patch.yml` 以 `- insert: [...]` 组织 | 我们的插件最终是**一个包 + profile 里一行 insert** |
|
|
14
|
+
| Web profile 由 bundle 组成:`@deepseek-ai/dsh-base` + `@deepseek-ai/dsh-web-app`;用户层是 `/data/profiles/web/cordis.patch.yml` | `/data/profiles/web/package.json` 的 `dsh.profile.bundles` | 挂载点明确:往 `cordis.patch.yml` 追加 `insert` 行 |
|
|
15
|
+
| 持久插件 = 宿主半(Cordis plugin)+ 可选浏览器半(`dsh.client` 元数据 + `exports["./client"]`) | `dsh-client-ui-jobs/package.json` | 浏览器半由 `client-modules` 扫描已启用 entry、按 `/plugins` 提供 |
|
|
16
|
+
| 浏览器半是**普通经典脚本**,格式为 `window.__ModuleLoader__.load({ id, factory: (require) => {…} })` | `dsh-client-ui-jobs/lib/client.js` 首行 | **可以手写,不需要 tsdown/打包器**;`factory` 里 `require("react")` 可解析 |
|
|
17
|
+
| 浏览器半**无法 `require` 自己的宿主代码**(一个包 = 一个模块节点) | `dsh-client-modules` README(扁平模块图) | 浏览器半必须自包含;共享纯逻辑用「同一份源码 + 测试 shim」策略(见 §4.3) |
|
|
18
|
+
| 没有图表库可 `require`(模块图里只有 react 与 DSH 自己的包) | 同上 | **图表手写 SVG**——反而让图变成可单测的纯函数 |
|
|
19
|
+
| 宿主半可注册 HTTP 路由:`ctx.webServer.register({ path, kind, handler })` | `dsh-host-webserver` README | 浏览器半 ↔ 宿主半的**首选通信方式**(免 Typert codegen) |
|
|
20
|
+
| 另有 Typert Remote(需要 codegen)可做正式 RPC | `dsh-host-plugin-inventory/typert` | 作为备选,不进 M0–M10 主路径 |
|
|
21
|
+
|
|
22
|
+
### 1.2 动态 Cordis 包(创造模式)的真实边界
|
|
23
|
+
|
|
24
|
+
| 事实 | 依据 | 结论 |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| 动态包只活在**进程内存**,不落盘、重启即失、无法自动晋升为正式插件 | `dsh-tool-cordis` README | 只能做原型/演示,不能做产品 |
|
|
27
|
+
| 沙箱里 `fetch` 是**抛错 trap**,提示改用 `ctx.web` | `dsh-cordis-host-runner/lib/index.js` 的 `NODE_API_REDIRECTS` | 动态宿主半**不能取数** |
|
|
28
|
+
| 本部署 `tool-web` 配置 `fetch: false`,**未挂载任何 fetch provider** | `dsh-base/cordis.patch.yml` 第 400–410 行注释 | `ctx.web.fetch()` 必失败,**任何取数都不要依赖它** |
|
|
29
|
+
| 宿主半在 vm 中的**同步段**受 `vmTimeoutMs`(默认 5000ms)约束 | `dsh-cordis-host-runner` README | 动态宿主半只能注册,不能阻塞 |
|
|
30
|
+
| 浏览器半可用浏览器 `fetch`,但受 CORS | 实测:FRED 返回 `Access-Control-Allow-Origin: null` | 动态原型只能用 CORS 友好的源(东方财富 `*`、World Bank `*`、ECB `*`、Treasury `*`) |
|
|
31
|
+
|
|
32
|
+
**决策**:产品走「真正安装的插件包」。创造模式的用途是
|
|
33
|
+
**(a) 用 `cordis_inspect` 读真实接口校准本文档,(b) 用动态包快速迭代 UI 外观与交互**。
|
|
34
|
+
|
|
35
|
+
### 1.3 可用的宿主服务(实现时用 `cordis_inspect` 精确核对签名)
|
|
36
|
+
|
|
37
|
+
| 服务 | 用途 | 备注 |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| `webServer` | 注册 `/api/show-me-data/*` 路由 | 免 codegen 的 C→H 通道 |
|
|
40
|
+
| `storage` / `storageDomain` | 可选持久化(domain KV,JSON backend) | 本方案**首选自建文件仓储**(更易测、无 codegen);domain 作为可选适配器 |
|
|
41
|
+
| `fs` | 读写快照与配置 | 走 `ctx.fs` 以受沙箱策略约束 |
|
|
42
|
+
| `tools` | `ctx.tools.register(definition)` 注册模型工具 | `output` 声明必填 |
|
|
43
|
+
| `llm` | `ctx.llm.stream()` / `prepareCall()` 直接调模型 | AI 适配器之一 |
|
|
44
|
+
| `agentDefaultModel` | `currentSelection()` 取默认 provider/model | AI 调用需要它兜底 |
|
|
45
|
+
| `timer` | 定时刷新(必须 `inject: ['timer']`,用 `ctx.interval`) | 不要用全局 setInterval |
|
|
46
|
+
| `settings` | 设置分区持久化 | 配合 `settings.section` 槽位 |
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 2. 分层与目录结构
|
|
51
|
+
|
|
52
|
+
**依赖方向永远向内**:`client / host → application → core`,`sources / infra → ports`。
|
|
53
|
+
`core/` 里**不允许出现任何 DSH、fetch、fs、Date.now 的直接使用**。
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
/root/show_me_data/
|
|
57
|
+
├── package.json # 包元数据:main=host 半, dsh.client 元数据, exports["./client"]
|
|
58
|
+
├── cordis.patch.yml # 供挂载用的 profile patch 片段(不自动生效,见 09)
|
|
59
|
+
├── README.md
|
|
60
|
+
├── docs/ # 本套文档
|
|
61
|
+
├── scripts/
|
|
62
|
+
│ ├── build-client.mjs # src/client/** → lib/client.js(零依赖包装器,可单测)
|
|
63
|
+
│ └── record-fixtures.mjs # 真网抓一次 → test/fixtures/*.json(人工触发)
|
|
64
|
+
├── src/
|
|
65
|
+
│ ├── core/ # ★ 纯领域层:无 IO、无 DSH,100% 可单测
|
|
66
|
+
│ │ ├── types.js # JSDoc typedef + 运行时校验器
|
|
67
|
+
│ │ ├── time/range.js # 范围解析、分桶、上一周期、交易日近似
|
|
68
|
+
│ │ ├── stats/series.js # 对齐/排序/去重/缺失/环比/同比/均值/σ/分位/斜率/回撤
|
|
69
|
+
│ │ ├── stats/derive.js # 派生序列(利差、比值、实际利率、4周均值)
|
|
70
|
+
│ │ ├── indicators/catalog.js # 种子目录(数据,不是逻辑)
|
|
71
|
+
│ │ ├── indicators/resolve.js # 自然语言/模糊检索 → 指标候选(+ AI 输入)
|
|
72
|
+
│ │ ├── insight/rules.js # 8 条值得关注规则(纯函数)
|
|
73
|
+
│ │ ├── insight/rank.js # 评分合并与排序(纯函数)
|
|
74
|
+
│ │ ├── insight/digest.js # 构造 AI 输入的数据摘要(控 token)
|
|
75
|
+
│ │ └── chart/{scale,line,bar,spark,axis}.js # 纯几何 → SVG 片段
|
|
76
|
+
│ ├── ports/ # ★ 端口(接口 + 契约测试辅助)
|
|
77
|
+
│ │ ├── source-adapter.js # 数据源适配器契约 + 契约测试工厂
|
|
78
|
+
│ │ ├── snapshot-repo.js # 快照缓存读写
|
|
79
|
+
│ │ ├── watchlist-repo.js # 关注清单持久化
|
|
80
|
+
│ │ ├── conversation-gateway.js # Agent 中继(把问题送回会话,可选)
|
|
81
|
+
│ │ ├── ai-gateway.js # AI 端口(explain/summarize/answer/propose)
|
|
82
|
+
│ │ └── clock.js # 时间源(测试可控)
|
|
83
|
+
│ ├── sources/ # ★ 适配器实现(薄、可替换、契约测试)
|
|
84
|
+
│ │ ├── registry.js # id → adapter 注册表 + 能力查询(扩展点)
|
|
85
|
+
│ │ ├── fred.js # FRED CSV(无需 key)
|
|
86
|
+
│ │ ├── eastmoney-quote.js # push2his kline(股/债指数)
|
|
87
|
+
│ │ ├── eastmoney-macro.js # datacenter-web RPT_ECONOMY_*
|
|
88
|
+
│ │ ├── us-treasury.js # fiscaldata
|
|
89
|
+
│ │ ├── worldbank.js # api.worldbank.org
|
|
90
|
+
│ │ └── ecb.js # data-api.ecb.europa.eu
|
|
91
|
+
│ ├── app/ # ★ 用例层:只依赖 ports + core
|
|
92
|
+
│ │ ├── overview.js # 今日值得关注 + 核心卡片
|
|
93
|
+
│ │ ├── series-view.js # 单指标详情(图 + 统计 + 表)
|
|
94
|
+
│ │ ├── watchlist.js # 关注清单增删改查
|
|
95
|
+
│ │ ├── ai-explain.js ai-summary.js ai-qa.js
|
|
96
|
+
│ │ ├── propose-indicator.js # 自然语言 → 指标定义候选
|
|
97
|
+
│ │ └── refresh.js # 缓存/TTL/并发/降级编排
|
|
98
|
+
│ ├── host/ # ★ DSH 宿主半:薄装配层
|
|
99
|
+
│ │ ├── index.js # name / inject / apply(唯一 composition 入口)
|
|
100
|
+
│ │ ├── http/routes.js # webServer 路由 → 用例 → JSON
|
|
101
|
+
│ │ ├── tools/register.js # ctx.tools.register 模型工具
|
|
102
|
+
│ │ ├── infra/fs-snapshot-repo.js
|
|
103
|
+
│ │ ├── infra/fs-watchlist-repo.js
|
|
104
|
+
│ │ └── ai/dsh-llm-gateway.js # ctx.llm 适配器
|
|
105
|
+
│ └── client/ # ★ 浏览器半:自包含,手写,无打包器
|
|
106
|
+
│ ├── index.js # 导出 apply/inject + 纯辅助函数(可被测试 shim 加载)
|
|
107
|
+
│ ├── api.js # fetch 封装(唯一传输层)
|
|
108
|
+
│ ├── components/{Panel,MetricCard,NoteworthyCard,Chart,RangeTabs,AiPanel,QaBox,AddIndicator}.js
|
|
109
|
+
│ └── format.js # 数值/单位/日期格式化(纯函数,可单测)
|
|
110
|
+
├── lib/
|
|
111
|
+
│ └── client.js # 由 scripts/build-client.mjs 生成,格式见 09 文档
|
|
112
|
+
└── test/
|
|
113
|
+
├── core/… # 纯逻辑
|
|
114
|
+
├── sources/… # 适配器(fixtures 回放)
|
|
115
|
+
├── contract/… # 端口契约(同一套测试跑所有适配器)
|
|
116
|
+
├── app/… # 用例(内存端口)
|
|
117
|
+
├── host/… # 装配冒烟(假服务)
|
|
118
|
+
└── fixtures/… # 录制样本
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 3. 端口契约(这是「低耦合」的落点)
|
|
124
|
+
|
|
125
|
+
### 3.1 `SourceAdapter`(数据源)
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
/** @typedef {Object} SourceAdapter
|
|
129
|
+
* @property {string} id // 'fred' | 'eastmoney-quote' | ...
|
|
130
|
+
* @property {string} label // 'FRED'(UI 徽章用)
|
|
131
|
+
* @property {SourceCapability[]} capabilities
|
|
132
|
+
* @property {(req: FetchSeriesRequest, deps: {fetch, clock, signal}) => Promise<RawSeries>} fetchSeries
|
|
133
|
+
* @property {(id: string) => { url: string, seriesRef: string }} sourceRef // 溯源 URL
|
|
134
|
+
*/
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
约束(会被契约测试强制):
|
|
138
|
+
|
|
139
|
+
1. **纯函数式**:`fetchSeries` 不读全局、不写文件、不改自身状态;依赖从 `deps` 注入。
|
|
140
|
+
2. **失败要分类**:抛出 `SourceError{ kind: 'network'|'http'|'parse'|'empty'|'unsupported', sourceId, detail }`。
|
|
141
|
+
3. **必须能离线回放**:任何适配器都要能用一个 fixture(原始响应体)跑通,测试不打网。
|
|
142
|
+
4. **输出必须归一化**:只返回 `RawSeries`,不允许返回上游私有结构。
|
|
143
|
+
|
|
144
|
+
### 3.2 其他端口
|
|
145
|
+
|
|
146
|
+
| 端口 | 方法 | 适配器 |
|
|
147
|
+
| --- | --- | --- |
|
|
148
|
+
| `SnapshotRepository` | `read(key)`, `write(key, payload, {ttlMs})`, `list()` | `fs` 版 / 内存版(测试) |
|
|
149
|
+
| `WatchlistRepository` | `list()`, `add(item)`, `remove(id)`, `update(id, patch)` | `fs` JSON 版 / 内存版 |
|
|
150
|
+
| `AiGateway` | `explain(ctx)`, `summarize(ctx)`, `answer(ctx)`, `propose(ctx)` → `AsyncIterable<string>` 或 `Promise<Result>` | `ctx.llm` 版 / Agent 中继版 / Stub 版(离线确定性) |
|
|
151
|
+
| `Clock` | `now()`, `today()` | 系统版 / 固定版(测试) |
|
|
152
|
+
|
|
153
|
+
**关键约定**:`AiGateway` 的 Stub 适配器**不是 mock,是产品的一部分**——无 LLM 时面板必须仍可用,
|
|
154
|
+
所以 Stub 要产出真实可读的确定性摘要(由 `core/insight/digest.js` 渲染),并标注 `mode: 'deterministic'`。
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 4. 数据流
|
|
159
|
+
|
|
160
|
+
### 4.1 全景
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
┌────────────────────────── 浏览器半 (client) ─────────────────────────┐
|
|
164
|
+
用户操作 ─────────▶│ Panel / MetricCard / Chart(SVG) / AiPanel / QaBox / AddIndicator │
|
|
165
|
+
└───────────────┬──────────────────────────────────────────────────────┘
|
|
166
|
+
│ fetch('/api/show-me-data/…') ← 唯一传输层 api.js
|
|
167
|
+
┌───────────────▼──────────────────────────────────────────────────────┐
|
|
168
|
+
│ 宿主半 (host) http/routes.js → 校验入参 → 调 app/ 用例 │
|
|
169
|
+
│ tools/register.js → 模型工具(同一批用例) │
|
|
170
|
+
└───────────────┬──────────────────────────────────────────────────────┘
|
|
171
|
+
│
|
|
172
|
+
┌───────────────▼──────────────────────────────────────────────────────┐
|
|
173
|
+
│ app/ 用例层:overview · series-view · watchlist · ai-* · refresh │
|
|
174
|
+
│ (缓存编排、TTL、并发去重、降级、把数据整理成 core 需要的形状) │
|
|
175
|
+
└──────┬─────────────────────────────────┬─────────────────────────────┘
|
|
176
|
+
│ │
|
|
177
|
+
┌────────────────▼──────────┐ ┌─────────────▼───────────────────────────┐
|
|
178
|
+
│ ports: SnapshotRepository │ │ core: stats → derive → insight → chart │
|
|
179
|
+
│ WatchlistRepository│ │ (纯函数,无 IO,可 100% 单测) │
|
|
180
|
+
│ AiGateway, Clock │ └─────────────────────────────────────────┘
|
|
181
|
+
└────────────────┬──────────┘
|
|
182
|
+
│
|
|
183
|
+
┌────────────────▼──────────────────────────────────────────────────────────────┐
|
|
184
|
+
│ sources/ 适配器们 → RawSeries (真网只在宿主半发生,测试走 fixtures) │
|
|
185
|
+
└───────────────────────────────────────────────────────────────────────────────┘
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 4.2 缓存与刷新策略(`app/refresh.js`,必须有单测)
|
|
189
|
+
|
|
190
|
+
- key = `sourceId + seriesRef + range`;TTL 按频率:日频 15 分钟、月频 6 小时、季频 24 小时。
|
|
191
|
+
- 同一 key 的并发请求**合并为一次上游调用**(in-flight promise 去重)。
|
|
192
|
+
- 上游失败时:**返回上次快照**并把 `status='stale'` + `lastSuccessAt` 一并返回;无快照才 `status='error'`。
|
|
193
|
+
- 卡片之间互不阻塞:`overview` 用 `Promise.allSettled` 并逐项降级。
|
|
194
|
+
|
|
195
|
+
### 4.3 浏览器半的共享策略与测试 shim
|
|
196
|
+
|
|
197
|
+
浏览器半**不能 `require` 本包的宿主代码**(一个包在模块图里只有一个节点)。而图表几何、
|
|
198
|
+
数值格式化、哈希指纹这类纯逻辑我们希望只写一份。做法:
|
|
199
|
+
|
|
200
|
+
1. 源码只有一份。构建脚本按**固定顺序**拼接两类来源:
|
|
201
|
+
① `src/core/chart/**` 与 `src/core/format.js`(纯 ESM、不依赖 DSH、不碰 IO)——
|
|
202
|
+
于是**图表几何全世界只有一份实现,且只被测试一次**;
|
|
203
|
+
② `src/client/**`(组件与传输层)。
|
|
204
|
+
2. `scripts/build-client.mjs`(零依赖、约 80 行)按固定顺序拼接、剥掉 `import`/`export` 关键字、
|
|
205
|
+
把内部模块降级为具名函数与常量(拼接前做**符号唯一性检查**,重名直接构建失败)、
|
|
206
|
+
包成 `window.__ModuleLoader__.load({ id, factory: (require) => { … } })` 写进 `lib/client.js`。
|
|
207
|
+
3. **该构建脚本自身可单测**:给定样例源码,断言产物能被 `new Function()` 解析、
|
|
208
|
+
含有且仅含一个 `__ModuleLoader__.load`、`id` 正确、没有残留 `import` 语句。
|
|
209
|
+
4. **测试 shim**(`test/helpers/client-shim.js`):在 Node 里
|
|
210
|
+
`globalThis.window = { __ModuleLoader__: { load: (m) => (captured = m) } }`,
|
|
211
|
+
然后 `require`/`import` 生成的 `lib/client.js`,取出 `captured.factory`,
|
|
212
|
+
用一个假的 `require`(返回 React 的 stub)执行,从而**直接单测浏览器半的纯函数与组件树形状**。
|
|
213
|
+
|
|
214
|
+
于是「图能测」「格式化能测」「面板在缺数据时不炸」都能进 CI,不需要浏览器。
|
|
215
|
+
真正的视觉与交互仍由人工在 GUI 里验收(M10)。
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 5. 扩展点(承诺给用户的两个「加东西」动作)
|
|
220
|
+
|
|
221
|
+
### 5.1 加一个**指标**(0 行逻辑代码)
|
|
222
|
+
|
|
223
|
+
在 `src/core/indicators/catalog.js` 追加一条:
|
|
224
|
+
|
|
225
|
+
```js
|
|
226
|
+
{
|
|
227
|
+
id: 'us.payrolls', group: 'US', label: { zh: '美国非农就业', en: 'US Nonfarm Payrolls' },
|
|
228
|
+
unit: '万人', freq: 'monthly', seasonal: 'SA', importance: 5,
|
|
229
|
+
source: { adapter: 'fred', seriesRef: 'PAYEMS' },
|
|
230
|
+
display: { transform: 'diff', window: 12 }, // 原始水平值 → 显示月度变化
|
|
231
|
+
notes: { zh: '就业市场最重要的月度指标,同时也修正前两月。' },
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
→ 自动获得:卡片、图表、环比/同比、规则命中、AI 上下文、对话可查。**无需改任何逻辑文件。**
|
|
236
|
+
|
|
237
|
+
### 5.2 加一个**数据源**(1 个文件 + 1 行注册 + 1 个 fixture)
|
|
238
|
+
|
|
239
|
+
```js
|
|
240
|
+
// src/sources/example.js
|
|
241
|
+
export const id = 'example'
|
|
242
|
+
export const label = 'Example Source'
|
|
243
|
+
export const capabilities = [{ kinds: ['macro'], frequencies: ['monthly'] }]
|
|
244
|
+
export function sourceRef(seriesRef) { return { url: `https://example.com/${seriesRef}`, seriesRef } }
|
|
245
|
+
export async function fetchSeries(req, deps) { /* → RawSeries */ }
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
```js
|
|
249
|
+
// src/sources/registry.js
|
|
250
|
+
import * as example from './example.js'
|
|
251
|
+
const ADAPTERS = [fred, eastmoneyQuote, eastmoneyMacro, usTreasury, worldbank, ecb, example]
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
→ 契约测试套件 `test/contract/source-adapter.test.js` **自动把新适配器纳入**(遍历注册表,
|
|
255
|
+
对每个适配器跑同一组断言:归一化、错误分类、sourceRef 可溯源、fixture 回放)。
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## 6. 运行时校准(M0 的第一件事,必须做)
|
|
260
|
+
|
|
261
|
+
本文档基于对**当前部署**的静态阅读与网络实测。以下项目**必须在创造模式里用 `cordis_inspect` 现场核对**,
|
|
262
|
+
并把结果写进 `docs/12-runtime-verified.md`(假设 → 实测 → 结论):
|
|
263
|
+
|
|
264
|
+
| # | 待核对 | 用什么 | 若与假设不符怎么办 |
|
|
265
|
+
| --- | --- | --- | --- |
|
|
266
|
+
| 1 | 浮层槽位真实 key/协议:候选 `shell.overlay`,确认 `kind`(`list`/`single`)、注册选项(`id`/`order`/`key`)、props | `cordis_inspect what:"client"` + 精确 query 该 slot | 退回 `settings.section` + `conversation.chat.turnTail` 双入口,面板改为设置页内 |
|
|
267
|
+
| 2 | `webServer.register` 的真实签名(`path`/`kind:'exact'|'prefix'`/`handler` 形态、响应写法) | `cordis_inspect what:"api" name:"webServer"` | 若不可注册路由,改用 Typert Remote 或让浏览器半直连源(仅 CORS 友好源) |
|
|
268
|
+
| 3 | `tools.register` 的 `output` 声明要求与渲染钩子(`render`/`presentationMeta`) | `cordis_inspect what:"api" name:"tools"` + `Tool.listTools` | 简化输出为 `text` + `json` 两种 |
|
|
269
|
+
| 4 | `ctx.llm.stream` 与 `agentDefaultModel.currentSelection()` 的准确用法、是否需要 `inject` | `cordis_inspect what:"api" name:"llm"` | AI 层退化为 Stub 模式 + 走 Agent 中继(对话式问答) |
|
|
270
|
+
| 5 | `settings.section` 注册协议与持久化读写路径 | `cordis_inspect what:"client"` + `what:"api" name:"settings"` | 设置改存自有文件,页面内自渲染 |
|
|
271
|
+
| 6 | 主题令牌名(颜色/间距) | `Theme.listTokens` | 用 CSS 变量回退值,不硬编码产品色 |
|
|
272
|
+
| 7 | `dsh.client.inject` 需要声明哪些客户端包 | 对照 `dsh-client-ui-jobs/package.json` | 以实际加载报错为准,逐个补齐 |
|
|
273
|
+
|
|
274
|
+
**纪律**:`cordis_inspect` 的结果是**事实**,本文档相应段落是**假设**;冲突时以运行时为准,并在
|
|
275
|
+
`12-runtime-verified.md` 记录差异与后续调整(包括需要改哪些测试)。
|