File size: 10,025 Bytes
c2d8817 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | #!/usr/bin/env python3
"""
LLM 部署和配置脚本
支持的 LLM 后端:
1. Ollama (推荐用于本地部署)
2. vLLM (高性能推理)
3. OpenAI 兼容 API
推荐模型:
- llama3:8b-instruct (轻量级,适合测试)
- llama3:70b-instruct (PRD 推荐)
- meditron:7b (医学专用)
- med42-v2 (医学专用)
"""
import os
import sys
import subprocess
import json
from pathlib import Path
# 项目根目录
PROJECT_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(PROJECT_ROOT))
def check_ollama():
"""检查 Ollama 是否安装"""
try:
result = subprocess.run(
["ollama", "--version"],
capture_output=True,
text=True
)
if result.returncode == 0:
print(f"✅ Ollama 已安装: {result.stdout.strip()}")
return True
except FileNotFoundError:
pass
print("❌ Ollama 未安装")
return False
def install_ollama():
"""安装 Ollama"""
print("\n📦 安装 Ollama...")
print("请访问 https://ollama.ai 下载安装")
print("\n或使用以下命令:")
print(" curl -fsSL https://ollama.ai/install.sh | sh")
return False
def list_ollama_models():
"""列出已安装的 Ollama 模型"""
try:
result = subprocess.run(
["ollama", "list"],
capture_output=True,
text=True
)
if result.returncode == 0:
print("\n📋 已安装的模型:")
print(result.stdout)
return result.stdout
except:
pass
return ""
def pull_ollama_model(model_name: str):
"""下载 Ollama 模型"""
print(f"\n⬇️ 下载模型: {model_name}")
print("这可能需要几分钟...")
try:
process = subprocess.Popen(
["ollama", "pull", model_name],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True
)
for line in process.stdout:
print(f" {line.strip()}")
process.wait()
if process.returncode == 0:
print(f"✅ 模型 {model_name} 下载完成")
return True
else:
print(f"❌ 模型下载失败")
return False
except Exception as e:
print(f"❌ 错误: {e}")
return False
def test_ollama_model(model_name: str):
"""测试 Ollama 模型"""
print(f"\n🧪 测试模型: {model_name}")
try:
import ollama
response = ollama.chat(
model=model_name,
messages=[
{
"role": "system",
"content": "你是一名放射科医生,请用中文回答。"
},
{
"role": "user",
"content": "请简单描述肺结节的影像特征。"
}
]
)
print(f"\n📝 模型响应:")
print("-" * 40)
print(response['message']['content'])
print("-" * 40)
return True
except ImportError:
print("❌ 请安装 ollama Python 包: pip install ollama")
return False
except Exception as e:
print(f"❌ 测试失败: {e}")
return False
def update_config(llm_model: str, llm_backend: str = "ollama"):
"""更新配置文件"""
env_path = PROJECT_ROOT / ".env"
config = {
"LLM_MODEL": llm_model,
"LLM_BASE_URL": "http://localhost:11434/v1" if llm_backend == "ollama" else "http://localhost:8000/v1",
"LLM_API_KEY": "ollama" if llm_backend == "ollama" else "local-key",
"LLM_TEMPERATURE": "0.1",
"LLM_MAX_TOKENS": "4096"
}
# 读取现有配置
existing_config = {}
if env_path.exists():
with open(env_path, 'r') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#') and '=' in line:
key, value = line.split('=', 1)
existing_config[key] = value
# 更新配置
existing_config.update(config)
# 写入配置
with open(env_path, 'w') as f:
f.write("# NeuroScan AI 配置\n\n")
f.write("# LLM 配置\n")
for key, value in existing_config.items():
f.write(f"{key}={value}\n")
print(f"\n✅ 配置已更新: {env_path}")
print(f" LLM_MODEL={llm_model}")
print(f" LLM_BASE_URL={config['LLM_BASE_URL']}")
def test_report_generation():
"""测试报告生成"""
print("\n🧪 测试报告生成...")
from app.services.report import ReportGenerator
# 使用模板模式测试 (不需要 LLM)
generator = ReportGenerator(llm_backend="template")
# 测试单次扫描报告
findings = [
{
"nodule_id": "nodule_1",
"organ": "右肺上叶",
"location": "后段",
"max_diameter_mm": 12.5,
"volume_cc": 0.85,
"mean_hu": 35.2,
"shape": "分叶状",
"density_type": "部分实性"
}
]
report = generator.generate_single_report(
patient_id="TEST_001",
study_date="2026-01-24",
body_part="胸部",
findings=findings,
clinical_info="体检发现肺结节",
modality="CT"
)
print("\n📄 单次扫描报告 (模板模式):")
print("=" * 50)
print(report[:1000] + "..." if len(report) > 1000 else report)
# 保存报告
output_dir = PROJECT_ROOT / "test_case" / "reports"
output_dir.mkdir(parents=True, exist_ok=True)
report_path = generator.save_report(
report,
output_dir / "single_scan_report",
format="html"
)
print(f"\n✅ 报告已保存: {report_path}")
# 测试纵向对比报告
baseline_findings = [
{
"nodule_id": "nodule_1",
"organ": "右肺上叶",
"location": "后段",
"max_diameter_mm": 10.0,
"volume_cc": 0.52,
"mean_hu": 32.0,
"shape": "圆形",
"density_type": "实性"
}
]
followup_findings = [
{
"nodule_id": "nodule_1",
"organ": "右肺上叶",
"location": "后段",
"max_diameter_mm": 12.5,
"volume_cc": 0.85,
"mean_hu": 35.2,
"shape": "分叶状",
"density_type": "部分实性"
}
]
longitudinal_report = generator.generate_longitudinal_report(
patient_id="TEST_001",
baseline_date="2025-10-01",
followup_date="2026-01-24",
baseline_findings=baseline_findings,
followup_findings=followup_findings,
registration_results={"mae_before": 432.5, "mae_after": 385.5},
change_results={"diameter_change_pct": 25.0},
modality="CT"
)
print("\n📄 纵向对比报告 (模板模式):")
print("=" * 50)
print(longitudinal_report[:1000] + "..." if len(longitudinal_report) > 1000 else longitudinal_report)
report_path = generator.save_report(
longitudinal_report,
output_dir / "longitudinal_report",
format="html"
)
print(f"\n✅ 报告已保存: {report_path}")
return True
def main():
"""主函数"""
print("=" * 60)
print("NeuroScan AI - LLM 部署配置")
print("=" * 60)
# 1. 检查 Ollama
has_ollama = check_ollama()
if has_ollama:
# 列出已安装模型
models = list_ollama_models()
# 推荐的医学模型
recommended_models = [
("llama3:8b-instruct", "通用模型,适合测试"),
("llama3.1:8b-instruct", "最新通用模型"),
("meditron:7b", "医学专用模型"),
("medllama2:7b", "医学专用模型"),
]
print("\n💡 推荐的模型:")
for model, desc in recommended_models:
print(f" - {model}: {desc}")
# 检查是否有可用模型
if "llama3" in models.lower() or "meditron" in models.lower():
print("\n✅ 已有可用的 LLM 模型")
else:
print("\n⚠️ 建议下载一个模型:")
print(" ollama pull llama3:8b-instruct")
else:
print("\n💡 Ollama 是推荐的本地 LLM 部署方案")
print(" 安装后可以运行: ollama pull llama3:8b-instruct")
# 2. 测试报告生成 (模板模式)
print("\n" + "=" * 60)
print("测试报告生成 (模板模式 - 不需要 LLM)")
print("=" * 60)
try:
test_report_generation()
print("\n✅ 报告生成测试成功!")
except Exception as e:
print(f"\n❌ 报告生成测试失败: {e}")
import traceback
traceback.print_exc()
# 3. 总结
print("\n" + "=" * 60)
print("部署总结")
print("=" * 60)
print("""
📋 报告生成模块已就绪!
支持的模式:
1. 模板模式 (无需 LLM): 使用预定义模板生成报告
2. Ollama 模式: 使用本地 LLM 生成更智能的报告
3. vLLM/OpenAI 模式: 使用高性能推理服务
使用方法:
```python
from app.services.report import ReportGenerator
# 模板模式
generator = ReportGenerator(llm_backend="template")
# Ollama 模式 (需要先安装 Ollama 和模型)
generator = ReportGenerator(llm_backend="ollama")
# 生成报告
report = generator.generate_single_report(
patient_id="P001",
study_date="2026-01-24",
body_part="胸部",
findings=[...],
modality="CT"
)
```
如需使用 LLM 增强报告:
1. 安装 Ollama: curl -fsSL https://ollama.ai/install.sh | sh
2. 下载模型: ollama pull llama3:8b-instruct
3. 启动服务: ollama serve
4. 使用 Ollama 模式: ReportGenerator(llm_backend="ollama")
""")
if __name__ == "__main__":
main()
|