作业3:大模型工具的引入
一、文本型疾病描述数据的获取
(一)获取源数据集
采用的数据集:Chinese Medical Dialogue Dataset 中文医疗对话数据集_数据集-阿里云天池 (aliyun.com)
中的 sample_IM5000-6000.csv
里面有大量“医生解释疾病”的纯文本段落(从对话中提取描述部分),覆盖症状、多发人群等,适合结构化任务
数据示例截图
(二)获得50条用例子集
python prepare_50_diseases.py
prepare_50_diseases.py
# 文件名: prepare_50_diseases.py
import pandas as pd
import re
import random
# 1. 读取你下载的 sample 文件(根据实际路径修改)
df = pd.read_csv("sample_IM5000-6000.csv", encoding='gbk')
# 2. 只保留医生回复中长度 > 80 字的段落(通常是疾病解释)
# 注意这里改成了 'answer'
df['doctor_length'] = df['answer'].astype(str).apply(len)
candidate = df[df['doctor_length'] > 80].copy()
# 3. 简单清洗:去掉多余换行、空格、常见的对话前缀
def clean_doctor_text(text):
text = str(text)
# 去掉常见前缀
text = re.sub(r"^(医生:|医生:|医生\s*|答:)", "", text)
# 去掉多余空白和换行
text = re.sub(r"\s+", " ", text).strip()
return text
# 注意这里也改成了 'answer'
candidate['clean_description'] = candidate['answer'].apply(clean_doctor_text)
# 4. 去重 + 再过滤一次长度(保证是真正的疾病描述)
candidate = candidate.drop_duplicates(subset=['clean_description'])
candidate = candidate[candidate['clean_description'].apply(len) > 100]
# 5. 随机抽50条作为最终实验集
final_50 = candidate.sample(n=50, random_state=42)[['clean_description']].reset_index(drop=True)
final_50.columns = ['disease_text']
# 6. 保存(后面所有实验都直接读这个文件)
final_50.to_csv("disease_50_samples.csv", index=False, encoding="utf-8-sig")
print("完成!已生成 disease_50_samples.csv,共50条纯疾病描述文本")
print(final_50.head())

