dsh-data-domain-knowledge 0.1.0-alpha.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.
Files changed (231) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +360 -0
  3. package/cordis.patch.yml +410 -0
  4. package/eval/README.md +76 -0
  5. package/eval/evaluate-retrieval.mjs +121 -0
  6. package/eval/queries.example.jsonl +2 -0
  7. package/lib/client.js +111415 -0
  8. package/lib/types/config-schema.d.ts +35 -0
  9. package/lib/types/config-schema.d.ts.map +1 -0
  10. package/lib/types/config-schema.js +34 -0
  11. package/lib/types/config-schema.js.map +1 -0
  12. package/lib/types/convert/core-loader.d.ts +32 -0
  13. package/lib/types/convert/core-loader.d.ts.map +1 -0
  14. package/lib/types/convert/core-loader.js +60 -0
  15. package/lib/types/convert/core-loader.js.map +1 -0
  16. package/lib/types/convert/host.d.ts +88 -0
  17. package/lib/types/convert/host.d.ts.map +1 -0
  18. package/lib/types/convert/host.js +479 -0
  19. package/lib/types/convert/host.js.map +1 -0
  20. package/lib/types/convert/ingest.d.ts +125 -0
  21. package/lib/types/convert/ingest.d.ts.map +1 -0
  22. package/lib/types/convert/ingest.js +228 -0
  23. package/lib/types/convert/ingest.js.map +1 -0
  24. package/lib/types/convert/settings-schema.d.ts +18 -0
  25. package/lib/types/convert/settings-schema.d.ts.map +1 -0
  26. package/lib/types/convert/settings-schema.js +26 -0
  27. package/lib/types/convert/settings-schema.js.map +1 -0
  28. package/lib/types/convert/types.d.ts +46 -0
  29. package/lib/types/convert/types.d.ts.map +1 -0
  30. package/lib/types/convert/types.js +41 -0
  31. package/lib/types/convert/types.js.map +1 -0
  32. package/lib/types/diagnosis/cards.d.ts +71 -0
  33. package/lib/types/diagnosis/cards.d.ts.map +1 -0
  34. package/lib/types/diagnosis/cards.js +482 -0
  35. package/lib/types/diagnosis/cards.js.map +1 -0
  36. package/lib/types/diagnosis/problem-classes.d.ts +51 -0
  37. package/lib/types/diagnosis/problem-classes.d.ts.map +1 -0
  38. package/lib/types/diagnosis/problem-classes.js +66 -0
  39. package/lib/types/diagnosis/problem-classes.js.map +1 -0
  40. package/lib/types/diagnosis/schema.d.ts +65 -0
  41. package/lib/types/diagnosis/schema.d.ts.map +1 -0
  42. package/lib/types/diagnosis/schema.js +208 -0
  43. package/lib/types/diagnosis/schema.js.map +1 -0
  44. package/lib/types/diagnosis/seed-source.d.ts +20 -0
  45. package/lib/types/diagnosis/seed-source.d.ts.map +1 -0
  46. package/lib/types/diagnosis/seed-source.js +96 -0
  47. package/lib/types/diagnosis/seed-source.js.map +1 -0
  48. package/lib/types/diagnosis/seed.d.ts +50 -0
  49. package/lib/types/diagnosis/seed.d.ts.map +1 -0
  50. package/lib/types/diagnosis/seed.js +242 -0
  51. package/lib/types/diagnosis/seed.js.map +1 -0
  52. package/lib/types/diagnosis/store.d.ts +81 -0
  53. package/lib/types/diagnosis/store.d.ts.map +1 -0
  54. package/lib/types/diagnosis/store.js +409 -0
  55. package/lib/types/diagnosis/store.js.map +1 -0
  56. package/lib/types/diagnosis/types.d.ts +124 -0
  57. package/lib/types/diagnosis/types.d.ts.map +1 -0
  58. package/lib/types/diagnosis/types.js +13 -0
  59. package/lib/types/diagnosis/types.js.map +1 -0
  60. package/lib/types/directory-picker.d.ts +94 -0
  61. package/lib/types/directory-picker.d.ts.map +1 -0
  62. package/lib/types/directory-picker.js +110 -0
  63. package/lib/types/directory-picker.js.map +1 -0
  64. package/lib/types/extract-model.d.ts +76 -0
  65. package/lib/types/extract-model.d.ts.map +1 -0
  66. package/lib/types/extract-model.js +193 -0
  67. package/lib/types/extract-model.js.map +1 -0
  68. package/lib/types/extract.d.ts +121 -0
  69. package/lib/types/extract.d.ts.map +1 -0
  70. package/lib/types/extract.js +549 -0
  71. package/lib/types/extract.js.map +1 -0
  72. package/lib/types/fts.d.ts +96 -0
  73. package/lib/types/fts.d.ts.map +1 -0
  74. package/lib/types/fts.js +184 -0
  75. package/lib/types/fts.js.map +1 -0
  76. package/lib/types/index.d.ts +39 -0
  77. package/lib/types/index.d.ts.map +1 -0
  78. package/lib/types/index.js +1112 -0
  79. package/lib/types/index.js.map +1 -0
  80. package/lib/types/intent/engine.d.ts +108 -0
  81. package/lib/types/intent/engine.d.ts.map +1 -0
  82. package/lib/types/intent/engine.js +501 -0
  83. package/lib/types/intent/engine.js.map +1 -0
  84. package/lib/types/intent/host.d.ts +88 -0
  85. package/lib/types/intent/host.d.ts.map +1 -0
  86. package/lib/types/intent/host.js +389 -0
  87. package/lib/types/intent/host.js.map +1 -0
  88. package/lib/types/intent/preset-tools.d.ts +37 -0
  89. package/lib/types/intent/preset-tools.d.ts.map +1 -0
  90. package/lib/types/intent/preset-tools.js +45 -0
  91. package/lib/types/intent/preset-tools.js.map +1 -0
  92. package/lib/types/intent/settings-schema.d.ts +59 -0
  93. package/lib/types/intent/settings-schema.d.ts.map +1 -0
  94. package/lib/types/intent/settings-schema.js +57 -0
  95. package/lib/types/intent/settings-schema.js.map +1 -0
  96. package/lib/types/intent/store.d.ts +75 -0
  97. package/lib/types/intent/store.d.ts.map +1 -0
  98. package/lib/types/intent/store.js +139 -0
  99. package/lib/types/intent/store.js.map +1 -0
  100. package/lib/types/intent/tools.d.ts +63 -0
  101. package/lib/types/intent/tools.d.ts.map +1 -0
  102. package/lib/types/intent/tools.js +108 -0
  103. package/lib/types/intent/tools.js.map +1 -0
  104. package/lib/types/intent/types.d.ts +189 -0
  105. package/lib/types/intent/types.d.ts.map +1 -0
  106. package/lib/types/intent/types.js +62 -0
  107. package/lib/types/intent/types.js.map +1 -0
  108. package/lib/types/knowledge-bases.d.ts +266 -0
  109. package/lib/types/knowledge-bases.d.ts.map +1 -0
  110. package/lib/types/knowledge-bases.js +1107 -0
  111. package/lib/types/knowledge-bases.js.map +1 -0
  112. package/lib/types/layout.d.ts +262 -0
  113. package/lib/types/layout.d.ts.map +1 -0
  114. package/lib/types/layout.js +312 -0
  115. package/lib/types/layout.js.map +1 -0
  116. package/lib/types/migrate.d.ts +110 -0
  117. package/lib/types/migrate.d.ts.map +1 -0
  118. package/lib/types/migrate.js +280 -0
  119. package/lib/types/migrate.js.map +1 -0
  120. package/lib/types/recall-rank.d.ts +158 -0
  121. package/lib/types/recall-rank.d.ts.map +1 -0
  122. package/lib/types/recall-rank.js +261 -0
  123. package/lib/types/recall-rank.js.map +1 -0
  124. package/lib/types/runtime.d.ts +568 -0
  125. package/lib/types/runtime.d.ts.map +1 -0
  126. package/lib/types/runtime.js +3045 -0
  127. package/lib/types/runtime.js.map +1 -0
  128. package/lib/types/settings-schema.d.ts +117 -0
  129. package/lib/types/settings-schema.d.ts.map +1 -0
  130. package/lib/types/settings-schema.js +92 -0
  131. package/lib/types/settings-schema.js.map +1 -0
  132. package/lib/types/settings-volatile.d.ts +102 -0
  133. package/lib/types/settings-volatile.d.ts.map +1 -0
  134. package/lib/types/settings-volatile.js +114 -0
  135. package/lib/types/settings-volatile.js.map +1 -0
  136. package/lib/types/types.d.ts +799 -0
  137. package/lib/types/types.d.ts.map +1 -0
  138. package/lib/types/types.js +387 -0
  139. package/lib/types/types.js.map +1 -0
  140. package/lib/types/wiki-types.d.ts +68 -0
  141. package/lib/types/wiki-types.d.ts.map +1 -0
  142. package/lib/types/wiki-types.js +322 -0
  143. package/lib/types/wiki-types.js.map +1 -0
  144. package/package.json +101 -0
  145. package/preset/tita-data-agent/agent.cordis.yml +376 -0
  146. package/preset/tita-data-agent/preset.yml +3 -0
  147. package/python/requirements.txt +8 -0
  148. package/scripts/doctor.mjs +70 -0
  149. package/scripts/e2e-pipeline.mjs +355 -0
  150. package/scripts/install-agent-preset.mjs +92 -0
  151. package/scripts/setup-embedding.sh +34 -0
  152. package/scripts/sync-agent-preset.mjs +188 -0
  153. package/src/client/AgentPresetCard.tsx +247 -0
  154. package/src/client/DirectoryPicker.tsx +357 -0
  155. package/src/client/InfoHint.tsx +65 -0
  156. package/src/client/IntentLogicTab.tsx +185 -0
  157. package/src/client/IntentTab.tsx +715 -0
  158. package/src/client/KnowledgeBaseTab.tsx +1454 -0
  159. package/src/client/KnowledgeGraphView.tsx +306 -0
  160. package/src/client/KnowledgePanel.tsx +381 -0
  161. package/src/client/KnowledgeStepRail.tsx +60 -0
  162. package/src/client/NodeSettings.tsx +392 -0
  163. package/src/client/PreviewSections.tsx +116 -0
  164. package/src/client/RecallPanel.tsx +399 -0
  165. package/src/client/TypeDefinitionDrawer.tsx +215 -0
  166. package/src/client/WikiFlowCard.tsx +226 -0
  167. package/src/client/WikiTypeEditor.tsx +332 -0
  168. package/src/client/agent-preset.ts +121 -0
  169. package/src/client/arch/AgentArchitectureGraph.tsx +219 -0
  170. package/src/client/arch/NodeLogicPanel.tsx +133 -0
  171. package/src/client/arch/ProblemDetailPanel.tsx +88 -0
  172. package/src/client/arch/model.ts +691 -0
  173. package/src/client/architecture.css +191 -0
  174. package/src/client/architecture.tsx +530 -0
  175. package/src/client/attribution/AttributionGraph.tsx +104 -0
  176. package/src/client/attribution/AttributionTab.tsx +332 -0
  177. package/src/client/attribution/CaseView.tsx +102 -0
  178. package/src/client/attribution/GraphStage.tsx +87 -0
  179. package/src/client/attribution/RunView.tsx +184 -0
  180. package/src/client/attribution/StageChrome.tsx +403 -0
  181. package/src/client/attribution/ViewSwitch.tsx +107 -0
  182. package/src/client/attribution/graph-options.ts +381 -0
  183. package/src/client/attribution/model.ts +462 -0
  184. package/src/client/config-scope.ts +119 -0
  185. package/src/client/convert/ConvertPanel.tsx +594 -0
  186. package/src/client/convert/styles.css +138 -0
  187. package/src/client/convert/styles.ts +49 -0
  188. package/src/client/format.ts +39 -0
  189. package/src/client/graph/layout.ts +181 -0
  190. package/src/client/graph.css +113 -0
  191. package/src/client/index.ts +98 -0
  192. package/src/client/problem-domains.ts +114 -0
  193. package/src/client/styles.css +940 -0
  194. package/src/client/styles.ts +11 -0
  195. package/src/client/useFitScale.ts +68 -0
  196. package/src/config-schema.ts +45 -0
  197. package/src/convert/core-loader.ts +68 -0
  198. package/src/convert/host.ts +579 -0
  199. package/src/convert/ingest.ts +367 -0
  200. package/src/convert/settings-schema.ts +27 -0
  201. package/src/convert/types.ts +65 -0
  202. package/src/diagnosis/cards.ts +498 -0
  203. package/src/diagnosis/problem-classes.ts +117 -0
  204. package/src/diagnosis/schema.ts +260 -0
  205. package/src/diagnosis/seed-source.ts +107 -0
  206. package/src/diagnosis/seed.ts +291 -0
  207. package/src/diagnosis/store.ts +432 -0
  208. package/src/diagnosis/types.ts +152 -0
  209. package/src/directory-picker.ts +164 -0
  210. package/src/extract-model.ts +227 -0
  211. package/src/extract.ts +632 -0
  212. package/src/fts.ts +203 -0
  213. package/src/index.ts +1091 -0
  214. package/src/intent/README.md +63 -0
  215. package/src/intent/engine.ts +527 -0
  216. package/src/intent/host.ts +462 -0
  217. package/src/intent/preset-tools.ts +48 -0
  218. package/src/intent/settings-schema.ts +63 -0
  219. package/src/intent/store.ts +143 -0
  220. package/src/intent/tools.ts +139 -0
  221. package/src/intent/types.ts +245 -0
  222. package/src/knowledge-bases.ts +1160 -0
  223. package/src/layout.ts +350 -0
  224. package/src/migrate.ts +262 -0
  225. package/src/python/embedding_worker.py +471 -0
  226. package/src/recall-rank.ts +299 -0
  227. package/src/runtime.ts +3069 -0
  228. package/src/settings-schema.ts +94 -0
  229. package/src/settings-volatile.ts +122 -0
  230. package/src/types.ts +1140 -0
  231. package/src/wiki-types.ts +357 -0
