feat: 添加realtime_dialog和realtime_dialog_external_rag_test项目,更新test2项目
This commit is contained in:
@@ -1,172 +0,0 @@
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
class VoiceChatConfigBuilder {
|
||||
/**
|
||||
* 构建 StartVoiceChat 的完整配置(S2S 端到端语音大模型 + LLM 混合编排)
|
||||
* OutputMode=1: 混合模式,S2S 处理普通对话,LLM 处理工具调用
|
||||
*/
|
||||
static build(options) {
|
||||
const {
|
||||
roomId,
|
||||
taskId,
|
||||
userId,
|
||||
botName = '小智',
|
||||
systemRole = '你是一个友善的智能助手。',
|
||||
speakingStyle = '请使用温和、清晰的口吻。',
|
||||
modelVersion = '1.2.1.0',
|
||||
speaker = 'zh_female_vv_jupiter_bigtts',
|
||||
tools = [],
|
||||
llmSystemPrompt = '',
|
||||
enableWebSearch = false,
|
||||
vadEndMs = 1200,
|
||||
chatHistory = [],
|
||||
} = options;
|
||||
|
||||
const botUserId = `bot_${uuidv4().slice(0, 8)}`;
|
||||
|
||||
const providerParams = {
|
||||
app: {
|
||||
appid: process.env.VOLC_S2S_APP_ID,
|
||||
token: process.env.VOLC_S2S_TOKEN,
|
||||
},
|
||||
dialog: this._buildDialogConfig(modelVersion, botName, systemRole, speakingStyle, enableWebSearch, chatHistory),
|
||||
tts: {
|
||||
speaker: speaker,
|
||||
},
|
||||
asr: {
|
||||
extra: {
|
||||
enable_custom_vad: true,
|
||||
end_smooth_window_ms: vadEndMs,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// === 调试模式:纯 S2S(OutputMode=0),排除 LLM 干扰 ===
|
||||
// ARK 端点已配置正确,启用混合编排模式
|
||||
const DEBUG_PURE_S2S = false;
|
||||
|
||||
const llmConfig = {
|
||||
Mode: 'ArkV3',
|
||||
EndPointId: process.env.VOLC_ARK_ENDPOINT_ID,
|
||||
MaxTokens: 1024,
|
||||
Temperature: 0.1,
|
||||
TopP: 0.3,
|
||||
SystemMessages: [llmSystemPrompt || this._buildDefaultLLMPrompt(tools)],
|
||||
HistoryLength: 10,
|
||||
ThinkingType: 'disabled',
|
||||
};
|
||||
if (tools.length > 0) {
|
||||
llmConfig.Tools = tools;
|
||||
}
|
||||
|
||||
// 混合模式:通过 UserPrompts 传入聊天历史作为上下文(官方推荐方式)
|
||||
if (chatHistory && chatHistory.length > 0 && !DEBUG_PURE_S2S) {
|
||||
const userPrompts = chatHistory.slice(-10).map(m => ({
|
||||
Role: m.role === 'user' ? 'user' : 'assistant',
|
||||
Content: m.content,
|
||||
}));
|
||||
llmConfig.UserPrompts = userPrompts;
|
||||
console.log(`[VoiceChatConfig] Injected ${userPrompts.length} UserPrompts into LLMConfig`);
|
||||
}
|
||||
|
||||
const config = {
|
||||
AppId: process.env.VOLC_RTC_APP_ID,
|
||||
RoomId: roomId,
|
||||
TaskId: taskId,
|
||||
AgentConfig: {
|
||||
TargetUserId: [userId],
|
||||
WelcomeMessage: `你好,我是${botName},有什么需要帮忙的吗?`,
|
||||
UserId: botUserId,
|
||||
EnableConversationStateCallback: true,
|
||||
},
|
||||
Config: {
|
||||
S2SConfig: {
|
||||
Provider: 'volcano',
|
||||
OutputMode: DEBUG_PURE_S2S ? 0 : 1,
|
||||
ProviderParams: providerParams,
|
||||
},
|
||||
// 注意:S2S 端到端模式下不需要独立 TTSConfig
|
||||
// ExternalTextToSpeech 在 S2S 模式下不产生音频,只用 Command:function
|
||||
SubtitleConfig: {
|
||||
SubtitleMode: 1,
|
||||
},
|
||||
InterruptMode: 0,
|
||||
},
|
||||
};
|
||||
|
||||
// 混合模式才需要 LLMConfig
|
||||
if (!DEBUG_PURE_S2S) {
|
||||
config.Config.LLMConfig = llmConfig;
|
||||
|
||||
// Function Calling 回调配置:RTC 服务通过此 URL 发送 tool call 请求
|
||||
if (tools.length > 0) {
|
||||
const serverUrl = process.env.FC_SERVER_URL || 'https://demo.tensorgrove.com.cn/api/voice/fc_callback';
|
||||
config.Config.FunctionCallingConfig = {
|
||||
ServerMessageUrl: serverUrl,
|
||||
ServerMessageSignature: process.env.FC_SIGNATURE || 'default_signature',
|
||||
};
|
||||
console.log(`[VoiceChatConfig] FunctionCallingConfig enabled, URL: ${serverUrl}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[VoiceChatConfig] DEBUG_PURE_S2S:', DEBUG_PURE_S2S);
|
||||
console.log('[VoiceChatConfig] OutputMode:', config.Config.S2SConfig.OutputMode);
|
||||
console.log('[VoiceChatConfig] ProviderParams type:', typeof config.Config.S2SConfig.ProviderParams);
|
||||
console.log('[VoiceChatConfig] S2S AppId:', providerParams.app.appid);
|
||||
console.log('[VoiceChatConfig] S2S Token:', providerParams.app.token ? '***set***' : '***MISSING***');
|
||||
|
||||
return { config, botUserId };
|
||||
}
|
||||
|
||||
static _buildDialogConfig(modelVersion, botName, systemRole, speakingStyle, enableWebSearch, chatHistory = []) {
|
||||
const isOSeries = modelVersion === 'O' || modelVersion.startsWith('1.');
|
||||
const dialog = {
|
||||
extra: { model: modelVersion },
|
||||
};
|
||||
|
||||
// 如果有文字聊天历史,将其追加到 system_role 作为上下文
|
||||
let fullSystemRole = systemRole;
|
||||
if (chatHistory && chatHistory.length > 0) {
|
||||
const historyText = chatHistory
|
||||
.slice(-10)
|
||||
.map(m => `${m.role === 'user' ? '用户' : '助手'}:${m.content}`)
|
||||
.join('\n');
|
||||
fullSystemRole += `\n\n## 之前的对话记录(请延续此上下文)\n${historyText}`;
|
||||
console.log(`[VoiceChatConfig] Injected ${chatHistory.length} chat history messages into system_role`);
|
||||
}
|
||||
|
||||
if (isOSeries) {
|
||||
dialog.bot_name = botName;
|
||||
dialog.system_role = fullSystemRole;
|
||||
dialog.speaking_style = speakingStyle;
|
||||
} else {
|
||||
dialog.character_manifest = `${fullSystemRole}\n你的名字是${botName}。${speakingStyle}`;
|
||||
}
|
||||
|
||||
if (enableWebSearch && process.env.VOLC_WEBSEARCH_API_KEY) {
|
||||
dialog.extra.enable_volc_websearch = true;
|
||||
dialog.extra.volc_websearch_api_key = process.env.VOLC_WEBSEARCH_API_KEY;
|
||||
dialog.extra.volc_websearch_type = 'web_summary';
|
||||
dialog.extra.volc_websearch_no_result_message = '抱歉,我没有查到相关信息。';
|
||||
}
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
static _buildDefaultLLMPrompt(tools) {
|
||||
const toolNames = tools.map((t) => t.function?.name).filter(Boolean);
|
||||
if (toolNames.length === 0) {
|
||||
return '你是一个智能助手。对于所有问题直接回答即可。';
|
||||
}
|
||||
return `你是一个企业智能客服助手。你可以使用以下工具:${toolNames.join('、')}。
|
||||
|
||||
## 最高优先级规则
|
||||
1. 每次用户提问,你**必须**先调用 search_knowledge 工具查询知识库
|
||||
2. 收到工具返回的知识库内容后,你**必须完整、详细地朗读**知识库返回的内容给用户
|
||||
3. 不要省略、总结或缩写知识库的内容,要逐字朗读
|
||||
4. 如果知识库没有相关内容,再用你自己的知识简洁回答
|
||||
5. 如果知识库返回"未找到相关信息",直接告诉用户并提供建议`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VoiceChatConfigBuilder;
|
||||
Reference in New Issue
Block a user