@tencentcloud/sdk-mcp 1.7.7 → 1.7.9-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +7 -9
- package/index.js +8 -8
- package/knowledge/chunker.js +1 -0
- package/knowledge/doc-url-resolver.js +16 -9
- package/knowledge/embedding/build-vectors.js +92 -0
- package/knowledge/embedding/embed-model.js +41 -0
- package/knowledge/embedding/vector-augment.js +126 -0
- package/knowledge/embedding/vector-index.js +70 -0
- package/knowledge/frontmatter-meta.js +23 -0
- package/knowledge/path-meta.js +56 -0
- package/knowledge/search/exact-lookup.js +145 -16
- package/knowledge/search/filter-pool.js +77 -33
- package/knowledge/search/rank-profile.js +25 -0
- package/knowledge/search/requires-framework.js +4 -1
- package/knowledge/search/search-core.js +74 -13
- package/knowledge/search/search-knowledge.js +24 -34
- package/knowledge/search/search-pass.js +495 -169
- package/knowledge/search/url-row-scorer.js +24 -6
- package/package.json +3 -1
- package/resource/index/manifest.json +1 -1
- package/resource/index/vectors/index.json +4 -0
- package/resource/integration/callkit/preset/android/integration.md +0 -2
- package/resource/integration/callkit/preset/ios/integration.md +0 -2
- package/resource/integration/chat/preset/react/integration.md +9 -5
- package/resource/integration/chat/preset/uni-app/integration.md +4 -0
- package/resource/integration/chat/preset/vue/integration.md +10 -6
- package/resource/integration/livekit/preset/live/android/prepare-integration.md +0 -4
- package/resource/integration/livekit/preset/live/ios/prepare-integration.md +0 -4
- package/resource/integration/roomkit/preset/vue/integration.md +154 -208
- package/resource/knowledge/callkit/best-practice/android/video-chat-dating.md +0 -2
- package/resource/knowledge/callkit/core-sdk/android/answer-call.md +0 -2
- package/resource/knowledge/callkit/core-sdk/android/make-call.md +0 -2
- package/resource/knowledge/callkit/core-sdk/flutter/answer-call.md +0 -4
- package/resource/knowledge/callkit/core-sdk/flutter/make-call.md +0 -2
- package/resource/knowledge/callkit/core-sdk/flutter/offline-push.md +0 -2
- package/resource/knowledge/callkit/core-sdk/ios/answer-call.md +0 -2
- package/resource/knowledge/callkit/core-sdk/ios/make-call.md +0 -4
- package/resource/knowledge/callkit/core-sdk/ios/offline-push.md +0 -2
- package/resource/knowledge/callkit/core-sdk/uni-app/answer-call.md +0 -2
- package/resource/knowledge/callkit/core-sdk/uni-app/make-call.md +0 -2
- package/resource/knowledge/callkit/faq/ios.md +0 -2
- package/resource/knowledge/callkit/faq/uniapp-app.md +0 -2
- package/resource/knowledge/callkit/preset/miniprogram/integration.md +2 -3
- package/resource/knowledge/callkit/preset/miniprogram/uniapp-integration.md +0 -2
- package/resource/knowledge/chat/preset/ios-uikit/integration.md +0 -16
- package/resource/knowledge/chat/preset/react/h5/integration.md +16 -5
- package/resource/knowledge/chat/preset/vue/features/conversation-list.md +6 -3
- package/resource/knowledge/chat/preset/vue/h5/integration.md +7 -4
- package/resource/knowledge/iot/app/activate-service.md +95 -0
- package/resource/knowledge/iot/app/demo/android.md +93 -0
- package/resource/knowledge/iot/app/demo/ios.md +87 -0
- package/resource/knowledge/iot/app/demo/miniprogram.md +79 -0
- package/resource/knowledge/iot/app/device-binding.md +378 -0
- package/resource/knowledge/iot/app/device-sharing.md +414 -0
- package/resource/knowledge/iot/app/family-management.md +762 -0
- package/resource/knowledge/iot/app/integration/android.md +328 -0
- package/resource/knowledge/iot/app/integration/harmonyos.md +326 -0
- package/resource/knowledge/iot/app/integration/ios.md +351 -0
- package/resource/knowledge/iot/app/integration/miniprogram.md +268 -0
- package/resource/knowledge/iot/app/monitor-playback.md +557 -0
- package/resource/knowledge/iot/app/remote-control.md +139 -0
- package/resource/knowledge/iot/app/video-call.md +559 -0
- package/resource/knowledge/iot/app/wechat-call/device-call-miniprogram.md +204 -0
- package/resource/knowledge/iot/app/wechat-call/miniprogram-call-device.md +193 -0
- package/resource/knowledge/iot/client-api/app/android/txiotcallsession.md +189 -0
- package/resource/knowledge/iot/client-api/app/android/txiotcloudstorage.md +77 -0
- package/resource/knowledge/iot/client-api/app/android/txiotdevicemanager.md +175 -0
- package/resource/knowledge/iot/client-api/app/android/txiotengine.md +172 -0
- package/resource/knowledge/iot/client-api/app/android/txiotenginedef.md +163 -0
- package/resource/knowledge/iot/client-api/app/android/txiotfamilymanager.md +146 -0
- package/resource/knowledge/iot/client-api/app/android/txiotmonitorsession.md +246 -0
- package/resource/knowledge/iot/client-api/app/harmonyos/txiotcallsession.md +191 -0
- package/resource/knowledge/iot/client-api/app/harmonyos/txiotcloudstorage.md +77 -0
- package/resource/knowledge/iot/client-api/app/harmonyos/txiotdevicemanager.md +183 -0
- package/resource/knowledge/iot/client-api/app/harmonyos/txiotengine.md +186 -0
- package/resource/knowledge/iot/client-api/app/harmonyos/txiotenginedef.md +164 -0
- package/resource/knowledge/iot/client-api/app/harmonyos/txiotfamilymanager.md +150 -0
- package/resource/knowledge/iot/client-api/app/harmonyos/txiotmonitorsession.md +252 -0
- package/resource/knowledge/iot/client-api/app/ios/txiotcallsession.md +199 -0
- package/resource/knowledge/iot/client-api/app/ios/txiotcloudstorage.md +76 -0
- package/resource/knowledge/iot/client-api/app/ios/txiotdevicemanager.md +173 -0
- package/resource/knowledge/iot/client-api/app/ios/txiotengine.md +173 -0
- package/resource/knowledge/iot/client-api/app/ios/txiotenginedef.md +171 -0
- package/resource/knowledge/iot/client-api/app/ios/txiotfamilymanager.md +146 -0
- package/resource/knowledge/iot/client-api/app/ios/txiotmonitorsession.md +250 -0
- package/resource/knowledge/iot/client-api/app/miniprogram/txiotcallsession.md +180 -0
- package/resource/knowledge/iot/client-api/app/miniprogram/txiotcloudstorage.md +76 -0
- package/resource/knowledge/iot/client-api/app/miniprogram/txiotdevicemanager.md +281 -0
- package/resource/knowledge/iot/client-api/app/miniprogram/txiotengine.md +183 -0
- package/resource/knowledge/iot/client-api/app/miniprogram/txiotenginedef.md +152 -0
- package/resource/knowledge/iot/client-api/app/miniprogram/txiotfamilymanager.md +179 -0
- package/resource/knowledge/iot/client-api/app/miniprogram/txiotmonitorsession.md +226 -0
- package/resource/knowledge/iot/client-api/device/android-smart-device.md +813 -0
- package/resource/knowledge/iot/client-api/device/linux-rtos-device.md +2027 -0
- package/resource/knowledge/iot/device/activate-service.md +167 -0
- package/resource/knowledge/iot/device/ai-voice-chat/advanced-features/custom-agent.md +171 -0
- package/resource/knowledge/iot/device/ai-voice-chat/advanced-features/smart-device-control.md +721 -0
- package/resource/knowledge/iot/device/ai-voice-chat/integration-guide/android-smart-device.md +247 -0
- package/resource/knowledge/iot/device/ai-voice-chat/integration-guide/linux-rtos-device.md +368 -0
- package/resource/knowledge/iot/device/ai-voice-chat/overview.md +84 -0
- package/resource/knowledge/iot/device/cloud-storage.md +696 -0
- package/resource/knowledge/iot/device/data-model.md +972 -0
- package/resource/knowledge/iot/device/demo/android-smart-device.md +97 -0
- package/resource/knowledge/iot/device/demo/linux-rtos-device.md +106 -0
- package/resource/knowledge/iot/device/device-ota-upgrade.md +237 -0
- package/resource/knowledge/iot/device/dynamic-registration/android-smart-device.md +275 -0
- package/resource/knowledge/iot/device/dynamic-registration/linux-rtos-device.md +297 -0
- package/resource/knowledge/iot/device/login-and-register/android-smart-device.md +131 -0
- package/resource/knowledge/iot/device/login-and-register/linux-rtos-device.md +291 -0
- package/resource/knowledge/iot/device/monitor-pushing/android-smart-device.md +237 -0
- package/resource/knowledge/iot/device/monitor-pushing/linux-rtos-device.md +493 -0
- package/resource/knowledge/iot/device/video-call/android-smart-device.md +299 -0
- package/resource/knowledge/iot/device/video-call/linux-rtos-device.md +465 -0
- package/resource/knowledge/iot/device/wechat-call/android-smart-device.md +534 -0
- package/resource/knowledge/iot/device/wechat-call/linux-rtos-device.md +681 -0
- package/resource/knowledge/iot/kids-watch/overview.md +28 -0
- package/resource/knowledge/iot/security-camera/activate-service.md +74 -0
- package/resource/knowledge/iot/security-camera/monitor-playback/app-integration.md +302 -0
- package/resource/knowledge/iot/security-camera/monitor-playback/device-integration/android.md +237 -0
- package/resource/knowledge/iot/security-camera/monitor-playback/device-integration/linux-rtos.md +493 -0
- package/resource/knowledge/iot/security-camera/overview.md +31 -0
- package/resource/knowledge/iot/security-camera/ptz-control.md +153 -0
- package/resource/knowledge/iot/security-camera/recording-and-snapshot.md +182 -0
- package/resource/knowledge/iot/security-camera/voice-intercom.md +174 -0
- package/resource/knowledge/iot/smart-vision/serviceoverview.md +114 -0
- package/resource/knowledge/iot/voice-recorder/app-demo.md +99 -0
- package/resource/knowledge/iot/voice-recorder/device-demo.md +189 -0
- package/resource/knowledge/iot/voice-recorder/overview.md +64 -0
- package/resource/knowledge/livekit/core-sdk/features/android/base-beauty.md +0 -2
- package/resource/knowledge/livekit/core-sdk/features/android/gift.md +0 -4
- package/resource/knowledge/livekit/core-sdk/features/android/live-list.md +0 -2
- package/resource/knowledge/livekit/core-sdk/features/flutter/audio-effect.md +0 -2
- package/resource/knowledge/livekit/core-sdk/features/flutter/base-beauty.md +0 -2
- package/resource/knowledge/livekit/core-sdk/features/flutter/gift.md +0 -4
- package/resource/knowledge/livekit/core-sdk/features/ios/base-beauty.md +0 -2
- package/resource/knowledge/livekit/core-sdk/features/ios/gift.md +0 -4
- package/resource/knowledge/livekit/core-sdk/features/ios/live-list.md +0 -2
- package/resource/knowledge/livekit/core-sdk/features/uni-app/base-beauty.md +0 -2
- package/resource/knowledge/livekit/core-sdk/features/uni-app/gift.md +0 -4
- package/resource/knowledge/livekit/core-sdk/live/android/co-guest-interaction.md +0 -4
- package/resource/knowledge/livekit/core-sdk/live/android/host-interaction.md +0 -10
- package/resource/knowledge/livekit/core-sdk/live/android/integration.md +0 -2
- package/resource/knowledge/livekit/core-sdk/live/flutter/co-guest-interaction.md +0 -2
- package/resource/knowledge/livekit/core-sdk/live/flutter/host-interaction.md +0 -10
- package/resource/knowledge/livekit/core-sdk/live/flutter/integration.md +0 -2
- package/resource/knowledge/livekit/core-sdk/live/ios/co-guest-interaction.md +0 -4
- package/resource/knowledge/livekit/core-sdk/live/ios/host-interaction.md +0 -10
- package/resource/knowledge/livekit/core-sdk/live/ios/integration.md +0 -2
- package/resource/knowledge/livekit/core-sdk/live/uni-app/co-guest-interaction.md +0 -4
- package/resource/knowledge/livekit/core-sdk/live/uni-app/host-interaction.md +0 -4
- package/resource/knowledge/livekit/core-sdk/live/uni-app/integration.md +0 -2
- package/resource/knowledge/livekit/core-sdk/voice/android/co-guest-interaction.md +0 -2
- package/resource/knowledge/livekit/core-sdk/voice/android/host-interaction.md +0 -4
- package/resource/knowledge/livekit/core-sdk/voice/android/integration.md +0 -4
- package/resource/knowledge/livekit/core-sdk/voice/flutter/integration.md +0 -2
- package/resource/knowledge/livekit/core-sdk/voice/ios/co-guest-interaction.md +0 -2
- package/resource/knowledge/livekit/core-sdk/voice/ios/host-interaction.md +0 -4
- package/resource/knowledge/livekit/core-sdk/voice/ios/integration.md +0 -4
- package/resource/knowledge/livekit/core-sdk/voice/uni-app/co-guest-interaction.md +0 -2
- package/resource/knowledge/livekit/core-sdk/voice/uni-app/integration.md +0 -2
- package/resource/knowledge/livekit/faq/error-code.md +0 -2
- package/resource/knowledge/push/config/android/honor.md +16 -1
- package/resource/knowledge/push/config/android/huawei.md +20 -4
- package/resource/knowledge/push/config/android/meizu.md +19 -5
- package/resource/knowledge/push/config/android/vivo.md +20 -1
- package/resource/knowledge/push/config/donut.md +0 -2
- package/resource/knowledge/push/config/harmonyos.md +21 -5
- package/resource/knowledge/push/integration/android.md +0 -5
- package/resource/knowledge/push/integration/donut.md +0 -3
- package/resource/knowledge/push/integration/flutter.md +1 -4
- package/resource/knowledge/push/integration/harmonyos.md +0 -3
- package/resource/knowledge/push/integration/ios.md +0 -3
- package/resource/knowledge/push/integration/react-native.md +0 -5
- package/resource/knowledge/push/integration/uni-app.md +0 -3
- package/resource/knowledge/push/integration/unity.md +0 -3
- package/resource/knowledge/push/integration/unreal-engine.md +0 -3
- package/resource/knowledge/roomkit/core-sdk/android/call-user-to-room.md +0 -2
- package/resource/knowledge/roomkit/core-sdk/android/room-participant-state.md +0 -2
- package/resource/knowledge/roomkit/core-sdk/android/room-state.md +0 -2
- package/resource/knowledge/roomkit/core-sdk/android/schedule-room.md +0 -2
- package/resource/knowledge/roomkit/core-sdk/flutter/call-user-to-room.md +0 -2
- package/resource/knowledge/roomkit/core-sdk/flutter/room-participant-state.md +0 -2
- package/resource/knowledge/roomkit/core-sdk/flutter/room-state.md +0 -2
- package/resource/knowledge/roomkit/core-sdk/flutter/schedule-room.md +0 -2
- package/resource/knowledge/roomkit/core-sdk/ios/call-user-to-room.md +0 -2
- package/resource/knowledge/roomkit/core-sdk/ios/room-participant-state.md +0 -2
- package/resource/knowledge/roomkit/core-sdk/ios/room-state.md +0 -2
- package/resource/knowledge/roomkit/core-sdk/ios/schedule-room.md +0 -2
- package/resource/knowledge/roomkit/core-sdk/vue/room-view.md +0 -3
- package/resource/knowledge/roomkit/preset/uniapp-app/integration.md +18 -14
- package/resource/knowledge/rtcengine/web/how_to_run_web_sdk_demo.md +0 -2
- package/resource/knowledge/rtcengine/web/plugin/23-advanced-support-detection.md +30 -0
- package/resource/knowledge/rtcengine/web/plugin/24-advanced-network-quality.md +0 -2
- package/resource/knowledge/rtcengine/web/plugin/31-advanced-plugins-overview.md +1 -1
- package/resource/knowledge/rtcengine/web/plugin/35-advanced-ai-denoiser.md +1 -1
- package/resource/knowledge/rtcengine/web/plugin/36-advanced-virtual-background.md +4 -0
- package/resource/knowledge/trtc/android/TRTCCloud.md +94 -85
- package/resource/knowledge/trtc/android/TRTCCloudDef.md +48 -46
- package/resource/knowledge/trtc/android/TRTCCloudListener.md +48 -48
- package/resource/knowledge/trtc/android/TXAudioEffectManager.md +18 -18
- package/resource/knowledge/trtc/android/TXDeviceManager.md +4 -4
- package/resource/knowledge/trtc/android/TXLiteAVCode.md +1 -0
- package/resource/knowledge/trtc/c++/ITRTCCloud.md +90 -90
- package/resource/knowledge/trtc/c++/ITXAudioEffectManager.md +18 -18
- package/resource/knowledge/trtc/c++/ITXDeviceManager.md +9 -9
- package/resource/knowledge/trtc/c++/TRTCCloudCallback.md +47 -47
- package/resource/knowledge/trtc/c++/TRTCTypeDef.md +47 -45
- package/resource/knowledge/trtc/c++/TXLiteAVCode.md +1 -0
- package/resource/knowledge/trtc/ios/TRTCCloud.md +93 -93
- package/resource/knowledge/trtc/ios/TRTCCloudDelegate.md +50 -50
- package/resource/knowledge/trtc/ios/TXAudioEffectManager.md +8 -8
- package/resource/knowledge/trtc/ios/TXDeviceManager.md +9 -9
- package/resource/knowledge/trtc/ios/TXLiteAVCode.md +1 -0
- package/resource/knowledge/trtc/ios/TXLiveSDKTypeDef.md +48 -46
- package/server-config.js +8 -9
- package/server.js +1 -1
- package/tools/common/finalize_answer.js +1 -1
- package/tools/common/present_framework_choice.js +1 -1
- package/tools/common/search_trtc_knowledge.js +22 -3
- package/tools/definitions/search_trtc_knowledge_def.js +3 -2
- package/utils/preflight-route.js +19 -2
- package/utils/report-cls-client.js +1 -1
- package/utils/report-es-client.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## Version 1.7.8 @2026.09.20
|
|
4
|
+
|
|
5
|
+
### 新增
|
|
6
|
+
- 新增基于 RRF 的 BM25 + Dense 混合检索能力,提升复杂自然语言查询下的召回与排序稳定性。
|
|
7
|
+
- 新增向量索引构建流程:接入 `build:vectors`,在构建阶段生成/更新向量索引,支持检索链路的语义召回。
|
|
8
|
+
|
|
9
|
+
### 变更
|
|
10
|
+
- 优化框架前置门控规则:扩展跨平台问法识别,新增英文 `callback` / `server callback` 信号,降低服务端回调类问题被误判为必须先选框架的概率。
|
|
11
|
+
- 增强 `preflight-route` 的 prompt 框架识别能力:补充 `vue/react` 显式信号采集,提升 product-only 入口下的框架补全准确性。
|
|
12
|
+
|
|
13
|
+
### 修复
|
|
14
|
+
- 修复部分 Chat 场景下因框架信号缺失或英文回调问法导致的 `needs_framework` 误触发问题,减少本应可直接召回的请求被前置拦截。
|
|
15
|
+
|
|
3
16
|
## Version 1.7.7 @2026.09.11
|
|
4
17
|
|
|
5
18
|
### 变更
|
package/README.md
CHANGED
|
@@ -88,17 +88,15 @@
|
|
|
88
88
|
|
|
89
89
|
> 仅展示最近更新,完整历史请查看 `CHANGELOG.md`。
|
|
90
90
|
|
|
91
|
-
## Version 1.7.
|
|
91
|
+
## Version 1.7.8 @2026.09.20
|
|
92
92
|
|
|
93
|
-
###
|
|
94
|
-
-
|
|
95
|
-
|
|
96
|
-
## Version 1.7.6 @2026.09.09
|
|
93
|
+
### 新增
|
|
94
|
+
- 新增基于 RRF 的 BM25 + Dense 混合检索能力,提升复杂自然语言查询下的召回与排序稳定性。
|
|
95
|
+
- 新增向量索引构建流程:接入 `build:vectors`,在构建阶段生成/更新向量索引,支持检索链路的语义召回。
|
|
97
96
|
|
|
98
97
|
### 变更
|
|
99
|
-
-
|
|
100
|
-
-
|
|
101
|
-
- 调整全局 Workflow 语义为“咨询流专用”,降低工具路由歧义。
|
|
98
|
+
- 优化框架前置门控规则:扩展跨平台问法识别,新增英文 `callback` / `server callback` 信号,降低服务端回调类问题被误判为必须先选框架的概率。
|
|
99
|
+
- 增强 `preflight-route` 的 prompt 框架识别能力:补充 `vue/react` 显式信号采集,提升 product-only 入口下的框架补全准确性。
|
|
102
100
|
|
|
103
101
|
### 修复
|
|
104
|
-
-
|
|
102
|
+
- 修复部分 Chat 场景下因框架信号缺失或英文回调问法导致的 `needs_framework` 误触发问题,减少本应可直接召回的请求被前置拦截。
|
package/index.js
CHANGED
|
@@ -15,14 +15,14 @@ async function main() {
|
|
|
15
15
|
console.error('RTC Official MCP Server running on stdio');
|
|
16
16
|
}
|
|
17
17
|
main().then(() => {
|
|
18
|
-
reportCLSClient({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
});
|
|
18
|
+
// reportCLSClient({
|
|
19
|
+
// method: 'mcp_connect_success',
|
|
20
|
+
// from: 'stdio',
|
|
21
|
+
// info: JSON.stringify({
|
|
22
|
+
// status: 'success',
|
|
23
|
+
// latency_ms: Date.now() - startTime,
|
|
24
|
+
// }),
|
|
25
|
+
// });
|
|
26
26
|
}).catch((error) => {
|
|
27
27
|
reportCLSClient({
|
|
28
28
|
method: 'mcp_connect_fail',
|
package/knowledge/chunker.js
CHANGED
|
@@ -130,6 +130,7 @@ function buildChunk(meta, source, title, body, id, char_start, char_end, extra)
|
|
|
130
130
|
doc_kind: meta.doc_kind,
|
|
131
131
|
capability_status: meta.capability_status,
|
|
132
132
|
retrievable_scope: meta.retrievable_scope,
|
|
133
|
+
feature_domain: meta.feature_domain,
|
|
133
134
|
meta_source: meta.meta_source,
|
|
134
135
|
meta_override_fields: meta.meta_override_fields,
|
|
135
136
|
title,
|
|
@@ -112,22 +112,29 @@ export function resolveFragmentDocUrls(source, content, fullSourceContent) {
|
|
|
112
112
|
}
|
|
113
113
|
return urls.slice(0, 3);
|
|
114
114
|
}
|
|
115
|
-
/** 从 fragments
|
|
116
|
-
*
|
|
115
|
+
/** 从 fragments 汇总去重后的参考链接列表(rank 与 fragment.rank 对齐)。
|
|
116
|
+
*
|
|
117
|
+
* 规则:
|
|
118
|
+
* 1. 每个 fragment 仅生成一个 reference(优先 doc_urls[0]),避免单片段膨胀出多个 references。
|
|
119
|
+
* 2. rank 直接沿用 fragment.rank,保证 references 与 fragments 位置一一对应;
|
|
120
|
+
* 历史实现用 refs.length+1 自增序号,导致 fragments[0] 无 URL 时 references[0]
|
|
121
|
+
* 的 rank/source 都会与 fragments[0] 错位(LLM/观测层难对齐)。
|
|
122
|
+
* 3. 当 fragment.doc_urls 全为空或全为噪音 URL 时,仍生成占位 reference(url=''),
|
|
123
|
+
* 保持 references.length == fragments.length;下游若判空可用 url==='' 兜底展示 title/source。
|
|
124
|
+
* 4. URL 去重:同一 URL 只在首次出现的 fragment 上保留,后续同 URL fragment 降级为占位。 */
|
|
117
125
|
export function buildReferences(fragments) {
|
|
118
126
|
const refs = [];
|
|
119
127
|
const seen = new Set();
|
|
120
128
|
for (const fragment of fragments) {
|
|
121
129
|
const primaryUrl = fragment.doc_urls.find((u) => isUsefulDocUrl(u));
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
seen.add(primaryUrl);
|
|
130
|
+
// 占位规则:无可用 URL 或 URL 已被更靠前的 fragment 占用 → url='',rank/source 仍保留
|
|
131
|
+
const url = primaryUrl && !seen.has(primaryUrl) ? primaryUrl : '';
|
|
132
|
+
if (url)
|
|
133
|
+
seen.add(url);
|
|
127
134
|
refs.push({
|
|
128
|
-
rank:
|
|
135
|
+
rank: fragment.rank,
|
|
129
136
|
title: fragment.title,
|
|
130
|
-
url
|
|
137
|
+
url,
|
|
131
138
|
source: fragment.source,
|
|
132
139
|
});
|
|
133
140
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { LocalIndex } from 'vectra';
|
|
5
|
+
import { attachPoolContent } from '../content-loader.js';
|
|
6
|
+
import { chunkSearchText } from '../bm25.js';
|
|
7
|
+
import { embedTexts } from './embed-model.js';
|
|
8
|
+
import { allIndexPath } from './vector-index.js';
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const RESOURCE_ROOT = path.resolve(__dirname, '../../resource');
|
|
11
|
+
const BATCH = 32;
|
|
12
|
+
function loadManifest() {
|
|
13
|
+
const p = path.join(RESOURCE_ROOT, 'index', 'manifest.json');
|
|
14
|
+
const m = JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
15
|
+
return m.chunks;
|
|
16
|
+
}
|
|
17
|
+
/** chunk 内容指纹:offset / title 变化即视为内容变化,触发重算 */
|
|
18
|
+
function fingerprint(chunk) {
|
|
19
|
+
return `${chunk.source}:${chunk.char_start}:${chunk.char_end}:${chunk.title}`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* vectra 主键(唯一)。manifest 存在 27 个重复 id(同文件同名 section),
|
|
23
|
+
* 直接用 chunk.id 做主键会互相覆盖导致丢向量;用 id + char_start 复合键保证唯一。
|
|
24
|
+
* metadata.chunk_id 仍存原始 id,供运行期召回映射回 pool。
|
|
25
|
+
*/
|
|
26
|
+
function vectorKey(chunk) {
|
|
27
|
+
return `${chunk.id}@${chunk.char_start}`;
|
|
28
|
+
}
|
|
29
|
+
/** product 小写归一:容忍数据源大小写不一致(如 livekit faq 的 'Live' vs 'live'),
|
|
30
|
+
* 与 vector-index.queryVectors 的归一保持一致,避免向量召回因大小写漏检。*/
|
|
31
|
+
function normalizedProduct(chunk) {
|
|
32
|
+
return (chunk.product || '*').toLowerCase();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 增量构建单一全局向量库(不再按 product 物理分片,product 作为 metadata 字段存入每条 item):
|
|
36
|
+
* - 已存在且指纹一致 → 跳过
|
|
37
|
+
* - 新增 / 指纹变化 → 重新编码 upsert
|
|
38
|
+
* - manifest 已删除的 id → 从索引 delete
|
|
39
|
+
*/
|
|
40
|
+
export async function buildVectors() {
|
|
41
|
+
const chunks = loadManifest();
|
|
42
|
+
console.warn(`[build-vectors] ${chunks.length} chunks (single global index)`);
|
|
43
|
+
const idx = new LocalIndex(allIndexPath());
|
|
44
|
+
if (!(await idx.isIndexCreated())) {
|
|
45
|
+
await idx.createIndex({ version: 1, deleteIfExists: false });
|
|
46
|
+
}
|
|
47
|
+
// 现有条目:vectorKey → fingerprint
|
|
48
|
+
const existing = new Map();
|
|
49
|
+
for (const item of await idx.listItems()) {
|
|
50
|
+
const key = String(item.metadata.vkey ?? item.metadata.chunk_id);
|
|
51
|
+
existing.set(key, String(item.metadata.fp ?? ''));
|
|
52
|
+
}
|
|
53
|
+
const wantKeys = new Set(chunks.map((c) => vectorKey(c)));
|
|
54
|
+
const toEncode = chunks.filter((c) => existing.get(vectorKey(c)) !== fingerprint(c));
|
|
55
|
+
const toDelete = [...existing.keys()].filter((key) => !wantKeys.has(key));
|
|
56
|
+
if (toDelete.length > 0) {
|
|
57
|
+
await idx.deleteItems(toDelete);
|
|
58
|
+
}
|
|
59
|
+
if (toEncode.length === 0) {
|
|
60
|
+
console.warn(`[build-vectors] up-to-date (${chunks.length} chunks, +0/-${toDelete.length})`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const withContent = attachPoolContent(toEncode);
|
|
64
|
+
const texts = withContent.map((c) => chunkSearchText(c));
|
|
65
|
+
await idx.beginUpdate();
|
|
66
|
+
try {
|
|
67
|
+
for (let i = 0; i < texts.length; i += BATCH) {
|
|
68
|
+
const batchChunks = withContent.slice(i, i + BATCH);
|
|
69
|
+
const vectors = await embedTexts(texts.slice(i, i + BATCH));
|
|
70
|
+
for (let j = 0; j < batchChunks.length; j += 1) {
|
|
71
|
+
const c = batchChunks[j];
|
|
72
|
+
await idx.upsertItem({
|
|
73
|
+
id: vectorKey(c),
|
|
74
|
+
vector: Array.from(vectors[j]),
|
|
75
|
+
metadata: {
|
|
76
|
+
chunk_id: c.id,
|
|
77
|
+
vkey: vectorKey(c),
|
|
78
|
+
fp: fingerprint(c),
|
|
79
|
+
product: normalizedProduct(c),
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
await idx.endUpdate();
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
idx.cancelUpdate();
|
|
88
|
+
throw e;
|
|
89
|
+
}
|
|
90
|
+
console.warn(`[build-vectors] +${toEncode.length}/-${toDelete.length} (total ${chunks.length})`);
|
|
91
|
+
console.warn('[build-vectors] done');
|
|
92
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { pipeline } from '@huggingface/transformers';
|
|
2
|
+
/**
|
|
3
|
+
* 本地 ONNX 文本编码器。
|
|
4
|
+
*
|
|
5
|
+
* 模型:BGE-small-zh-v1.5(512 维,中文检索优化,体积轻)。
|
|
6
|
+
* 后端:device='cpu'(@huggingface/transformers 4.x 的 Node 端支持 coreml/webgpu/cpu;
|
|
7
|
+
* 'wasm' 仅浏览器可用)。cpu 后端底层走 onnxruntime-node。
|
|
8
|
+
*/
|
|
9
|
+
const MODEL_ID = 'Xenova/bge-small-zh-v1.5';
|
|
10
|
+
export const EMBED_DIM = 512;
|
|
11
|
+
let extractorPromise = null;
|
|
12
|
+
function getExtractor() {
|
|
13
|
+
if (!extractorPromise) {
|
|
14
|
+
extractorPromise = pipeline('feature-extraction', MODEL_ID, {
|
|
15
|
+
device: 'cpu',
|
|
16
|
+
dtype: 'q8',
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return extractorPromise;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 编码一批文本 → 归一化向量(余弦相似度可直接点积)。
|
|
23
|
+
* mean pooling + L2 normalize(BGE 官方推荐)。
|
|
24
|
+
*/
|
|
25
|
+
export async function embedTexts(texts) {
|
|
26
|
+
if (texts.length === 0)
|
|
27
|
+
return [];
|
|
28
|
+
const extractor = await getExtractor();
|
|
29
|
+
const output = await extractor(texts, { pooling: 'mean', normalize: true });
|
|
30
|
+
const rows = output.tolist();
|
|
31
|
+
return rows.map((row) => Float32Array.from(row));
|
|
32
|
+
}
|
|
33
|
+
/** 单条查询编码(运行期热路径,texts=1) */
|
|
34
|
+
export async function embedQuery(text) {
|
|
35
|
+
const [vec] = await embedTexts([text]);
|
|
36
|
+
return vec;
|
|
37
|
+
}
|
|
38
|
+
/** BGE 建议给查询加指令前缀以提升检索质量 */
|
|
39
|
+
export function withQueryInstruction(query) {
|
|
40
|
+
return `为这个句子生成表示以用于检索相关文章:${query}`;
|
|
41
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { embedQuery, withQueryInstruction } from './embed-model.js';
|
|
2
|
+
import { queryVectors } from './vector-index.js';
|
|
3
|
+
export const DEFAULT_VECTOR_OPTS = {
|
|
4
|
+
alpha: 0.15,
|
|
5
|
+
baseBeta: 0.3,
|
|
6
|
+
topK: 30,
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* 软融合:在既有 BM25 ranked 上叠加向量语义信号,**输出仍是 BM25 量纲**。
|
|
10
|
+
*
|
|
11
|
+
* 融合公式:
|
|
12
|
+
* BM25 已命中: finalScore = bm25 * (1 + alpha * simNorm)
|
|
13
|
+
* 仅向量命中(补召回): finalScore = topBM25 * baseBeta * (1 + alpha * simNorm)
|
|
14
|
+
*
|
|
15
|
+
* simNorm:对本轮向量候选做 min-max 归一化到 [0,1]。
|
|
16
|
+
*
|
|
17
|
+
* 关键不变量:
|
|
18
|
+
* - 下游 assessConfidence / postProcess / best-practice 竞争 / 低置信裁剪的阈值全部继续有效(量纲不变)
|
|
19
|
+
* - 补召回 base 分 < topBM25 * 0.35,不越过断崖截断保护线
|
|
20
|
+
* - 容错:向量库缺失 / 编码失败 → 返回原 bm25Ranked(降级纯 BM25),绝不阻断主链路
|
|
21
|
+
*/
|
|
22
|
+
export async function vectorAugment(query,
|
|
23
|
+
/** hard 层过滤后的 chunk id 集合(product + retrievable_scope),不应包含 framework/types/scenario 过滤 */
|
|
24
|
+
poolIds, bm25Ranked, products, opts = DEFAULT_VECTOR_OPTS) {
|
|
25
|
+
if (bm25Ranked.length === 0) {
|
|
26
|
+
return {
|
|
27
|
+
ranked: bm25Ranked,
|
|
28
|
+
stats: {
|
|
29
|
+
attempted: false,
|
|
30
|
+
applied: false,
|
|
31
|
+
reason: 'bm25_empty',
|
|
32
|
+
raw_hit_count: 0,
|
|
33
|
+
pool_hit_count: 0,
|
|
34
|
+
bm25_adjusted_count: 0,
|
|
35
|
+
vector_only_added_count: 0,
|
|
36
|
+
alpha: opts.alpha,
|
|
37
|
+
base_beta: opts.baseBeta,
|
|
38
|
+
top_k: opts.topK,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
let hits;
|
|
43
|
+
try {
|
|
44
|
+
const qvec = await embedQuery(withQueryInstruction(query));
|
|
45
|
+
hits = await queryVectors(products, qvec, opts.topK);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return {
|
|
49
|
+
ranked: bm25Ranked,
|
|
50
|
+
stats: {
|
|
51
|
+
attempted: true,
|
|
52
|
+
applied: false,
|
|
53
|
+
reason: 'embed_or_query_failed',
|
|
54
|
+
raw_hit_count: 0,
|
|
55
|
+
pool_hit_count: 0,
|
|
56
|
+
bm25_adjusted_count: 0,
|
|
57
|
+
vector_only_added_count: 0,
|
|
58
|
+
alpha: opts.alpha,
|
|
59
|
+
base_beta: opts.baseBeta,
|
|
60
|
+
top_k: opts.topK,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// 只保留在 hard 层(product + retrievable_scope)过滤后的向量候选。
|
|
65
|
+
// framework/types/scenario 属于软过滤层,不应在此二次砍杀向量语义召回。
|
|
66
|
+
const vecHits = hits.filter((h) => poolIds.has(h.id));
|
|
67
|
+
if (vecHits.length === 0) {
|
|
68
|
+
return {
|
|
69
|
+
ranked: bm25Ranked,
|
|
70
|
+
stats: {
|
|
71
|
+
attempted: true,
|
|
72
|
+
applied: false,
|
|
73
|
+
reason: 'no_vector_hit_in_pool',
|
|
74
|
+
raw_hit_count: hits.length,
|
|
75
|
+
pool_hit_count: 0,
|
|
76
|
+
bm25_adjusted_count: 0,
|
|
77
|
+
vector_only_added_count: 0,
|
|
78
|
+
alpha: opts.alpha,
|
|
79
|
+
base_beta: opts.baseBeta,
|
|
80
|
+
top_k: opts.topK,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// simNorm:min-max 归一化
|
|
85
|
+
const sims = vecHits.map((h) => h.score);
|
|
86
|
+
const minS = Math.min(...sims);
|
|
87
|
+
const maxS = Math.max(...sims);
|
|
88
|
+
const range = maxS - minS || 1;
|
|
89
|
+
const simMap = new Map(vecHits.map((h) => [h.id, (h.score - minS) / range]));
|
|
90
|
+
const topBM25 = bm25Ranked[0].score;
|
|
91
|
+
const bm25Ids = new Set(bm25Ranked.map((item) => item.id));
|
|
92
|
+
let bm25AdjustedCount = 0;
|
|
93
|
+
let vectorOnlyAddedCount = 0;
|
|
94
|
+
// 1) BM25 已命中的 chunk 做加权
|
|
95
|
+
const fused = bm25Ranked.map((item) => {
|
|
96
|
+
const sim = simMap.get(item.id);
|
|
97
|
+
if (sim === undefined)
|
|
98
|
+
return item;
|
|
99
|
+
bm25AdjustedCount += 1;
|
|
100
|
+
return { id: item.id, score: item.score * (1 + opts.alpha * sim) };
|
|
101
|
+
});
|
|
102
|
+
// 2) 向量补召回(BM25 未命中但语义相近)
|
|
103
|
+
const baseScore = topBM25 * opts.baseBeta;
|
|
104
|
+
for (const h of vecHits) {
|
|
105
|
+
if (bm25Ids.has(h.id))
|
|
106
|
+
continue;
|
|
107
|
+
const sim = simMap.get(h.id) ?? 0;
|
|
108
|
+
fused.push({ id: h.id, score: baseScore * (1 + opts.alpha * sim) });
|
|
109
|
+
vectorOnlyAddedCount += 1;
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
ranked: fused.sort((a, b) => b.score - a.score),
|
|
113
|
+
stats: {
|
|
114
|
+
attempted: true,
|
|
115
|
+
applied: bm25AdjustedCount > 0 || vectorOnlyAddedCount > 0,
|
|
116
|
+
reason: 'fused',
|
|
117
|
+
raw_hit_count: hits.length,
|
|
118
|
+
pool_hit_count: vecHits.length,
|
|
119
|
+
bm25_adjusted_count: bm25AdjustedCount,
|
|
120
|
+
vector_only_added_count: vectorOnlyAddedCount,
|
|
121
|
+
alpha: opts.alpha,
|
|
122
|
+
base_beta: opts.baseBeta,
|
|
123
|
+
top_k: opts.topK,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { LocalIndex } from 'vectra';
|
|
4
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
/**
|
|
6
|
+
* 运行期向量库根目录。
|
|
7
|
+
* 编译后本文件位于 dist/knowledge/embedding/,向量产物在 dist/resource/index/vectors/。
|
|
8
|
+
*/
|
|
9
|
+
function vectorsRoot() {
|
|
10
|
+
return path.resolve(__dirname, '../../resource/index/vectors');
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 单一全局向量索引目录(不再按 product 物理分片)。
|
|
14
|
+
*
|
|
15
|
+
* 背景:vectra LocalIndex.queryItems 本身就是全量线性扫描 + 堆排序取 topK
|
|
16
|
+
* (非 ANN 索引结构),按 product 物理拆分索引文件不会带来性能收益
|
|
17
|
+
* (全库 4173 条 chunk 的线性扫描耗时在毫秒级,远小于 embedQuery 的 ONNX 推理耗时)。
|
|
18
|
+
* 反而在 products 未指定时(恰恰是最需要语义补召回的模糊查询场景),
|
|
19
|
+
* 只能退回体量极小的 '*' 分片,导致向量召回几乎失效——这是不合理的设计。
|
|
20
|
+
*
|
|
21
|
+
* 现改为单一索引 + metadata.product 字段 + queryItems 的 MetadataFilter 过滤:
|
|
22
|
+
* - products 有值 → filter: { product: { $in: products } }
|
|
23
|
+
* - products 为空 → 不传 filter,全库扫描(行为对齐 BM25 的全量兜底语义)
|
|
24
|
+
*
|
|
25
|
+
* 索引直接落在 vectors/ 目录下(index.json 等文件),不再套一层多余的 'all' 子目录。
|
|
26
|
+
*/
|
|
27
|
+
export function allIndexPath() {
|
|
28
|
+
return vectorsRoot();
|
|
29
|
+
}
|
|
30
|
+
// 进程内缓存已加载的 LocalIndex,避免每次查询重开
|
|
31
|
+
let cachedIndex;
|
|
32
|
+
async function loadIndex() {
|
|
33
|
+
if (cachedIndex !== undefined)
|
|
34
|
+
return cachedIndex;
|
|
35
|
+
const idx = new LocalIndex(allIndexPath());
|
|
36
|
+
const exists = await idx.isIndexCreated();
|
|
37
|
+
cachedIndex = exists ? idx : null;
|
|
38
|
+
return cachedIndex;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 在全局向量库里查 topK 相似 chunk,按 products 做 metadata 过滤。
|
|
42
|
+
* products 为空/未指定 → 不加过滤条件,全库扫描(不再退化为几乎空的 '*' 分片)。
|
|
43
|
+
* 索引不存在 → 返回空数组(容错,降级为纯 BM25)。
|
|
44
|
+
*/
|
|
45
|
+
export async function queryVectors(products, queryVec, topK) {
|
|
46
|
+
const idx = await loadIndex();
|
|
47
|
+
if (!idx)
|
|
48
|
+
return [];
|
|
49
|
+
// product 小写归一,与 build-vectors.groupByProduct 一致(容忍数据源大小写不一致)
|
|
50
|
+
const normalized = products && products.length
|
|
51
|
+
? [...new Set(products.map((p) => p.toLowerCase()))]
|
|
52
|
+
: [];
|
|
53
|
+
const vec = Array.from(queryVec);
|
|
54
|
+
const filter = normalized.length ? { product: { $in: normalized } } : undefined;
|
|
55
|
+
const results = await idx.queryItems(vec, '', topK, filter);
|
|
56
|
+
const best = new Map();
|
|
57
|
+
for (const r of results) {
|
|
58
|
+
const id = String(r.item.metadata.chunk_id);
|
|
59
|
+
const prev = best.get(id);
|
|
60
|
+
if (prev === undefined || prev < r.score)
|
|
61
|
+
best.set(id, r.score);
|
|
62
|
+
}
|
|
63
|
+
return [...best.entries()]
|
|
64
|
+
.map(([id, score]) => ({ id, score }))
|
|
65
|
+
.sort((a, b) => b.score - a.score)
|
|
66
|
+
.slice(0, topK);
|
|
67
|
+
}
|
|
68
|
+
export function clearVectorCache() {
|
|
69
|
+
cachedIndex = undefined;
|
|
70
|
+
}
|
|
@@ -52,6 +52,24 @@ function normalizeRetrievableScope(value) {
|
|
|
52
52
|
return 'always';
|
|
53
53
|
return normalizeStringArray(value);
|
|
54
54
|
}
|
|
55
|
+
function normalizeFeatureDomain(value) {
|
|
56
|
+
const allowed = [
|
|
57
|
+
'app',
|
|
58
|
+
'app_demo',
|
|
59
|
+
'app_integration',
|
|
60
|
+
'device',
|
|
61
|
+
'device_demo',
|
|
62
|
+
'device_integration',
|
|
63
|
+
'security-camera',
|
|
64
|
+
'smart-vision',
|
|
65
|
+
'kids-watch',
|
|
66
|
+
'voice-recorder',
|
|
67
|
+
'client_api',
|
|
68
|
+
];
|
|
69
|
+
return allowed.includes(value)
|
|
70
|
+
? value
|
|
71
|
+
: undefined;
|
|
72
|
+
}
|
|
55
73
|
function parseFrontmatterObject(input) {
|
|
56
74
|
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
57
75
|
return {};
|
|
@@ -135,6 +153,11 @@ export function resolveFrontmatterMeta(rawMarkdown) {
|
|
|
135
153
|
frontmatterMeta.retrievable_scope = retrievableScope;
|
|
136
154
|
overrideFields.push('retrievable_scope');
|
|
137
155
|
}
|
|
156
|
+
const featureDomain = normalizeFeatureDomain(fm.feature_domain);
|
|
157
|
+
if (featureDomain) {
|
|
158
|
+
frontmatterMeta.feature_domain = featureDomain;
|
|
159
|
+
overrideFields.push('feature_domain');
|
|
160
|
+
}
|
|
138
161
|
return {
|
|
139
162
|
content,
|
|
140
163
|
contentOffset,
|
package/knowledge/path-meta.js
CHANGED
|
@@ -55,6 +55,23 @@ function inferShareScope(fileName) {
|
|
|
55
55
|
}
|
|
56
56
|
return 'always';
|
|
57
57
|
}
|
|
58
|
+
function inferIotFramework(normalizedPath) {
|
|
59
|
+
const pathLower = normalizedPath.toLowerCase();
|
|
60
|
+
if (pathLower.includes('miniprogram'))
|
|
61
|
+
return ['minprogram'];
|
|
62
|
+
if (pathLower.includes('react-native') || /\/(rn)\//.test(pathLower))
|
|
63
|
+
return ['rn'];
|
|
64
|
+
if (pathLower.includes('android'))
|
|
65
|
+
return ['android'];
|
|
66
|
+
if (pathLower.includes('ios'))
|
|
67
|
+
return ['ios'];
|
|
68
|
+
if (pathLower.includes('harmonyos'))
|
|
69
|
+
return ['harmonyos'];
|
|
70
|
+
if (pathLower.includes('linux-rtos') || pathLower.includes('/linux') || pathLower.includes('rtos')) {
|
|
71
|
+
return ['linux'];
|
|
72
|
+
}
|
|
73
|
+
return ['*'];
|
|
74
|
+
}
|
|
58
75
|
export function inferPathMeta(relativeSource) {
|
|
59
76
|
const normalized = relativeSource.replace(/\\/g, '/');
|
|
60
77
|
const parts = normalized.split('/');
|
|
@@ -69,6 +86,7 @@ export function inferPathMeta(relativeSource) {
|
|
|
69
86
|
let doc_kind;
|
|
70
87
|
let capability_status;
|
|
71
88
|
let retrievable_scope;
|
|
89
|
+
let feature_domain;
|
|
72
90
|
if (normalized.startsWith('knowledge/share/')) {
|
|
73
91
|
// 通配语料(跨产品共享)显式建模,不再依赖默认兜底路径。
|
|
74
92
|
product = '*';
|
|
@@ -76,6 +94,43 @@ export function inferPathMeta(relativeSource) {
|
|
|
76
94
|
types.push('faq', 'product');
|
|
77
95
|
retrievable_scope = inferShareScope(fileName);
|
|
78
96
|
}
|
|
97
|
+
else if (normalized.startsWith('knowledge/iot/')) {
|
|
98
|
+
product = 'iot';
|
|
99
|
+
framework = inferIotFramework(normalized);
|
|
100
|
+
const top = parts[2] ?? '';
|
|
101
|
+
const topIsFeature = ['app', 'device', 'security-camera', 'smart-vision', 'kids-watch', 'voice-recorder'].includes(top);
|
|
102
|
+
const topIsApi = top === 'client-api';
|
|
103
|
+
if (topIsFeature) {
|
|
104
|
+
types.push('feature');
|
|
105
|
+
}
|
|
106
|
+
else if (topIsApi) {
|
|
107
|
+
types.push('api');
|
|
108
|
+
}
|
|
109
|
+
if (topIsFeature || topIsApi) {
|
|
110
|
+
if (top === 'app') {
|
|
111
|
+
if (parts[3] === 'demo')
|
|
112
|
+
feature_domain = 'app_demo';
|
|
113
|
+
else if (parts[3] === 'integration')
|
|
114
|
+
feature_domain = 'app_integration';
|
|
115
|
+
else
|
|
116
|
+
feature_domain = 'app';
|
|
117
|
+
}
|
|
118
|
+
else if (top === 'device') {
|
|
119
|
+
if (parts[3] === 'demo')
|
|
120
|
+
feature_domain = 'device_demo';
|
|
121
|
+
else if (parts[3] === 'integration')
|
|
122
|
+
feature_domain = 'device_integration';
|
|
123
|
+
else
|
|
124
|
+
feature_domain = 'device';
|
|
125
|
+
}
|
|
126
|
+
else if (top === 'client-api') {
|
|
127
|
+
feature_domain = 'client_api';
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
feature_domain = top;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
79
134
|
else if (normalized.startsWith('knowledge/chat/') || normalized.startsWith('integration/chat/')) {
|
|
80
135
|
product = 'chat';
|
|
81
136
|
if (parts.includes('preset') && parts.length >= 4) {
|
|
@@ -386,6 +441,7 @@ export function inferPathMeta(relativeSource) {
|
|
|
386
441
|
doc_kind,
|
|
387
442
|
capability_status,
|
|
388
443
|
retrievable_scope,
|
|
444
|
+
feature_domain,
|
|
389
445
|
meta_source: 'path',
|
|
390
446
|
meta_override_fields: [],
|
|
391
447
|
};
|