@tencentcloud/sdk-mcp 1.6.0-beta.3 → 1.6.0
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 +1 -1
- package/cursor-setting.png +0 -0
- package/knowledge/build-index.js +4 -0
- package/knowledge/content-loader.js +3 -0
- package/knowledge/doc-url-resolver.js +118 -0
- package/knowledge/path-meta.js +4 -2
- package/knowledge/search/exact-lookup.js +1 -1
- package/knowledge/search/filter-pool.js +5 -5
- package/knowledge/search/requires-framework.js +6 -6
- package/knowledge/search/search-knowledge.js +12 -5
- package/mcp-server-prompt.png +0 -0
- package/package.json +1 -1
- package/resource/index/doc-source-urls.json +1 -0
- package/resource/knowledge/push/config/donut.md +445 -0
- package/resource/knowledge/push/config/harmonyos.md +87 -0
- package/resource/knowledge/push/config/ios.md +341 -0
- package/resource/knowledge/push/config/react-native.md +205 -0
- package/resource/knowledge/push/config/unity.md +199 -0
- package/resource/knowledge/push/config/unreal-engine.md +199 -0
- package/server.js +14 -1
- package/tools/common/record_answer.js +1 -1
- package/tools/common/record_feedback.js +1 -1
- package/tools/common/search_trtc_knowledge.js +1 -1
- package/tools/definitions/record_answer_def.js +6 -5
- package/tools/definitions/record_feedback_def.js +2 -2
- package/tools/definitions/search_trtc_knowledge_def.js +20 -55
- package/utils/report-cls-client.js +4 -4
- package/utils/report-es-client.js +1 -1
- package/utils/report-runtime-error-cls-client.js +0 -90
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ npx -y @tencentcloud/sdk-mcp
|
|
|
41
41
|
"mcpServers": {
|
|
42
42
|
"tencentcloud-sdk-mcp": {
|
|
43
43
|
"command": "npx",
|
|
44
|
-
"args": ["-y", "@tencentcloud/sdk-mcp"],
|
|
44
|
+
"args": ["-y", "@tencentcloud/sdk-mcp@latest"],
|
|
45
45
|
"env": {
|
|
46
46
|
"SDKAPPID": "YOUR_SDKAPPID",
|
|
47
47
|
"SECRETKEY": "YOUR_SECRET_KEY"
|
package/cursor-setting.png
CHANGED
|
Binary file
|
package/knowledge/build-index.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { chunkMarkdown, toManifestChunk } from './chunker.js';
|
|
5
|
+
import { buildDocSourceUrlMap } from './doc-url-resolver.js';
|
|
5
6
|
import { inferPathMeta } from './path-meta.js';
|
|
6
7
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
8
|
const RESOURCE_ROOT = path.resolve(__dirname, '../resource');
|
|
@@ -57,6 +58,9 @@ export function buildKnowledgeIndex() {
|
|
|
57
58
|
const componentSlugs = collectComponentSlugs(chunks);
|
|
58
59
|
fs.writeFileSync(path.join(indexDir, 'manifest.json'), JSON.stringify(manifest));
|
|
59
60
|
fs.writeFileSync(path.join(indexDir, 'component-slugs.json'), JSON.stringify(componentSlugs));
|
|
61
|
+
const syncManifestPath = path.resolve(__dirname, '../../sync/sync-manifest.json');
|
|
62
|
+
const docSourceUrls = buildDocSourceUrlMap(syncManifestPath);
|
|
63
|
+
fs.writeFileSync(path.join(indexDir, 'doc-source-urls.json'), JSON.stringify(docSourceUrls));
|
|
60
64
|
const staleSearchIndex = path.join(indexDir, 'search-index.json');
|
|
61
65
|
if (fs.existsSync(staleSearchIndex)) {
|
|
62
66
|
fs.unlinkSync(staleSearchIndex);
|
|
@@ -16,6 +16,9 @@ function readSourceFile(source) {
|
|
|
16
16
|
fileCache.set(source, text);
|
|
17
17
|
return text;
|
|
18
18
|
}
|
|
19
|
+
export function getSourceFileContent(source) {
|
|
20
|
+
return readSourceFile(source);
|
|
21
|
+
}
|
|
19
22
|
/** 按 build 时写入的 char 偏移从 md 源文件切片(方案 B:manifest 不存 content) */
|
|
20
23
|
export function getChunkContent(chunk) {
|
|
21
24
|
const full = readSourceFile(chunk.source);
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
/** 控制台/购买页等不适合作为「参考文档」的链接 */
|
|
6
|
+
const NOISE_URL_RE = /console\.cloud\.tencent|buy\.cloud\.tencent|account\.renewal|edu\/learning/i;
|
|
7
|
+
const PREFERRED_DOC_HOST_RE = /cloud\.tencent\.com|web\.sdk\.qcloud\.com|github\.com\/Tencent(?:Cloud|RTC)/i;
|
|
8
|
+
let cachedSourceUrlMap = null;
|
|
9
|
+
function loadSourceUrlMap() {
|
|
10
|
+
if (cachedSourceUrlMap)
|
|
11
|
+
return cachedSourceUrlMap;
|
|
12
|
+
const mapPath = path.resolve(__dirname, '../resource/index/doc-source-urls.json');
|
|
13
|
+
if (!fs.existsSync(mapPath)) {
|
|
14
|
+
cachedSourceUrlMap = {};
|
|
15
|
+
return cachedSourceUrlMap;
|
|
16
|
+
}
|
|
17
|
+
cachedSourceUrlMap = JSON.parse(fs.readFileSync(mapPath, 'utf-8'));
|
|
18
|
+
return cachedSourceUrlMap;
|
|
19
|
+
}
|
|
20
|
+
function normalizeUrl(raw) {
|
|
21
|
+
return raw.replace(/[.,;)\]]+$/, '').trim();
|
|
22
|
+
}
|
|
23
|
+
function isUsefulDocUrl(url) {
|
|
24
|
+
if (!/^https?:\/\//i.test(url))
|
|
25
|
+
return false;
|
|
26
|
+
if (NOISE_URL_RE.test(url))
|
|
27
|
+
return false;
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
/** 从 markdown 正文中提取可参考的官网链接(优先 cloud.tencent / web.sdk.qcloud) */
|
|
31
|
+
export function extractUrlsFromContent(content, max = 3) {
|
|
32
|
+
const found = [];
|
|
33
|
+
const preferred = [];
|
|
34
|
+
for (const match of content.matchAll(/https?:\/\/[^\s`|)\]"'>]+/g)) {
|
|
35
|
+
const url = normalizeUrl(match[0]);
|
|
36
|
+
if (!isUsefulDocUrl(url))
|
|
37
|
+
continue;
|
|
38
|
+
if (PREFERRED_DOC_HOST_RE.test(url)) {
|
|
39
|
+
if (!preferred.includes(url))
|
|
40
|
+
preferred.push(url);
|
|
41
|
+
}
|
|
42
|
+
else if (!found.includes(url)) {
|
|
43
|
+
found.push(url);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const merged = [...preferred, ...found];
|
|
47
|
+
return merged.slice(0, max);
|
|
48
|
+
}
|
|
49
|
+
/** L1 URL 映射 chunk:表格行中的目标文档 URL */
|
|
50
|
+
export function extractUrlMappingPrimaryUrl(content) {
|
|
51
|
+
const row = content.match(/\|\s*[^|]+\s*\|\s*`?(https?:\/\/[^|`\s]+)`?\s*\|/);
|
|
52
|
+
return row?.[1] ? normalizeUrl(row[1]) : null;
|
|
53
|
+
}
|
|
54
|
+
function isUrlMappingSource(source) {
|
|
55
|
+
return /knowledge\/chat\/urls\/.+\.md$/i.test(source.replace(/\\/g, '/'));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 为检索 fragment 解析可参考的官网 URL(去重、按优先级)。
|
|
59
|
+
* 1. sync-manifest 落盘的 localPath → 官网 URL(rtcengine 等)
|
|
60
|
+
* 2. L1 URL 映射行内目标 URL
|
|
61
|
+
* 3. 本地文档正文中的官网链接
|
|
62
|
+
*/
|
|
63
|
+
export function resolveFragmentDocUrls(source, content, fullSourceContent) {
|
|
64
|
+
const normalized = source.replace(/\\/g, '/');
|
|
65
|
+
const urls = [];
|
|
66
|
+
const canonical = loadSourceUrlMap()[normalized];
|
|
67
|
+
if (canonical)
|
|
68
|
+
urls.push(canonical);
|
|
69
|
+
if (isUrlMappingSource(normalized)) {
|
|
70
|
+
const primary = extractUrlMappingPrimaryUrl(content);
|
|
71
|
+
if (primary && !urls.includes(primary))
|
|
72
|
+
urls.push(primary);
|
|
73
|
+
return urls;
|
|
74
|
+
}
|
|
75
|
+
const collect = (text) => {
|
|
76
|
+
for (const url of extractUrlsFromContent(text)) {
|
|
77
|
+
if (!urls.includes(url))
|
|
78
|
+
urls.push(url);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
collect(content);
|
|
82
|
+
if (urls.length === 0 && fullSourceContent && fullSourceContent !== content) {
|
|
83
|
+
collect(fullSourceContent);
|
|
84
|
+
}
|
|
85
|
+
return urls.slice(0, 3);
|
|
86
|
+
}
|
|
87
|
+
/** 从 fragments 汇总去重后的参考链接列表(按 fragment 相关度排序) */
|
|
88
|
+
export function buildReferences(fragments) {
|
|
89
|
+
const refs = [];
|
|
90
|
+
const seen = new Set();
|
|
91
|
+
for (const fragment of fragments) {
|
|
92
|
+
for (const url of fragment.doc_urls) {
|
|
93
|
+
if (seen.has(url))
|
|
94
|
+
continue;
|
|
95
|
+
seen.add(url);
|
|
96
|
+
refs.push({
|
|
97
|
+
rank: refs.length + 1,
|
|
98
|
+
title: fragment.title,
|
|
99
|
+
url,
|
|
100
|
+
source: fragment.source,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return refs;
|
|
105
|
+
}
|
|
106
|
+
/** build 时从 sync-manifest 生成 localPath → 官网 URL 映射 */
|
|
107
|
+
export function buildDocSourceUrlMap(syncManifestPath) {
|
|
108
|
+
if (!fs.existsSync(syncManifestPath))
|
|
109
|
+
return {};
|
|
110
|
+
const manifest = JSON.parse(fs.readFileSync(syncManifestPath, 'utf-8'));
|
|
111
|
+
const map = {};
|
|
112
|
+
for (const entry of Object.values(manifest.entries ?? {})) {
|
|
113
|
+
if (entry.nodeId?.startsWith('http') && entry.localPath) {
|
|
114
|
+
map[entry.localPath.replace(/\\/g, '/')] = entry.nodeId;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return map;
|
|
118
|
+
}
|
package/knowledge/path-meta.js
CHANGED
|
@@ -148,8 +148,10 @@ export function inferPathMeta(relativeSource) {
|
|
|
148
148
|
types.push('integration');
|
|
149
149
|
}
|
|
150
150
|
else if (category === 'config' && parts[3]) {
|
|
151
|
-
// config/{platform}/{vendor}.md
|
|
152
|
-
framework
|
|
151
|
+
// config/{platform}/{vendor}.md(Android 厂商)或 config/{platform}.md(非 Android 平台配置)
|
|
152
|
+
// platform 决定 framework,vendor 靠 BM25 命中
|
|
153
|
+
const platform = parts[3].replace(/\.mdx?$/, '');
|
|
154
|
+
framework = [platform];
|
|
153
155
|
types.push('integration');
|
|
154
156
|
}
|
|
155
157
|
else {
|
|
@@ -248,7 +248,7 @@ export function runExactLookup(prompt, pool, norm) {
|
|
|
248
248
|
}
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
|
-
const errorCodeIntent = /错误码/i.test(prompt) || errorMatches.length > 0 || norm.
|
|
251
|
+
const errorCodeIntent = /错误码/i.test(prompt) || errorMatches.length > 0 || norm.searchIntent?.includes('error_code');
|
|
252
252
|
if (errorCodeIntent) {
|
|
253
253
|
for (const chunk of pool) {
|
|
254
254
|
if (chunk.source.includes('knowledge/chat/urls/error-code.md')) {
|
|
@@ -12,15 +12,14 @@ function expandFrameworks(framework) {
|
|
|
12
12
|
return ['react', 'vue', 'web'];
|
|
13
13
|
return FRAMEWORK_SEARCH_ALIASES[framework] ?? [framework];
|
|
14
14
|
}
|
|
15
|
-
/** Chat 无独立 faq.md;types 含 faq
|
|
15
|
+
/** Chat 无独立 faq.md;types 含 faq 时退化到全语料检索(feature/component/urls 等均命中) */
|
|
16
16
|
function resolveTypes(products, types) {
|
|
17
17
|
if (!types?.length)
|
|
18
18
|
return undefined;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
resolved = resolved.filter((t) => t !== 'faq');
|
|
19
|
+
if (products?.includes('chat') && types.includes('faq')) {
|
|
20
|
+
return undefined;
|
|
22
21
|
}
|
|
23
|
-
return
|
|
22
|
+
return types;
|
|
24
23
|
}
|
|
25
24
|
/** 单平台产品的隐式 framework 推断:避免对显然唯一的平台再发起询问 */
|
|
26
25
|
function inferImplicitFramework(products, framework) {
|
|
@@ -40,6 +39,7 @@ export function normalizeParams(params) {
|
|
|
40
39
|
product: params.product,
|
|
41
40
|
framework: effectiveFramework,
|
|
42
41
|
frameworks,
|
|
42
|
+
searchIntent: params.types,
|
|
43
43
|
types,
|
|
44
44
|
scenario: params.scenario,
|
|
45
45
|
limit,
|
|
@@ -31,7 +31,7 @@ function isChatErrorCodeAmbiguous(norm) {
|
|
|
31
31
|
return false;
|
|
32
32
|
if (norm.framework)
|
|
33
33
|
return false;
|
|
34
|
-
if (norm.
|
|
34
|
+
if (norm.searchIntent?.includes('error_code'))
|
|
35
35
|
return true;
|
|
36
36
|
return /错误码|error\s*code|ERR_/i.test(norm.prompt);
|
|
37
37
|
}
|
|
@@ -44,7 +44,7 @@ export function requiresFramework(norm) {
|
|
|
44
44
|
return true;
|
|
45
45
|
// push:判定逻辑(自上而下)
|
|
46
46
|
if (hasProduct(norm, 'push')) {
|
|
47
|
-
if (norm.
|
|
47
|
+
if (norm.searchIntent?.length && norm.searchIntent.every((t) => CROSS_PLATFORM_TYPES.has(t))) {
|
|
48
48
|
return false;
|
|
49
49
|
}
|
|
50
50
|
if (PUSH_PLATFORM_SPECIFIC_PROMPT.test(norm.prompt))
|
|
@@ -58,13 +58,13 @@ export function requiresFramework(norm) {
|
|
|
58
58
|
return true;
|
|
59
59
|
if (CROSS_PLATFORM_PROMPT.test(norm.prompt))
|
|
60
60
|
return false;
|
|
61
|
-
if (norm.
|
|
62
|
-
if (norm.
|
|
61
|
+
if (norm.searchIntent?.length) {
|
|
62
|
+
if (norm.searchIntent.some((t) => PLATFORM_TYPES.has(t)))
|
|
63
63
|
return true;
|
|
64
|
-
if (norm.
|
|
64
|
+
if (norm.searchIntent.every((t) => CROSS_PLATFORM_TYPES.has(t)))
|
|
65
65
|
return false;
|
|
66
66
|
// faq / error_code 等默认可跨平台检索
|
|
67
|
-
if (norm.
|
|
67
|
+
if (norm.searchIntent.every((t) => t === 'faq' || t === 'error_code' || CROSS_PLATFORM_TYPES.has(t))) {
|
|
68
68
|
return false;
|
|
69
69
|
}
|
|
70
70
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
|
-
import { attachPoolContent } from '../content-loader.js';
|
|
4
|
+
import { attachPoolContent, getSourceFileContent } from '../content-loader.js';
|
|
5
|
+
import { buildReferences, resolveFragmentDocUrls } from '../doc-url-resolver.js';
|
|
5
6
|
import { rankSubsetWithIndex } from '../bm25.js';
|
|
6
7
|
import { getProductsBM25Index } from '../bm25-cache.js';
|
|
7
8
|
import { filterPool, normalizeParams } from './filter-pool.js';
|
|
@@ -79,6 +80,7 @@ function postProcess(ranked, chunksById, limit) {
|
|
|
79
80
|
title: chunk.title,
|
|
80
81
|
content: chunk.content,
|
|
81
82
|
source: chunk.source,
|
|
83
|
+
doc_urls: resolveFragmentDocUrls(chunk.source, chunk.content, getSourceFileContent(chunk.source)),
|
|
82
84
|
product: chunk.product,
|
|
83
85
|
framework: chunk.framework,
|
|
84
86
|
types: chunk.types,
|
|
@@ -135,8 +137,11 @@ function assessConfidence(fragments, exactHits) {
|
|
|
135
137
|
}
|
|
136
138
|
return { confidence: 'low', status: 'low_confidence' };
|
|
137
139
|
}
|
|
138
|
-
function buildAgentMessage(status, fragments, confidence) {
|
|
139
|
-
const
|
|
140
|
+
function buildAgentMessage(status, fragments, confidence, references) {
|
|
141
|
+
const citeTail = references?.length
|
|
142
|
+
? '作答时 MUST 在答案末尾附「**参考文档**」小节,逐条列出 `meta.references`(或各 fragment 的 `doc_urls`)中的 URL,便于用户核对;禁止编造未出现的链接。'
|
|
143
|
+
: '若 fragments 含 `doc_urls`,作答时须在末尾附「**参考文档**」列出对应 URL;禁止编造链接。';
|
|
144
|
+
const tail = `${citeTail} 合成答案后请调用 \`record_answer\`;用户反馈请调用 \`record_feedback\`。`;
|
|
140
145
|
if (status === 'needs_framework') {
|
|
141
146
|
return '【硬规则】问题依赖具体平台实现,但未指定 framework,全平台混检信息密度低且被禁止。MUST 先调用 `present_framework_choice`(传入 product),等用户选定 android/ios/flutter/react/vue/uni-app 等后再调用本工具,并 MUST 带上 **product + framework**。禁止传空 framework 硬搜。';
|
|
142
147
|
}
|
|
@@ -171,7 +176,7 @@ export function searchKnowledge(params) {
|
|
|
171
176
|
return {
|
|
172
177
|
status: 'needs_framework',
|
|
173
178
|
confidence: 'none',
|
|
174
|
-
message: buildAgentMessage('needs_framework', [], 'none'),
|
|
179
|
+
message: buildAgentMessage('needs_framework', [], 'none', undefined),
|
|
175
180
|
query: params,
|
|
176
181
|
meta: {
|
|
177
182
|
pool_size: manifest.chunks.length,
|
|
@@ -195,6 +200,7 @@ export function searchKnowledge(params) {
|
|
|
195
200
|
const ranked = rankSubsetWithIndex(norm.prompt, pool.map((chunk) => chunk.id), productIndex, boosts);
|
|
196
201
|
const fragments = postProcess(ranked, chunksById, norm.limit);
|
|
197
202
|
const urlMappingSources = [...new Set(fragments.map((f) => f.source).filter((source) => isUrlMappingSource(source)))];
|
|
203
|
+
const references = buildReferences(fragments);
|
|
198
204
|
// P0-3:相对阈值评估置信度(替代原硬编码 0.5 死阈值)
|
|
199
205
|
const { confidence, status } = assessConfidence(fragments, exactHits);
|
|
200
206
|
const topScore = fragments[0]?.score;
|
|
@@ -202,13 +208,14 @@ export function searchKnowledge(params) {
|
|
|
202
208
|
return {
|
|
203
209
|
status,
|
|
204
210
|
confidence,
|
|
205
|
-
message: buildAgentMessage(status, fragments, confidence),
|
|
211
|
+
message: buildAgentMessage(status, fragments, confidence, references),
|
|
206
212
|
query: params,
|
|
207
213
|
meta: {
|
|
208
214
|
pool_size: allChunks.length,
|
|
209
215
|
filtered_size: pool.length,
|
|
210
216
|
exact_hits: exactHits,
|
|
211
217
|
...(urlMappingSources.length ? { url_mapping_sources: urlMappingSources } : {}),
|
|
218
|
+
...(references.length ? { references } : {}),
|
|
212
219
|
...(topScore !== undefined ? { top_score: Number(topScore.toFixed(2)) } : {}),
|
|
213
220
|
...(secondScore !== undefined ? { second_score: Number(secondScore.toFixed(2)) } : {}),
|
|
214
221
|
},
|
package/mcp-server-prompt.png
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"knowledge/rtcengine/web/how_to_run_web_sdk_demo.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-10-basic-get-started-with-demo.md","knowledge/rtcengine/web/plugin/20-advanced-customized-capture-rendering.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-20-advanced-customized-capture-rendering.md","knowledge/rtcengine/web/plugin/21-advanced-auto-play-policy.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-21-advanced-auto-play-policy.md","knowledge/rtcengine/web/plugin/22-advanced-audio-mixer.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-22-advanced-audio-mixer.md","knowledge/rtcengine/web/plugin/23-advanced-support-detection.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-23-advanced-support-detection.md","knowledge/rtcengine/web/plugin/24-advanced-network-quality.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-24-advanced-network-quality.md","knowledge/rtcengine/web/plugin/25-advanced-device-change.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-25-advanced-device-change.md","knowledge/rtcengine/web/plugin/26-advanced-publish-cdn-stream.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-26-advanced-publish-cdn-stream.md","knowledge/rtcengine/web/plugin/27-advanced-small-stream.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-27-advanced-small-stream.md","knowledge/rtcengine/web/plugin/28-advanced-beauty.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-28-advanced-beauty.md","knowledge/rtcengine/web/plugin/29-advanced-water-mark.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-29-advanced-water-mark.md","knowledge/rtcengine/web/plugin/30-advanced-cross-room-link.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-30-advanced-cross-room-link.md","knowledge/rtcengine/web/plugin/31-advanced-plugins-overview.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-31-advanced-plugins-overview.md","knowledge/rtcengine/web/plugin/34-advanced-proxy.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-34-advanced-proxy.md","knowledge/rtcengine/web/plugin/35-advanced-ai-denoiser.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-35-advanced-ai-denoiser.md","knowledge/rtcengine/web/plugin/36-advanced-virtual-background.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-36-advanced-virtual-background.md","knowledge/rtcengine/web/plugin/37-advanced-voice-changer.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-37-advanced-voice-changer.md","knowledge/rtcengine/web/plugin/38-advanced-basic-beauty.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-38-advanced-basic-beauty.md","knowledge/rtcengine/web/plugin/39-advanced-video-decoder.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-39-advanced-video-decoder.md","knowledge/rtcengine/web/plugin/40-advanced-video-mixer.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-40-advanced-video-mixer.md","knowledge/rtcengine/web/plugin/41-advanced-small-stream-auto-switcher.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-41-advanced-small-stream-auto-switcher.md","knowledge/rtcengine/web/plugin/42-advanced-realtime-transcriber.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-42-advanced-realtime-transcriber.md","knowledge/rtcengine/web/plugin/43-advanced-face-detection.md":"https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-43-advanced-face-detection.md"}
|