欢迎光临
我们一直在努力

鸿蒙Electron框架下鸿蒙PC——命枢AI生命科学模拟器 - 症状诊断功能实现与UI设计详解

欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/

Atomgit仓库地址:https://atomgit.com/feng8403000/mingshu_ai

匹配视频链接: https://www.bilibili.com/video/BV1gw5b6vEsa

一、功能概述

症状诊断系统是命枢AI生命科学模拟器的核心功能之一,位于左侧面板的"病原体与侵害因子"区域。该系统通过自然语言处理技术,让用户能够输入身体症状描述,系统自动分析可能的感染类型并计算感染概率,为后续的免疫细胞对抗模拟提供数据支撑。

1.1 功能定位

症状诊断系统在整体架构中的作用:

层级功能说明
输入层 症状采集 用户输入或选择症状标签
处理层 AI分析 调用DeepSeek-V3模型进行症状分析
输出层 诊断结果 展示可能的感染源及其概率
应用层 战场集成 将诊断结果转化为对战系统中的病原体

1.2 核心特性

  • 智能症状识别:支持手动输入和快捷标签选择两种方式
  • AI驱动分析:基于大语言模型进行专业医疗分析
  • 概率排序展示:按照感染概率从高到低排列可能的病原体
  • 一键集成:诊断结果可直接添加到对战系统中
  • 多重诊断模式:提供症状诊断和AI问诊两种模式

二、UI设计详解

2.1 整体布局

症状诊断区域位于左侧面板顶部,采用卡片式设计,包含以下几个功能区域:

┌─────────────────────────────────────┐
│ 🤒 症状诊断 │
├─────────────────────────────────────┤
│ [输入框] 输入身体反应,如:发热、… │
├─────────────────────────────────────┤
│ 常见症状: │
│ 🌡️发热 😣头痛 🤧咳嗽 😫乏力 … │
├─────────────────────────────────────┤
│ [🤖 AI诊断] [❓ AI问诊] │
├─────────────────────────────────────┤
│ [诊断结果区域] │
└─────────────────────────────────────┘

2.2 核心组件设计

2.2.1 症状输入框

输入框采用深色主题设计,与整体暗黑风格保持一致:

.symptom-input {
width: 100%;
height: 80px;
padding: 12px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
color: #fff;
font-size: 14px;
resize: none;
}

设计要点:

  • 背景透明度为0.05,保持深色主题的层次感
  • 边框采用半透明白色,增加视觉深度
  • 圆角设计(8px),符合现代UI设计趋势
2.2.2 常见症状标签

常见症状标签采用可点击的标签按钮形式,支持快速选择:

<div class="symptoms-tags">
<span class="symptom-tag" onclick="addSymptom('发热')">🌡️ 发热</span>
<span class="symptom-tag" onclick="addSymptom('头痛')">😣 头痛</span>
<span class="symptom-tag" onclick="addSymptom('咳嗽')">🤧 咳嗽</span>
<span class="symptom-tag" onclick="addSymptom('乏力')">😫 乏力</span>
<span class="symptom-tag" onclick="addSymptom('恶心')">🤢 恶心</span>
<span class="symptom-tag" onclick="addSymptom('腹泻')">💩 腹泻</span>
<span class="symptom-tag" onclick="addSymptom('呼吸困难')">😮‍💨 呼吸困难</span>
<span class="symptom-tag" onclick="addSymptom('肌肉酸痛')">💪 肌肉酸痛</span>
<span class="symptom-tag" onclick="addSymptom('喉咙痛')">🤒 喉咙痛</span>
<span class="symptom-tag" onclick="addSymptom('鼻塞')">👃 鼻塞</span>
<span class="symptom-tag" onclick="addSymptom('呕吐')">🤮 呕吐</span>
<span class="symptom-tag" onclick="addSymptom('头晕')">😵 头晕</span>
</div>

标签样式设计:

.symptom-tag {
display: inline-block;
padding: 6px 12px;
margin: 4px;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 20px;
font-size: 13px;
cursor: pointer;
transition: all 0.2s ease;
}

.symptom-tag:hover {
background: rgba(78, 205, 196, 0.2);
border-color: rgba(78, 205, 196, 0.5);
}

设计特点:

  • 使用emoji图标增强视觉识别性
  • 圆角胶囊形状(20px),现代感十足
  • 悬停效果增强交互反馈
2.2.3 诊断按钮

诊断区域包含两个核心按钮:

按钮样式功能
AI诊断 青绿色渐变 基于症状进行病原体分析
AI问诊 金黄色渐变 打开AI问答问诊模态框

按钮样式代码:

.analyze-btn {
width: 100%;
padding: 12px;
margin-top: 10px;
background: linear-gradient(135deg, #4ecdc4, #44a08d);
border: none;
border-radius: 8px;
color: #fff;
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}

.analyze-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(78, 205, 196, 0.4);
}

