File size: 29,096 Bytes
c4ab56a
 
aa6deae
 
b091824
61c23fd
 
 
 
 
 
 
 
 
 
 
 
 
91e6c82
 
 
 
61c23fd
 
 
 
91e6c82
61c23fd
 
 
91e6c82
61c23fd
 
 
c4ab56a
ead4a96
b4329a1
aa6deae
 
91e6c82
 
 
 
aa6deae
 
91e6c82
6756d28
 
 
3c2b39a
6756d28
3c2b39a
6756d28
 
3c2b39a
 
 
 
d56127e
 
6756d28
3c2b39a
 
6756d28
3c2b39a
d56127e
 
 
3c2b39a
 
 
 
d56127e
3c2b39a
 
d56127e
3c2b39a
03251c6
 
 
3c2b39a
03251c6
 
 
 
 
 
3c2b39a
 
03251c6
 
 
 
3c2b39a
 
03251c6
 
 
91e6c82
6756d28
 
 
 
 
 
 
 
 
 
 
 
 
 
91e6c82
 
6756d28
 
 
 
 
de0479d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3c2b39a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
03251c6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6756d28
03251c6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d56127e
 
 
03251c6
6756d28
 
 
 
 
 
 
 
03251c6
 
 
 
 
 
 
 
 
6756d28
91e6c82
de0479d
 
 
 
 
 
 
 
 
 
3c2b39a
 
d56127e
03251c6
d56127e
 
3c2b39a
 
d56127e
 
 
03251c6
3c2b39a
 
 
 
 
 
 
 
d56127e
de0479d
03251c6
3c2b39a
d56127e
 
 
ead4a96
91e6c82
6756d28
91e6c82
6756d28
 
 
 
 
 
91e6c82
 
 
 
 
6756d28
 
91e6c82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6756d28
 
91e6c82
 
 
03251c6
 
 
 
 
 
 
d56127e
3c2b39a
03251c6
 
 
 
 
 
 
 
 
 
3c2b39a
03251c6
 
 
 
d56127e
 
91e6c82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6756d28
 
ead4a96
 
f94d2cf
f123ff3
 
 
 
 
 
f94d2cf
ece9fc6
 
91e6c82
967c448
 
 
 
 
 
 
 
 
 
f123ff3
967c448
cb879e6
967c448
 
 
cb879e6
967c448
 
 
 
 
 
 
 
 
 
cb879e6
967c448
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cb879e6
967c448
 
cb879e6
967c448
 
 
cb879e6
ead4a96
 
91e6c82
 
c4ab56a
aa6deae
 
 
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
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
import gradio as gr
from huggingface_hub import InferenceClient
import os
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"  # 中国大陆加速
# 部署后查看 Logs 标签页的输出
import sys
from importlib.util import find_spec

def verify_environment():
    # 检查 Python 版本
    if sys.version_info < (3, 7):
        raise RuntimeError("需要 Python 3.7 或更高版本")

    # 检查关键依赖
    for package in ["tenacity", "gradio"]:
        if not find_spec(package):
            raise ModuleNotFoundError(f"缺失关键依赖: {package}")

    # 检查环境变量
    if "HF_TOKEN" not in os.environ:
        print("警告: 未设置HF_TOKEN环境变量,模型调用可能会失败")
        
    # 打印版本信息
    try:
        from importlib.metadata import version
        print(f"Tenacity 版本: {version('tenacity')}")
        print(f"Gradio 版本: {version('gradio')}")
    except ImportError:
        from pkg_resources import get_distribution
        print(f"Tenacity 版本: {get_distribution('tenacity').version}")
        print(f"Gradio 版本: {get_distribution('gradio').version}")

if __name__ == "__main__":
    verify_environment()

# 使用 Hugging Face Inference API 调用云端模型(无需本地加载)
import os
from tenacity import retry, stop_after_attempt, wait_exponential

