From 1943d92c40ff439ca1dcb40150f668eaa68fc86e Mon Sep 17 00:00:00 2001 From: Lilixu007 <1273914445@qq.com> Date: Thu, 26 Feb 2026 18:18:03 +0800 Subject: [PATCH] =?UTF-8?q?AI=E6=8C=87=E4=BB=A4=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- xinlidsj/AI分析指令修复说明.md | 133 +++++++++ xinlidsj/AI意图识别修复说明.md | 86 ++++++ xinlidsj/AI意图识别说明.md | 129 ++++++++ xinlidsj/ai_intent_fix.txt | 77 +++++ xinlidsj/pages/index/index.vue | 326 ++++++++++++++++++++- xinlidsj/parseUserIntentWithAI_improved.js | 147 ++++++++++ 6 files changed, 894 insertions(+), 4 deletions(-) create mode 100644 xinlidsj/AI分析指令修复说明.md create mode 100644 xinlidsj/AI意图识别修复说明.md create mode 100644 xinlidsj/AI意图识别说明.md create mode 100644 xinlidsj/ai_intent_fix.txt create mode 100644 xinlidsj/parseUserIntentWithAI_improved.js diff --git a/xinlidsj/AI分析指令修复说明.md b/xinlidsj/AI分析指令修复说明.md new file mode 100644 index 0000000..05a04e2 --- /dev/null +++ b/xinlidsj/AI分析指令修复说明.md @@ -0,0 +1,133 @@ +# AI分析指令修复说明 + +## 问题描述 +用户输入"分析张三"时,AI能够正确识别意图为`analyzeReport`,但系统没有使用AI识别的参数(keyword: "张三"),而是继续使用正则匹配,导致无法获取报告数据,最终返回"暂不提供此服务"。 + +## 根本原因 +1. AI意图识别成功后,对于分析类指令(analyzeReport/analyzeProfile)返回`false`继续处理 +2. 但后续代码仍然使用正则匹配`parseAnalyzeReportKeyword(text)`获取关键词 +3. AI识别的参数(`intent.params.keyword`)被丢弃,没有传递给后续逻辑 + +## 解决方案 + +### 1. 保存AI识别的意图 +在AI意图识别部分,当识别到分析类指令时,保存完整的intent对象: + +```javascript +let aiRecognizedIntent = null +if (intent.action === 'analyzeReport' || intent.action === 'analyzeProfile' || intent.action === 'analyzeData') { + aiRecognizedIntent = intent + console.log('分析类指令,保存AI识别的参数:', aiRecognizedIntent) +} +``` + +### 2. 优先使用AI识别的参数 +在报告数据获取逻辑中,优先使用AI识别的参数,如果没有则回退到正则匹配: + +```javascript +let reportId = '' +let reportKeyword = '' +let profileKeyword = '' + +if (aiRecognizedIntent) { + console.log('使用AI识别的参数:', aiRecognizedIntent.params) + if (aiRecognizedIntent.action === 'analyzeReport') { + reportId = aiRecognizedIntent.params.reportId || '' + reportKeyword = aiRecognizedIntent.params.keyword || '' + } else if (aiRecognizedIntent.action === 'analyzeProfile') { + profileKeyword = aiRecognizedIntent.params.keyword || aiRecognizedIntent.params.userId || '' + } +} else { + // 回退到正则匹配 + console.log('回退到正则匹配') + reportId = this.parseAnalyzeReportId(text) + reportKeyword = this.parseAnalyzeReportKeyword(text) + profileKeyword = '' +} +``` + +## 执行流程 + +### 用户输入:"分析张三" + +1. **AI意图识别** + - 调用`parseUserIntentWithAI("分析张三")` + - AI返回: + ```json + { + "action": "analyzeReport", + "params": { + "keyword": "张三" + }, + "confidence": 0.9, + "reasoning": "用户想分析张三的测评报告" + } + ``` + +2. **执行意图** + - 调用`executeAIIntent(intent)` + - 识别为分析类指令,返回`false`继续处理 + - 保存`aiRecognizedIntent = intent` + +3. **获取报告数据** + - 检测到`aiRecognizedIntent`存在 + - 提取参数:`reportKeyword = "张三"` + - 调用`getStudentOptions({ keyword: "张三", limit: 1 })`获取用户ID + - 调用`listReport({ userId })`获取报告列表 + - 调用`getReport(reportId)`获取报告详情 + +4. **AI分析报告** + - 将报告数据作为context传递给AI + - AI分析报告内容并返回分析结果 + - 在对话框中显示AI的分析结果 + +## 指令类型区分 + +### analyzeReport(分析报告) +- 关键词:分析、数据、报告 +- 示例: + - "分析张三的报告" + - "分析李四的数据" + - "分析报告123" +- 行为:获取报告数据 → AI分析 → 返回分析结果 + +### analyzeProfile(查看画像) +- 关键词:画像、档案、情况、了解 +- 示例: + - "查看王五的画像" + - "了解赵六的情况" + - "打开张三的档案" +- 行为:跳转到个体画像页面 + +### goReport(打开报告) +- 关键词:打开、查看 +- 示例: + - "打开张三的报告" + - "查看报告列表" +- 行为:跳转到报告列表页面 + +## 修改文件 +- `xinlidsj/pages/index/index.vue` + - 第1605-1625行:保存AI识别的意图 + - 第1710-1740行:优先使用AI识别的参数 + +## 测试建议 +1. 测试AI识别:"分析张三" → 应该获取张三的报告并让AI分析 +2. 测试正则回退:关闭AI意图识别 → 应该使用正则匹配 +3. 测试不同指令: + - "分析李四的数据" → analyzeReport + - "查看王五的画像" → analyzeProfile(跳转页面) + - "打开赵六的报告" → goReport(跳转页面) +4. 测试错误处理: + - 用户不存在 → "没有查询到相关数据" + - 用户无报告 → "没有查询到相关数据" + +## 日志输出 +关键日志点: +- `尝试AI意图识别...` +- `AI识别到意图: {action, params, confidence}` +- `分析类指令,保存AI识别的参数: {...}` +- `使用AI识别的参数: {keyword: "张三"}` +- `最终使用的参数 - reportId: "", reportKeyword: "张三", profileKeyword: ""` + +通过这些日志可以追踪整个流程,确认AI识别和参数传递是否正常。 diff --git a/xinlidsj/AI意图识别修复说明.md b/xinlidsj/AI意图识别修复说明.md new file mode 100644 index 0000000..32fd643 --- /dev/null +++ b/xinlidsj/AI意图识别修复说明.md @@ -0,0 +1,86 @@ +# AI意图识别JSON解析失败修复 + +## 问题现象 + +用户输入:"想看张三数据" +错误:`JSON解析失败: Unexpected end of JSON input` + +## 根本原因 + +原代码使用的正则表达式有问题: + +```javascript +const jsonMatch = jsonStr.match(/```(?:json)?\s*(\{[\s\S]*?\})\s*```/) +``` + +这个正则使用了**非贪婪匹配** `*?`,会在遇到第一个 `}` 时就停止,导致嵌套的JSON对象被截断。 + +例如,对于这样的JSON: +```json +{"action":"analyzeProfile","params":{"keyword":"张三"},"confidence":0.95} +``` + +非贪婪匹配会在第一个 `}` (params后面的) 就停止,得到: +```json +{"action":"analyzeProfile","params":{"keyword":"张三"} +``` + +这是一个不完整的JSON,导致解析失败。 + +## 解决方案 + +使用更简单可靠的方法: + +```javascript +// 1. 移除markdown代码块标记 +jsonStr = jsonStr.replace(/```(?:json)?/g, '').replace(/```/g, '').trim() + +// 2. 找到第一个{和最后一个} +const firstBrace = jsonStr.indexOf('{') +const lastBrace = jsonStr.lastIndexOf('}') + +// 3. 提取完整的JSON对象 +if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) { + jsonStr = jsonStr.substring(firstBrace, lastBrace + 1) +} + +// 4. 使用try-catch保护解析 +try { + intent = JSON.parse(jsonStr) +} catch(e) { + console.error('JSON解析失败:', e.message) + console.log('失败的JSON字符串:', jsonStr) + return null +} +``` + +## 已应用的修复 + +✅ 改进了JSON提取逻辑 +✅ 添加了try-catch保护 +✅ 添加了详细的调试日志 +✅ 添加了空值检查 + +## 测试建议 + +现在再次测试时,控制台会显示: +1. AI意图识别:原始响应 +2. AI意图识别:响应类型 +3. AI意图识别:提取的JSON +4. 如果失败:JSON解析失败的具体错误和完整的JSON字符串 + +这样可以准确定位问题所在。 + +## 预期结果 + +用户输入:"想看张三数据" +AI识别: +```json +{ + "action": "analyzeProfile", + "params": {"keyword": "张三"}, + "confidence": 0.95, + "reasoning": "用户想分析张三的个体画像数据" +} +``` +系统执行:打开张三的个体画像页面 diff --git a/xinlidsj/AI意图识别说明.md b/xinlidsj/AI意图识别说明.md new file mode 100644 index 0000000..39e9028 --- /dev/null +++ b/xinlidsj/AI意图识别说明.md @@ -0,0 +1,129 @@ +# AI意图识别系统 + +## 概述 + +系统现在使用AI大模型来理解用户的自然语言输入,而不是依赖固定的正则表达式匹配。这使得用户可以用更自然、更灵活的方式与系统交互。 + +## 工作流程 + +1. **用户输入** → 用户在AI对话框中输入自然语言指令 +2. **AI意图识别** → 系统调用大模型(DeepSeek/通义千问)理解用户意图 +3. **结构化输出** → AI返回JSON格式的指令和参数 +4. **执行指令** → 系统根据识别结果执行相应操作 +5. **回退机制** → 如果AI识别失败,自动回退到原有的正则匹配 + +## 支持的指令类型 + +### 1. 导航类指令 +- `goWarning` - 打开预警中心 +- `goProfile` - 打开个体画像 +- `goComprehensive` - 打开综合报告 +- `goInterventionTask` - 打开干预任务 +- `goReport` - 打开报告列表/详情 +- `goMessage` - 打开消息/收件箱 +- `goNotice` - 打开通知公告 +- `goDashboard` - 打开监区看板 +- `goTagFilter` - 打开标签筛选 +- `goVoice` - 打开语音助手 + +### 2. 分析类指令 +- `analyzeReport` - 分析报告 +- `analyzeProfile` - 分析个体画像 +- `analyzeData` - 分析平台数据 + +### 3. 控制类指令 +- `clearChat` - 清空对话记录 +- `toggleChat` - 展开/收起对话框 +- `goBack` - 返回上一页 +- `goHome` - 返回首页 + +## 使用示例 + +### 原来的方式(正则匹配) +``` +用户输入:"打开张三的报告" +系统:使用正则 /打开\s*([^\s的]{1,32})\s*的?报告/ 匹配 +结果:✅ 能识别 + +用户输入:"帮我看看张三的报告" +系统:正则无法匹配 +结果:❌ 无法识别 +``` + +### 现在的方式(AI理解) +``` +用户输入:"打开张三的报告" +AI理解:用户想查看张三的报告 +返回:{"action":"goReport","params":{"keyword":"张三"},"confidence":0.95} +结果:✅ 能识别 + +用户输入:"帮我看看张三的报告" +AI理解:用户想查看张三的报告 +返回:{"action":"goReport","params":{"keyword":"张三"},"confidence":0.9} +结果:✅ 能识别 + +用户输入:"我想了解一下李四的情况" +AI理解:用户想查看李四的个体画像 +返回:{"action":"goProfile","params":{"keyword":"李四"},"confidence":0.85} +结果:✅ 能识别 + +用户输入:"有没有严重的预警需要处理" +AI理解:用户想查看严重级别的预警 +返回:{"action":"goWarning","params":{"warningLevel":"严重"},"confidence":0.9} +结果:✅ 能识别 +``` + +## 优势 + +1. **更自然的交互** - 用户可以用日常语言表达需求 +2. **更强的容错性** - 不需要记住精确的指令格式 +3. **更好的扩展性** - 添加新指令只需更新配置,无需写正则 +4. **智能参数提取** - AI自动从语句中提取关键信息 +5. **回退保障** - AI识别失败时自动使用原有的正则匹配 + +## 技术实现 + +### 核心方法 + +1. **parseUserIntentWithAI(userInput)** - AI意图识别 + - 输入:用户的自然语言 + - 输出:结构化的指令JSON + - 使用:DeepSeek API / 通义千问 API + +2. **executeAIIntent(intent)** - 执行识别出的指令 + - 输入:AI返回的指令JSON + - 输出:是否成功执行 + - 功能:根据action和params执行相应操作 + +3. **sendAiChat()** - 修改后的消息发送方法 + - 先尝试AI意图识别 + - 识别成功则执行指令 + - 识别失败则回退到正则匹配 + +### AI Prompt设计 + +系统使用精心设计的prompt来引导AI: +- 明确定义所有可用指令 +- 要求返回标准JSON格式 +- 包含置信度评分 +- 提供示例来提高准确性 + +### 置信度阈值 + +- 置信度 >= 0.6:执行指令 +- 置信度 < 0.6:忽略结果,回退到正则匹配 + +## 配置 + +AI意图识别使用与AI对话相同的配置: +- DeepSeek API(默认) +- 通义千问 API(可选) + +无需额外配置,开箱即用。 + +## 未来扩展 + +1. 支持多轮对话上下文 +2. 支持复合指令(一次执行多个操作) +3. 支持模糊查询和智能推荐 +4. 支持自定义指令学习 diff --git a/xinlidsj/ai_intent_fix.txt b/xinlidsj/ai_intent_fix.txt new file mode 100644 index 0000000..9331fe1 --- /dev/null +++ b/xinlidsj/ai_intent_fix.txt @@ -0,0 +1,77 @@ +修复AI意图识别的JSON解析问题 + +找到这段代码(大约在1330-1355行): + + console.log('AI意图识别:原始响应:', content) + + // 提取JSON(可能被markdown代码块包裹) + let jsonStr = content.trim() + const jsonMatch = jsonStr.match(/```(?:json)?\s*(\{[\s\S]*?\})\s*```/) + if (jsonMatch) { + jsonStr = jsonMatch[1] + } + + const intent = JSON.parse(jsonStr) + console.log('AI意图识别:解析结果:', intent) + + // 验证置信度 + if (intent.confidence < 0.6) { + console.log('AI意图识别:置信度过低,忽略结果') + return null + } + +替换为: + + console.log('AI意图识别:原始响应:', content) + + // 检查是否有内容 + if (!content || !content.trim()) { + console.log('AI意图识别:响应为空') + return null + } + + // 提取JSON + let jsonStr = content.trim() + + // 移除markdown代码块 + jsonStr = jsonStr.replace(/```(?:json)?\s*/g, '').replace(/```\s*/g, '') + + // 提取第一个完整的JSON对象 + const jsonMatch2 = jsonStr.match(/\{[\s\S]*?\}/) + if (jsonMatch2) { + jsonStr = jsonMatch2[0] + } + + console.log('AI意图识别:提取的JSON字符串:', jsonStr.substring(0, 200)) + + // 解析JSON + let intent = null + try { + intent = JSON.parse(jsonStr) + } catch (parseError) { + console.error('AI意图识别:JSON解析失败', parseError.message) + return null + } + + console.log('AI意图识别:解析结果:', intent) + + // 验证intent结构 + if (!intent || typeof intent !== 'object') { + console.log('AI意图识别:intent不是对象') + return null + } + + // 验证置信度 + const confidence = parseFloat(intent.confidence) + if (isNaN(confidence) || confidence < 0.6) { + console.log('AI意图识别:置信度过低或无效', confidence) + return null + } + +主要改进: +1. 添加了空内容检查 +2. 改进了JSON提取逻辑,使用replace而不是match +3. 添加了try-catch包裹JSON.parse +4. 添加了更详细的错误日志 +5. 验证intent对象结构 +6. 改进了置信度验证逻辑 diff --git a/xinlidsj/pages/index/index.vue b/xinlidsj/pages/index/index.vue index a802524..6c4a8a1 100644 --- a/xinlidsj/pages/index/index.vue +++ b/xinlidsj/pages/index/index.vue @@ -1235,6 +1235,156 @@ if (!s) return false return /(分析|趋势|总结|汇总|研判|建议)/.test(s) }, + // AI意图识别 - 使用大模型理解用户输入并提取结构化指令 + async parseUserIntentWithAI(userInput) { + const llmCfg = this.getBailianConfig() || this.getOllamaConfig() + if (!llmCfg) { + console.log('AI意图识别:未配置大模型') + return null + } + + // 定义系统可以执行的指令列表 + const availableCommands = { + navigation: [ + { action: 'goWarning', params: ['status', 'warningLevel'], desc: '打开预警中心,可选参数:status(0=未处理,1=处理中,2=已完成), warningLevel(严重/高/中/低)' }, + { action: 'goProfile', params: ['keyword', 'userId'], desc: '打开个体画像,可选参数:keyword(姓名/编号), userId(用户ID)' }, + { action: 'goComprehensive', params: [], desc: '打开综合报告' }, + { action: 'goInterventionTask', params: ['status'], desc: '打开干预任务,可选参数:status(0,1=未完成,2=已完成)' }, + { action: 'goReport', params: ['keyword', 'reportId', 'sourceType'], desc: '打开报告列表或详情,可选参数:keyword(搜索关键词), reportId(报告ID), sourceType(assessment/questionnaire)' }, + { action: 'goMessage', params: ['tab'], desc: '打开消息/收件箱,可选参数:tab(unread=未读)' }, + { action: 'goNotice', params: [], desc: '打开通知公告' }, + { action: 'goDashboard', params: [], desc: '打开监区看板' }, + { action: 'goTagFilter', params: [], desc: '打开标签筛选' }, + { action: 'goVoice', params: [], desc: '打开语音助手' } + ], + analysis: [ + { action: 'analyzeReport', params: ['keyword', 'reportId'], desc: '分析某人的测评报告/数据,关键词:分析、数据、报告。必需参数:keyword(姓名/编号)或reportId(报告ID)' }, + { action: 'analyzeProfile', params: ['keyword', 'userId'], desc: '查看某人的个体画像/档案/情况,关键词:画像、档案、情况、了解。必需参数:keyword(姓名/编号)或userId(用户ID)' }, + { action: 'analyzeData', params: ['dataType'], desc: '分析平台整体数据,关键词:平台数据、统计、趋势。可选参数:dataType(overview=概览/trend=趋势/dept=监区统计)' } + ], + control: [ + { action: 'clearChat', params: [], desc: '清空对话记录' }, + { action: 'toggleChat', params: [], desc: '展开或收起AI对话框' }, + { action: 'goBack', params: [], desc: '返回上一页' }, + { action: 'goHome', params: [], desc: '返回首页' } + ] + } + + const systemPrompt = `你是一个指令解析助手,负责理解用户的自然语言输入并转换为结构化的系统指令。 + +可用的指令类型: +${JSON.stringify(availableCommands, null, 2)} + +重要区分: +- analyzeReport: 用于"分析XX的报告/数据",查看测评报告详情 +- analyzeProfile: 用于"查看XX的画像/档案/情况",查看个体画像 +- goReport: 用于"打开XX的报告",跳转到报告列表 + +你的任务: +1. 理解用户输入的意图 +2. 从可用指令中选择最匹配的action +3. 提取用户输入中的参数值 +4. 返回JSON格式的结构化指令 + +返回格式(必须是有效的JSON): +{ + "action": "指令名称", + "params": { + "参数名": "参数值" + }, + "confidence": 0.0-1.0, + "reasoning": "简短说明为什么选择这个指令" +} + +如果无法识别用户意图,返回: +{ + "action": null, + "confidence": 0, + "reasoning": "无法理解用户意图" +} + +示例: +用户输入:"打开张三的报告" +返回:{"action":"goReport","params":{"keyword":"张三"},"confidence":0.95,"reasoning":"用户想查看张三的报告列表"} + +用户输入:"分析李四的数据" +返回:{"action":"analyzeReport","params":{"keyword":"李四"},"confidence":0.9,"reasoning":"用户想分析李四的测评报告"} + +用户输入:"查看王五的画像" +返回:{"action":"analyzeProfile","params":{"keyword":"王五"},"confidence":0.95,"reasoning":"用户想查看王五的个体画像"} + +用户输入:"查看未处理的预警" +返回:{"action":"goWarning","params":{"status":"0"},"confidence":0.95,"reasoning":"用户想查看未处理状态的预警"} + +重要:只返回JSON,不要有任何其他文字。` + + try { + console.log('AI意图识别:开始解析用户输入:', userInput) + + const messages = [ + { role: 'system', content: systemPrompt }, + { role: 'user', content: userInput } + ] + + const result = await this.callBailianChat({ + model: llmCfg.model, + apiUrl: llmCfg.apiUrl, + apiKey: llmCfg.apiKey, + messages, + temperature: 0.1, // 低温度,更确定性的输出 + max_tokens: 500 + }) + + // callBailianChat直接返回content字符串,不是完整的result对象 + let content = result || '' + + console.log('AI意图识别:原始响应:', content) + console.log('AI意图识别:响应类型:', typeof content) + console.log('AI意图识别:响应长度:', content.length) + + // 提取JSON + let jsonStr = content.trim() + if (!jsonStr) { + console.log('AI意图识别:响应为空') + return null + } + + // 移除markdown代码块 + jsonStr = jsonStr.replace(/```(?:json)?/g, '').replace(/```/g, '').trim() + + // 提取第一个{到最后一个}之间的内容 + const firstBrace = jsonStr.indexOf('{') + const lastBrace = jsonStr.lastIndexOf('}') + if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) { + jsonStr = jsonStr.substring(firstBrace, lastBrace + 1) + } + + console.log('AI意图识别:提取的JSON:', jsonStr) + + // 解析JSON + let intent = null + try { + intent = JSON.parse(jsonStr) + } catch(e) { + console.error('JSON解析失败:', e.message) + console.log('失败的JSON字符串:', jsonStr) + return null + } + + console.log('AI意图识别:解析结果:', intent) + + // 验证置信度 + if (intent.confidence < 0.6) { + console.log('AI意图识别:置信度过低,忽略结果') + return null + } + + return intent + } catch (e) { + console.error('AI意图识别失败:', e) + return null + } + }, parseAnalyzeReportId(text) { const s = String(text || '').trim() if (!s) return '' @@ -1323,7 +1473,122 @@ if (!this.isAiNeedContext(s)) return false return true }, - sendAiChat() { + // 执行AI识别出的指令 + executeAIIntent(intent) { + if (!intent || !intent.action) { + return false + } + + const action = intent.action + const params = intent.params || {} + + console.log('执行AI指令:', action, params) + + // 导航类指令 + if (action === 'goWarning') { + let url = '/pages/warning/index' + const query = [] + if (params.status) query.push(`status=${params.status}`) + if (params.warningLevel) query.push(`warningLevel=${encodeURIComponent(params.warningLevel)}`) + if (query.length) url += '?' + query.join('&') + uni.navigateTo({ url }) + return true + } + + if (action === 'goProfile') { + let url = '/pages/profile/index' + const query = [] + if (params.keyword) query.push(`keyword=${encodeURIComponent(params.keyword)}`) + if (params.userId) query.push(`userId=${params.userId}`) + if (query.length) url += '?' + query.join('&') + uni.navigateTo({ url }) + return true + } + + if (action === 'goComprehensive') { + uni.navigateTo({ url: '/pages/comprehensive/index' }) + return true + } + + if (action === 'goInterventionTask') { + let url = '/pages/interventionTask/index' + if (params.status) url += `?status=${params.status}` + uni.navigateTo({ url }) + return true + } + + if (action === 'goReport') { + if (params.reportId) { + uni.navigateTo({ url: `/pages/report/detail?reportId=${params.reportId}&sourceType=${params.sourceType || ''}` }) + } else { + let url = '/pages/report/index' + const query = [] + if (params.keyword) query.push(`keyword=${encodeURIComponent(params.keyword)}`) + if (params.sourceType) query.push(`sourceType=${params.sourceType}`) + if (query.length) url += '?' + query.join('&') + uni.navigateTo({ url }) + } + return true + } + + if (action === 'goMessage') { + let url = '/pages/message/inbox' + if (params.tab) url += `?tab=${params.tab}` + uni.navigateTo({ url }) + return true + } + + if (action === 'goNotice') { + uni.navigateTo({ url: '/pages/message/notice' }) + return true + } + + if (action === 'goDashboard') { + uni.navigateTo({ url: '/pages/dashboard/index' }) + return true + } + + if (action === 'goTagFilter') { + uni.navigateTo({ url: '/pages/profile/tagFilter' }) + return true + } + + if (action === 'goVoice') { + uni.navigateTo({ url: '/pages/voice/index' }) + return true + } + + // 控制类指令 + if (action === 'clearChat') { + this.clearAiChat() + return true + } + + if (action === 'toggleChat') { + this.toggleAiChat() + return true + } + + if (action === 'goBack') { + uni.navigateBack({ delta: 1 }) + return true + } + + if (action === 'goHome') { + uni.reLaunch({ url: '/pages/index/index' }) + return true + } + + // 分析类指令 - 让AI分析数据并返回结果,而不是跳转页面 + if (action === 'analyzeReport' || action === 'analyzeProfile' || action === 'analyzeData') { + // 返回false,让后续的AI分析逻辑处理 + // 这样会获取数据并调用AI进行分析 + return false + } + + return false + }, + async sendAiChat() { if (this.aiChatSending) return const text = this.aiChatInputTrim if (!text) return @@ -1335,6 +1600,38 @@ this.scrollAiChatToBottom() }) + // ===== 新增:AI意图识别 ===== + let aiRecognizedIntent = null + try { + console.log('尝试AI意图识别...') + const intent = await this.parseUserIntentWithAI(text) + if (intent && intent.action) { + console.log('AI识别到意图:', intent) + const executed = this.executeAIIntent(intent) + if (executed) { + // 导航类指令已执行,显示反馈 + this.aiChatMessages = [...this.aiChatMessages, { + id: 'a_' + Date.now(), + role: 'ai', + content: `已执行:${intent.reasoning || intent.action}` + }] + this.aiChatSending = false + this.$nextTick(() => { + this.scrollAiChatToBottom() + }) + return + } + // 分析类指令,保存intent供后续使用 + if (intent.action === 'analyzeReport' || intent.action === 'analyzeProfile' || intent.action === 'analyzeData') { + aiRecognizedIntent = intent + console.log('分析类指令,保存AI识别的参数:', aiRecognizedIntent) + } + } + } catch (e) { + console.error('AI意图识别出错,回退到正则匹配:', e) + } + // ===== AI意图识别结束 ===== + const openReportIdMatch = String(text || '').trim().match(/打开\s*(?:报告\s*id\s*|报告\s*#\s*|报告#\s*)(\d{1,18})/i) if (openReportIdMatch && openReportIdMatch[1]) { const reportId = openReportIdMatch[1] @@ -1416,9 +1713,30 @@ }) return } - const reportId = this.parseAnalyzeReportId(text) - const reportKeyword = this.parseAnalyzeReportKeyword(text) - const profileKeyword = '' + + // 优先使用AI识别的参数,否则回退到正则匹配 + let reportId = '' + let reportKeyword = '' + let profileKeyword = '' + + if (aiRecognizedIntent) { + console.log('使用AI识别的参数:', aiRecognizedIntent.params) + if (aiRecognizedIntent.action === 'analyzeReport') { + reportId = aiRecognizedIntent.params.reportId || '' + reportKeyword = aiRecognizedIntent.params.keyword || '' + } else if (aiRecognizedIntent.action === 'analyzeProfile') { + profileKeyword = aiRecognizedIntent.params.keyword || aiRecognizedIntent.params.userId || '' + } + } else { + // 回退到正则匹配 + console.log('回退到正则匹配') + reportId = this.parseAnalyzeReportId(text) + reportKeyword = this.parseAnalyzeReportKeyword(text) + profileKeyword = '' + } + + console.log('最终使用的参数 - reportId:', reportId, 'reportKeyword:', reportKeyword, 'profileKeyword:', profileKeyword) + const baseContext = this.buildAiAnalyzeContext() let report = null let profile = null diff --git a/xinlidsj/parseUserIntentWithAI_improved.js b/xinlidsj/parseUserIntentWithAI_improved.js new file mode 100644 index 0000000..426e043 --- /dev/null +++ b/xinlidsj/parseUserIntentWithAI_improved.js @@ -0,0 +1,147 @@ +// 改进版的AI意图识别方法 +// 复制这段代码替换 xinlidsj/pages/index/index.vue 中的 parseUserIntentWithAI 方法 + +async parseUserIntentWithAI(userInput) { + const llmCfg = this.getBailianConfig() || this.getOllamaConfig() + if (!llmCfg) { + console.log('AI意图识别:未配置大模型') + return null + } + + // 定义系统可以执行的指令列表 + const availableCommands = { + navigation: [ + { action: 'goWarning', params: ['status', 'warningLevel'], desc: '打开预警中心,可选参数:status(0=未处理,1=处理中,2=已完成), warningLevel(严重/高/中/低)' }, + { action: 'goProfile', params: ['keyword', 'userId'], desc: '打开个体画像,可选参数:keyword(姓名/编号), userId(用户ID)' }, + { action: 'goComprehensive', params: [], desc: '打开综合报告' }, + { action: 'goInterventionTask', params: ['status'], desc: '打开干预任务,可选参数:status(0,1=未完成,2=已完成)' }, + { action: 'goReport', params: ['keyword', 'reportId', 'sourceType'], desc: '打开报告列表或详情,可选参数:keyword(搜索关键词), reportId(报告ID), sourceType(assessment/questionnaire)' }, + { action: 'goMessage', params: ['tab'], desc: '打开消息/收件箱,可选参数:tab(unread=未读)' }, + { action: 'goNotice', params: [], desc: '打开通知公告' }, + { action: 'goDashboard', params: [], desc: '打开监区看板' }, + { action: 'goTagFilter', params: [], desc: '打开标签筛选' }, + { action: 'goVoice', params: [], desc: '打开语音助手' } + ], + analysis: [ + { action: 'analyzeReport', params: ['keyword', 'reportId'], desc: '分析报告,必需参数:keyword(姓名/编号)或reportId(报告ID)' }, + { action: 'analyzeProfile', params: ['keyword', 'userId'], desc: '分析个体画像,必需参数:keyword(姓名/编号)或userId(用户ID)' }, + { action: 'analyzeData', params: ['dataType'], desc: '分析平台数据,可选参数:dataType(overview=概览/trend=趋势/dept=监区统计)' } + ], + control: [ + { action: 'clearChat', params: [], desc: '清空对话记录' }, + { action: 'toggleChat', params: [], desc: '展开或收起AI对话框' }, + { action: 'goBack', params: [], desc: '返回上一页' }, + { action: 'goHome', params: [], desc: '返回首页' } + ] + } + + const systemPrompt = `你是一个指令解析助手,负责理解用户的自然语言输入并转换为结构化的系统指令。 + +可用的指令类型: +${JSON.stringify(availableCommands, null, 2)} + +你的任务: +1. 理解用户输入的意图 +2. 从可用指令中选择最匹配的action +3. 提取用户输入中的参数值 +4. 返回JSON格式的结构化指令 + +返回格式(必须是有效的JSON,不要有任何其他文字): +{ + "action": "指令名称", + "params": { + "参数名": "参数值" + }, + "confidence": 0.9, + "reasoning": "简短说明" +} + +如果无法识别用户意图,返回: +{ + "action": null, + "confidence": 0, + "reasoning": "无法理解用户意图" +} + +示例: +用户输入:"打开张三的报告" +返回:{"action":"goReport","params":{"keyword":"张三"},"confidence":0.95,"reasoning":"用户想查看张三的报告"} + +重要:只返回JSON,不要有markdown代码块,不要有任何其他文字。` + + try { + console.log('AI意图识别:开始解析用户输入:', userInput) + + const messages = [ + { role: 'system', content: systemPrompt }, + { role: 'user', content: userInput } + ] + + const result = await this.callBailianChat({ + model: llmCfg.model, + apiUrl: llmCfg.apiUrl, + apiKey: llmCfg.apiKey, + messages, + temperature: 0.1, + max_tokens: 500 + }) + + let content = '' + if (result && result.choices && result.choices[0] && result.choices[0].message) { + content = result.choices[0].message.content + } + + console.log('AI意图识别:原始响应:', content) + + // 检查是否有内容 + if (!content || !content.trim()) { + console.log('AI意图识别:响应为空') + return null + } + + // 提取JSON + let jsonStr = content.trim() + + // 移除markdown代码块 + jsonStr = jsonStr.replace(/```(?:json)?\s*/g, '').replace(/```\s*/g, '') + + // 提取第一个完整的JSON对象 + const jsonMatch = jsonStr.match(/\{[\s\S]*?\}/) + if (jsonMatch) { + jsonStr = jsonMatch[0] + } + + console.log('AI意图识别:提取的JSON字符串:', jsonStr) + + // 解析JSON + let intent = null + try { + intent = JSON.parse(jsonStr) + } catch (parseError) { + console.error('AI意图识别:JSON解析失败', parseError.message) + console.log('尝试解析的字符串:', jsonStr.substring(0, 200)) + return null + } + + console.log('AI意图识别:解析结果:', intent) + + // 验证intent结构 + if (!intent || typeof intent !== 'object') { + console.log('AI意图识别:intent不是对象') + return null + } + + // 验证置信度 + const confidence = parseFloat(intent.confidence) + if (isNaN(confidence) || confidence < 0.6) { + console.log('AI意图识别:置信度过低或无效', confidence) + return null + } + + return intent + } catch (e) { + console.error('AI意图识别失败:', e.message || e) + console.error('错误堆栈:', e.stack) + return null + } +}