@v587d/capital-generation 2.8.0 → 2.8.1

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/CHANGELOG.md CHANGED
@@ -7,6 +7,44 @@
7
7
  - **第三位(patch)**:不破坏既有工具 / 配置 / 会话的新增与修复(如 2.1.1、2.1.2)。
8
8
  - **第二位(minor)**:有需要用户知晓的行为变更,且段内附迁移说明(如 2.1.0 的图表呈现通道重做、2.2.0 的数据源与检索来源扩容、2.4.0 的**必须换宿主版本**——本插件自己的工具与数据契约没变,所以只抬第二位)。
9
9
 
10
+ ## [2.8.1] - 2026-10-16
11
+
12
+ 接入第二家结构化数据上游 **FTShare(非凸科技)**,数据收集能力 **94 → 118** 项(新增 24 项)。宿主要求与
13
+ 2.8.0 相同(DSH`@0.2.0-rc.2`),**不必换宿主版本**。
14
+
15
+ ### Added
16
+
17
+ - **A 股主力资金流**:个股、行业板块、概念板块的日度资金流(近 45 个交易日,亿元),外加个股主力五档的
18
+ 15 分钟切片(元)。这一块此前只有一家、而那一家的同类接口明确不对外部开放,问得到、取不到。
19
+ - **盘面截面四颗**:沪深北全市场实时行情(含 ST 与停牌)、任一交易日的全市场日 K(含北交所)、
20
+ 全市场成交额与涨跌家数分布(九档)、单只标的的逐日昨收价序列(用来校验复权与涨跌停)。
21
+ - **五处真正的空白**:每日涨跌停价与昨收(含北证——此前只有腾讯的当日快照,而它明确不接受北证代码)、
22
+ 龙虎榜前五买入/卖出席位(此前只到个股汇总,没有席位明细)、股东人数**历史**(此前只有最新一期截面)、
23
+ 中证系列指数成分**权重数值**(此前只有成分名单)、可转债历史 K 线(此前只有发行条款,没有价格序列)。
24
+ - **业绩与行业**:大宗交易明细(一笔一行)、A 股业绩预告与业绩快报(按报告期或单票,含北证与更正行)、
25
+ 申万行业估值快照与单行业日度量价序列、交易日历(沪北深与港股)。
26
+ - **宏观利率长尾五颗**:LPR 全历史、中债收益率曲线(国债 / AAA 票据,八档期限)、SHIBOR、HIBOR、
27
+ 全球财经日历(含公布时刻与前值)。
28
+ - 每一项都按上游真实返回逐列核验后才登记:单位、哪些列可以为空、时间与分页口径写进该项的说明里;
29
+ 上游文档与实际不符的地方(例如某项只滚动保留最近一个交易日、某项标称的上游自己并不遵守)以实测为准,
30
+ 说明里点名,取数失败时也会点名是哪一处、并给出该怎么改。
31
+
32
+ ### Changed
33
+
34
+ - **一次任务里要看多个数据能力时,等待变短**:能力目录与单项契约的读取都是纯读、无副作用,现在允许
35
+ 在同一条消息里一次发出多个,由框架并发执行、结果一起返回,不再一份一份串行等。取数本身仍然一次一条
36
+ ——上游限流按这个节奏设计,没有改。
37
+
38
+ ### 迁移说明
39
+
40
+ - 要用上 FTShare 这 24 项,需要向服务方申请免费档密钥,并写入 `~/.dsh/.credentials.yaml`,名称
41
+ `FTSHARE_API_KEY`。**这一把目前不在「插件」→ Capital Generation 的「数据源」卡片上**(卡片仍是四格),
42
+ 下一版会补输入框。没配这把密钥时,这 24 项仍在能力目录里,但每次取数都会点名 `FTSHARE_API_KEY` 失败;
43
+ 其余来源与功能照常。
44
+ - 不需要任何其它动作:已有的密钥、卡片设置、精选 Skills 勾选、自选股清单与持仓标记全部照旧读得到。
45
+ - 覆盖范围见 [数据收集能力表](docs/data-collector-capabilities.md)。**尚未接入**:期货、期权与贵金属
46
+ (同一家的免费档里有,排在下一批)。
47
+
10
48
  ## [2.8.0] - 2026-10-15
11
49
 
12
50
  主 Agent 的工具面按用户决策调整为「**开 read、同时保留 bash**」,三个平台的口径终于一致。宿主要求与
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
  <p align="center">
16
16
  <a href="https://awesome-dsh-plugin.com"><img src="https://awesome-dsh-plugin.com/badge.svg" alt="awesome · DSH plugin"></a>
17
17
  <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="License"></a>
18
- <a href="https://github.com/v587d/capital-generation/releases"><img src="https://img.shields.io/badge/version-2.8.0-9cf" alt="Version"></a>
18
+ <a href="https://github.com/v587d/capital-generation/releases"><img src="https://img.shields.io/badge/version-2.8.1-9cf" alt="Version"></a>
19
19
  <a href="https://www.npmjs.com/package/@v587d/capital-generation"><img src="https://img.shields.io/npm/v/%40v587d%2Fcapital-generation" alt="npm version"></a>
