ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

大模型中医辨证系统实战:Ollama部署与提示词工程详解

大模型中医辨证系统实战:Ollama部署与提示词工程详解 简介一套融合通义千问大模型与传统中医知识的智慧医药系统项目基于SpringBoot框架构建以人工智能充当智能医生提供中医诊断与养生咨询。整体功能设计简洁界面简约大气适合编程初学者、人工智能应用开发者学习也适用于学校项目答辩或毕业设计选题。压缩包共十七个文件大小约九十四兆主要包含脚本程序、模型数据、配置文件、说明文档、演示视频与音频等目录结构围绕项目运行与展示需求组织便于按需查阅。目前已有一百二十五人学习下载。资源附带了完整的前后端启动脚本、依赖清单、中英文说明、面部特征点模型及演示录屏能够直观呈现调用大模型接口、处理自然语言请求、输出中医知识的过程对理解人工智能与传统医学结合的实现路径、掌握一体化开发方式很有帮助。1. 从问诊单到方剂这套基于大模型的中医诊断系统到底怎么工作一个患者说自己“怕冷、清鼻涕、嗓子疼前两天吹了风”这套系统给出的不是“感冒”两个字而是“风寒束表、卫阳被遏”的证型判断外加荆防败毒散加减的剂量建议——这就是基于AI大模型的中医诊断系统在干的事。它把传统问诊单拆成结构化字段交给本地部署的大模型做八纲辨证最后输出证型、治法、方剂和服药说明。适合两类人一是想验证大模型在垂直行业能不能真正落地的AI工程师二是想给诊所或健康管理场景搭一个辅助辨证原型的开发者。这篇文章按我实际拆这套资源的过程把辨证链路、模型部署、前端渲染和几个高频坑逐一讲透文末会给出一个可以拿去用的验证方法。2. 辨证推理链路八纲辨证提示词框架与寒热虚实JSON输出2.1 问诊单为什么设计成JSON而不是自由对话先解决一个方向性问题这套资源里的问诊数据为什么非要转成JSON结构再丢给大模型而不是像ChatGPT那样直接闲聊我的判断是中医辨证本质上是一个结构化分类任务患者的主诉、兼症、舌象、脉象本身就是分类特征。“风寒束表”和“风热犯卫”的区分靠的是“怕冷还是怕热”“鼻涕是清还是黄”“咽喉是痒还是痛”这些离散特征而不是一段自由文本里的语气和修辞。把输入结构化模型要做的事就变成“理解特征组合并映射到证型”难度比“从一大段口语里抽取关键信息”低一个量级。这套资源在工程上的第一个关键决策就是把输入限制成如下结构{ chief_complaint: 怕冷清鼻涕嗓子疼, onset: 2天, chills_fever: 怕冷明显无明显发热, sweating: 无汗, thirst: 口不渴, stool: 大便偏稀, urine: 小便清长, tongue: 舌淡苔薄白, pulse: 脉浮紧, constitution: 平时体质偏弱 }字段名、取值倾向、可选范围都是固定的。这样做的第二个好处是前端表单可以直接把每个字段渲染成单选、多选或下拉框患者不需要组织语言模型也不需要对口语做归一化。我在实际跑这套流程时发现凡是让患者自由输入“症状描述”的方案后期都会被同一个症状的不同说法折磨——这个坑到第5章专门展开。输入结构定了输出结构也要定。辨证结果如果让模型随意发挥前端就没法渲染医生也没法核对。这套资源把输出也约束成了JSON核心字段包括证型、辨证依据、治法、主方、药材剂量、加减项、注意事项和置信度。输入输出全结构化意味着整条链路可以脱离人工介入自动运行这是它能做成一个“系统”而不是一个“聊天机器人”的根本原因。2.2 提示词搭建先讲辨证思路再输出结构化JSON跑通这套系统的核心其实是一段并不算长的System Prompt。我把这套资源里最有价值的提示词框架还原如下你拿到资源后可以对照自己的模型微调你是一位执业中医师擅长八纲辨证。请根据患者问诊单完成以下任务。 第一步先用两段话分析患者的寒热、虚实、表里、阴阳倾向说明你判断的关键依据。 第二步再输出一个JSON对象字段必须严格遵循 { pattern: 证型例如风寒束表, basis: 辨证依据摘要, therapy: 治法例如辛温解表、宣肺散寒, formula: 主方名称例如荆防败毒散, herbs: [{name: 药材, dose: 剂量, note: 炮制或煎法说明}], modification: 随症加减说明, caution: 注意事项, confidence: 0.0 } 约束 1. 只允许引用经方和时方禁止自创方剂。 2. 药材剂量参考《中国药典》毒性药材不得超出法定剂量上限。 3. 患者信息不足以判断时confidence不得高于0.6并在caution里注明需要追问的项目。 4. 禁止输出JSON之外的任何结构。这段提示词里有三个设计细节值得单独说。第一“先分析、后输出”的顺序。这是整套资源里最有效的一招。让模型先写两段推理文字再出JSON准确率比“直接出结果”高不少。原因在于多步推理让模型被迫把寒热、虚实、表里的判断过程走一遍降低了它直接跳到结论的概率。模型在辨证这种需要特征组合的任务上跳步输出往往就是幻觉的开始。第二置信度字段。这个设计在中医场景里非常关键。模型不是总能做出可靠判断——比如患者没填舌象和脉象模型硬判的可靠性就很低。设置confidence字段等于给系统留了一条“承认不确定”的出路。我在跑测试时把阈值定在0.7低于这个值的诊断结果在前端会被标注“建议线下就诊”而不是硬着头皮给方子。第三毒性药材的剂量约束。大模型对“附子”“细辛”这类药材的剂量上限认知很不稳定自由度一高就会编出离谱数字。把“参考《中国药典》”写进提示词可以把剂量错误率压低不少但这还不够——第5章会讲在代码层再加一道白名单校验。2.3 证型到方剂的映射不是让模型凭空开方提示词约束了“禁止自创方剂”模型仍然可能在经方集合里挑错。这个资源在辨证和开方之间加了一层规则映射我把它理解成“半开放决策”。模型只负责判定证型而证型到主方的对应关系走的是内置映射表证型治法基础方常见加减方向风寒束表辛温解表荆防败毒散头痛加川芎、白芷咳嗽加杏仁风热犯卫辛凉解表银翘散咽痛加玄参、射干口渴加芦根脾胃虚寒温中健脾理中丸寒重加附子先煎呕吐加半夏肝气郁结疏肝解郁逍遥散胁痛加川楝子失眠加合欢皮气阴两虚益气养阴生脉散汗多加浮小麦心悸加酸枣仁代码层的判断逻辑是如果模型输出的证型在映射表里直接取对应主方如果不在就把模型输出的formula字段当作候选进入人工复核通道。这一层规则把“模型选错方剂”的风险挡掉一部分同时也保证输出的方剂一定来自可溯源的清单而不是模型脑子里拼出来的组合。这套“结构化输入推理前置提示词证型映射白名单”的链路是这套资源最值得下载的部分。模型可以换提示词可以调但这个架构本身是通用的——你把这个架构里的中医辨证换成其他领域的故障诊断它照样成立。3. 模型部署与接口封装Ollama加载量化模型与问诊调用3.1 模型选型与量化说明这套资源在本地跑、用开源模型部署方案是Ollama。选它不是因为花哨是因为一条命令就能拉起一个支持OpenAI兼容接口的推理服务对做原型验证来说成本最低。模型层面我建议从Qwen2.5 7B的指令微调版开始量化级别选q4_K_M。选7B而不是14B的理由很实际辨证任务没有复杂的数学和代码推理7B的语义理解能力在这个场景下已经够用对显存的要求也低得多。q4_K_M是质量和体积的平衡点4比特量化之后模型文件不到5GB一张6GB显存的显卡或者16GB内存的Mac都能跑再低到q2或q3模型输出JSON的稳定性会明显下降经常出现字段残缺或括号不闭合的问题。这套资源里没有写死模型你完全可以把模型替换成其他中文指令模型。但我给一个经验值参数量低于7B的中文模型在“先推理后输出JSON”的双阶段任务上翻车率会显著上升表现为输出一段说明文字后拒绝输出JSON或者JSON里的药材列表缺失。所以建议别低于7B这个档位。3.2 首次部署与启动校验下载资源解压后先确认Ollama服务可用。部署命令分两步# 拉取模型q4_K_M是4比特量化版 ollama pull qwen2.5:7b-instruct-q4_K_M # 启动服务默认监听127.0.0.1:11434 ollama serve拉取时注意观察网络状态模型文件接近5GB断了就重新执行pullOllama支持断点续传。服务起来后先用一条命令验证推理是否正常ollama run qwen2.5:7b-instruct-q4_K_M 用一句话回答八纲辨证包含哪八纲如果模型能答出“阴阳、表里、寒热、虚实”说明服务可用。接着把Ollama的API地址记下来后面所有问诊调用都会走这个HTTP端点。默认是http://localhost:11434如果你在同一台机器上跑前端和调用脚本不需要改配置。3.3 diagnose函数封装与输出容错直接调Ollama的generate接口当然能跑但一个能交付的系统必须在调用层做三件事参数固定、JSON解析容错、失败兜底。这套资源里给出了一段可改的Python封装核心逻辑如下import json import requests OLLAMA_URL http://localhost:11434/api/generate def build_prompt(patient: dict) - str: system 你是一位执业中医师擅长八纲辨证。 请先分析患者寒热、虚实、表里、阴阳倾向再输出JSON对象。 # 把问诊单JSON作为用户消息传给模型 return f{system}\n患者问诊单\n{json.dumps(patient, ensure_asciiFalse)} def diagnose(patient: dict) - dict: prompt build_prompt(patient) payload { model: qwen2.5:7b-instruct-q4_K_M, prompt: prompt, stream: False, temperature: 0.2, top_p: 0.9, max_tokens: 1024, repeat_penalty: 1.1, } resp requests.post(OLLAMA_URL, jsonpayload, timeout120) raw resp.json()[response] return parse_result(raw) def parse_result(raw: str) - dict: # 优先提取json代码块 if json in raw: raw raw.split(json)[1].split()[0] else: # 退而求其次截取第一个{到最后一个}之间的内容 start, end raw.find({), raw.rfind(}) if start ! -1 and end ! -1: raw raw[start:end1] try: result json.loads(raw) except json.JSONDecodeError: raise ValueError(模型输出JSON解析失败原始输出 raw[:200]) return result参数说明就按我调过的来temperature设0.2。辨证任务不是创意写作低温度能让模型每次输出都偏保守减少随机发散。你如果把它调到0.7以上同一个问诊单跑三次能出三种证型这种系统没法交付。top_p设0.9配合低temperature使用。max_tokens设1024这个长度足够输出两段推理文字加一个完整的JSON太小会截断方剂列表。repeat_penalty设1.1防止模型在输出药材列表时重复某味药。parse_result这个函数是整个调用层的安全网。大模型偶尔会在JSON外裹一层解释文字或者把JSON放在段落中间这段代码会把杂质剥掉再解析。注意兜底逻辑的边界如果解析失败我选择直接抛异常而不是返回空结果——宁可让前端显示“请重新提交”也不能给患者一张缺了剂量的半成品方剂。这里有个判断分享给你封装层绝不能追求“尽量解析成功”。我在调试时发现正则强行修复残缺JSON有时候会把“附子10g”修成“附子100g”这种静默错误比显式失败危险得多。所以解析失败就大声报错让调用方决定怎么处理。4. 问诊界面与结果渲染从表单字段到可打印的诊断卡4.1 问诊表单字段设计这套资源的前端界面不算复杂但表单字段的设计是从辨证需求倒推出来的。我拆的时候数了一下主界面包含了11个字段每个字段都和提示词里的JSON键一一对应字段控件类型选项/范围设计理由主诉文本输入30字以内必填作为整体语境发病时长下拉3天 / 3-7天 / 7天判断表证还是里证倾向怕冷怕热单选怕冷明显 / 怕热明显 / 无明显寒热辨证核心特征出汗单选无汗 / 有汗 / 自汗 / 盗汗卫表是否开合口渴单选口不渴 / 口渴喜热饮 / 口渴喜冷饮判断寒热真假大便单选偏干 / 偏稀 / 正常里证虚实参考小便单选清长 / 短赤 / 正常配合口渴判断寒热舌象多选舌淡 / 舌红 / 苔白 / 苔黄 / 苔腻辨证重要依据脉象下拉浮 / 沉 / 迟 / 数 / 弦 / 细 / 滑提示词里直接引用既往病史多选糖尿病 / 高血压 / 胃病史等用药安全过滤体质单选平和 / 气虚 / 阳虚 / 阴虚 / 痰湿辅助证型判断这些字段几乎全用单选和下拉收口没有给患者留自由发挥的空间。这是刻意为之——模型处理“自定义文本”和“点选预设选项”的稳定性差距非常大用控件约束输入等于在源头消灭了第5章要讲的归一化问题。其中“口渴”这个字段我单独说。它是辨别寒热真假的关键真热假寒的患者往往口渴喜冷饮真寒假热的患者可能表现为口渴但喜热饮。这两个在语义上很接近但证型方向完全相反所以不能只做“口渴/不渴”二选一必须把“喜热饮”还是“喜冷饮”拆开。4.2 诊断结果渲染把推理链和方剂分开展示拿到模型输出的JSON后前端渲染的要点是“把辨证依据和方剂分开”。证型、治法、方剂是结果但患者和医生都需要看到“为什么是这么判”。这套资源的结果页包含四块证型标识卡、辨证依据折叠面板、方剂明细表、置信度警示条。function renderResult(result) { // 证型标识 const pattern document.getElementById(pattern); pattern.textContent result.pattern; // 辨证依据默认展开让医生核对 const basis document.getElementById(basis); basis.textContent result.basis; // 方剂表格 const tbody document.querySelector(#herbs-table tbody); tbody.innerHTML ; result.herbs.forEach((herb) { const row document.createElement(tr); row.innerHTML td${herb.name}/tdtd${herb.dose}/tdtd${herb.note || }/td; tbody.appendChild(row); }); // 置信度警示低于0.7建议线下就诊 const confidence document.getElementById(confidence); const progress document.getElementById(confidence-bar); progress.style.width ${result.confidence * 100}%; const warning document.getElementById(offline-warning); if (result.confidence 0.7) { warning.style.display block; } }这里有三个设计细节是资源里自带的我觉得值得留用。第一方剂明细表里药材名、剂量、煎法说明三者分列。剂量是医疗安全信息必须单独一列让医生一眼扫到煎法说明如“附子先煎”“生姜三片引”放在note列是给患者执行时看的。第二置信度用进度条加警示横幅双通道展示。进度条是视觉参考横幅是强制提示。低于0.7时显示“建议线下就诊本结果不可作为处方依据”这句文案在系统里写死不允许前端改动。理由很直接辅助辨证系统的第一原则是不要鼓励患者自行抓药。第三辨证依据默认展开而不是折叠。很多AI产品把推理过程藏起来但医疗场景相反——医生必须能快速看到模型判断的依据否则他没法决定采信还是推翻。依据越透明系统才越有可能被专业用户接受。5. 避坑指南归一化、毒性剂量与上下文漂移的四条翻车实录5.1 “胃疼”与“胃脘痛”症状归一化做不好辨证就漂移第一版系统上线测试时我发现一个诡异现象同一患者第一次说“胃疼”模型判成“肝气犯胃”一周后复诊说“胃脘痛”模型判成“脾胃虚寒”。患者症状没变证型却变了。原因出在模型对不同表述的语义理解不稳定。“胃疼”是口语“胃脘痛”是书面语哪怕它们指向同一个部位模型在组合“症状舌象脉象”时还是会因为措辞差异调整特征权重。这个问题的隐蔽之处在于单次诊断看不出问题只有同一患者复诊时才会暴露。解决方法是加一层症状归一化映射在构建提示词之前把口语替换成统一术语。我在代码里维护了一个同义词表“胃疼/胃痛/胃脘痛”统一映射为“胃脘疼痛”“拉肚子/腹泻/泄泻”统一映射为“大便稀溏”。这样模型每次看到的都是同一套术语复诊对比才有意义。从那以后我习惯把所有与患者直接交互的文本入口都过一遍归一化前端下拉选项本身也用统一术语做label。5.2 模型编了一个不存在的经方还开出附子30g这是个让我冷汗直流的案例。我拿一个“脾肾阳虚”的测试问诊单跑模型输出结果里出现了一首听起来很像经方的方剂但我在方剂库里查不到更离谱的是方中“附子”剂量写的是30g而《中国药典》规定的附子常规剂量上限是15g30g属于超量使用。原因有两层。第一模型在“禁止自创方剂”的约束下仍然可能把两三个相似方剂的名字融合生成一个似曾相识的伪经方第二模型对毒性药材的剂量上限没有可靠记忆它在训练语料里见过附子大剂量的个例就会把个例当作通用值。这个坑靠提示词约束是堵不死的必须在代码层加两道保险第一道是药材白名单模型输出herbs数组后逐一校验药材名是否在预置清单里不在的直接丢弃并告警第二道是剂量上下限表附子、细辛、生半夏这类毒性药材有明确的min和max值超出范围的剂量直接拒绝整张方剂。我现在跑这套系统的默认策略是宁可扔给人工复核也不能让一个编造方剂通过校验。5.3 多轮追问后证型自己跟自己打架资源里带了一个补充问诊功能模型在置信度不足时会反问患者。功能本身没问题但实测中发现一个坑患者在五轮追问后补充了“夏天怕吹空调”模型把证型从“风寒束表”悄悄改成了“阳虚外感”而下结论的依据里用的还是最初那版辩证逻辑。前后矛盾患者和医生都懵。原因在于长对话上下文膨胀模型对早期system prompt里的约束记忆淡化了容易被新信息带偏。解决方法是给追问加硬限制轮数上限固定为5轮答满5轮仍不置信就强制降级为“建议线下就诊”每一轮都把完整的system prompt重放一遍而不是只传对话历史同时把上一轮已经判出的寒热、虚实倾向单独提出来作为“既有判定”塞进下一轮的prompt里要求模型“如无足够证据不得修改既有判定”。这样既保留了补充问诊能力又不会让结论在对话中反复横跳。5.4 JSON字段名飘忽不定解析器差点被带崩我把模型换过一个版本之后发现parse_result偶发返回空字段。查了原始输出才发现新模型在某些问诊单上把“pattern”写成了“zheng_type”把“herbs”写成了“medicines”。字段名变了解析器拿到None前端直接渲染空白。原因不复杂提示词里虽然给了JSON模板但模型在长输出时偶尔会“忘键”用自己更熟悉的同义字段代替。解决方法是给parse_result加一个字段映射兜底读取时同时尝试pattern、zheng_type、zhengxing三个键herbs和medicines同理同时把workload降下来在提示词末尾加一句“必须原样返回模板中的键名不得替换”。我再后来给输出JSON加了一道schema校验任何未知字段触发告警并列入待审清单防止静默吞掉结构变化。6. 验证一个诊断靠不靠谱置信度、追问与复现性测试模型输出一张诊断卡不代表它可以见患者。我在落地这套系统时最常用的一套验证方法总共四步每一步成本都很低但对系统质量是刚性的。第一步批量回放历史问诊单。我手上攒了十几个真实标注过的病例每次改完提示词或换模型就把这批数据全部跑一遍对比新旧输出的证型和方剂差异。改提示词影响到的病例数超过三分之一说明改动方向偏了需要回滚。第二步复现性测试。同一个问诊单固定跑5次统计证型一致率。我给自己定的标准是至少4次一致才算稳定达到这个标准才允许进测试环境。如果5次出现3种证型基本可以断定是temperature设置有问题我会先把temperature压到0.1再重新测。第三步置信度分布审计。把最近100个问诊结果的confidence画个分布如果中位数低于0.7说明当前提示词或者问诊表单设计有问题——大概率是字段收集的信息量不够模型在很多病例上都在“勉强作答”。这时候该改的不是提示词而是回到第4章问诊表单里补字段。第四步方剂配伍核对。把模型输出的药味清单和它声称的主方做比对计算Jaccard相似度。相似度低于0.5的极有可能是模型选错了主方或者自创了方剂直接送到人工复核队列。这一步和我第5章说的药材白名单校验配套使用一个管“有什么药”一个管“药合理不合理”。最后说一个我自己的习惯从那以后我每次改完提示词或调完模型参数都强制把历史问诊单跑一遍对比验证通过前绝不上线。这套系统是用来辅助判断一个人该怎么调理身体的你要是拿一个自己都解释不了的输出点“提交”将来出了问题连个对标依据都拿不出来——所以宁可慢一点也别跳过这一步。希望帮到你。本文还有配套的精品资源点击获取
返回列表