二、使用大模型工具提取疾病结构化数据,测试3种不同temperature值、3种不同prompt的提取效果
(一)获取通问千义api
第一步:注册并登录阿里云
- 访问阿里云官网:https://www.aliyun.com/
- 点击右上角的“免费注册”或“登录”。
- 如果你已有账号,直接登录。如果没有,请先注册。
- 重要:登录后,请根据提示完成实名认证。这是使用阿里云大部分服务的必要条件。
第二步:开通“百炼”大模型服务
- 登录后,在顶部搜索框中搜索“百炼”或“大模型服务平台百炼”,进入产品页面。
- 点击“立即开通”。
- 通义千问模型服务通常有免费额度供新用户试用,开通后即可在额度内免费调用。请仔细阅读计费说明。
第三步:创建AccessKey(API Key)
(二)执行脚本
建议使用用conda创建虚拟环境
# 创建名为 langchain_env 的环境,指定 Python 3.11(兼容性最佳)
conda create -n langchain_env python=3.11 -y
然后虚拟环境下执行extraction_experiments.py
D:\annaconda\envs\langchain_env\python.exe extraction_experiments.py
extraction_experiments.py
# 文件名: extraction_experiments.py
# 9种组合测试版:测试3种Temperature x 3种Prompt的提取效果
import pandas as pd
import os
import time
import re
from tqdm import tqdm
# ==================== 1. 密钥检查与交互输入(必须放在最前面) ====================
from dotenv import load_dotenv
load_dotenv()
from langchain_openai import ChatOpenAI
# 检查密钥
api_key = os.environ.get('DASHSCOPE_API_KEY')
if not api_key:
print("未检测到 DASHSCOPE_API_KEY 环境变量")
api_key = input("请在此粘贴你的阿里云 DashScope API-Key: ").strip()
os.environ['DASHSCOPE_API_KEY'] = api_key
print("密钥已临时写入当前进程环境变量")
print("API密钥状态:", "就位" if os.environ.get('DASHSCOPE_API_KEY') else "仍缺失!")
# 模型连通性测试
print("正在进行模型连通性测试...", end="")
try:
test_llm = ChatOpenAI(
model="qwen-plus",
temperature=0,
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key=os.environ["DASHSCOPE_API_KEY"]
)
test_resp = test_llm.invoke("你好,只回复一个字:好")
print("成功!模型返回:", test_resp.content.strip())
except Exception as e:
print("失败!")
print("错误信息:", str(e))
print("请检查密钥是否正确、网络是否通畅后重新运行")
exit(1)
# ==================== 2. 加载数据 ====================
try:
df = pd.read_csv("disease_50_samples.csv")
print(f"成功加载 {len(df)} 条对话文本数据")
except FileNotFoundError:
print("错误:未找到 'disease_50_samples.csv' 文件。请确保它在同一目录下。")
exit()
# ==================== 3. Kor 提取链定义(支持多种Prompt) ====================
from kor.extraction import create_extraction_chain
from kor.nodes import Object, Text
def get_schema_base():
"""返回基础的Schema结构,不包含任何Prompt描述或示例"""
schema = Object(
id="disease_summary",
description="""请从以下医疗对话文本中提取信息,并填充到JSON格式的字段中。如果某个字段信息不存在,请返回空字符串。""",
attributes=[
Text(id="疾病名称", description="..."), Text(id="相关症状", description="..."),
Text(id="相关人群或特征", description="..."), Text(id="关键建议", description="..."),
],
examples=[], # 示例将动态添加
many=False
)
return schema
def get_prompt_variants():
"""定义3种不同的Prompt变体"""
# --- Prompt A: 简洁指令型 ---
prompt_a = get_schema_base()
prompt_a.description = "从文本中提取信息。"
# --- Prompt B: 详细指令型 ---
prompt_b = get_schema_base()
prompt_b.description = """你是一个专业的医疗信息分析师。请仔细阅读以下在线问诊的对话文本,这些文本可能包含口语化表达和隐含信息。
你的任务是从中总结并提取出关于疾病的关键信息,并填充到指定的JSON字段中。
请确保提取的信息准确、具体,避免使用“建议”、“注意”等模糊词汇。如果信息确实不存在,对应字段留空。"""
# --- Prompt C: 示例引导型 ---
prompt_c = get_schema_base()
prompt_c.description = """根据提供的对话文本,总结关于疾病的关键信息。
文本通常包含患者提问和医生建议,信息可能不完整或隐含。
如果某个类别的信息在文本中完全没有提及,或者无法推断,请返回空字符串。
请参考以下示例的格式和风格进行提取:"""
prompt_c.examples = [
(
"高血压患者是可以适当的吃许多南瓜子,南瓜子的功效还是比较多的,因为它含有脂肪酸,可以使前列腺保持良好的功能,因此它能有效的以防前列腺的疾病,它还所含泛酸,可以缓解静止不动型的心绞痛,除了一定的降压作用,但是并不能治疗高血压,高血压患者,最终还是要通过药物来平稳的控制血压。",
[{
"疾病名称": "高血压",
"相关症状": "心绞痛",
"相关人群或特征": "高血压患者,前列腺疾病患者",
"关键建议": "通过药物来平稳的控制血压"
}]
)
]
return {
"简洁指令": prompt_a,
"详细指令": prompt_b,
"示例引导": prompt_c
}
# ==================== 4. 智能评分函数 ====================
def score_extraction(data):
score = 0
generic_phrases = ["无明确提及", "建议", "注意", "患者", "需要", "应该", "可以考虑", "有关", "根据情况"]
if data.get("疾病名称") and len(data.get("疾病名称")) > 1:
score += 1
symptom = data.get("相关症状", "")
if symptom and len(symptom) > 2 and not any(phrase in symptom for phrase in generic_phrases):
score += 1
population = data.get("相关人群或特征", "")
if population and len(population) > 2 and "患者" not in population:
score += 1
advice = data.get("关键建议", "")
if advice and len(advice) > 10 and not any(phrase in advice for phrase in generic_phrases):
score += 1
return score
# ==================== 5. 主实验循环(9种组合) ====================
temperatures = [0.0, 0.5, 1.0]
prompt_variants = get_prompt_variants()
results = []
# 外层循环遍历所有Prompt
for prompt_name, prompt_schema in prompt_variants.items():
# 内层循环遍历所有Temperature
for temp in temperatures:
print(f"\n{'='*20} 实验: Prompt='{prompt_name}', Temp={temp} {'='*20}")
llm = ChatOpenAI(
model="qwen-plus",
temperature=temp,
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key=os.environ["DASHSCOPE_API_KEY"]
)
# 使用当前循环的Prompt创建Chain
chain = create_extraction_chain(llm, prompt_schema)
total_score = 0
successful_extractions = 0
total = len(df)
for idx, row in tqdm(df.iterrows(), total=total, desc=f"Prompt='{prompt_name}', Temp={temp}"):
text = row['disease_text']
try:
result = chain.invoke({"text": text})
if 'disease_summary' not in result['text']['data']:
continue
extracted_info_list = result['text']['data']['disease_summary']
if not extracted_info_list:
continue
data = extracted_info_list[0]
total_score += score_extraction(data)
successful_extractions += 1
time.sleep(0.12)
except Exception as e:
print(f"\n[未知错误] 在处理第 {idx+1} 条文本时发生错误: {e}")
pass
avg_score = total_score / successful_extractions if successful_extractions > 0 else 0
results.append({
"Temperature": temp,
"Prompt_Type": prompt_name,
"成功提取条数": successful_extractions,
"总条数": total,
"平均得分(满分4.0)": round(avg_score, 2)
})
print(f"本组完成 → 成功提取 {successful_extractions}/{total} 条,平均得分: {avg_score:.2f} / 4.0")
# ==================== 6. 输出最终对比表格 ====================
result_df = pd.DataFrame(results)
print("\n" + "="*80)
print("【9种组合实验结果对比】")
print("="*80)
# 为了方便查看,可以按Temperature或Prompt_Type排序
print(result_df.sort_values(by=['Temperature', 'Prompt_Type']))
# 保存为Excel
try:
result_df.to_excel("结构化提取实验结果_9种组合版.xlsx", index=False)
print("\n已保存结果到:结构化提取实验结果_9种组合版.xlsx")
print("实验完成!")
except ImportError:
print("\n提示:未安装 openpyxl,无法保存为 Excel 文件。请运行 'pip install openpyxl' 安装。")
print("结果已在上方显示。")

