@tencentcloud/sdk-mcp 1.7.8 → 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.
Files changed (172) hide show
  1. package/knowledge/chunker.js +1 -0
  2. package/knowledge/frontmatter-meta.js +23 -0
  3. package/knowledge/path-meta.js +56 -0
  4. package/knowledge/search/filter-pool.js +9 -1
  5. package/knowledge/search/rank-profile.js +25 -0
  6. package/knowledge/search/requires-framework.js +4 -1
  7. package/knowledge/search/search-core.js +56 -1
  8. package/knowledge/search/search-pass.js +126 -5
  9. package/package.json +1 -1
  10. package/resource/index/manifest.json +1 -1
  11. package/resource/index/vectors/index.json +2 -2
  12. package/resource/integration/callkit/preset/android/integration.md +0 -2
  13. package/resource/integration/callkit/preset/ios/integration.md +0 -2
  14. package/resource/integration/livekit/preset/live/android/prepare-integration.md +0 -4
  15. package/resource/integration/livekit/preset/live/ios/prepare-integration.md +0 -4
  16. package/resource/knowledge/callkit/best-practice/android/video-chat-dating.md +0 -2
  17. package/resource/knowledge/callkit/core-sdk/android/answer-call.md +0 -2
  18. package/resource/knowledge/callkit/core-sdk/android/make-call.md +0 -2
  19. package/resource/knowledge/callkit/core-sdk/flutter/answer-call.md +0 -4
  20. package/resource/knowledge/callkit/core-sdk/flutter/make-call.md +0 -2
  21. package/resource/knowledge/callkit/core-sdk/flutter/offline-push.md +0 -2
  22. package/resource/knowledge/callkit/core-sdk/ios/answer-call.md +0 -2
  23. package/resource/knowledge/callkit/core-sdk/ios/make-call.md +0 -4
  24. package/resource/knowledge/callkit/core-sdk/ios/offline-push.md +0 -2
  25. package/resource/knowledge/callkit/core-sdk/uni-app/answer-call.md +0 -2
  26. package/resource/knowledge/callkit/core-sdk/uni-app/make-call.md +0 -2
  27. package/resource/knowledge/callkit/faq/ios.md +0 -2
  28. package/resource/knowledge/callkit/faq/uniapp-app.md +0 -2
  29. package/resource/knowledge/callkit/preset/miniprogram/integration.md +0 -2
  30. package/resource/knowledge/callkit/preset/miniprogram/uniapp-integration.md +0 -2
  31. package/resource/knowledge/iot/app/activate-service.md +95 -0
  32. package/resource/knowledge/iot/app/demo/android.md +93 -0
  33. package/resource/knowledge/iot/app/demo/ios.md +87 -0
  34. package/resource/knowledge/iot/app/demo/miniprogram.md +79 -0
  35. package/resource/knowledge/iot/app/device-binding.md +378 -0
  36. package/resource/knowledge/iot/app/device-sharing.md +414 -0
  37. package/resource/knowledge/iot/app/family-management.md +762 -0
  38. package/resource/knowledge/iot/app/integration/android.md +328 -0
  39. package/resource/knowledge/iot/app/integration/harmonyos.md +326 -0
  40. package/resource/knowledge/iot/app/integration/ios.md +351 -0
  41. package/resource/knowledge/iot/app/integration/miniprogram.md +268 -0
  42. package/resource/knowledge/iot/app/monitor-playback.md +557 -0
  43. package/resource/knowledge/iot/app/remote-control.md +139 -0
  44. package/resource/knowledge/iot/app/video-call.md +559 -0
  45. package/resource/knowledge/iot/app/wechat-call/device-call-miniprogram.md +204 -0
  46. package/resource/knowledge/iot/app/wechat-call/miniprogram-call-device.md +193 -0
  47. package/resource/knowledge/iot/client-api/app/android/txiotcallsession.md +189 -0
  48. package/resource/knowledge/iot/client-api/app/android/txiotcloudstorage.md +77 -0
  49. package/resource/knowledge/iot/client-api/app/android/txiotdevicemanager.md +175 -0
  50. package/resource/knowledge/iot/client-api/app/android/txiotengine.md +172 -0
  51. package/resource/knowledge/iot/client-api/app/android/txiotenginedef.md +163 -0
  52. package/resource/knowledge/iot/client-api/app/android/txiotfamilymanager.md +146 -0
  53. package/resource/knowledge/iot/client-api/app/android/txiotmonitorsession.md +246 -0
  54. package/resource/knowledge/iot/client-api/app/harmonyos/txiotcallsession.md +191 -0
  55. package/resource/knowledge/iot/client-api/app/harmonyos/txiotcloudstorage.md +77 -0
  56. package/resource/knowledge/iot/client-api/app/harmonyos/txiotdevicemanager.md +183 -0
  57. package/resource/knowledge/iot/client-api/app/harmonyos/txiotengine.md +186 -0
  58. package/resource/knowledge/iot/client-api/app/harmonyos/txiotenginedef.md +164 -0
  59. package/resource/knowledge/iot/client-api/app/harmonyos/txiotfamilymanager.md +150 -0
  60. package/resource/knowledge/iot/client-api/app/harmonyos/txiotmonitorsession.md +252 -0
  61. package/resource/knowledge/iot/client-api/app/ios/txiotcallsession.md +199 -0
  62. package/resource/knowledge/iot/client-api/app/ios/txiotcloudstorage.md +76 -0
  63. package/resource/knowledge/iot/client-api/app/ios/txiotdevicemanager.md +173 -0
  64. package/resource/knowledge/iot/client-api/app/ios/txiotengine.md +173 -0
  65. package/resource/knowledge/iot/client-api/app/ios/txiotenginedef.md +171 -0
  66. package/resource/knowledge/iot/client-api/app/ios/txiotfamilymanager.md +146 -0
  67. package/resource/knowledge/iot/client-api/app/ios/txiotmonitorsession.md +250 -0
  68. package/resource/knowledge/iot/client-api/app/miniprogram/txiotcallsession.md +180 -0
  69. package/resource/knowledge/iot/client-api/app/miniprogram/txiotcloudstorage.md +76 -0
  70. package/resource/knowledge/iot/client-api/app/miniprogram/txiotdevicemanager.md +281 -0
  71. package/resource/knowledge/iot/client-api/app/miniprogram/txiotengine.md +183 -0
  72. package/resource/knowledge/iot/client-api/app/miniprogram/txiotenginedef.md +152 -0
  73. package/resource/knowledge/iot/client-api/app/miniprogram/txiotfamilymanager.md +179 -0
  74. package/resource/knowledge/iot/client-api/app/miniprogram/txiotmonitorsession.md +226 -0
  75. package/resource/knowledge/iot/client-api/device/android-smart-device.md +813 -0
  76. package/resource/knowledge/iot/client-api/device/linux-rtos-device.md +2027 -0
  77. package/resource/knowledge/iot/device/activate-service.md +167 -0
  78. package/resource/knowledge/iot/device/ai-voice-chat/advanced-features/custom-agent.md +171 -0
  79. package/resource/knowledge/iot/device/ai-voice-chat/advanced-features/smart-device-control.md +721 -0
  80. package/resource/knowledge/iot/device/ai-voice-chat/integration-guide/android-smart-device.md +247 -0
  81. package/resource/knowledge/iot/device/ai-voice-chat/integration-guide/linux-rtos-device.md +368 -0
  82. package/resource/knowledge/iot/device/ai-voice-chat/overview.md +84 -0
  83. package/resource/knowledge/iot/device/cloud-storage.md +696 -0
  84. package/resource/knowledge/iot/device/data-model.md +972 -0
  85. package/resource/knowledge/iot/device/demo/android-smart-device.md +97 -0
  86. package/resource/knowledge/iot/device/demo/linux-rtos-device.md +106 -0
  87. package/resource/knowledge/iot/device/device-ota-upgrade.md +237 -0
  88. package/resource/knowledge/iot/device/dynamic-registration/android-smart-device.md +275 -0
  89. package/resource/knowledge/iot/device/dynamic-registration/linux-rtos-device.md +297 -0
  90. package/resource/knowledge/iot/device/login-and-register/android-smart-device.md +131 -0
  91. package/resource/knowledge/iot/device/login-and-register/linux-rtos-device.md +291 -0
  92. package/resource/knowledge/iot/device/monitor-pushing/android-smart-device.md +237 -0
  93. package/resource/knowledge/iot/device/monitor-pushing/linux-rtos-device.md +493 -0
  94. package/resource/knowledge/iot/device/video-call/android-smart-device.md +299 -0
  95. package/resource/knowledge/iot/device/video-call/linux-rtos-device.md +465 -0
  96. package/resource/knowledge/iot/device/wechat-call/android-smart-device.md +534 -0
  97. package/resource/knowledge/iot/device/wechat-call/linux-rtos-device.md +681 -0
  98. package/resource/knowledge/iot/kids-watch/overview.md +28 -0
  99. package/resource/knowledge/iot/security-camera/activate-service.md +74 -0
  100. package/resource/knowledge/iot/security-camera/monitor-playback/app-integration.md +302 -0
  101. package/resource/knowledge/iot/security-camera/monitor-playback/device-integration/android.md +237 -0
  102. package/resource/knowledge/iot/security-camera/monitor-playback/device-integration/linux-rtos.md +493 -0
  103. package/resource/knowledge/iot/security-camera/overview.md +31 -0
  104. package/resource/knowledge/iot/security-camera/ptz-control.md +153 -0
  105. package/resource/knowledge/iot/security-camera/recording-and-snapshot.md +182 -0
  106. package/resource/knowledge/iot/security-camera/voice-intercom.md +174 -0
  107. package/resource/knowledge/iot/smart-vision/serviceoverview.md +114 -0
  108. package/resource/knowledge/iot/voice-recorder/app-demo.md +99 -0
  109. package/resource/knowledge/iot/voice-recorder/device-demo.md +189 -0
  110. package/resource/knowledge/iot/voice-recorder/overview.md +64 -0
  111. package/resource/knowledge/livekit/core-sdk/features/android/base-beauty.md +0 -2
  112. package/resource/knowledge/livekit/core-sdk/features/android/gift.md +0 -4
  113. package/resource/knowledge/livekit/core-sdk/features/android/live-list.md +0 -2
  114. package/resource/knowledge/livekit/core-sdk/features/flutter/audio-effect.md +0 -2
  115. package/resource/knowledge/livekit/core-sdk/features/flutter/base-beauty.md +0 -2
  116. package/resource/knowledge/livekit/core-sdk/features/flutter/gift.md +0 -4
  117. package/resource/knowledge/livekit/core-sdk/features/ios/base-beauty.md +0 -2
  118. package/resource/knowledge/livekit/core-sdk/features/ios/gift.md +0 -4
  119. package/resource/knowledge/livekit/core-sdk/features/ios/live-list.md +0 -2
  120. package/resource/knowledge/livekit/core-sdk/features/uni-app/base-beauty.md +0 -2
  121. package/resource/knowledge/livekit/core-sdk/features/uni-app/gift.md +0 -4
  122. package/resource/knowledge/livekit/core-sdk/live/android/co-guest-interaction.md +0 -4
  123. package/resource/knowledge/livekit/core-sdk/live/android/host-interaction.md +0 -10
  124. package/resource/knowledge/livekit/core-sdk/live/android/integration.md +0 -2
  125. package/resource/knowledge/livekit/core-sdk/live/flutter/co-guest-interaction.md +0 -2
  126. package/resource/knowledge/livekit/core-sdk/live/flutter/host-interaction.md +0 -10
  127. package/resource/knowledge/livekit/core-sdk/live/flutter/integration.md +0 -2
  128. package/resource/knowledge/livekit/core-sdk/live/ios/co-guest-interaction.md +0 -4
  129. package/resource/knowledge/livekit/core-sdk/live/ios/host-interaction.md +0 -10
  130. package/resource/knowledge/livekit/core-sdk/live/ios/integration.md +0 -2
  131. package/resource/knowledge/livekit/core-sdk/live/uni-app/co-guest-interaction.md +0 -4
  132. package/resource/knowledge/livekit/core-sdk/live/uni-app/host-interaction.md +0 -4
  133. package/resource/knowledge/livekit/core-sdk/live/uni-app/integration.md +0 -2
  134. package/resource/knowledge/livekit/core-sdk/voice/android/co-guest-interaction.md +0 -2
  135. package/resource/knowledge/livekit/core-sdk/voice/android/host-interaction.md +0 -4
  136. package/resource/knowledge/livekit/core-sdk/voice/android/integration.md +0 -4
  137. package/resource/knowledge/livekit/core-sdk/voice/flutter/integration.md +0 -2
  138. package/resource/knowledge/livekit/core-sdk/voice/ios/co-guest-interaction.md +0 -2
  139. package/resource/knowledge/livekit/core-sdk/voice/ios/host-interaction.md +0 -4
  140. package/resource/knowledge/livekit/core-sdk/voice/ios/integration.md +0 -4
  141. package/resource/knowledge/livekit/core-sdk/voice/uni-app/co-guest-interaction.md +0 -2
  142. package/resource/knowledge/livekit/core-sdk/voice/uni-app/integration.md +0 -2
  143. package/resource/knowledge/livekit/faq/error-code.md +0 -2
  144. package/resource/knowledge/push/config/donut.md +0 -2
  145. package/resource/knowledge/push/integration/android.md +0 -5
  146. package/resource/knowledge/push/integration/donut.md +0 -3
  147. package/resource/knowledge/push/integration/flutter.md +0 -3
  148. package/resource/knowledge/push/integration/harmonyos.md +0 -3
  149. package/resource/knowledge/push/integration/ios.md +0 -3
  150. package/resource/knowledge/push/integration/react-native.md +0 -5
  151. package/resource/knowledge/push/integration/uni-app.md +0 -3
  152. package/resource/knowledge/push/integration/unity.md +0 -3
  153. package/resource/knowledge/push/integration/unreal-engine.md +0 -3
  154. package/resource/knowledge/roomkit/core-sdk/android/call-user-to-room.md +0 -2
  155. package/resource/knowledge/roomkit/core-sdk/android/room-participant-state.md +0 -2
  156. package/resource/knowledge/roomkit/core-sdk/android/room-state.md +0 -2
  157. package/resource/knowledge/roomkit/core-sdk/android/schedule-room.md +0 -2
  158. package/resource/knowledge/roomkit/core-sdk/flutter/call-user-to-room.md +0 -2
  159. package/resource/knowledge/roomkit/core-sdk/flutter/room-participant-state.md +0 -2
  160. package/resource/knowledge/roomkit/core-sdk/flutter/room-state.md +0 -2
  161. package/resource/knowledge/roomkit/core-sdk/flutter/schedule-room.md +0 -2
  162. package/resource/knowledge/roomkit/core-sdk/ios/call-user-to-room.md +0 -2
  163. package/resource/knowledge/roomkit/core-sdk/ios/room-participant-state.md +0 -2
  164. package/resource/knowledge/roomkit/core-sdk/ios/room-state.md +0 -2
  165. package/resource/knowledge/roomkit/core-sdk/ios/schedule-room.md +0 -2
  166. package/resource/knowledge/roomkit/core-sdk/vue/room-view.md +0 -3
  167. package/resource/knowledge/rtcengine/web/how_to_run_web_sdk_demo.md +0 -2
  168. package/resource/knowledge/rtcengine/web/plugin/24-advanced-network-quality.md +0 -2
  169. package/tools/common/finalize_answer.js +1 -1
  170. package/tools/common/search_trtc_knowledge.js +2 -2
  171. package/tools/definitions/search_trtc_knowledge_def.js +3 -2
  172. package/utils/preflight-route.js +11 -2
