1. 项目概述:当中医古籍遇上大语言模型
最近在GitHub上看到一个挺有意思的项目,叫“QiZhenGPT”。光看名字,你大概能猜到它想做什么——“岐黄”是中医的代称,“GPT”则是当下最火的大语言模型。没错,这个项目的核心,就是尝试用现代的人工智能技术,特别是大语言模型,来解读、分析和应用那些浩如烟海的中医古籍与经典方剂。
我干了十多年技术,也一直对传统医学的智慧抱有敬意。中医典籍,从《黄帝内经》、《伤寒论》到《本草纲目》,里面蕴含的不仅是药方,更是一套完整的、关于人体、疾病与自然的哲学体系和实践经验。但它们的语言古奥,逻辑与现代医学迥异,这让很多初学者甚至从业者都望而却步。QiZhenGPT这个项目,本质上是在搭建一座桥梁:用AI作为“翻译”和“助理”,降低理解古籍的门槛,辅助进行方剂分析、药材配伍甚至初步的问诊思路梳理。它不是要取代医生,而是想成为一个强大的辅助工具,尤其对于中医学生、年轻医师和中医文化爱好者来说,可能是个“开挂”般的知识库和思考伙伴。
这个项目的价值在于,它触及了一个非常具体且需求明确的垂直领域。通用大模型虽然知识渊博,但对中医这种高度专业化、语境依赖强的领域,往往力不从心,容易产生“一本正经的胡说八道”。QiZhenGPT的目标,就是通过领域知识的深度注入(微调、知识库增强等),让AI的输出更精准、更“内行”。接下来,我会结合常见的技术路径,深入拆解这类项目是如何从想法落地的,以及在实际操作中会遇到哪些“坑”。
2. 核心思路与技术选型背后的考量
做一个垂直领域的专业AI助手,绝不是简单地把《伤寒论》全文喂给ChatGPT就完事了。这里面涉及到一系列关键的技术决策,每一个选择都直接影响最终效果的好坏。
2.1 基座模型的选择:通用与专业的平衡
首先面临的是基座模型选型。是用开源模型还是闭源API?用多大的参数规模?
- 闭源API(如GPT-4、Claude等):优点是开箱即用,能力强大,特别是逻辑推理和复杂指令跟随方面。初期快速验证想法非常合适。但缺点也很明显:成本高(尤其是高频调用时),数据隐私性存疑(你的中医问答数据可能被用于模型训练),并且无法针对中医术语和逻辑进行深度定制。对于想要长期运营、构建核心壁垒的项目来说,这不是最佳选择。
- 开源大模型(如LLaMA系列、Qwen、Baichuan等):这是目前主流的选择。你可以获得模型的全部权重,在自己的服务器上进行部署、微调和优化。QiZhenGPT这类项目,很可能选择如Qwen-7B或LLaMA2-13B这类中等规模的开源模型作为起点。为什么是7B或13B?因为更大的模型(如70B)对算力要求极高,而更小的模型(如1B)在理解复杂古籍文意和进行多步推理时能力不足。7B-13B这个量级,在效果和资源消耗上取得了较好的平衡,适合中小团队或个人开发者进行领域适配。
注意:选择开源模型时,务必仔细审查其许可证(License),确保允许商用和二次分发。同时,要关注模型的中文能力,一些以英文语料为主的模型,直接处理古文效果可能不佳。
2.2 知识注入方式:微调 vs. 检索增强生成(RAG)
这是项目的核心技术环节。如何让模型“学会”中医知识?
全参数微调:这是最彻底的方式。准备一个高质量的中医问答或文本补全数据集,用这个数据集对整个模型的所有参数进行再训练。这样训练出来的模型,其“世界观”里就深深烙上了中医的印记,在生成相关内容时风格和准确性都会更高。但缺点是需要大量的标注数据(成本高),且训练过程计算开销大,容易发生过拟合(模型只记住了数据,而丧失了原有的通用推理能力)。对于古籍这种数据量相对固定且专业的领域,在数据质量极高的情况下,全微调效果可能很好。
检索增强生成(RAG):这是当前更流行、也更灵活的方案。其核心思想是“外挂知识库”。我们不改变大模型本身,而是为它配备一个专属的、可随时更新的“中医古籍数据库”。
- 流程:当用户提问“风寒感冒用什么方子?”时,系统首先从这个专用数据库中检索出与“风寒”、“感冒”、“方剂”最相关的古籍原文片段(如《伤寒论》中桂枝汤的条文)。
- 然后,将这些检索到的原文片段,连同用户的问题,一起作为提示词(Prompt)提交给大模型,指令模型“基于以下资料回答问题”。
- 优点:知识更新容易(只需更新数据库),成本低,能有效减少模型“幻觉”(胡编乱造),因为答案有据可查。对于QiZhenGPT,RAG几乎是必选项,因为中医知识体系庞大且固定,RAG能确保模型输出的每一味药、每一个方剂出处都有古籍原文支撑,极大提升可信度。
- 挑战:关键在于检索的准确性。如何对古文进行高质量的分块(Chunking)和向量化(Embedding),使得检索时能精准找到相关段落,是技术难点。
在实际项目中,往往会采用“RAG为主,轻量微调为辅”的策略。即用一个在通用中文语料上表现良好的基座模型,结合一个强大的中医古籍RAG系统,再可能对模型进行一些轻量级的微调(如LoRA),让它更熟悉中医问答的对话格式和术语,这样能在效果、成本和灵活性上取得最佳平衡。
2.3 数据处理:从古籍扫描版到结构化知识
原始的中医古籍可能是PDF扫描件、图片甚至影印本。第一步就是要将它们变成机器可读、可理解的文本和数据。
- OCR与文字校对:使用高精度的OCR工具(如PaddleOCR、CnOCR)将图片转为文字。这一步错误率不低,尤其是面对繁体字、竖排版、无标点的古文时。必须辅以人工校对,甚至需要中医专业人士参与,因为“茯苓”误识别为“伏苓”将会是灾难性的。这是一个极其耗时但无法绕过的“脏活累活”。
- 文本清洗与结构化:识别出的文本是连续的。需要根据古籍的目录结构,将其分割成独立的篇章、条文。例如,将《伤寒论》按“辨太阳病脉证并治上”这样的章节拆分,再将每条“太阳病,发热,汗出,恶风,脉缓者,名为中风。”这样的条文作为独立单元。更进一步,可以尝试抽取结构化信息:方剂名、组成药材、剂量、制法、主治症状等,构建一个中医知识图谱。这能极大提升后续检索的精度和效率。
- 向量化与索引构建:这是RAG的核心。将清洗后的每一条文本(比如一个方剂条文),通过嵌入模型(Embedding Model)转换为一个高维向量(一堆数字)。这个向量就像是这段文本的“数学指纹”,语义相近的文本,其向量在空间中的距离也相近。所有文本的向量存入向量数据库(如Milvus, Pinecone, Chroma)。当用户提问时,问题也被转换成向量,然后在数据库里快速找到与之最“相似”(向量距离最近)的几条古籍原文。
这里的一个关键技巧是嵌入模型的选择。通用的嵌入模型(如text-embedding-ada-002)对现代中文效果好,但对古文可能“词不达意”。理想情况下,应该使用在中医古籍语料上微调过的嵌入模型,或者至少选择在中文上表现突出的开源模型(如BGE、M3E等),这样才能确保“风寒”的向量能准确关联到古籍中关于“风寒”的论述,而不是现代语境下的“风寒”。
3. 系统架构设计与核心模块解析
一个完整的QiZhenGPT系统,不会只是一个简单的对话界面。其背后是一个协同工作的技术栈。下面是一个典型的架构设计:
用户界面 (Web/App) | v API接口层 (FastAPI/Flask) | v 业务逻辑层 (对话管理、流程控制) | v [核心] RAG引擎 <---> 向量数据库 (存储古籍文本向量) | ^ v | 大语言模型 (LLM) | | | v | 提示词工程 (Prompt Engineering) ---> 知识库 (原始古籍文本)3.1 RAG引擎:系统的大脑
这是最复杂的部分,它负责将用户问题、检索到的知识、以及对话历史,整合成一个高质量的提示词(Prompt)送给LLM。一个进阶的RAG系统会做很多优化:
- 查询重写:用户的问题可能很口语化,如“我头疼流清鼻涕怎么办?”。直接用它去检索古籍,效果差。RAG引擎可以先用LLM将问题重写为更接近古籍表述的专业查询:“症见头痛,鼻流清涕,属何证候?有何方剂可治?”。这能大幅提升检索相关性。
- 混合检索:不仅仅依赖向量检索(语义搜索),还可以结合关键词检索(全文搜索)。比如,对于明确的方剂名“桂枝汤”,用关键词检索更直接准确;对于复杂的症状描述“发热恶寒交替”,用向量检索更能抓住语义。两者结果融合,取长补短。
- 引用与溯源:这是专业领域应用的生命线。模型生成的每一句关于药方、症候的结论,都必须明确指出是来源于哪本古籍、哪一卷、哪一条。在返回给用户的答案中,要以脚注或链接的形式清晰展示。例如:“…可考虑桂枝汤[1]加减。\n\n[1] 引自《伤寒论·辨太阳病脉证并治上》:‘太阳病,头痛,发热,汗出,恶风,桂枝汤主之。’” 这不仅是学术规范,更是建立用户信任的关键。
3.2 提示词工程:与模型沟通的艺术
给模型的指令(Prompt)直接决定了回答的质量。一个针对中医问答优化过的Prompt模板可能长这样:
你是一位资深的中医助手,精通《黄帝内经》、《伤寒论》、《金匮要略》等经典典籍。请严格依据我所提供的相关古籍条文来回答问题,不可自行编造知识。 相关古籍条文: {retrieved_context} 用户问题: {user_question} 请遵循以下步骤思考并回答: 1. 首先,分析用户问题描述的症状或症候,与上文提供的古籍条文进行比对。 2. 然后,归纳出可能的证型(如风寒表实证、少阳病等)。 3. 接着,列出相关的经典方剂,并说明其组成和配伍思路。 4. 最后,**必须**在回答的每一处结论后,用方括号[]标注出所引用的具体条文编号。 记住:如果提供的古籍条文中没有明确依据,请如实告知“根据所提供的典籍,未找到明确对应论述”,切勿臆测。这个Prompt明确了角色、任务、思考步骤和最重要的——引用规范。它强制模型进行链式思考(Chain-of-Thought),并将其输出牢牢锚定在提供的知识上下文中,有效抑制幻觉。
3.3 前端与交互设计:用户体验的关键
对于非技术用户,一个友好、专业的界面至关重要。
- 对话界面:类似ChatGPT,但可以设计一些快捷输入模板,如“症状描述框”、“舌苔脉象选择器”,帮助用户更结构化地描述病情。
- 知识图谱可视化:当模型提到一个方剂(如“桂枝汤”)时,可以点击展开一个可视化图谱,展示其组成药材(桂枝、白芍、生姜、大枣、甘草),以及这些药材的性味归经、常见配伍关系。这比纯文字直观得多。
- 对比分析功能:允许用户输入两个方剂名,让AI对比其组成、功效和主治异同。这对于学习方剂学非常有帮助。
4. 实操搭建:从零构建一个简易版QiZhenGPT
我们不可能完全复现原项目,但可以勾勒出一个最小可行产品(MVP)的搭建路径,让你理解其中每一个环节的具体操作。
4.1 环境准备与数据获取
假设我们选择Qwen-7B-Chat作为基座模型,Chroma作为向量数据库。
环境配置:
# 创建Python虚拟环境 python -m venv qizhen_env source qizhen_env/bin/activate # Linux/Mac # qizhen_env\Scripts\activate # Windows # 安装核心依赖 pip install torch transformers accelerate sentence-transformers chromadb langchain openai tiktoken # 如果需要Web界面,可以安装gradio或streamlit pip install gradio数据准备:
- 来源:可以从公开的电子古籍网站(需注意版权)或已数字化的开源项目中获取《伤寒论》、《金匮要略》等核心典籍的纯文本文件(.txt)。
- 预处理:编写脚本,按“条”进行分割。例如,以“第X条:”或“【原文】”作为分隔符。将每条原文及其对应的出处(如“伤寒论-辨太阳病脉证并治上-第12条”)保存为一个字典或JSON对象。
# 示例:简单的文本分割 with open('shanghanlun.txt', 'r', encoding='utf-8') as f: content = f.read() # 假设每条以数字加句号开头,如“1. 太阳之为病,脉浮,头项强痛而恶寒。” import re entries = re.split(r'\n(\d+)\.\s*', content)[1:] # 简单分割,实际更复杂 processed_data = [] for i in range(0, len(entries), 2): if i+1 < len(entries): entry_num = entries[i] entry_text = entries[i+1].strip() processed_data.append({ "id": f"shl_{entry_num}", "text": entry_text, "source": f"《伤寒论》第{entry_num}条" })
4.2 构建向量知识库
加载嵌入模型:我们选用一个中文效果好的开源模型,比如
BAAI/bge-small-zh-v1.5。from sentence_transformers import SentenceTransformer embed_model = SentenceTransformer('BAAI/bge-small-zh-v1.5')生成向量并存入数据库:
import chromadb from chromadb.config import Settings # 初始化Chroma客户端,持久化到磁盘 client = chromadb.PersistentClient(path="./qizhen_chroma_db") collection = client.create_collection(name="tcm_classics") # 为每条文本生成向量并添加 texts = [item["text"] for item in processed_data] metadatas = [{"source": item["source"], "id": item["id"]} for item in processed_data] ids = [item["id"] for item in processed_data] embeddings = embed_model.encode(texts).tolist() # 转换为列表 collection.add( embeddings=embeddings, documents=texts, metadatas=metadatas, ids=ids ) print("向量知识库构建完成!")
4.3 集成大模型与实现RAG链
加载大语言模型:
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline import torch model_name = "Qwen/Qwen-7B-Chat" # 假设使用Qwen tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True) model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.float16, # 半精度节省显存 device_map="auto", trust_remote_code=True ) pipe = pipeline("text-generation", model=model, tokenizer=tokenizer)实现RAG查询函数:
def rag_query(user_question, top_k=3): # 1. 将用户问题转换为向量 query_embedding = embed_model.encode([user_question]).tolist()[0] # 2. 在向量数据库中检索最相似的条文 results = collection.query( query_embeddings=[query_embedding], n_results=top_k ) # 3. 组装检索到的上下文 context = "" for doc, meta in zip(results['documents'][0], results['metadatas'][0]): context += f"出处:{meta['source']}\n内容:{doc}\n\n" # 4. 构建Prompt prompt_template = f"""你是一位中医助手,请根据以下古籍条文回答问题。 相关条文: {context} 用户问题:{user_question} 请基于上述条文,用中文给出专业、简洁的回答,并注明出处。如果条文不相关,请说明。""" # 5. 调用大模型生成回答 response = pipe( prompt_template, max_new_tokens=500, do_sample=True, temperature=0.3, # 较低的温度使输出更确定、更专业 ) return response[0]['generated_text']
4.4 搭建简易交互界面
使用Gradio快速创建一个Web界面。
import gradio as gr def answer_question(question, history): # history是Gradio维护的对话历史,这里我们简化处理,只关注当前问题 answer = rag_query(question) # 简单处理,只返回本次答案 return answer iface = gr.Interface( fn=answer_question, inputs=gr.Textbox(lines=2, placeholder="请输入您的中医问题,例如:风寒感冒有什么症状?"), outputs="text", title="岐黄GPT (简易演示版)", description="基于中医古籍的智能问答助手。提示:本演示仅为技术验证,不构成医疗建议。" ) iface.launch(share=True) # share=True会生成一个临时公网链接运行这段代码,一个最基础的、具备RAG能力的中医古籍问答助手就启动了。你可以通过浏览器访问本地链接进行提问。
5. 避坑指南与进阶优化
在实际操作中,你会遇到远比上述demo复杂的问题。以下是一些关键的“坑”和优化方向:
5.1 数据质量是天花板
- 坑:OCR错误、文本错简(古籍流传中的错位)、标点缺失,导致检索和模型理解全盘皆错。
- 解:没有捷径。必须投入精力进行数据清洗和校对。可以建立“可疑词”列表(如剂量单位“两”与“钱”的识别),进行批量检查和替换。考虑引入专家众核或与中医院校合作。
5.2 检索不精准的根源
- 坑:用户问“上火”,检索到的却是《伤寒论》里关于“太阳病,发热”的条文,因为向量模型认为“火”和“热”语义接近。
- 解:
- 优化文本分块:不要简单按固定长度分块。尝试按语义分块,例如,将一个完整的“病脉证治”条文(包括症状、脉象、方药)作为一个块。
- 使用领域微调的嵌入模型:用清洗后的高质量中医古籍文本,对开源的嵌入模型(如BGE)进行继续预训练或微调,让它的向量空间更贴合古文语义。
- 重排序:在向量检索出Top K个结果后,再用一个更精细的交叉编码器模型对它们进行相关性重排,选出最相关的1-2条。
5.3 大模型的“幻觉”与可控性
- 坑:即使提供了准确的古籍条文,模型在组织答案时,仍可能添加一些原文中没有的、自己“臆想”出来的症状或药材。
- 解:
- 强化Prompt约束:在Prompt中反复强调“严格依据”、“不可编造”,并设定严格的输出格式(如必须包含引用标记)。
- 后处理校验:编写规则,对模型输出进行扫描,检查提到的方剂名、药材名是否在提供的上下文中出现,若未出现则给出警告或要求模型重新生成。
- 使用具有更强指令跟随能力的模型:一些经过严格SFT和RLHF训练的模型,如Qwen-Chat系列,在遵循指令方面表现更佳。
5.4 专业性与安全性的平衡
- 坑:模型给出了一个看似合理的方剂建议,但用户直接照方抓药,存在风险。
- 解:
- 多重免责声明:在界面显著位置和每次回答的开头/结尾,都必须强调“本助手仅供参考,不能替代专业医师诊断,用药请遵医嘱”。
- 风险内容过滤:建立风险词库(如含有剧毒药材的方剂名“乌头汤”、“砒霜”等),当模型输出涉及这些内容时,自动触发强警告,甚至屏蔽具体剂量和用法。
- 引导而非诊断:将助手的角色定位为“古籍查询和知识梳理工具”,回答范式应是“《伤寒论》中记载,对于XX证候,常使用YY方剂,其组成为...”,而非“你得了XX病,应该吃YY药”。
5.5 性能与成本考量
- 坑:7B模型在CPU上推理速度慢,在GPU上又面临显存压力。RAG每次查询都要编码和检索,延迟可能较高。
- 解:
- 模型量化:使用GPTQ、AWQ等技术将模型量化到4bit甚至更低精度,可以大幅降低显存占用和提升推理速度,而对精度影响相对较小。
- 向量检索优化:Chroma等数据库支持持久化,首次加载后后续查询很快。确保检索的top_k值不要太大(通常3-5条足够)。
- 异步处理与缓存:对于Web服务,可以将耗时的模型推理放入异步队列。对常见问题(如“什么是桂枝汤?”)的答案进行缓存。
6. 应用场景与未来展望
这样一个系统,其价值远不止于一个问答机器人。
- 教学辅助:中医学生可以把它当作一个“智能古籍索引”,快速定位知识点,对比不同典籍对同一症候的论述。
- 临床辅助(参考):医师在思考方剂配伍时,可以快速查询古籍中类似案例的治法,拓宽思路。但必须重申,绝不能用于直接开方。
- 文献研究:研究者可以进行跨典籍的知识关联分析,例如,“某个药材在哪些方剂中出现频率最高?”、“某个理论在不同朝代典籍中的演变”。
- 文化普及:对中医感兴趣的公众,可以用通俗的语言询问“什么是阴虚火旺?”,系统从古籍中找出核心论述,再用易懂的语言转译。
未来的演进方向可能包括:
- 多模态能力:结合舌苔、面色图片的识别,让AI辅助“望诊”。这需要训练视觉-语言多模态模型。
- 个性化知识库:允许用户上传自己的医案、笔记,构建个人专属的、与古籍联动的知识体系。
- 辩证推理链:让AI模拟中医“望闻问切”的辩证过程,通过多轮问答逐步缩小证型范围,而不仅仅是单次问答。
构建QiZhenGPT这样的项目,是一个典型的“领域知识+AI技术”的深度结合案例。它技术栈复杂,涉及NLP、机器学习、数据库、前端等多个方面,但每一步都有相对成熟的开源工具可供选择。最大的挑战和核心价值,其实不在AI模型本身,而在于对中医领域知识的深度理解、高质量数据的构建以及如何将专业约束安全、可靠地“灌输”给AI系统。这个过程,本身也是对传统智慧进行一次系统性数字梳理的过程,意义深远。