dsh-data-domain-knowledge 0.1.1-alpha.2 → 0.1.1-alpha.4
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/README.md +33 -16
- package/lib/client.js +1042 -2097
- package/lib/contract-refs.d.ts +0 -1
- package/lib/doc-page.js +1 -1
- package/lib/pages.js +2489 -1060
- package/lib/types/agent-behavior-rules.d.ts.map +1 -1
- package/lib/types/agent-behavior-rules.js +3 -2
- package/lib/types/agent-behavior-rules.js.map +1 -1
- package/lib/types/axis-contract.js +1 -1
- package/lib/types/axis-contract.js.map +1 -1
- package/lib/types/card-organize.d.ts +2 -1
- package/lib/types/card-organize.d.ts.map +1 -1
- package/lib/types/card-organize.js +5 -3
- package/lib/types/card-organize.js.map +1 -1
- package/lib/types/config-schema.d.ts +1 -1
- package/lib/types/config-schema.js +1 -1
- package/lib/types/convert/host.d.ts +2 -3
- package/lib/types/convert/host.d.ts.map +1 -1
- package/lib/types/convert/host.js +11 -2
- package/lib/types/convert/host.js.map +1 -1
- package/lib/types/convert/ingest.d.ts +14 -20
- package/lib/types/convert/ingest.d.ts.map +1 -1
- package/lib/types/convert/ingest.js +8 -5
- package/lib/types/convert/ingest.js.map +1 -1
- package/lib/types/diagnosis/store.d.ts +2 -2
- package/lib/types/diagnosis/store.d.ts.map +1 -1
- package/lib/types/diagnosis/store.js +10 -2
- package/lib/types/diagnosis/store.js.map +1 -1
- package/lib/types/diagnosis/types.d.ts +1 -1
- package/lib/types/diagnosis/types.js +1 -1
- package/lib/types/extract-model.d.ts +3 -0
- package/lib/types/extract-model.d.ts.map +1 -1
- package/lib/types/extract-model.js.map +1 -1
- package/lib/types/images.js +1 -1
- package/lib/types/index.d.ts +0 -4
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +99 -91
- package/lib/types/index.js.map +1 -1
- package/lib/types/knowledge-bases.d.ts +3 -0
- package/lib/types/knowledge-bases.d.ts.map +1 -1
- package/lib/types/knowledge-bases.js +22 -2
- package/lib/types/knowledge-bases.js.map +1 -1
- package/lib/types/knowledge-contract.d.ts +6 -0
- package/lib/types/knowledge-contract.d.ts.map +1 -1
- package/lib/types/knowledge-contract.js +6 -2
- package/lib/types/knowledge-contract.js.map +1 -1
- package/lib/types/layout.d.ts +9 -26
- package/lib/types/layout.d.ts.map +1 -1
- package/lib/types/layout.js +10 -28
- package/lib/types/layout.js.map +1 -1
- package/lib/types/materialize-snapshot.d.ts +5 -0
- package/lib/types/materialize-snapshot.d.ts.map +1 -1
- package/lib/types/migrate.d.ts +3 -1
- package/lib/types/migrate.d.ts.map +1 -1
- package/lib/types/migrate.js +3 -1
- package/lib/types/migrate.js.map +1 -1
- package/lib/types/model-text.d.ts +7 -0
- package/lib/types/model-text.d.ts.map +1 -1
- package/lib/types/model-text.js +31 -9
- package/lib/types/model-text.js.map +1 -1
- package/lib/types/organize-contract.d.ts.map +1 -1
- package/lib/types/organize-contract.js +2 -1
- package/lib/types/organize-contract.js.map +1 -1
- package/lib/types/pipeline/engine.d.ts +28 -4
- package/lib/types/pipeline/engine.d.ts.map +1 -1
- package/lib/types/pipeline/engine.js +68 -15
- package/lib/types/pipeline/engine.js.map +1 -1
- package/lib/types/pipeline/progress.d.ts +12 -0
- package/lib/types/pipeline/progress.d.ts.map +1 -0
- package/lib/types/pipeline/progress.js +37 -0
- package/lib/types/pipeline/progress.js.map +1 -0
- package/lib/types/pipeline/resumable-extract.d.ts +45 -0
- package/lib/types/pipeline/resumable-extract.d.ts.map +1 -0
- package/lib/types/pipeline/resumable-extract.js +262 -0
- package/lib/types/pipeline/resumable-extract.js.map +1 -0
- package/lib/types/pipeline/store.d.ts +8 -2
- package/lib/types/pipeline/store.d.ts.map +1 -1
- package/lib/types/pipeline/store.js +27 -5
- package/lib/types/pipeline/store.js.map +1 -1
- package/lib/types/pipeline/types.d.ts +56 -0
- package/lib/types/pipeline/types.d.ts.map +1 -1
- package/lib/types/pipeline/types.js +18 -1
- package/lib/types/pipeline/types.js.map +1 -1
- package/lib/types/present.d.ts +1 -1
- package/lib/types/present.js +1 -1
- package/lib/types/preset-ids.d.ts +0 -1
- package/lib/types/preset-ids.d.ts.map +1 -1
- package/lib/types/preset-ids.js +0 -3
- package/lib/types/preset-ids.js.map +1 -1
- package/lib/types/query-analysis.d.ts +0 -5
- package/lib/types/query-analysis.d.ts.map +1 -1
- package/lib/types/query-analysis.js +0 -16
- package/lib/types/query-analysis.js.map +1 -1
- package/lib/types/recall-delivery.d.ts +36 -0
- package/lib/types/recall-delivery.d.ts.map +1 -0
- package/lib/types/recall-delivery.js +16 -0
- package/lib/types/recall-delivery.js.map +1 -0
- package/lib/types/retrieval/scope.d.ts +17 -0
- package/lib/types/retrieval/scope.d.ts.map +1 -0
- package/lib/types/retrieval/scope.js +11 -0
- package/lib/types/retrieval/scope.js.map +1 -0
- package/lib/types/runtime.d.ts +75 -14
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/runtime.js +454 -198
- package/lib/types/runtime.js.map +1 -1
- package/lib/types/settings-volatile.d.ts +0 -7
- package/lib/types/settings-volatile.d.ts.map +1 -1
- package/lib/types/settings-volatile.js +0 -7
- package/lib/types/settings-volatile.js.map +1 -1
- package/lib/types/source-identity.d.ts +34 -0
- package/lib/types/source-identity.d.ts.map +1 -0
- package/lib/types/source-identity.js +85 -0
- package/lib/types/source-identity.js.map +1 -0
- package/lib/types/source-read.d.ts +18 -0
- package/lib/types/source-read.d.ts.map +1 -0
- package/lib/types/source-read.js +49 -0
- package/lib/types/source-read.js.map +1 -0
- package/lib/types/store/axis-references.d.ts.map +1 -1
- package/lib/types/store/axis-references.js +5 -3
- package/lib/types/store/axis-references.js.map +1 -1
- package/lib/types/store/knowledge-db.d.ts +5 -29
- package/lib/types/store/knowledge-db.d.ts.map +1 -1
- package/lib/types/store/knowledge-db.js +57 -2
- package/lib/types/store/knowledge-db.js.map +1 -1
- package/lib/types/store/source-identity.d.ts +8 -0
- package/lib/types/store/source-identity.d.ts.map +1 -0
- package/lib/types/store/source-identity.js +56 -0
- package/lib/types/store/source-identity.js.map +1 -0
- package/lib/types/types.d.ts +46 -5
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js +6 -3
- package/lib/types/types.js.map +1 -1
- package/lib/types/wiki-availability.d.ts +11 -0
- package/lib/types/wiki-availability.d.ts.map +1 -0
- package/lib/types/wiki-availability.js +39 -0
- package/lib/types/wiki-availability.js.map +1 -0
- package/lib/types/wiki-finalize.d.ts +4 -2
- package/lib/types/wiki-finalize.d.ts.map +1 -1
- package/lib/types/wiki-finalize.js +9 -27
- package/lib/types/wiki-finalize.js.map +1 -1
- package/lib/types/wiki-links.d.ts +35 -0
- package/lib/types/wiki-links.d.ts.map +1 -0
- package/lib/types/wiki-links.js +129 -0
- package/lib/types/wiki-links.js.map +1 -0
- package/lib/types/wiki-trial-types.d.ts +3 -0
- package/lib/types/wiki-trial-types.d.ts.map +1 -1
- package/lib/types/wiki-trial.d.ts +3 -3
- package/lib/types/wiki-trial.d.ts.map +1 -1
- package/lib/types/wiki-trial.js +14 -3
- package/lib/types/wiki-trial.js.map +1 -1
- package/package.json +16 -16
- package/scripts/doctor.mjs +0 -7
- package/scripts/setup-embedding.sh +0 -0
- package/src/agent-behavior-rules.ts +3 -2
- package/src/axis-contract.ts +1 -1
- package/src/card-organize.ts +5 -3
- package/src/client/AttributionEventGuide.tsx +3 -3
- package/src/client/AxisMatrix.tsx +86 -19
- package/src/client/AxisRegistrySection.tsx +1 -1
- package/src/client/CompileSettingsView.tsx +473 -0
- package/src/client/DirectoryPicker.tsx +0 -2
- package/src/client/ExtractionTasksPage.tsx +557 -423
- package/src/client/GuideArchitectureCanvas.tsx +5 -12
- package/src/client/ImportFilesDialog.tsx +1 -0
- package/src/client/InfoHint.tsx +1 -1
- package/src/client/KnowledgeBaseTab.tsx +141 -148
- package/src/client/KnowledgePanel.tsx +16 -9
- package/src/client/OperationLogTab.tsx +1 -1
- package/src/client/PipelineRunView.tsx +173 -0
- package/src/client/RecallPanel.tsx +5 -16
- package/src/client/TabGuide.tsx +3 -7
- package/src/client/WikiFlowPanel.tsx +10 -11
- package/src/client/arch/NodeLogicPanel.tsx +3 -3
- package/src/client/arch/model.ts +5 -113
- package/src/client/architecture.css +1 -1
- package/src/client/architecture.tsx +13 -27
- package/src/client/attribution/AttributionTab.tsx +141 -10
- package/src/client/pages.css +305 -180
- package/src/client/pages.tsx +7 -12
- package/src/client/styles.css +15 -96
- package/src/config-schema.ts +1 -1
- package/src/convert/host.ts +13 -5
- package/src/convert/ingest.ts +20 -7
- package/src/diagnosis/store.ts +7 -2
- package/src/diagnosis/types.ts +1 -1
- package/src/extract-model.ts +3 -0
- package/src/images.ts +1 -1
- package/src/index.ts +92 -97
- package/src/knowledge-bases.ts +23 -3
- package/src/knowledge-contract.ts +7 -2
- package/src/layout.ts +10 -32
- package/src/materialize-snapshot.ts +1 -0
- package/src/migrate.ts +3 -1
- package/src/model-text.ts +33 -11
- package/src/organize-contract.ts +2 -1
- package/src/pipeline/engine.ts +85 -22
- package/src/pipeline/progress.ts +40 -0
- package/src/pipeline/resumable-extract.ts +233 -0
- package/src/pipeline/store.ts +28 -6
- package/src/pipeline/types.ts +47 -2
- package/src/present.ts +1 -1
- package/src/preset-ids.ts +0 -4
- package/src/query-analysis.ts +0 -20
- package/src/recall-delivery.ts +19 -0
- package/src/retrieval/scope.ts +10 -0
- package/src/runtime.ts +412 -206
- package/src/settings-volatile.ts +0 -7
- package/src/source-identity.ts +85 -0
- package/src/source-read.ts +47 -0
- package/src/store/axis-references.ts +5 -3
- package/src/store/knowledge-db.ts +54 -2
- package/src/store/source-identity.ts +42 -0
- package/src/types.ts +18 -5
- package/src/wiki-availability.ts +37 -0
- package/src/wiki-finalize.ts +9 -25
- package/src/wiki-links.ts +120 -0
- package/src/wiki-trial-types.ts +3 -2
- package/src/wiki-trial.ts +17 -6
- package/lib/types/intent/engine.d.ts +0 -122
- package/lib/types/intent/engine.d.ts.map +0 -1
- package/lib/types/intent/engine.js +0 -488
- package/lib/types/intent/engine.js.map +0 -1
- package/lib/types/intent/host.d.ts +0 -68
- package/lib/types/intent/host.d.ts.map +0 -1
- package/lib/types/intent/host.js +0 -249
- package/lib/types/intent/host.js.map +0 -1
- package/lib/types/intent/scope.d.ts +0 -25
- package/lib/types/intent/scope.d.ts.map +0 -1
- package/lib/types/intent/scope.js +0 -116
- package/lib/types/intent/scope.js.map +0 -1
- package/lib/types/intent/store.d.ts +0 -33
- package/lib/types/intent/store.d.ts.map +0 -1
- package/lib/types/intent/store.js +0 -239
- package/lib/types/intent/store.js.map +0 -1
- package/lib/types/intent/tools.d.ts +0 -62
- package/lib/types/intent/tools.d.ts.map +0 -1
- package/lib/types/intent/tools.js +0 -130
- package/lib/types/intent/tools.js.map +0 -1
- package/lib/types/intent/types.d.ts +0 -170
- package/lib/types/intent/types.d.ts.map +0 -1
- package/lib/types/intent/types.js +0 -56
- package/lib/types/intent/types.js.map +0 -1
- package/src/client/IntentLogic.tsx +0 -192
- package/src/client/IntentTab.tsx +0 -343
- package/src/client/TypeDefinitionDrawer.tsx +0 -195
- package/src/intent/README.md +0 -42
- package/src/intent/engine.ts +0 -515
- package/src/intent/host.ts +0 -315
- package/src/intent/scope.ts +0 -125
- package/src/intent/store.ts +0 -218
- package/src/intent/tools.ts +0 -154
- package/src/intent/types.ts +0 -229
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState, type ReactNode } from 'react'
|
|
2
2
|
import {
|
|
3
3
|
Activity, AlertCircle, BarChart3, Box, BrainCircuit, Compass, Database, FileText, HardDrive,
|
|
4
|
-
Layers3, History, Settings2, Stethoscope,
|
|
4
|
+
Layers3, History, Settings2, Stethoscope, Wrench, X, Zap,
|
|
5
5
|
} from '@tita-agent/ui/icons'
|
|
6
6
|
import type { LucideIcon } from '@tita-agent/ui/icons'
|
|
7
7
|
import { Badge, Button, Card, Tabs, TabsContent, TabsList, TabsTrigger, cn } from '@tita-agent/ui/components'
|
|
@@ -22,7 +22,6 @@ import { PROBLEM_DOMAINS } from './problem-domains.ts'
|
|
|
22
22
|
import { AttributionTab } from './attribution/AttributionTab.tsx'
|
|
23
23
|
import { KnowledgeBaseTab } from './KnowledgeBaseTab.tsx'
|
|
24
24
|
import { OperationLogTab } from './OperationLogTab.tsx'
|
|
25
|
-
import { IntentTab } from './IntentTab.tsx'
|
|
26
25
|
import { RecallPanel } from './RecallPanel.tsx'
|
|
27
26
|
import { InfoHint } from './InfoHint.tsx'
|
|
28
27
|
import { readRemembered, remember } from './panel-state.ts'
|
|
@@ -55,7 +54,6 @@ type Selection = NodeId | null
|
|
|
55
54
|
*/
|
|
56
55
|
const VIEWS: Record<ViewId, { label: string; icon: LucideIcon }> = {
|
|
57
56
|
overview: { label: 'Agent 概览', icon: Layers3 },
|
|
58
|
-
intent: { label: '意图', icon: Workflow },
|
|
59
57
|
knowledge: { label: '知识库', icon: Database },
|
|
60
58
|
attribution: { label: '智能归因', icon: Stethoscope },
|
|
61
59
|
memory: { label: '记忆', icon: BrainCircuit },
|
|
@@ -63,14 +61,13 @@ const VIEWS: Record<ViewId, { label: string; icon: LucideIcon }> = {
|
|
|
63
61
|
operations: { label: '操作日志', icon: History },
|
|
64
62
|
}
|
|
65
63
|
|
|
66
|
-
const VIEW_ORDER: ViewId[] = ['overview', '
|
|
64
|
+
const VIEW_ORDER: ViewId[] = ['overview', 'knowledge', 'attribution', 'memory', 'eval', 'operations']
|
|
67
65
|
|
|
68
66
|
/**
|
|
69
67
|
* 说明入口只放在五个有完整产品说明的 Tab 上。它们统一落到一张总览页,`node` 是
|
|
70
68
|
* 页面内的稳定定位键;这样说明入口不会再各自开一套孤立页面。
|
|
71
69
|
*/
|
|
72
70
|
const TAB_GUIDE: Partial<Record<ViewId, { label: string; node: string }>> = {
|
|
73
|
-
intent: { label: '意图', node: 'intent' },
|
|
74
71
|
knowledge: { label: '知识库', node: 'knowledge' },
|
|
75
72
|
attribution: { label: '智能归因', node: 'attribution' },
|
|
76
73
|
memory: { label: '记忆', node: 'memory' },
|
|
@@ -210,7 +207,14 @@ export function KnowledgePanel({ visible, currentQuestion, onOpenTab, settings,
|
|
|
210
207
|
const [selectedDomain, setSelectedDomain] = useState<string | null>(null)
|
|
211
208
|
const [notice, setNotice] = useState<{ tone: 'success' | 'error'; text: string } | null>(null)
|
|
212
209
|
/** 从架构图节点跳到知识库分栏:tab + 一个递增的 nonce(同栏再点也要生效)。 */
|
|
213
|
-
const [knowledgeFocus, setKnowledgeFocus] = useState<{
|
|
210
|
+
const [knowledgeFocus, setKnowledgeFocus] = useState<{
|
|
211
|
+
tab: KnowledgeTab
|
|
212
|
+
nonce: number
|
|
213
|
+
wikiEntityId?: string
|
|
214
|
+
wikiEntityType?: string
|
|
215
|
+
} | undefined>(undefined)
|
|
216
|
+
/** Attribution and Wiki inspection must read the same selected knowledge base. */
|
|
217
|
+
const [activeKnowledgeBase, setActiveKnowledgeBase] = useState<{ id: string; name: string } | null>(null)
|
|
214
218
|
/** 设置抽屉入口。`null` = 关闭;否则定位到对应分组。 */
|
|
215
219
|
const [settingsTarget, setSettingsTarget] = useState<KnowledgeSettingsTarget | null>(null)
|
|
216
220
|
/**
|
|
@@ -374,9 +378,6 @@ export function KnowledgePanel({ visible, currentQuestion, onOpenTab, settings,
|
|
|
374
378
|
<span className={styles.srOnly} role="status" aria-live="polite">{jumpLabel ?? ''}</span>
|
|
375
379
|
</TabsContent>
|
|
376
380
|
|
|
377
|
-
<TabsContent value="intent">
|
|
378
|
-
<IntentTab visible={visible && activeTab === 'intent'} />
|
|
379
|
-
</TabsContent>
|
|
380
381
|
|
|
381
382
|
{/* `forceMount`:知识库这一栏**不要在离开 tab 时卸载**。
|
|
382
383
|
它有一整套已经拉回来的状态(库列表、文件、卡片、图谱、统计),而 Radix 的
|
|
@@ -388,6 +389,7 @@ export function KnowledgePanel({ visible, currentQuestion, onOpenTab, settings,
|
|
|
388
389
|
<KnowledgeBaseTab
|
|
389
390
|
visible={visible && activeTab === 'knowledge'}
|
|
390
391
|
onChanged={() => void refreshStats()}
|
|
392
|
+
onActiveBaseChange={setActiveKnowledgeBase}
|
|
391
393
|
{...(settings?.knowledge ? { knowledge: settings.knowledge } : {})}
|
|
392
394
|
{...(knowledgeFocus ? { focus: knowledgeFocus } : {})}
|
|
393
395
|
onOpenSettings={openSettings}
|
|
@@ -400,7 +402,12 @@ export function KnowledgePanel({ visible, currentQuestion, onOpenTab, settings,
|
|
|
400
402
|
<AttributionTab
|
|
401
403
|
visible={visible && activeTab === 'attribution'}
|
|
402
404
|
{...(currentQuestion ? { activeQuestion: currentQuestion } : {})}
|
|
405
|
+
{...(activeKnowledgeBase ? { kbId: activeKnowledgeBase.id } : {})}
|
|
403
406
|
onOpenKnowledge={() => changeTab('knowledge')}
|
|
407
|
+
onOpenWiki={card => {
|
|
408
|
+
changeTab('knowledge')
|
|
409
|
+
setKnowledgeFocus({ tab: 'wiki', nonce: Date.now(), wikiEntityId: card.entityId, wikiEntityType: card.entityType })
|
|
410
|
+
}}
|
|
404
411
|
/>
|
|
405
412
|
</TabsContent>
|
|
406
413
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* 那一栏的四个分栏(SourceRaw / Wiki / 知识图谱 / 召回重排)是**知识链路的一段**,
|
|
7
7
|
* 每一栏回答「这一段现在长什么样」。操作日志回答的是另一个问题:「**刚才那一下**
|
|
8
8
|
* 到底做了什么」—— 它横着切过所有分栏(导入落在 SourceRaw、编译落在 Wiki),
|
|
9
|
-
* 而且要看得出「同一次导入里文件与卡片的关系」。所以它跟
|
|
9
|
+
* 而且要看得出「同一次导入里文件与卡片的关系」。所以它跟 记忆 / 评测 并列,
|
|
10
10
|
* 是**时间轴上的账本**,不是链路上的一个工位。
|
|
11
11
|
*
|
|
12
12
|
* ## 一次操作一条,明细在条目里
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { useEffect, useRef, useState, type ReactNode } from 'react'
|
|
2
|
+
import { Badge, Button } from '@tita-agent/ui/components'
|
|
3
|
+
import { Progress } from '@tita-agent/ui/ui/progress'
|
|
4
|
+
import { Activity, AlertCircle, Check, Clock3, ExternalLink, LoaderCircle, Pause, Play, X } from '@tita-agent/ui/icons'
|
|
5
|
+
import { openAppPath } from '@tita-agent/ui/overlays'
|
|
6
|
+
import { PIPELINE_ACTIVE_STATES, PIPELINE_STAGE_LABEL, PIPELINE_STAGE_ORDER, type PipelineActivity, type PipelineEvent, type PipelineTask, type PipelineWorkStage } from '../pipeline/types.js'
|
|
7
|
+
import { PIPELINE_STATE_LABEL } from '../pipeline/progress.js'
|
|
8
|
+
import { requestJson } from './request.ts'
|
|
9
|
+
import { formatDateTime } from './format.ts'
|
|
10
|
+
|
|
11
|
+
const ACTIVITY_LABEL: Record<PipelineActivity['phase'], string> = {
|
|
12
|
+
preparing: '正在准备', processing: '正在处理', 'waiting-model': '等待模型返回', receiving: '正在接收模型输出',
|
|
13
|
+
validating: '正在校验结果', saving: '正在保存结果', retrying: '等待自动重试', splitting: '正在拆分长分片',
|
|
14
|
+
reused: '已复用保存结果', completed: '已保存结果', failed: '有未完成内容',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Keep observing even when raw output is collapsed. A failed read preserves the last snapshot. */
|
|
18
|
+
export function useTaskFeed(kb: string, id: string | undefined, onTask: (task: PipelineTask) => void): { events: PipelineEvent[]; failure: string } {
|
|
19
|
+
const [feed, setFeed] = useState<{ id?: string; events: PipelineEvent[]; failure: string }>({ events: [], failure: '' })
|
|
20
|
+
const callback = useRef(onTask)
|
|
21
|
+
callback.current = onTask
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!id) return
|
|
24
|
+
let active = true, busy = false, cursor = 0, terminal = false
|
|
25
|
+
setFeed({ id, events: [], failure: '' })
|
|
26
|
+
const read = async (): Promise<void> => {
|
|
27
|
+
if (busy || terminal) return
|
|
28
|
+
busy = true
|
|
29
|
+
try {
|
|
30
|
+
let more = true
|
|
31
|
+
while (active && more) {
|
|
32
|
+
const result = await requestJson<{ task: PipelineTask; events: PipelineEvent[]; next: number; hasMore: boolean }>(`/knowledge/pipeline-task?${new URLSearchParams({ kb, id, after: String(cursor) })}`)
|
|
33
|
+
if (!active) return
|
|
34
|
+
if (!result.data) throw new Error('任务响应缺少数据')
|
|
35
|
+
const page = result.data
|
|
36
|
+
cursor = page.next
|
|
37
|
+
more = page.hasMore
|
|
38
|
+
terminal = !more && !PIPELINE_ACTIVE_STATES.includes(page.task.state)
|
|
39
|
+
callback.current(page.task)
|
|
40
|
+
setFeed(current => {
|
|
41
|
+
const all = [...(current.id === id ? current.events : []), ...page.events]
|
|
42
|
+
// Streaming output must not evict all meaningful activity and warning events.
|
|
43
|
+
const keep = new Set([...all.filter(event => event.kind === 'output').slice(-600), ...all.filter(event => event.kind !== 'output').slice(-150)].map(event => event.seq))
|
|
44
|
+
return { id, events: all.filter(event => keep.has(event.seq)), failure: '' }
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
} catch (cause) { if (active) setFeed(current => ({ ...current, id, failure: cause instanceof Error ? cause.message : String(cause) })) }
|
|
48
|
+
finally { busy = false }
|
|
49
|
+
}
|
|
50
|
+
void read()
|
|
51
|
+
const timer = window.setInterval(() => { void read() }, 1200)
|
|
52
|
+
return () => { active = false; window.clearInterval(timer) }
|
|
53
|
+
}, [kb, id])
|
|
54
|
+
return feed.id === id ? feed : { events: [], failure: '' }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function duration(ms: number): string {
|
|
58
|
+
const seconds = Math.max(0, Math.floor(ms / 1000))
|
|
59
|
+
return seconds < 60 ? `${seconds} 秒` : seconds < 3600 ? `${Math.floor(seconds / 60)} 分 ${seconds % 60} 秒` : `${Math.floor(seconds / 3600)} 小时 ${Math.floor(seconds / 60) % 60} 分`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function RunSummary({ task, events, failure, busy, onAct, onFollow, onRecovery, following, queueCount, queuePosition, objectTitle }: {
|
|
63
|
+
task: PipelineTask; events: PipelineEvent[]; failure: string; busy: boolean
|
|
64
|
+
onAct: (id: string, action: string) => void; onFollow: () => void; onRecovery: (id: string) => void
|
|
65
|
+
following: boolean; queueCount: number; queuePosition: number; objectTitle?: string
|
|
66
|
+
}): ReactNode {
|
|
67
|
+
const active = PIPELINE_ACTIVE_STATES.includes(task.state)
|
|
68
|
+
const [now, setNow] = useState(Date.now)
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
setNow(Date.now())
|
|
71
|
+
if (!active) return
|
|
72
|
+
const timer = window.setInterval(() => setNow(Date.now()), 1000)
|
|
73
|
+
return () => window.clearInterval(timer)
|
|
74
|
+
}, [active, task.id])
|
|
75
|
+
const activity = task.activity
|
|
76
|
+
const lastEvent = events.at(-1)
|
|
77
|
+
const lastAt = Math.max(Date.parse(activity?.at ?? task.startedAt ?? task.queuedAt), Date.parse(lastEvent?.at ?? task.queuedAt))
|
|
78
|
+
const chunks = activity?.chunks
|
|
79
|
+
const stage = task.currentStage ?? (task.stage === 'all' ? 'extract' : task.stage)
|
|
80
|
+
const unit = stage === 'materialize' ? '个分组' : stage === 'derive' ? '个 Case' : stage === 'build' ? '次构建' : '份文档'
|
|
81
|
+
const end = task.finishedAt ? Date.parse(task.finishedAt) : now
|
|
82
|
+
const failed = ['failed', 'partial', 'interrupted'].includes(task.state)
|
|
83
|
+
const currentLabel = !active || task.state === 'paused' || task.state === 'queued' || task.state === 'pausing' || task.state === 'cancelling'
|
|
84
|
+
? PIPELINE_STATE_LABEL[task.state] : activity ? ACTIVITY_LABEL[activity.phase] : '正在运行,详细进展见本次活动'
|
|
85
|
+
return <section className="ktCurrent" aria-label="本次任务">
|
|
86
|
+
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
87
|
+
<div className="min-w-0">
|
|
88
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
89
|
+
<h2 className="m-0 text-base font-semibold text-tita-ink">{task.stage === 'all' ? '全链路处理' : `仅运行${PIPELINE_STAGE_LABEL[task.stage]}`}</h2>
|
|
90
|
+
<Badge tone={failed ? 'warning' : task.state === 'succeeded' ? 'success' : active && task.state !== 'paused' ? 'blue' : 'neutral'}>
|
|
91
|
+
{active && task.state !== 'paused' && <LoaderCircle size={12} className="motion-safe:animate-spin" />}{task.state === 'succeeded' && <Check size={12} />}{PIPELINE_STATE_LABEL[task.state]}
|
|
92
|
+
</Badge>
|
|
93
|
+
</div>
|
|
94
|
+
<p className="ktCurrentScope">本次范围:{task.stage === 'build' ? '整个知识库' : task.selection.sourceIds ? `${task.selection.sourceIds.length} 份指定来源` : task.stage === 'materialize' ? '待物化分组' : '全部来源'}{task.selection.groupKeys ? ` · ${task.selection.groupKeys.length} 个指定分组` : ''} · {formatDateTime(task.startedAt ?? task.queuedAt)}{task.selection.force ? ' · 强制重跑' : ''}</p>
|
|
95
|
+
</div>
|
|
96
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
97
|
+
{active && <>
|
|
98
|
+
<Button disabled={busy || task.state === 'pausing' || task.state === 'cancelling'} onClick={() => onAct(task.id, task.state === 'paused' ? 'resume' : 'pause')}>
|
|
99
|
+
{task.state === 'paused' ? <Play size={13} /> : <Pause size={13} />}{task.state === 'paused' ? '继续' : task.state === 'pausing' ? '等待暂停' : '暂停'}
|
|
100
|
+
</Button>
|
|
101
|
+
<Button variant="ghost" disabled={busy || task.state === 'cancelling'} onClick={() => onAct(task.id, 'cancel')}><X size={13} />停止</Button>
|
|
102
|
+
</>}
|
|
103
|
+
{!active && task.recoveryTaskId && <Button onClick={() => onRecovery(task.recoveryTaskId!)}>查看自动续跑<ExternalLink size={12} /></Button>}
|
|
104
|
+
{!active && !task.recoveryTaskId && task.state !== 'succeeded' && <Button disabled={busy} onClick={() => onAct(task.id, 'retry')}><Play size={13} />继续未完成部分</Button>}
|
|
105
|
+
{active && !following && task.currentStage && <Button variant="ghost" onClick={onFollow}>返回运行阶段</Button>}
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
<div className="ktCurrentWork" role="status" aria-live="polite">
|
|
109
|
+
<strong>{PIPELINE_STAGE_LABEL[stage]}{activity?.objectTitle || objectTitle ? ` · ${activity?.objectTitle ?? objectTitle}` : ''}</strong>
|
|
110
|
+
<span><Activity size={13} />{currentLabel}{activity?.attempt && activity.attempt > 1 ? ` · 第 ${activity.attempt} 次尝试` : ''}</span>
|
|
111
|
+
</div>
|
|
112
|
+
<div className="ktCurrentCounts">
|
|
113
|
+
<span>已处理 <strong>{task.done}/{task.total || '待确定'}</strong> {unit}</span>
|
|
114
|
+
<span>成功 {Math.max(0, task.done - task.failed)} · 复用 {task.reused} · 失败 {task.failed}</span>
|
|
115
|
+
{chunks && <span>分片已保存 <strong>{chunks.saved}/{chunks.total}</strong> · 复用 {chunks.reused} · 失败 {chunks.failed}{chunks.total !== chunks.initial ? ` · 初始 ${chunks.initial} 片,计划已拆分` : ''}</span>}
|
|
116
|
+
</div>
|
|
117
|
+
{(chunks?.total || task.total > 0) ? <Progress
|
|
118
|
+
value={chunks ? chunks.saved : task.done} max={chunks?.total || task.total}
|
|
119
|
+
aria-label={chunks ? '当前文档已保存分片进度' : '当前阶段已处理进度'}
|
|
120
|
+
getValueLabel={(value, max) => `${value}/${max}${chunks ? ' 片已保存' : ` ${unit}已处理`}`}
|
|
121
|
+
/> : null}
|
|
122
|
+
<div className="ktCurrentTiming">
|
|
123
|
+
<span><Clock3 size={12} />{task.startedAt ? '已用时(含暂停)' : '已等待'} {duration(end - Date.parse(task.startedAt ?? task.queuedAt))}</span>
|
|
124
|
+
{active && task.state !== 'paused' && <span>最近活动 {duration(now - lastAt)}前</span>}
|
|
125
|
+
{queueCount > 0 && <span>另有 {queueCount} 个任务排队</span>}
|
|
126
|
+
{task.state === 'queued' && queuePosition > 0 && <span>本次在等待队列第 {queuePosition} 位</span>}
|
|
127
|
+
{task.model.model && <span>模型:{task.model.model}</span>}
|
|
128
|
+
{task.model.maxTokens !== undefined && <span>单次最大输出:{task.model.maxTokens.toLocaleString('zh-CN')} token</span>}
|
|
129
|
+
{task.extractionTimeoutMinutes !== undefined && (task.stage === 'extract' || task.stage === 'all') && <span>文档抽取时限:{task.extractionTimeoutMinutes} 分钟 / 每轮</span>}
|
|
130
|
+
</div>
|
|
131
|
+
{active && <p className="ktCurrentHint">{task.state === 'pausing' || task.state === 'cancelling' ? task.summary : '暂停在安全点生效;停止会保留已保存的结果。关闭页面后任务继续运行。'}</p>}
|
|
132
|
+
{!active && <div className="ktCompletion">
|
|
133
|
+
<p>{task.summary}</p>
|
|
134
|
+
<ul>{PIPELINE_STAGE_ORDER.filter(id => task.results?.[id]).map(id => {
|
|
135
|
+
const result = task.results![id]!
|
|
136
|
+
return <li key={id}><strong>{PIPELINE_STAGE_LABEL[id]}</strong><span>{result.summary ?? `成功 ${result.done - result.failed}/${result.total} · 复用 ${result.reused} · 失败 ${result.failed}`}</span></li>
|
|
137
|
+
})}</ul>
|
|
138
|
+
<p>{task.results?.build && !task.results.build.failed ? task.results.build.skipped ? '检索索引无变化,继续使用已有版本。' : '本次检索索引已发布。' : '本次未更新检索索引。抽取、归一或物化完成后,仍需构建索引才能更新检索结果。'}</p>
|
|
139
|
+
</div>}
|
|
140
|
+
{failure && <p className="ktError" role="alert"><AlertCircle size={14} /><span>进度连接中断,正在自动重连。保留上次状态;任务是否仍在运行需重连后确认。{failure}</span></p>}
|
|
141
|
+
</section>
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function RunActivity({ task, events, onInspect }: { task: PipelineTask; events: PipelineEvent[]; onInspect: (id: string, stage?: PipelineWorkStage) => void }): ReactNode {
|
|
145
|
+
const [showAll, setShowAll] = useState(false)
|
|
146
|
+
const recent = events.filter(event => event.kind === 'progress' || event.kind === 'warning').slice(-8).reverse()
|
|
147
|
+
return <div className="ktActivity">
|
|
148
|
+
<h3>本次活动</h3>
|
|
149
|
+
<p className="ktActivityHint">保存、复用、拆分与重试会记录在这里。</p>
|
|
150
|
+
{task.warnings.length > 0 && <div className="ktWarnings">
|
|
151
|
+
<strong><AlertCircle size={13} />{task.warnings.length} 条需要关注</strong>
|
|
152
|
+
<ul>{(showAll ? task.warnings : task.warnings.slice(0, 3)).map((warning, index) => <li key={index}>{warning}</li>)}</ul>
|
|
153
|
+
{task.warnings.length > 3 && <Button variant="ghost" onClick={() => setShowAll(value => !value)}>{showAll ? '收起提醒' : '查看全部提醒'}</Button>}
|
|
154
|
+
</div>}
|
|
155
|
+
{recent.length ? <ol className="ktActivityList">{recent.map(event => <li key={event.seq} data-warning={event.kind === 'warning' || undefined}>
|
|
156
|
+
<time dateTime={event.at}>{new Date(event.at).toLocaleTimeString('zh-CN', { hour12: false })}</time>
|
|
157
|
+
<div><p>{event.text}</p>{(event.activity?.objectId || event.sourceId) && event.stage !== 'derive' && <Button variant="ghost" size="sm" className="ktLink" onClick={() => onInspect(event.activity?.objectId ?? event.sourceId!, event.stage)}>定位处理对象</Button>}</div>
|
|
158
|
+
</li>)}</ol> : <p className="ktActivityHint">{task.state === 'queued' ? '任务已入队,轮到后开始处理。' : '正在读取活动记录;历史任务可能只有原始输出。'}</p>}
|
|
159
|
+
</div>
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function RawTaskOutput({ kb, task, events, stage, onOpenFailed }: { kb: string; task: PipelineTask; events: PipelineEvent[]; stage: PipelineWorkStage; onOpenFailed: (message: string) => void }): ReactNode {
|
|
163
|
+
const scoped = events.filter(event => event.stage === stage || (!event.stage && task.stage === stage))
|
|
164
|
+
const output = scoped.filter(event => event.kind === 'output').map(event => event.text).join('').slice(-200000)
|
|
165
|
+
const reports = scoped.filter(event => event.kind === 'report')
|
|
166
|
+
return <div className="ktOutputBody">
|
|
167
|
+
<p>仅显示本次任务在当前阶段的模型原始输出。进展与警告见「本次活动」。</p>
|
|
168
|
+
<pre className="ktPre" tabIndex={0} aria-label={`${PIPELINE_STAGE_LABEL[stage]}原始输出`}>{output || '本阶段尚无原始模型输出。'}</pre>
|
|
169
|
+
<div className="ktReportsInline">{reports.map(event => <Button key={event.seq} variant="ghost" className="ktLink" onClick={() => {
|
|
170
|
+
void openAppPath(`/knowledge/pages/run?${new URLSearchParams({ kb, run: event.text })}`).then(opened => { if (!opened) onOpenFailed('无法打开阶段轨迹,请重试。') })
|
|
171
|
+
}}>查看阶段轨迹<ExternalLink size={12} /></Button>)}</div>
|
|
172
|
+
</div>
|
|
173
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { Fragment, useEffect, useRef, useState, type ReactNode } from 'react'
|
|
13
|
-
import { ArrowRight, ArrowUpDown,
|
|
13
|
+
import { ArrowRight, ArrowUpDown, CircleHelp, GitBranch, Layers3, Play, Search, Shapes } from '@tita-agent/ui/icons'
|
|
14
14
|
import { Button, Card, Input, cn } from '@tita-agent/ui/components'
|
|
15
15
|
import { ExpansionGraph } from './ExpansionGraph.tsx'
|
|
16
16
|
import { requestJson } from './request.ts'
|
|
@@ -91,7 +91,6 @@ export interface QueryAxisMatchView {
|
|
|
91
91
|
|
|
92
92
|
export interface QueryAnalysisView {
|
|
93
93
|
query: string
|
|
94
|
-
intent: { id: string; confidence: number }
|
|
95
94
|
axes: Record<QueryAxisKind, QueryAxisMatchView[]>
|
|
96
95
|
coordinates: Array<{ cardKey: string; score: number; matchedAxes: QueryAxisKind[] }>
|
|
97
96
|
unresolved: QueryAxisKind[]
|
|
@@ -134,7 +133,7 @@ export interface RecallResultView {
|
|
|
134
133
|
abstention?: string
|
|
135
134
|
diagnosticsReason?: string
|
|
136
135
|
encoder?: { ok: boolean; reason?: string }
|
|
137
|
-
/**
|
|
136
|
+
/** 本次查询的 Object / Action / Scene 坐标解析结果。 */
|
|
138
137
|
queryAnalysis?: QueryAnalysisView
|
|
139
138
|
hits: RecallHitView[]
|
|
140
139
|
context: ContextItemView[]
|
|
@@ -197,20 +196,15 @@ export function LedgerList({ rows, scoreLabel, droppedLabel, emptyLabel }: {
|
|
|
197
196
|
</ul>
|
|
198
197
|
}
|
|
199
198
|
|
|
200
|
-
const QUERY_INTENT_LABEL: Record<string, string> = {
|
|
201
|
-
lookup: '查事实', explain: '解释概念', procedure: '执行步骤', troubleshoot: '排查问题', compare: '比较差异', unknown: '未识别',
|
|
202
|
-
}
|
|
203
|
-
|
|
204
199
|
const QUERY_AXIS_LABEL: Record<QueryAxisKind, string> = { object: 'Object', action: 'Action', scene: 'Scene' }
|
|
205
200
|
|
|
206
|
-
/**
|
|
201
|
+
/** 运行时解析当前查询中的 Object、Action、Scene 坐标。 */
|
|
207
202
|
export function QueryAnalysisSummary({ analysis }: { analysis: QueryAnalysisView }): ReactNode {
|
|
208
203
|
const axisKinds: QueryAxisKind[] = ['object', 'action', 'scene']
|
|
209
204
|
const matchedCount = axisKinds.reduce((total, kind) => total + analysis.axes[kind].length, 0)
|
|
210
|
-
return <section className={styles.recallAnalysis} aria-label="
|
|
205
|
+
return <section className={styles.recallAnalysis} aria-label="本次查询坐标解析">
|
|
211
206
|
<div className={styles.recallAnalysisHead}>
|
|
212
|
-
<div className={styles.recallAnalysisTitle}><
|
|
213
|
-
<span className={styles.recallAnalysisIntent}>{QUERY_INTENT_LABEL[analysis.intent.id] ?? analysis.intent.id}<b>{analysis.intent.confidence.toFixed(2)}</b></span>
|
|
207
|
+
<div className={styles.recallAnalysisTitle}><GitBranch size={13} /><div><h4>坐标解析</h4><span>Query → Object / Action / Scene</span></div></div>
|
|
214
208
|
</div>
|
|
215
209
|
<div className={styles.recallAnalysisAxes}>
|
|
216
210
|
{axisKinds.map(kind => {
|
|
@@ -537,7 +531,6 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
|
|
|
537
531
|
}
|
|
538
532
|
queryAnalysis?: {
|
|
539
533
|
query?: string
|
|
540
|
-
intent?: { id?: string; confidence?: number }
|
|
541
534
|
axes?: Partial<Record<QueryAxisKind, QueryAxisMatchView[]>>
|
|
542
535
|
coordinates?: Array<{ cardKey?: string; score?: number; matchedAxes?: QueryAxisKind[] }>
|
|
543
536
|
unresolved?: QueryAxisKind[]
|
|
@@ -557,10 +550,6 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
|
|
|
557
550
|
const data = payload.data ?? {}
|
|
558
551
|
const queryAnalysis: QueryAnalysisView | undefined = data.queryAnalysis === undefined ? undefined : {
|
|
559
552
|
query: data.queryAnalysis.query ?? trimmed,
|
|
560
|
-
intent: {
|
|
561
|
-
id: data.queryAnalysis.intent?.id ?? 'unknown',
|
|
562
|
-
confidence: Number(data.queryAnalysis.intent?.confidence ?? 0),
|
|
563
|
-
},
|
|
564
553
|
axes: {
|
|
565
554
|
object: data.queryAnalysis.axes?.object ?? [],
|
|
566
555
|
action: data.queryAnalysis.axes?.action ?? [],
|
package/src/client/TabGuide.tsx
CHANGED
|
@@ -71,11 +71,7 @@ interface GuideNode {
|
|
|
71
71
|
/** 与服务端抽取提示词共用同一份固定坐标契约,页面不再维护第二份文案。 */
|
|
72
72
|
const AXIS_JUDGMENT_PROMPT = AXIS_CONTRACT_LINES.join('\n')
|
|
73
73
|
|
|
74
|
-
const TAB_SECTIONS: Record<'
|
|
75
|
-
intent: [
|
|
76
|
-
{ title: '它解决什么问题', points: ['把“用户想做什么”从聊天文本中单独提出来。', '意图记录保留置信度、槽位和来源,方便复盘,不直接代替业务控制流。'] },
|
|
77
|
-
{ title: '与其他模块怎么协作', points: ['意图会参与知识召回和上下文组织,帮助系统判断哪些内容相关。', '它是检索的输入之一;坐标、关键词和向量仍然共同决定召回结果。'] },
|
|
78
|
-
],
|
|
74
|
+
const TAB_SECTIONS: Record<'attribution' | 'memory' | 'eval', GuideSection[]> = {
|
|
79
75
|
attribution: [
|
|
80
76
|
{ title: '它解决什么问题', points: ['不是只给出“可能原因”,而是保留从现象到结论的证据链。', '每次排查是一条 Event,Event 可以关联 SourceRaw、Knowledge 和 WikiCard。'] },
|
|
81
77
|
{ title: '具体步骤', points: ['记录异常症状与影响范围。', '调用知识召回和数据工具,生成候选原因与排查动作。', '把检查结果、证据和结论写入 Event,保留可回放的关系链。'] },
|
|
@@ -103,7 +99,7 @@ const CONCEPT_GUIDES: GuideNode[] = [
|
|
|
103
99
|
{ label: 'Document', text: '来源。保存原文、事实和出处,是后续抽取与追溯的依据。' },
|
|
104
100
|
{ label: 'Knowledge', text: '可复用内容。一条可独立检索、验证或执行的最小知识单元。' },
|
|
105
101
|
{ label: 'Event', text: 'Knowledge 的一次使用或验证。记录实际过程、结果、证据和反馈。' },
|
|
106
|
-
{ label: 'WikiCard', text: 'Knowledge 的坐标化组织。按 Object × Action × Scene
|
|
102
|
+
{ label: 'WikiCard', text: 'Knowledge 的坐标化组织。按 Object × Action × Scene 分别组织 Wiki Fact、Wiki Way,完整坐标与类型共同确定身份。相关文件通过双链按需读取。' },
|
|
107
103
|
],
|
|
108
104
|
},
|
|
109
105
|
{
|
|
@@ -193,7 +189,7 @@ const COMPILE_PROMPT_SECTIONS: GuideSection[] = [
|
|
|
193
189
|
},
|
|
194
190
|
]
|
|
195
191
|
|
|
196
|
-
const ARCHITECTURE_SECTIONS: Partial<Record<NodeId, GuideSection[]>> = {
|
|
192
|
+
const ARCHITECTURE_SECTIONS: Partial<Record<NodeId, GuideSection[]>> = { compile: COMPILE_PROMPT_SECTIONS }
|
|
197
193
|
|
|
198
194
|
const ARCHITECTURE_GUIDES: GuideNode[] = NODE_ORDER.map((id): GuideNode => {
|
|
199
195
|
const meta = NODE_META[id]
|
|
@@ -249,14 +249,13 @@ const RECALL_STAGES: RecallStage[] = [
|
|
|
249
249
|
id: 'parse',
|
|
250
250
|
icon: FileText,
|
|
251
251
|
tone: 'violet',
|
|
252
|
-
name: '
|
|
253
|
-
io: 'User Query →
|
|
252
|
+
name: '坐标解析',
|
|
253
|
+
io: 'User Query → Object / Action / Scene',
|
|
254
254
|
body: [
|
|
255
|
-
'
|
|
255
|
+
'用 Axis Registry 的 canonical 名称和 alias 解析 Object、Action、Scene。',
|
|
256
256
|
'解析结果保留标准 id、置信度和歧义候选;匹配不唯一时不猜,未解析的原文仍交给关键词和向量路线。',
|
|
257
|
-
'`Intent` 表示用户要完成的事,例如查原因、核事实、回顾历史或执行修复;它不是 Coordinate 的第四个分量。',
|
|
258
257
|
],
|
|
259
|
-
boundary: '
|
|
258
|
+
boundary: '解析失败不应让结果归零。坐标补充候选,未解析的原文继续进入关键词、向量和重排。',
|
|
260
259
|
},
|
|
261
260
|
{
|
|
262
261
|
id: 'candidate',
|
|
@@ -276,13 +275,13 @@ const RECALL_STAGES: RecallStage[] = [
|
|
|
276
275
|
icon: GitBranch,
|
|
277
276
|
tone: 'violet',
|
|
278
277
|
name: 'Coordinate Card 召回',
|
|
279
|
-
io: '
|
|
278
|
+
io: 'Object / Action / Scene → Wiki Card → 候选 Section',
|
|
280
279
|
body: [
|
|
281
|
-
'高置信度识别出 OAS
|
|
280
|
+
'高置信度识别出 OAS 后,找到对应 Wiki Card,再把 Card 拆成候选 Section。',
|
|
282
281
|
'匹配不唯一时保留多个坐标候选;Coordinate 是强先验,不直接替代 Section 重排。',
|
|
283
282
|
'Coordinate 路、FTS 路和 Vector 路最终都统一成 Section 候选,才能使用同一套重排逻辑。',
|
|
284
283
|
],
|
|
285
|
-
boundary: 'Coordinate 是三个轴 id
|
|
284
|
+
boundary: 'Coordinate 是三个轴 id 的复合键;空轴不会被猜成通用范围。',
|
|
286
285
|
},
|
|
287
286
|
{
|
|
288
287
|
id: 'merge',
|
|
@@ -304,7 +303,7 @@ const RECALL_STAGES: RecallStage[] = [
|
|
|
304
303
|
name: 'Section 统一重排',
|
|
305
304
|
io: '统一候选 Section → Top K Section',
|
|
306
305
|
body: [
|
|
307
|
-
'用 Cross-Encoder 判断 Query 与 Section
|
|
306
|
+
'用 Cross-Encoder 判断 Query 与 Section 的语义相关性,并结合坐标解析结果排序。',
|
|
308
307
|
'重排对象是 Section,不是整张 Card;这样每条结果都能说明“具体命中了哪一章”。',
|
|
309
308
|
'先取足够大的 `sectionTopK`,再按 Card 归并,避免同一张 Card 占满最终结果。',
|
|
310
309
|
],
|
|
@@ -352,8 +351,8 @@ const COMPILE_PIPELINE: PipelineGroup[] = [
|
|
|
352
351
|
]
|
|
353
352
|
|
|
354
353
|
const RECALL_PIPELINE: PipelineGroup[] = [
|
|
355
|
-
{ id: '
|
|
356
|
-
{ id: 'candidate-recall', title: '三路候选生成', input: '
|
|
354
|
+
{ id: 'coordinate-parse', title: '坐标解析', input: 'User Query', output: 'Object / Action / Scene', tone: 'violet', stageIds: ['parse'] },
|
|
355
|
+
{ id: 'candidate-recall', title: '三路候选生成', input: 'Query + 坐标', output: 'Card + Section 候选', tone: 'blue', stageIds: ['coordinate', 'candidate'] },
|
|
357
356
|
{ id: 'section-merge', title: 'Section 候选归并', input: '三路候选', output: '统一 Section 池', tone: 'violet', stageIds: ['merge'] },
|
|
358
357
|
{ id: 'section-rerank', title: 'Section 统一重排', input: '统一 Section 池', output: 'Top K Section', tone: 'green', stageIds: ['rerank'] },
|
|
359
358
|
{ id: 'card-merge', title: 'Wiki Card 归并', input: 'Top K Section', output: 'Top 5 完整 Wiki Card', tone: 'amber', stageIds: ['section'] },
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* 一列:图被挤窄、面板和图各占一块表面,读起来是两件事。现在它是画布内的浮层,
|
|
6
6
|
* 由 `AgentArchitectureGraph` 的 `inspector` 插槽承载,壳(边框、圆角、投影)由浮层给。
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* 这里**没有设置项**,一个都没有。设置已经回到它们真正生效的地方(知识库的各个分栏),
|
|
9
|
+
* 面板只回答三个问题:这个节点是干什么的、它现在的数字是多少、
|
|
10
10
|
* 它实现到哪一步,以及「要改它就去哪」。
|
|
11
11
|
*
|
|
12
12
|
* 空态已经撤掉:画布上不再有「未选中」这一态 —— 没选中时浮层根本不出现,
|
|
@@ -123,7 +123,7 @@ export function NodeLogicPanel({ selected, stats, version, onJump, onOpenTab, on
|
|
|
123
123
|
? <Button variant="default" size="sm" onClick={() => onJump(jump.tab, jump.knowledgeTab)}>
|
|
124
124
|
去配置<ArrowRight size={14} />
|
|
125
125
|
</Button>
|
|
126
|
-
: <p className={styles.archPanelHint}><CircleHelp size={13} />这个节点没有可配置页:{meta.status === 'host' ? '它由宿主提供' : meta.status === 'planned' ? '
|
|
126
|
+
: <p className={styles.archPanelHint}><CircleHelp size={13} />这个节点没有可配置页:{meta.status === 'host' ? '它由宿主提供' : meta.status === 'planned' ? '它还没实现,当前只记录设计方案' : '它的行为由代码决定,没有可调参数'}。</p>}
|
|
127
127
|
{(selected === 'answer' || selected === 'data-sheet' || selected === 'data-canvas') && onOpenTab && <div className={styles.archPanelActions}>
|
|
128
128
|
<Button variant="outline" size="sm" onClick={() => onOpenTab('data-sheet')}><ChevronRight size={13} />打开 Table</Button>
|
|
129
129
|
{/* 画布与 Table 并列,都是可编辑的工作区产物。 */}
|
package/src/client/arch/model.ts
CHANGED
|
@@ -94,7 +94,7 @@ export const LANE_HEAD_H = 30
|
|
|
94
94
|
* 带名条**只占自己内容的宽度**,不铺满整条带。
|
|
95
95
|
*
|
|
96
96
|
* 铺满的话它就是一块横贯全图的障碍物,垂直的跨链路走线只能绕到它的左边或右边 ——
|
|
97
|
-
*
|
|
97
|
+
* 一条跨链路反馈本来要从「编译」与「构建」之间的列缝里直穿 B 带,
|
|
98
98
|
* 被逼成「上、左、上、右」四段绕行,而且正好从带名文字上穿过去。缩成一块牌子之后,
|
|
99
99
|
* 垂直走线从牌子右边自由通过。
|
|
100
100
|
*/
|
|
@@ -206,7 +206,6 @@ interface Slot { lane: number; col: number; /** 在竖排里的序号(0 起)
|
|
|
206
206
|
|
|
207
207
|
export const ARCH_SLOTS: Record<NodeId, Slot> = {
|
|
208
208
|
conversation: { lane: 0, col: 0 },
|
|
209
|
-
intent: { lane: 0, col: 1 },
|
|
210
209
|
runtime: { lane: 0, col: 2 },
|
|
211
210
|
answer: { lane: 0, col: 3 },
|
|
212
211
|
'data-sheet': { lane: 0, col: 4, stack: 0 },
|
|
@@ -279,8 +278,7 @@ export interface ArchEdge {
|
|
|
279
278
|
*/
|
|
280
279
|
export const ARCH_EDGES: ArchEdge[] = [
|
|
281
280
|
// A · 用户操作链路
|
|
282
|
-
{ id: 'a1', source: 'conversation', target: '
|
|
283
|
-
{ id: 'a2', source: 'intent', target: 'runtime', kind: 'flow' },
|
|
281
|
+
{ id: 'a1', source: 'conversation', target: 'runtime', kind: 'flow' },
|
|
284
282
|
{ id: 'a3', source: 'runtime', target: 'answer', kind: 'flow' },
|
|
285
283
|
{ id: 'a4', source: 'answer', target: 'data-sheet', kind: 'branch' },
|
|
286
284
|
// 画布与 Table 是同一格的两种可编辑输出产物。
|
|
@@ -312,7 +310,6 @@ export const ARCH_EDGES: ArchEdge[] = [
|
|
|
312
310
|
* 「记忆自己就能变成卡片」,那正是它不该说的话。
|
|
313
311
|
*/
|
|
314
312
|
{ id: 'x2', source: 'daily-update', target: 'source-raw', kind: 'feedback', label: '沉淀进知识库' },
|
|
315
|
-
{ id: 'x3', source: 'memory-quality', target: 'intent', kind: 'feedback', label: '反馈到意图理解' },
|
|
316
313
|
{ id: 'x4', source: 'context', target: 'runtime', kind: 'inject', label: 'Agent Context 返回' },
|
|
317
314
|
]
|
|
318
315
|
|
|
@@ -333,7 +330,7 @@ export const ARCH_EDGES: ArchEdge[] = [
|
|
|
333
330
|
* 反过来若按「`branch` 就走总线」写,多对一的那种(情节 / 操作记忆 → 记忆质量检查,
|
|
334
331
|
* 那是两条 `flow`)就漏了,而它们看着是同一毛病。
|
|
335
332
|
*
|
|
336
|
-
*
|
|
333
|
+
* 跨链路的长线不在这里:它们不同带,本来就有自己的正交路由与避障。
|
|
337
334
|
*/
|
|
338
335
|
const BUS_EDGE_IDS: ReadonlySet<string> = (() => {
|
|
339
336
|
const local = ARCH_EDGES.filter(edge => {
|
|
@@ -386,103 +383,6 @@ export function busControlPoints(edge: ArchEdge): Array<[number, number]> | null
|
|
|
386
383
|
return [[trunkX, from.y], [trunkX, to.y]]
|
|
387
384
|
}
|
|
388
385
|
|
|
389
|
-
// ----------------------------------------------------------------- 手工走线的跨链路边
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* 跨链路边里**唯一一条自己给拐点的**:「记忆质量检查 → 意图」。
|
|
393
|
-
*
|
|
394
|
-
* ## 为什么其余三条不用管(2026-10-05 实测)
|
|
395
|
-
*
|
|
396
|
-
* 四条跨链路边都交给 G6 的正交路由器(`shortest-path` + `enableObstacleAvoidance`)。
|
|
397
|
-
* 那个「避障」**看不见这张图的卡片**:卡片是 HTML 节点(`cardHtml` 产出的是 DOM),
|
|
398
|
-
* 路由器手里只有它自己认得的图形。另外三条碰巧走到了空处 —— 实测的走线是:
|
|
399
|
-
* 回答 → 用户交互绕到最右侧外圈、每日更新 → SourceRaw 走 B 带下沿、
|
|
400
|
-
* 重排 → Runtime 走 A/B 带之间的通道,三段都不压卡片。
|
|
401
|
-
*
|
|
402
|
-
* 「记忆质量检查 → 意图」不走运:它的最短路径是「从卡片正上方升到目标那一行,再横着
|
|
403
|
-
* 进目标」。那条竖线从「构建」直穿到「Agent Runtime」—— 两张卡把它中间的两段挡住,
|
|
404
|
-
* 屏幕上剩下的是一条断线;而剩下的那一头正好落在「意图与上下文」的**右边**,
|
|
405
|
-
* 与带内流量「意图 → Runtime」的箭头合成一对左右箭头,读起来像两个节点在互相指。
|
|
406
|
-
*
|
|
407
|
-
* ## 这条路怎么走
|
|
408
|
-
*
|
|
409
|
-
* 两个判断:
|
|
410
|
-
* 1. **竖着走 1/2 列之间的列缝**(`columnGapX`)。列缝是带内唯一不压卡片的纵向通道;
|
|
411
|
-
* 从「记忆质量检查」的左边出去、贴着列缝升到 A/B 带之间的通道,全程没有一张卡。
|
|
412
|
-
* 2. **从下方进入「意图与上下文」**。带内流量已经从它的右边出去了,回程走它的下边 ——
|
|
413
|
-
* 两条线各占一条边,那对左右箭头就不会再出现。
|
|
414
|
-
*/
|
|
415
|
-
interface CrossRoute {
|
|
416
|
-
controlPoints: Array<[number, number]>
|
|
417
|
-
/** 标签压在第几段的中点(0 起)。这一段上不许有卡片 —— 标签带白底,压上去就是糊掉一张卡。 */
|
|
418
|
-
labelSegment: number
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
const CROSS_ROUTES: Record<string, CrossRoute> = {
|
|
422
|
-
x3: {
|
|
423
|
-
controlPoints: [
|
|
424
|
-
/* 1. 出「记忆质量检查」的左边,落到 1/2 列缝上(与源节点同高 → 出口在左边中点)。 */
|
|
425
|
-
[columnGapX(1), laneCenter(2)],
|
|
426
|
-
/* 2. 沿列缝升到 A/B 带之间的通道。 */
|
|
427
|
-
[columnGapX(1), laneGapY(0)],
|
|
428
|
-
/* 3. 在通道里横着走到「意图与上下文」正下方(与目标同 x → 箭头落在下边中点)。 */
|
|
429
|
-
[columnX(1), laneGapY(0)],
|
|
430
|
-
],
|
|
431
|
-
/* 通道那一段(第 2 段)没有卡片,标签放它中点。 */
|
|
432
|
-
labelSegment: 2,
|
|
433
|
-
},
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/** 射线(节点中心 → 目标点)与节点盒子边界的交点 —— 与 G6 取两端出口的规则一致。 */
|
|
437
|
-
function boundaryOf(id: NodeId, toward: [number, number]): [number, number] {
|
|
438
|
-
const box = boxOf(id)
|
|
439
|
-
const dx = toward[0] - box.x
|
|
440
|
-
const dy = toward[1] - box.y
|
|
441
|
-
const scale = Math.min(
|
|
442
|
-
dx === 0 ? Number.POSITIVE_INFINITY : (box.width / 2) / Math.abs(dx),
|
|
443
|
-
dy === 0 ? Number.POSITIVE_INFINITY : (box.height / 2) / Math.abs(dy),
|
|
444
|
-
)
|
|
445
|
-
return [box.x + dx * scale, box.y + dy * scale]
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* 一条走线的**完整折线**:首尾是节点边界点(G6 自己算的那两个点),中间是拐点。
|
|
450
|
-
*
|
|
451
|
-
* 它只服务一件事 —— 算标签的弧长比例。绘制用的仍然只有 `controlPoints`:两端由 G6 取,
|
|
452
|
-
* 我们复算一遍是为了知道标签会落在哪一段上。
|
|
453
|
-
*/
|
|
454
|
-
export function routePoints(edge: Pick<ArchEdge, 'source' | 'target'>, controlPoints: Array<[number, number]>): Array<[number, number]> {
|
|
455
|
-
return [
|
|
456
|
-
boundaryOf(edge.source, controlPoints[0]),
|
|
457
|
-
...controlPoints,
|
|
458
|
-
boundaryOf(edge.target, controlPoints[controlPoints.length - 1]),
|
|
459
|
-
]
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* 折线第 `segment` 段中点对应的弧长比例(0..1),给 G6 的 `labelPlacement`。
|
|
464
|
-
*
|
|
465
|
-
* 为什么不用默认的中点:G6 把标签摆在**弧长中点**上,而跨链路走线的中点常常正好落在
|
|
466
|
-
* 卡片行上(竖线最长,它的中点就在卡片高度上)。标签带白底,压上去就是把一张卡片糊掉一块。
|
|
467
|
-
*/
|
|
468
|
-
export function labelRatioOf(points: Array<[number, number]>, segment: number): number {
|
|
469
|
-
const lengths = points.slice(1).map((point, index) =>
|
|
470
|
-
Math.hypot(point[0] - points[index]![0], point[1] - points[index]![1]))
|
|
471
|
-
const total = lengths.reduce((sum, value) => sum + value, 0)
|
|
472
|
-
const before = lengths.slice(0, segment).reduce((sum, value) => sum + value, 0)
|
|
473
|
-
return (before + lengths[segment]! / 2) / total
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/** 这个 id 的跨链路边是否有手工走线。测试与 `toGraphData` 读同一个判据。 */
|
|
477
|
-
export function crossRouteOf(edge: Pick<ArchEdge, 'id' | 'source' | 'target'>): { controlPoints: Array<[number, number]>; labelPlacement?: number } | null {
|
|
478
|
-
const route = CROSS_ROUTES[edge.id]
|
|
479
|
-
if (!route) return null
|
|
480
|
-
return {
|
|
481
|
-
controlPoints: route.controlPoints,
|
|
482
|
-
labelPlacement: labelRatioOf(routePoints(edge, route.controlPoints), route.labelSegment),
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
386
|
/**
|
|
487
387
|
* 边的外观。灰 / 蓝两档,没有第三档色相。
|
|
488
388
|
*
|
|
@@ -833,16 +733,8 @@ export function toGraphData(stats: KnowledgeStats, version: number, selected: No
|
|
|
833
733
|
endArrow: true, endArrowType: 'triangle', endArrowSize: 8,
|
|
834
734
|
}
|
|
835
735
|
if (CROSS_LANE.includes(edge.kind)) {
|
|
836
|
-
const route = crossRouteOf(edge)
|
|
837
736
|
style.radius = 10
|
|
838
|
-
|
|
839
|
-
// 手工走线的那一条:拐点是我们算的,**不要路由器**(见 `CROSS_ROUTES` 那段说明)。
|
|
840
|
-
style.controlPoints = route.controlPoints
|
|
841
|
-
if (route.labelPlacement !== undefined) style.labelPlacement = route.labelPlacement
|
|
842
|
-
} else {
|
|
843
|
-
// 其余三条:正交路由 + 自动避障。它们实测都走在空处(见 `CROSS_ROUTES` 那段说明)。
|
|
844
|
-
style.router = { type: 'shortest-path', enableObstacleAvoidance: true, offset: 14, gridSize: 8 }
|
|
845
|
-
}
|
|
737
|
+
style.router = { type: 'shortest-path', enableObstacleAvoidance: true, offset: 14, gridSize: 8 }
|
|
846
738
|
} else if (busControlPoints(edge) !== null) {
|
|
847
739
|
// 总线上的边:拐点是我们算的,**不要路由器** —— 路由器每条边各找一个出口,
|
|
848
740
|
// 那正是「三根箭头从三个高度出来、各自还带一个小台阶」的原因。
|
|
@@ -938,7 +830,7 @@ export function recordRows(id: NodeId, stats: KnowledgeStats, version: number):
|
|
|
938
830
|
rows.push(['模型 / 设备', `${stats.embeddingModel ?? '本地 embedding'} · ${stats.embeddingDevice ?? 'auto'}`])
|
|
939
831
|
break
|
|
940
832
|
case 'interaction':
|
|
941
|
-
rows.push(['
|
|
833
|
+
rows.push(['交互采集', '待实现'])
|
|
942
834
|
rows.push(['memory/ 落盘', `${memoryCountOf(stats)} 个文件`])
|
|
943
835
|
break
|
|
944
836
|
case 'episodic-memory':
|