# 安全地获取token,如果不存在则使用空字符串
hf_token = os.environ.get("HF_TOKEN", "")
client = InferenceClient(token=hf_token)

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def safe_model_call(prompt):
    try:
        print(f"尝试调用模型,使用token长度: {len(hf_token)}")
        print(f"使用的HF_ENDPOINT: {os.environ.get('HF_ENDPOINT', '默认')}")
        
        # 优先使用通用大模型,因为它们生成能力更强
        try:
            # 首先尝试使用通用大模型
            return client.text_generation(
                prompt=prompt,
                model="meta-llama/Llama-2-70b-chat-hf",  # 强大的通用大模型
                max_new_tokens=500,  # 增加输出长度
                temperature=0.7,  # 适中的温度,平衡创造性和准确性
                repetition_penalty=1.1,
                do_sample=True,
                top_p=0.9
            )
        except Exception as llama_error:
            print(f"Llama模型调用失败: {str(llama_error)}")
            
            # 尝试使用另一个通用大模型
            try:
                return client.text_generation(
                    prompt=prompt,
                    model="Qwen/Qwen-72B-Chat",  # 阿里的大模型,支持中文
                    max_new_tokens=500,
                    temperature=0.7,
                    repetition_penalty=1.1
                )
            except Exception as qwen_error:
                print(f"Qwen模型调用失败: {str(qwen_error)}")
                
                # 尝试使用医疗专业模型
                try:
                    return client.text_generation(
                        prompt=prompt,
                        model="epfl-llm/meditron-7b",  # 医疗领域专用模型
                        max_new_tokens=300,
                        temperature=0.3,
                        repetition_penalty=1.2,
                        do_sample=True,
                        top_p=0.9
                    )
                except Exception as meditron_error:
                    print(f"Meditron模型调用失败: {str(meditron_error)}")
                    
                    # 最后尝试使用小模型
                    return client.text_generation(
                        prompt=prompt,
                        model="bigscience/bloomz-7b1",  # 备选
                        max_new_tokens=300,
                        temperature=0.5,
                        repetition_penalty=1.2
                    )
    except Exception as e:
        error_type = type(e).__name__
        error_msg = str(e)
        print(f"模型调用失败: {error_type} - {error_msg}")
        
        # 检查是否是认证错误
        if "401" in error_msg or "unauthorized" in error_msg.lower() or "authentication" in error_msg.lower():
            print("可能是认证问题,请检查HF_TOKEN是否正确设置")
        # 检查是否是网络错误
        elif "timeout" in error_msg.lower() or "connection" in error_msg.lower():
            print("可能是网络连接问题")
        # 检查是否是模型不可用
        elif "not found" in error_msg.lower() or "404" in error_msg:
            print("模型可能不存在或不可用")
            
        raise

