文章目录前言一、如何实现 AI 医生顾问1. 整体思路2. 涉及改动的文件3. 核心代码二、第一次测试功能跑通但发现明显缺陷三、完善接入启用条目与实时规则检测1. 问题分析2. 改动方案四、完善后再次测试前言在实际使用过程中发现了一个明显的体验缺口医生使用者在问诊过程中有时会陷入不知道该问什么的困境只能等到对话结束后看评分报告才知道哪里没问到。这种事后复盘的模式对于训练效率来说是不够的。因此在前端加一个询问 AI的按钮使用者点击后大模型切换为医生顾问角色根据当前对话进度给出下一步提问的方向和话术建议实现实时教练的效果。一、如何实现 AI 医生顾问1. 整体思路这个功能本质上是一次单轮问答从 Redis 取出当前会话的对话历史去掉原来的患者 system message避免模型被病人的角色设定干扰换上培训专家的 system prompt把对话历史作为上下文传入模型给出建议返回给前端2. 涉及改动的文件文件改动内容llm_service.py新增get_doctor_advice函数endpoints.py新增/chat/advice接口补充 importchat.py新增AdviceResponse响应模型3. 核心代码chat.py— 新增响应模型classAdviceResponse(BaseModel):status:strsuccessadvice:strendpoints.py— 新增接口接口直接复用了已有的ChatRequest只需要session_idscene_key不需要新建请求模型。router.post(/chat/advice,response_modelAdviceResponse)asyncdefget_advice(request:ChatRequest): AI 医生顾问接口前端点击询问AI按钮时调用。 读取当前会话的对话历史以培训专家视角给出下一步提问的方向与话术建议。 不修改对话历史不影响正在进行的问诊流程。 try:adviceawaitget_doctor_advice(request.session_id,request.scene_key)returnAdviceResponse(adviceadvice)exceptExceptionase:raiseHTTPException(status_code500,detailstr(e))llm_service.py— 核心函数asyncdefget_doctor_advice(session_id:str,scene_key:str)-str:redis_keyfchat_history:{session_id}:{scene_key}# 从 Redis 读取当前对话历史history_strawaitredis_pool.get(redis_key)messagesjson.loads(history_str)ifhistory_strelse[]# 从数据库读取场景配置scene_dataawaitget_scene_config(scene_key)advisor_system_promptf 你是一位经验丰富的医患沟通培训专家正在辅助一名医生进行问诊训练。 【患者背景信息】{scene_data[patient_basic]}【本阶段问诊目标】{scene_data[patient_prompt]}【你的任务】 根据下方的对话历史判断医生目前的问诊进展给出下一步提问建议。 【输出格式要求】 按以下结构输出不超过150字 ➤ 建议方向一句话说明应该往哪个维度继续问 ➤ 参考话术给出1~2句医生可以直接说的口语示例 注意 - 建议要贴合当前对话的上下文不要重复已经问过的内容 - 话术要口语化、自然符合医生和患者交流的语气 - 不要评价医生之前说的对不对只给下一步建议 # 去掉原患者 system message避免角色混淆dialogue_only[mforminmessagesifm.get(role)!system]advisor_messages[{role:system,content:advisor_system_prompt},*dialogue_only,{role:user,content:请根据以上对话给我下一步的提问建议。},]responseawaitllm_client.chat.completions.create(modeldeepseek-chat,messagesadvisor_messages,temperature0.5,max_tokens300,)returnresponse.choices[0].message.content二、第一次测试功能跑通但发现明显缺陷测试流程如下先调用/chat接口几次让 Redis 里积累一定的对话历史再调用/chat/advice传入相同的session_id和scene_key测试结果如下功能上是跑通了建议方向和话术也比较自然。但仔细分析后发现了一个关键问题模型给出的建议完全依赖对上下文的感觉不知道这个场景实际需要覆盖哪些评分条目。我们设定的不同场景有不同的评分条目所以有更侧重的提问点。比如某个场景只启用了第6、7、8、10条模型却可能因为对话里提到了疼痛就一直围绕生理症状建议忽略了社会心理维度第8条、疾病对生活的影响第10条等同样需要覆盖的条目。此外顾问完全不知道当前对话里是否已经触发了黑话违规或命令式提问无法在给建议的同时提醒医生注意规范用语。三、完善接入启用条目与实时规则检测1. 问题分析数据库scenario_scenes表中有一列active_items存储了该场景启用的评分条目编号JSON 数组格式如[1, 6, 7, 8, 10, 12, 19, 22, 24, 25]。第一版的get_doctor_advice完全没有读取这一列导致顾问的引导缺乏针对性。同时系统已经有一套完整的规则检测函数run_all_rule_checks、detect_item12_commanding、detect_item19_jargon等评分时才调用。顾问功能可以复用这些函数在建议里加入实时违规提醒。2. 改动方案只需修改llm_service.py中的get_doctor_advice函数其他文件不动。改动分三步第一步新增 SEGUE 条目描述表把 25 个条目的编号和描述写成字典用于把active_items中的数字还原成可读文字SEGUE_ITEMS{1:有礼貌地称呼病人,6:让病人主动讲述对健康问题/疾病发展的看法,7:系统询问影响疾病的物理、生理因素,8:系统询问影响疾病的社会、心理、情感因素,10:讨论疾病对病人生活质量的影响,12:避免诱导性提问/命令式提问,19:根据病人理解能力调整表达避免或解释专业术语,# ... 共25条}第二步解析 active_items生成条目描述列表try:active_itemsjson.loads(scene_data.get(active_items)or[])exceptException:active_items[]active_items_desc\n.join(f 第{i}条{SEGUE_ITEMS.get(i,未知条目)}foriinactive_items)第三步对当前对话跑规则检测生成违规提醒块复用已有的检测函数在给建议之前先扫一遍当前对话dialogue_only[mforminmessagesifm.get(role)!system]rule_resultsrun_all_rule_checks(dialogue_only,patient_education_level)warning_lines[]# 第12条命令式/诱导式提问item12rule_results.get(item12,{})ifitem12.get(triggered)and12inactive_items:phrases[pforvinitem12.get(violations,[])forpinv.get(matched_phrases,[])]warning_lines.append(f⚠️ 第12条违规已检测到命令式/诱导式用语{, .join(set(phrases))}请注意避免。)# 第19条专业术语未解释item19rule_results.get(item19,{})ifitem19.get(triggered)and19inactive_items:unexp[wforvinitem19.get(violations,[])forwinv.get(jargon,[])]warning_lines.append(f⚠️ 第19条违规以下专业词尚未通俗解释{, .join(set(unexp))}f{患者文化程度低这将直接导致该条扣分。ifpatient_education_level低else建议补充解释。})# 第1条礼貌称呼if1inactive_items:item1detect_item1_greeting(dialogue_only)ifnotitem1.get(triggered):warning_lines.append(⚠️ 第1条提醒目前尚未出现礼貌称呼如您好、先生、女士等。)最终 system prompt 结构把条目列表和违规提醒一起注入 prompt输出格式也新增了注意事项一项无违规时自动省略advisor_system_promptf 你是一位经验丰富的医患沟通培训专家正在实时辅助一名医生进行问诊训练。 【患者背景信息】{scene_data[patient_basic]}【本阶段问诊目标】{scene_data[patient_prompt]}【本场景需要覆盖的评分条目】 以下是本场景会被评分的条目医生需要在对话中尽量全部覆盖{active_items_desc}【当前规则检测结果】{warnings_block}【输出格式】 按以下结构输出总字数不超过180字 ➤ 建议方向优先选尚未覆盖的条目 ➤ 参考话术1~2句口语示例 ➤ 注意事项有违规时提醒没有则省略此项 四、完善后再次测试保持相同的对话历史再次调用/chat/advice测试结果如下对比两次测试结果改进效果非常明显对比项第一版完善后建议依据仅凭对话上下文感觉明确指向未覆盖的评分条目第6、8条条目意识无不知道场景要评哪些有优先引导覆盖缺失条目违规提醒无有实时检测并在注意事项中反馈话术质量自然但方向随机自然且有针对性贴合具体条目要求
山东大学软件学院创新项目实训 —— 基于UE与LLM的医患沟通模拟与评价系统(七)
发布时间:2026/6/12 12:54:58
文章目录前言一、如何实现 AI 医生顾问1. 整体思路2. 涉及改动的文件3. 核心代码二、第一次测试功能跑通但发现明显缺陷三、完善接入启用条目与实时规则检测1. 问题分析2. 改动方案四、完善后再次测试前言在实际使用过程中发现了一个明显的体验缺口医生使用者在问诊过程中有时会陷入不知道该问什么的困境只能等到对话结束后看评分报告才知道哪里没问到。这种事后复盘的模式对于训练效率来说是不够的。因此在前端加一个询问 AI的按钮使用者点击后大模型切换为医生顾问角色根据当前对话进度给出下一步提问的方向和话术建议实现实时教练的效果。一、如何实现 AI 医生顾问1. 整体思路这个功能本质上是一次单轮问答从 Redis 取出当前会话的对话历史去掉原来的患者 system message避免模型被病人的角色设定干扰换上培训专家的 system prompt把对话历史作为上下文传入模型给出建议返回给前端2. 涉及改动的文件文件改动内容llm_service.py新增get_doctor_advice函数endpoints.py新增/chat/advice接口补充 importchat.py新增AdviceResponse响应模型3. 核心代码chat.py— 新增响应模型classAdviceResponse(BaseModel):status:strsuccessadvice:strendpoints.py— 新增接口接口直接复用了已有的ChatRequest只需要session_idscene_key不需要新建请求模型。router.post(/chat/advice,response_modelAdviceResponse)asyncdefget_advice(request:ChatRequest): AI 医生顾问接口前端点击询问AI按钮时调用。 读取当前会话的对话历史以培训专家视角给出下一步提问的方向与话术建议。 不修改对话历史不影响正在进行的问诊流程。 try:adviceawaitget_doctor_advice(request.session_id,request.scene_key)returnAdviceResponse(adviceadvice)exceptExceptionase:raiseHTTPException(status_code500,detailstr(e))llm_service.py— 核心函数asyncdefget_doctor_advice(session_id:str,scene_key:str)-str:redis_keyfchat_history:{session_id}:{scene_key}# 从 Redis 读取当前对话历史history_strawaitredis_pool.get(redis_key)messagesjson.loads(history_str)ifhistory_strelse[]# 从数据库读取场景配置scene_dataawaitget_scene_config(scene_key)advisor_system_promptf 你是一位经验丰富的医患沟通培训专家正在辅助一名医生进行问诊训练。 【患者背景信息】{scene_data[patient_basic]}【本阶段问诊目标】{scene_data[patient_prompt]}【你的任务】 根据下方的对话历史判断医生目前的问诊进展给出下一步提问建议。 【输出格式要求】 按以下结构输出不超过150字 ➤ 建议方向一句话说明应该往哪个维度继续问 ➤ 参考话术给出1~2句医生可以直接说的口语示例 注意 - 建议要贴合当前对话的上下文不要重复已经问过的内容 - 话术要口语化、自然符合医生和患者交流的语气 - 不要评价医生之前说的对不对只给下一步建议 # 去掉原患者 system message避免角色混淆dialogue_only[mforminmessagesifm.get(role)!system]advisor_messages[{role:system,content:advisor_system_prompt},*dialogue_only,{role:user,content:请根据以上对话给我下一步的提问建议。},]responseawaitllm_client.chat.completions.create(modeldeepseek-chat,messagesadvisor_messages,temperature0.5,max_tokens300,)returnresponse.choices[0].message.content二、第一次测试功能跑通但发现明显缺陷测试流程如下先调用/chat接口几次让 Redis 里积累一定的对话历史再调用/chat/advice传入相同的session_id和scene_key测试结果如下功能上是跑通了建议方向和话术也比较自然。但仔细分析后发现了一个关键问题模型给出的建议完全依赖对上下文的感觉不知道这个场景实际需要覆盖哪些评分条目。我们设定的不同场景有不同的评分条目所以有更侧重的提问点。比如某个场景只启用了第6、7、8、10条模型却可能因为对话里提到了疼痛就一直围绕生理症状建议忽略了社会心理维度第8条、疾病对生活的影响第10条等同样需要覆盖的条目。此外顾问完全不知道当前对话里是否已经触发了黑话违规或命令式提问无法在给建议的同时提醒医生注意规范用语。三、完善接入启用条目与实时规则检测1. 问题分析数据库scenario_scenes表中有一列active_items存储了该场景启用的评分条目编号JSON 数组格式如[1, 6, 7, 8, 10, 12, 19, 22, 24, 25]。第一版的get_doctor_advice完全没有读取这一列导致顾问的引导缺乏针对性。同时系统已经有一套完整的规则检测函数run_all_rule_checks、detect_item12_commanding、detect_item19_jargon等评分时才调用。顾问功能可以复用这些函数在建议里加入实时违规提醒。2. 改动方案只需修改llm_service.py中的get_doctor_advice函数其他文件不动。改动分三步第一步新增 SEGUE 条目描述表把 25 个条目的编号和描述写成字典用于把active_items中的数字还原成可读文字SEGUE_ITEMS{1:有礼貌地称呼病人,6:让病人主动讲述对健康问题/疾病发展的看法,7:系统询问影响疾病的物理、生理因素,8:系统询问影响疾病的社会、心理、情感因素,10:讨论疾病对病人生活质量的影响,12:避免诱导性提问/命令式提问,19:根据病人理解能力调整表达避免或解释专业术语,# ... 共25条}第二步解析 active_items生成条目描述列表try:active_itemsjson.loads(scene_data.get(active_items)or[])exceptException:active_items[]active_items_desc\n.join(f 第{i}条{SEGUE_ITEMS.get(i,未知条目)}foriinactive_items)第三步对当前对话跑规则检测生成违规提醒块复用已有的检测函数在给建议之前先扫一遍当前对话dialogue_only[mforminmessagesifm.get(role)!system]rule_resultsrun_all_rule_checks(dialogue_only,patient_education_level)warning_lines[]# 第12条命令式/诱导式提问item12rule_results.get(item12,{})ifitem12.get(triggered)and12inactive_items:phrases[pforvinitem12.get(violations,[])forpinv.get(matched_phrases,[])]warning_lines.append(f⚠️ 第12条违规已检测到命令式/诱导式用语{, .join(set(phrases))}请注意避免。)# 第19条专业术语未解释item19rule_results.get(item19,{})ifitem19.get(triggered)and19inactive_items:unexp[wforvinitem19.get(violations,[])forwinv.get(jargon,[])]warning_lines.append(f⚠️ 第19条违规以下专业词尚未通俗解释{, .join(set(unexp))}f{患者文化程度低这将直接导致该条扣分。ifpatient_education_level低else建议补充解释。})# 第1条礼貌称呼if1inactive_items:item1detect_item1_greeting(dialogue_only)ifnotitem1.get(triggered):warning_lines.append(⚠️ 第1条提醒目前尚未出现礼貌称呼如您好、先生、女士等。)最终 system prompt 结构把条目列表和违规提醒一起注入 prompt输出格式也新增了注意事项一项无违规时自动省略advisor_system_promptf 你是一位经验丰富的医患沟通培训专家正在实时辅助一名医生进行问诊训练。 【患者背景信息】{scene_data[patient_basic]}【本阶段问诊目标】{scene_data[patient_prompt]}【本场景需要覆盖的评分条目】 以下是本场景会被评分的条目医生需要在对话中尽量全部覆盖{active_items_desc}【当前规则检测结果】{warnings_block}【输出格式】 按以下结构输出总字数不超过180字 ➤ 建议方向优先选尚未覆盖的条目 ➤ 参考话术1~2句口语示例 ➤ 注意事项有违规时提醒没有则省略此项 四、完善后再次测试保持相同的对话历史再次调用/chat/advice测试结果如下对比两次测试结果改进效果非常明显对比项第一版完善后建议依据仅凭对话上下文感觉明确指向未覆盖的评分条目第6、8条条目意识无不知道场景要评哪些有优先引导覆盖缺失条目违规提醒无有实时检测并在注意事项中反馈话术质量自然但方向随机自然且有针对性贴合具体条目要求