.analyze-btn.diagnosis {
background: linear-gradient(135deg, #fdcb6e, #f39c12);
color: #1a1a2e;
}

2.2.4 诊断结果展示

诊断结果采用卡片列表形式展示,每个卡片包含:

<div class="infection-option" data-index="0">
<div class="infection-header">
<span class="infection-icon">🦠</span>
<span class="infection-name">流感病毒</span>
<span class="infection-prob">85%</span>
</div>
<div class="infection-details">
<span class="infection-type">病毒</span>
<span class="infection-severity">中度</span>
</div>
<div class="infection-reason">症状与流感典型表现高度匹配</div>
<div class="infection-checkbox"></div>
</div>

设计要点:

  • 使用颜色区分概率等级(红色>70%,橙色40-70%,绿色<40%)
  • 使用颜色区分严重程度(红色重度,橙色中度,绿色轻度)
  • 支持多选和一键添加到战场

三、核心代码实现

3.1 症状添加功能

当用户点击症状标签时,触发addSymptom()函数:

function addSymptom(symptom) {
const input = document.getElementById('symptomInput');
const currentValue = input.value.trim();
if (currentValue) {
input.value = currentValue + '、' + symptom;
} else {
input.value = symptom;
}
}

实现逻辑:

  • 获取输入框当前值
  • 如果已有内容,使用顿号连接新症状
  • 如果为空,直接设置新症状
  • 3.2 AI查询核心函数

    queryAI()是与AI模型交互的核心函数:

    async function queryAI(prompt) {
    try {
    console.log('========== AI 请求开始 (queryAI) ==========');
    console.log('API_URL:', API_URL);
    console.log('API_KEY:', API_KEY.substring(0, 10) + '…');
    console.log('模型: deepseek-ai/DeepSeek-V3');
    console.log('prompt长度:', prompt.length, '字符');
    console.log('prompt预览:', prompt.substring(0, 100) + '…');

    const response = await fetch(API_URL, {
    method: 'POST',
    headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Content-Type': 'application/json'
    },
    body: JSON.stringify({
    model: "deepseek-ai/DeepSeek-V3",
    messages: [{ role: "user", content: prompt }],
    stream: false,
    max_tokens: 2048,
    temperature: 0.6,
    top_p: 0.95,
    top_k: 50,
    frequency_penalty: 0,
    thinking_budget: 2048
    })
    });

    console.log('HTTP状态码:', response.status);

    if (!response.ok) {
    const errorText = await response.text();
    console.error('HTTP错误响应:', errorText);
    throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`);
    }

    const contentType = response.headers.get('content-type') || '';
    let fullResponse = '';
    let rawResponse = '';

    if (contentType.includes('text/event-stream') || contentType.includes('stream')) {
    console.log('检测到流式响应,使用SSE解析');
    const reader = response.body.getReader();
    const decoder = new TextDecoder('utf-8');

    while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    const chunk = decoder.decode(value);
    rawResponse += chunk;
    const lines = chunk.split('\\n');

    for (const line of lines) {
    if (!line.startsWith('data:')) continue;
    if (line.trim() === 'data:[DONE]') break;

    try {
    const jsonStr = line.replace(/^data:\\s*/, '').trim();
    if (jsonStr) {
    const data = JSON.parse(jsonStr);
    if (data.choices && data.choices[0]) {
    if (data.choices[0].delta) {
    fullResponse += data.choices[0].delta.content || '';
    } else if (data.choices[0].message) {
    fullResponse += data.choices[0].message.content || '';
    }
    }
    }
    } catch (e) {
    console.warn('SSE解析错误:', e);
    }
    }
    }
    } else {
    console.log('检测到非流式响应,使用普通解析');
    rawResponse = await response.text();
    console.log('原始响应文本:', rawResponse);

    try {
    const jsonData = JSON.parse(rawResponse);
    if (jsonData.choices && jsonData.choices[0]) {
    if (jsonData.choices[0].delta) {
    fullResponse = jsonData.choices[0].delta.content || '';
    } else if (jsonData.choices[0].message) {
    fullResponse = jsonData.choices[0].message.content || '';
    }
    }
    } catch (e) {
    console.warn('JSON解析失败,尝试作为纯文本处理:', e);
    fullResponse = rawResponse;
    }
    }

    console.log('========== AI 响应解析结果 ==========');
    console.log('提取的文本内容:', fullResponse || '(空)');
    console.log('==========================================');

    return fullResponse;
    } catch (error) {
    console.error('========== AI 请求失败 ==========');
    console.error('错误:', error.message);
    console.log('==========================================');
    return null;
    }
    }

    核心配置参数说明:

    参数值说明
    model deepseek-ai/DeepSeek-V3 使用的AI模型
    max_tokens 2048 最大输出token数
    temperature 0.6 温度参数,控制输出随机性
    top_p 0.95 核采样参数
    top_k 50 Top-K采样
    stream false 是否流式响应

    3.3 症状分析主函数

    analyzeSymptoms()是症状诊断的入口函数:

    async function analyzeSymptoms() {
    const symptoms = document.getElementById('symptomInput').value;
    const resultEl = document.getElementById('symptomResult');
    const statusEl = document.getElementById('symptomStatus');

    if (!symptoms.trim()) {
    alert('请输入身体反应症状');
    return;
    }

    statusEl.textContent = 'AI分析中…';
    statusEl.className = 'symptom-status loading';
    resultEl.classList.remove('show');

    const prompt = `根据以下身体症状,请分析可能感染的病毒或细菌。请返回3-5种最可能的感染情况,按照概率从高到低排序。

    症状:${symptoms}

    只返回纯JSON,不要包含任何其他文本。严格按照以下格式返回:
    {
    "possibleInfections": [
    {
    "name": "流感病毒",
    "type": "virus",
    "probability": 0.85,
    "symptoms": ["发热", "咳嗽", "头痛"],
    "severity": "中度",
    "matchingReason": "症状与流感典型表现高度匹配"
    }
    ]
    }

    注意:
    1. 只返回JSON,不要有其他任何文字说明
    2. type字段只能是"virus"或"bacteria"
    3. probability必须是0-1之间的小数
    4. severity只能是"轻度"、"中度"或"重度"
    5. 确保JSON格式正确,没有语法错误`;

    const result = await queryAI(prompt);

    console.log('========== 症状诊断 AI 返回 ==========');
    console.log('用户输入症状:', symptoms);
    console.log('AI返回原始文本:', result);
    console.log('======================================================');

    if (result && result.trim()) {
    try {
    const parsedData = parseAIResponse(result);

    console.log('解析后的数据:', parsedData);

    if (parsedData && parsedData.possibleInfections &&
    Array.isArray(parsedData.possibleInfections) &&
    parsedData.possibleInfections.length > 0) {
    symptomAnalysisResult = parsedData;
    displaySymptomAnalysis(parsedData);
    statusEl.textContent = '分析完成';
    statusEl.className = 'symptom-status success';
    resultEl.classList.add('show');
    } else {
    console.log('AI返回数据无效,使用默认数据');
    symptomAnalysisResult = { possibleInfections: defaultInfections };
    displaySymptomAnalysis(symptomAnalysisResult);
    statusEl.textContent = '使用默认数据';
    statusEl.className = 'symptom-status warning';
    resultEl.classList.add('show');
    }
    } catch (e) {
    console.log('解析失败,使用默认数据:', e);
    symptomAnalysisResult = { possibleInfections: defaultInfections };
    displaySymptomAnalysis(symptomAnalysisResult);
    statusEl.textContent = '使用默认数据';
    statusEl.className = 'symptom-status warning';
    resultEl.classList.add('show');
    }
    } else {
    console.log('AI返回空数据,使用默认数据');
    symptomAnalysisResult = { possibleInfections: defaultInfections };
    displaySymptomAnalysis(symptomAnalysisResult);
    statusEl.textContent = '使用默认数据';
    statusEl.className = 'symptom-status warning';
    resultEl.classList.add('show');
    }
    }

    执行流程:

  • 获取用户输入的症状
  • 验证输入是否为空
  • 设置加载状态
  • 构造AI提示词
  • 调用AI接口
  • 解析响应数据
  • 展示结果或使用默认数据
  • 3.4 AI响应解析

    parseAIResponse()函数负责解析AI返回的数据:

    function parseAIResponse(response) {
    try {
    console.log('原始AI响应:', response);
    let jsonStr = response.trim();

    jsonStr = jsonStr.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');

    const codeBlockMatch = jsonStr.match(/```(?:json)?\\s*([\\s\\S]*?)\\s*```/);
    if (codeBlockMatch) {
    jsonStr = codeBlockMatch[1];
    console.log('提取代码块:', jsonStr);
    }

    const braceStart = jsonStr.indexOf('{');
    const braceEnd = jsonStr.lastIndexOf('}');

    if (braceStart === 1 || braceEnd === 1 || braceStart > braceEnd) {
    console.error('未找到有效的JSON结构');
    return generateFallbackResponse();
    }

    jsonStr = jsonStr.substring(braceStart, braceEnd + 1);
    console.log('提取的JSON:', jsonStr);

    jsonStr = jsonStr.replace(/,\\s*([}\\]])/g, '$1');
    jsonStr = jsonStr.replace(/\\s*(\\{|\\}|\\[|\\]|,|:)\\s*/g, '$1');

    let data;
    try {
    data = JSON.parse(jsonStr);
    } catch (parseError) {
    console.error('JSON解析错误,尝试修复:', parseError.message);

    try {
    jsonStr = jsonStr
    .replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:/g, '"$2":')
    .replace(/'/g, '"')
    .replace(/,(\\s*[}\\]])/g, '$1');

    data = JSON.parse(jsonStr);
    } catch (secondError) {
    console.error('二次解析失败:', secondError.message);
    return generateFallbackResponse();
    }
    }

    if (!data.possibleInfections || !Array.isArray(data.possibleInfections)) {
    console.error('缺少possibleInfections数组');
    return generateFallbackResponse();
    }

    data.possibleInfections = data.possibleInfections.map((item, idx) => {
    let name = item.name || '未知感染';
    let type = item.type || 'virus';
    let probability = 0.5;
    let symptoms = Array.isArray(item.symptoms) ? item.symptoms : ['发热', '乏力'];
    let severity = item.severity || '中度';
    let matchingReason = item.matchingReason || '症状匹配';

    if (typeof item.probability === 'number') {
    probability = Math.max(0, Math.min(1, item.probability));
    } else if (typeof item.probability === 'string') {
    const parsed = parseFloat(item.probability);
    probability = isNaN(parsed) ? 0.5 : Math.max(0, Math.min(1, parsed));
    }

    if (!['virus', 'bacteria'].includes(type)) {
    type = 'virus';
    }

    if (!['轻度', '中度', '重度'].includes(severity)) {
    severity = '中度';
    }

    if (idx === 0) probability = Math.max(0.7, probability);
    if (idx === 1) probability = Math.max(0.5, Math.min(0.8, probability));

    return {
    name,
    type,
    probability,
    symptoms,
    severity,
    matchingReason
    };
    }).slice(0, 5);

    console.log('解析后的结果:', data);
    return data;
    } catch (e) {
    console.error('JSON解析最终失败:', e);
    return generateFallbackResponse();
    }
    }

    解析策略:

  • 去除多余换行符
  • 提取代码块中的内容
  • 定位JSON边界({ 和 })
  • 清理JSON格式(去除尾部逗号等)
  • 尝试解析,失败则尝试修复格式
  • 验证并规范化数据字段
  • 限制返回数量为5条
  • 3.5 结果展示函数

    displaySymptomAnalysis()负责将解析结果渲染到页面:

    function displaySymptomAnalysis(data) {
    const resultEl = document.getElementById('symptomResult');
    let html = '<strong>可能的感染(点击选择):</strong><br><br>';

    if (data.possibleInfections && Array.isArray(data.possibleInfections)) {
    data.possibleInfections.forEach((inf, index) => {
    const probPercent = (inf.probability * 100).toFixed(0);
    const severityColor = inf.severity === '重度' ? '#ff6b6b' :
    inf.severity === '中度' ? '#f39c12' : '#00b894';
    const typeIcon = inf.type === 'virus' ? '🦠' : '🦟';
    const isSelected = index === 0;
    const selectedClass = isSelected ? ' selected' : '';
    const checkbox = isSelected ? '☑' : '☐';
    const borderStyle = isSelected ? 'border-color: #00cec9; background: rgba(0, 206, 201, 0.1);' : '';

    html += `
    <div class="infection-option
    ${selectedClass}"
    onclick="selectInfection(
    ${index}, this)"
    data-index="
    ${index}"
    style="
    ${borderStyle}">
    <div class="infection-header">
    <span class="infection-icon">
    ${typeIcon}</span>
    <span class="infection-name">
    ${inf.name}</span>
    <span class="infection-prob" style="color:
    ${probPercent > 70 ? '#ff6b6b' : probPercent > 40 ? '#f39c12' : '#00b894'}">${probPercent}%</span>
    </div>
    <div class="infection-details">
    <span class="infection-type">
    ${inf.type === 'virus' ? '病毒' : '细菌'}</span>
    <span class="infection-severity" style="color:
    ${severityColor}">${inf.severity}</span>
    </div>
    <div class="infection-reason">
    ${inf.matchingReason || ''}</div>
    <div class="infection-checkbox">
    ${checkbox}</div>
    </div>
    `
    ;
    });
    }

    if (data.recommendations && data.recommendations.length > 0) {
    html += '<br><strong>建议:</strong><br>';
    data.recommendations.forEach((rec, index) => {
    html += `${index + 1}. ${rec}<br>`;
    });
    }

    html += '<br><button class="confirm-selection-btn" onclick="confirmSelectedInfections()">确认添加选中的感染源到战场</button>';

    resultEl.innerHTML = html;
    }

    渲染逻辑:

  • 遍历感染列表
  • 根据概率设置颜色(红色>70%,橙色40-70%,绿色<40%)
  • 根据严重程度设置颜色
  • 默认选中第一个结果
  • 添加多选复选框
  • 渲染确认按钮
  • 3.6 确认选择并添加到战场

    confirmSelectedInfections()函数处理用户选择并添加到对战系统:

    function confirmSelectedInfections() {
    console.log('confirmSelectedInfections被调用');
    console.log('symptomAnalysisResult:', symptomAnalysisResult);

    if (typeof symptomAnalysisResult === 'undefined' || symptomAnalysisResult === null) {
    alert('请先进行症状分析');
    return;
    }

    if (!symptomAnalysisResult.possibleInfections) {
    alert('请先进行症状分析');
    return;
    }

    const selectedElements = document.querySelectorAll('.infection-option.selected');
    console.log('选中的元素数量:', selectedElements.length);

    if (selectedElements.length === 0) {
    alert('请至少选择一种感染源');
    return;
    }

    let addedCount = 0;
    let errorMessages = [];

    selectedElements.forEach(el => {
    const index = parseInt(el.dataset.index);
    console.log('处理index:', index);

    if (index >= symptomAnalysisResult.possibleInfections.length) {
    errorMessages.push('选项 ' + (index + 1) + ': 数据错误');
    return;
    }

    const inf = symptomAnalysisResult.possibleInfections[index];
    console.log('处理感染:', inf);

    if (!inf || !inf.type || !inf.name) {
    errorMessages.push('选项 ' + (index + 1) + ': 数据不完整');
    return;
    }

    const type = inf.type;
    const subtype = findMatchingSubtype(type, inf.name);
    console.log('匹配结果:', type, subtype);

    if (!subtype) {
    errorMessages.push(inf.name + ': 无法匹配');
    return;
    }

    if (!factorConfigs[type]) {
    errorMessages.push(inf.name + ': 类型"' + type + '"不存在');
    return;
    }

    if (!factorConfigs[type][subtype]) {
    errorMessages.push(inf.name + ': 找不到"' + subtype + '"');
    return;
    }

    const damageMultiplier = inf.severity === '重度' ? 1.5 :
    inf.severity === '中度' ? 1.0 : 0.7;
    const amount = Math.max(1, Math.min(5, Math.round(inf.probability * 5)));

    console.log('添加感染源:', inf.name, type, subtype, amount);

    for (let i = 0; i < amount; i++) {
    addFactor(type, subtype, damageMultiplier);
    addedCount++;
    }

    selectedFactors[type] = subtype;
    highlightSelectedFactor(type, subtype);
    });

    console.log('共添加了', addedCount, '个感染源');

    if (addedCount > 0) {
    analyzeFactors();
    alert('已成功添加 ' + addedCount + ' 个感染源到战场!');
    } else {
    if (errorMessages.length > 0) {
    alert('添加失败:\\n' + errorMessages.join('\\n'));
    } else {
    alert('没有成功添加任何感染源,请重试');
    }
    }
    }

    处理逻辑:

  • 验证分析结果是否存在
  • 获取用户选中的感染源
  • 验证选中数量
  • 遍历选中项,匹配配置中的病原体
  • 根据严重程度计算伤害倍率
  • 根据概率计算数量
  • 调用addFactor()添加到战场
  • 更新UI高亮显示
  • 3.7 名称匹配函数

    findMatchingSubtype()用于将AI返回的感染名称匹配到系统配置:

    function findMatchingSubtype(type, name) {
    console.log('寻找匹配:', { type, name });

    const configs = factorConfigs[type];
    if (!configs) {
    console.error('未找到类型配置:', type);
    return null;
    }

    const nameLower = name.toLowerCase();
    const nameNormalized = name.replace(/病毒|细菌|\\s/g, '');

    for (const [key, config] of Object.entries(configs)) {
    const configNameLower = config.name.toLowerCase();
    const configNameNormalized = config.name.replace(/病毒|细菌|\\s/g, '');

    if (config.name.includes(name) || name.includes(config.name)) {
    console.log('直接匹配:', key);
    return key;
    }
    if (configNameLower.includes(nameLower) || nameLower.includes(configNameLower)) {
    console.log('小写匹配:', key);
    return key;
    }
    if (configNameNormalized.includes(nameNormalized) ||
    nameNormalized.includes(configNameNormalized)) {
    console.log('归一化匹配:', key);
    return key;
    }
    }

    const keywordMap = {
    virus: {
    '流感': 'influenza',
    'influenza': 'influenza',
    '新冠': 'coronavirus',
    '冠状病毒': 'coronavirus',
    'coronavirus': 'coronavirus',
    'HIV': 'hiv',
    '肝炎': 'hepatitis',
    '疱疹': 'herpes',
    '登革': 'dengue',
    '狂犬': 'rabies',
    '麻疹': '麻疹',
    '埃博拉': 'ebola',
    'HPV': 'hpv'
    },
    bacteria: {
    '大肠杆菌': 'e-coli',
    'e-coli': 'e-coli',
    'ecoli': 'e-coli',
    '金黄葡萄球菌': 'staph',
    'staph': 'staph',
    '沙门': 'salmonella',
    '霍乱': 'cholera',
    '结核': 'tuberculosis',
    '肺炎': 'pneumonia',
    '破伤风': 'tetanus',
    '鼠疫': 'plague',
    '莱姆': 'lyme',
    '梅毒': 'syphilis'
    }
    };

    if (keywordMap[type]) {
    for (const [keyword, targetKey] of Object.entries(keywordMap[type])) {
    if (name.includes(keyword)) {
    console.log('关键词匹配:', targetKey);
    if (factorConfigs[type][targetKey]) return targetKey;
    }
    }
    }

    const keys = Object.keys(configs);
    const fallback = keys[0];
    console.log('使用默认:', fallback);
    return fallback;
    }

    匹配策略:

  • 直接名称匹配
  • 小写转换匹配
  • 归一化匹配(去除"病毒"、"细菌"等后缀)
  • 关键词映射匹配
  • 默认返回第一个配置项

  • 四、AI问诊模态框系统

    4.1 模态框结构

    DiagnosisModal是一个独立的诊断模态框系统,支持两种模式:

    模式功能触发方式
    question 文本问答问诊 点击"AI问诊"按钮
    image 图片分析问诊 点击"AI图片问诊"按钮

    4.2 模态框创建

    const DiagnosisModal = (function() {
    let modalContainer = null;
    let currentType = null;

    function createModal() {
    if (modalContainer) return;

    modalContainer = document.createElement('div');
    modalContainer.id = 'diagnosisModal';
    modalContainer.className = 'diagnosis-modal-overlay';
    modalContainer.style.cssText = `
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.85);
    z-index: 10000;
    justify-content: center;
    align-items: center;
    overflow: auto;
    `
    ;

    const modalContent = document.createElement('div');
    modalContent.className = 'diagnosis-modal-content';
    modalContent.style.cssText = `
    background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
    border-radius: 16px;
    width: 90%;
    max-width: 550px;
    max-height: 90vh;
    overflow: hidden;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
    border: 1px solid rgba(255, 255, 255, 0.1);
    `
    ;

    const modalHeader = document.createElement('div');
    modalHeader.style.cssText = `
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 20px 24px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    `
    ;

    const title = document.createElement('h3');
    title.id = 'diagnosisModalTitle';
    title.style.cssText = `
    margin: 0;
    color: #fdcb6e;
    font-size: 20px;
    `
    ;
    title.textContent = 'AI问诊';

    const closeBtn = document.createElement('button');
    closeBtn.className = 'diagnosis-modal-close';
    closeBtn.style.cssText = `
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: none;
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
    font-size: 20px;
    cursor: pointer;
    transition: background 0.2s;
    `
    ;
    closeBtn.innerHTML = '&times;';
    closeBtn.onclick = closeModal;
    closeBtn.onmouseover = () => { closeBtn.style.background = 'rgba(255, 255, 255, 0.2);'; };
    closeBtn.onmouseout = () => { closeBtn.style.background = 'rgba(255, 255, 255, 0.1);'; };

    modalHeader.appendChild(title);
    modalHeader.appendChild(closeBtn);

    const modalBody = document.createElement('div');
    modalBody.id = 'diagnosisModalBody';
    modalBody.style.cssText = `
    padding: 24px;
    max-height: calc(90vh – 120px);
    overflow-y: auto;
    `
    ;

    modalContent.appendChild(modalHeader);
    modalContent.appendChild(modalBody);
    modalContainer.appendChild(modalContent);
    document.body.appendChild(modalContainer);
    }
    // …
    })();

    4.3 问答问诊模式

    function showQuestionDiagnosis() {
    document.getElementById('diagnosisModalTitle').innerHTML = '❓ AI问答问诊';

    const body = document.getElementById('diagnosisModalBody');
    body.innerHTML = `
    <style>
    .diagnosis-textarea {
    width: 100%;
    height: 120px;
    padding: 12px;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 8px;
    color: #fff;
    font-size: 14px;
    resize: none;
    box-sizing: border-box;
    font-family: inherit;
    }
    .diagnosis-textarea::placeholder { color: #666; }
    .diagnosis-submit-btn {
    width: 100%;
    padding: 12px;
    margin-top: 15px;
    background: linear-gradient(135deg, #fdcb6e, #f39c12);
    border: none;
    border-radius: 8px;
    color: #1a1a2e;
    font-size: 16px;
    font-weight: bold;
    cursor: pointer;
    }
    .diagnosis-common-questions {
    margin-top: 15px;
    }
    .diagnosis-question-tag {
    display: inline-block;
    padding: 5px 12px;
    margin: 5px;
    background: rgba(255, 255, 255, 0.1);
    border-radius: 20px;
    font-size: 12px;
    cursor: pointer;
    transition: background 0.3s;
    }
    .diagnosis-question-tag:hover {
    background: rgba(78, 205, 196, 0.3);
    }
    </style>
    <textarea id="diagnosisQuestionInput" class="diagnosis-textarea"
    placeholder="请输入您的健康问题…"></textarea>
    <div class="diagnosis-common-questions">
    <h4>💬 常见问题:</h4>
    <div>
    <span class="diagnosis-question-tag" onclick="DiagnosisModal.setQuestion('最近总是失眠怎么办?')">失眠问题</span>
    <span class="diagnosis-question-tag" onclick="DiagnosisModal.setQuestion('经常头痛是什么原因?')">头痛原因</span>
    <span class="diagnosis-question-tag" onclick="DiagnosisModal.setQuestion('如何提高免疫力?')">提高免疫力</span>
    <span class="diagnosis-question-tag" onclick="DiagnosisModal.setQuestion('感冒了应该吃什么药?')">感冒用药</span>
    <span class="diagnosis-question-tag" onclick="DiagnosisModal.setQuestion('如何缓解疲劳?')">缓解疲劳</span>
    <span class="diagnosis-question-tag" onclick="DiagnosisModal.setQuestion('饮食不规律怎么办?')">饮食问题</span>
    </div>
    </div>
    <button class="diagnosis-submit-btn" onclick="DiagnosisModal.submitQuestion()">📤 提交问诊</button>
    <div id="diagnosisAnswerArea" style="display: none; margin-top: 20px;">
    <h3 style="color: #fdcb6e; margin-bottom: 15px;">📊 诊断建议</h3>
    <div id="diagnosisAnswerContent"></div>
    </div>
    `
    ;
    }

    4.4 多技能协同诊断

    问答问诊调用多个AI技能进行综合分析:

    async function submitQuestion() {
    const input = document.getElementById('diagnosisQuestionInput');
    const question = input ? input.value.trim() : selectedQuestion;

    if (!question) {
    alert('请输入您的健康问题');
    return;
    }

    const answerArea = document.getElementById('diagnosisAnswerArea');
    const answerContent = document.getElementById('diagnosisAnswerContent');

    if (answerArea) answerArea.style.display = 'block';
    if (answerContent) answerContent.innerHTML = `
    <div class="diagnosis-loading">
    <div style="margin-bottom: 15px;">🔄 正在进行综合诊断分析…</div>
    <div id="skillProgress">
    <div class="skill-item" id="skill1">
    <span class="skill-icon">🤖</span>
    <span class="skill-name">QuestionDiagnosisSkill</span>
    <span class="skill-status" id="status1">等待中…</span>
    </div>
    <div class="skill-item" id="skill2">
    <span class="skill-icon">🧬</span>
    <span class="skill-name">SymptomAnalysisSkill</span>
    <span class="skill-status" id="status2">等待中…</span>
    </div>
    <div class="skill-item" id="skill3">
    <span class="skill-icon">💡</span>
    <span class="skill-name">HealthAdviceSkill</span>
    <span class="skill-status" id="status3">等待中…</span>
    </div>
    </div>
    </div>
    `
    ;

    try {
    console.log('========== 综合诊断开始 ==========');

    const updateStatus = (skillId, status, className) => {
    const statusEl = document.getElementById(status);
    if (statusEl) {
    statusEl.textContent = skillId;
    statusEl.className = 'skill-status ' + className;
    }
    };

    updateStatus('正在分析…', 'status1', 'running');
    const diagnosisPromise = QuestionDiagnosisSkill.diagnose(question).then(result => {
    updateStatus('✓ 完成', 'status1', 'completed');
    return result;
    }).catch(error => {
    updateStatus('✗ 失败', 'status1', 'failed');
    throw error;
    });

    updateStatus('正在分析…', 'status2', 'running');
    const analysisPromise = SymptomAnalysisSkill.analyze(question).then(result => {
    updateStatus('✓ 完成', 'status2', 'completed');
    return result;
    }).catch(error => {
    updateStatus('✗ 失败', 'status2', 'failed');
    throw error;
    });

    updateStatus('正在分析…', 'status3', 'running');
    const advicePromise = HealthAdviceSkill.getAdvice(question).then(result => {
    updateStatus('✓ 完成', 'status3', 'completed');
    return result;
    }).catch(error => {
    updateStatus('✗ 失败', 'status3', 'failed');
    throw error;
    });

    const [diagnosisResult, analysisResult, adviceResult] = await Promise.all([
    diagnosisPromise,
    analysisPromise,
    advicePromise
    ]);

    const combinedResult = combineResults(diagnosisResult, analysisResult, adviceResult);
    displayQuestionAnswer(combinedResult);

    console.log('========== 综合诊断完成 ==========');
    } catch (error) {
    console.error('诊断错误:', error.message);
    if (answerContent) {
    answerContent.innerHTML = '<div style="color: #e74c3c;">❌ 诊断失败:' + error.message + '</div>';
    }
    }
    }

    多技能协同流程:

  • 同时调用三个AI技能
  • 实时更新各技能状态
  • 合并分析结果
  • 统一展示诊断报告

  • 五、技能模块详解

    5.1 SymptomAnalysisSkill

    症状分析技能负责分析症状严重程度和可能影响的身体系统:

    const SymptomAnalysisSkill = (function() {
    const API_URL = "https://api-ai.gitcode.com/v1/chat/completions";
    const API_KEY = "qBPRwKM_kHgbBjzzxvW9ws–";

    async function queryAI(prompt) {
    try {
    const response = await fetch(API_URL, {
    method: "POST",
    headers: {
    "Authorization": `Bearer ${API_KEY}`,
    "Content-Type": "application/json"
    },
    body: JSON.stringify({
    model: "deepseek-ai/DeepSeek-V3",
    messages: [{
    role: "user",
    content: prompt
    }],
    stream: false,
    max_tokens: 1024,
    temperature: 0.5,
    top_p: 0.95
    })
    });

    if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data = await response.json();
    if (data.choices && data.choices[0] && data.choices[0].message) {
    return data.choices[0].message.content;
    }
    return null;
    } catch (error) {
    console.error('Symptom Analysis AI Error:', error.message);
    throw error;
    }
    }

    async function analyze(symptoms) {
    console.log('Symptom Analysis Skill – analyzing:', symptoms);

    const prompt = `你是一位专业的医学症状分析专家。请分析以下症状描述,给出专业的医学分析:\\n\\n症状:${symptoms}\\n\\n请以JSON格式返回分析结果,包含以下字段:\\n- symptoms: 识别到的具体症状列表(字符串数组)\\n- severity: 症状严重程度(轻微/中等/严重,字符串)\\n- possibleConditions: 可能的疾病或健康状况(字符串数组)\\n- affectedSystems: 可能受影响的身体系统(字符串数组)\\n\\n请确保 severity 是字符串类型,不要是对象或数组。`;

    const response = await queryAI(prompt);

    if (!response) {
    return generateFallbackResponse();
    }

    try {
    const parsed = parseResponse(response);
    return sanitizeResponse(parsed);
    } catch (error) {
    console.error('Symptom Analysis parse error:', error.message);
    return generateFallbackResponse();
    }
    }

    return {
    analyze: analyze
    };
    })();

    5.2 QuestionDiagnosisSkill

    问题诊断技能负责分析健康问题的可能原因和应对措施:

    const QuestionDiagnosisSkill = (function() {
    const API_URL = "https://api-ai.gitcode.com/v1/chat/completions";
    const API_KEY = "qBPRwKM_kHgbBjzzxvW9ws–";

    async function diagnose(question) {
    console.log('Question Diagnosis Skill – diagnose called with:', question);

    const prompt = `你是一位专业的医疗健康顾问,请详细分析以下健康问题并给出专业建议:\\n\\n${question}\\n\\n请以JSON格式返回结果,包含以下字段:\\n- possibleCauses: 可能的原因(字符串数组)\\n- suggestions: 应对措施(字符串数组)\\n- medicalAdvice: 是否需要就医及建议(字符串)\\n- healthTips: 健康小贴士(字符串数组)\\n\\n请确保 medicalAdvice 是字符串类型,不要是对象或数组。`;

    const response = await queryAI(prompt);

    if (!response) {
    return generateFallbackResponse();
    }

    try {
    const parsed = parseResponse(response);
    return sanitizeResponse(parsed);
    } catch (error) {
    console.error('Question Diagnosis parse error:', error.message);
    return generateFallbackResponse();
    }
    }

    return {
    diagnose: diagnose,
    generateFallbackResponse: generateFallbackResponse
    };
    })();

    5.3 HealthAdviceSkill

    健康建议技能提供生活方式建议:

    const HealthAdviceSkill = (function() {
    const API_URL = "https://api-ai.gitcode.com/v1/chat/completions";
    const API_KEY = "qBPRwKM_kHgbBjzzxvW9ws–";

    async function getAdvice(condition) {
    console.log('Health Advice Skill – getting advice for:', condition);

    const prompt = `你是一位专业的健康管理顾问。请根据以下健康状况提供详细的生活建议:\\n\\n健康状况:${condition}\\n\\n请以JSON格式返回建议,包含以下字段:\\n- sleepAdvice: 睡眠建议(字符串)\\n- stressManagement: 压力管理建议(字符串)\\n- hydration: 饮水建议(字符串)\\n- restAdvice: 休息与活动建议(字符串)\\n\\n请确保所有字段都是字符串类型,不要嵌套对象或数组。`;

    const response = await queryAI(prompt);

    if (!response) {
    return generateFallbackResponse();
    }

    try {
    const parsed = parseResponse(response);
    return sanitizeResponse(parsed);
    } catch (error) {
    console.error('Health Advice parse error:', error.message);
    return generateFallbackResponse();
    }
    }

    return {
    getAdvice: getAdvice
    };
    })();


    六、工作流程总结

    6.1 症状诊断流程

    用户输入症状

    点击"AI诊断"按钮

    构造AI提示词

    调用queryAI()发送请求

    解析AI响应 (parseAIResponse)

    验证数据有效性

    渲染诊断结果 (displaySymptomAnalysis)

    用户选择感染源

    点击"确认添加到战场"

    匹配配置并添加 (confirmSelectedInfections)

    更新对战系统状态

    6.2 AI问诊流程

    点击"AI问诊"按钮

    打开诊断模态框

    用户输入问题

    点击"提交问诊"

    并行调用三个AI技能

    实时更新进度状态

    合并分析结果

    展示综合诊断报告

    6.3 数据流向图

    ┌─────────────────────────────────────────────────────────────────┐
    │ 用户界面 │
    │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
    │ │ 症状输入框 │ │ 常见症状标签 │ │ AI问诊 │ │
    │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
    └─────────│──────────────────│──────────────────│───────────────┘
    ▼ ▼ ▼
    ┌─────────────────────────────────────────────────────────────────┐
    │ 前端处理层 │
    │ ┌──────────────────────────────────────────────────────────┐ │
    │ │ addSymptom() → analyzeSymptoms() → queryAI() │ │
    │ └──────────────────────────────────────────────────────────┘ │
    └─────────────────────────────────────────────────────────────────┘


    ┌─────────────────────────────────────────────────────────────────┐
    │ AI服务层 │
    │ ┌──────────────────────────────────────────────────────────┐ │
    │ │ DeepSeek-V3 API (api-ai.gitcode.com) │ │
    │ └──────────────────────────────────────────────────────────┘ │
    └─────────────────────────────────────────────────────────────────┘


    ┌─────────────────────────────────────────────────────────────────┐
    │ 响应处理层 │
    │ ┌──────────────────────────────────────────────────────────┐ │
    │ │ parseAIResponse() → sanitizeResponse() │ │
    │ │ ↓ │ │
    │ │ displaySymptomAnalysis() → confirmSelectedInfections()│ │
    │ └──────────────────────────────────────────────────────────┘ │
    └─────────────────────────────────────────────────────────────────┘


    ┌─────────────────────────────────────────────────────────────────┐
    │ 对战系统层 │
    │ ┌──────────────────────────────────────────────────────────┐ │
    │ │ addFactor() → battle.js 更新状态 │ │
    │ └──────────────────────────────────────────────────────────┘ │
    └─────────────────────────────────────────────────────────────────┘


    七、错误处理与容错机制

    7.1 AI响应异常处理

    系统实现了多层容错机制:

    function generateFallbackResponse() {
    return {
    possibleInfections: [
    {
    name: '流感病毒',
    type: 'virus',
    probability: 0.85,
    symptoms: ['发热', '咳嗽', '头痛'],
    severity: '中度',
    matchingReason: '常见感染表现'
    },
    {
    name: '普通感冒',
    type: 'virus',
    probability: 0.55,
    symptoms: ['鼻塞', '流涕'],
    severity: '轻度',
    matchingReason: '典型症状'
    }
    ]
    };
    }

    容错策略:

  • AI请求失败 → 使用默认数据
  • JSON解析失败 → 使用默认数据
  • 数据格式异常 → 使用默认数据
  • 网络超时 → 使用默认数据
  • 7.2 数据验证机制

    function sanitizeResponse(response) {
    const fallback = generateFallbackResponse();
    return {
    symptoms: ensureArray(response.symptoms, fallback.symptoms),
    severity: ensureString(response.severity, fallback.severity),
    possibleConditions: ensureArray(response.possibleConditions, fallback.possibleConditions),
    affectedSystems: ensureArray(response.affectedSystems, fallback.affectedSystems)
    };
    }

    function ensureArray(value, fallback) {
    if (value === null || value === undefined) return fallback;
    if (Array.isArray(value)) return value;
    if (typeof value === 'string') {
    try {
    const parsed = JSON.parse(value);
    return Array.isArray(parsed) ? parsed : fallback;
    } catch {
    return [value];
    }
    }
    return fallback;
    }

    function ensureString(value, fallback) {
    if (value === null || value === undefined) return fallback;
    if (typeof value === 'string') return value.trim() || fallback;
    if (typeof value === 'object') {
    try {
    return JSON.stringify(value);
    } catch {
    return fallback;
    }
    }
    return String(value).trim() || fallback;
    }


    八、性能优化策略

    8.1 请求并行化

    在AI问诊模式中,三个技能请求并行执行:

    const [diagnosisResult, analysisResult, adviceResult] = await Promise.all([
    diagnosisPromise,
    analysisPromise,
    advicePromise
    ]);

    8.2 状态缓存

    let symptomAnalysisResult = null;

    async function analyzeSymptoms() {
    // … 分析逻辑 …
    symptomAnalysisResult = parsedData;
    }

    function confirmSelectedInfections() {
    if (typeof symptomAnalysisResult === 'undefined' || symptomAnalysisResult === null) {
    alert('请先进行症状分析');
    return;
    }
    }

    8.3 渐进式加载

    statusEl.textContent = 'AI分析中…';
    statusEl.className = 'symptom-status loading';
    resultEl.classList.remove('show');

    // 分析完成后
    statusEl.textContent = '分析完成';
    statusEl.className = 'symptom-status success';
    resultEl.classList.add('show');


    九、总结

    症状诊断系统是命枢AI生命科学模拟器的核心功能模块,实现了从症状采集到AI分析再到战场集成的完整流程。系统采用现代化的UI设计,支持多种交互方式,具备完善的错误处理和容错机制。

    核心技术亮点:

  • 多技能协同诊断:同时调用三个AI技能进行综合分析
  • 智能名称匹配:支持多种匹配策略,提高识别准确率
  • 渐进式UI反馈:实时显示分析进度和状态
  • 多层容错机制:确保系统稳定运行
  • 模块化设计:各技能独立封装,便于维护和扩展
  • 该系统为用户提供了直观、专业的症状诊断体验,为后续的免疫细胞对抗模拟奠定了数据基础。

    赞(0)
    未经允许不得转载:171主机测评 » 鸿蒙Electron框架下鸿蒙PC——命枢AI生命科学模拟器 - 症状诊断功能实现与UI设计详解
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址