@@ -0,0 +1,410 @@
1
+ # data-domain-knowledge bundle patch: mount the knowledge / intent / convert host.
2
+ #
3
+ # 0.1.7 起这条 entry **自己就是设置文档**:插件导出的 `Config`(见
4
+ # `src/config-schema.ts`)把三块设置合成一个 schema —— `knowledge` / `intent` /
5
+ # `convert`,正是 0.1.5 那三个设置命名空间(`tita-data-agent-knowledge` /
6
+ # `-intent` / `-convert`)。设置页里的改动由 `configEditor` 写回**本 entry 的
7
+ # `config:`**,客户端按路径读写(`{op:'set', path:['knowledge','recallTopK']}`)。
8
+ #
9
+ # 所以这里**故意不写 `config:`**:三组字段全都有 schema `.default()`,而
10
+ # `knowledge.agentRoot` 的默认值就是 `~/Desktop/titaagent`(`defaultAgentRoot()`,
11
+ # 可被 `DSH_AGENT_ROOT` 覆盖)。往这里写死一个 `agentRoot` 会把「Agent 的家在哪」
12
+ # 这件事从代码挪进某台机器的配置文件里 —— 那正是 0029 想收拢的那一个答案。
13
+ - insert:
14
+ - id: tita-data-domain-knowledge
15
+ name: 'dsh-data-domain-knowledge'
16
+
17
+ # >>> agent preset (generated) >>>
18
+ # Tita Data Agent 的 agent preset 声明。
19
+ #
20
+ # 这一段由 `scripts/sync-agent-preset.mjs` 从 `preset/tita-data-agent/` 生成 ——
21
+ # **不要手改**:改真源再跑 `pnpm sync:preset`。`pnpm doctor` 会校验它与真源一致。
22
+ #
23
+ # 为什么 preset 必须长在插件的 patch 里:0.1.7 起 DSH 不再扫描
24
+ # `$DSH_HOME/.agent-presets/`,preset 的唯一形态就是这条 insert。详见脚本头注释。
25
+ - insert:
26
+ - id: preset-tita-data-agent
27
+ name: '@deepseek-ai/dsh-agent-preset'
28
+ config:
29
+ id: tita-data-agent
30
+ name: 'Tita Data Agent'
31
+ description: 'TitaAgent 自己的组合:数据 Agent 的提示词,加上只有这个 preset 才有的意图记录工具。'
32
+ order: 10
33
+ plugins:
34
+ # Tita Data Agent 的 agent 组合(0029)。
35
+ #
36
+ # 这是 `standard` 的一份**拷贝**,只改了四件事。preset 是「一份完整组合」,没有
37
+ # patch 语义(`dsh-agent-presets` 的 authoring 是 copy-only),所以它必须是一整份
38
+ # 文件、而不是对 standard 的增量。
39
+ #
40
+ # 改了什么:
41
+ #
42
+ # 1. **身份**(`persona` 行):把「你是一个编码 Agent」换成 Tita Data Agent 自己的
43
+ # 提示词。这一行是**这个 preset 存在的理由** —— 它 shadow 掉部署级的 persona,
44
+ # 所以只有加入这个 preset 的会话会以数据 Agent 的身份说话。提示词正文就在下面,
45
+ # 直接改它即可;改完**新建一个会话**才生效(preset 在会话创建时固定)。
46
+ # 2. **意图工具**(`intent-tools` 行):`intent_record` / `intent_store_view` /
47
+ # `intent_registry_list` 三个工具搬到 agent 平面,只有这个 preset 的会话能看到。
48
+ # DSH 里「只有某个 preset 能用」唯一支持的形态就是注册作用域,所以它们必须在这里
49
+ # 挂载,而不是在宿主平面全局注册(那会让每个 agent 都看到 intent_record)。
50
+ # 3. **子代理不许继承意图工具**:子代理会加入父会话的组合,所以要在 delegation 的每一
51
+ # 行上显式 deny 掉 —— 意图记录描述的是「用户在跟 Agent 要什么」,子代理做的是被拆
52
+ # 出来的一个片段,它记一条意图只会污染任务树。
53
+ # 4. 其余每一行都与 `standard` 逐字相同:工具集不变,这个 preset 换的是身份与意图
54
+ # 能力,不是一个更小的 Agent。
55
+ #
56
+ # 其余纪律与 `standard` 一致:这是 AGENT-PLANE 组合,roster 在 standing scope 下挂载
57
+ # 一次,每个命名它的会话按作用域父子关系加入。需要 provide 服务的行必须落在带
58
+ # `isolate` realm 的 group 里,否则它会发布到根 realm(进程级)并被挂载审计拒绝 ——
59
+ # 本文件里唯一这样的行是 delegation 那个 group。
60
+
61
+ # ── identity ────────────────────────────────────────────────────────────────
62
+
63
+ # The preset's own persona, shadowing the deployment default for this agent.
64
+ # `{{model}}` and `{{cwd}}` resolve from the agent's own route and workspace.
65
+ - id: persona
66
+ name: '@deepseek-ai/dsh-persona'
67
+ config:
68
+ # `{{model}}` / `{{cwd}}` 从本会话自己的路由与工作区解析。
69
+ suffix: Your working directory is {{cwd}}.
70
+ # ↓↓↓ Tita Data Agent 的提示词。**要改的就是这一段。** ↓↓↓
71
+ prefix: |
72
+ You are Tita Data Agent, the data analyst on this deployment. You are powered
73
+ by the {{model}} model. Your job is to answer questions about this
74
+ organisation's business with evidence, and to write down what you learn so the
75
+ next answer is cheaper than this one.
76
+
77
+ ## What you work with
78
+
79
+ - **The knowledge store.** Each knowledge base on disk is a tree.
80
+ `01_source_truth` holds SourceRaw: the original business documents, never
81
+ rewritten, kept so that a conclusion can point back at where it came from.
82
+ `02_wiki` holds the cards people read — one per entity, each with mandatory
83
+ `source_refs`, each following a type (`concept`, `metric`, `table`, `event`,
84
+ `playbook`, `case`). `03_compiled` is what `recall_knowledge` actually
85
+ searches: wikiNotes, a vector index and a knowledge graph. `05_diagnosis` is
86
+ the attribution graph the panel renders. There can be several bases.
87
+
88
+ - **The data surfaces, which are a different thing.** `workbook_*` reads and
89
+ writes the workbooks the user has open; `notebook_*` runs SQL in the user's
90
+ own browser. That is where a number is *computed*. The knowledge store is
91
+ where a number is *defined* — a `metric` card says how it must be calculated,
92
+ a `table` card says what one row means. Computing without the definition gets
93
+ you a number nobody agreed to.
94
+
95
+ - **Conversion.** `convert_source` turns a file, a web page or a video into
96
+ knowledge-ready Markdown and says where it landed. It is not knowledge until
97
+ `convert_ingest` has run, so keep saying which of the two steps you are on.
98
+
99
+ ## Working rules
100
+
101
+ 1. **Record the intent before the work.** Call `intent_record` with what the
102
+ user wants and what you are about to do, before you do it. The vocabulary,
103
+ and the rule for continuing existing work instead of starting a new task,
104
+ live in that tool's own description. The record never gates the request:
105
+ record, then continue.
106
+ 2. **Recall before you answer about this organisation.** For its own rules,
107
+ definitions, metrics, processes or history, call `recall_knowledge` first and
108
+ answer from what comes back. An empty result is a finding, not a licence to
109
+ improvise — say the store has nothing on it. The wiki layer answers "what does
110
+ this organisation mean by X"; when you need "where exactly does it say that",
111
+ or a card and the document behind it disagree, go read the source.
112
+ 3. **Never invent a number.** Every figure you report must come from a tool
113
+ result in this turn, and you must say which one. "I don't have that data" is
114
+ a correct answer; a made-up number is not.
115
+ 4. **Put it in writing while you still have the evidence.** A definition, a
116
+ metric's exact calculation, a process, a diagnosed cause — if it will be
117
+ asked again, write it as a card with `write_knowledge_card`, with
118
+ `source_refs` that already exist in the store. A card whose refs point
119
+ nowhere is rejected, and rightly so.
120
+ 5. **Correct a card, do not quietly overwrite it.** When something in the store
121
+ is wrong, set its status to `problem` and say what is wrong with it. Cards
122
+ marked `problem` drop out of recall by default — that is how a wrong
123
+ definition stops propagating.
124
+ 6. **Never invent a knowledge base id.** Call `list_knowledge_bases` before
125
+ addressing one you have not seen this session; omit `kb` only when the
126
+ default base is genuinely the right one.
127
+ 7. **Be concrete about uncertainty.** When sources disagree, present both and
128
+ name them. When a conclusion rests on a single document, say so.
129
+ 8. **Ask only what the user owns.** Which of two readings they meant, what
130
+ matters more, what to do next — ask. Anything you can look up, look up
131
+ instead of asking.
132
+ 9. **Deliverables belong to the project, not to your home.** Write every file
133
+ you produce for the user under `output/<YYYY-MM-DD>/` in your working
134
+ directory, never into the Agent root: the Agent root accumulates across
135
+ projects, a deliverable belongs to one. Name it `<topic>-<kind>.<ext>` where
136
+ `<kind>` is exactly one of `报告`, `图表`, `数据集`, `清洗脚本`, `SQL` — the
137
+ name alone has to say what the file is a year later. Append `-2`, `-3` rather
138
+ than overwriting, and say the path in your answer.
139
+
140
+ ## How to write
141
+
142
+ Answer in the user's language. Lead with the answer, then the evidence. Prefer
143
+ a table when you are comparing things and a short paragraph when you are not.
144
+ Skip preamble and praise; the user is here for the analysis.
145
+ # ↑↑↑ Tita Data Agent 的提示词结束 ↑↑↑
146
+
147
+ - id: agent-instructions
148
+ name: '@deepseek-ai/dsh-agent-instructions'
149
+ config:
150
+ maxBytes: 65536
151
+
152
+ # ── 意图记录(agent 平面)────────────────────────────────────────────────────
153
+ #
154
+ # 0029 之前这三个工具由知识插件在**宿主平面**全局注册,于是每个 agent、每个 preset
155
+ # 的会话都会在提示词里看到 `intent_record`。现在它们只在这里挂载:这个模块只往
156
+ # `tools` 注册表里塞定义、不 provide 任何服务,所以和随包的 `tool-fs` 一样不需要
157
+ # realm,而它的注册只覆盖加入本 preset 的会话。
158
+ #
159
+ # 引擎本身(设置、轮次记账、`/intent/**` 只读面)仍在宿主平面,通过
160
+ # `titaDataAgentIntent` 服务把操作面暴露给这一行 —— 引擎只有一份。
161
+ - id: intent-tools
162
+ name: 'dsh-data-domain-knowledge/intent-tools'
163
+
164
+ # ── shell ───────────────────────────────────────────────────────────────────
165
+
166
+ # `shell-env` stays in the HOST composition: `apps/cli/src/web.ts` injects it to
167
+ # publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is
168
+ # the criterion for host-plane ownership — injection resolves before any session
169
+ # exists, so there is no agent to key by. Behind a preset realm those variables
170
+ # never reached the model's shell at all. Both shell tools consume the host
171
+ # registry from here; their executors (`bash-sandbox`/`pwsh-sandbox`) are
172
+ # host-plane too.
173
+ - id: tool-bash
174
+ name: '@deepseek-ai/dsh-tool-bash'
175
+ disabled: !!js process.platform === 'win32'
176
+
177
+ - id: tool-pwsh
178
+ name: '@deepseek-ai/dsh-tool-pwsh'
179
+ disabled: !!js process.platform !== 'win32'
180
+
181
+ # ── filesystem ──────────────────────────────────────────────────────────────
182
+
183
+ # Both register into the host `tools` registry and provide nothing, so
184
+ # they need no realm. The `fs` service and its policy stay in the host.
185
+ - id: tool-fs
186
+ name: '@deepseek-ai/dsh-tool-fs'
187
+
188
+ - id: tool-fs-search
189
+ name: '@deepseek-ai/dsh-tool-fs-search'
190
+ config:
191
+ sampleOverCapGlobResults: false
192
+
193
+ # ── background jobs ────────────────────────────────────────────────────────
194
+
195
+ # Only the model-facing controls. The task REGISTRY stays on the host plane:
196
+ # its producers sit outside any realm this file could put it in — `tool-bash`
197
+ # above resolves it with `ctx.get`, and an entry-local realm here is invisible
198
+ # to every sibling row, so `run_in_background` would answer "background jobs
199
+ # unavailable" while these controls sat in the catalog. The registry is keyed by
200
+ # owning agent anyway, so one host instance serves every session. What a preset
201
+ # chooses is whether its agent can collect and stop background work at all.
202
+ - id: tool-jobs
203
+ name: '@deepseek-ai/dsh-tool-jobs'
204
+
205
+ # ── skills ──────────────────────────────────────────────────────────────────
206
+
207
+ # The skill REGISTRY lives in the host composition and is layered per scope:
208
+ # these rows register into THIS preset's layer of it, so they need no realm.
209
+ # `skill-filesystem` contributes local-root discovery for agents on this preset, and
210
+ # `tool-skill` gives them the catalog and loader; the merged catalog also
211
+ # carries whatever the deployment registered globally (repository plugins).
212
+ - id: skill-filesystem
213
+ name: '@deepseek-ai/dsh-skill-filesystem'
214
+
215
+ - id: tool-skill
216
+ name: '@deepseek-ai/dsh-tool-skill'
217
+
218
+ # ── goals ───────────────────────────────────────────────────────────────────
219
+
220
+ # The goal service and session driver stay on the host plane, where the Gateway
221
+ # can resolve them. The human command and model-facing tool register into this
222
+ # preset's scoped layers.
223
+ - id: command-goal
224
+ name: '@deepseek-ai/dsh-command-goal'
225
+
226
+ - id: tool-goal
227
+ name: '@deepseek-ai/dsh-tool-goal'
228
+
229
+ # ── plan mode ───────────────────────────────────────────────────────────────
230
+
231
+ # Plan state is per-agent by nature, so an entry-local realm is not a
232
+ # workaround here — it is the correct lifetime.
233
+ - id: planning
234
+ name: cordis:group
235
+ group: true
236
+ isolate:
237
+ planMode: true
238
+ config:
239
+ - id: plan-mode
240
+ name: '@deepseek-ai/dsh-plan-mode'
241
+ config:
242
+ section: |
243
+ You are in plan mode. Stay in plan mode until exit_plan_mode succeeds or the user switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
244
+
245
+ Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
246
+
247
+ The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
248
+
249
+ Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
250
+
251
+ Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
252
+
253
+ When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
254
+
255
+ # ── compaction ──────────────────────────────────────────────────────────────
256
+
257
+ # `compaction-basic` reads `toolResultPrune` through `ctx.get`, so the pruner must
258
+ # share this realm rather than sit outside it.
259
+ #
260
+ # `tokenMeter` is deliberately NOT in this realm: the meter stays on the HOST
261
+ # plane, and the rows here resolve that one instance. It takes no configuration,
262
+ # keys every fold by Session, and owns the context-meter projection units the
263
+ # browser reads for every session — behind a realm those units would come and go
264
+ # with whichever presets happen to be mounted. What a preset chooses is whether
265
+ # its agent compacts at all, which is `compaction-basic` below.
266
+ - id: compaction
267
+ name: cordis:group
268
+ group: true
269
+ isolate:
270
+ compaction: true
271
+ toolResultPruner: true
272
+ config:
273
+ - id: compaction-basic
274
+ name: '@deepseek-ai/dsh-compaction-basic'
275
+
276
+ - id: command-compact
277
+ name: '@deepseek-ai/dsh-command-compact'
278
+
279
+ - id: tool-result-pruner
280
+ name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
281
+ config:
282
+ thresholdChars: 8192
283
+ headChars: 4096
284
+ tailChars: 1024
285
+
286
+ # ── delegation and workflows ────────────────────────────────────────────────
287
+
288
+ # The `subagents` registry and its spawn/fork backends live in the HOST
289
+ # composition: the registry is a process singleton whose cross-session queries
290
+ # the api-proxy serves to the browser, and a provider name may only be
291
+ # registered once. This preset contributes the delegation TOOLS, which resolve
292
+ # that host registry.
293
+ #
294
+ # `workflows` is different — nothing outside an agent reads it — so every row
295
+ # that reaches it shares one entry-local realm here, and a consumer left
296
+ # outside would resolve a host registry this preset does not populate.
297
+ - id: delegation
298
+ name: cordis:group
299
+ group: true
300
+ isolate:
301
+ workflowEngine: true
302
+ config:
303
+ - id: tool-subagent-control
304
+ name: '@deepseek-ai/dsh-tool-subagent-control'
305
+
306
+ - id: tool-subagent-list-agents
307
+ name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
308
+
309
+ - id: tool-subagent
310
+ name: '@deepseek-ai/dsh-tool-subagent'
311
+ config:
312
+ provider: spawn
313
+ toolName: subagent
314
+ modelSelectionSettings: true
315
+ backgroundMode: continuable
316
+ toolFilter:
317
+ deny:
318
+ - intent_record
319
+ - intent_store_view
320
+ - intent_registry_list
321
+
322
+ # Fork omits model selection so provider/model stay equal to the parent and
323
+ # the inherited history remains eligible for KV Cache reuse. This preset
324
+ # keeps fork continuable; parent and child inherit the same messaging tool,
325
+ # while the parent id and return guidance follow the inherited history.
326
+ - id: tool-subagent-fork
327
+ name: '@deepseek-ai/dsh-tool-subagent'
328
+ config:
329
+ provider: fork
330
+ toolName: subagent_fork
331
+ backgroundMode: continuable
332
+ toolFilter:
333
+ deny:
334
+ - intent_record
335
+ - intent_store_view
336
+ - intent_registry_list
337
+
338
+ # Production dsh does not install these optional providers. Install the
339
+ # matching Bundle in this Profile and restart the Host, then copy this
340
+ # preset and remove `disabled` from the matching tool row. Host availability
341
+ # alone grants no tool.
342
+ - id: tool-subagent-codex
343
+ name: '@deepseek-ai/dsh-tool-subagent'
344
+ disabled: true
345
+ config:
346
+ provider: codex
347
+ toolName: subagent_codex
348
+ backgroundMode: one-shot
349
+ maxDepth: provider-managed
350
+ toolFilter:
351
+ deny:
352
+ - intent_record
353
+ - intent_store_view
354
+ - intent_registry_list
355
+
356
+ - id: tool-subagent-claude-code
357
+ name: '@deepseek-ai/dsh-tool-subagent'
358
+ disabled: true
359
+ config:
360
+ provider: claude-code
361
+ toolName: subagent_claude_code
362
+ backgroundMode: one-shot
363
+ maxDepth: provider-managed
364
+ toolFilter:
365
+ deny:
366
+ - intent_record
367
+ - intent_store_view
368
+ - intent_registry_list
369
+
370
+ # 0.1.7 把这行换掉了:旧包名 `@deepseek-ai/dsh-workflow-worker-thread` 在
371
+ # 0.1.7 的安装里已经不存在,对应物是 `@deepseek-ai/dsh-workflow-ptc`
372
+ # (见 `dsh-web-app` 的 `presets/cordis.patch.yml`)。留着旧名不会报「找不到
373
+ # 包」,而是让**整个 preset 挂载失败**,症状是它出现在 Agent 预设名单里、
374
+ # 却带一条 `workflow-worker-thread (…): never started` 诊断 —— 这也是
375
+ # 「自定义 agent 没生效」最像「配置没生效」的一种失败。
376
+ - id: workflow-ptc
377
+ name: '@deepseek-ai/dsh-workflow-ptc'
378
+ config:
379
+ provider: spawn
380
+
381
+ - id: tool-workflow
382
+ name: '@deepseek-ai/dsh-tool-workflow'
383
+
384
+ - id: tool-ralph
385
+ name: '@deepseek-ai/dsh-tool-ralph'
386
+ config:
387
+ subagentProvider: spawn
388
+ maxRounds: 64
389
+
390
+ # ── remaining model-facing rows ─────────────────────────────────────────────
391
+
392
+ - id: tool-ask-user
393
+ name: '@deepseek-ai/dsh-tool-ask-user'
394
+
395
+ - id: tool-todo
396
+ name: '@deepseek-ai/dsh-tool-todo'
397
+ config:
398
+ allowParallelInProgress: true
399
+
400
+ # The `web` service and its search provider stay in the host composition; only
401
+ # the model-facing tool is per-session.
402
+ - id: tool-web
403
+ name: '@deepseek-ai/dsh-tool-web'
404
+ config:
405
+ fetch: true
406
+ searchTimeoutMs: 60000
407
+
408
+ - id: present
409
+ name: '@deepseek-ai/dsh-tool-present'
410
+ # <<< agent preset (generated) <<<
package/eval/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # Tita Knowledge Retrieval Evaluation
2
+
3
+ `evaluate-retrieval.mjs` 对同一份已经编译好的 Knowledge Store,逐条运行
4
+ `fts`、`vector`、`hybrid`,输出 Recall、MRR、nDCG 和 p50/p95 latency。它不自动
5
+ 下载公开数据集,也不会改写知识库;公开数据集需要先转换为本插件的 qrels 格式。
6
+
7
+ ## qrels 格式
8
+
9
+ 每行一个 JSON 对象,`relevant_unit_ids` 使用编译后的稳定 `unitId`,例如
10
+ `metric:sales_amount`。可以附加 `source_types`、`include_problems` 和 `subset`,用于
11
+ 分组评测文本、图片 Markdown、表格截图等场景。
12
+
13
+ ```json
14
+ {"id":"q1","query":"销售额如何计算","relevant_unit_ids":["metric:sales_amount"],"subset":"text"}
15
+ ```
16
+
17
+ ## 本地运行
18
+
19
+ 先在 UI 点击一次「编译」,再运行:
20
+
21
+ ```bash
22
+ pnpm --filter dsh-data-domain-knowledge build
23
+ node plugins/dsh-data-domain-knowledge/eval/evaluate-retrieval.mjs \
24
+ --root "$HOME/.tita-data-agent/knowledge" \
25
+ --qrels plugins/dsh-data-domain-knowledge/eval/queries.example.jsonl
26
+ ```
27
+
28
+ 如果 Knowledge Store 使用 WeMM:
29
+
30
+ ```bash
31
+ TITA_EVAL_PROVIDER=wemm-python \
32
+ TITA_EVAL_PYTHON="$HOME/.tita-data-agent/embedding-venv/bin/python" \
33
+ TITA_EVAL_MODEL="$HOME/.tita-data-agent/models/WeMM-Embedding-2B-Quantized" \
34
+ node plugins/dsh-data-domain-knowledge/eval/evaluate-retrieval.mjs \
35
+ --root "$HOME/.tita-data-agent/knowledge" \
36
+ --qrels ./queries.jsonl
37
+ ```
38
+
39
+ ## 建议的对比矩阵
40
+
41
+ | 组别 | 检索方式 | 目的 |
42
+ | --- | --- | --- |
43
+ | A | FTS | 当前精确匹配基线,观察术语、ID 和公式查询 |
44
+ | B | WeMM 量化 1024 维,binary/Hamming + int8 重排 | Tita 默认本地多模态方案 |
45
+ | C | WeMM 同一模型的未量化/BF16 向量 | 分离量化损失与模型能力 |
46
+ | D | hybrid(当前默认) | 观察精确词命中与语义/图片召回的互补收益 |
47
+
48
+ A、B、D 可直接由本脚本比较;C 需要在另一份 compiled root 中用官方 BF16
49
+ checkpoint 编译后再跑同一份 qrels。每次比较固定知识卡片、qrels、top-k、查询顺序
50
+ 和硬件,避免把数据变化误认为模型差异。
51
+
52
+ ## 数据集分层
53
+
54
+ 1. 公共文本 sanity:从 [BEIR](https://github.com/beir-cellar/beir) 选一个小型
55
+ domain subset,或从 [MTEB 可用 benchmark 列表](https://docs.mteb.org/overview/available_benchmarks/)
56
+ 选择 retrieval task。它们适合比较文本 embedding,不足以代表 Tita 的 Markdown
57
+ 图片和知识卡片工作流。
58
+ 2. 公共多模态:使用 [MMEB-V3](https://huggingface.co/datasets/VLM2Vec/MMEB-V3)
59
+ 的 image/text、visual-document 和 agent-centric 子集;它与 [WeMM 官方仓库](https://github.com/Tencent/WeMM-Embedding)
60
+ 的模型使用场景更接近。MTEB 的 benchmark 列表也收录了 MIEB 这类图像 embedding
61
+ 评测,但它仍然是模型级评测,不是 Tita 端到端评测。
62
+ 3. Tita 专属 golden set:建议先人工标注 100–200 条查询,至少包含指标口径、表/字段
63
+ 语义、同义词、跨语言、图片中的 UI/图表信息、hard negative 和“不应召回”样本。
64
+ 每条 query 标注 1–3 个正例 unitId;每轮发布前固定这份集合,新增样本走版本控制。
65
+
66
+ ## 指标与门槛
67
+
68
+ 主指标使用 `nDCG@10`、`Recall@10`、`MRR@10`,辅助看 `Recall@5/20`;同时记录
69
+ 向量编译吞吐(units/min)、查询 p50/p95、模型常驻内存、`03_compiled` 索引大小和
70
+ 向量复用率。多模态集合要单独报告 text→image、image→text、text+image 三类结果,
71
+ 不能只看总平均。
72
+
73
+ 建议先以 FTS 为守门基线:hybrid 在整体和每个关键子集都不能低于 FTS,WeMM vector
74
+ 至少应在语义改写和图片子集上带来可观增益;具体阈值在首轮 100–200 条标注跑完后再
75
+ 根据置信区间确定,不预先假设量化版一定优于 BF16。最终结果同时保存 qrels、配置、
76
+ 模型 revision、硬件和原始 JSON,确保可复现。
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFile } from 'node:fs/promises'
4
+ import { performance } from 'node:perf_hooks'
5
+ import { DEFAULT_KNOWLEDGE_CONFIG, KnowledgeRuntime } from '../lib/types/index.js'
6
+
7
+ function usage(message) {
8
+ if (message) console.error(`Error: ${message}\n`)
9
+ console.error('Usage: node eval/evaluate-retrieval.mjs --root <knowledge-root> --qrels <queries.jsonl> [--modes fts,vector,hybrid] [--k 5,10,20]')
10
+ process.exit(message ? 2 : 0)
11
+ }
12
+
13
+ function options(argv) {
14
+ const result = {}
15
+ for (let index = 0; index < argv.length; index += 1) {
16
+ const item = argv[index]
17
+ if (item === '--help') usage()
18
+ if (!item?.startsWith('--')) usage(`unknown argument: ${item ?? ''}`)
19
+ const key = item.slice(2)
20
+ const value = argv[index + 1]
21
+ if (!value || value.startsWith('--')) usage(`missing value for --${key}`)
22
+ result[key] = value
23
+ index += 1
24
+ }
25
+ return result
26
+ }
27
+
28
+ function percentile(values, percentileValue) {
29
+ if (!values.length) return 0
30
+ const sorted = [...values].sort((left, right) => left - right)
31
+ const position = (sorted.length - 1) * percentileValue
32
+ const lower = Math.floor(position)
33
+ const upper = Math.ceil(position)
34
+ if (lower === upper) return Number(sorted[lower].toFixed(2))
35
+ const value = sorted[lower] + (sorted[upper] - sorted[lower]) * (position - lower)
36
+ return Number(value.toFixed(2))
37
+ }
38
+
39
+ function ndcg(hits, relevant, limit) {
40
+ let dcg = 0
41
+ for (let index = 0; index < Math.min(limit, hits.length); index += 1) {
42
+ if (relevant.has(hits[index].unitId)) dcg += 1 / Math.log2(index + 2)
43
+ }
44
+ const idealCount = Math.min(limit, relevant.size)
45
+ let ideal = 0
46
+ for (let index = 0; index < idealCount; index += 1) ideal += 1 / Math.log2(index + 2)
47
+ return ideal ? dcg / ideal : 0
48
+ }
49
+
50
+ const args = options(process.argv.slice(2))
51
+ if (!args.root || !args.qrels) usage('--root and --qrels are required')
52
+ const limits = String(args.k ?? '5,10,20').split(',').map(Number).filter(value => Number.isInteger(value) && value > 0 && value <= 50)
53
+ const modes = String(args.modes ?? 'fts,vector,hybrid').split(',').filter(value => ['fts', 'vector', 'hybrid'].includes(value))
54
+ if (!limits.length || !modes.length) usage('invalid --k or --modes')
55
+
56
+ const lines = (await readFile(args.qrels, 'utf8')).split(/\r?\n/).map(line => line.trim()).filter(Boolean).filter(line => !line.startsWith('#'))
57
+ const qrels = lines.map((line, index) => {
58
+ const item = JSON.parse(line)
59
+ const relevant = item.relevant_unit_ids ?? item.relevant ?? []
60
+ if (!item.query || !Array.isArray(relevant) || relevant.length === 0) throw new Error(`qrels line ${index + 1} needs query and non-empty relevant_unit_ids`)
61
+ return { ...item, relevant_unit_ids: [...new Set(relevant.map(String))] }
62
+ })
63
+ if (!qrels.length) throw new Error('qrels is empty')
64
+
65
+ const root = String(args.root)
66
+ const config = {
67
+ ...DEFAULT_KNOWLEDGE_CONFIG,
68
+ rootDir: root,
69
+ embeddingProvider: process.env.TITA_EVAL_PROVIDER ?? DEFAULT_KNOWLEDGE_CONFIG.embeddingProvider,
70
+ embeddingModel: process.env.TITA_EVAL_MODEL ?? DEFAULT_KNOWLEDGE_CONFIG.embeddingModel,
71
+ python: process.env.TITA_EVAL_PYTHON ?? DEFAULT_KNOWLEDGE_CONFIG.python,
72
+ embeddingDevice: process.env.TITA_EVAL_DEVICE ?? DEFAULT_KNOWLEDGE_CONFIG.embeddingDevice,
73
+ }
74
+ const runtime = new KnowledgeRuntime(root, config)
75
+ const maxLimit = Math.max(...limits)
76
+ const output = {
77
+ root,
78
+ provider: config.embeddingProvider,
79
+ model: config.embeddingModel,
80
+ queryCount: qrels.length,
81
+ limits,
82
+ modes: {},
83
+ }
84
+
85
+ try {
86
+ for (const mode of modes) {
87
+ const latencies = []
88
+ const rows = []
89
+ let fallbackCount = 0
90
+ for (const qrel of qrels) {
91
+ const started = performance.now()
92
+ // The evaluator compares the recall routes themselves, so stage 4 rerank
93
+ // is disabled and the raw fused ranking is measured.
94
+ const response = await runtime.recall(qrel.query, maxLimit, qrel.source_types ?? [], qrel.include_problems === true, mode, undefined, { rerank: false })
95
+ latencies.push(performance.now() - started)
96
+ if (response.data.backend === 'fts' && mode !== 'fts') fallbackCount += 1
97
+ rows.push({ hits: response.data.hits, relevant: new Set(qrel.relevant_unit_ids) })
98
+ }
99
+ const metrics = {}
100
+ for (const limit of limits) {
101
+ let recall = 0
102
+ let mrr = 0
103
+ let score = 0
104
+ for (const row of rows) {
105
+ const hits = row.hits.slice(0, limit)
106
+ recall += hits.filter(hit => row.relevant.has(hit.unitId)).length / row.relevant.size
107
+ const rank = hits.findIndex(hit => row.relevant.has(hit.unitId))
108
+ mrr += rank < 0 ? 0 : 1 / (rank + 1)
109
+ score += ndcg(row.hits, row.relevant, limit)
110
+ }
111
+ metrics[`recall@${limit}`] = Number((recall / rows.length).toFixed(4))
112
+ metrics[`mrr@${limit}`] = Number((mrr / rows.length).toFixed(4))
113
+ metrics[`ndcg@${limit}`] = Number((score / rows.length).toFixed(4))
114
+ }
115
+ output.modes[mode] = { metrics, latencyMs: { p50: percentile(latencies, 0.5), p95: percentile(latencies, 0.95) }, fallbackCount }
116
+ }
117
+ } finally {
118
+ runtime.close()
119
+ }
120
+
121
+ console.log(JSON.stringify(output, null, 2))
@@ -0,0 +1,2 @@
1
+ {"id":"q-agent-architecture","query":"知识编译与召回的 Agent 架构","relevant_unit_ids":["concept:agent_architecture"],"subset":"text"}
2
+ {"id":"q-ui-architecture","query":"Tita 数据 Agent 的界面和架构","relevant_unit_ids":["concept:agent_architecture","concept:ui_reference"],"subset":"text-image"}