# 添加一个简单的本地备用模式
def local_fallback_response(query):
    """当API调用失败时提供基本的本地响应"""
    query = query.lower()
    
    # 处理烫伤情况
    if any(keyword in query for keyword in ["烫伤", "烧伤", "烫到", "烫着"]):
        return """对于烫伤的紧急处理,请按以下步骤操作:

**第一步:立即冷却**
1. 立即将烫伤部位置于冷自来水下冲洗10-15分钟
2. 水温应为凉水或微凉水,不要使用冰水或冰块(可能会加重组织损伤)
3. 如果无法将烫伤部位放在水下,可以用浸湿的冷毛巾敷在烫伤处

**第二步:评估烫伤程度**
- 一度烫伤:皮肤发红,轻微肿胀,有疼痛感
- 二度烫伤:皮肤起水泡,明显红肿,疼痛明显
- 三度烫伤:皮肤呈白色、棕色或黑色,可能没有疼痛感(因为神经已损伤)

**第三步:处理伤口**
1. 轻度烫伤(一度):
   - 冷却后,可以涂抹芦荟凝胶或专用烫伤药膏
   - 用干净的纱布轻轻覆盖
2. 中度烫伤(二度):
   - 不要刺破水泡
   - 用干净的纱布轻轻覆盖
   - 不要使用棉花或有绒毛的材料
3. 严重烫伤(三度):
   - 立即就医,不要自行处理

**禁忌事项**:
- 不要在烫伤处涂抹牙膏、酱油、油脂等民间偏方
- 不要刺破水泡
- 不要使用冰块直接接触烫伤处
- 不要在严重烫伤处涂抹任何药物,应立即就医

**何时就医**:
- 任何三度烫伤
- 面积较大的二度烫伤(大于手掌大小)
- 烫伤位于面部、手、脚、生殖器或关节处
- 烫伤后出现感染迹象(如红肿加剧、有脓液、发热)
- 婴幼儿的任何烫伤

这些是基本的急救措施,如果情况严重,请立即就医。"""

    # 处理呛水/窒息情况
    if any(keyword in query for keyword in ["呛水", "窒息", "噎住", "喉咙卡"]):
        return """对于呛水或窒息的紧急处理,请按以下步骤操作:

**呛水处理**:
1. 如果孩子能够咳嗽、说话或呼吸:
   - 鼓励他们咳嗽,帮助排出呛入的水
   - 不要拍打背部,让自然咳嗽将水排出
   - 密切观察呼吸情况

2. 如果孩子无法咳嗽、说话或呼吸(窒息):
   - 对于1岁以上儿童:执行海姆立克急救法(腹部冲击法)
     a. 站在孩子身后,双臂环抱孩子腰部
     b. 一只手握拳,拇指侧抵住孩子上腹部(肚脐上方,胸骨下方)
     c. 另一只手抓住拳头
     d. 快速向内上方挤压5次
     e. 重复直到异物排出或孩子失去意识

   - 对于1岁以下婴儿:
     a. 将婴儿面朝下放在您的前臂上,头低于躯干
     b. 用手掌根部在婴儿背部肩胛骨之间拍打5次
     c. 如果异物未排出,将婴儿翻过来,面朝上放在您的前臂上
     d. 用两根手指在胸骨中央(乳头连线下方)按压5次
     e. 交替进行背部拍打和胸部按压,直到异物排出

3. 如果孩子失去意识:
   - 立即拨打急救电话
   - 开始心肺复苏(CPR)

**窒息处理**(食物或异物):
- 步骤与上述呛水窒息处理相同

**注意事项**:
- 任何严重呛水事件后,即使症状消失,也应就医检查
- 如果孩子在呛水后出现呼吸困难、持续咳嗽、发热或异常嗜睡,应立即就医
- 预防呛水:监督儿童游泳,教导安全饮食习惯,避免在进食时玩耍或奔跑

这些是基本的急救措施,如果情况严重,请立即就医或拨打急救电话。"""

    # 处理发烧情况
    if any(keyword in query for keyword in ["发烧", "发热", "体温高"]):
        return """对于儿童发烧的处理,请按以下指导操作:

**评估体温**:
- 轻度发热:37.5°C-38°C
- 中度发热:38.1°C-39°C
- 高热:39.1°C以上

**家庭处理措施**:
1. **保持水分摄入**:
   - 鼓励孩子多喝水,防止脱水
   - 可以提供稀释的果汁或电解质饮料

2. **物理降温**:
   - 穿着轻薄、透气的衣物
   - 保持室温舒适,不要过热
   - 可以用温水(不是冷水)擦拭额头、腋下、颈部和腹股沟
   - 避免使用酒精擦拭

3. **药物降温**(在医生指导下):
   - 对于38.5°C以上的发热,可以考虑使用对乙酰氨基酚或布洛芬
   - 严格按照年龄和体重给药
   - 不要同时使用多种退热药
   - 不要给18岁以下儿童使用阿司匹林(可能导致瑞氏综合征)

**何时就医**:
- 3个月以下婴儿:任何发热都应立即就医
- 3-6个月婴儿:体温超过38°C
- 6个月-2岁儿童:体温超过39°C,或发热持续超过24小时
- 任何年龄:
  - 发热伴随剧烈头痛、颈部僵硬、皮疹、呼吸困难、持续呕吐或腹泻
  - 孩子异常嗜睡、烦躁或对周围环境反应迟钝
  - 发热持续超过3天
  - 孩子看起来非常不适或您感到担忧

**注意事项**:
- 发热本身是身体对抗感染的自然反应,不一定需要立即降温
- 观察孩子的整体状态比单纯看体温更重要
- 确保孩子充分休息
- 记录体温变化、用药情况和其他症状,以便向医生报告

这些是基本的家庭护理建议,如有疑虑,请咨询医生。"""

    # 处理擦伤、流血等急救情况
    if any(keyword in query for keyword in ["擦伤", "擦破", "流血", "出血", "伤口", "摔倒", "摔伤"]):
        return """对于擦伤和轻微流血的紧急处理,请按以下步骤操作:

**第一步:保持冷静**
- 安抚小孩情绪,保持冷静

**第二步:清洁伤口**
1. 用清水或生理盐水轻轻冲洗伤口,去除污垢和异物
2. 如果没有流动水,可以使用干净的瓶装水
3. 用温和的肥皂和水清洗伤口周围的皮肤,但避免肥皂直接接触开放性伤口

**第三步:止血**
1. 用干净的纱布或干净的布轻轻按压伤口
2. 保持压力约5-10分钟,直到出血停止
3. 如果血液浸透了纱布,不要移除它,而是在上面再加一层

**第四步:消毒处理**
1. 使用医用酒精、碘伏或双氧水等消毒液轻轻擦拭伤口周围(不要直接涂在开放性伤口上)
2. 可以使用无刺激性的抗菌药膏涂抹在伤口上

**第五步:包扎保护**
1. 用干净的创可贴或纱布覆盖伤口
2. 如果是较大的伤口,使用绷带轻轻包扎,但不要太紧

**注意事项**:
- 如果伤口较深、面积较大、异物无法清除,或者持续出血超过15分钟,应立即就医
- 观察伤口是否有感染迹象(如红肿、疼痛加剧、有脓液或发热),如有这些症状应及时就医
- 确认小孩的破伤风疫苗接种情况,如果超过5年未接种且伤口较深或被污染,应咨询医生是否需要接种破伤风疫苗

这些是基本的急救措施,如果情况严重,请立即就医。"""
    
    # 详细的失眠响应
    if "失眠" in query or "睡不着" in query or "睡眠" in query:
        days_match = None
        if "三天" in query or "3天" in query:
            days_match = "三天"
        elif "两天" in query or "2天" in query:
            days_match = "两天"
        elif "一周" in query or "7天" in query:
            days_match = "一周"
        elif "长期" in query or "慢性" in query:
            days_match = "长期"
        
        response = f"""关于{'持续'+days_match+'的' if days_match else ''}失眠问题,我可以提供以下专业建议:

**症状分析**:
失眠是指难以入睡、维持睡眠困难、早醒或睡眠质量差的情况。{'连续'+days_match+'的失眠可能会导致白天疲劳、注意力不集中、情绪波动等问题。' if days_match else '失眠会导致白天疲劳、注意力不集中、情绪波动等问题。'}

**可能的原因**:
1. 心理因素:压力、焦虑、抑郁或其他情绪问题
2. 生活习惯:不规律的睡眠时间、白天小睡过多
3. 环境因素:噪音、光线、温度不适宜
4. 电子设备使用:睡前使用手机、电脑等发光设备
5. 饮食因素:睡前摄入咖啡因、酒精或大量食物
6. 身体不适:疼痛、呼吸问题或其他身体不适

**建议措施**:
1. **建立规律的睡眠时间表**:
   - 每天同一时间上床和起床,包括周末
   - 避免白天长时间小睡,如需小睡,控制在30分钟以内

2. **优化睡眠环境**:
   - 保持卧室安静、黑暗和凉爽
   - 使用舒适的床垫和枕头
   - 考虑使用白噪音机器或耳塞来屏蔽干扰噪音

3. **改善睡前习惯**:
   - 睡前1-2小时避免使用电子设备
   - 建立放松的睡前仪式,如温水浴、阅读、冥想或轻柔的伸展运动
   - 避免睡前进行激烈运动或处理紧张事务

4. **注意饮食**:
   - 睡前4-6小时避免摄入咖啡因
   - 睡前避免大量饮水,减少夜间起床
   - 睡前避免大量或重口味食物
   - 限制酒精摄入,虽然酒精可能帮助入睡,但会影响睡眠质量

5. **放松技巧**:
   - 尝试深呼吸练习:吸气4秒,屏息7秒,呼气8秒
   - 渐进性肌肉放松:依次绷紧然后放松身体各部位肌肉
   - 引导冥想或正念练习

**何时就医**:
- {'由于失眠已持续'+days_match+',建议咨询医生,特别是如果它影响了您的日常生活和工作' if days_match else '如果失眠持续超过两周,或严重影响日常生活,应咨询医生'}
- 如果失眠伴随其他症状,如呼吸暂停、剧烈打鼾、夜间出汗、心悸等
- 如果您怀疑失眠可能与其他健康问题或药物副作用有关

**预防建议**:
- 保持规律的体育锻炼,但避免睡前3小时内进行
- 管理日间压力,可以通过冥想、瑜伽或其他放松技巧
- 限制白天的咖啡因摄入
- 保持健康的生活方式和饮食习惯

希望这些信息对您有所帮助。请记住,这只是一般性建议,不能替代专业医疗咨询。如果失眠问题持续或严重,建议咨询医生或睡眠专家。"""
        return response
    
    # 详细的头痛响应
    if "头痛" in query:
        days_match = None
        if "三天" in query or "3天" in query:
            days_match = "三天"
        elif "两天" in query or "2天" in query:
            days_match = "两天"
        elif "一天" in query or "1天" in query:
            days_match = "一天"
        
        response = f"""关于{'持续'+days_match+'的' if days_match else ''}头痛问题,我可以提供以下建议:

**可能的原因**:
- 紧张性头痛:由压力、焦虑或肌肉紧张引起
- 偏头痛:常伴有对光、声音敏感,有时伴随恶心
- 脱水:体内水分不足可导致头痛
- 睡眠不足或睡眠质量差
- 眼睛疲劳:长时间用眼或需要更新眼镜处方
- 感染:如感冒、流感或鼻窦感染

**建议措施**:
1. 休息:在安静、黑暗的房间休息
2. 水分补充:确保充分饮水
3. 热/冷敷:在疼痛部位使用热敷或冷敷
4. 按摩:轻轻按摩太阳穴或颈部
5. 非处方止痛药:如对乙酰氨基酚或布洛芬(请按说明使用)
6. 减轻压力:尝试放松技巧如深呼吸或冥想

**注意事项**:
- {'由于头痛已持续'+days_match+',建议咨询医生' if days_match else '如果头痛持续超过3天,应咨询医生'}
- 如果头痛非常剧烈、突然发作或伴随其他症状(如发热、颈部僵硬、视力问题、意识混乱),请立即就医
- 定期头痛需要专业医生评估

希望这些信息对您有所帮助。请记住,这只是一般性建议,不能替代专业医疗咨询。"""
        return response
    
    # 其他关键词匹配
    responses = {
        "发烧": """关于发热问题:

**可能的原因**:
- 感染(病毒、细菌)
- 免疫反应
- 疫苗接种后反应
- 其他炎症反应

**建议措施**:
1. 休息:保持充分休息
2. 水分补充:多喝水防止脱水
3. 降温:使用退热药物(如对乙酰氨基酚)
4. 轻便衣物:穿着轻便,避免过热

**注意事项**:
- 如果体温超过38.5°C,可以使用退烧药
- 高烧(39°C以上)或持续发热超过3天应就医
- 如果发热伴随严重症状(如呼吸困难、剧烈头痛、意识模糊),请立即就医

请记住,这只是一般性建议,不能替代专业医疗咨询。""",

        "咳嗽": """关于咳嗽问题:

**可能的原因**:
- 上呼吸道感染(感冒、流感)
- 过敏
- 哮喘
- 胃酸反流
- 环境刺激物

**建议措施**:
1. 保持水分摄入:多喝温水
2. 使用蜂蜜柠檬水:可缓解喉咙刺激
3. 湿度:使用加湿器增加空气湿度
4. 非处方止咳药:根据咳嗽类型选择

**注意事项**:
- 如果咳嗽持续超过2周,应咨询医生
- 如果咳嗽伴有血痰、呼吸困难、高热或胸痛,请立即就医
- 夜间咳嗽加重或影响睡眠也需要医疗关注

请记住,这只是一般性建议,不能替代专业医疗咨询。""",

        "疫苗": "疫苗接种后出现接种部位疼痛、轻微发热是正常反应,通常会在几天内消失。如出现严重过敏反应或高烧,应立即就医。",
        "感冒": "感冒通常会自行痊愈,建议多休息、多喝水、可使用非处方药缓解症状。如症状加重或持续一周以上,请咨询医生。",
        "腹痛": "腹痛可能由多种原因引起,如消化不良、胃肠炎等。建议注意饮食、避免辛辣刺激食物。如果疼痛剧烈或伴随其他症状,请咨询医生。",
        "过敏": "避免接触过敏原,可使用抗组胺药缓解症状。如果出现呼吸困难等严重症状,请立即就医。"
    }
    
    # 检查查询中是否包含关键词
    for keyword, response in responses.items():
        if keyword in query:
            return response
    
    # 默认回复
    return """您好,我是AI医疗助手。我可以提供一些基本的健康建议,但不能替代专业医生的诊断。

为了给您提供更有针对性的建议,请尽量详细描述您的症状,包括:
- 症状的具体表现
- 持续时间
- 是否有加重或缓解因素
- 是否尝试过任何缓解方法

对于任何严重或持续的健康问题,建议您咨询专业医生获取准确诊断和治疗方案。"""

