鸿蒙Electron框架下鸿蒙PC——命枢AI生命科学模拟器 - 症状诊断功能实现与UI设计详解
欢迎加入开源鸿蒙PC社区:
https://harmonypc.csdn.net/
Atomgit仓库地址:https://gitcode.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 = '×';
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)