20
20
  </p>
21
21
 
@@ -72,10 +72,12 @@ https://github.com/v587d/capital-generation
72
72
  | [AnySearch](https://www.anysearch.com/docs) | 网页搜索和正文提取 |
73
73
  | [Wind Alice](https://market.windalice.com/#/home) | 公告与新闻检索,以及宏观/行业/汇率指标与按日期区间取的历史 K 线 |
74
74
  | [PaddleOCR AIStudio](https://aistudio.baidu.com/paddleocr) | 解析 PDF 和图片中的文字 |
75
+ | [FTShare(非凸科技)](https://market.ft.tech/gateway/doc/p/pb8eizu3) | A 股行情与日 K 截面、主力资金流、涨跌停、龙虎榜席位、股东人数、指数权重、可转债行情与宏观利率等第二家上游口径 |
75
76
 
76
77
  这些都是第三方服务的密钥,需分别向服务方申请;额度和条款以各服务方为准。
77
78
  保存后**新建 Capital 模式会话**即可生效,无需重启。
78
- 也可以将密钥写入 `~/.dsh/.credentials.yaml`,名称依次为 `FUYAO_API_KEY`、`ANYSEARCH_API_KEY`、`WIND_API_KEY`、`PADDLE_OCR_TOKEN`。
79
+ 也可以将密钥写入 `~/.dsh/.credentials.yaml`,名称依次为 `FUYAO_API_KEY`、`ANYSEARCH_API_KEY`、`WIND_API_KEY`、`PADDLE_OCR_TOKEN`、`FTSHARE_API_KEY`。
80
+ 其中 **FTShare 这一把暂时只能走这份文件**:卡片上那一格还没给它留输入框,没配时目录里这 24 项仍在,但每次取数都会点名 `FTSHARE_API_KEY` 失败。
79
81
  密钥由 DSH 凭据服务保存,不会显示在会话配置中。
80
82
 
81
83
  <p align="center">
@@ -130,7 +132,7 @@ https://github.com/v587d/capital-generation
130
132
 
131
133
  ## 还能做什么
132
134
 
133
- - **查数据和资料:** 覆盖A股、港股、美股,由 Agents 按需获取行情、财务、指数、基金与宏观行业数据,检索公告、新闻和研报。共计 **94** 个数据收集能力和 **14** 个网页检索能力,部分公开数据源无需额外密钥;具体覆盖范围见 [数据收集能力表](docs/data-collector-capabilities.md) 和 [网页检索能力表](docs/web-retriever-capabilities.md)。
135
+ - **查数据和资料:** 覆盖A股、港股、美股,由 Agents 按需获取行情、财务、指数、基金与宏观行业数据,检索公告、新闻和研报。共计 **118** 个数据收集能力和 **14** 个网页检索能力,部分公开数据源无需额外密钥;具体覆盖范围见 [数据收集能力表](docs/data-collector-capabilities.md) 和 [网页检索能力表](docs/web-retriever-capabilities.md)。
134
136
  - **数据分析:** 所有 Agents 均不直接接触原始结构数据,Agents 按需读取数据、校验数据,并写脚本挖掘数据背后含义。
135
137
  - **可视化:** Agent 视情况将校验后数据交给可视化专家生成可交互图表。图表会作为本轮交付物出现在会话中,可在侧栏查看或离线打开。了解处理流程可看 [Agent 角色说明](docs/agent-roles.md)。
136
138
 
@@ -166,13 +168,14 @@ https://github.com/v587d/capital-generation
166
168
  ## 常见问题
167
169
 
168
170
  - **安装后找不到 Capital 模式?** 确认插件出现在「插件」→ 已安装列表;Web / CLI profile 安装后要重启该 profile,并在新会话选择模式。
169
- - **配置在哪?** 打开「插件」→ 已安装的 **Capital Generation**,页面顶部就是「数据源 / 精选 Skills / 包含的组件」三格,四把密钥都在第一格;不在 DSH 的普通「设置」页面,也不用点进任何一行组件。保存密钥后新建会话再试。
171
+ - **配置在哪?** 打开「插件」→ 已安装的 **Capital Generation**,页面顶部就是「数据源 / 精选 Skills / 包含的组件」三格,密钥都在第一格;不在 DSH 的普通「设置」页面,也不用点进任何一行组件。保存密钥后新建会话再试。只有 **FTShare** 那一把目前不在卡片上,要写进 `~/.dsh/.credentials.yaml`(名称 `FTSHARE_API_KEY`)。
170
172
  - **升级到 2.7.11 后,卡片里的开关回到默认值了?** 这一版把插件内部两个组件换了目录名,宿主按旧路径记住的那份保存值因此接不上。四把 API Key、你的自选股清单与持仓标记都不受影响;「精选 Skills」的勾选、附加人设与「本机直连」开关需要重新设一次并保存,之后就稳定了。已经设过的话,旧的那份仍写在 DSH 的 profile 文件里,手改那一行的路径也能取回来。
171
- - **桌面端升级后仍是旧版?** 桌面端安装时会固定所解析的版本,不会自动升级。卸载后重新安装,再到插件详情查看版本号;如需指定版本,可在地址末尾加 `#v2.8.0` 或 `@2.8.0`。
173
+ - **桌面端升级后仍是旧版?** 桌面端安装时会固定所解析的版本,不会自动升级。卸载后重新安装,再到插件详情查看版本号;如需指定版本,可在地址末尾加 `#v2.8.1` 或 `@2.8.1`。
172
174
  - **刚发布的新版本装不到?** 桌面端取包时会跳过**刚发布不久**的版本(宿主侧包管理器的发布冷却策略,实测会静默装成上一个版本),国内镜像的同步也可能滞后一段时间。急着用最新代码,改用仓库地址安装最直接。
173
175
  - **网页抓取失败?** 配置卡片中的「允许启动本地提取网页内容」默认开启:AnySearch 提取失败时可尝试本机直连。关闭后,本机直连的具名来源查询也不可用;请检查该开关和网络连接。
174
176
  - **Wind 那部分会消耗积分?** 会。公告与新闻检索、宏观 / 行业 / 汇率指标、按日期区间取的历史 K 线都按次消耗你在 Wind 侧的额度;额度与条款以 Wind 为准,插件不做用量封顶。没配 `WIND_API_KEY` 时这几类不可用,其余功能照常。
175
- - **运行时主 Agent没有加载已打开的精选 Skill?** 由 LLM 判断是否值得加载精选 Skill, 如果你需要强制使用某一种技能,可通过 `/技能名称` 斜杠命令的形式手动加载已在设置中打开的 Skill。
177
+ - **运行时主 Agent 没有加载已打开的精选 Skill?** 由 LLM 判断是否值得加载精选 Skill,如果你需要强制使用某一种技能,可通过 `/技能名称` 斜杠命令的形式手动加载已在设置中打开的 Skill。
178
+ - **精选 Skill 的方法论很长,Agent 读得完吗?** 读得完。超过一次可读长度的正文已经重排成「入口 + 分册」:Agent 先看入口目录,需要具体规则、模板或阈值时把对应那一册整本读进来,并会告诉你它读了哪几册、哪些没读。设置卡片里的「看原文」仍然只给入口那一份,想看某一册直接问 Agent。
176
179
 
177
180
  目前 Windows 桌面端有一项[已知宿主差异](CHANGELOG.md):主 Agent 的部分工具限制可能不生效。请特别留意它实际调用了哪些工具;结构化数据的取数限制仍由工具自身执行。
178
181
 
@@ -195,6 +198,6 @@ npm run smoke:boot # 检查插件装配
195
198
 
196
199
  - 「精选 Skills」的研究、交易过程与财务方法 Skill 按固定 commit **快照收录**自 [finterm-ai/investment-skills](https://github.com/finterm-ai/investment-skills)、[spikeHongg/china-stock-research-skills](https://github.com/spikeHongg/china-stock-research-skills)、[yennanliu/InvestSkill](https://github.com/yennanliu/InvestSkill)、[DjNero11/peter-lynch-skill](https://github.com/DjNero11/peter-lynch-skill)、[marian2js/trading-skills](https://github.com/marian2js/trading-skills)、[GAJETOso/financeskills](https://github.com/GAJETOso/financeskills)、[Howard-Jerry/quant-agent-skills](https://github.com/Howard-Jerry/quant-agent-skills)(以上均 MIT License)与 [jwangkun/claude-for-financial-services-cn](https://github.com/jwangkun/claude-for-financial-services-cn)(Apache-2.0)。快照目录、收录口径与逐颗排除理由见 [selected-skills/](selected-skills/README.md),第三方组件与许可见下面的清单文件。
197
200
  - 会话里的可交互图表由 [TradingView Lightweight Charts](https://github.com/tradingview/lightweight-charts)(Apache-2.0)渲染,图上保留它自带的归属标识。
198
- - 行情、财务、公告与网页材料来自四类需自备密钥的服务(同花顺 Fuyao、Wind Alice、AnySearch、PaddleOCR)与一批公开口径的来源(东方财富、腾讯、新浪、财联社、华尔街见闻、巨潮互动易、上证 e互动、同花顺公开站点);数据版权归各服务方,额度与条款以服务方为准,本项目不重新分发原始数据。
201
+ - 行情、财务、公告与网页材料来自五类需自备密钥的服务(同花顺 Fuyao、Wind Alice、AnySearch、PaddleOCR、FTShare 非凸科技)与一批公开口径的来源(东方财富、腾讯、新浪、财联社、华尔街见闻、巨潮互动易、上证 e互动、同花顺公开站点);数据版权归各服务方,额度与条款以服务方为准,本项目不重新分发原始数据。
199
202
 
200
203
  第三方组件、版本与许可,以及宿主提供哪部分、构建期用什么、各服务的数据会出到哪些地方,逐条列在 [第三方组件与许可](THIRD_PARTY_NOTICES.md)。
@@ -127,7 +127,7 @@ roadmap 与贡献指南、站点体检记录、给别的宿主用的接线配置
127
127
 
128
128
  以下服务**不是代码依赖,也不受上表许可约束**;接口条款、额度与数据版权归各服务方所有。分两组列全:
129
129
 
130
- **需要使用者自备密钥的四项**(对应首页「数据源」那四格,密钥一律只进宿主的凭据域):
130
+ **需要使用者自备密钥的五项**(前四项对应首页「数据源」卡片里的那四格,密钥一律只进宿主的凭据域):
131
131
 
132
132
  - **同花顺 Fuyao** —— 结构化数据:行情、财务、基金与指数,以及自选股报价。
133
133
  - **Wind Alice** —— 三个能力面:公告与新闻等信披文档检索、宏观 / 行业 / 汇率等指标(EDB)、按日期区间取的
@@ -136,6 +136,11 @@ roadmap 与贡献指南、站点体检记录、给别的宿主用的接线配置
136
136
  - **PaddleOCR(百度 AIStudio)** —— PDF 与图片的文字解析。注意解析结果不是从作业接口直接返回的:
137
137
  本插件按服务方给的**预签名地址**到它的对象存储主机取回结果(与提交作业的不是同一台主机),
138
138
  取回后只落在使用者本机。
139
+ - **FTShare(非凸科技)** —— 结构化数据的第二家上游口径:A 股行情与日 K 截面、主力资金流(个股 / 行业 /
140
+ 概念)、涨跌停价、龙虎榜席位、股东人数、指数成分权重、可转债行情、大宗交易、业绩预告与快报、交易日历,
141
+ 以及 LPR / SHIBOR / HIBOR / 中债收益率曲线等利率长尾。免费档按次限流(服务方标 10 QPS / 突发 20),
142
+ 本插件用进程级节流链把节奏压在这条线内。**这一把密钥目前只能写进宿主的凭据文件**(名称
143
+ `FTSHARE_API_KEY`),配置卡片上还没给它留输入框。
139
144
 
140
145
  **公开口径的来源**(无需密钥,但同样受各服务方的条款与数据版权约束):东方财富(宏观与个股数据、公告与
141
146
  研报列表、7×24 快讯与新闻)、腾讯(公开行情与历史 K 线、代码检索)、新浪(研报列表)、财联社(电报)、
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@v587d/capital-charts",
3
- "version": "2.8.0",
3
+ "version": "2.8.1",
4
4
  "description": "Capital 模式的图表前端组件(浏览器半边 + 序列旁路路由):由 @v587d/capital-generation 随包携带并作为 host 平面行挂载。",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/config/index.js CHANGED
@@ -94,6 +94,7 @@ export const Config = z.object({
94
94
  customPersona: z.string().default('').volatile().description('Capital 模式附加人设文本'),
95
95
  selectedSkills: SelectedSkillsSchema.default(SELECTED_SKILL_DEFAULTS).description('Explicitly selected community experiments; default off and visible only to the Capital root Agent'),
96
96
  fuyaoCredentialRef: z.string().default('FUYAO_API_KEY').volatile().description('Fuyao credentials 引用名'),
97
+ ftshareCredentialRef: z.string().default('FTSHARE_API_KEY').volatile().description('FTShare(非凸科技)credentials 引用名'),
97
98
  retriever: z.object({
98
99
  baseURL: z.string().default('').volatile().description('AnySearch API 地址'),
99
100
  credentialRef: z.string().default('ANYSEARCH_API_KEY').volatile().description('AnySearch credentials 引用名'),
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@v587d/config",
3
- "version": "2.8.0",
3
+ "version": "2.8.1",
4
4
  "description": "Capital 模式的取数凭据与偏好配置行:卡片坐在 @v587d/capital-generation 插件主页的「配置」段,点这一行本身没有表单。",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -108,6 +108,11 @@ function tool(name, description, parameters, schema, execute, options = {}) {
108
108
  // 会被放进有界并发池(默认上限 10),结果仍按模型顺序在**同一个下一步**一起返回。
109
109
  // 这正是 describe_dataset 省往返的手段——多个 dataset 由模型一次发完,而不是把 N 份结果
110
110
  // 合并成一个超长载荷(见 docs/design/describe-dataset-design.md §2.1)。
111
+ // 宿主侧是 **fail-closed**,两版源码同为「没有这个函数就 exclusive」;2026-10-16 用一次性
112
+ // headless 探针在真机核过(账本 L30):两颗除声明与否外逐字相同的工具,三颗**已声明**在
113
+ // 1 毫秒内一起派发、整批 706 毫秒;三颗**未声明**严格 call→result 交替、整批 1426 毫秒。
114
+ // ⚠ 混发会把已声明的那颗拖回串行(池在遇到第一颗未声明调用处断开),所以"要不要标"是
115
+ // 按**工具**定的、不是按**这一次批次**定的:见下面 query_dataset 那条刻意不标的理由。
111
116
  ...(options.concurrencySafe === true ? { isConcurrencySafe: () => true } : {}),
112
117
  };
113
118
  }
@@ -363,6 +368,10 @@ export function registerDatasetTools(ctx, store) {
363
368
  primary_key: typeof args.primary_key === 'string' ? args.primary_key : undefined,
364
369
  signal: exec.signal,
365
370
  })),
371
+ // 刻意**不**标并发安全(与 describe_dataset 相反,且这是量出来的):真会话里 query_dataset
372
+ // 单颗中位 17 毫秒(25 次样本),一次消息发 6 颗串行也只多付约 85 毫秒;而它的成本主体是
373
+ // 读 raw.json 后的 `JSON.parse` + 聚合,Node 单线程下这部分**并发也不会重叠**,标了买不到什么。
374
+ // 多份数据 / 多条查询的诉求已经由 describe_dataset 的 `queries` 承接(那颗标了)。
366
375
  tool('query_dataset', `对当前 session 已授权的 json_rows Dataset 执行固定 QuerySpec:参数是一个 JSON 对象,QuerySpec 字段既可平铺在根上(flat),也可整体放进 query(query_request envelope,宿主自动展开);两种形态同时给出时以 query 为准。支持受限 filter、group_by、count/min/max/avg/sum、asc/desc 和 limit。必须返回聚合或分组结果,禁止原始行投影;select 可省略,默认返回分组列和聚合别名。默认 error_policy=skip_with_warning,脏值会被排除并在 warnings 披露;需要全量类型一致性时显式使用 strict。**时间筛选直接写日期**:时间列(如 date_ms)的 filter 值可写 "2026-08-23" / "2026-08" / "2026" 或 {period:"last_3_months"}(period 省略锚点时以该 Dataset 最后一天为准),宿主按该列自身的时区偏移换算成边界,不要自己算毫秒;日期写法配 >、<、!= 无意义会报 query_type_conflict(用 >= / <= / =)。结果里时间列会附 *_iso 可读日期。最多 ${MAX_QUERY_FILTERS} 个条件、${MAX_QUERY_GROUP_BY} 个分组列、${MAX_QUERY_AGGREGATES} 个聚合、${MAX_QUERY_LIMIT} 行,结果最多 ${MAX_QUERY_OUTPUT_BYTES} 字节。数值字符串只在 Dataset schema 明确为 number/integer 时兼容。query_dataset 是受控分组/聚合工具,不是通用 raw.json 读取或自定义分析工具:select 只能引用 group_by 列或聚合别名,取不到原始行(首末/最新值请用 profile 的 time_facts)。select/group_by/aggregates/order_by 请传真正的 JSON 数组(宿主也兼容 JSON 字符串,但不要依赖)。若还要 profile/元数据,可用 describe_dataset 的 queries 参数一次拿到;本工具用于单点补查。`, queryParametersSchema, queryResultSchema, async (args, exec) => {
367
376
  const normalized = normalizeQueryArgs(args);
368
377
  const session = delegatedSession(exec, 'query_dataset');
@@ -47,8 +47,20 @@ function render(_args, value) {
47
47
  // ToolOutputDefinition.render(args, value):第一参是入参、第二参才是返回值。
48
48
  return [{ type: 'text', text: JSON.stringify(value) }];
49
49
  }
50
- function tool(name, description, parameters, schema, execute) {
51
- return { name, description, parameters, output: { schema, render }, execute };
50
+ function tool(name, description, parameters, schema, execute, options = {}) {
51
+ return {
52
+ name,
53
+ description,
54
+ parameters,
55
+ output: { schema, render },
56
+ execute,
57
+ // 与 `dataset-tools.ts` 的 `describe_dataset` 同一机制:框架把同一条消息里的多个调用
58
+ // 放进有界并发池,N 份结果在同一个下一步一起返回(宿主 fail-closed:不标就串行,2026-10-16
59
+ // 一次性 headless 探针核过,账本 L30)。⛔ 只给**纯读**的发现面开:
60
+ // `request_data` 刻意不标——Hub 的执行器是 FIFO 串行(`hub.ts` 的 Phase 1 注释),
61
+ // 让模型并行发取数等于把上游节流与 in-flight 合并放在我们没承诺过的位置上。
62
+ ...(options.concurrencySafe === true ? { isConcurrencySafe: () => true } : {}),
63
+ };
52
64
  }
53
65
  /**
54
66
  * 调用方 Agent session(官方注入,不可由模型伪造)。请求归属恒为当前调用者,
@@ -151,8 +163,8 @@ export function registerDataCollectorTools(ctx, hub, diagnostics) {
151
163
  throw requestParamsError(error, requestedCapability) ?? error;
152
164
  }
153
165
  }),
154
- tool('list_capabilities', '列出当前已注册的数据能力目录:**纯文本,一行一条** `capability|summary|paginated`,paginated 为 1(分页)/ 0(不分页)。名字或摘要里的 `|`、换行会转义成 `\\|`、`\\n`,所以一行就是一条能力,不要按冒号或空格拆分。目录是发现入口,**一个任务只需调用一次**:同一 session 的历史里已经保有这份目录,重复调用只会白占上下文。目录刻意不含参数与输出结构(完整 schema 约 23KB,会被剪枝截断);确定要用的能力后,用 describe_capability 单独取那一个能力的 input_schema 后即可 request_data。request_data 的 capability 必须以本目录为准,不要自行编造。', jsonObject(), { type: 'string' }, async (_args, exec) => { delegatedSession(exec, 'list_capabilities'); return hub.capabilityDirectory(); }),
155
- tool('describe_capability', '读取**一个**能力的完整契约:description(单位、null 语义、时间与分页口径)、input_schema(params 的取值与约束)、output_fields(返回字段的逐行字典 `路径:类型[:说明]`,类型后缀 `?` 为可为 null、`[].` 为数组元素的字段)与 paginated。request_data 的 params 必须按这里返回的 input_schema 填写;request_data 若返回 request_params_invalid,应回到这里重新读取后再重试。一个能力描述一次即可:结果已在本 session 历史里,重复描述同一个能力只会白占上下文;需要几个能力就分别调用几次,不要用逗号把多个名字塞进一次调用。错误按 code 引导:capability_required(没给名字,先 list_capabilities())、capability_invalid(不是字符串/超长/含逗号,改为目录中的单个名字)、capability_catalog_empty(数据源未注册,别重试,回告主 Agent)、capability_unknown(名字不存在,错误里会列出全部可用能力名并要求原样复制)。', jsonObject({ capability: { type: 'string' } }, ['capability']), jsonObject({
166
+ tool('list_capabilities', '列出当前已注册的数据能力目录:**纯文本,一行一条** `capability|summary|paginated`,paginated 为 1(分页)/ 0(不分页)。名字或摘要里的 `|`、换行会转义成 `\\|`、`\\n`,所以一行就是一条能力,不要按冒号或空格拆分。目录是发现入口,**一个任务只需调用一次**:同一 session 的历史里已经保有这份目录,重复调用只会白占上下文。目录刻意不含参数与输出结构(完整 schema 约 23KB,会被剪枝截断);确定要用的能力后,用 describe_capability 单独取那一个能力的 input_schema 后即可 request_data。request_data 的 capability 必须以本目录为准,不要自行编造。', jsonObject(), { type: 'string' }, async (_args, exec) => { delegatedSession(exec, 'list_capabilities'); return hub.capabilityDirectory(); }, { concurrencySafe: true }),
167
+ tool('describe_capability', '读取**一个**能力的完整契约:description(单位、null 语义、时间与分页口径)、input_schema(params 的取值与约束)、output_fields(返回字段的逐行字典 `路径:类型[:说明]`,类型后缀 `?` 为可为 null、`[].` 为数组元素的字段)与 paginated。request_data 的 params 必须按这里返回的 input_schema 填写;request_data 若返回 request_params_invalid,应回到这里重新读取后再重试。一个能力描述一次即可:结果已在本 session 历史里,重复描述同一个能力只会白占上下文;需要几个能力就**在同一条消息里一次发出多个调用**(本工具是纯读、可并发执行,框架会把它们放进并发池、在同一个下一步一起返回),但一次仍只传**一个**名字,不要用逗号把多个名字塞进一条调用。错误按 code 引导:capability_required(没给名字,先 list_capabilities())、capability_invalid(不是字符串/超长/含逗号,改为目录中的单个名字)、capability_catalog_empty(数据源未注册,别重试,回告主 Agent)、capability_unknown(名字不存在,错误里会列出全部可用能力名并要求原样复制)。', jsonObject({ capability: { type: 'string' } }, ['capability']), jsonObject({
156
168
  capability: { type: 'string' },
157
169
  summary: { type: 'string' },
158
170
  paginated: { type: 'boolean' },
@@ -166,7 +178,7 @@ export function registerDataCollectorTools(ctx, hub, diagnostics) {
166
178
  if (!detail)
167
179
  throw capabilityError('capability_unknown', `未注册的能力 "${capability}"。`);
168
180
  return detail;
169
- }),
181
+ }, { concurrencySafe: true }),
170
182
  ...(diagnostics ? [
171
183
  tool('dc_status', '诊断工具:返回数据管道运行状态 —— at/call 为本次真实执行的时间戳与计数(防伪)、api_key 为**本次调用现取**的凭据解析结果(present/source,不含密钥值)、registered_capabilities 为当前已注册 capability 列表、root_tool_policy 为根 Agent 工具收敛的现场记录(preset_id / outcome / denied / failed,排查"主 Agent 为什么看得见本该收敛掉的工具"用)。数据源注册不依赖 key:api_key.present=false 时同花顺能力仍在目录里,但每次取数会点名 FUYAO_API_KEY 失败。排障时优先调用。', jsonObject(), jsonObject({
172
184
  at: { type: 'integer' },
package/lib/index.d.ts CHANGED
@@ -86,6 +86,8 @@ export interface Config {
86
86
  selectedSkills?: SelectedSkillSelection;
87
87
  /** Fuyao credentials 引用名;空值回退到 FUYAO_API_KEY。 */
88
88
  fuyaoCredentialRef?: string;
89
+ /** FTShare(非凸科技)credentials 引用名;空值回退到 FTSHARE_API_KEY。 */
90
+ ftshareCredentialRef?: string;
89
91
  /** web_retriever 配置(可选;缺省使用 AnySearch 默认地址与凭据名)。 */
90
92
  retriever?: RetrieverConfig;
91
93
  }
@@ -98,6 +100,7 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
98
100
  [k: string]: z<boolean, boolean, "defined">;
99
101
  }>>, "defined">;
100
102
  fuyaoCredentialRef: z<string, string, "defined">;
103
+ ftshareCredentialRef: z<string, string, "defined">;
101
104
  retriever: z<Schemastery.ObjectS<NoInfer<{
102
105
  baseURL: z<string, string, "defined">;
103
106
  credentialRef: z<string, string, "defined">;
@@ -183,6 +186,7 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
183
186
  [k: string]: z<boolean, boolean, "defined">;
184
187
  }>>, "defined">;
185
188
  fuyaoCredentialRef: z<string, string, "defined">;
189
+ ftshareCredentialRef: z<string, string, "defined">;
186
190
  retriever: z<Schemastery.ObjectS<NoInfer<{
187
191
  baseURL: z<string, string, "defined">;
188
192
  credentialRef: z<string, string, "defined">;
package/lib/index.js CHANGED
@@ -4,6 +4,7 @@ import { WorkspaceDatasetStore } from './data-collector/store.js';
4
4
  import { resolveFuyaoApiKey, createFuyaoRestSources } from './sources/fuyao-rest.js';
5
5
  import { createTencentSources } from './sources/tencent-http.js';
6
6
  import { createEastmoneySources } from './sources/eastmoney-http.js';
7
+ import { createFtshareSources, resolveFtshareApiKey } from './sources/ftshare-rest.js';
7
8
  import { createWindSources } from './sources/wind-mcp.js';
8
9
  import { registerDataCollectorTools } from './data-collector/tools.js';
9
10
  import { registerDatasetTools } from './data-collector/dataset-tools.js';
@@ -147,6 +148,9 @@ export const Config = z.object({
147
148
  fuyaoCredentialRef: z.string()
148
149
  .default('FUYAO_API_KEY')
149
150
  .description('Fuyao credentials 引用名,空 = FUYAO_API_KEY'),
151
+ ftshareCredentialRef: z.string()
152
+ .default('FTSHARE_API_KEY')
153
+ .description('FTShare(非凸科技)credentials 引用名,空 = FTSHARE_API_KEY'),
150
154
  retriever: RetrieverSchema.default({ baseURL: '', credentialRef: 'ANYSEARCH_API_KEY', windDocs: { endpoint: '', credentialRef: 'WIND_API_KEY', timeoutMs: 60000 }, localFetch: { ...LOCAL_FETCH_DEFAULTS }, paddleOcr: { ...PADDLE_OCR_DEFAULTS } })
151
155
  .description('web_retriever 配置(可选;缺省使用 AnySearch 默认地址与凭据名)'),
152
156
  });
@@ -404,6 +408,15 @@ export function apply(ctx, config) {
404
408
  return () => { for (const dispose of disposers)
405
409
  dispose(); };
406
410
  }, 'capital-generation.fuyao-sources()');
411
+ // FTShare(非凸科技)——同花顺资金流那一族的第二家上游。注册同样只看结构,key 每次调用现取(§9.7);
412
+ // 免费档限流 10 QPS / burst 20 由 src/sources/ftshare-rest.ts 的进程级节流链兜住。
413
+ ctx.effect(() => {
414
+ const sources = createFtshareSources(() => resolveFtshareApiKey(ctx, effectiveConfig.ftshareCredentialRef || 'FTSHARE_API_KEY'));
415
+ const disposers = sources.map((dataSource) => hub.registerSource(dataSource));
416
+ ctx.logger.info(`capital-generation: 已注册 ${disposers.length} 个 FTShare 数据源(ftshare_*,key 每次调用现取)`);
417
+ return () => { for (const dispose of disposers)
418
+ dispose(); };
419
+ }, 'capital-generation.ftshare-sources()');
407
420
  // ── web_retriever 域:anysearch(广度)+ wind_docs(public_document 精准)──────
408
421
  const retriever = effectiveConfig.retriever ?? {};
409
422
  const credentialRef = retriever.credentialRef || 'ANYSEARCH_API_KEY';
@@ -0,0 +1,54 @@
1
+ /**
2
+ * FTShare(非凸科技 `market.ft.tech/gateway`)REST source —— **资金流 + 行情/日 K 截面 + 快照与日历
3
+ * + 大宗与披露 + 申万行业**。
4
+ *
5
+ * 为什么接这一族(2026-10-15 逐条真报文实测):
6
+ * - 同花顺 `capital-flow/*` 对我们是 `code=2004` 永久关闭,东财 `eastmoney_main_capital_snapshot`
7
+ * 实测**只有查询日一股一行**——个股资金流的历史序列在本仓一直是空的。这一族给到最近约 45 个交易日
8
+ * (T-45 仍有 5199 行、T-50 起 total=0),主力五档更给到约 187 个交易日。
9
+ * - 板块(行业)资金滚动此前只有 `eastmoney_cashflow_rotation` 一条,上游一抖就没有第二家。
10
+ * - 北交所行情此前是**零**:腾讯那一路在参数层明确拒 BJ,而这一族实测含 349 只北证票。
11
+ * 上游分别是同花顺与同花顺 daec,与现有能力**不是同一条抓取路径**,才够称 plan B。
12
+ *
13
+ * ⛔ 本模块的 description 与注释**只允许写有原始报文可回查的数字**。2026-10-15 曾有一批"实测计数"
14
+ * (行数在 3766/3864/5450/5574 之间跳、`ys` 返回 309 行、业务码 100605、页码回显谎报)事后逐条回查
15
+ * **在任何一次真报文里都不存在**,是我把不同接口的读数串成了因果;据此撤掉的两颗能力已于当晚复验注册回来。
16
+ * 教训写进 `docs/design/ftshare-source-survey.md` §7.5–§7.7:**没打进日志的数字不算实测**。
17
+ *
18
+ * ⛔ 免费档限流 **10 QPS / burst 20**(官方口径)。`DataCollectorHub` 的 FIFO 只保证同一时刻
19
+ * 一个请求,**不含最小间隔**——串行 ≠ 节流(`src/net/eastmoney-client.ts` 同一条教训)。
20
+ * 本模块用进程级节流链把上限压到 ~8 QPS,留突发余量。
21
+ *
22
+ * ⛔ 凭据一律**调用期解析**,注册只看结构(AGENTS.md §9.7,2026-09-30 桌面端 61 能力静默缺席
23
+ * 那一族事故)。套餐外路由返回 403 `route_not_allowed`,归类成 `plan_closed` 且不重试——
24
+ * 它和 Fuyao 的 `2004` 是同一件事:能力级不可用,重试只白烧回合。
25
+ */
26
+ import type { Context } from '@deepseek-ai/cordis';
27
+ import type { DataSource } from '../data-collector/hub.js';
28
+ type JsonRecord = Record<string, unknown>;
29
+ /** 10 QPS 的 80% 上限:120ms 最小间隔 ⇒ 稳态 ~8.3 次/秒,突发也不会越过 20。 */
30
+ export declare const FTSHARE_MIN_INTERVAL_MS = 120;
31
+ /** 失败归类:与 `FuyaoFailureKind` 同一套语汇,`plan_closed` 对应 Fuyao 的 `capability_closed`。 */
32
+ type FtshareFailureKind = 'credential_missing' | 'rate_limited' | 'plan_closed' | 'invalid_parameter' | 'http_error' | 'upstream_error' | 'invalid_response';
33
+ export declare class FtshareError extends Error {
34
+ readonly kind: FtshareFailureKind;
35
+ readonly status?: number;
36
+ readonly code?: number;
37
+ constructor(kind: FtshareFailureKind, message: string, details?: {
38
+ status?: number;
39
+ code?: number;
40
+ });
41
+ }
42
+ export interface FtshareRequester {
43
+ getJson(path: string, search: URLSearchParams, signal: AbortSignal): Promise<JsonRecord>;
44
+ }
45
+ /** key 每次调用现取(`resolveApiKey` 由装配层注入),构造客户端不依赖 key。 */
46
+ export declare function createFtshareClient(resolveApiKey: () => Promise<string | undefined>, baseUrl?: string): FtshareRequester;
47
+ /**
48
+ * key 每次调用现取(与 `resolveFuyaoApiKey` 同一口径):credentials 优先、环境变量兜底。
49
+ * ⛔ 注册面**不得**以"装配期读到 key"为条件(AGENTS.md §9.7),也不要把这里的 rejection 缓存住——
50
+ * 一次装配期抢跑会把这个能力钉到进程结束。
51
+ */
52
+ export declare function resolveFtshareApiKey(ctx: Context, credentialRef?: string): Promise<string | undefined>;
53
+ export declare function createFtshareSources(resolveApiKey: () => Promise<string | undefined>): DataSource[];
54
+ export {};