def medical_chat(user_input, history):
    # 检查是否是紧急情况,如果是,直接使用本地备用响应
    emergency_keywords = ["摔倒", "摔伤", "流血", "出血", "擦伤", "擦破", "伤口", 
                         "烫伤", "烧伤", "烫到", "烫着", 
                         "呛水", "窒息", "噎住", "喉咙卡",
                         "急救", "紧急", "立即", "马上"]
    
    if any(keyword in user_input.lower() for keyword in emergency_keywords):
        print(f"检测到紧急情况关键词,直接使用本地备用响应")
        return local_fallback_response(user_input)
    
    # 构建更详细的医学对话提示词,针对通用大模型进行优化
    system_prompt = """你是一位经验丰富的医生,专注于提供准确、客观、专业且详细的医疗建议。
请根据患者描述的症状,提供科学的建议和可能的解决方案。

你的回答应该:
1. 基于医学事实和科学证据
2. 详细全面,包含充分的解释和建议
3. 使用通俗易懂的语言,避免过于专业的术语
4. 在必要时建议患者就医
5. 不做确定性的诊断,因为这需要面诊和检查
6. 不推荐具体的药品品牌
7. 不要说"给你开药",因为你不能开处方
8. 提供深入的分析和多方面的建议,而不是简单的答复

回答结构应包含:
- 症状分析:解释患者描述的症状可能意味着什么
- 可能的原因:列出几种可能导致这些症状的常见原因
- 建议措施:提供3-5个具体的自我管理建议
- 何时就医:明确说明在什么情况下患者应该寻求专业医疗帮助
- 预防建议:如何预防类似问题再次发生

对于紧急情况(如外伤、烫伤、窒息等),请提供清晰的急救步骤和注意事项。

请记住,你的建议仅供参考,不能替代专业医生的面诊。每个回答都应该全面、详细、有条理,就像一位真正的医生会提供的那种深入建议。"""
    
    # 构建完整的提示词
    prompt = f"{system_prompt}\n\n患者:{user_input}\n医生:"
    
    try:
        # 尝试调用云端模型
        print(f"发送到模型的提示: {prompt}")
        
        # 检查是否有有效的token
        if not hf_token.strip():
            print("HF_TOKEN未设置,使用本地备用模式")
            return local_fallback_response(user_input)
            
        response = safe_model_call(prompt)
        print(f"模型原始响应: {response}")
        
        # 检查响应是否为空
        if not response:
            print("模型返回空响应,使用本地备用模式")
            return local_fallback_response(user_input)
            
        # 检查响应类型并适当处理
        if isinstance(response, str):
            # 如果响应是字符串,直接处理
            doctor_response = response
        else:
            # 如果响应是其他类型的对象,尝试获取文本内容
            # 这取决于InferenceClient的返回类型
            print(f"响应类型: {type(response)}")
            try:
                # 尝试不同的属性或方法来获取文本
                if hasattr(response, 'generated_text'):
                    doctor_response = response.generated_text
                elif hasattr(response, 'text'):
                    doctor_response = response.text
                else:
                    # 尝试将响应转换为字符串
                    doctor_response = str(response)
            except Exception as e:
                print(f"处理响应时出错: {str(e)},使用本地备用模式")
                return local_fallback_response(user_input)
        
        print(f"处理后的响应: {doctor_response}")
        
        # 增强响应质量检测
        low_quality_indicators = [
            "没药", "不知道", "开点", "开药", "处方药", "非处方药", 
            "既然是", "那就", "我就给你", "吧", "呢", "哦", "呀",
            "简单", "随便", "试试看", "看看", "应该没事"
        ]
        
        # 检查响应质量
        if len(doctor_response.strip()) < 100:  # 增加最小长度要求
            print(f"响应太短: '{doctor_response}',使用本地备用模式")
            return local_fallback_response(user_input)
            
        # 检查是否包含低质量指标
        for indicator in low_quality_indicators:
            if indicator in doctor_response:
                print(f"响应包含低质量指标 '{indicator}': '{doctor_response}',使用本地备用模式")
                return local_fallback_response(user_input)
                
        # 检查是否包含足够的医疗建议内容
        medical_advice_indicators = ["建议", "可以", "应该", "如果", "症状", "原因", "方法", "帮助", "改善"]
        has_medical_advice = any(indicator in doctor_response for indicator in medical_advice_indicators)
        
        if not has_medical_advice:
            print(f"响应缺乏医疗建议: '{doctor_response}',使用本地备用模式")
            return local_fallback_response(user_input)
        
        # 如果响应中没有"医生:",则直接返回整个响应
        if "医生:" not in doctor_response:
            # 添加安全过滤
            if "死亡" in doctor_response or "癌症" in doctor_response:
                return "⚠️ 请及时联系线下医疗机构进行专业诊断!"
            return doctor_response
            
        # 提取医生回复部分
        doctor_response = doctor_response.split("医生:")[-1].strip()
        
        # 添加安全过滤
        if "死亡" in doctor_response or "癌症" in doctor_response:
            return "⚠️ 请及时联系线下医疗机构进行专业诊断!"
        
        return doctor_response
    except Exception as e:
        print(f"调用模型时出错: {str(e)},使用本地备用模式")
        return local_fallback_response(user_input)

