From 1d3afb3c9a655ab7c69f4791ade5c4843b2bde77 Mon Sep 17 00:00:00 2001 From: zhuoqinglian <1035449612@qq.com> Date: Thu, 2 Apr 2026 09:45:33 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=E8=A7=92=E8=89=B2=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E8=A1=A8=E5=8D=95=E6=A0=87=E9=A2=98=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?Tooltip=E9=BC=A0=E6=A0=87=E6=82=AC=E6=B5=AE=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/manager-web/src/i18n/de.js | 22 +++- main/manager-web/src/i18n/en.js | 22 +++- main/manager-web/src/i18n/pt_BR.js | 22 +++- main/manager-web/src/i18n/vi.js | 22 +++- main/manager-web/src/i18n/zh_CN.js | 22 +++- main/manager-web/src/i18n/zh_TW.js | 22 +++- main/manager-web/src/views/roleConfig.vue | 145 +++++++++++++++++++--- 7 files changed, 256 insertions(+), 21 deletions(-) diff --git a/main/manager-web/src/i18n/de.js b/main/manager-web/src/i18n/de.js index 6d91d472..d19fbbd2 100644 --- a/main/manager-web/src/i18n/de.js +++ b/main/manager-web/src/i18n/de.js @@ -757,7 +757,8 @@ export default { 'roleConfig.interactionLanguage': 'Interaktionssprache', 'roleConfig.vad': 'VAD', 'roleConfig.asr': 'ASR', - 'roleConfig.llm': 'LLM', + 'roleConfig.llm': 'Hauptsprachmodell (LLM)', + 'roleConfig.slm': 'Kleines Sprachmodell (SLM)', 'roleConfig.vllm': 'VLLM', 'roleConfig.tts': 'TTS', 'roleConfig.memoryHis': 'Speicher', @@ -802,6 +803,25 @@ export default { 'roleConfig.cannotPlayAudio': 'Audio kann nicht abgespielt werden', 'roleConfig.audioPlayError': 'Fehler bei der Audio-Wiedergabe', + // Tooltip-Beschreibungen für Formularfelder + 'roleConfig.tooltip.agentName': 'Legen Sie den Namen Ihres KI-Agenten fest, um ihn zu identifizieren und wiederzuerkennen', + 'roleConfig.tooltip.roleTemplate': 'Wählen Sie aus voreingestellten Rollenvorlagen, um die Grundeinstellungen Ihres Agenten schnell zu konfigurieren', + 'roleConfig.tooltip.contextProvider': 'Wenn XiaoZhi aktiviert wird, werden externe Systemdaten abgerufen und dynamisch in die Systemaufforderung des LLM eingefügt', + 'roleConfig.tooltip.roleIntroduction': 'Definieren Sie die Rollenpositionierung, Persönlichkeitsmerkmale, Verhaltensnormen und beruflichen Wissenshintergründe des KI-Agenten', + 'roleConfig.tooltip.memoryHis': 'Zusammenfassung des Chatverlaufs', + 'roleConfig.tooltip.languageCode': 'Legen Sie Sprachcodes fest, z.B. de-DE, en-US usw., die für die Erkennung bestimmter Funktionen verwendet werden', + 'roleConfig.tooltip.interactionLanguage': 'Legen Sie die Interaktionssprache fest und geben Sie die primäre Sprache an, die der KI-Agent für die Kommunikation verwendet', + 'roleConfig.tooltip.vad': 'Sprachaktivitätserkennung: Erkennt, wann der Benutzer zu sprechen beginnt oder aufhört, wird verwendet, um Gesprächsbeginn und -ende zu bestimmen und ermöglicht die Unterbrechungsfunktion', + 'roleConfig.tooltip.asr': 'Automatische Spracherkennung: Wandelt Benutzersprache in Text um, der erste Schritt im Mensch-Computer-Dialog, unterstützt mehrsprachige Erkennung', + 'roleConfig.tooltip.llm': 'Hauptsprachmodell (Large Language Model): Das "Gehirn" des KI-Agenten, verantwortlich für das Verstehen von Benutzerabsichten, das Generieren von Antworten und das Ausführen verschiedener Aufgaben', + 'roleConfig.tooltip.slm': 'Kleines Parameter-Modell (Small Language Model): Wird für die KI-Agenten-Aktivierung verwendet, erstellt Zusammenfassungstitel für Erinnerungen', + 'roleConfig.tooltip.vllm': 'Visuelles großes Sprachmodell: Verarbeitet Bild- und Videoverständnis, ermöglicht dem KI-Agenten das Analysieren und Beschreiben von kameragefangenen Inhalten', + 'roleConfig.tooltip.intent': 'Absichtserkennung: Analysiert Benutzersprache oder -text, um die wahre Absicht des Benutzers zu bestimmen, wie z.B. Anfragen, Chat, Gerätesteuerung usw.', + 'roleConfig.tooltip.memory': 'Gedächtnismodell: Verwaltet Speicherung und Zusammenfassung des Gesprächsverlaufs, bestimmt, ob sich der KI-Agent an frühere Gespräche erinnern kann für Langzeitgedächtnis', + 'roleConfig.tooltip.tts': 'Text-zu-Sprache: Konvertiert Text in natürliche Sprache, bestimmt Stimme, Geschwindigkeit und Tonfall des KI-Agenten', + 'roleConfig.tooltip.language': 'Wählen Sie die Sprache der Stimme; das System filtert verfügbare Stimmen, die diese Sprache unterstützen', + 'roleConfig.tooltip.voiceType': 'Wählen Sie die Stimme für den KI-Agenten; verschiedene Stimmen haben unterschiedliche Eigenschaften und Stile. Einige Stimmen unterstützen die Vorschaufunktion, klicken Sie auf die Wiedergabeschaltfläche für eine Vorschau', + // Function management dialog text 'functionDialog.title': 'Funktionsverwaltung', 'functionDialog.unselectedFunctions': 'Nicht ausgewählte Funktionen', diff --git a/main/manager-web/src/i18n/en.js b/main/manager-web/src/i18n/en.js index 6ccf9ddb..9697629c 100644 --- a/main/manager-web/src/i18n/en.js +++ b/main/manager-web/src/i18n/en.js @@ -757,7 +757,8 @@ export default { 'roleConfig.interactionLanguage': 'Interaction Language', 'roleConfig.vad': 'Voice Detect', 'roleConfig.asr': 'Speech Recognition', - 'roleConfig.llm': 'Language Model', + 'roleConfig.llm': 'Main Language Model', + 'roleConfig.slm': 'Small Language Model', 'roleConfig.vllm': 'Vision Model', 'roleConfig.tts': 'Text-to-Speech', 'roleConfig.memoryHis': 'Memory', @@ -802,6 +803,25 @@ export default { 'roleConfig.cannotPlayAudio': 'Cannot play audio', 'roleConfig.audioPlayError': 'Error occurred during audio playback', + // Form field Tooltip descriptions + 'roleConfig.tooltip.agentName': 'Set the name of your AI agent for identification and recognition', + 'roleConfig.tooltip.roleTemplate': 'Choose from preset role templates to quickly configure your agent\'s basic settings', + 'roleConfig.tooltip.contextProvider': 'When XiaoZhi is awakened, fetch external system data and dynamically inject it into the LLM system prompt', + 'roleConfig.tooltip.roleIntroduction': 'Define the AI agent\'s role positioning, personality traits, behavioral norms, and professional knowledge background', + 'roleConfig.tooltip.memoryHis': 'Summarize chat record content', + 'roleConfig.tooltip.languageCode': 'Set language code such as zh-CN, en-US, etc., used for specific feature recognition', + 'roleConfig.tooltip.interactionLanguage': 'Set the interaction language, specifying the primary language the AI agent uses for communication', + 'roleConfig.tooltip.vad': 'Voice Activity Detection: Detects when the user starts or stops speaking, used to determine conversation start and end, enabling interrupt functionality', + 'roleConfig.tooltip.asr': 'Automatic Speech Recognition: Converts user speech to text, the first step in human-computer dialogue, supporting multilingual recognition', + 'roleConfig.tooltip.llm': 'Main Language Model (Large Language Model): The "brain" of the AI agent, responsible for understanding user intent, generating responses, and executing various tasks', + 'roleConfig.tooltip.slm': 'Small Parameter Model (Small Language Model): Used for AI agent wake-up, generating memory summary titles', + 'roleConfig.tooltip.vllm': 'Visual Large Language Model: Processes image and video understanding, enabling the AI agent to analyze and describe camera-captured content', + 'roleConfig.tooltip.intent': 'Intent Detection: Analyzes user speech or text to determine the user\'s true intent, such as queries, chat, device control, etc.', + 'roleConfig.tooltip.memory': 'Memory Model: Manages conversation history storage and summarization, determining whether the AI can remember previous conversations for long-term memory', + 'roleConfig.tooltip.tts': 'Text-to-Speech: Converts text to natural speech, determining the AI\'s voice, speed, and tone', + 'roleConfig.tooltip.language': 'Select the language of the voice; the system will filter available voices that support that language', + 'roleConfig.tooltip.voiceType': 'Choose the voice for the AI agent; different voices have different characteristics and styles. Some voices support preview functionality, click the play button to preview', + // Function management dialog text 'functionDialog.title': 'Function Management', 'functionDialog.unselectedFunctions': 'Unselected Functions', diff --git a/main/manager-web/src/i18n/pt_BR.js b/main/manager-web/src/i18n/pt_BR.js index 5e519203..767e10a6 100644 --- a/main/manager-web/src/i18n/pt_BR.js +++ b/main/manager-web/src/i18n/pt_BR.js @@ -757,7 +757,8 @@ export default { 'roleConfig.interactionLanguage': 'Idioma de Interação', 'roleConfig.vad': 'Detecção de Voz', 'roleConfig.asr': 'Reconhecimento de Fala', - 'roleConfig.llm': 'Modelo de Linguagem', + 'roleConfig.llm': 'Modelo de Linguagem Principal', + 'roleConfig.slm': 'Modelo de Linguagem Pequeno', 'roleConfig.vllm': 'Modelo de Visão', 'roleConfig.intent': 'Reconhecimento de Intenção', 'roleConfig.memoryHis': 'Memória', @@ -802,6 +803,25 @@ export default { 'roleConfig.cannotPlayAudio': 'Não é possível reproduzir áudio', 'roleConfig.audioPlayError': 'Ocorreu um erro durante a reprodução de áudio', + // Descrições de Tooltip para campos de formulário + 'roleConfig.tooltip.agentName': 'Defina o nome do seu agente de IA para identificação e reconhecimento', + 'roleConfig.tooltip.roleTemplate': 'Escolha entre modelos de função predefinidos para configurar rapidamente as configurações básicas do seu agente', + 'roleConfig.tooltip.contextProvider': 'Quando o XiaoZhi é ativado, busca dados de sistemas externos e os injeta dinamicamente no prompt do sistema do LLM', + 'roleConfig.tooltip.roleIntroduction': 'Defina o posicionamento do papel, traços de personalidade, normas comportamentais e histórico de conhecimento profissional do agente de IA', + 'roleConfig.tooltip.memoryHis': 'Resumir conteúdo do registro de chat', + 'roleConfig.tooltip.languageCode': 'Defina o código de idioma como pt-BR, en-US, etc., usado para reconhecimento de recursos específicos', + 'roleConfig.tooltip.interactionLanguage': 'Defina o idioma de interação, especificando o idioma principal que o agente de IA usa para comunicação', + 'roleConfig.tooltip.vad': 'Detecção de Atividade de Voz: Detecta quando o usuário começa ou para de falar, usado para determinar o início e fim da conversa, permitindo funcionalidade de interrupção', + 'roleConfig.tooltip.asr': 'Reconhecimento Automático de Fala: Converte fala do usuário em texto, o primeiro passo no diálogo humano-computador, suporta reconhecimento multilíngue', + 'roleConfig.tooltip.llm': 'Modelo de Linguagem Principal (Large Language Model): O "cérebro" do agente de IA, responsável por entender a intenção do usuário, gerar respostas e executar várias tarefas', + 'roleConfig.tooltip.slm': 'Modelo de Parâmetros Pequeno (Small Language Model): Usado para ativação do agente de IA, gerando títulos de resumo de memória', + 'roleConfig.tooltip.vllm': 'Modelo de Linguagem Visual Grande: Processa compreensão de imagem e vídeo, permitindo que o agente de IA analise e descreva o conteúdo capturado pela câmera', + 'roleConfig.tooltip.intent': 'Detecção de Intenção: Analisa fala ou texto do usuário para determinar a verdadeira intenção do usuário, como consultas, bate-papo, controle de dispositivos, etc.', + 'roleConfig.tooltip.memory': 'Modelo de Memória: Gerencia armazenamento e resumo do histórico de conversas, determina se a IA pode lembrar conversas anteriores para memória de longo prazo', + 'roleConfig.tooltip.tts': 'Texto para Fala: Converte texto em fala natural, determinando a voz, velocidade e tom da IA', + 'roleConfig.tooltip.language': 'Selecione o idioma da voz; o sistema filtrará vozes disponíveis que suportam esse idioma', + 'roleConfig.tooltip.voiceType': 'Escolha a voz para o agente de IA; diferentes vozes têm diferentes características e estilos. Algumas vozes suportam funcionalidade de visualização, clique no botão de reprodução para visualizar', + // Diálogo de gerenciamento de funções 'functionDialog.title': 'Gerenciamento de Funções', 'functionDialog.unselectedFunctions': 'Funções Não Selecionadas', diff --git a/main/manager-web/src/i18n/vi.js b/main/manager-web/src/i18n/vi.js index d04a0f0b..544317b7 100644 --- a/main/manager-web/src/i18n/vi.js +++ b/main/manager-web/src/i18n/vi.js @@ -757,7 +757,8 @@ export default { 'roleConfig.interactionLanguage': 'Ngôn ngữ tương tác', 'roleConfig.vad': 'Phát hiện giọng nói', 'roleConfig.asr': 'Nhận dạng giọng nói', - 'roleConfig.llm': 'Mô hình ngôn ngữ', + 'roleConfig.llm': 'Mô hình ngôn ngữ chính', + 'roleConfig.slm': 'Mô hình ngôn ngữ nhỏ', 'roleConfig.vllm': 'Mô hình thị giác', 'roleConfig.tts': 'Văn bản thành giọng nói', 'roleConfig.memoryHis': 'Bộ nhớ', @@ -802,6 +803,25 @@ export default { 'roleConfig.cannotPlayAudio': 'Không thể phát âm thanh', 'roleConfig.audioPlayError': 'Lỗi trong quá trình phát âm thanh', + // Mô tả Tooltip cho trường biểu mẫu + 'roleConfig.tooltip.agentName': 'Đặt tên cho tác nhân AI của bạn để nhận dạng và nhận biết', + 'roleConfig.tooltip.roleTemplate': 'Chọn từ các mẫu vai trò được đặt trước để nhanh chóng định cấu hình cài đặt cơ bản cho tác nhân của bạn', + 'roleConfig.tooltip.contextProvider': 'Khi XiaoZhi được kích hoạt, lấy dữ liệu từ hệ thống bên ngoài và tiêm động vào lời nhắc hệ thống của LLM', + 'roleConfig.tooltip.roleIntroduction': 'Xác định vị trí vai trò, đặc điểm tính cách, quy tắc hành vi và nền tảng kiến thức chuyên môn của tác nhân AI', + 'roleConfig.tooltip.memoryHis': 'Tóm tắt nội dung bản ghi trò chuyện', + 'roleConfig.tooltip.languageCode': 'Đặt mã ngôn ngữ như vi-VN, en-US, v.v., được sử dụng để nhận dạng các tính năng cụ thể', + 'roleConfig.tooltip.interactionLanguage': 'Đặt ngôn ngữ tương tác, chỉ định ngôn ngữ chính mà tác nhân AI sử dụng để giao tiếp', + 'roleConfig.tooltip.vad': 'Phát hiện Hoạt động Giọng nói: Phát hiện khi người dùng bắt đầu hoặc ngừng nói, được sử dụng để xác định thời điểm bắt đầu và kết thúc cuộc trò chuyện, cho phép chức năng ngắt', + 'roleConfig.tooltip.asr': 'Nhận dạng Giọng nói Tự động: Chuyển đổi giọng nói của người dùng thành văn bản, bước đầu tiên trong hội thoại người-máy, hỗ trợ nhận dạng đa ngôn ngữ', + 'roleConfig.tooltip.llm': 'Mô hình ngôn ngữ chính (Large Language Model): "Bộ não" của tác nhân AI, chịu trách nhiệm hiểu ý định của người dùng, tạo phản hồi và thực hiện các nhiệm vụ khác nhau', + 'roleConfig.tooltip.slm': 'Mô hình tham số nhỏ (Small Language Model): Được sử dụng để kích hoạt tác nhân AI, tạo tiêu đề tóm tắt ký ức', + 'roleConfig.tooltip.vllm': 'Mô hình Ngôn ngữ Lớn Thị giác: Xử lý hiểu hình ảnh và video, cho phép tác nhân AI phân tích và mô tả nội dung được camera ghi lại', + 'roleConfig.tooltip.intent': 'Phát hiện Ý định: Phân tích giọng nói hoặc văn bản của người dùng để xác định ý định thực sự của người dùng như truy vấn, trò chuyện, điều khiển thiết bị, v.v.', + 'roleConfig.tooltip.memory': 'Mô hình Bộ nhớ: Quản lý lưu trữ và tóm tắt lịch sử cuộc trò chuyện, quyết định liệu AI có thể nhớ các cuộc trò chuyện trước đó để có trí nhớ dài hạn hay không', + 'roleConfig.tooltip.tts': 'Chuyển văn bản thành giọng nói: Chuyển đổi văn bản thành giọng nói tự nhiên, xác định giọng nói, tốc độ và ngữ điệu của AI', + 'roleConfig.tooltip.language': 'Chọn ngôn ngữ của giọng nói; hệ thống sẽ lọc các giọng nói khả dụng hỗ trợ ngôn ngữ đó', + 'roleConfig.tooltip.voiceType': 'Chọn giọng nói cho tác nhân AI; các giọng nói khác nhau có đặc điểm và phong cách khác nhau. Một số giọng nói hỗ trợ chức năng xem trước, nhấp vào nút phát để xem trước', + // Function management dialog text 'functionDialog.title': 'Quản lý chức năng', 'functionDialog.unselectedFunctions': 'Chức năng chưa chọn', diff --git a/main/manager-web/src/i18n/zh_CN.js b/main/manager-web/src/i18n/zh_CN.js index d25e80d5..d205fb3c 100644 --- a/main/manager-web/src/i18n/zh_CN.js +++ b/main/manager-web/src/i18n/zh_CN.js @@ -757,7 +757,8 @@ export default { 'roleConfig.interactionLanguage': '交互语种', 'roleConfig.vad': '语音活动检测(VAD)', 'roleConfig.asr': '语音识别(ASR)', - 'roleConfig.llm': '大语言模型(LLM)', + 'roleConfig.llm': '主语言模型(LLM)', + "roleConfig.slm": "小参数模型(SLM)", 'roleConfig.vllm': '视觉大模型(VLLM)', 'roleConfig.intent': '意图识别(Intent)', 'roleConfig.memoryHis': '记忆', @@ -802,6 +803,25 @@ export default { 'roleConfig.cannotPlayAudio': '无法播放音频', 'roleConfig.audioPlayError': '播放音频过程出错', + // 表单字段 Tooltip 提示说明 + 'roleConfig.tooltip.agentName': '设置智能体的名称,用于标识和识别您的AI助手', + 'roleConfig.tooltip.roleTemplate': '从预设的角色模板中选择,快速配置智能体的基础设定', + 'roleConfig.tooltip.contextProvider': '在小智被唤醒时,获取外部系统的数据,并将其动态注入到大模型的系统提示词中', + 'roleConfig.tooltip.roleIntroduction': '定义AI助手的角色定位、人格特征、行为规范和专业知识背景', + 'roleConfig.tooltip.memoryHis': '总结聊天记录内容', + 'roleConfig.tooltip.languageCode': '设置语言代码,如zh-CN、en-US等,用于特定功能识别', + 'roleConfig.tooltip.interactionLanguage': '设置交互语言,指定AI助手使用的主要语言进行交流', + 'roleConfig.tooltip.vad': '语音活动检测(Voice Activity Detection):检测用户何时开始或结束说话,用于判断对话的开始和结束,实现打断功能', + 'roleConfig.tooltip.asr': '自动语音识别(Automatic Speech Recognition):将用户的语音转换为文字,是人机对话的第一步,支持多语言识别', + 'roleConfig.tooltip.llm': '主语言模型(Large Language Model):AI助手的"大脑",负责理解用户意图、生成回答和执行各种任务', + "roleConfig.tooltip.slm": '小参数模型(Small Language Model):用于智能体唤醒,生成记忆总结标题', + 'roleConfig.tooltip.vllm': '视觉大语言模型(Visual LLM):处理图像和视频理解,使AI助手能够分析和描述摄像头捕获的画面内容', + 'roleConfig.tooltip.intent': '意图识别(Intent Detection):分析用户语音或文本,判断用户的真实意图,如查询、聊天、控制设备等', + 'roleConfig.tooltip.memory': '记忆模型(Memory Model):管理对话历史的存储和摘要,决定AI能否记住之前的对话内容,实现长期记忆功能', + 'roleConfig.tooltip.tts': '语音合成(Text-to-Speech):将文字转换为自然语音,决定AI说话的声音、语速和语调', + 'roleConfig.tooltip.language': '选择音色所属的语言,系统将筛选出支持该语言的可用音色', + 'roleConfig.tooltip.voiceType': '选择AI助手说话的声音,不同音色具有不同的声音特点和风格,部分音色支持试听功能,点击播放按钮可预览效果', + // 功能管理对话框文本 'functionDialog.title': '功能管理', 'functionDialog.unselectedFunctions': '未选功能', diff --git a/main/manager-web/src/i18n/zh_TW.js b/main/manager-web/src/i18n/zh_TW.js index a75a4755..e2068d3b 100644 --- a/main/manager-web/src/i18n/zh_TW.js +++ b/main/manager-web/src/i18n/zh_TW.js @@ -757,7 +757,8 @@ export default { 'roleConfig.interactionLanguage': '交互語種', 'roleConfig.vad': '語音活動檢測(VAD)', 'roleConfig.asr': '語音識別(ASR)', - 'roleConfig.llm': '大語言模型(LLM)', + 'roleConfig.llm': '主語言模型(LLM)', + 'roleConfig.slm': '小參數模型(SLM)', 'roleConfig.vllm': '視覺大模型(VLLM)', 'roleConfig.tts': '語音合成(TTS)', 'roleConfig.memoryHis': '記憶', @@ -802,6 +803,25 @@ export default { 'roleConfig.cannotPlayAudio': '無法播放音訊', 'roleConfig.audioPlayError': '播放音訊過程出錯', + // 表單欄位 Tooltip 提示說明 + 'roleConfig.tooltip.agentName': '設定智慧體的名稱,用於標識和識別您的AI助手', + 'roleConfig.tooltip.roleTemplate': '從預設的角色模板中選擇,快速配置智慧體的基礎設定', + 'roleConfig.tooltip.contextProvider': '在小智被喚醒時,獲取外部系統的資料,並將其動態注入到大模型的系統提示詞中', + 'roleConfig.tooltip.roleIntroduction': '定義AI助手的角色定位、人格特徵、行為規範和專業知識背景', + 'roleConfig.tooltip.memoryHis': '總結聊天記錄內容', + 'roleConfig.tooltip.languageCode': '設定語言代碼,如zh-TW、en-US等,用於特定功能識別', + 'roleConfig.tooltip.interactionLanguage': '設定互動語言,指定AI助手使用的主要語言進行交流', + 'roleConfig.tooltip.vad': '語音活動檢測(Voice Activity Detection):檢測用戶何時開始或結束說話,用於判斷對話的開始和結束,實現打斷功能', + 'roleConfig.tooltip.asr': '自動語音識別(Automatic Speech Recognition):將用戶的語音轉換為文字,是人機對話的第一步,支持多語言識別', + 'roleConfig.tooltip.llm': '主語言模型(Large Language Model):AI助手的"大腦",負責理解用戶意圖、生成回答和執行各種任務', + 'roleConfig.tooltip.slm': '小參數模型(Small Language Model):用於智慧體喚醒,生成記憶總結標題', + 'roleConfig.tooltip.vllm': '視覺大型語言模型(Visual LLM):處理圖像和視頻理解,使AI助手能夠分析和描述攝像頭擷取的畫面內容', + 'roleConfig.tooltip.intent': '意圖識別(Intent Detection):分析用戶語音或文字,判斷用戶的真實意圖,如查詢、聊天、控制設備等', + 'roleConfig.tooltip.memory': '記憶模型(Memory Model):管理對話歷史的存儲和摘要,決定AI能否記住之前的對話內容,實現長期記憶功能', + 'roleConfig.tooltip.tts': '語音合成(Text-to-Speech):將文字轉換為自然語音,決定AI說話的聲音、語速和語調', + 'roleConfig.tooltip.language': '選擇音色所屬的語言,系統將篩選出支援該語言的可用音色', + 'roleConfig.tooltip.voiceType': '選擇AI助手說話的聲音,不同音色具有不同的聲音特點和風格,部分音色支援預覽功能,點擊播放按鈕可預覽效果', + // 功能管理對話框文本 'functionDialog.title': '功能管理', 'functionDialog.unselectedFunctions': '未選功能', diff --git a/main/manager-web/src/views/roleConfig.vue b/main/manager-web/src/views/roleConfig.vue index 467022ab..364efa7a 100644 --- a/main/manager-web/src/views/roleConfig.vue +++ b/main/manager-web/src/views/roleConfig.vue @@ -60,14 +60,24 @@
- + + - + +
- + +
{{ $t('roleConfig.contextProviderSuccess', { count: currentContextProviders.length }) }}{{ $t('roleConfig.contextProviderDocLink') }} @@ -94,7 +109,12 @@
- + + - + + + + +
+
+
+ + +
+ + + +
+
+ + +
+ + + +
+
+
+
功能名称: {{ func.name }}
@@ -259,7 +351,12 @@
- + +
- + +
{ @@ -1744,4 +1851,12 @@ export default { width: 90px !important; } } + + + + From 7dcc014ce469182ef2c808758f4f7cd480b886ac Mon Sep 17 00:00:00 2001 From: zhuoqinglian <1035449612@qq.com> Date: Wed, 8 Apr 2026 11:41:12 +0800 Subject: [PATCH 2/6] =?UTF-8?q?update:=20=E8=B0=83=E6=95=B4=E5=B0=8F?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=A8=A1=E5=9E=8B=E8=A1=A8=E5=8D=95=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../manager-web/src/components/ChatHistoryDialog.vue | 4 ++-- main/manager-web/src/views/roleConfig.vue | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/main/manager-web/src/components/ChatHistoryDialog.vue b/main/manager-web/src/components/ChatHistoryDialog.vue index 7821ba2d..3dc3126f 100644 --- a/main/manager-web/src/components/ChatHistoryDialog.vue +++ b/main/manager-web/src/components/ChatHistoryDialog.vue @@ -8,7 +8,7 @@ :class="{ active: currentSessionId === session.sessionId }" @click="selectSession(session)">
-
{{ formatTime(session.createdAt) }}
+
{{ session.title || formatTime(session.createdAt) }}
{{ session.chatCount > 99 ? '99' : session.chatCount }}
@@ -139,7 +139,7 @@ export default { if (this.messages[0]) { result.push({ type: 'time', - content: this.formatTime(this.messages[0].createdAt), + content: this.formatTime(this.messages[this.messages.length - 1].createdAt), id: `time-${Date.now()}-${Math.random().toString(36).substr(2, 9)}` }); } diff --git a/main/manager-web/src/views/roleConfig.vue b/main/manager-web/src/views/roleConfig.vue index 364efa7a..34224e5a 100644 --- a/main/manager-web/src/views/roleConfig.vue +++ b/main/manager-web/src/views/roleConfig.vue @@ -268,10 +268,9 @@ filterable :placeholder="$t('roleConfig.pleaseSelect')" class="form-select" - @change="handleModelChange('SLM', $event)" > { From 64630e1b4e80ac8d61b8f06a95b555e5ccc1373c Mon Sep 17 00:00:00 2001 From: zhuoqinglian <1035449612@qq.com> Date: Wed, 8 Apr 2026 16:26:48 +0800 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20mobile=20=E8=A7=92=E8=89=B2?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=B7=BB=E5=8A=A0=E5=B0=8F=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/manager-mobile/src/api/agent/types.ts | 1 + .../src/api/chat-history/types.ts | 1 + main/manager-mobile/src/i18n/de.ts | 3 +- main/manager-mobile/src/i18n/en.ts | 3 +- main/manager-mobile/src/i18n/pt_BR.ts | 3 +- main/manager-mobile/src/i18n/vi.ts | 3 +- main/manager-mobile/src/i18n/zh_CN.ts | 3 +- main/manager-mobile/src/i18n/zh_TW.ts | 3 +- main/manager-mobile/src/pages/agent/edit.vue | 28 ++++++++++++++++++- 9 files changed, 41 insertions(+), 7 deletions(-) diff --git a/main/manager-mobile/src/api/agent/types.ts b/main/manager-mobile/src/api/agent/types.ts index 2c61c80f..f04ef3d1 100644 --- a/main/manager-mobile/src/api/agent/types.ts +++ b/main/manager-mobile/src/api/agent/types.ts @@ -28,6 +28,7 @@ export interface AgentDetail { asrModelId: string vadModelId: string llmModelId: string + slmModelId: string vllmModelId: string ttsModelId: string ttsVoiceId: string diff --git a/main/manager-mobile/src/api/chat-history/types.ts b/main/manager-mobile/src/api/chat-history/types.ts index b895ed47..32bcf8d7 100644 --- a/main/manager-mobile/src/api/chat-history/types.ts +++ b/main/manager-mobile/src/api/chat-history/types.ts @@ -3,6 +3,7 @@ export interface ChatSession { sessionId: string createdAt: string chatCount: number + title: string } // 聊天会话列表响应 diff --git a/main/manager-mobile/src/i18n/de.ts b/main/manager-mobile/src/i18n/de.ts index d76a4093..d2b24453 100644 --- a/main/manager-mobile/src/i18n/de.ts +++ b/main/manager-mobile/src/i18n/de.ts @@ -108,7 +108,8 @@ export default { 'agent.modelConfig': 'Modellkonfiguration', 'agent.vad': 'Sprachaktivitätserkennung', 'agent.asr': 'Spracherkennung', - 'agent.llm': 'Großes Sprachmodell', + 'agent.llm': 'Hauptsprachenmodell', + 'agent.slm': 'Kleine Parametermodelle', 'agent.vllm': 'Vision-Sprachmodell', 'agent.intent': 'Absichtserkennung', 'agent.memory': 'Speicher', diff --git a/main/manager-mobile/src/i18n/en.ts b/main/manager-mobile/src/i18n/en.ts index 0066b0fd..2a39b551 100644 --- a/main/manager-mobile/src/i18n/en.ts +++ b/main/manager-mobile/src/i18n/en.ts @@ -108,7 +108,8 @@ export default { 'agent.modelConfig': 'Model Configuration', 'agent.vad': 'Voice Activity Detection', 'agent.asr': 'Speech Recognition', - 'agent.llm': 'Large Language Model', + 'agent.llm': 'Main language model', + 'agent.slm': 'Small parameter model', 'agent.vllm': 'Vision Language Model', 'agent.intent': 'Intent Recognition', 'agent.memory': 'Memory', diff --git a/main/manager-mobile/src/i18n/pt_BR.ts b/main/manager-mobile/src/i18n/pt_BR.ts index 416b1f8f..79b6b700 100644 --- a/main/manager-mobile/src/i18n/pt_BR.ts +++ b/main/manager-mobile/src/i18n/pt_BR.ts @@ -108,7 +108,8 @@ export default { 'agent.modelConfig': 'Configuração do Modelo', 'agent.vad': 'Detecção de Atividade de Voz', 'agent.asr': 'Reconhecimento de Fala', - 'agent.llm': 'Modelo de Linguagem Grande', + 'agent.llm': 'Modelo de Linguagem Principal', + 'agent.slm': 'Modelo de pequenos parâmetros', 'agent.vllm': 'Modelo de Linguagem Visual', 'agent.intent': 'Reconhecimento de Intenção', 'agent.memory': 'Memória', diff --git a/main/manager-mobile/src/i18n/vi.ts b/main/manager-mobile/src/i18n/vi.ts index 8ee1a35e..15830036 100644 --- a/main/manager-mobile/src/i18n/vi.ts +++ b/main/manager-mobile/src/i18n/vi.ts @@ -108,7 +108,8 @@ export default { 'agent.modelConfig': 'Cấu hình mô hình', 'agent.vad': 'Phát hiện hoạt động giọng nói', 'agent.asr': 'Nhận dạng giọng nói', - 'agent.llm': 'Mô hình ngôn ngữ lớn', + 'agent.llm': 'Mô hình ngôn ngữ chính', + 'agent.slm': 'Mô hình tham số nhỏ', 'agent.vllm': 'Mô hình ngôn ngữ thị giác', 'agent.intent': 'Nhận dạng ý định', 'agent.memory': 'Bộ nhớ', diff --git a/main/manager-mobile/src/i18n/zh_CN.ts b/main/manager-mobile/src/i18n/zh_CN.ts index a5dec898..16366b20 100644 --- a/main/manager-mobile/src/i18n/zh_CN.ts +++ b/main/manager-mobile/src/i18n/zh_CN.ts @@ -108,7 +108,8 @@ export default { 'agent.modelConfig': '模型配置', 'agent.vad': '语音活动检测', 'agent.asr': '语音识别', - 'agent.llm': '大语言模型', + 'agent.llm': '主语言模型', + 'agent.slm': '小参数模型', 'agent.vllm': '视觉大模型', 'agent.intent': '意图识别', 'agent.memory': '记忆', diff --git a/main/manager-mobile/src/i18n/zh_TW.ts b/main/manager-mobile/src/i18n/zh_TW.ts index 9125e296..d2247c53 100644 --- a/main/manager-mobile/src/i18n/zh_TW.ts +++ b/main/manager-mobile/src/i18n/zh_TW.ts @@ -129,7 +129,8 @@ export default { 'agent.modelConfig': '模型配置', 'agent.vad': '語音活動檢測', 'agent.asr': '語音識別', - 'agent.llm': '大語言模型', + 'agent.llm': '主語言模型', + 'agent.slm': '小參數模型', 'agent.vllm': '視覺大模型', 'agent.intent': '意圖識別', 'agent.memory': '記憶', diff --git a/main/manager-mobile/src/pages/agent/edit.vue b/main/manager-mobile/src/pages/agent/edit.vue index ef61477f..cc1844d2 100644 --- a/main/manager-mobile/src/pages/agent/edit.vue +++ b/main/manager-mobile/src/pages/agent/edit.vue @@ -29,6 +29,7 @@ const formData = ref>({ vadModelId: '', asrModelId: '', llmModelId: '', + slmModelId: '', vllmModelId: '', intentModelId: '', memModelId: '', @@ -46,6 +47,7 @@ const displayNames = ref({ vad: t('agent.pleaseSelect'), asr: t('agent.pleaseSelect'), llm: t('agent.pleaseSelect'), + slm: t('agent.pleaseSelect'), vllm: t('agent.pleaseSelect'), intent: t('agent.pleaseSelect'), memory: t('agent.pleaseSelect'), @@ -94,6 +96,7 @@ const pickerShow = ref<{ vad: false, asr: false, llm: false, + slm: false, vllm: false, intent: false, memory: false, @@ -272,6 +275,7 @@ function updateDisplayNames() { displayNames.value.vad = getModelDisplayName('VAD', formData.value.vadModelId) displayNames.value.asr = getModelDisplayName('ASR', formData.value.asrModelId) displayNames.value.llm = getModelDisplayName('LLM', formData.value.llmModelId) + displayNames.value.slm = getModelDisplayName('LLM', formData.value.slmModelId) displayNames.value.vllm = getModelDisplayName('VLLM', formData.value.vllmModelId) displayNames.value.intent = getModelDisplayName('Intent', formData.value.intentModelId) displayNames.value.memory = getModelDisplayName('Memory', formData.value.memModelId) @@ -437,6 +441,7 @@ function selectRoleTemplate(templateId: string) { vadModelId: template.vadModelId || formData.value.vadModelId, asrModelId: template.asrModelId || formData.value.asrModelId, llmModelId: template.llmModelId || formData.value.llmModelId, + slmModelId: template.llmModelId || formData.value.slmModelId, vllmModelId: template.vllmModelId || formData.value.vllmModelId, intentModelId: template.intentModelId || formData.value.intentModelId, memModelId: template.memModelId || formData.value.memModelId, @@ -474,6 +479,9 @@ async function onPickerConfirm(type: string, value: any, name: string) { case 'llm': formData.value.llmModelId = value break + case 'slm': + formData.value.slmModelId = value + break case 'vllm': formData.value.vllmModelId = value break @@ -490,7 +498,8 @@ async function onPickerConfirm(type: string, value: any, name: string) { if (value === 'Memory_nomem' || value === 'Memory_mem_report_only') { tempSummaryMemory.value = formData.value.summaryMemory formData.value.summaryMemory = '' - } else if (tempSummaryMemory.value !== '' && formData.value.summaryMemory === '') { + } + else if (tempSummaryMemory.value !== '' && formData.value.summaryMemory === '') { formData.value.summaryMemory = tempSummaryMemory.value tempSummaryMemory.value = '' } @@ -839,6 +848,16 @@ onMounted(async () => { + + + {{ t('agent.slm') }} + + + {{ displayNames.slm }} + + + + {{ t('agent.vllm') }} @@ -995,6 +1014,13 @@ onMounted(async () => { @select="({ item }) => onPickerConfirm('llm', item.value, item.name)" /> + + Date: Thu, 9 Apr 2026 09:23:54 +0800 Subject: [PATCH 4/6] =?UTF-8?q?update:=E6=B7=BB=E5=8A=A0SLM=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E9=85=8D=E7=BD=AE=E5=92=8C=E4=BC=9A=E8=AF=9D=E6=A0=87?= =?UTF-8?q?=E9=A2=98=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xiaozhi/common/constant/Constant.java | 10 ++ .../modules/agent/dao/AgentChatTitleDao.java | 12 ++ .../agent/dto/AgentChatSessionDTO.java | 5 + .../modules/agent/dto/AgentUpdateDTO.java | 3 + .../agent/entity/AgentChatTitleEntity.java | 36 +++++ .../modules/agent/entity/AgentEntity.java | 3 + .../agent/service/AgentChatTitleService.java | 10 ++ .../impl/AgentChatHistoryServiceImpl.java | 8 +- .../impl/AgentChatSummaryServiceImpl.java | 133 ++++++++++++++---- .../impl/AgentChatTitleServiceImpl.java | 60 ++++++++ .../agent/service/impl/AgentServiceImpl.java | 29 ++++ .../modules/llm/service/LLMService.java | 9 ++ .../impl/OpenAIStyleLLMServiceImpl.java | 89 ++++++++++++ .../resources/db/changelog/202604011545.sql | 16 +++ .../db/changelog/db.changelog-master.yaml | 7 + .../main/resources/mapper/agent/AgentDao.xml | 3 + 16 files changed, 406 insertions(+), 27 deletions(-) create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentChatTitleDao.java create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatTitleEntity.java create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatTitleService.java create mode 100644 main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatTitleServiceImpl.java create mode 100644 main/manager-api/src/main/resources/db/changelog/202604011545.sql diff --git a/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java b/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java index b438fec1..37029501 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java +++ b/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java @@ -156,6 +156,16 @@ public interface Constant { */ String MEMORY_MEM_REPORT_ONLY = "Memory_mem_report_only"; + /** + * Mem0AI记忆 + */ + String MEMORY_MEM0AI = "Memory_mem0ai"; + + /** + * PowerMem记忆 + */ + String MEMORY_POWERMEM = "Memory_powermem"; + /** * 火山引擎双声道语音克隆 */ diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentChatTitleDao.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentChatTitleDao.java new file mode 100644 index 00000000..0ca51d3f --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dao/AgentChatTitleDao.java @@ -0,0 +1,12 @@ +package xiaozhi.modules.agent.dao; + +import org.apache.ibatis.annotations.Mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import xiaozhi.modules.agent.entity.AgentChatTitleEntity; + +@Mapper +public interface AgentChatTitleDao extends BaseMapper { + +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatSessionDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatSessionDTO.java index c65f05c6..841b6976 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatSessionDTO.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentChatSessionDTO.java @@ -23,4 +23,9 @@ public class AgentChatSessionDTO { * 聊天条数 */ private Integer chatCount; + + /** + * 会话标题 + */ + private String title; } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentUpdateDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentUpdateDTO.java index 8f6dead8..13d95ddc 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentUpdateDTO.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/AgentUpdateDTO.java @@ -33,6 +33,9 @@ public class AgentUpdateDTO implements Serializable { @Schema(description = "大语言模型标识", example = "llm_model_02", nullable = true) private String llmModelId; + @Schema(description = "小模型标识", example = "slm_model_02", nullable = true) + private String slmModelId; + @Schema(description = "VLLM模型标识", example = "vllm_model_02", required = false) private String vllmModelId; diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatTitleEntity.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatTitleEntity.java new file mode 100644 index 00000000..d9361366 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentChatTitleEntity.java @@ -0,0 +1,36 @@ +package xiaozhi.modules.agent.entity; + +import java.util.Date; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "ai_agent_chat_title") +public class AgentChatTitleEntity { + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + @TableField(value = "session_id") + private String sessionId; + + @TableField(value = "title") + private String title; + + @TableField(value = "created_at") + private Date createdAt; + + @TableField(value = "updated_at") + private Date updatedAt; +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentEntity.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentEntity.java index dff620ca..ce752b01 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentEntity.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/entity/AgentEntity.java @@ -37,6 +37,9 @@ public class AgentEntity { @Schema(description = "大语言模型标识") private String llmModelId; + @Schema(description = "小模型标识") + private String slmModelId; + @Schema(description = "VLLM模型标识") private String vllmModelId; diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatTitleService.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatTitleService.java new file mode 100644 index 00000000..71699c58 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentChatTitleService.java @@ -0,0 +1,10 @@ +package xiaozhi.modules.agent.service; + +import xiaozhi.modules.agent.entity.AgentChatTitleEntity; + +public interface AgentChatTitleService { + + void saveOrUpdateTitle(String sessionId, String title); + + String getTitleBySessionId(String sessionId); +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatHistoryServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatHistoryServiceImpl.java index 494107a2..9cfece89 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatHistoryServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatHistoryServiceImpl.java @@ -6,6 +6,7 @@ import java.util.Map; import java.util.stream.Collectors; import cn.hutool.core.collection.ListUtil; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,6 +27,7 @@ import xiaozhi.modules.agent.dto.AgentChatHistoryDTO; import xiaozhi.modules.agent.dto.AgentChatSessionDTO; import xiaozhi.modules.agent.entity.AgentChatHistoryEntity; import xiaozhi.modules.agent.service.AgentChatHistoryService; +import xiaozhi.modules.agent.service.AgentChatTitleService; import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO; /** @@ -36,9 +38,12 @@ import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO; * @since 1.0.0 */ @Service +@RequiredArgsConstructor public class AgentChatHistoryServiceImpl extends ServiceImpl implements AgentChatHistoryService { + private final AgentChatTitleService agentChatTitleService; + @Override public PageData getSessionListByAgentId(Map params) { String agentId = (String) params.get("agentId"); @@ -61,6 +66,7 @@ public class AgentChatHistoryServiceImpl extends ServiceImpl> batch = ListUtil.split(audioIds, 1000); - batch.forEach(dataList->{ + batch.forEach(dataList -> { baseMapper.deleteAudioByIds(dataList); }); } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatSummaryServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatSummaryServiceImpl.java index 26e601ec..02e54145 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatSummaryServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatSummaryServiceImpl.java @@ -22,6 +22,7 @@ import xiaozhi.modules.agent.dto.AgentUpdateDTO; import xiaozhi.modules.agent.entity.AgentChatHistoryEntity; import xiaozhi.modules.agent.service.AgentChatHistoryService; import xiaozhi.modules.agent.service.AgentChatSummaryService; +import xiaozhi.modules.agent.service.AgentChatTitleService; import xiaozhi.modules.agent.service.AgentService; import xiaozhi.modules.agent.vo.AgentInfoVO; import xiaozhi.modules.device.entity.DeviceEntity; @@ -42,6 +43,7 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService { private final AgentChatHistoryService agentChatHistoryService; private final AgentService agentService; + private final AgentChatTitleService agentChatTitleService; private final DeviceService deviceService; private final LLMService llmService; private final ModelConfigService modelConfigService; @@ -91,40 +93,42 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService { @Override public boolean generateAndSaveChatSummary(String sessionId) { try { - // 1. 获取设备信息(通过会话关联的设备) DeviceEntity device = getDeviceBySessionId(sessionId); if (device == null) { log.info("未找到与会话 {} 关联的设备", sessionId); return false; } - // 2. 检查记忆模型类型,如果是仅上报聊天记录模式则跳过总结 - String memModelId = agentService.getAgentById(device.getAgentId()).getMemModelId(); - if (memModelId != null && memModelId.equals(Constant.MEMORY_MEM_REPORT_ONLY)) { + String agentId = device.getAgentId(); + String memModelId = agentService.getAgentById(agentId).getMemModelId(); + + if (memModelId == null || memModelId.equals(Constant.MEMORY_MEM_REPORT_ONLY)) { log.info("会话 {} 使用仅上报聊天记录模式,跳过记忆总结", sessionId); + generateAndSaveChatTitle(sessionId, agentId); return true; } - // 3. 生成总结 - AgentChatSummaryDTO summaryDTO = generateChatSummary(sessionId); - if (!summaryDTO.isSuccess()) { - log.info("生成总结失败: {}", summaryDTO.getErrorMessage()); - return false; - } + boolean shouldSummarizeMemory = !memModelId.equals(Constant.MEMORY_NO_MEM) + && !memModelId.equals(Constant.MEMORY_MEM0AI) + && !memModelId.equals(Constant.MEMORY_POWERMEM); - // 4. 更新智能体记忆 - AgentMemoryDTO memoryDTO = new AgentMemoryDTO(); - memoryDTO.setSummaryMemory(summaryDTO.getSummary()); - - // 调用现有接口更新记忆 - agentService.updateAgentById(device.getAgentId(), - new AgentUpdateDTO() { + if (shouldSummarizeMemory) { + AgentChatSummaryDTO summaryDTO = generateChatSummary(sessionId); + if (summaryDTO.isSuccess()) { + agentService.updateAgentById(agentId, new AgentUpdateDTO() { { setSummaryMemory(summaryDTO.getSummary()); } }); + log.info("成功保存会话 {} 的聊天记录总结到智能体 {}", sessionId, agentId); + } else { + log.info("生成总结失败: {}", summaryDTO.getErrorMessage()); + } + } else { + log.info("会话 {} 使用 {} 模式,跳过记忆总结", sessionId, memModelId); + } - log.info("成功保存会话 {} 的聊天记录总结到智能体 {}", sessionId, device.getAgentId()); + generateAndSaveChatTitle(sessionId, agentId); return true; } catch (Exception e) { @@ -133,6 +137,87 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService { } } + private void generateAndSaveChatTitle(String sessionId, String agentId) { + try { + List chatHistory = getChatHistoryBySessionId(sessionId); + if (chatHistory == null || chatHistory.isEmpty()) { + return; + } + + List meaningfulMessages = extractMeaningfulMessages(chatHistory); + if (meaningfulMessages.isEmpty()) { + return; + } + + StringBuilder conversation = new StringBuilder(); + for (int i = 0; i < meaningfulMessages.size(); i++) { + conversation.append("消息").append(i + 1).append(": ").append(meaningfulMessages.get(i)).append("\n"); + } + + String slmModelId = getSlmModelId(agentId); + String title = llmService.generateTitle(conversation.toString(), slmModelId); + + if (StringUtils.isNotBlank(title)) { + agentChatTitleService.saveOrUpdateTitle(sessionId, title); + log.info("成功保存会话 {} 的标题: {}", sessionId, title); + } + } catch (Exception e) { + log.error("生成会话 {} 的标题时发生错误: {}", sessionId, e.getMessage()); + } + } + + private String getSlmModelId(String agentId) { + try { + if (StringUtils.isBlank(agentId)) { + return null; + } + + AgentInfoVO agentInfo = agentService.getAgentById(agentId); + if (agentInfo == null) { + return null; + } + + String slmModelId = agentInfo.getSlmModelId(); + if (StringUtils.isNotBlank(slmModelId)) { + log.info("会话 {} 使用SLM模型: {}", agentId, slmModelId); + return slmModelId; + } + + ModelConfigEntity defaultLlmConfig = getDefaultLLMConfig(); + if (defaultLlmConfig != null) { + log.info("会话 {} 使用默认LLM模型: {}", agentId, defaultLlmConfig.getId()); + return defaultLlmConfig.getId(); + } + + String llmModelId = agentInfo.getLlmModelId(); + log.info("会话 {} 使用LLM模型(最终回退): {}", agentId, llmModelId); + return llmModelId; + } catch (Exception e) { + log.error("获取智能体slm模型ID失败,agentId: {}, 错误: {}", agentId, e.getMessage()); + return null; + } + } + + private ModelConfigEntity getDefaultLLMConfig() { + try { + List llmConfigs = modelConfigService.getEnabledModelsByType("LLM"); + if (llmConfigs == null || llmConfigs.isEmpty()) { + return null; + } + + for (ModelConfigEntity config : llmConfigs) { + if (config.getIsDefault() != null && config.getIsDefault() == 1) { + return config; + } + } + + return llmConfigs.get(0); + } catch (Exception e) { + log.error("获取默认LLM配置失败: {}", e.getMessage()); + return null; + } + } + /** * 根据会话ID获取聊天记录 */ @@ -313,15 +398,13 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService { */ private String callJavaLLMForSummaryWithHistory(String conversation, String historyMemory, String agentId) { try { - // 获取智能体配置,从中提取记忆总结的模型ID - String modelId = getMemorySummaryModelId(agentId); + String modelId = getSlmModelId(agentId); if (StringUtils.isBlank(modelId)) { - log.info("未找到记忆总结的LLM模型配置,使用默认LLM服务"); + log.info("未找到SLM模型,使用默认LLM服务"); return llmService.generateSummaryWithHistory(conversation, historyMemory, null, null); } - // 使用指定的模型ID调用LLM服务(支持历史记忆合并) String summary = llmService.generateSummaryWithHistory(conversation, historyMemory, null, modelId); if (StringUtils.isNotBlank(summary) && !summary.equals("服务暂不可用") && !summary.equals("总结生成失败")) { @@ -341,15 +424,13 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService { */ private String callJavaLLMForSummary(String conversation, String agentId) { try { - // 获取智能体配置,从中提取记忆总结的模型ID - String modelId = getMemorySummaryModelId(agentId); + String modelId = getSlmModelId(agentId); if (StringUtils.isBlank(modelId)) { - log.info("未找到记忆总结的LLM模型配置,使用默认LLM服务"); + log.info("未找到SLM模型,使用默认LLM服务"); return llmService.generateSummary(conversation); } - // 使用指定的模型ID调用LLM服务 String summary = llmService.generateSummaryWithModel(conversation, modelId); if (StringUtils.isNotBlank(summary) && !summary.equals("服务暂不可用") && !summary.equals("总结生成失败")) { diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatTitleServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatTitleServiceImpl.java new file mode 100644 index 00000000..b875a073 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentChatTitleServiceImpl.java @@ -0,0 +1,60 @@ +package xiaozhi.modules.agent.service.impl; + +import java.util.Date; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; + +import lombok.RequiredArgsConstructor; +import xiaozhi.modules.agent.dao.AgentChatTitleDao; +import xiaozhi.modules.agent.entity.AgentChatTitleEntity; +import xiaozhi.modules.agent.service.AgentChatTitleService; + +@Service +@RequiredArgsConstructor +public class AgentChatTitleServiceImpl implements AgentChatTitleService { + + private final AgentChatTitleDao agentChatTitleDao; + + @Override + public void saveOrUpdateTitle(String sessionId, String title) { + if (StringUtils.isBlank(sessionId) || StringUtils.isBlank(title)) { + return; + } + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("session_id", sessionId); + + AgentChatTitleEntity existing = agentChatTitleDao.selectOne(wrapper); + + if (existing != null) { + existing.setTitle(title); + existing.setUpdatedAt(new Date()); + agentChatTitleDao.updateById(existing); + } else { + AgentChatTitleEntity newEntity = AgentChatTitleEntity.builder() + .id(java.util.UUID.randomUUID().toString().replace("-", "")) + .sessionId(sessionId) + .title(title) + .createdAt(new Date()) + .updatedAt(new Date()) + .build(); + agentChatTitleDao.insert(newEntity); + } + } + + @Override + public String getTitleBySessionId(String sessionId) { + if (StringUtils.isBlank(sessionId)) { + return null; + } + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("session_id", sessionId); + + AgentChatTitleEntity entity = agentChatTitleDao.selectOne(wrapper); + return entity != null ? entity.getTitle() : null; + } +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java index 8cf187df..ff8add76 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentServiceImpl.java @@ -300,6 +300,9 @@ public class AgentServiceImpl extends BaseServiceImpl imp if (dto.getLlmModelId() != null) { existingEntity.setLlmModelId(dto.getLlmModelId()); } + if (dto.getSlmModelId() != null) { + existingEntity.setSlmModelId(dto.getSlmModelId()); + } if (dto.getVllmModelId() != null) { existingEntity.setVllmModelId(dto.getVllmModelId()); } @@ -506,6 +509,13 @@ public class AgentServiceImpl extends BaseServiceImpl imp entity.setLanguage(template.getLanguage()); } + if (entity.getSlmModelId() == null) { + String defaultSlmModelId = getDefaultLLMModelId(); + if (defaultSlmModelId != null) { + entity.setSlmModelId(defaultSlmModelId); + } + } + // 设置用户ID和创建者信息 UserDetail user = SecurityUser.getUser(); entity.setUserId(user.getId()); @@ -544,4 +554,23 @@ public class AgentServiceImpl extends BaseServiceImpl imp return entity.getId(); } + private String getDefaultLLMModelId() { + try { + List llmConfigs = modelConfigService.getEnabledModelsByType("LLM"); + if (llmConfigs == null || llmConfigs.isEmpty()) { + return null; + } + + for (ModelConfigEntity config : llmConfigs) { + if (config.getIsDefault() != null && config.getIsDefault() == 1) { + return config.getId(); + } + } + + return llmConfigs.get(0).getId(); + } catch (Exception e) { + return null; + } + } + } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/llm/service/LLMService.java b/main/manager-api/src/main/java/xiaozhi/modules/llm/service/LLMService.java index a3f10e9f..4436f1ca 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/llm/service/LLMService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/llm/service/LLMService.java @@ -67,4 +67,13 @@ public interface LLMService { * @return 是否可用 */ boolean isAvailable(String modelId); + + /** + * 生成会话标题 + * + * @param conversation 对话内容 + * @param modelId 模型ID + * @return 标题(约15字) + */ + String generateTitle(String conversation, String modelId); } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/llm/service/impl/OpenAIStyleLLMServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/llm/service/impl/OpenAIStyleLLMServiceImpl.java index 55fda70d..585826f3 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/llm/service/impl/OpenAIStyleLLMServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/llm/service/impl/OpenAIStyleLLMServiceImpl.java @@ -37,6 +37,8 @@ public class OpenAIStyleLLMServiceImpl implements LLMService { private static final String DEFAULT_SUMMARY_PROMPT = "你是一个经验丰富的记忆总结者,擅长将对话内容进行总结摘要,遵循以下规则:\n1、总结用户的重要信息,以便在未来的对话中提供更个性化的服务\n2、不要重复总结,不要遗忘之前记忆,除非原来的记忆超过了1800字,否则不要遗忘、不要压缩用户的历史记忆\n3、用户操控的设备音量、播放音乐、天气、退出、不想对话等和用户本身无关的内容,这些信息不需要加入到总结中\n4、聊天内容中的今天的日期时间、今天的天气情况与用户事件无关的数据,这些信息如果当成记忆存储会影响后续对话,这些信息不需要加入到总结中\n5、不要把设备操控的成果结果和失败结果加入到总结中,也不要把用户的一些废话加入到总结中\n6、不要为了总结而总结,如果用户的聊天没有意义,请返回原来的历史记录也是可以的\n7、只需要返回总结摘要,严格控制在1800字内\n8、不要包含代码、xml,不需要解释、注释和说明,保存记忆时仅从对话提取信息,不要混入示例内容\n9、如果提供了历史记忆,请将新对话内容与历史记忆进行智能合并,保留有价值的历史信息,同时添加新的重要信息\n\n历史记忆:\n{history_memory}\n\n新对话内容:\n{conversation}"; + private static final String DEFAULT_TITLE_PROMPT = "请根据以下对话内容,生成一个简洁的会话标题(约15字以内),只返回标题,不要包含任何解释或标点符号:\n{conversation}"; + @Override public String generateSummary(String conversation) { return generateSummary(conversation, null, null); @@ -302,4 +304,91 @@ public class OpenAIStyleLLMServiceImpl implements LLMService { return null; } } + + @Override + public String generateTitle(String conversation, String modelId) { + if (!isAvailable()) { + log.warn("LLM服务不可用,无法生成标题"); + return null; + } + + try { + ModelConfigEntity llmConfig; + if (modelId != null && !modelId.trim().isEmpty()) { + llmConfig = modelConfigService.getModelByIdFromCache(modelId); + } else { + llmConfig = getDefaultLLMConfig(); + } + + if (llmConfig == null || llmConfig.getConfigJson() == null) { + log.error("未找到可用的LLM模型配置,modelId: {}", modelId); + return null; + } + + JSONObject configJson = llmConfig.getConfigJson(); + String baseUrl = configJson.getStr("base_url"); + String model = configJson.getStr("model_name"); + String apiKey = configJson.getStr("api_key"); + + if (StringUtils.isBlank(baseUrl) || StringUtils.isBlank(apiKey)) { + log.error("LLM配置不完整,baseUrl或apiKey为空"); + return null; + } + + String prompt = DEFAULT_TITLE_PROMPT.replace("{conversation}", conversation); + + Map requestBody = new HashMap<>(); + requestBody.put("model", model != null ? model : "gpt-3.5-turbo"); + + Map[] messages = new Map[1]; + Map message = new HashMap<>(); + message.put("role", "user"); + message.put("content", prompt); + messages[0] = message; + + requestBody.put("messages", messages); + requestBody.put("temperature", 0.3); + requestBody.put("max_tokens", 50); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer " + apiKey); + + HttpEntity> entity = new HttpEntity<>(requestBody, headers); + + String apiUrl = baseUrl; + if (!apiUrl.endsWith("/chat/completions")) { + if (!apiUrl.endsWith("/")) { + apiUrl += "/"; + } + apiUrl += "chat/completions"; + } + + ResponseEntity response = restTemplate.exchange( + apiUrl, HttpMethod.POST, entity, String.class); + + if (response.getStatusCode().is2xxSuccessful()) { + JSONObject responseJson = JSONUtil.parseObj(response.getBody()); + JSONArray choices = responseJson.getJSONArray("choices"); + if (choices != null && choices.size() > 0) { + JSONObject choice = choices.getJSONObject(0); + JSONObject messageObj = choice.getJSONObject("message"); + String title = messageObj.getStr("content"); + if (StringUtils.isNotBlank(title)) { + title = title.trim().replaceAll("[,。!?、:;''\"\"【】()]", ""); + if (title.length() > 15) { + title = title.substring(0, 15); + } + return title; + } + } + } else { + log.error("LLM API调用失败,状态码:{},响应:{}", response.getStatusCode(), response.getBody()); + } + } catch (Exception e) { + log.error("调用LLM服务生成标题时发生异常,modelId: {}", modelId, e); + } + + return null; + } } \ No newline at end of file diff --git a/main/manager-api/src/main/resources/db/changelog/202604011545.sql b/main/manager-api/src/main/resources/db/changelog/202604011545.sql new file mode 100644 index 00000000..1ef5be69 --- /dev/null +++ b/main/manager-api/src/main/resources/db/changelog/202604011545.sql @@ -0,0 +1,16 @@ +-- 智能体表添加小模型ID字段 +SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_agent' AND COLUMN_NAME = 'slm_model_id'); +SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_agent` ADD COLUMN `slm_model_id` VARCHAR(255) NULL COMMENT ''小模型ID'' AFTER `llm_model_id`', 'SELECT ''Column slm_model_id already exists'' AS msg'); +PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +-- 创建聊天标题表 +DROP TABLE IF EXISTS `ai_agent_chat_title`; +CREATE TABLE `ai_agent_chat_title` ( + `id` VARCHAR(32) NOT NULL COMMENT '主键ID', + `session_id` VARCHAR(255) NOT NULL COMMENT '会话ID', + `title` VARCHAR(255) DEFAULT NULL COMMENT '聊天标题', + `created_at` DATETIME DEFAULT NULL COMMENT '创建时间', + `updated_at` DATETIME DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `idx_session_id` (`session_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能体聊天标题表'; diff --git a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml index 4510fa16..49cf28f4 100755 --- a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml @@ -599,3 +599,10 @@ databaseChangeLog: - sqlFile: encoding: utf8 path: classpath:db/changelog/202604011035.sql + - changeSet: + id: 202604011545 + author: rainv123 + changes: + - sqlFile: + encoding: utf8 + path: classpath:db/changelog/202604011545.sql \ No newline at end of file diff --git a/main/manager-api/src/main/resources/mapper/agent/AgentDao.xml b/main/manager-api/src/main/resources/mapper/agent/AgentDao.xml index 9c417909..c250afe3 100644 --- a/main/manager-api/src/main/resources/mapper/agent/AgentDao.xml +++ b/main/manager-api/src/main/resources/mapper/agent/AgentDao.xml @@ -14,6 +14,8 @@ + + @@ -46,6 +48,7 @@ a.asr_model_id AS asrModelId, a.vad_model_id AS vadModelId, a.llm_model_id AS llmModelId, + a.slm_model_id AS slmModelId, a.vllm_model_id AS vllmModelId, a.tts_model_id AS ttsModelId, a.tts_voice_id AS ttsVoiceId, From 784ba359f2128943604b7a0bd34f91f71d70933f Mon Sep 17 00:00:00 2001 From: zhuoqinglian <1035449612@qq.com> Date: Thu, 9 Apr 2026 14:32:32 +0800 Subject: [PATCH 5/6] =?UTF-8?q?update:=20mobile=20=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E6=A0=87=E9=A2=98=E4=BC=98=E5=8C=96=EF=BC=8C?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E8=AF=A6=E6=83=85=E5=8F=82=E6=95=B0=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E6=8A=98=E5=8F=A0=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/api/chat-history/types.ts | 2 +- .../src/pages/chat-history/detail.vue | 168 +++++++++++++++++- .../src/pages/chat-history/index.vue | 31 ++-- 3 files changed, 182 insertions(+), 19 deletions(-) diff --git a/main/manager-mobile/src/api/chat-history/types.ts b/main/manager-mobile/src/api/chat-history/types.ts index 32bcf8d7..9652db6d 100644 --- a/main/manager-mobile/src/api/chat-history/types.ts +++ b/main/manager-mobile/src/api/chat-history/types.ts @@ -15,7 +15,7 @@ export interface ChatSessionsResponse { // 聊天消息 export interface ChatMessage { createdAt: string - chatType: 1 | 2 // 1是用户,2是AI + chatType: 1 | 2 | 3 // 1是用户,2是AI,3是参数说明 content: string audioId: string | null macAddress: string diff --git a/main/manager-mobile/src/pages/chat-history/detail.vue b/main/manager-mobile/src/pages/chat-history/detail.vue index c3d399d5..e60bd93b 100644 --- a/main/manager-mobile/src/pages/chat-history/detail.vue +++ b/main/manager-mobile/src/pages/chat-history/detail.vue @@ -61,6 +61,7 @@ const loading = ref(false) // 音频播放相关 const audioContext = ref(null) const playingAudioId = ref(null) +const expandedToolResults = ref({}) // 返回上一页 function goBack() { @@ -124,8 +125,50 @@ function getSpeakerName(message: ChatMessage): string { // 格式化时间 function formatTime(timeStr: string) { - const date = new Date(timeStr) - return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}` + if (!timeStr) + return t('chatHistory.unknownTime') + + // 处理时间字符串,确保格式正确 + const date = new Date(timeStr.replace(' ', 'T')) // 转换为ISO格式 + const now = new Date() + + // 检查日期是否有效 + if (Number.isNaN(date.getTime())) { + return timeStr // 如果解析失败,直接返回原字符串 + } + + const diff = now.getTime() - date.getTime() + + // 小于1分钟 + if (diff < 60000) + return t('chatHistory.justNow') + + // 小于1小时 + if (diff < 3600000) + return t('chatHistory.minutesAgo', { minutes: Math.floor(diff / 60000) }) + + // 小于1天(24小时) + if (diff < 86400000) + return t('chatHistory.hoursAgo', { hours: Math.floor(diff / 3600000) }) + + // 小于7天 + if (diff < 604800000) { + const days = Math.floor(diff / 86400000) + return t('chatHistory.daysAgo', { days }) + } + + // 超过7天,显示具体日期 + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const currentYear = now.getFullYear() + + // 如果是当前年份,不显示年份 + if (year === currentYear) { + return `${month}-${day}` + } + + return `${year}-${month}-${day}` } // 播放音频 @@ -192,6 +235,52 @@ const playAudio = debounce(async (audioId: string) => { } }, 400) +function extractContentFromString(content: string) { + if (!content || content.trim() === '') { + return content + } + + // 尝试解析为 JSON + try { + const jsonObj = JSON.parse(content) + + // 如果是数组格式(包含 text 和 tool) + if (Array.isArray(jsonObj)) { + return jsonObj + } + + // 如果是对象且有 content 字段 + if (jsonObj && typeof jsonObj === 'object' && jsonObj.content) { + return jsonObj.content + } + } + catch (e) { + // 如果不是有效的 JSON,直接返回原内容 + } + + // 如果不是 JSON 格式或没有 content 字段,直接返回原内容 + return content +} + +function toggleToolResult(messageIndex, itemIndex) { + const key = `${messageIndex}-${itemIndex}` + expandedToolResults.value[key] = !expandedToolResults.value[key] +} + +function isToolResultCollapsed(messageIndex, itemIndex) { + const key = `${messageIndex}-${itemIndex}` + // 默认折叠(true表示折叠) + return !expandedToolResults.value[key] +} + +function getFirstLineText(text: string) { + if (!text) { + return '' + } + const firstLine = text.split('\n')[0] + return firstLine.length < text.length ? `${firstLine}...` : text +} + onLoad((options) => { if (options?.sessionId && options?.agentId) { sessionId.value = options.sessionId @@ -256,6 +345,7 @@ onUnload(() => { :class="{ 'items-end': message.chatType === 1, 'items-start': message.chatType === 2, + 'tool-message': message.chatType === 3, }" > @@ -263,11 +353,39 @@ onUnload(() => { class="shadow-message break-words rounded-[20rpx] p-[24rpx] leading-[1.4]" :class="{ 'bg-[#336cff] text-white': message.chatType === 1, - 'bg-white text-[#232338] border border-[#eeeeee]': message.chatType === 2, + 'bg-white text-[#232338] border border-[#eeeeee]': [2, 3].includes(message.chatType), }" > + - + { .animate-pulse-audio { animation: pulse-audio 1.5s infinite; } + +.text-content { + display: block; + margin-bottom: 8rpx; +} + +.tool-call-text { + color: #1890ff; + font-family: 'Courier New', monospace; + font-weight: 500; + font-size: 24rpx; + display: block; + margin-top: 8rpx; +} + +.user-message .tool-call-text { + color: #e6f7ff; +} + +.tool-message .message-content { + background-color: #f0f0f0; +} + +.tool-result-wrapper { + position: relative; + padding-right: 40rpx; +} + +.tool-result-collapsed { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.tool-toggle-btn { + position: absolute; + right: 0; + top: 0; + cursor: pointer; + color: #1890ff; + font-size: 24rpx; +} diff --git a/main/manager-mobile/src/pages/chat-history/index.vue b/main/manager-mobile/src/pages/chat-history/index.vue index aa5c8414..b922ccdd 100644 --- a/main/manager-mobile/src/pages/chat-history/index.vue +++ b/main/manager-mobile/src/pages/chat-history/index.vue @@ -8,15 +8,15 @@ defineOptions({ name: 'ChatHistory', }) +const props = withDefaults(defineProps(), { + agentId: 'default', +}) + // 接收props interface Props { agentId?: string } -const props = withDefaults(defineProps(), { - agentId: 'default' -}) - // 获取屏幕边界到安全区域距离 let safeAreaInsets: any let systemInfo: any @@ -187,8 +187,8 @@ defineExpose({ - {{ t('chatHistory.loading') }} - + {{ t('chatHistory.loading') }} + @@ -205,7 +205,7 @@ defineExpose({ - {{ t('chatHistory.conversationRecord') }} {{ session.sessionId.substring(0, 8) }}... + {{ session.title || `${t('chatHistory.conversationRecord')} ${session.sessionId.substring(0, 8)}...` }} {{ formatTime(session.createdAt) }} @@ -242,11 +242,11 @@ defineExpose({ - {{ t('chatHistory.noChatRecords') }} - - - {{ t('chatHistory.chatRecordsDescription') }} - + {{ t('chatHistory.noChatRecords') }} + + + {{ t('chatHistory.chatRecordsDescription') }} + @@ -333,7 +333,7 @@ defineExpose({ padding: 32rpx; .session-info { - flex: 1; + width: 94%; .session-header { display: flex; @@ -345,8 +345,11 @@ defineExpose({ font-size: 32rpx; font-weight: 600; color: #232338; - max-width: 70%; + width: 70%; word-break: break-all; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .session-time { From 2cd9484c54959fb01dc9bf493c57d819336e4434 Mon Sep 17 00:00:00 2001 From: zhuoqinglian <1035449612@qq.com> Date: Fri, 10 Apr 2026 16:11:04 +0800 Subject: [PATCH 6/6] =?UTF-8?q?update:=20mobile=20=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E8=8E=B7=E5=8F=96=E6=9C=80=E6=96=B0=E6=95=B0?= =?UTF-8?q?=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/api/chat-history/chat-history.ts | 3 ++ main/manager-mobile/src/pages/agent/edit.vue | 1 - .../src/pages/chat-history/detail.vue | 5 ++- .../src/pages/chat-history/index.vue | 19 +++++++---- main/manager-mobile/src/utils/index.ts | 33 +++++++++++++++++++ 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/main/manager-mobile/src/api/chat-history/chat-history.ts b/main/manager-mobile/src/api/chat-history/chat-history.ts index 89c3f1ef..dc7000ef 100644 --- a/main/manager-mobile/src/api/chat-history/chat-history.ts +++ b/main/manager-mobile/src/api/chat-history/chat-history.ts @@ -34,6 +34,9 @@ export function getChatHistory(agentId: string, sessionId: string) { ignoreAuth: false, toast: false, }, + cacheFor: { + expire: -1, + }, }) } diff --git a/main/manager-mobile/src/pages/agent/edit.vue b/main/manager-mobile/src/pages/agent/edit.vue index cc1844d2..964ad0b2 100644 --- a/main/manager-mobile/src/pages/agent/edit.vue +++ b/main/manager-mobile/src/pages/agent/edit.vue @@ -283,7 +283,6 @@ function updateDisplayNames() { // 角色音色特殊处理 displayNames.value.report = reportOptions.find(item => item.value === formData.value.chatHistoryConf)?.name - displayNames.value.language = formData.value.ttsLanguage isVisibleReport.value = formData.value.memModelId !== 'Memory_nomem' diff --git a/main/manager-mobile/src/pages/chat-history/detail.vue b/main/manager-mobile/src/pages/chat-history/detail.vue index e60bd93b..ae2d3d41 100644 --- a/main/manager-mobile/src/pages/chat-history/detail.vue +++ b/main/manager-mobile/src/pages/chat-history/detail.vue @@ -285,7 +285,6 @@ onLoad((options) => { if (options?.sessionId && options?.agentId) { sessionId.value = options.sessionId agentId.value = options.agentId - loadChatHistory() } else { console.error('缺少必要参数') @@ -293,6 +292,10 @@ onLoad((options) => { } }) +onShow(() => { + loadChatHistory() +}) + // 页面销毁时清理音频资源 onUnload(() => { if (audioContext.value) { diff --git a/main/manager-mobile/src/pages/chat-history/index.vue b/main/manager-mobile/src/pages/chat-history/index.vue index b922ccdd..b4cf6d97 100644 --- a/main/manager-mobile/src/pages/chat-history/index.vue +++ b/main/manager-mobile/src/pages/chat-history/index.vue @@ -3,6 +3,7 @@ import type { ChatSession } from '@/api/chat-history/types' import { computed, onMounted, ref } from 'vue' import { getChatSessions } from '@/api/chat-history/chat-history' import { t } from '@/i18n' +import { deepClone } from '@/utils' defineOptions({ name: 'ChatHistory', @@ -43,7 +44,7 @@ const sessionList = ref([]) const loading = ref(false) const loadingMore = ref(false) const hasMore = ref(true) -const currentPage = ref(1) +const currentPage = ref(0) const pageSize = 10 // 使用传入的智能体ID @@ -52,10 +53,8 @@ const currentAgentId = computed(() => { }) // 加载聊天会话列表 -async function loadChatSessions(page = 1, isRefresh = false) { +async function loadChatSessions(page = 1, isUpdate = false) { try { - console.log(t('chatHistory.getChatSessions'), { page, isRefresh }) - // 检查是否有当前选中的智能体 if (!currentAgentId.value) { console.warn(t('chatHistory.noSelectedAgent')) @@ -76,7 +75,10 @@ async function loadChatSessions(page = 1, isRefresh = false) { }) if (page === 1) { - sessionList.value = response.list || [] + const oldSessionList = deepClone(sessionList.value) + oldSessionList.splice(0, 10) + oldSessionList.unshift(...(response.list || [])) + sessionList.value = isUpdate ? oldSessionList : response.list || [] } else { sessionList.value.push(...(response.list || [])) @@ -170,10 +172,15 @@ function goToChatDetail(session: ChatSession) { onMounted(async () => { // 智能体已简化为默认 - loadChatSessions(1) }) +onShow(() => { + if (currentPage.value !== 0) { + loadChatSessions(1, true) + } +}) + // 暴露方法给父组件 defineExpose({ refresh, diff --git a/main/manager-mobile/src/utils/index.ts b/main/manager-mobile/src/utils/index.ts index 8ac842c8..4017494a 100644 --- a/main/manager-mobile/src/utils/index.ts +++ b/main/manager-mobile/src/utils/index.ts @@ -300,3 +300,36 @@ export function debounce( return debounced } + +type DeepCloneTarget = string | number | boolean | null | undefined | object + +/** + * 深拷贝方法 + * @param target 要拷贝的目标 + * @returns 拷贝后的新对象 + */ +export function deepClone(target: T): T { + if (target === null || typeof target !== 'object') { + return target + } + + if (target instanceof Date) { + return new Date(target.getTime()) as any + } + + if (Array.isArray(target)) { + return target.map(item => deepClone(item)) as any + } + + if (target instanceof Object) { + const clonedObj = {} as T + for (const key in target) { + if (Object.prototype.hasOwnProperty.call(target, key)) { + (clonedObj as any)[key] = deepClone((target as any)[key]) + } + } + return clonedObj + } + + return target +}