/** * TalkingQ Platform - Agent Configuration Workbench * Pure JS component functions following the existing static SPA router. */ const AGENT_CONFIG_STEPS = [ { id: 'voice', index: 1, title: '语音交互' }, { id: 'understand', index: 2, title: '理解和生成' }, { id: 'skills', index: 3, title: '技能' }, { id: 'advanced', index: 4, title: '高级设置' }, ]; const AGENT_CONFIG_DEFAULT_APPS = [ { id: 'app-001', type: 'multimodal', name: '小Q 故事机器人', status: 'published' }, { id: 'app-002', type: 'voice', name: '英语学习助手', status: 'editing' }, { id: 'app-003', type: 'voice', name: '科学百科探索者', status: 'draft' }, { id: 'app-004', type: 'multimodal', name: '数学小课堂', status: 'published' }, ]; const AGENT_SCENE_TEMPLATES = [ { id: 'ai-audio-glasses', name: 'AI音频眼镜', icon: 'headphones', desc: '随身携带的语音助手' }, { id: 'ar-glasses', name: 'AR眼镜', icon: 'glasses', desc: '具备屏幕显示能力' }, { id: 'desktop-robot', name: '桌面机器人', icon: 'bot', desc: '实时语音对话,日常情感陪伴' }, { id: 'smart-home', name: '智能家居', icon: 'home', desc: '语音控制家中设备' }, { id: 'toy', name: '儿童玩具', icon: 'star', desc: '陪伴孩子聊天、放音乐、讲故事' }, { id: 'story-player', name: '儿童故事机', icon: 'book-open', desc: '给孩子讲故事、放儿歌' }, { id: 'smart-speaker', name: '智能音箱', icon: 'volume-2', desc: '语音控制播放音乐' }, { id: 'mobile-app', name: '手机应用', icon: 'smartphone', desc: '实时语音互动应用' }, ]; const AGENT_SCENE_VERSIONS = [ { id: 'full', name: '全能版', desc: '支持意图识别、工具调用、联网搜索、多场景 Agent,适合完整智能体体验。', }, { id: 'lite', name: '轻量版', desc: '更快速、更低价的语音闲聊,不启用复杂工具调用和 Agent 编排。', }, ]; const AGENT_SCENE_LANGUAGES = [ { id: 'zh', name: '中文', desc: '适合中文普通话和中文儿童内容场景' }, { id: 'en', name: '英文', desc: '适合英语对话、英语学习和双语启蒙场景' }, ]; const AGENT_CONFIG_MOCK = { voice: { languageOptions: [ { id: 'zh', label: '中文' }, { id: 'en', label: '英文' }, ], asrModels: [ { id: 'fun-asr', icon: 'audio-lines', label: 'Fun-ASR实时语音识别', desc: '中英文自由切换,多地区方言覆盖,适应多样复杂环境。', }, { id: 'qwen3-asr', icon: 'audio-lines', label: '通义千问3-ASR-Flash-Realtime', desc: '高精度语音识别,中英日韩德法俄意西葡阿和多种方言。', }, { id: 'paraformer', icon: 'audio-lines', label: 'Paraformer实时语音识别', desc: '新一代非自回归端到端模型,支持中英日韩德法俄等语言。', }, { id: 'multimodal-lite-asr', icon: 'audio-lines', label: '多模态交互轻量版语音识别', desc: '超低价格,预算友好,支持中英等语言。', }, ], ttsGroups: [ { id: 'cosyvoice', title: 'CosyVoice-实时语音合成', models: [ { id: 'cosyvoice-v3-flash', label: 'CosyVoice-v3-Flash大模型', desc: '独家音色,性价比高,支持情感控制。' }, { id: 'cosyvoice-v3-plus', label: 'CosyVoice-v3-Plus大模型', desc: '情感表达最佳,支持声音复刻。' }, { id: 'cosyvoice-v35-plus', label: 'CosyVoice-v3.5-Plus大模型', desc: '声音设计/复刻最佳,多种方言和外语。' }, { id: 'cosyvoice-v35-flash', label: 'CosyVoice-v3.5-Flash大模型', desc: '声音设计/复刻性价比高,多种方言和外语。' }, { id: 'cosyvoice-v2', label: 'CosyVoice-v2大模型', desc: '音色丰富,70+音色可选,一句话声音复刻。' }, ], }, { id: 'qwen-tts', title: '通义千问3-实时语音合成', models: [ { id: 'qwen3-tts-flash', label: '通义千问3-TTS-Flash-Realtime', desc: '多语言,拥有17种高表现力的拟人音色。' }, { id: 'qwen3-tts-instruct', label: '通义千问3-TTS-Instruct-Flash-Realtime', desc: '支持指令控制,调整音调、语速、情感、特点。' }, { id: 'qwen3-tts-design', label: '通义千问3-TTS-声音设计', desc: '通过文本描述生成定制化音色,支持多维度定义。' }, { id: 'qwen3-tts-clone', label: '通义千问3-TTS-声音复刻', desc: '10-20秒音频即可生成效果最佳的定制音色。' }, ], }, { id: 'others', title: '其他语音合成模型', models: [ { id: 'sambert-tts', label: 'Sambert-实时语音合成', desc: '音色选择多,多语言,合成速度快。' }, { id: 'lite-tts', label: '多模态交互轻量版语音合成', desc: '预算友好,支持多语种和方言。' }, ], }, ], defaultHotwords: ['TalkingQ', '小Q', '白雪公主', '睡前故事', '恐龙百科'], correctionHint: '通过接口上传词表,对语音识别结果进行实时干预。', }, understand: { intentModel: { icon: 'route', label: '意图识别', desc: '关闭将不再触发调用工具/Agent 等基于语义理解的交互功能。', }, textModels: [ { id: 'recommended-fast', group: '推荐模型', label: '多模态交互专有模型-高速版', desc: '对话速度更快,适合通用场景、闲聊陪伴场景。' }, { id: 'recommended-tool', group: '推荐模型', label: '多模态交互专有模型', desc: '指令遵循能力更强,适合工作学习场景。' }, { id: 'recommended-style', group: '推荐模型', label: '多模态交互专有模型-风格化版', desc: '风格化能力更强,适合角色扮演等场景。' }, { id: 'qwen', group: '更多模型', label: '多千问', desc: '通用能力均衡,适合大多数任务。' }, { id: 'deepseek', group: '更多模型', label: 'DeepSeek', desc: '推理和代码表现更强。' }, { id: 'chatgpt', group: '更多模型', label: 'ChatGPT', desc: '多场景通用对话能力。' }, { id: 'doubao', group: '更多模型', label: '豆包', desc: '适合中文对话和内容生成。' }, ], prompt: `# 角色 你是小Q,一个专为 3-10 岁儿童设计的 AI 玩具智能体。 ## 沟通风格 - 语言简单、生动、温暖。 - 回复通常控制在 150 字以内。 - 优先使用比喻、拟声词和故事化表达。 - 不能输出任何不适合儿童的危险内容。 ## 行为准则 1. 当孩子要求讲故事时,先确认主题,再生成短故事。 2. 当孩子提问百科知识时,先给出结论,再给出一个生活例子。 3. 当识别到情绪陪伴需求时,优先安抚,再引导表达。 \`\`\`policy 安全等级:儿童模式 联网搜索:必要时开启 知识库优先级:高 \`\`\``, variables: [ { name: 'child_age', desc: '儿童年龄' }, { name: 'device_mode', desc: '设备当前模式' }, { name: 'ambient_noise', desc: '环境噪音等级' }, { name: 'weather', desc: '当前位置天气' }, ], knowledgeBases: [ { name: '儿童科学百科', desc: '动物、太空、自然科学问答素材', weight: '0.72' }, { name: '故事素材库 V2', desc: '睡前故事、角色设定、儿童互动剧情', weight: '0.68' }, ], }, skills: { commands: [ { icon: 'phone-call', name: '打电话', count: 6, desc: '拨打联系人电话、语音拨号等电话相关控制' }, { icon: 'alarm-clock', name: '定闹钟', count: 1, desc: '创建、关闭、修改闹钟' }, { icon: 'volume-2', name: '音量设置', count: 7, desc: '调节媒体音量、铃声音量、静音等' }, { icon: 'bluetooth', name: '蓝牙', count: 4, desc: '打开/关闭蓝牙、连接蓝牙设备' }, { icon: 'toggle-right', name: '应用开关', count: 10, desc: '打开/关闭指定 App 或系统功能' }, { icon: 'cpu', name: '设备控制', count: 5, desc: '系统级设备控制能力(当前已选中)' }, { icon: 'music', name: '音乐播放', count: 5, desc: '播放音乐、暂停、下一首等' }, { icon: 'circle-play', name: '多媒体控制', count: 6, desc: '控制媒体播放状态与音频输出' }, { icon: 'camera', name: '拍照录像', count: 6, desc: '打开相机、拍照、录像' }, { icon: 'mic', name: '录音', count: 2, desc: '开始/停止录音' }, { icon: 'book-open', name: '朗读文字', count: 1, desc: '语音朗读输入文本' }, { icon: 'message-circle', name: '发短信', count: 3, desc: '发送短信消息' }, { icon: 'sun', name: '亮度设置', count: 5, desc: '调节屏幕亮度' }, { icon: 'sun-medium', name: '色温设置', count: 3, desc: '调节屏幕色温/护眼模式' }, { icon: 'video', name: '视频播放', count: 1, desc: '打开并播放视频' }, { icon: 'map-pinned', name: '打开地图', count: 1, desc: '打开地图导航类应用' }, { icon: 'monitor', name: '屏幕控制', count: 4, desc: '锁屏、亮屏、熄屏、旋转等屏幕操作' }, ], plugins: [ { icon: 'cloud-sun', name: '天气查询', desc: '查询实时天气、空气质量和未来预报', status: '系统执行' }, { icon: 'calendar', name: '万年历', desc: '查询日期、农历、节假日和宜忌信息', status: '系统执行' }, { icon: 'newspaper', name: '新闻资讯', desc: '获取最新新闻资讯和摘要播报', status: '系统执行' }, { icon: 'navigation', name: '路线规划', desc: '规划出行路线、距离和预计耗时', status: '系统执行' }, { icon: 'map', name: '本地生活', desc: '查询周边商户、生活服务和地点信息', status: '系统执行' }, ], bailianApps: [ { name: '故事生成器', desc: '基于主题生成短故事' }, { name: '儿童内容安全', desc: '对回答内容进行安全审查' }, ], thirdPartyApps: [ { name: '音频内容库', desc: '接入第三方儿歌和故事资源' }, ], }, }; registerPage('agent-config', function(container) { if (typeof window.__ensureAgentHotwordGroups === 'function') { window.__ensureAgentHotwordGroups(); } else if (!window.__agentHotwordGroups) { window.__agentHotwordGroups = [ { id: 'hw-001', name: '品牌基础热词', mode: 'manual', words: [ { text: 'TalkingQ', lang: '英文' }, { text: '小Q', lang: '中文' }, { text: '故事机', lang: '中文' }, ], updatedAt: '2026-05-20 11:57:38', }, { id: 'hw-002', name: '儿童内容热词', mode: 'manual', words: [ { text: '白雪公主', lang: '中文' }, { text: '睡前故事', lang: '中文' }, ], updatedAt: '2026-05-20 12:08:16', }, ]; } const apps = window.__agentManagedApps || AGENT_CONFIG_DEFAULT_APPS; const selectedApp = apps.find(app => app.id === window.__selectedAppId) || apps[0] || AGENT_CONFIG_DEFAULT_APPS[0]; const firstHotwordGroup = window.__agentHotwordGroups?.[0]?.id || ''; const state = { app: selectedApp, activeStep: 'voice', preview: 'empty', selectedSceneId: window.__selectedSceneTemplateId || 'toy', selectedSceneVersion: window.__selectedSceneVersion || 'full', selectedSceneLanguage: window.__selectedSceneLanguage || 'zh', voiceEnabled: true, asrEnabled: true, ttsEnabled: true, selectedAsrModelId: AGENT_CONFIG_MOCK.voice.asrModels[0]?.id || '', selectedTtsGroupId: AGENT_CONFIG_MOCK.voice.ttsGroups[0]?.id || '', selectedTtsModelId: AGENT_CONFIG_MOCK.voice.ttsGroups[0]?.models[0]?.id || '', selectedHotwordGroupId: firstHotwordGroup, realtimeCorrectionEnabled: true, ttsFilterEnabled: false, ttsFilterPairs: [ { enabled: true, left: '/*', right: '*/' }, { enabled: true, left: '[', right: ']' }, { enabled: false, left: '{', right: '}' }, ], ttsFilterSingles: [], interruptPrimaryEnabled: true, interruptSecondaryEnabled: true, interruptMode: 'full', semanticStopEnabled: true, edgeAlgorithmExpanded: true, selectedTextModelMode: 'recommended', selectedTextModelId: AGENT_CONFIG_MOCK.understand.textModels[0]?.id || '', intentEnabled: true, dialogueContinuationEnabled: true, knowledgeSearchEnabled: true, searchMode: 'fast', longTermMemoryEnabled: false, knowledgeBases: AGENT_CONFIG_MOCK.understand.knowledgeBases.map(item => ({ ...item })), customCommands: [], customPlugins: [], }; function render() { const scrollArea = container.querySelector('.bailian-config-scroll'); const previousScrollTop = scrollArea?.scrollTop || 0; if (state.selectedHotwordGroupId && !(window.__agentHotwordGroups || []).some(group => group.id === state.selectedHotwordGroupId)) { state.selectedHotwordGroupId = window.__agentHotwordGroups?.[0]?.id || ''; } container.innerHTML = renderAgentWorkbench(state); renderAgentIcons(container); bindWorkbenchEvents(container, state, render); bindPromptCounter(container); requestAnimationFrame(() => { const nextScrollArea = container.querySelector('.bailian-config-scroll'); if (nextScrollArea) { nextScrollArea.scrollTop = previousScrollTop; } }); } const onHotwordGroupsChanged = () => { if (!container.isConnected || window.__currentPage !== 'agent-config') { window.removeEventListener('hotwordGroupsChanged', onHotwordGroupsChanged); return; } render(); }; window.addEventListener('hotwordGroupsChanged', onHotwordGroupsChanged); render(); }); function renderAgentWorkbench(state) { return `
${renderAgentHeader(state.app)}
${renderPreviewPane(state.preview)}
`; } function renderAgentHeader(app) { const status = getAgentStatus(app.status); return `

${escapeHtml(app.name)}

${status.label}
ID: ${escapeHtml(app.id)} · AI 智能体配置平台
`; } function renderStepTabs(activeStep) { return ` `; } function renderAllStepSections(state) { return AGENT_CONFIG_STEPS.map(step => `
${renderStepContent(step.id, state)}
`).join(''); } function renderStepContent(stepId, state) { const renderers = { voice: renderVoiceStep, understand: renderUnderstandStep, skills: renderSkillsStep, advanced: renderAdvancedStep, }; return (renderers[stepId] || renderVoiceStep)(state); } function renderVoiceStep(state) { const data = AGENT_CONFIG_MOCK.voice; const scene = getSelectedScene(state); const version = getSelectedSceneVersion(state); const language = getSelectedSceneLanguage(state); const hotwordGroups = window.__agentHotwordGroups || []; return `

语音交互

${renderSceneCard(scene, version, language)}
开启语音交互
打开后运行将使用语音进行交互,关闭后仅保留文字交互
${state.voiceEnabled ? `
语音 AI
${renderFeatureSelect({ title: '语音识别', toggleKey: 'asr-enabled', checked: state.asrEnabled, activeId: state.selectedAsrModelId, options: data.asrModels, hint: '前面的开关决定是否启用 ASR,后面的下拉用于切换识别模型', })} ${renderFeatureSelect({ title: '语音合成', toggleKey: 'tts-enabled', checked: state.ttsEnabled, activeId: state.selectedTtsModelId, options: data.ttsGroups.flatMap(group => group.models.map(model => ({ ...model, groupTitle: group.title }))), groupedOptions: data.ttsGroups, hint: '前面的开关决定是否启用 TTS,后面的下拉用于切换播报模型', })}
热词
热词组绑定
热词组来自热词库管理,新增后会自动出现在这里
即时纠错
开启即时纠错
${escapeHtml(data.correctionHint)}
可上传词表对识别结果进行实时干预
语音合成播报过滤
开启播报过滤
特定符号对中的内容将不会进行 TTS 播报
${state.ttsFilterEnabled ? `
选中符号对内的文字将不会通过 TTS 播报,如 [动作/表情编码]、[设备参数] 及 $$公式$$ 等。
` : ''}
${state.ttsFilterEnabled ? renderTtsFilterSummary(state) : ''}
对话打断
此配置仅用于效果体验
长按对话(半双工,仅移动端支持) 运行后可在移动端长按触发
语义判停与拒识
开启语义判停与拒识
开启后可能导致对话延迟增加
基于用户语义,判断是否结束语音输入,以及是否需要模型回复。全双工对话场景下,本功能效果更显著。
端侧算法
${state.edgeAlgorithmExpanded ? `
${[ ['语音唤醒', '默认唤醒词“小云小云”'], ['端侧 VAD', '语音活性检测,判断是否存在语音信号'], ['AEC', '回声消除,减少自身播报的干扰'], ['定向拾音', '增强特定方向拾音,抑制其他方向'], ].map(([title, desc]) => `
${escapeHtml(title)} ${escapeHtml(desc)}
`).join('')}
` : ''}
` : `
当前为文字交互模式 关闭语音交互后,不展示语音 AI、热词、纠错、播报过滤、打断和端侧算法配置。
`}
`; } function renderUnderstandStep(state) { const data = AGENT_CONFIG_MOCK.understand; const selectedMode = state.selectedTextModelMode || 'recommended'; const selectedModelId = state.selectedTextModelId || data.textModels[0]?.id || ''; const selectedModel = data.textModels.find(item => item.id === selectedModelId) || data.textModels.find(item => item.group === '推荐模型') || data.textModels[0]; return `
${renderSection('意图识别', `
开启意图识别
${escapeHtml(data.intentModel.desc)}
关闭将不再触发调用工具/Agent 等基于语义理解的交互功能,适用于低成本、轻量级语音交互场景。
`)} ${renderSection('文本模型', `
模型选择
建议使用推荐模型,为用户提供流畅的对话体验
推荐 / 更多
${escapeHtml(selectedModel.desc)}
`)} ${renderPromptEditor(data.prompt)} ${renderSection('对话变量', `
支持在对话输入中添加变量,为模型提供用户语音之外的更多信息,如天气、周围环境等。
${data.variables.map(item => `
${escapeHtml(item.name)}
${escapeHtml(item.desc)}
`).join('')}
`)} ${renderSection('上下文轮数', `
携带最近 轮对话作为上下文
`)} ${renderKnowledgeBaseSection(state.knowledgeBases || data.knowledgeBases)} ${renderSection('对话承接语', `
开启对话承接语
大模型基于用户输入自动生成承接语,减少回复等待时间
`)} ${renderSection('互联网搜索', `
开启互联网搜索
关闭后不展示搜索模式,仅保留本地知识和模型能力
${state.knowledgeSearchEnabled !== false ? `
` : ''} `)} ${renderSection('长期记忆', `
开启长期记忆
基于对话历史形成专属记忆库,并在后续对话中体现
开启长期记忆后,结合记忆内容进行回复的对话会增加额外耗时。
`)}
`; } function renderSkillsStep(state) { const data = AGENT_CONFIG_MOCK.skills; const commands = [...data.commands, ...(state.customCommands || [])]; const plugins = [...data.plugins, ...(state.customPlugins || [])]; return `
${renderSection('指令', `
当前已选择的指令可以编辑,右侧加号可添加新的指令类型
${commands.map(item => `
${escapeHtml(item.name)} ${item.count || 1} 指令
${escapeHtml(item.desc)}
`).join('')}
`)} ${renderSection('插件', `
插件能力默认由系统执行,也可以独立配置启用状态
${plugins.map(item => `
${escapeHtml(item.name)}
${escapeHtml(item.desc)}
${escapeHtml(item.status || '系统执行')}
`).join('')}
`)} ${renderMiniAppSection('百炼应用', data.bailianApps, 'boxes')} ${renderMiniAppSection('三方应用', data.thirdPartyApps, 'plug')} ${renderMcpSection()} ${renderSection('Planning / Function Calling', ` ${renderSwitchRow('开启 Planning', '让智能体先规划步骤,再调用函数或插件完成复杂任务', true)} ${renderSwitchRow('开启 Function Calling', '允许模型按照结构化参数调用已授权函数', true)} `)}
`; } function renderAdvancedStep() { return `
${renderSection('高级设置', ` ${renderSwitchRow('儿童安全模式', '强制启用儿童内容安全策略、危险行为拒答和敏感信息保护', true)} ${renderSwitchRow('链路追踪', '记录 ASR、LLM、RAG、TTS 的耗时和调用链路', true)} ${renderSwitchRow('自动降级', '主模型异常时自动切换到备用模型,保证设备可用性', true)} `)} ${renderSection('调用预算', `
`)} ${renderSection('发布策略', `
${renderRadio('release', 'gray', '灰度发布 10%', true)} ${renderRadio('release', 'full', '全量发布')} ${renderRadio('release', 'schedule', '定时发布')}
`)}
`; } function renderPreviewPane(previewState) { const title = previewState === 'running' ? '预览环境已准备好' : '请填好配置后点击运行'; const desc = previewState === 'running' ? '当前智能体已加载最新 mock 配置,可继续调整参数后再次运行。' : '当前应用测试会产生实际调用,按对话次数计费'; return `

${title}

${desc}

建议佩戴耳机,并在安静环境下测试,对话体验更佳。
`; } function renderSceneCard(scene, version, language) { return ` `; } function renderSection(title, content) { return `

${escapeHtml(title)}

${content}
`; } function renderModelSelect(model) { return ` `; } function renderFeatureSelect(config) { const { title, toggleKey, checked, activeId, options = [], groupedOptions = null, hint = '', } = config; const fieldAction = toggleKey === 'asr-enabled' ? 'asr-model' : toggleKey === 'tts-enabled' ? 'tts-model' : 'model'; const selectedOption = options.find(item => item.id === activeId) || options[0] || null; return `
${escapeHtml(title)}
${escapeHtml(hint)}
${selectedOption ? `
${escapeHtml(selectedOption.desc)}
` : ''}
`; } function renderSwitchRow(label, desc, checked) { return `
${escapeHtml(label)}
${escapeHtml(desc)}
`; } function renderRadio(name, value, label, checked = false) { return ` `; } function renderTtsFilterSummary(state) { if (!state.ttsFilterEnabled) return ''; const commonPairs = (state.ttsFilterPairs || []) .filter(item => item.enabled !== false) .map(item => `${item.left} ${item.right}`.trim()); const singles = state.ttsFilterSingles || []; return `
常用过滤符号对
${commonPairs.length ? commonPairs.map(item => `${escapeHtml(item)}`).join('') : '暂无'}
自定义过滤符号对与单字符
${singles.length ? singles.map(item => `${escapeHtml(item)}`).join('') : '暂无'}
`; } function renderPromptEditor(prompt) { return `

Prompt 提示词

`; } function renderKnowledgeBaseSection(items) { return renderSection(`知识库 ${items.length}/10`, `
已挂载知识库会参与 RAG 检索和答案补充
${items.map(item => `
${escapeHtml(item.name)}
${escapeHtml(item.desc)}
权重 ${escapeHtml(item.weight)}
`).join('')}
`); } function renderWebSearchSection(modes) { return renderSection('联网搜索', `
${modes.map(mode => ` `).join('')}
`); } function renderMiniAppSection(title, items, iconName) { return renderSection(title, `
${items.map(item => `
${escapeHtml(item.name)} ${escapeHtml(item.desc)}
`).join('')}
`); } function renderMcpSection() { return renderSection('MCP服务 0', `
MCP服务 0

智能体可以通过标准化协议(MCP)连接企业内部服务API并发起调用

`); } function bindWorkbenchEvents(container, state, rerender) { container.querySelectorAll('[data-step]').forEach(tab => { tab.addEventListener('click', () => { state.activeStep = tab.dataset.step; updateActiveStepTab(container, state.activeStep); scrollToAgentStep(container, state.activeStep); }); }); bindStepScrollSpy(container, state); container.querySelectorAll('[data-toggle]').forEach(input => { input.addEventListener('change', () => { const key = input.dataset.toggle; const checked = input.checked; const map = { 'voice-enabled': 'voiceEnabled', 'asr-enabled': 'asrEnabled', 'tts-enabled': 'ttsEnabled', 'realtime-correction': 'realtimeCorrectionEnabled', 'tts-filter': 'ttsFilterEnabled', 'interrupt-primary': 'interruptPrimaryEnabled', 'interrupt-secondary': 'interruptSecondaryEnabled', 'semantic-stop': 'semanticStopEnabled', 'intent-enabled': 'intentEnabled', 'continuation': 'dialogueContinuationEnabled', 'knowledge-search': 'knowledgeSearchEnabled', 'long-term-memory': 'longTermMemoryEnabled', }; const stateKey = map[key]; if (stateKey) { state[stateKey] = checked; rerender(); } }); }); container.querySelectorAll('input[name="textModelMode"]').forEach(radio => { radio.addEventListener('change', () => { if (!radio.checked) return; state.selectedTextModelMode = radio.value; const group = radio.value === 'recommended' ? '推荐模型' : '更多模型'; const candidates = AGENT_CONFIG_MOCK.understand.textModels.filter(item => item.group === group); state.selectedTextModelId = candidates[0]?.id || state.selectedTextModelId; rerender(); }); }); container.querySelectorAll('input[name="webSearchMode"]').forEach(radio => { radio.addEventListener('change', () => { if (!radio.checked) return; state.searchMode = radio.value; rerender(); }); }); container.querySelectorAll('[data-action="text-model"]').forEach(select => { select.addEventListener('change', () => { state.selectedTextModelId = select.value; const current = AGENT_CONFIG_MOCK.understand.textModels.find(item => item.id === select.value); if (current) state.selectedTextModelMode = current.group === '更多模型' ? 'more' : 'recommended'; rerender(); }); }); container.querySelectorAll('[data-action="asr-model"]').forEach(select => { select.addEventListener('change', () => { state.selectedAsrModelId = select.value; rerender(); }); }); container.querySelectorAll('[data-action="tts-model"]').forEach(select => { select.addEventListener('change', () => { state.selectedTtsModelId = select.value; const matchedGroup = AGENT_CONFIG_MOCK.voice.ttsGroups.find(group => group.models.some(model => model.id === select.value)); state.selectedTtsGroupId = matchedGroup?.id || state.selectedTtsGroupId; rerender(); }); }); container.querySelectorAll('[data-action="hotword-group"]').forEach(select => { select.addEventListener('change', () => { state.selectedHotwordGroupId = select.value; rerender(); }); }); container.querySelectorAll('[data-action="scene-language"]').forEach(select => { select.addEventListener('change', () => { state.selectedSceneLanguage = select.value; window.__selectedSceneLanguage = select.value; rerender(); }); }); container.querySelectorAll('[data-action]').forEach(el => { el.addEventListener('click', event => { const action = el.dataset.action; if (action === 'back') { navigateTo('agent-manage-multimodal'); return; } if (action === 'rename') { openRenameModal(state, rerender); return; } if (action === 'publish') { state.app.status = 'published'; Toast.success('应用已提交发布,预计 2-5 分钟生效'); rerender(); return; } if (action === 'run') { state.preview = 'running'; updatePreviewOnly(container, state.preview); Toast.success('运行预览已启动'); return; } if (action === 'add-kb') { navigateTo('knowledge-base'); return; } if (action === 'knowledge-weight') { openKnowledgeWeightModal(state, rerender); return; } if (action === 'scene') { openSceneTemplateModal(state, rerender); return; } if (action === 'hotwords') { navigateTo('hotword-library'); return; } if (action === 'tts-filter-config') { openTtsFilterModal(state, rerender); return; } if (action === 'command-config') { openCommandConfigModal(state, rerender); return; } if (action === 'plugin-config') { openPluginConfigModal(state, rerender); return; } if (action === 'edge-collapse') { state.edgeAlgorithmExpanded = !state.edgeAlgorithmExpanded; rerender(); return; } const actionText = { history: '打开历史记录', api: '打开 API 接入文档', license: '打开 License 购买页', model: '切换模型', variable: '打开变量配置', setting: '打开设置', remove: '已移除当前项', mcp: '添加 MCP 服务', 'add-app': '添加应用', 'correction-upload': '上传词表', }; Toast.info(actionText[action] || '操作已触发'); event.stopPropagation(); }); }); } function updateActiveStepTab(container, stepId) { container.querySelectorAll('.bailian-step-tab').forEach(tab => { tab.classList.toggle('active', tab.dataset.step === stepId); }); } function scrollToAgentStep(container, stepId) { const scrollArea = container.querySelector('.bailian-config-scroll'); const target = container.querySelector(`#agent-step-${stepId}`); if (!scrollArea || !target) return; const offset = scrollArea.scrollTop + target.getBoundingClientRect().top - scrollArea.getBoundingClientRect().top - 8; scrollArea.scrollTo({ top: Math.max(offset, 0), behavior: 'smooth' }); } function bindStepScrollSpy(container, state) { const scrollArea = container.querySelector('.bailian-config-scroll'); if (!scrollArea) return; let ticking = false; scrollArea.addEventListener('scroll', () => { if (ticking) return; ticking = true; requestAnimationFrame(() => { const marker = scrollArea.getBoundingClientRect().top + 96; let currentStep = AGENT_CONFIG_STEPS[0].id; AGENT_CONFIG_STEPS.forEach(step => { const section = container.querySelector(`#agent-step-${step.id}`); if (section && section.getBoundingClientRect().top <= marker) { currentStep = step.id; } }); if (state.activeStep !== currentStep) { state.activeStep = currentStep; updateActiveStepTab(container, currentStep); } ticking = false; }); }); } function updatePreviewOnly(container, previewState) { const preview = container.querySelector('.bailian-preview-pane'); if (!preview) return; const temp = document.createElement('div'); temp.innerHTML = renderPreviewPane(previewState); preview.replaceWith(temp.firstElementChild); renderAgentIcons(container.querySelector('.bailian-preview-pane')); } function bindPromptCounter(container) { const editor = container.querySelector('#agentPromptEditor'); const counter = container.querySelector('#promptCounter'); if (!editor || !counter) return; const update = () => { counter.textContent = `${editor.value.length}/8000`; }; editor.addEventListener('input', update); update(); } function openSceneTemplateModal(state, rerender) { let pendingSceneId = state.selectedSceneId; let pendingVersionId = state.selectedSceneVersion; let pendingLanguageId = state.selectedSceneLanguage; if (typeof createModal !== 'function') { Toast.info('场景模板选择器暂不可用'); return; } const modal = createModal({ title: '切换场景模板', size: 'xl', confirmText: '应用模板', cancelText: '取消', content: `
选择场景
${AGENT_SCENE_TEMPLATES.map(scene => ` `).join('')}
选择版本
${AGENT_SCENE_VERSIONS.map(version => ` `).join('')}
对话语言 *
${AGENT_SCENE_LANGUAGES.map(language => ` `).join('')}
`, onConfirm: () => { if (!pendingLanguageId || !AGENT_SCENE_LANGUAGES.some(language => language.id === pendingLanguageId)) { Toast.warning('请选择对话语言'); return false; } state.selectedSceneId = pendingSceneId; state.selectedSceneVersion = pendingVersionId; state.selectedSceneLanguage = pendingLanguageId; window.__selectedSceneTemplateId = pendingSceneId; window.__selectedSceneVersion = pendingVersionId; window.__selectedSceneLanguage = pendingLanguageId; const scene = getSelectedScene(state); const version = getSelectedSceneVersion(state); const language = getSelectedSceneLanguage(state); Toast.success(`已切换至「${scene.name} · ${version.name} · ${language.name}」`); rerender(); return true; }, }); const overlay = modal.overlay; const syncSceneOptions = () => { overlay.querySelectorAll('.bailian-scene-option').forEach(option => { option.classList.toggle('selected', option.dataset.sceneId === pendingSceneId); option.querySelector('input').checked = option.dataset.sceneId === pendingSceneId; }); }; const syncVersionOptions = () => { overlay.querySelectorAll('.bailian-scene-version-option').forEach(option => { option.classList.toggle('selected', option.dataset.versionId === pendingVersionId); option.querySelector('input').checked = option.dataset.versionId === pendingVersionId; }); }; const syncLanguageOptions = () => { overlay.querySelectorAll('.bailian-scene-language-option').forEach(option => { option.classList.toggle('selected', option.dataset.languageId === pendingLanguageId); option.querySelector('input').checked = option.dataset.languageId === pendingLanguageId; }); }; overlay.querySelectorAll('.bailian-scene-option').forEach(option => { option.addEventListener('click', () => { pendingSceneId = option.dataset.sceneId; syncSceneOptions(); }); }); overlay.querySelectorAll('.bailian-scene-version-option').forEach(option => { option.addEventListener('click', () => { pendingVersionId = option.dataset.versionId; syncVersionOptions(); }); }); overlay.querySelectorAll('.bailian-scene-language-option').forEach(option => { option.addEventListener('click', () => { pendingLanguageId = option.dataset.languageId; syncLanguageOptions(); }); }); renderAgentIcons(overlay); } function openKnowledgeWeightModal(state, rerender) { const draft = (state.knowledgeBases || AGENT_CONFIG_MOCK.understand.knowledgeBases).map(item => ({ ...item })); const modal = createModal({ title: '知识库权重配置', size: 'lg', confirmText: '确定', cancelText: '取消', content: `
调整知识库权重可人工干预召回片段排序。知识库召回片段得分 = 知识库权重 × 切片相似度。
${draft.map((item, index) => `
${escapeHtml(item.name)}
${escapeHtml(item.desc)}
`).join('')}
`, onConfirm: () => { const inputs = Array.from(modal.overlay.querySelectorAll('.bailian-weight-input')); const next = inputs.map((input, index) => { const weight = Number.parseFloat(input.value); if (Number.isNaN(weight) || weight < 0.5 || weight > 2) { Toast.warning('知识库权重需在 0.5 到 2 之间'); input.focus(); return null; } return { ...draft[index], weight: weight.toFixed(2) }; }); if (next.some(item => !item)) return false; state.knowledgeBases = next; Toast.success('知识库权重已更新'); rerender(); return true; }, }); renderAgentIcons(modal.overlay); } function openTtsFilterModal(state, rerender) { const commonPairs = [ { id: 'slash', left: '/*', right: '*/', label: '/* */' }, { id: 'square', left: '[', right: ']', label: '[ ]' }, { id: 'curly', left: '{', right: '}', label: '{ }' }, ]; const draftPairs = (state.ttsFilterPairs || []).map(item => ({ ...item })); const draftSingles = [...(state.ttsFilterSingles || [])]; const modal = createModal({ title: '语音合成播报过滤设置', size: 'xl', confirmText: '确定', cancelText: '取消', content: `
选中符号对内的文字将不会通过 TTS 播报;单字符过滤在所有场景下都不会播报,请谨慎评估业务文本特点。
常用过滤符号对
${commonPairs.map(item => ` `).join('')}
自定义过滤符号对
自定义过滤单字符
`, onConfirm: () => { const pairRows = Array.from(modal.overlay.querySelectorAll('[data-filter-pair-row]')); const singleRows = Array.from(modal.overlay.querySelectorAll('[data-filter-single-row]')); const nextPairs = []; commonPairs.forEach(item => { const enabled = modal.overlay.querySelector(`[data-common-pair="${item.id}"]`)?.checked; if (enabled) nextPairs.push({ enabled: true, left: item.left, right: item.right }); }); pairRows.forEach(row => { const left = row.querySelector('[data-left]')?.value.trim() || ''; const right = row.querySelector('[data-right]')?.value.trim() || ''; if (left || right) nextPairs.push({ enabled: true, left, right }); }); const nextSingles = singleRows.map(row => row.querySelector('[data-single]')?.value.trim() || '').filter(Boolean); state.ttsFilterEnabled = true; state.ttsFilterPairs = nextPairs; state.ttsFilterSingles = nextSingles; Toast.success('播报过滤设置已保存'); rerender(); return true; }, }); const overlay = modal.overlay; const pairList = overlay.querySelector('#filterPairList'); const singleList = overlay.querySelector('#filterSingleList'); function renderPairRows() { pairList.innerHTML = draftPairs.length ? draftPairs.map((pair, index) => `
`).join('') : '
暂无自定义符号对
'; } function renderSingleRows() { singleList.innerHTML = draftSingles.length ? draftSingles.map((char, index) => `
`).join('') : '
暂无自定义单字符
'; } function bindDynamicEvents() { overlay.querySelectorAll('[data-delete-pair]').forEach(btn => { btn.addEventListener('click', () => { draftPairs.splice(Number(btn.dataset.deletePair), 1); renderPairRows(); bindDynamicEvents(); renderAgentIcons(overlay); }); }); overlay.querySelectorAll('[data-delete-single]').forEach(btn => { btn.addEventListener('click', () => { draftSingles.splice(Number(btn.dataset.deleteSingle), 1); renderSingleRows(); bindDynamicEvents(); renderAgentIcons(overlay); }); }); } overlay.querySelector('#addFilterPairBtn')?.addEventListener('click', () => { draftPairs.push({ enabled: true, left: '', right: '' }); renderPairRows(); bindDynamicEvents(); renderAgentIcons(overlay); }); overlay.querySelector('#addFilterSingleBtn')?.addEventListener('click', () => { draftSingles.push(''); renderSingleRows(); bindDynamicEvents(); renderAgentIcons(overlay); }); renderPairRows(); renderSingleRows(); bindDynamicEvents(); renderAgentIcons(overlay); } function openCommandConfigModal(state, rerender) { const baseCommands = AGENT_CONFIG_MOCK.skills.commands; const customCommands = state.customCommands || []; const modal = createModal({ title: '指令配置', size: 'xl', confirmText: '确定', cancelText: '取消', content: `
下发设备控制指令由端侧执行,可在这里查看系统指令、编辑已选指令并添加自定义指令类型。
${[...baseCommands, ...customCommands].map(item => ` `).join('')}
启用 指令名称 指令说明 指令示例 参数名称 回复模式
${escapeHtml(item.name)} ${escapeHtml(item.desc)} ${escapeHtml(item.example || `${item.name} 示例`)} ${escapeHtml(item.params || 'amount')}

设置自定义指令

`, onConfirm: () => { const rows = Array.from(modal.overlay.querySelectorAll('[data-custom-command-row]')); const next = rows.map(row => ({ icon: 'settings-2', name: row.querySelector('[data-name]')?.value.trim() || '', desc: row.querySelector('[data-desc]')?.value.trim() || '', example: row.querySelector('[data-example]')?.value.trim() || '', params: row.querySelector('[data-params]')?.value.trim() || '', count: 1, })).filter(item => item.name); state.customCommands = next; Toast.success('自定义指令已保存'); rerender(); return true; }, }); const overlay = modal.overlay; const list = overlay.querySelector('#customCommandList'); const draft = (state.customCommands || []).map(item => ({ ...item })); function renderRows() { list.innerHTML = draft.length ? draft.map((item, index) => `
`).join('') : '
暂无自定义指令
'; } function bindCommandEvents() { overlay.querySelectorAll('[data-delete-command]').forEach(btn => { btn.addEventListener('click', () => { draft.splice(Number(btn.dataset.deleteCommand), 1); renderRows(); bindCommandEvents(); renderAgentIcons(overlay); }); }); } overlay.querySelector('#addCustomCommandBtn')?.addEventListener('click', () => { draft.push({ name: '', desc: '', example: '', params: '' }); renderRows(); bindCommandEvents(); renderAgentIcons(overlay); }); renderRows(); bindCommandEvents(); renderAgentIcons(overlay); } function openPluginConfigModal(state, rerender) { const basePlugins = AGENT_CONFIG_MOCK.skills.plugins; const customPlugins = (state.customPlugins || []).map(item => ({ ...item })); const modal = createModal({ title: '插件配置', size: 'xl', confirmText: '确定', cancelText: '取消', content: `
插件默认由系统执行,也可以在这里单独增删自定义插件并控制是否启用。

系统插件

${basePlugins.length} 项
${basePlugins.map(item => ` `).join('')}

自定义插件

`, onConfirm: () => { const rows = Array.from(modal.overlay.querySelectorAll('[data-custom-plugin-row]')); state.customPlugins = rows.map(row => ({ icon: row.querySelector('[data-icon]')?.value.trim() || 'plug', name: row.querySelector('[data-name]')?.value.trim() || '', desc: row.querySelector('[data-desc]')?.value.trim() || '', status: row.querySelector('[data-status]')?.value.trim() || '系统执行', })).filter(item => item.name); Toast.success('插件配置已保存'); rerender(); return true; }, }); renderAgentIcons(modal.overlay); const overlay = modal.overlay; const list = overlay.querySelector('#customPluginList'); const draft = customPlugins; function renderRows() { list.innerHTML = draft.length ? draft.map((item, index) => `
`).join('') : '
暂无自定义插件
'; } function bindRows() { overlay.querySelectorAll('[data-delete-plugin]').forEach(btn => { btn.addEventListener('click', () => { draft.splice(Number(btn.dataset.deletePlugin), 1); renderRows(); bindRows(); renderAgentIcons(overlay); }); }); } overlay.querySelector('#addCustomPluginBtn')?.addEventListener('click', () => { draft.push({ icon: 'plug', name: '', desc: '', status: '系统执行' }); renderRows(); bindRows(); renderAgentIcons(overlay); }); renderRows(); bindRows(); renderAgentIcons(overlay); } function openRenameModal(state, rerender) { if (typeof createModal !== 'function') { const name = prompt('请输入应用名称', state.app.name); if (name && name.trim()) { state.app.name = name.trim(); rerender(); } return; } createModal({ title: '编辑应用名称', content: `
`, confirmText: '保存', cancelText: '取消', onConfirm: () => { const input = document.getElementById('renameAgentInput'); const nextName = input?.value.trim(); if (!nextName) { Toast.warning('请输入应用名称'); return false; } state.app.name = nextName; Toast.success('应用名称已更新'); rerender(); return true; }, }); setTimeout(() => document.getElementById('renameAgentInput')?.focus(), 50); } function getAgentStatus(status) { const map = { published: { label: '已发布', className: 'published' }, editing: { label: '编辑中', className: 'editing' }, draft: { label: '草稿', className: 'draft' }, }; return map[status] || map.draft; } function getSelectedScene(state) { return AGENT_SCENE_TEMPLATES.find(scene => scene.id === state.selectedSceneId) || AGENT_SCENE_TEMPLATES.find(scene => scene.id === 'toy') || AGENT_SCENE_TEMPLATES[0]; } function getSelectedSceneVersion(state) { return AGENT_SCENE_VERSIONS.find(version => version.id === state.selectedSceneVersion) || AGENT_SCENE_VERSIONS[0]; } function getSelectedSceneLanguage(state) { return AGENT_SCENE_LANGUAGES.find(language => language.id === state.selectedSceneLanguage) || AGENT_SCENE_LANGUAGES[0]; } function escapeHtml(value) { const div = document.createElement('div'); div.textContent = String(value ?? ''); return div.innerHTML; } function escapeAttr(value) { return escapeHtml(value).replace(/"/g, '"').replace(/'/g, '''); } function renderAgentIcons(root) { if (!root || !window.lucide?.createIcons) return; try { lucide.createIcons({ elements: [root] }); } catch (error) { console.warn('Agent config icon render failed:', error); } }