# 构建 Gradio 界面
with gr.Blocks(theme=gr.themes.Soft(), css="""
    /* 使用CSS选择器定位提交按钮并将其移到右侧 */
    .gradio-container .submit-btn {
        position: absolute;
        right: 10px;
        bottom: 10px;
    }
""") as demo:
    gr.Markdown("# 🏥 专业医疗咨询助手")
    gr.Markdown("### 本系统提供基于AI的医疗咨询服务,仅供参考,不能替代专业医生的诊断")
    
    with gr.Column():
        gr.Markdown("### 请描述您的症状或健康问题")
        chat_interface = gr.ChatInterface(
            fn=medical_chat,
            examples=[
                "咳嗽一周了,有什么缓解方法?", 
                "经常失眠怎么调理?",
                "孩子有轻微发烧,需要去医院吗?"
            ],
            title="症状咨询",
            chatbot=gr.Chatbot(height=500)
        )
        
        with gr.Accordion("医疗免责声明", open=True):
            gr.Markdown("""
            ### 📋 医疗免责声明
            
            1. 本系统提供的信息仅供参考,不构成医疗建议、诊断或治疗方案
            2. 本系统不能替代专业医疗人员的面诊和专业判断
            3. 如遇紧急情况,请立即前往医院就诊或拨打急救电话
            4. 系统可能无法识别所有医疗紧急情况,请谨慎使用
            5. 使用本系统即表示您理解并接受以上声明
            """)
        
        with gr.Accordion("常见医疗问题", open=False):
            gr.Markdown("""
            ### 常见医疗问题解答
            
            **头痛**
            - 轻度头痛可以休息、按摩太阳穴或服用非处方止痛药
            - 如果头痛剧烈、持续时间长或伴随其他症状,请咨询医生
            
            **发热**
            - 轻度发热(38°C以下)可多喝水、休息并使用退烧药
            - 高烧(38°C以上)或持续发热超过3天应就医
            
            **咳嗽**
            - 保持水分摄入,可使用非处方止咳药
            - 如果咳嗽持续超过2周或伴有血痰,请咨询医生
            
            **疫苗接种后反应**
            - 接种部位疼痛、轻微发热是正常反应
            - 如出现严重过敏反应或高烧,应立即就医
            """)
        
        with gr.Accordion("使用说明", open=False):
            gr.Markdown("""
            ### 💡 使用说明
            
            1. 请简明扼要地描述您的症状或健康问题
            2. 尽量提供症状的持续时间、严重程度等相关信息
            3. 系统会根据您的描述提供一般性的健康建议
            4. 如果系统无法提供有用的建议,请尝试重新描述您的问题
            5. 对于复杂或严重的健康问题,请务必咨询专业医生
            """)
            
        with gr.Accordion("系统状态", open=False):
            hf_token_status = "✅ 已设置" if "HF_TOKEN" in os.environ and os.environ.get("HF_TOKEN", "").strip() else "❌ 未设置"
            gr.Markdown(f"- HF_TOKEN: {hf_token_status}")
            gr.Markdown(f"- HF_ENDPOINT: {os.environ.get('HF_ENDPOINT', '默认')}")
            
            if hf_token_status == "❌ 未设置":
                gr.Markdown("""
                ### ⚠️ 警告:未设置HF_TOKEN
                请在Hugging Face Spaces的设置中添加HF_TOKEN环境变量。
                
                1. 前往 [Hugging Face](https://huggingface.co/settings/tokens) 创建访问令牌
                2. 在Spaces设置中添加环境变量HF_TOKEN,值为你的访问令牌
                3. 重新部署应用
                """)

# 启动应用(Hugging Face Spaces 会自动处理)
print("启动应用...")
demo.launch(debug=True)  # 启用调试模式以获取更多错误信息