Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions plugins/medical_kb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Medical Knowledge Base Plugin for DeepSearch

医疗知识库插件,为 [openJiuwen DeepSearch](https://gitcode.com/openJiuwen/deepsearch) 提供**诊疗指南智能检索与问答**能力。

## 功能特性

- **诊疗指南问答**:基于中国2型糖尿病防治指南(2024/2020版)、中国高血压防治指南(2024版)
- **三重融合检索**:向量语义 + BM25关键词 + 知识图谱,综合打分
- **引用溯源**:每个回答附带指南原文引用(名称+版本+章节+条目)
- **版本对比**:新旧版指南差异检测(如糖尿病指南2024版 vs 2020版)
- **多轮对话**:根据历史对话推断疾病,提高命中率

## 快速开始

```bash
# 1. 安装依赖
pip install medical-ai-innovation

# 2. 测试插件
python -m plugins.medical_kb
```

## 使用方法

```python
from plugins.medical_kb import MedicalKnowledgeBasePlugin

# 初始化插件
plugin = MedicalKnowledgeBasePlugin()
plugin.load_demo_guidelines()

# 搜索
result = plugin.search("空腹血糖正常值是多少?")
print(result["answer"])

# 获取引用
for c in result["citations"]:
print(f"{c['source']} (score: {c['score']})")
```

## 内置指南数据

| 指南名称 | 版本 | 条目数 |
|---------|------|--------|
| 中国2型糖尿病防治指南 | 2024版(最新) | 6条 |
| 中国2型糖尿病防治指南 | 2020版(旧版) | 3条 |
| 中国高血压防治指南 | 2024版(最新) | 3条 |

## 插件接口

实现 DeepSearch 标准插件接口:

| 方法 | 说明 |
|------|------|
| `search(query, top_k=5)` | 知识库搜索 |
| `batch_search(queries)` | 批量搜索 |
| `get_guideline_list()` | 获取指南列表 |
| `get_stats()` | 获取统计信息 |
| `explain(query)` | 检索过程解释 |

## 许可证

Apache-2.0
202 changes: 202 additions & 0 deletions plugins/medical_kb/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
"""
Medical Knowledge Base Adapter for openJiuwen DeepSearch

将 medical-ai-innovation 的知识库RAG能力适配到 DeepSearch 框架,
作为医疗领域知识库插件,提供诊疗指南的智能检索与问答。

适配器说明:
- 实现 DeepSearch 的 KnowledgeBasePlugin 接口
- 支持多级检索(向量+关键词+知识图谱)
- 支持引用溯源(片段级引用)
"""
from __future__ import annotations
import sys
import os
from typing import Optional

# 将 medical-ai-innovation 加入路径(相对路径,安装后可不依赖)
try:
from medical_ai_innovation.knowledge_base.schemas import (
Guideline, RetrievalResult, AnswerWithCitations
)
from medical_ai_innovation.knowledge_base.kb_engine import KnowledgeBaseEngine
from medical_ai_innovation.knowledge_base.rag_pipeline import RAGPipeline
except ImportError:
# 开发模式:通过相对路径查找
_plugin_dir = os.path.dirname(os.path.abspath(__file__))
_project_root = os.path.abspath(os.path.join(_plugin_dir, "..", "..", ".."))
if _project_root not in sys.path:
sys.path.insert(0, _project_root)
from medical_ai_innovation.knowledge_base.schemas import (
Guideline, RetrievalResult, AnswerWithCitations
)
from medical_ai_innovation.knowledge_base.kb_engine import KnowledgeBaseEngine
from medical_ai_innovation.knowledge_base.rag_pipeline import RAGPipeline


class MedicalKnowledgeBasePlugin:
"""
DeepSearch 医疗知识库插件

提供医疗领域知识库的检索与问答能力,支持:
- 诊疗指南的智能问答
- 多级检索(向量 + BM25关键词 + 知识图谱)
- 引用溯源(每个回答附带指南原文引用)
- 版本对比(新旧版指南差异检测)

用法:
plugin = MedicalKnowledgeBasePlugin()
plugin.load_demo_guidelines()
result = plugin.search("空腹血糖正常值是多少?")
print(result["answer"])
"""

def __init__(self):
self.engine = KnowledgeBaseEngine()
self.pipeline = RAGPipeline(self.engine)

# ---------- DeepSearch 标准接口 ----------

def search(self, query: str, top_k: int = 5, **kwargs) -> dict:
"""
搜索知识库(DeepSearch标准接口)

Args:
query: 查询文本
top_k: 返回结果数
**kwargs: 其他参数(disease, latest_only等)

Returns:
{
"answer": str,
"citations": [{"content": str, "source": str, "score": float}],
"total_found": int,
"plugin": "medical_kb"
}
"""
disease = kwargs.get("disease", "")
include_version_compare = kwargs.get("include_version_compare", False)

result = self.pipeline.answer(
question=query,
top_k=top_k,
disease=disease,
include_version_compare=include_version_compare,
)

citations = []
for r in result.citations:
clause = r.clause
if not clause:
continue
citations.append({
"content": clause.content or "",
"source": f"{clause.guideline_id or ''} | {clause.clause_num or ''} {clause.title or ''}",
"score": getattr(r, "score", 0.0) or 0.0,
"method": getattr(r, "method", "") or "",
"evidence_level": clause.evidence_level or "",
"recommendation": clause.recommendation or "",
})

return {
"answer": result.answer,
"citations": citations,
"total_found": len(result.citations),
"plugin": "medical_kb",
"retrieval_methods": result.retrieval_methods,
}

def batch_search(self, queries: list[str], **kwargs) -> list[dict]:
"""批量搜索"""
return [self.search(q, **kwargs) for q in queries]

# ---------- 知识库管理 ----------

def load_guideline(self, guideline: Guideline):
"""加载诊疗指南"""
self.engine.load_guideline(guideline)

def load_guidelines(self, guidelines: list[Guideline]):
"""批量加载指南"""
self.engine.load_guidelines(guidelines)
self.engine.build_index()

def load_demo_guidelines(self):
"""加载内置示例指南(糖尿病+高血压)"""
from medical_ai_innovation.knowledge_base.demo_data import get_demo_guidelines
self.load_guidelines(get_demo_guidelines())

def get_guideline_list(self) -> list[dict]:
"""获取已加载的指南列表"""
return self.engine.get_guideline_summary()

def get_stats(self) -> dict:
"""获取知识库统计"""
return self.engine.stats

# ---------- 工具方法 ----------

def explain(self, query: str) -> dict:
"""解释检索过程"""
return self.pipeline.explain_retrieval(query)

def reset(self):
"""重置对话上下文"""
self.pipeline.reset_context()

@property
def plugin_info(self) -> dict:
"""插件元信息"""
return {
"name": "medical_kb",
"display_name": "医疗知识库",
"description": "基于诊疗指南的医疗知识库检索与问答",
"version": "1.0.0",
"author": "leppardwang",
"languages": ["zh"],
"domains": ["healthcare", "medical"],
"guidelines_count": len(self.engine.guidelines),
"knowledge_base": "中国2型糖尿病防治指南(2024版/2020版), 中国高血压防治指南(2024版)",
}


# ========== 命令行测试 ==========

def main():
"""测试插件"""
print("🩺 Medical Knowledge Base Plugin for DeepSearch")
print("=" * 50)

plugin = MedicalKnowledgeBasePlugin()
plugin.load_demo_guidelines()

stats = plugin.get_stats()
print(f"📚 已加载 {stats.get('guidelines', 0)} 部指南")
print()

while True:
try:
q = input("🧑 请输入医疗问题 (输入 /quit 退出): ").strip()
except (EOFError, KeyboardInterrupt):
break

if not q:
continue
if q.lower() in ("/quit", "/exit"):
break

result = plugin.search(q, include_version_compare=True)
answer_preview = result["answer"][:300]
print(f"\n🤖 {answer_preview}")
if len(result["answer"]) > 300:
print(" ...(回答已截断,完整内容请查看引用)")
print(f"📚 引用 {result['total_found']} 条")
for i, c in enumerate(result["citations"][:3], 1):
source = c.get("source", "未知来源")
score = c.get("score", 0)
print(f" [{i}] {source} (score: {score})")
print()


if __name__ == "__main__":
main()