@@ -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,
@@ -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,
@@ -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
  };
@@ -6,7 +6,12 @@ import { hasExplicitFrameworkMatch, hasFrameworkFallbackMatch } from './framewor
6
6
  * path-meta.ts 已通过 normalizeFrameworkDir 把目录名归一到标准 framework,
7
7
  * 所有产品的 framework 均已对齐枚举值,无需额外别名扩展。
8
8
  */
9
- const FRAMEWORK_SEARCH_ALIASES = {};
9
+ const FRAMEWORK_SEARCH_ALIASES = {
10
+ miniprogram: ['miniprogram', 'minprogram'],
11
+ minprogram: ['minprogram', 'miniprogram'],
12
+ 'react-native': ['react-native', 'rn'],
13
+ rn: ['rn', 'react-native'],
14
+ };
10
15
  function expandFrameworks(frameworks, _products) {
11
16
  if (!frameworks?.length)
12
17
  return undefined;
@@ -166,6 +171,9 @@ export function isSoftAllowed(chunk, norm) {
166
171
  if (norm.scenario && chunk.scenario && chunk.scenario !== norm.scenario) {
167
172
  return false;
168
173
  }
174
+ // IoT 端别过滤改为“召回保留 + 排序分层”:
175
+ // Phase2 中 exact/common/conflict 的优先级由 applyRankMultipliers 处理,
176
+ // 这里不做硬过滤,避免因标注偏差误杀有效候选。
169
177
  // best-practice 文档对 types 过滤免疫:内容不依赖 types 分类,应始终进 pool 靠 BM25 自然排名。
170
178
  // 典型场景:Agent 传 types=['api'] 时,types=['faq'] 的 best-practice 不应被过滤掉。
171
179
  if (norm.types?.length && !norm.types.some((t) => chunk.types.includes(t))) {
@@ -0,0 +1,25 @@
1
+ const DEFAULT_PROFILE = {
2
+ name: 'default',
3
+ maxPerSource: 3,
4
+ rerankTopK: 20,
5
+ lowConfidenceRerankTopK: 0,
6
+ lowConfidenceTopScoreThreshold: 0,
7
+ lowConfidenceGapRatioThreshold: 0,
8
+ titleExactMultiplier: 1.03,
9
+ titleTokenBoost: 0.05,
10
+ };
11
+ const IOT_PROFILE = {
12
+ name: 'iot',
13
+ maxPerSource: 2,
14
+ rerankTopK: 24,
15
+ lowConfidenceRerankTopK: 10,
16
+ lowConfidenceTopScoreThreshold: 28,
17
+ lowConfidenceGapRatioThreshold: 1.08,
18
+ titleExactMultiplier: 1.15,
19
+ titleTokenBoost: 0.15,
20
+ };
21
+ export function resolveRankProfile(params) {
22
+ if (params.product?.includes('iot'))
23
+ return IOT_PROFILE;
24
+ return DEFAULT_PROFILE;
25
+ }
@@ -8,7 +8,7 @@ const PLATFORM_TYPES = new Set([
8
8
  'integration',
9
9
  ]);
10
10
  const CROSS_PLATFORM_TYPES = new Set(['server_api', 'webhook', 'product']);
11
- const CROSS_PLATFORM_PROMPT = /计费|套餐|价格|UserSig\s*原理|签发流程|控制台|服务端\s*REST|云\s*API|Webhook|回调|callback|server\s*callback/i;
11
+ const CROSS_PLATFORM_PROMPT = /计费|套餐|价格|UserSig\s*原理|签发流程|控制台|服务端\s*REST|云\s*API|Webhook|回调|callback|server\s*callback|物联网开发平台|安防摄像头|智能视觉服务|儿童手表|录音卡/i;
12
12
  const PLATFORM_PROMPT = /API|组件|怎么用|怎么开|怎么设置|怎么配置|虚拟背景|屏幕分享|enterRoom|SDK|插件|集成步骤|客户端|Feature|主题定制|连麦|上麦/i;
13
13
  /**
14
14
  * push 必须问平台的强信号:仅当 prompt 明确提到具体「平台」(android/ios/flutter/...)时才拦询问。
@@ -60,6 +60,9 @@ export function requiresFramework(norm) {
60
60
  return false;
61
61
  return false;
62
62
  }
63
+ // iot:产品级/能力线/API 问法第一阶段允许跨平台检索,不做 framework 前置拦截
64
+ if (hasProduct(norm, 'iot'))
65
+ return false;
63
66
  // native_trtc_sdk:所有平台相关咨询都依赖具体客户端(android/ios/c++),必须先问
64
67
  if (hasProduct(norm, 'native_trtc_sdk'))
65
68
  return true;
@@ -51,6 +51,7 @@ export function postProcess(ranked, chunksById, limit, opts) {
51
51
  }
52
52
  const sourceCount = {};
53
53
  const urlMappingSourceCount = {};
54
+ const maxPerSource = Math.max(1, opts?.maxPerSource ?? 3);
54
55
  /** 已入选的 push config/android/{vendor} 文件,用于抑制其他厂商噪声 */
55
56
  let selectedVendorFile = null;
56
57
  const fragments = [];
@@ -70,7 +71,7 @@ export function postProcess(ranked, chunksById, limit, opts) {
70
71
  }
71
72
  else {
72
73
  const count = sourceCount[chunk.source] ?? 0;
73
- if (count >= 3)
74
+ if (count >= maxPerSource)
74
75
  continue;
75
76
  sourceCount[chunk.source] = count + 1;
76
77
  }
@@ -384,7 +385,29 @@ export function hasNativeClientFramework(frameworks) {
384
385
  return false;
385
386
  return frameworks.some((fw) => ['android', 'ios', 'flutter', 'c++'].includes(fw));
386
387
  }
388
+ function normalizeCompactText(input) {
389
+ return input.toLowerCase().replace(/[\s\-_]+/g, '');
390
+ }
391
+ function hasExactTitleMatch(prompt, title) {
392
+ if (!prompt || !title)
393
+ return false;
394
+ const p = normalizeCompactText(prompt);
395
+ const t = normalizeCompactText(title);
396
+ if (!p || !t)
397
+ return false;
398
+ return (t.length >= 3 && p.includes(t)) || (p.length >= 3 && t.includes(p));
399
+ }
400
+ function tokenizePromptForTitle(prompt) {
401
+ return [...new Set(prompt
402
+ .toLowerCase()
403
+ .split(/[^a-z0-9\u4e00-\u9fff]+/)
404
+ .map((s) => s.trim())
405
+ .filter((s) => s.length >= 2))];
406
+ }
387
407
  export function applyRankMultipliers(ranked, chunksById, opts) {
408
+ const prompt = opts.prompt ?? '';
409
+ const promptTokens = tokenizePromptForTitle(prompt);
410
+ const isIotProfile = opts.rank_profile === 'iot';
388
411
  const adjusted = ranked.map((item) => {
389
412
  const chunk = chunksById.get(item.id);
390
413
  if (!chunk)
@@ -402,6 +425,38 @@ export function applyRankMultipliers(ranked, chunksById, opts) {
402
425
  multiplier *= 0.2;
403
426
  }
404
427
  }
428
+ // Phase1: title/h2 与 query 精确短语命中增强
429
+ if (prompt && hasExactTitleMatch(prompt, chunk.title)) {
430
+ multiplier *= isIotProfile ? 1.15 : 1.03;
431
+ }
432
+ if (promptTokens.length > 0) {
433
+ const titleText = `${chunk.title} ${(chunk.key_phrases ?? []).join(' ')}`.toLowerCase();
434
+ const overlap = promptTokens.reduce((acc, token) => (titleText.includes(token) ? acc + 1 : acc), 0);
435
+ if (overlap > 0) {
436
+ multiplier *= 1 + Math.min(0.06, overlap * (isIotProfile ? 0.015 : 0.008));
437
+ }
438
+ }
439
+ // Phase2: IoT feature_domain 软回退(IoT 唯一排序信号)
440
+ if (chunk.product === 'iot') {
441
+ if (chunk.feature_domain && opts.preferred_iot_feature_domain) {
442
+ if (chunk.feature_domain === opts.preferred_iot_feature_domain)
443
+ multiplier *= 1.15;
444
+ else
445
+ multiplier *= 0.9;
446
+ }
447
+ // Phase2b: 强 feature 意图 → source 精准置顶
448
+ // 中文意图词(云台/语音对讲)与英文文件名(ptz-control/voice-intercom)无词面关系,
449
+ // 用 source 路径关键词精准命中 feature 文档,越过短文本泛文档的 BM25 优势。
450
+ if (opts.preferred_iot_feature_source && chunk.source.includes(opts.preferred_iot_feature_source)) {
451
+ multiplier *= 1.7;
452
+ }
453
+ // 非 demo 意图下,弱化 app_demo/device_demo 对功能问法的抢位
454
+ if (chunk.feature_domain && /demo$/i.test(chunk.feature_domain)) {
455
+ if (!/\bdemo\b|示例|sample|example|演示/i.test(prompt)) {
456
+ multiplier *= 0.82;
457
+ }
458
+ }
459
+ }
405
460
  return { ...item, score: item.score * multiplier };
406
461
  });
407
462
  return adjusted.sort((a, b) => b.score - a.score);
@@ -8,6 +8,7 @@ import { computeGenericAnchorBoosts } from './generic-rerank.js';
8
8
  import { embedQuery, withQueryInstruction } from '../embedding/embed-model.js';
9
9
  import { queryVectors } from '../embedding/vector-index.js';
10
10
  import { applyRankMultipliers, computeApiNameCoverage, computeMissingApiTokens, isUrlMappingSource, postProcess, } from './search-core.js';
11
+ import { resolveRankProfile } from './rank-profile.js';
11
12
  function isBestPracticeSource(source) {
12
13
  return /\/best-practice\//i.test(source);
13
14
  }
@@ -311,6 +312,74 @@ function stableSortRanked(items) {
311
312
  return a.id.localeCompare(b.id);
312
313
  });
313
314
  }
315
+ function inferIotFeatureDomainFromPrompt(prompt) {
316
+ if (!prompt)
317
+ return undefined;
318
+ if (/安防摄像头|云台|\bPTZ\b|语音对讲|监控|回放|录像|录制|截图/i.test(prompt)) {
319
+ return 'security-camera';
320
+ }
321
+ if (/儿童手表/i.test(prompt))
322
+ return 'kids-watch';
323
+ if (/录音卡|语音卡/i.test(prompt))
324
+ return 'voice-recorder';
325
+ if (/智能视觉|视觉服务/i.test(prompt))
326
+ return 'smart-vision';
327
+ if (/设备端|linux|rtos|模组|芯片/i.test(prompt))
328
+ return 'device';
329
+ if (/客户端\s*api|client\s*api|\bTXIoT[A-Za-z0-9_]+\b/i.test(prompt))
330
+ return 'client_api';
331
+ if (/app\s*demo|示例工程|demo/i.test(prompt))
332
+ return 'app_demo';
333
+ if (/应用端接入|app接入|app\s*集成/i.test(prompt))
334
+ return 'app_integration';
335
+ return undefined;
336
+ }
337
+ /**
338
+ * IoT 强 feature 意图 → source 路径关键词映射。
339
+ * 中文意图词(云台/语音对讲)与英文文件名(ptz-control/voice-intercom)无直接词面关系,
340
+ * 需显式映射才能在排序层精准置顶对应 feature 文档,避免被短文本泛文档的 BM25 优势压制。
341
+ *
342
+ * 注意:仅收录「一 feature 一精准文件」的映射。像 monitor-playback 这类分布在
343
+ * 多端侧(app/device/security-camera)的 feature 不在此列,避免无差别 source boost
344
+ * 把 device 侧文档抬高、压制 app 侧(见 regression case 22)。
345
+ */
346
+ const IOT_FEATURE_INTENT_MAP = [
347
+ { re: /云台|\bPTZ\b/i, keyword: 'ptz-control' },
348
+ { re: /语音对讲|对讲|intercom/i, keyword: 'voice-intercom' },
349
+ ];
350
+ function inferIotFeatureSourceKeyword(prompt) {
351
+ if (!prompt)
352
+ return undefined;
353
+ for (const { re, keyword } of IOT_FEATURE_INTENT_MAP) {
354
+ if (re.test(prompt))
355
+ return keyword;
356
+ }
357
+ return undefined;
358
+ }
359
+ function inferPreferredIotFeatureDomain(ranked, chunksById, prompt) {
360
+ const byPrompt = inferIotFeatureDomainFromPrompt(prompt ?? '');
361
+ if (byPrompt)
362
+ return byPrompt;
363
+ const scoreByDomain = new Map();
364
+ const sampleWindow = Math.min(ranked.length, 80);
365
+ for (let i = 0; i < sampleWindow; i += 1) {
366
+ const item = ranked[i];
367
+ const chunk = chunksById.get(item.id);
368
+ if (!chunk || chunk.product !== 'iot' || !chunk.feature_domain)
369
+ continue;
370
+ const domain = chunk.feature_domain;
371
+ scoreByDomain.set(domain, (scoreByDomain.get(domain) ?? 0) + Math.max(item.score, 0));
372
+ }
373
+ let preferred;
374
+ let best = -1;
375
+ for (const [domain, score] of scoreByDomain.entries()) {
376
+ if (score > best) {
377
+ best = score;
378
+ preferred = domain;
379
+ }
380
+ }
381
+ return preferred;
382
+ }
314
383
  function fuseByRRF(params) {
315
384
  const { bm25Ranked, vectorHits, rrfK, baseBeta, alpha, profile, } = params;
316
385
  const bmWeight = profile?.bmWeight ?? 1;
@@ -392,8 +461,35 @@ function resolveFusionProfile(prompt, queryApiTokens) {
392
461
  }
393
462
  return { name: 'default', bmWeight: 1.0, vecWeight: 1.0, alphaScale: 1.0 };
394
463
  }
464
+ function normalizeCompactText(input) {
465
+ return input.toLowerCase().replace(/[\s\-_]+/g, '');
466
+ }
467
+ function hasTitleExactMatch(prompt, title) {
468
+ if (!prompt || !title)
469
+ return false;
470
+ const p = normalizeCompactText(prompt);
471
+ const t = normalizeCompactText(title);
472
+ if (!p || !t)
473
+ return false;
474
+ if (t.length >= 3 && p.includes(t))
475
+ return true;
476
+ if (p.length >= 3 && t.includes(p))
477
+ return true;
478
+ return false;
479
+ }
480
+ function shouldTriggerLowConfidenceRerank(params) {
481
+ const { ranked, rankProfile } = params;
482
+ if (rankProfile.lowConfidenceRerankTopK <= 0 || ranked.length < 2)
483
+ return false;
484
+ const top = ranked[0]?.score ?? 0;
485
+ const second = ranked[1]?.score ?? 0;
486
+ if (top <= 0)
487
+ return false;
488
+ const ratio = second > 0 ? top / second : Number.POSITIVE_INFINITY;
489
+ return top <= rankProfile.lowConfidenceTopScoreThreshold || ratio <= rankProfile.lowConfidenceGapRatioThreshold;
490
+ }
395
491
  function rerankTopK(params) {
396
- const { prompt, ranked, chunksById, topK } = params;
492
+ const { prompt, ranked, chunksById, topK, rankProfile, } = params;
397
493
  if (ranked.length < 2 || topK <= 0)
398
494
  return { ranked, applied: false };
399
495
  const head = ranked.slice(0, topK);
@@ -408,9 +504,13 @@ function rerankTopK(params) {
408
504
  return item;
409
505
  const text = [chunk.title, ...(chunk.key_phrases ?? [])].join(' ').toLowerCase();
410
506
  const overlap = promptTokens.reduce((acc, token) => (text.includes(token.toLowerCase()) ? acc + 1 : acc), 0);
411
- if (overlap <= 0)
507
+ if (overlap <= 0 && !hasTitleExactMatch(prompt, chunk.title))
412
508
  return item;
413
- const boost = topScore * 0.002 * overlap;
509
+ let boost = topScore * 0.002 * overlap;
510
+ if (hasTitleExactMatch(prompt, chunk.title)) {
511
+ boost += Math.max(topScore * 0.015, 1.5) * (rankProfile.titleExactMultiplier - 1);
512
+ }
513
+ boost += rankProfile.titleTokenBoost * overlap;
414
514
  return { ...item, score: item.score + boost };
415
515
  });
416
516
  const rerankedHead = stableSortRanked(bumped);
@@ -422,6 +522,7 @@ export async function runSearchPass(params) {
422
522
  const effectivePassNorm = retrievalMode === 'discovery'
423
523
  ? { ...passNorm, types: undefined }
424
524
  : passNorm;
525
+ const rankProfile = resolveRankProfile({ product: effectivePassNorm.product });
425
526
  let pool = filterPool(allChunks, effectivePassNorm);
426
527
  // types 误传自愈:首轮保留,二次扩检也同样保留该能力
427
528
  if (pool.length === 0 && effectivePassNorm.types?.length) {
@@ -468,7 +569,7 @@ export async function runSearchPass(params) {
468
569
  const baseBeta = readEnvNumber('VECTOR_AUGMENT_BASE_BETA', 0.3);
469
570
  const topK = Math.max(1, Math.floor(readEnvNumber('VECTOR_TOP_K', DEFAULT_VECTOR_TOP_K)));
470
571
  const rrfK = Math.max(1, Math.floor(readEnvNumber('VECTOR_RRF_K', DEFAULT_RRF_K)));
471
- const rerankTopKLimit = Math.max(0, Math.floor(readEnvNumber('VECTOR_RERANK_TOP_K', DEFAULT_RERANK_TOP_K)));
572
+ const rerankTopKLimit = Math.max(0, Math.floor(readEnvNumber('VECTOR_RERANK_TOP_K', rankProfile.rerankTopK || DEFAULT_RERANK_TOP_K)));
472
573
  const fullDocumentMaxChars = Math.max(1000, Math.floor(readEnvNumber('SEARCH_FULL_DOCUMENT_MAX_CHARS', 7000)));
473
574
  let rankedFused = ranked;
474
575
  // 向量归因埋点采集(终态归因在函数末尾统一构造):
@@ -536,6 +637,7 @@ export async function runSearchPass(params) {
536
637
  ranked: fused.ranked,
537
638
  chunksById,
538
639
  topK: rerankTopKLimit,
640
+ rankProfile,
539
641
  });
540
642
  rankedFused = reranked.ranked;
541
643
  }
@@ -545,12 +647,30 @@ export async function runSearchPass(params) {
545
647
  // 快照变量维持初始值即可。归因阶段 vector_top1_id 为 null 天然反映降级。
546
648
  }
547
649
  }
650
+ const preferredIotFeatureDomain = inferPreferredIotFeatureDomain(rankedFused, chunksById, passNorm.prompt);
651
+ const preferredIotFeatureSource = inferIotFeatureSourceKeyword(passNorm.prompt);
548
652
  const rankedWithMultipliers = applyRankMultipliers(rankedFused, chunksById, {
549
653
  product: passNorm.product,
550
654
  framework: passNorm.framework,
551
655
  frameworks: passNorm.frameworks,
656
+ preferred_iot_feature_domain: preferredIotFeatureDomain,
657
+ preferred_iot_feature_source: preferredIotFeatureSource,
658
+ prompt: passNorm.prompt,
659
+ rank_profile: rankProfile.name,
552
660
  });
553
- const rankedAfterIntentGuard = applyDiagnosisIntentGuard(rankedWithMultipliers, chunksById, passNorm.prompt);
661
+ let rankedAfterIntentGuard = applyDiagnosisIntentGuard(rankedWithMultipliers, chunksById, passNorm.prompt);
662
+ if (shouldTriggerLowConfidenceRerank({ ranked: rankedAfterIntentGuard, rankProfile })) {
663
+ const secondPass = rerankTopK({
664
+ prompt: passNorm.prompt,
665
+ ranked: rankedAfterIntentGuard,
666
+ chunksById,
667
+ topK: rankProfile.lowConfidenceRerankTopK,
668
+ rankProfile,
669
+ });
670
+ if (secondPass.applied) {
671
+ rankedAfterIntentGuard = secondPass.ranked;
672
+ }
673
+ }
554
674
  const promptLockPolicy = decidePromptLockPolicy(passNorm.prompt);
555
675
  const allowContentPriority = promptLockPolicy.enableContentPriority;
556
676
  const allowFullDocumentLock = promptLockPolicy.enableFullDocumentLock;
@@ -593,6 +713,7 @@ export async function runSearchPass(params) {
593
713
  preferredSource: dominantBestPractice.preferredSource,
594
714
  fullDocumentMode: dominantBestPractice.fullDocumentMode,
595
715
  fullDocumentMaxChars,
716
+ maxPerSource: rankProfile.maxPerSource,
596
717
  });
597
718
  const urlMappingSources = [...new Set(fragments.map((f) => f.source).filter((source) => isUrlMappingSource(source)))];
598
719
  const references = buildReferences(fragments);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tencentcloud/sdk-mcp",
3
- "version": "1.7.8",
3
+ "version": "1.7.9-beta.1",
4
4
  "description": "Tencent Cloud MCP Server for SDK",
5
5
  "type": "module",
6
6
  "main": "index.js",