Files
bigwo/test2/server/config/voiceChatConfig.js
2026-03-12 12:47:56 +08:00

173 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
},
},
};
// === 调试模式:纯 S2SOutputMode=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;