(三)结果展示



由结果可以看出
- Prompt是决定性因素:Prompt的类型对提取效果的影响,远大于Temperature的变化。选择正确的Prompt,比微调Temperature重要得多。
- “示例引导”是最佳策略:在所有测试中,“示例引导”型Prompt在各项指标上均取得了压倒性胜利,是本次实验的最大赢家。
- Temperature是“稳定性旋钮”:对于最佳Prompt(示例引导),Temperature越低,结果越稳定且质量越高。但对于较差的Prompt,Temperature的影响变得复杂且不规律。
三、调试成功Neo4j与大模型的接口,实现自然语言与Cypher语句的转化功能
(一)启动neo4j并登录图形化界面
neo4j console

浏览器输入网址
http://localhost:7474/

(二)创建执行脚本
安装依赖
D:\annaconda\envs\langchain_env\python.exe -m pip install neo4j
执行脚本·
D:\annaconda\envs\langchain_env\python.exe neo4jlangchain.py

neo4jlangchain.py
# -*- coding: utf-8 -*-
from langchain_openai import ChatOpenAI
from langchain_community.graphs import Neo4jGraph
from langchain.chains import GraphCypherQAChain
import os
# ==================== 密钥处理 ====================
from dotenv import load_dotenv
load_dotenv()
if not os.environ.get('DASHSCOPE_API_KEY'):
os.environ['DASHSCOPE_API_KEY'] = input("请粘贴你的 DashScope API-Key: ").strip()
os.environ["OPENAI_API_KEY"] = os.environ["DASHSCOPE_API_KEY"]
# ==================== 连接 Neo4j(关键改动在这里!)===================
# 我们增加一个参数 `refresh_schema=False` 来禁止它自动刷新
graph = Neo4jGraph(
url="bolt://localhost:7687",
username="neo4j",
password="12345678",
refresh_schema=False # <--- 这是最关键的改动!
)
# ==================== 建图 ====================
print("正在创建 Top Gun 图谱...")
# 在建图前,先清空一下,确保是干净的环境
graph.query("MATCH (n) DETACH DELETE n")
graph.query("""
MERGE (m:Movie {name:"Top Gun", runtime: 120})
WITH m
UNWIND ["Tom Cruise", "Val Kilmer", "Anthony Edwards", "Meg Ryan"] AS actor
MERGE (a:Actor {name:actor})
MERGE (a)-[:ACTED_IN]->(m)
""")
print("图谱创建完成!")
# ==================== 手动设置 schema(现在这步是必须的!)===================
# 因为我们禁止了自动刷新,所以必须手动设置 Schema
# langchain 会根据这个字符串来理解如何生成 Cypher 查询
graph.schema = """
Node properties are the following:
Movie {name: STRING, runtime: INTEGER}
Actor {name: STRING}
Relationship properties are the following:
(No relationship properties)
The relationships are the following:
(:Actor)-[:ACTED_IN]->(:Movie)
"""
print("Schema 已手动设置!")
# ==================== 创建大模型链 ====================
llm = ChatOpenAI(
model="qwen-plus",
temperature=0,
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
chain = GraphCypherQAChain.from_llm(
llm=llm,
graph=graph,
verbose=True, # 必须开!会打印生成的 Cypher
allow_dangerous_requests=True
)
# ==================== 满分测试题 ====================
questions = [
"Top Gun 这部电影有哪些演员?",
"Tom Cruise 演过什么电影?",
"Top Gun 的时长是多少?",
"一共有几个演员?",
"列出所有电影和演员关系"
]
print("\n" + "="*80)
print("开始测试(每题都会打印 Cypher 和答案)")
print("="*80)
for i, q in enumerate(questions, 1):
print(f"\n第 {i} 题:{q}")
result = chain.invoke({"query": q})
print("答案 →", result["result"])
(三)结果展示
执行后可以看到cypher和答案



四、在TuGraph平台的图数据库上接入大模型Cypher语句的生成
(一)启动tugraph登录图形化界面
启动点击docker
终端启动
docker run -d -p 17070:7070 -p 7687:7687 -p 9090:9090 -v D:\综合实践\作业2:/var/lib/lgraph/data -v D:\tugraph\log:/var/log/lgraph_log --name tugraph_instance tugraph/tugraph-runtime-centos7:latest

登录
http://localhost:17070
用默认账号密码登录:
- 用户名:
admin - 密码:
73@TuGraph - 连接地址为
bolt://localhost:7687
(二)创建执行脚本
用nvim创建脚本并用之前创建的虚拟环境执行
D:\annaconda\envs\langchain_env\python.exe tugraph_real_instance.py
tugraph_real_instance.py
# -*- coding: utf-8 -*-
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_community.graphs import Neo4jGraph
import os
import json
# ==================== 密钥 ====================
from dotenv import load_dotenv
load_dotenv()
if not os.environ.get('DASHSCOPE_API_KEY'):
os.environ['DASHSCOPE_API_KEY'] = input("请粘贴你的 DashScope API-Key: ").strip()
os.environ["OPENAI_API_KEY"] = os.environ["DASHSCOPE_API_KEY"]
# ==================== 连接 TuGraph(禁止自动刷新)===================
graph = Neo4jGraph(
url="bolt://localhost:7687",
username="admin",
password="73@TuGraph",
database="default",
refresh_schema=False # 关键:禁止自动刷新
)
# ==================== 从 JSON 文件生成完美的 Schema ====================
# 假设 default.json 和你的 Python 脚本在同一个文件夹里
json_file_path = 'default.json'
try:
with open(json_file_path, 'r', encoding='utf-8') as f:
schema_data = json.load(f)
vertex_props = []
relationships = []
for item in schema_data:
if item['type'] == 'VERTEX':
# 将属性列表格式化为 "name: TYPE" 的字符串
props_str = ", ".join([f"{p['name']}: {p['type']}" for p in item['properties']])
vertex_props.append(f"- {item['label']} {{{props_str}}}")
elif item['type'] == 'EDGE':
# 从 constraints 获取关系的起点和终点
start_node = item['constraints'][0][0]
end_node = item['constraints'][0][1]
relationships.append(f"- (:{start_node})-[:{item['label']}]->(:{end_node})")
# 拼接成最终的 Schema 字符串,并转义大括号以避免 langchain 的格式化错误
schema = f"""
节点属性:
{chr(10).join(vertex_props)}
关系属性:
- (No relationship properties)
关系:
{chr(10).join(relationships)}
""".replace('{', '{{').replace('}', '}}') # <--- 关键修复:转义大括号
print("✅ 成功从 default.json 生成 Schema!")
except FileNotFoundError:
print(f"❌ 错误:找不到 {json_file_path} 文件。请确保它和脚本在同一目录下。")
schema = "Schema 生成失败,请检查文件路径。"
except Exception as e:
print(f"❌ 解析 JSON 文件时出错: {e}")
schema = "Schema 生成失败。"
print("\n你的 TuGraph 图结构如下:")
print(schema)
# ==================== 优化后的大模型 Prompt(使用精确的 Schema)===================
llm = ChatOpenAI(model="qwen-plus", temperature=0,
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1")
prompt = ChatPromptTemplate.from_messages([
("system", f"""你是一个图数据库专家。你的任务是根据用户的自然语言问题,生成用于查询 TuGraph 数据库的 Cypher 语句。
请严格依据以下图数据库的 Schema 信息来生成查询:
{schema}
严格遵守以下规则:
1. 只输出一条纯 Cypher 查询语句,不要有任何额外的解释、说明或 markdown 语法。
2. 查询必须以 `MATCH` 开始,以 `RETURN` 结束。
3. 只能使用 Schema 中明确定义的节点标签和关系类型。
4. 只能使用 Schema 中明确定义的属性(所有节点都只有 `name` 属性)。
5. 如果问题无法用给定的 Schema 回答,请输出 `MATCH (n) RETURN count(n) LIMIT 1`。
"""),
("human", "{question}")
])
chain = prompt | llm
# ==================== 满分测试题 ====================
questions = [
"易疲乏该吃什么药?", # 对应 Drug
"头痛可能是什么疾病的症状?", # 对应 Symptom, Disease
"糖尿病应该用什么药?", # 对应 Disease, Drug
"哪些疾病常见于老年人?", # 对应 Disease, Age
"列出所有疾病及其症状" # 对应 Disease, Symptom
]
print("\n" + "="*80)
print("第四部分:使用 JSON Schema 生成 Cypher(终极稳定版!)")
print("="*80)
for i, q in enumerate(questions, 1):
print(f"\n第 {i} 题:{q}")
result = chain.invoke({"question": q})
cypher = result.content.strip()
print("生成的 Cypher:")
print(cypher)
try:
exec_result = graph.query(cypher)
print(f"✅ 执行成功!返回 {len(exec_result)} 条记录")
if exec_result:
print(" 结果预览:", exec_result[:2])
except Exception as e:
print(f"❌ 执行失败:{str(e)}")
print("-" * 80)

(三)结果展示
终端执行截图

用终端生成的cypher在tugraph中执行


浙公网安备 33010602011771号