@gt-fe/eap-runtime 0.0.20 → 0.0.22
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 +4 -3
- package/dist/api/handlers/invoke.handler.d.ts +7 -0
- package/dist/api/handlers/invoke.handler.js +52 -10
- package/dist/api/handlers/stream.handler.d.ts +6 -0
- package/dist/api/handlers/stream.handler.js +60 -39
- package/dist/api/routes/agent.routes.d.ts +8 -1
- package/dist/checkpoint/file-checkpoint-saver.d.ts +4 -0
- package/dist/checkpoint/file-checkpoint-saver.js +51 -22
- package/dist/checkpoint/index.d.ts +1 -1
- package/dist/checkpoint/index.js +1 -1
- package/dist/common/types.d.ts +2 -2
- package/dist/common/types.js +1 -1
- package/dist/execution/index.d.ts +2 -0
- package/dist/execution/index.js +2 -0
- package/dist/execution/interceptors.d.ts +40 -0
- package/dist/execution/interceptors.js +23 -0
- package/dist/execution/ports.d.ts +18 -0
- package/dist/execution/ports.js +13 -0
- package/dist/governance/audit-reporter.d.ts +14 -0
- package/dist/governance/audit-reporter.js +18 -0
- package/dist/governance/index.d.ts +2 -0
- package/dist/governance/index.js +1 -0
- package/dist/governance/permission-guard.d.ts +6 -2
- package/dist/governance/permission-guard.js +38 -5
- package/dist/graph/adapters/graph-nesting.d.ts +12 -0
- package/dist/graph/adapters/graph-nesting.js +29 -0
- package/dist/graph/adapters/index.d.ts +2 -0
- package/dist/graph/adapters/index.js +2 -0
- package/dist/graph/adapters/langgraph-checkpointer.d.ts +11 -0
- package/dist/graph/adapters/langgraph-checkpointer.js +48 -9
- package/dist/graph/adapters/langgraph.adapter.d.ts +33 -11
- package/dist/graph/adapters/langgraph.adapter.js +200 -98
- package/dist/graph/adapters/skill-isolate.d.ts +13 -0
- package/dist/graph/adapters/skill-isolate.js +39 -0
- package/dist/graph/adapters/subgraph-compiler.d.ts +7 -2
- package/dist/graph/adapters/subgraph-compiler.js +63 -30
- package/dist/graph/contracts/executable-graph.d.ts +33 -5
- package/dist/graph/hitl/hitl-handler.d.ts +13 -4
- package/dist/graph/hitl/hitl-handler.js +80 -7
- package/dist/graph/nodes/index.d.ts +1 -0
- package/dist/graph/nodes/index.js +1 -0
- package/dist/graph/nodes/llm-node.d.ts +3 -1
- package/dist/graph/nodes/llm-node.js +26 -2
- package/dist/graph/nodes/rag/dedup.d.ts +20 -0
- package/dist/graph/nodes/rag/dedup.js +77 -0
- package/dist/graph/nodes/rag/denoise.d.ts +58 -0
- package/dist/graph/nodes/rag/denoise.js +220 -0
- package/dist/graph/nodes/rag/fusion/fuse.d.ts +11 -0
- package/dist/graph/nodes/rag/fusion/fuse.js +25 -0
- package/dist/graph/nodes/rag/fusion/normalize.d.ts +9 -0
- package/dist/graph/nodes/rag/fusion/normalize.js +135 -0
- package/dist/graph/nodes/rag/index.d.ts +18 -0
- package/dist/graph/nodes/rag/index.js +17 -0
- package/dist/graph/nodes/rag/prompts.d.ts +12 -0
- package/dist/graph/nodes/rag/prompts.js +40 -0
- package/dist/graph/nodes/rag/rag-node.d.ts +21 -0
- package/dist/graph/nodes/rag/rag-node.js +135 -0
- package/dist/graph/nodes/rag/rag-search-tool.d.ts +48 -0
- package/dist/graph/nodes/rag/rag-search-tool.js +230 -0
- package/dist/graph/nodes/rag/react-loop.d.ts +57 -0
- package/dist/graph/nodes/rag/react-loop.js +467 -0
- package/dist/graph/nodes/rag/retrievers/http-embedder.d.ts +29 -0
- package/dist/graph/nodes/rag/retrievers/http-embedder.js +66 -0
- package/dist/graph/nodes/rag/retrievers/in-memory-retriever.d.ts +17 -0
- package/dist/graph/nodes/rag/retrievers/in-memory-retriever.js +18 -0
- package/dist/graph/nodes/rag/retrievers/local-sqlite-retriever.d.ts +46 -0
- package/dist/graph/nodes/rag/retrievers/local-sqlite-retriever.js +85 -0
- package/dist/graph/nodes/rag/tools.d.ts +90 -0
- package/dist/graph/nodes/rag/tools.js +260 -0
- package/dist/graph/nodes/rag/types.d.ts +226 -0
- package/dist/graph/nodes/rag/types.js +9 -0
- package/dist/graph/nodes/tool-node.d.ts +19 -1
- package/dist/graph/nodes/tool-node.js +158 -33
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/memory/semantic-retrieval.service.d.ts +1 -1
- package/dist/memory/semantic-retrieval.service.js +13 -23
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.js +2 -2
- package/dist/providers/local-llm-provider.d.ts +6 -0
- package/dist/providers/local-llm-provider.js +29 -5
- package/dist/providers/routing-llm-provider.d.ts +7 -2
- package/dist/providers/routing-llm-provider.js +71 -16
- package/dist/registry/deployment-snapshot.js +18 -4
- package/dist/registry/graph-loader.d.ts +2 -0
- package/dist/registry/graph-loader.js +39 -2
- package/dist/registry/preload-flags.d.ts +1 -1
- package/dist/registry/preload-flags.js +1 -1
- package/dist/runtime/agent-runtime.d.ts +5 -0
- package/dist/runtime/agent-runtime.js +107 -70
- package/dist/runtime/dev-rag-deps.d.ts +21 -0
- package/dist/runtime/dev-rag-deps.js +186 -0
- package/dist/runtime/dev-runtime.d.ts +18 -2
- package/dist/runtime/dev-runtime.js +68 -8
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/types.d.ts +10 -9
- package/dist/session/apply-session-compression.d.ts +85 -0
- package/dist/session/apply-session-compression.js +281 -0
- package/dist/session/apply-session-memory.d.ts +21 -0
- package/dist/session/apply-session-memory.js +48 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/session/persist-turn-messages.d.ts +57 -1
- package/dist/session/persist-turn-messages.js +160 -5
- package/dist/sse/events/index.d.ts +13 -4
- package/dist/sse/events/index.js +9 -1
- package/dist/sse/events/rag-events.d.ts +63 -0
- package/dist/sse/events/rag-events.js +9 -0
- package/dist/sse/events/session-events.d.ts +38 -0
- package/dist/sse/events/session-events.js +4 -0
- package/dist/token/index.d.ts +2 -1
- package/dist/token/index.js +2 -1
- package/dist/token/record-task-token-usage.d.ts +25 -0
- package/dist/token/record-task-token-usage.js +44 -0
- package/dist/token/token-detail-mode.d.ts +4 -4
- package/dist/token/token-detail-mode.js +4 -4
- package/dist/token/token-detail.d.ts +8 -0
- package/dist/token/token-detail.js +22 -0
- package/dist/token/token-stats.service.d.ts +2 -2
- package/dist/token/token-stats.service.js +1 -1
- package/dist/tool/create-tool-executor.d.ts +6 -0
- package/dist/tool/create-tool-executor.js +4 -3
- package/dist/tool/harness-primitives.d.ts +9 -1
- package/dist/tool/harness-primitives.js +43 -12
- package/dist/tool/index.d.ts +2 -1
- package/dist/tool/index.js +2 -1
- package/dist/tool/tool-confirmation.js +8 -5
- package/dist/tool/tool-executor.interface.d.ts +27 -0
- package/dist/tool/tool-orchestrator.d.ts +3 -1
- package/dist/tool/tool-orchestrator.js +15 -6
- package/dist/tool/tool-retry.d.ts +15 -0
- package/dist/tool/tool-retry.js +69 -0
- package/package.json +2 -2
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocalSqliteRetriever — 本地向量检索 IRetriever 实现(design-v2 §7.4)
|
|
3
|
+
*
|
|
4
|
+
* 两跳:embedder.embed(query)(远程向量化,统一走 gateway)→
|
|
5
|
+
* engine.search(后台线程 KNN,用户隔离 + 软删除过滤)→ 归一化 RetrievalDoc。
|
|
6
|
+
* 引擎实例与 embedder 均由宿主构造注入——runtime 不 import 引擎包,服务端
|
|
7
|
+
* 形态不挂本 retriever(部署差异零分支,§8)。
|
|
8
|
+
*/
|
|
9
|
+
export class LocalSqliteRetriever {
|
|
10
|
+
sourceId;
|
|
11
|
+
engine;
|
|
12
|
+
embedder;
|
|
13
|
+
modelVersion;
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
this.sourceId = opts.sourceId ?? 'local';
|
|
16
|
+
this.engine = opts.engine;
|
|
17
|
+
this.embedder = opts.embedder;
|
|
18
|
+
this.modelVersion = opts.modelVersion ?? opts.embedder.model;
|
|
19
|
+
}
|
|
20
|
+
async retrieve(query, opts) {
|
|
21
|
+
const [vector] = await this.embedder.embed([query]);
|
|
22
|
+
if (this.engine.assertQueryModel) {
|
|
23
|
+
await this.engine.assertQueryModel(this.modelVersion);
|
|
24
|
+
}
|
|
25
|
+
const rows = await this.engine.search(vector, { ...opts, fileIds: await this.resolveFileIds(opts.scope) });
|
|
26
|
+
// distance 越小越相关(与其他源 score 越大越好语义相反)→ 不进 score、进 metadata;
|
|
27
|
+
// 源内顺序由引擎 ORDER BY distance 给出,fuse 依赖此序(§6.2)
|
|
28
|
+
return rows.map((r) => ({
|
|
29
|
+
content: r.content,
|
|
30
|
+
sourceId: this.sourceId,
|
|
31
|
+
rawId: String(r.id),
|
|
32
|
+
metadata: {
|
|
33
|
+
fileId: r.fileId,
|
|
34
|
+
fileName: r.fileName,
|
|
35
|
+
filePath: r.filePath,
|
|
36
|
+
chunkId: r.chunkId,
|
|
37
|
+
distance: r.distance,
|
|
38
|
+
},
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
/** 关键词精确检索(FTS5 bm25):端口未提供该能力时不暴露(上层不会把 keyword_search 进工具表) */
|
|
42
|
+
async retrieveByKeyword(query, opts) {
|
|
43
|
+
if (!this.engine.searchByKeyword)
|
|
44
|
+
return [];
|
|
45
|
+
const rows = await this.engine.searchByKeyword(query, {
|
|
46
|
+
...opts,
|
|
47
|
+
fileIds: await this.resolveFileIds(opts.scope),
|
|
48
|
+
});
|
|
49
|
+
// rank 同 distance 语义(越小越相关)→ 进 metadata 不进 score;源内顺序 = bm25 序
|
|
50
|
+
return rows.map((r) => ({
|
|
51
|
+
content: r.content,
|
|
52
|
+
sourceId: this.sourceId,
|
|
53
|
+
rawId: String(r.id),
|
|
54
|
+
metadata: {
|
|
55
|
+
fileId: r.fileId,
|
|
56
|
+
fileName: r.fileName,
|
|
57
|
+
filePath: r.filePath,
|
|
58
|
+
chunkId: r.chunkId,
|
|
59
|
+
rank: r.rank,
|
|
60
|
+
},
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 请求级范围 → 引擎过滤键。语义三分:
|
|
65
|
+
* undefined(无 scope / 空对象)= 未圈定,不传 filter;
|
|
66
|
+
* fileIds 空数组且无 dirPaths = 圈定了但无本地源(会话勾选未含 local-file)→
|
|
67
|
+
* 引擎两路直接回空;fileIds 空数组与 dirPaths 并存是"无文件过滤、只圈目录"的
|
|
68
|
+
* 自然编码(契约并集语义),须走并集解析而非整体关闭;
|
|
69
|
+
* fileIds/dirPaths 有值 → dirPaths 经引擎解析(可选能力,未实现则仅 fileIds 生效)后并集。
|
|
70
|
+
*/
|
|
71
|
+
async resolveFileIds(scope) {
|
|
72
|
+
if (!scope || (scope.fileIds === undefined && !scope.dirPaths?.length))
|
|
73
|
+
return undefined;
|
|
74
|
+
if (scope.fileIds?.length === 0 && !scope.dirPaths?.length)
|
|
75
|
+
return [];
|
|
76
|
+
if (this.engine.resolveScopeFileIds) {
|
|
77
|
+
const ids = await this.engine.resolveScopeFileIds({
|
|
78
|
+
fileIds: scope.fileIds,
|
|
79
|
+
dirPathPrefixes: scope.dirPaths,
|
|
80
|
+
});
|
|
81
|
+
return ids ?? undefined;
|
|
82
|
+
}
|
|
83
|
+
return scope.fileIds?.length ? scope.fileIds : [];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RAG 工具表组装 — LLM 眼中只有一张工具表
|
|
3
|
+
*
|
|
4
|
+
* 四类工具拼成一张 apiTools:
|
|
5
|
+
* ① 注册工具:config.tools 圈定的 loadedTools 子集(缺省全部),复用 runtime 现成的
|
|
6
|
+
* loadedToolsToApiTools(schema 单一真相源在 manifest,DSL 只做名字引用)
|
|
7
|
+
* ② local_search:内置伪工具,schema 代码定义,执行直连 IRetriever(不经 ToolOrchestrator)
|
|
8
|
+
* ②' keyword_search:可选伪工具(至少一个 retriever 实现retrieveByKeyword 才进表),
|
|
9
|
+
* FTS5 精确匹配——数字/编号/专有名词是 embedding 短板,交给 LLM 按查询形态自选
|
|
10
|
+
* ③ rag_finish:终止信号,不执行任何东西
|
|
11
|
+
*
|
|
12
|
+
* @see design-v2 §5.4 / 前置讨论「工具定义放节点内」结论
|
|
13
|
+
*/
|
|
14
|
+
import type { LLMToolDefinition } from '../llm-node.js';
|
|
15
|
+
import type { LoadedToolMap } from '../../../tool/loaded-tool-definition.js';
|
|
16
|
+
import type { RagNodeConfig, IRetriever, RetrievalDoc } from './types.js';
|
|
17
|
+
export declare const RAG_FINISH_TOOL_NAME = "rag_finish";
|
|
18
|
+
export declare const LOCAL_SEARCH_TOOL_NAME = "local_search";
|
|
19
|
+
export declare const KEYWORD_SEARCH_TOOL_NAME = "keyword_search";
|
|
20
|
+
/**
|
|
21
|
+
* resolveRagSources — 检索面解析的单一入口(RagNode / rag_search 装饰器共用)
|
|
22
|
+
*
|
|
23
|
+
* 三种声明位(优先级递降):sources(描述符/字符串混排)→ retrievalTools → tools;
|
|
24
|
+
* 均未声明 = 全未圈定(全部 loadedTools + 本地源不限)。
|
|
25
|
+
* selectedIds 为会话级勾选(请求 ragSources):按 id 过滤目录条目,undefined = 全部启用。
|
|
26
|
+
* 字符串条目是外部源简写(id = 注册工具名);local-file 条目进本地目录白名单。
|
|
27
|
+
*/
|
|
28
|
+
export interface ResolvedRagSources {
|
|
29
|
+
/** 外部工具面:undefined = 未圈定(全部 loadedTools);[] = 圈定但无外部源 */
|
|
30
|
+
toolScope?: string[];
|
|
31
|
+
/** 目录是否声明了 local-file 数据源(决定"未勾选本地"是关还是不限) */
|
|
32
|
+
hasLocal: boolean;
|
|
33
|
+
/** sources 描述符目录是否声明(声明了 = 图即完整检索面:无 local-file 条目则本地不可用) */
|
|
34
|
+
hasCatalog: boolean;
|
|
35
|
+
/** 勾选后生效的本地文件夹路径(local-file source,旧式无 source 取 id),进检索 scope */
|
|
36
|
+
localDirs: string[];
|
|
37
|
+
/** 声明的本地目录全量面(不受 ragSources 勾选影响)——请求带 ragScope.dirPaths 时作为交集基准 */
|
|
38
|
+
localDirsAll: string[];
|
|
39
|
+
/**
|
|
40
|
+
* 工具名 → 该工具下生效的资源目录(仅含带 source 的描述符)。
|
|
41
|
+
* 一型一工具、源是参数:dw 的表/maxkb 的 appid 等资源值经工具 description
|
|
42
|
+
* 暴露给 LLM,由 LLM 决策调用时选参(会话勾选后目录同步收窄)
|
|
43
|
+
*/
|
|
44
|
+
sourceCatalog: Record<string, Array<{
|
|
45
|
+
id: string;
|
|
46
|
+
source: string;
|
|
47
|
+
}>>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* sourceToolName — 外部源描述符 → 工具名(type 约定解析)
|
|
51
|
+
*
|
|
52
|
+
* ref 显式指定 > `${type}_search` 约定;local-file 非工具源(undefined);
|
|
53
|
+
* 字符串简写(type 'external')工具名即字符串本身。约定名未注册由
|
|
54
|
+
* buildRagTools fail-fast 提示 ref 兜底。
|
|
55
|
+
*/
|
|
56
|
+
export declare function sourceToolName(e: {
|
|
57
|
+
type: string;
|
|
58
|
+
id: string;
|
|
59
|
+
ref?: string;
|
|
60
|
+
}): string | undefined;
|
|
61
|
+
export declare function resolveRagSources(config: RagNodeConfig | undefined, selectedIds?: string[]): ResolvedRagSources;
|
|
62
|
+
export declare function mergeLocalDirs(hasLocal: boolean, localDirs: string[], requestScope?: import('./types.js').RagScope): import('./types.js').RagScope | undefined;
|
|
63
|
+
export declare function appendSourceCatalog(description: string | undefined, entries: Array<{
|
|
64
|
+
id: string;
|
|
65
|
+
source: string;
|
|
66
|
+
}>): string;
|
|
67
|
+
/** RagToolSet — react-loop 派发所需的工具表元数据 */
|
|
68
|
+
export interface RagToolSet {
|
|
69
|
+
/** 传给 LLM 的 API tools 参数(①+②+③) */
|
|
70
|
+
apiTools: LLMToolDefinition[];
|
|
71
|
+
/** 注册工具名集合(派发路 B 用:命中才走 ToolOrchestrator) */
|
|
72
|
+
kbToolNames: Set<string>;
|
|
73
|
+
/** config.tools 声明了但 loadedTools 未注册的名字(execute 首轮 fail-fast 用) */
|
|
74
|
+
missingToolNames: string[];
|
|
75
|
+
/** config.tools 显式点名了 requireConfirmation 工具(execute 首轮 fail-fast 用) */
|
|
76
|
+
excludedConfirmToolNames: string[];
|
|
77
|
+
}
|
|
78
|
+
export declare function buildRagTools(config: RagNodeConfig, loadedTools: LoadedToolMap | undefined, retrievers: IRetriever[] | undefined, opts?: {
|
|
79
|
+
toolScope?: string[];
|
|
80
|
+
sourceCatalog?: ResolvedRagSources['sourceCatalog'];
|
|
81
|
+
localEnabled?: boolean;
|
|
82
|
+
}): RagToolSet;
|
|
83
|
+
/**
|
|
84
|
+
* briefList — 给 LLM 的精简结果清单
|
|
85
|
+
*
|
|
86
|
+
* 关键设计:检索全文只进 perSource(供 fuse/denoise),决策上下文只放
|
|
87
|
+
* 标题+短摘录——避免数万 token 检索结果把 ReAct 决策上下文撑爆。
|
|
88
|
+
*/
|
|
89
|
+
export declare function briefList(docs: RetrievalDoc[], maxExcerpt?: number): string;
|
|
90
|
+
export declare function docTitle(d: RetrievalDoc): string;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RAG 工具表组装 — LLM 眼中只有一张工具表
|
|
3
|
+
*
|
|
4
|
+
* 四类工具拼成一张 apiTools:
|
|
5
|
+
* ① 注册工具:config.tools 圈定的 loadedTools 子集(缺省全部),复用 runtime 现成的
|
|
6
|
+
* loadedToolsToApiTools(schema 单一真相源在 manifest,DSL 只做名字引用)
|
|
7
|
+
* ② local_search:内置伪工具,schema 代码定义,执行直连 IRetriever(不经 ToolOrchestrator)
|
|
8
|
+
* ②' keyword_search:可选伪工具(至少一个 retriever 实现retrieveByKeyword 才进表),
|
|
9
|
+
* FTS5 精确匹配——数字/编号/专有名词是 embedding 短板,交给 LLM 按查询形态自选
|
|
10
|
+
* ③ rag_finish:终止信号,不执行任何东西
|
|
11
|
+
*
|
|
12
|
+
* @see design-v2 §5.4 / 前置讨论「工具定义放节点内」结论
|
|
13
|
+
*/
|
|
14
|
+
import { loadedToolsToApiTools } from '../../../tool/system-prompt-builder.js';
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
export const RAG_FINISH_TOOL_NAME = 'rag_finish';
|
|
17
|
+
export const LOCAL_SEARCH_TOOL_NAME = 'local_search';
|
|
18
|
+
export const KEYWORD_SEARCH_TOOL_NAME = 'keyword_search';
|
|
19
|
+
const RAG_FINISH_TOOL = {
|
|
20
|
+
type: 'function',
|
|
21
|
+
function: {
|
|
22
|
+
name: RAG_FINISH_TOOL_NAME,
|
|
23
|
+
description: '结束检索循环并整理结果。满足任一即调用:各子问题均有高相关片段覆盖 / 新一轮结果与已有高度重复 / 已收集足够高相关片段。',
|
|
24
|
+
parameters: { type: 'object', properties: {} },
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
function localSearchTool(retrievers) {
|
|
28
|
+
return {
|
|
29
|
+
type: 'function',
|
|
30
|
+
function: {
|
|
31
|
+
name: LOCAL_SEARCH_TOOL_NAME,
|
|
32
|
+
description: `检索用户本机已向量化的文档(本地知识库)。可用本地源:${retrievers
|
|
33
|
+
.map((r) => r.sourceId)
|
|
34
|
+
.join('、')}。`,
|
|
35
|
+
parameters: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
query: { type: 'string', description: '自包含的检索查询' },
|
|
39
|
+
topK: { type: 'number', description: '每个源返回的条数,默认 10' },
|
|
40
|
+
},
|
|
41
|
+
required: ['query'],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function keywordSearchTool(retrievers) {
|
|
47
|
+
return {
|
|
48
|
+
type: 'function',
|
|
49
|
+
function: {
|
|
50
|
+
name: KEYWORD_SEARCH_TOOL_NAME,
|
|
51
|
+
description: `本地文档关键词精确匹配(FTS5 全文检索,可用源:${retrievers
|
|
52
|
+
.map((r) => r.sourceId)
|
|
53
|
+
.join('、')})。适合语义检索难以命中的查询:精确数字(金额/日期)、编号(单号/工号)、专有名词、文件名等。`,
|
|
54
|
+
parameters: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
query: { type: 'string', description: '关键词(可含空格分隔的多个词,须与原文精确一致)' },
|
|
58
|
+
topK: { type: 'number', description: '每个源返回的条数,默认 10' },
|
|
59
|
+
},
|
|
60
|
+
required: ['query'],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* sourceToolName — 外部源描述符 → 工具名(type 约定解析)
|
|
67
|
+
*
|
|
68
|
+
* ref 显式指定 > `${type}_search` 约定;local-file 非工具源(undefined);
|
|
69
|
+
* 字符串简写(type 'external')工具名即字符串本身。约定名未注册由
|
|
70
|
+
* buildRagTools fail-fast 提示 ref 兜底。
|
|
71
|
+
*/
|
|
72
|
+
export function sourceToolName(e) {
|
|
73
|
+
if (e.type === 'local-file')
|
|
74
|
+
return undefined;
|
|
75
|
+
if (e.type === 'external')
|
|
76
|
+
return e.id;
|
|
77
|
+
return e.ref ?? `${e.type}_search`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* local-file source 展开 —— 三种写法:
|
|
81
|
+
* ① 目录路径:原样进 scope(引擎前缀匹配,含子目录)
|
|
82
|
+
* ② 单文件路径:原样进 scope(引擎 LIKE 精确匹配行命中该文件)
|
|
83
|
+
* ③ .json 结尾:授权清单文件(`{dirs:[...]}` 或字符串数组),展开为目录列表——
|
|
84
|
+
* 每次解析现读(清单热更新随之生效);缺文件/坏格式 = 空集(宁空不越界)
|
|
85
|
+
*/
|
|
86
|
+
function expandLocalSource(src) {
|
|
87
|
+
if (!src.toLowerCase().endsWith('.json'))
|
|
88
|
+
return [src];
|
|
89
|
+
try {
|
|
90
|
+
const parsed = JSON.parse(readFileSync(src, 'utf-8'));
|
|
91
|
+
const dirs = Array.isArray(parsed) ? parsed : parsed?.dirs;
|
|
92
|
+
// 清单项类型约束:非字符串项(null/数字/对象)直接丢弃——String() 强转会把垃圾
|
|
93
|
+
// 变 "null"/"1"/"[object Object]" 路径穿透,破坏"坏格式=空集"的宁空不越界语义
|
|
94
|
+
return Array.isArray(dirs)
|
|
95
|
+
? dirs.filter((s) => typeof s === 'string').map((s) => s.trim()).filter(Boolean)
|
|
96
|
+
: [];
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export function resolveRagSources(config, selectedIds) {
|
|
103
|
+
const rs = config?.retrievalSources;
|
|
104
|
+
const entries = [];
|
|
105
|
+
if (rs?.sources) {
|
|
106
|
+
for (const e of rs.sources) {
|
|
107
|
+
if (typeof e === 'string')
|
|
108
|
+
entries.push({ type: 'external', id: e });
|
|
109
|
+
else
|
|
110
|
+
entries.push(e);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const legacy = rs?.retrievalTools ?? config?.tools;
|
|
115
|
+
if (!legacy)
|
|
116
|
+
return { toolScope: undefined, hasLocal: false, hasCatalog: false, localDirs: [], localDirsAll: [], sourceCatalog: {} };
|
|
117
|
+
for (const n of legacy)
|
|
118
|
+
entries.push({ type: 'external', id: n });
|
|
119
|
+
}
|
|
120
|
+
const active = selectedIds ? entries.filter((e) => selectedIds.includes(e.id)) : entries;
|
|
121
|
+
const sourceCatalog = {};
|
|
122
|
+
for (const e of active) {
|
|
123
|
+
const tool = sourceToolName(e);
|
|
124
|
+
if (tool === undefined || !e.source)
|
|
125
|
+
continue;
|
|
126
|
+
(sourceCatalog[tool] ??= []).push({ id: e.id, source: e.source });
|
|
127
|
+
}
|
|
128
|
+
// local-file 展开单次读盘约束:localDirsAll(声明全量面)与 localDirs(勾选后子集)必须
|
|
129
|
+
// 同源——双读除多一次同步 I/O 外,.json 清单恰好被并发改写时两者会读到不同内容错位
|
|
130
|
+
const localDirsAll = [];
|
|
131
|
+
const localDirs = [];
|
|
132
|
+
for (const e of entries) {
|
|
133
|
+
if (e.type !== 'local-file')
|
|
134
|
+
continue;
|
|
135
|
+
const dirs = expandLocalSource(e.source ?? e.id);
|
|
136
|
+
localDirsAll.push(...dirs);
|
|
137
|
+
if (!selectedIds || selectedIds.includes(e.id))
|
|
138
|
+
localDirs.push(...dirs);
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
toolScope: [...new Set(active.map(sourceToolName).filter((n) => n !== undefined))],
|
|
142
|
+
hasLocal: entries.some((e) => e.type === 'local-file'),
|
|
143
|
+
// sources 描述符声明了 = 图即完整检索面(legacy 字符串位仍是纯工具清单语义)。
|
|
144
|
+
// 空数组也算声明:检索面为空、本地不可用(fail-closed),不能当未声明放开本地全库
|
|
145
|
+
hasCatalog: Array.isArray(rs?.sources),
|
|
146
|
+
localDirs,
|
|
147
|
+
localDirsAll,
|
|
148
|
+
sourceCatalog,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 数据源目录的 local-file 白名单与请求级 ragScope 合成(参数只收窄不扩面):
|
|
153
|
+
* 声明过 local-file 但本次未勾选(勾选后 localDirs 空)→ fileIds:[](本地源关闭);
|
|
154
|
+
* 请求 dirPaths 圈定 → 在勾选后的声明面内取交集(面板逐授权目录勾选走这条路,
|
|
155
|
+
* 交集为空 = 关本地);请求未圈定 → 勾选面原样。路径比较分隔符正反/尾斜杠归一。
|
|
156
|
+
* 目录未声明本地源 → 请求 scope 原样(本地全库,检索器由宿主装配)。
|
|
157
|
+
* 节点形态(RagNode)与工具形态(RagSearchToolExecutor)共用。
|
|
158
|
+
*/
|
|
159
|
+
const normDirPath = (p) => p.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
160
|
+
export function mergeLocalDirs(hasLocal, localDirs, requestScope) {
|
|
161
|
+
if (!hasLocal)
|
|
162
|
+
return requestScope;
|
|
163
|
+
if (!localDirs.length)
|
|
164
|
+
return { fileIds: [] };
|
|
165
|
+
const declared = [...new Set(localDirs.map(normDirPath))];
|
|
166
|
+
if (requestScope?.dirPaths === undefined) {
|
|
167
|
+
return { dirPaths: declared, fileIds: requestScope?.fileIds };
|
|
168
|
+
}
|
|
169
|
+
// 交集比较键统一 toLowerCase:Windows 路径大小写不敏感,声明 D:/Docs 请求 d:/docs
|
|
170
|
+
// 须命中(大小写敏感会交集空误关本地);仅比较用,dirPaths 保留声明原样传引擎
|
|
171
|
+
const requested = new Set(requestScope.dirPaths.map((p) => normDirPath(p).toLowerCase()));
|
|
172
|
+
const dirPaths = declared.filter((d) => requested.has(d.toLowerCase()));
|
|
173
|
+
if (!dirPaths.length)
|
|
174
|
+
return { fileIds: [] }; // 圈定与声明面交集为空 → 关本地
|
|
175
|
+
return { dirPaths, fileIds: requestScope?.fileIds };
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 资源目录注入工具 description(替换语义):带 source 的外部源把合法资源值
|
|
179
|
+
* (表名/appid/collection…)前置给 LLM 选参——内核不猜参数名,按工具 schema 由 LLM 填。
|
|
180
|
+
* 节点形态内核组表与工具形态 adapter 白名单收敛两处共用:adapter 先注入全量目录、
|
|
181
|
+
* 内核再注入会话勾选后的收窄目录,既有 `(可用数据源:…)` 后缀须整体摘除再追加——
|
|
182
|
+
* 逐字相同才跳过的幂等挡不住全量+收窄两份并存(全量份会让内层 LLM 选到未勾选源)。
|
|
183
|
+
* 摘除用 lastIndexOf 而非 `[^)]*` 正则:目录名可含中文、括号,只有"最后一个标记到
|
|
184
|
+
* 句尾"才是本函数拼出的后缀整体形态。
|
|
185
|
+
*/
|
|
186
|
+
const SOURCE_CATALOG_MARKER = '(可用数据源:';
|
|
187
|
+
export function appendSourceCatalog(description, entries) {
|
|
188
|
+
if (!entries?.length)
|
|
189
|
+
return description ?? '';
|
|
190
|
+
const list = entries.map((c) => `${c.id}=${c.source}`).join('、');
|
|
191
|
+
const suffix = `(可用数据源:${list})`;
|
|
192
|
+
let base = description ?? '';
|
|
193
|
+
const at = base.lastIndexOf(SOURCE_CATALOG_MARKER);
|
|
194
|
+
if (at !== -1 && base.endsWith(')'))
|
|
195
|
+
base = base.slice(0, at);
|
|
196
|
+
return `${base}${suffix}`.trim();
|
|
197
|
+
}
|
|
198
|
+
export function buildRagTools(config, loadedTools, retrievers, opts) {
|
|
199
|
+
const available = new Set(Object.keys(loadedTools ?? {}));
|
|
200
|
+
// requireConfirmation 工具不进 rag 工具表:HITL 确认流挂在图级 ToolNode,
|
|
201
|
+
// rag 直调 orchestrator 会绕过确认(LLM 自主执行需确认操作是合规硬伤),
|
|
202
|
+
// 宁可不见——缺省静默剔除;数据源目录显式点名则 fail-fast(见 RagNode)
|
|
203
|
+
const requireConfirm = (name) => Boolean(loadedTools?.[name]?.manifest.requireConfirmation);
|
|
204
|
+
// 三 opts 齐备时跳过内部解析约束:调用方已显式传入勾选后的解析结果,再 resolve 一次
|
|
205
|
+
// 是对 .json 清单的重复同步读盘,且可能读到与调用方不一致的内容(目录与工具表错位)
|
|
206
|
+
const needsResolve = opts?.toolScope === undefined || opts?.sourceCatalog === undefined || opts?.localEnabled === undefined;
|
|
207
|
+
const resolved = needsResolve ? resolveRagSources(config) : undefined;
|
|
208
|
+
const toolScope = opts?.toolScope !== undefined ? opts.toolScope : resolved?.toolScope;
|
|
209
|
+
// ?? {} 仅为类型收窄:跳过解析时 opts.sourceCatalog 必有值,实际不会落到空目录
|
|
210
|
+
const sourceCatalog = opts?.sourceCatalog ?? resolved?.sourceCatalog ?? {};
|
|
211
|
+
// 本地源可用性:图声明了 sources(完整检索面)→ 必须含 local-file 条目才可用;
|
|
212
|
+
// 未声明 sources(legacy 位/无目录)→ 本地内置可用(宿主装配形态)
|
|
213
|
+
const localEnabled = opts?.localEnabled ?? (resolved?.hasCatalog ? resolved.hasLocal : true);
|
|
214
|
+
const effectiveRetrievers = localEnabled ? retrievers : undefined;
|
|
215
|
+
const requested = toolScope ?? [...available];
|
|
216
|
+
const allowed = requested.filter((n) => available.has(n) && !requireConfirm(n));
|
|
217
|
+
const excludedConfirmToolNames = toolScope?.filter((n) => available.has(n) && requireConfirm(n)) ?? [];
|
|
218
|
+
// 注意:loadedToolsToApiTools 的既有语义是"空名单=全部"(llm 节点 skillTools 在用);
|
|
219
|
+
// rag 语义相反——圈定了但为空(会话勾选剔除全部外部源)就是没有外部工具
|
|
220
|
+
const apiTools = allowed.length ? loadedToolsToApiTools(loadedTools, allowed) : [];
|
|
221
|
+
// 资源目录注入 description:带 source 的外部源把合法资源值(表名/appid/collection…)
|
|
222
|
+
// 前置到工具描述,LLM 据此选参——内核不猜参数名,按工具 schema 由 LLM 填
|
|
223
|
+
for (const t of apiTools) {
|
|
224
|
+
const catalog = sourceCatalog[t.function.name];
|
|
225
|
+
if (catalog?.length) {
|
|
226
|
+
t.function.description = appendSourceCatalog(t.function.description, catalog);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const kbToolNames = new Set(allowed);
|
|
230
|
+
const missingToolNames = toolScope?.filter((n) => !available.has(n)) ?? [];
|
|
231
|
+
if (effectiveRetrievers?.length) {
|
|
232
|
+
apiTools.push(localSearchTool(effectiveRetrievers));
|
|
233
|
+
const kwRetrievers = effectiveRetrievers.filter((r) => typeof r.retrieveByKeyword === 'function');
|
|
234
|
+
if (kwRetrievers.length)
|
|
235
|
+
apiTools.push(keywordSearchTool(kwRetrievers));
|
|
236
|
+
}
|
|
237
|
+
apiTools.push(RAG_FINISH_TOOL);
|
|
238
|
+
return { apiTools, kbToolNames, missingToolNames, excludedConfirmToolNames };
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* briefList — 给 LLM 的精简结果清单
|
|
242
|
+
*
|
|
243
|
+
* 关键设计:检索全文只进 perSource(供 fuse/denoise),决策上下文只放
|
|
244
|
+
* 标题+短摘录——避免数万 token 检索结果把 ReAct 决策上下文撑爆。
|
|
245
|
+
*/
|
|
246
|
+
export function briefList(docs, maxExcerpt = 80) {
|
|
247
|
+
if (!docs.length)
|
|
248
|
+
return '(无结果)';
|
|
249
|
+
return docs
|
|
250
|
+
.map((d, i) => {
|
|
251
|
+
const title = docTitle(d);
|
|
252
|
+
const excerpt = d.content.slice(0, maxExcerpt).replace(/\s+/g, ' ');
|
|
253
|
+
return `${i + 1}. [${d.sourceId}] ${title}:${excerpt}…`;
|
|
254
|
+
})
|
|
255
|
+
.join('\n');
|
|
256
|
+
}
|
|
257
|
+
export function docTitle(d) {
|
|
258
|
+
const meta = d.metadata ?? {};
|
|
259
|
+
return String(meta['title'] ?? meta['fileName'] ?? d.rawId);
|
|
260
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RAG 节点类型定义 — 检索子智能体
|
|
3
|
+
*
|
|
4
|
+
* RagNode 是黑盒节点(type: rag):query 进、降噪上下文出,
|
|
5
|
+
* 内部跑 ReAct 循环(LLM 决策 + 三路工具派发)。
|
|
6
|
+
*
|
|
7
|
+
* @see mt-docs/1-requirements/specs/agentic-rag/design-v2.md §5
|
|
8
|
+
*/
|
|
9
|
+
import type { ILLMProvider } from '../llm-node.js';
|
|
10
|
+
import type { NodeRetryConfig } from '../../contracts/executable-graph.js';
|
|
11
|
+
import type { ToolOrchestrator } from '../../../tool/tool-orchestrator.js';
|
|
12
|
+
import type { IGovernanceClient } from '../../../governance/governance-client.js';
|
|
13
|
+
/**
|
|
14
|
+
* RetrievalDoc — 融合唯一输入的统一结构
|
|
15
|
+
*
|
|
16
|
+
* 知识库 tool 的字符串结果经 normalize 转成此结构;
|
|
17
|
+
* 本地 retriever 直返此结构(这是本地源不走工具系统的核心理由)。
|
|
18
|
+
* @see design-v2 §6.1
|
|
19
|
+
*/
|
|
20
|
+
export interface RetrievalDoc {
|
|
21
|
+
/** 文本内容(必需) */
|
|
22
|
+
content: string;
|
|
23
|
+
/** 来源标识:'kb' | 'local' | 自定义源 id */
|
|
24
|
+
sourceId: string;
|
|
25
|
+
/** 源内原始 ID(跨源配额内去重键 sourceId:rawId 用) */
|
|
26
|
+
rawId: string;
|
|
27
|
+
/** 原始分数(仅记录,融合不跨源比较) */
|
|
28
|
+
score?: number;
|
|
29
|
+
/** 源特有信息(标题/文件路径/时间等) */
|
|
30
|
+
metadata?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* IRetriever — 本地检索内置接口
|
|
34
|
+
*
|
|
35
|
+
* 向量引擎(SQLite 向量库 / 文件解析 / 分片 / embed)整体封装在这一个方法之后,
|
|
36
|
+
* react-loop 只面向本接口编程;正式实现为 LocalSqliteRetriever(design-v2 §7)。
|
|
37
|
+
*/
|
|
38
|
+
export interface IRetriever {
|
|
39
|
+
readonly sourceId: string;
|
|
40
|
+
retrieve(query: string, opts: {
|
|
41
|
+
topK: number;
|
|
42
|
+
userId: string;
|
|
43
|
+
scope?: RagScope;
|
|
44
|
+
}): Promise<RetrievalDoc[]>;
|
|
45
|
+
/**
|
|
46
|
+
* 关键词精确检索(可选能力,FTS5 bm25):适合编号/数字/专有名词等 embedding 短板查询。
|
|
47
|
+
* 实现方缺省不提供——react-loop 检测到至少一个源支持才把 keyword_search 伪工具进表。
|
|
48
|
+
*/
|
|
49
|
+
retrieveByKeyword?(query: string, opts: {
|
|
50
|
+
topK: number;
|
|
51
|
+
userId: string;
|
|
52
|
+
scope?: RagScope;
|
|
53
|
+
}): Promise<RetrievalDoc[]>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* IEmbedder — query 向量化端口(design-v2 §7.3)
|
|
57
|
+
*
|
|
58
|
+
* 配置经构造参数注入(宿主装配):
|
|
59
|
+
* gateway embedding(Qwen3-Embedding-8B,OpenAI 兼容 /v1/embeddings);
|
|
60
|
+
* token 计量由实现侧落到 runtime 统一记账。
|
|
61
|
+
*/
|
|
62
|
+
export interface IEmbedder {
|
|
63
|
+
/** embedding 模型标识(对应引擎 model_version 锁,不一致引擎会拒) */
|
|
64
|
+
readonly model: string;
|
|
65
|
+
/** 向量维度(须与建库 dims 一致;引擎侧另有一致性校验兜底) */
|
|
66
|
+
readonly dims: number;
|
|
67
|
+
/** 批量向量化(结果与入参顺序一致) */
|
|
68
|
+
embed(texts: string[]): Promise<number[][]>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* RagScope — 请求级检索范围(前端圈定,随对话请求传入)
|
|
72
|
+
*
|
|
73
|
+
* fileIds/dirPaths 并集语义;dirPaths 由引擎按路径前缀解析成 fileIds(REPLACE
|
|
74
|
+
* 统一分隔符 + 尾分隔符锚定防前缀误中)。过滤叠加在 userId 隔离之内——圈到
|
|
75
|
+
* 他人属主的文件只会空结果,无越权读。仅作用于本地检索源(local/keyword)。
|
|
76
|
+
*/
|
|
77
|
+
export interface RagScope {
|
|
78
|
+
/** 圈定的文件 id 集合(查询面 listFiles 的 file_id) */
|
|
79
|
+
fileIds?: string[];
|
|
80
|
+
/** 圈定的目录路径前缀(按目录整棵圈定,含子目录) */
|
|
81
|
+
dirPaths?: string[];
|
|
82
|
+
}
|
|
83
|
+
/** 引擎返回的原始行(runtime 侧自持类型,不 import 引擎包) */
|
|
84
|
+
export interface VectorRow {
|
|
85
|
+
id: number | string;
|
|
86
|
+
distance: number;
|
|
87
|
+
content: string;
|
|
88
|
+
fileId: string;
|
|
89
|
+
fileName: string;
|
|
90
|
+
filePath: string;
|
|
91
|
+
chunkId: string;
|
|
92
|
+
}
|
|
93
|
+
/** 引擎关键词检索行(rank 为 FTS5 bm25,越小越相关;id 与向量检索同源——溯源对齐) */
|
|
94
|
+
export interface KeywordRow {
|
|
95
|
+
id: number | string;
|
|
96
|
+
rank: number;
|
|
97
|
+
content: string;
|
|
98
|
+
fileId: string;
|
|
99
|
+
fileName: string;
|
|
100
|
+
filePath: string;
|
|
101
|
+
chunkId: string;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* VectorEnginePort — 向量引擎窄端口(design-v2 §7.3 去服务化版)
|
|
105
|
+
*
|
|
106
|
+
* 宿主用引擎包实例满足(如 createAsyncEngine 产物:全 Promise、同步 SQLite
|
|
107
|
+
* 隔离在后台线程);runtime 对 native 零接触,服务端形态不装引擎包。
|
|
108
|
+
*/
|
|
109
|
+
export interface VectorEnginePort {
|
|
110
|
+
search(vector: number[], opts: {
|
|
111
|
+
topK: number;
|
|
112
|
+
userId: string;
|
|
113
|
+
fileIds?: string[];
|
|
114
|
+
}): Promise<VectorRow[]>;
|
|
115
|
+
/** 关键词检索(可选能力:引擎包已内置 FTS5;旧端口实现不提供即无 keyword_search 工具) */
|
|
116
|
+
searchByKeyword?(query: string, opts: {
|
|
117
|
+
topK: number;
|
|
118
|
+
userId: string;
|
|
119
|
+
fileIds?: string[];
|
|
120
|
+
}): Promise<KeywordRow[]>;
|
|
121
|
+
/** 请求级范围解析:目录前缀 → fileIds(可选能力;未实现时仅 fileIds 圈定生效) */
|
|
122
|
+
resolveScopeFileIds?(scope: {
|
|
123
|
+
fileIds?: string[];
|
|
124
|
+
dirPathPrefixes?: string[];
|
|
125
|
+
}): Promise<string[] | null>;
|
|
126
|
+
/** 查询侧模型标识校验(可选能力:引擎锁定 model_version 且请求不一致即拒) */
|
|
127
|
+
assertQueryModel?(modelVersion: string): Promise<void> | void;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* RagEventSink — rag 内部事件出口(SSE 旁路,rag.* 五事件)
|
|
131
|
+
*
|
|
132
|
+
* adapter 接线到 streamSink(stream() 存活期间有效,缺省静默);
|
|
133
|
+
* 测试与宿主可直接注入收集器。黑盒内部过程经此对外可见。
|
|
134
|
+
*/
|
|
135
|
+
export type RagEventSink = (type: 'rag.round' | 'rag.search' | 'rag.denoise' | 'rag.rewrite' | 'rag.finish', payload: Record<string, unknown>) => void;
|
|
136
|
+
/**
|
|
137
|
+
* RagSourceDescriptor — 数据源描述符(统一"数据源"抽象,避免与主智能体 tools 混淆)
|
|
138
|
+
*
|
|
139
|
+
* 三字段模型:type 定接入实现、id 对外唯一标识、source 定资源定位。
|
|
140
|
+
* type 是开放集合(无缝扩展契约:新数据源类型零内核改动接入):
|
|
141
|
+
* - 'local-file':本地引擎方法,source = 目录路径(旧式无 source 时 id 即路径,兼容)
|
|
142
|
+
* - 其余任意 type(kb/dw/api/maxkb/milvus/…):工具面实现,工具名 = ref 显式指定
|
|
143
|
+
* ?? `${type}_search` 约定;source = 工具内资源定位(dw=表/库、maxkb=appid、
|
|
144
|
+
* milvus=collection…),经工具 description 的资源目录暴露给 LLM——一型一工具、
|
|
145
|
+
* 源是参数,同型多源(两张数仓表)由 LLM 按目录选参调用
|
|
146
|
+
*/
|
|
147
|
+
export interface RagSourceDescriptor {
|
|
148
|
+
/** 数据源类别(开放集合):'local-file' | 'kb' | 'dw' | 'maxkb' | 'milvus' | … */
|
|
149
|
+
type: string;
|
|
150
|
+
/** 对外唯一标识:会话级勾选(请求 ragSources)按此过滤,亦作展示名 */
|
|
151
|
+
id: string;
|
|
152
|
+
/** 资源定位:local-file→目录路径;dw→表/库;maxkb→appid;milvus→collection(可选) */
|
|
153
|
+
source?: string;
|
|
154
|
+
/** 外部源显式工具名;缺省按约定 `${type}_search` 解析(local-file 非工具源,无此字段) */
|
|
155
|
+
ref?: string;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* RagRetrievalSources — 检索源配置(DSL config.retrievalSources)
|
|
159
|
+
*
|
|
160
|
+
* 产品概念只有"数据源"(文件夹/知识库/数仓…);本地向量库是宿主实现细节
|
|
161
|
+
* (单库,由装配注入检索器),不经 DSL 暴露路径。
|
|
162
|
+
* 会话级勾选(请求 ragSources 传 id 列表)在此基础上过滤,无需改图。
|
|
163
|
+
*/
|
|
164
|
+
export interface RagRetrievalSources {
|
|
165
|
+
/** 预定义数据源目录:外部源(字符串=注册工具名的简写,或描述符)+ 本地文件夹(描述符) */
|
|
166
|
+
sources?: Array<string | RagSourceDescriptor>;
|
|
167
|
+
/** @deprecated 旧位:外部工具名清单,等价 sources 全字符串简写(过渡期保留) */
|
|
168
|
+
retrievalTools?: string[];
|
|
169
|
+
}
|
|
170
|
+
/** RagNodeConfig — DSL config(全部可选,均有缺省) */
|
|
171
|
+
export interface RagNodeConfig {
|
|
172
|
+
/** 模型(决策/判分/重写共用同一主力模型,与 llm 节点同体系),缺省 'default' */
|
|
173
|
+
model?: string;
|
|
174
|
+
/** 融合后总条数,缺省 10 */
|
|
175
|
+
totalK?: number;
|
|
176
|
+
/** ReAct 决策轮硬上限(按决策轮计次,同轮并发多个 tool_call 只计 1),缺省 5 */
|
|
177
|
+
maxIterations?: number;
|
|
178
|
+
/** 近重判定阈值(字符 n-gram Jaccard,M4 启用),缺省 0.8 */
|
|
179
|
+
nearDupThreshold?: number;
|
|
180
|
+
/** 检索源:数据源目录 + 本地向量库(与旧 tools 字段同现时以本字段为准) */
|
|
181
|
+
retrievalSources?: RagRetrievalSources;
|
|
182
|
+
/** @deprecated 旧位:本节点可见的工具名清单,等价 retrievalSources.sources 字符串简写(过渡期保留) */
|
|
183
|
+
tools?: string[];
|
|
184
|
+
/** 工具名 → 融合源桶映射(如 { 'kb-search': 'kb', 'dw-query': 'dw' }),缺省全部工具归 'kb' 桶 */
|
|
185
|
+
toolSources?: Record<string, string>;
|
|
186
|
+
/** 决策调用重试(与 llm 节点同语义:3 次 + 指数退避,可覆盖) */
|
|
187
|
+
retry?: NodeRetryConfig;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* RagNodeDeps — 宿主装配时注入的依赖(LangGraphAdapter 第 6 参)
|
|
191
|
+
*
|
|
192
|
+
* 实例均为无状态单例,可跨请求复用;请求级数据(query/userId/loadedTools 等)
|
|
193
|
+
* 一律经 execute(state) 流转,节点实例上不放请求级状态。
|
|
194
|
+
*/
|
|
195
|
+
export interface RagNodeDeps {
|
|
196
|
+
/** LLM 调用提供者(与 llm 节点同一实例体系) */
|
|
197
|
+
llmProvider: ILLMProvider;
|
|
198
|
+
/** 调知识库等注册工具(缺省回退 adapter 已组装的 orchestrator) */
|
|
199
|
+
toolOrchestrator?: ToolOrchestrator;
|
|
200
|
+
/** 工具权限治理(预留:当前权限校验经 ToolOrchestrator 内置 governance) */
|
|
201
|
+
governanceClient?: IGovernanceClient;
|
|
202
|
+
/** 内置检索器(本地 SQLite 单库,宿主装配;服务端为空数组/缺省) */
|
|
203
|
+
retrievers?: IRetriever[];
|
|
204
|
+
/** rag.* 事件出口:adapter 接线 streamSink(宿主无需提供,测试可注入) */
|
|
205
|
+
emitEvent?: RagEventSink;
|
|
206
|
+
}
|
|
207
|
+
/** RagTokenUsage — 循环内单次 LLM 调用明细,写入 __llmTokenUsage 数组累加器 */
|
|
208
|
+
export interface RagTokenUsage {
|
|
209
|
+
nodeId: string;
|
|
210
|
+
model: string;
|
|
211
|
+
inputTokens: number;
|
|
212
|
+
outputTokens: number;
|
|
213
|
+
durationMs?: number;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* DenoiseVerdict — 相关性判定输出契约(design-v2 §6.3,一次结构化 LLM 调用产出)
|
|
217
|
+
*/
|
|
218
|
+
export interface DenoiseVerdict {
|
|
219
|
+
/** 复合键 sourceId:rawId(与候选文档标签同构;rawId 仅源内唯一,裸 id 跨源会碰撞) */
|
|
220
|
+
rawId: string;
|
|
221
|
+
relevant: boolean;
|
|
222
|
+
/** 一句话理由(进 trace / 空结果重写定向用) */
|
|
223
|
+
reason: string;
|
|
224
|
+
/** relevant 时保留的句子编号集合(缺省 / 无有效编号 = 整篇保留) */
|
|
225
|
+
keepSentences?: number[];
|
|
226
|
+
}
|