# 中医知识图谱问答系统 - API接口文档

## 📖 项目简介

基于FastAPI和LangGraph的中医知识图谱智能问答系统，提供RESTful API接口服务。

**技术栈：**
- FastAPI - 高性能Web框架
- LangGraph - 工作流编排
- Neo4j - 图数据库
- FAISS - 向量相似度搜索
- LLM - 大语言模型

---

## 🚀 快速开始

### 1. 启动服务

```bash
cd __005_fastapi
python main.py
```

或使用启动脚本：
```bash
# Windows
start_server.bat
```

### 2. 访问地址

- **API基础地址**: `http://localhost:8000`
- **交互式文档**: `http://localhost:8000/docs` (Swagger UI)
- **ReDoc文档**: `http://localhost:8000/redoc`

---

## 📡 API接口列表

### 1. 健康检查接口

**接口地址**: `GET /`

**功能描述**: 检查服务是否正常运行

**请求参数**: 无

**响应示例**:
```json
{
  "status": "ok",
  "message": "中医知识图谱问答系统服务运行正常"
}
```

**状态码**:
- `200` - 服务正常

---

### 2. 中医问答接口（核心接口）

**接口地址**: `POST /process`

**功能描述**: 接收用户问题，返回中医知识图谱的智能回答

**Content-Type**: `application/json`

#### 请求参数

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| input | string | 是 | 用户输入的问题 |
| user_id | string | 是 | 用户唯一标识,用于隔离不同用户的对话历史/记忆。同一用户需在多次请求中传入相同的user_id才能延续上下文,不同用户请使用不同的user_id |

#### 请求示例

```json
{
  "input": "人参有什么功效？",
  "user_id": "user-123"
}
```

#### 响应参数

| 参数名 | 类型 | 说明 |
|--------|------|------|
| input | string | 用户原始输入 |
| output | string | 系统生成的回答 |

#### 响应示例

**成功响应**:
```json
{
  "input": "人参有什么功效？",
  "output": "人参具有大补元气、复脉固脱、补脾益肺、生津养血、安神益智等功效。适用于体虚欲脱、肢冷脉微、脾虚食少、肺虚喘咳等症状。"
}
```

**错误响应**:
```json
{
  "input": "",
  "output": "系统出错了，请重试！"
}
```

#### 状态码

| 状态码 | 说明 |
|--------|------|
| 200 | 请求成功 |
| 500 | 服务器内部错误 |

#### cURL示例

```bash
curl -X POST "http://localhost:8000/process" \
  -H "Content-Type: application/json" \
  -d '{"input": "感冒了吃什么药？", "user_id": "user-123"}'
```

#### Python示例

```python
import requests

url = "http://localhost:8000/process"
data = {"input": "人参有什么功效？", "user_id": "user-123"}

response = requests.post(url, json=data)
result = response.json()

print(f"问题: {result['input']}")
print(f"回答: {result['output']}")
```

#### JavaScript示例

```javascript
fetch('http://localhost:8000/process', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    input: '人参有什么功效？',
    user_id: 'user-123'
  })
})
.then(response => response.json())
.then(data => {
  console.log('问题:', data.input);
  console.log('回答:', data.output);
})
.catch(error => {
  console.error('Error:', error);
});
```

---

### 3. 中医问答接口(流式版)

**接口地址**: `POST /process_stream`

**功能描述**: 与 `/process` 接受同样的请求参数(`input`、`user_id`),但以换行分隔的JSON消息流
(`text/plain`,非严格SSE)返回结果。LangGraph每个节点执行完毕,立即产出一条该节点的状态增量作为
"思考过程";图执行结束后再产出一条最终答案消息。

#### 请求参数

同 `/process`:`input`(必填)、`user_id`(必填)。

#### 响应格式(每行一个JSON对象)

| 消息type | 字段 | 说明 |
|---------|------|------|
| thinking | node, data | 某个节点刚执行完毕,`data`为该节点这一步产生的状态增量(思考过程中间结果) |
| final | input, output | 图执行成功结束,`output`为最终回答 |
| error | input, output | 图执行异常,`output`固定为"系统出错了，请重试！" |

#### 响应示例

```
{"type": "thinking", "node": "语义转写节点", "data": {"input_semantic_trans": "人参有什么功效？"}}
{"type": "thinking", "node": "意图识别节点", "data": {"is_zhongyi_intent": true}}
{"type": "thinking", "node": "实体抽取节点", "data": {"user_input_herbs": ["人参"]}}
...
{"type": "final", "input": "人参有什么功效？", "output": "人参具有大补元气、复脉固脱..."}
```

#### Python示例

```python
import json
import requests

resp = requests.post(
    "http://localhost:8000/process_stream",
    json={"input": "人参有什么功效？", "user_id": "user-123"},
    stream=True,
    timeout=120,
)
for line in resp.iter_lines(decode_unicode=True):
    if not line:
        continue
    message = json.loads(line)
    if message["type"] == "thinking":
        print("思考中:", message["node"], message["data"])
    else:
        print("最终回答:", message["output"])
```

---

### 4. 静态资源访问

**接口地址**: `GET /picture/{filename}`

**功能描述**: 访问静态图片资源

**路径参数**:
- `filename`: 图片文件名

**示例**:
```
http://localhost:8000/picture/graph.jpg
```

**配置说明**:
静态文件目录通过 `app.mount("/picture", StaticFiles(directory=...))` 挂载

---

## 🔄 工作流程

当用户通过 `/process` 接口提交问题时，系统会执行以下流程：

```
用户输入
  ↓
意图识别（判断是否中医相关）
  ↓
├─ 非中医问题 → 直接LLM回答
└─ 中医问题
     ↓
  实体抽取（症状、疾病、方剂、药材等）
     ↓
  FAISS向量匹配（标准化实体名称）
     ↓
  生成Cypher查询语句
     ↓
  验证Cypher语法
     ↓
  执行Neo4j图数据库查询
     ↓
  生成自然语言回答
     ↓
  返回结果
```

---

## 💡 使用示例

### 示例1: 中药查询

**请求**:
```json
{
  "input": "人参的性味归经是什么？"
}
```

**响应**:
```json
{
  "input": "人参的性味归经是什么？",
  "output": "人参性温，味甘、微苦，归脾、肺、心经。"
}
```

### 示例2: 方剂查询

**请求**:
```json
{
  "input": "桂枝汤的组成和功效是什么？"
}
```

**响应**:
```json
{
  "input": "桂枝汤的组成和功效是什么？",
  "output": "桂枝汤由桂枝、芍药、甘草、生姜、大枣组成。具有解肌发表、调和营卫的功效，主治外感风寒表虚证。"
}
```

### 示例3: 症状咨询

**请求**:
```json
{
  "input": "我感冒了，有点咳嗽和发热，应该吃什么药？"
}
```

**响应**:
```json
{
  "input": "我感冒了，有点咳嗽和发热，应该吃什么药？",
  "output": "根据您的症状（感冒、咳嗽、发热），建议可以考虑以下方剂：银翘散、桑菊饮等。但请注意，具体用药需要结合您的体质和病情，建议咨询专业中医师。"
}
```

### 示例4: 非中医问题

**请求**:
```json
{
  "input": "今天天气怎么样？"
}
```

**响应**:
```json
{
  "input": "今天天气怎么样？",
  "output": "抱歉，我无法提供天气信息。我是一个中医知识图谱问答系统，主要回答中医药相关的问题，如中药、方剂、症状、疾病等。如果您有中医方面的问题，欢迎提问！"
}
```

---

## ⚙️ 配置说明

### 环境变量配置

在 `common/.env` 文件中配置以下参数：

```env
# 大模型配置
MODEL_API_KEY=your_api_key
MODEL_BASE_URL=https://api.example.com
MODEL_NAME=gpt-3.5-turbo

# Neo4j数据库配置
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=your_password

# Embedding模型配置
EMBEDDING_MODEL_PATH=./bge-large-zh-v1.5
```

### FAISS索引路径

FAISS索引文件路径配置在以下文件中：

- `__004__langgraph_more_nodes/nodes/match_entity_node.py`
- `__003__create__information/__003__faiss_embedding.py`

**注意**: 由于FAISS库在Windows中文路径下的兼容性问题，建议使用英文路径存储FAISS索引文件。

```python
index_path = r"E:\ctm_index\nero4j_embedding_faiss.index"
id2text_path = r"E:\ctm_index\nero4j_embedding_faiss_id2text.pkl"
```

---

## 🔧 开发指南

### 项目结构

```
__005_fastapi/
├── main.py                 # FastAPI主程序
├── start_server.bat        # Windows启动脚本
├── test_api.py            # API测试脚本
├── quick_test.py          # 快速诊断脚本
├── README.md              # 使用说明
└── API_INTERFACE.md       # API接口文档（本文件）

__004__langgraph_more_nodes/
├── full_workflow.py       # LangGraph工作流
├── agent_state.py         # Agent状态定义
└── nodes/                 # 各个处理节点
    ├── zhongyi_intent_node.py      # 意图识别
    ├── extract_entity_node.py      # 实体抽取
    ├── match_entity_node.py        # 实体匹配
    ├── generate_cypher_node.py     # Cypher生成
    ├── check_cypher_node.py        # Cypher验证
    ├── run_cypher_node.py          # Cypher执行
    └── generate_answer_node.py     # 答案生成
```

### 添加新接口

在 `main.py` 中添加新的路由：

```python
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class QueryRequest(BaseModel):
    input: str
    max_retries: int = 3

@app.post("/api/custom")
async def custom_endpoint(request: QueryRequest):
    # 你的业务逻辑
    result = process_query(request.input)
    return {"result": result}
```

### 错误处理

系统会自动捕获异常并返回友好的错误信息：

```python
try:
    data = await request.json()
    input_text = data.get("input", "")
    output = await zhongyi_response(input_text)
    result = {
        "input": input_text,
        "output": output
    }
except Exception as e:
    import traceback
    traceback.print_exc()
    result = {
        "input": "",
        "output": "系统出错了，请重试！"
    }
```

---

## 📊 性能优化

### 1. 超时设置

对于长时间运行的查询，建议在客户端设置合理的超时时间：

```python
# Python requests示例
response = requests.post(url, json=data, timeout=120)  # 120秒超时
```

### 2. 并发处理

FastAPI天然支持异步处理，可以同时处理多个请求。

### 3. 缓存策略

对于频繁查询的问题，可以实现缓存机制：

```python
from functools import lru_cache

@lru_cache(maxsize=1000)
def cached_zhongyi_response(input_text: str):
    return zhongyi_response(input_text)
```

---

## ❓ 常见问题

### Q1: 服务启动失败怎么办？

**A**: 检查以下几点：
1. 确认端口8000未被占用
2. 检查 `common/.env` 配置文件是否正确
3. 确认所有依赖已安装：`pip install -r requirements.txt`
4. 查看控制台错误信息

### Q2: 响应时间过长怎么办？

**A**: 可能的原因：
1. LLM模型响应慢 - 考虑使用更快的模型
2. FAISS索引较大 - 优化索引结构
3. Neo4j查询复杂 - 优化Cypher语句
4. 首次加载需要初始化 - 后续请求会更快

### Q3: 如何修改端口号？

**A**: 修改 `main.py` 最后一行：

```python
uvicorn.run(app, host="0.0.0.0", port=8001)  # 改为8001端口
```

### Q4: 如何部署到生产环境？

**A**: 推荐使用以下方式之一：

**方式1: 使用Gunicorn + Uvicorn**
```bash
pip install gunicorn
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000
```

**方式2: 使用Docker**
```dockerfile
FROM python:3.10
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
```

**方式3: 使用Nginx反向代理**
```nginx
server {
    listen 80;
    server_name your-domain.com;
    
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
```

### Q5: 如何监控API使用情况？

**A**: 可以添加日志记录：

```python
import logging
from datetime import datetime

logging.basicConfig(filename='api.log', level=logging.INFO)

@app.post("/process")
async def process(request: Request):
    start_time = datetime.now()
    data = await request.json()
    
    # 记录请求
    logging.info(f"Request: {data.get('input')} at {start_time}")
    
    # ... 业务逻辑
    
    end_time = datetime.now()
    duration = (end_time - start_time).total_seconds()
    
    # 记录响应时间和结果
    logging.info(f"Response time: {duration}s")
    
    return JSONResponse(content=result)
```

---

## 🔐 安全建议

### 1. 添加API认证

```python
from fastapi import Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

security = HTTPBearer()

async def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
    if credentials.credentials != "your-secret-token":
        raise HTTPException(status_code=401, detail="Invalid token")
    return credentials

@app.post("/process")
async def process(request: Request, credentials: dict = Depends(verify_token)):
    # 你的业务逻辑
    pass
```

### 2. 限制请求频率

```python
from slowapi import SlowAPIMiddleware
from slowapi.errors import RateLimitExceeded

app.state.limiter = Limiter(default_rate_limit="10/minute")
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)

@app.post("/process")
@limiter.limit("5/minute")
async def process(request: Request):
    # 每分钟最多5次请求
    pass
```

### 3. 输入验证

```python
from pydantic import BaseModel, Field

class QueryRequest(BaseModel):
    input: str = Field(..., min_length=1, max_length=500, description="用户问题")
```

---

## 📝 更新日志

### v1.0.0 (2024-01-01)
- ✅ 初始版本发布
- ✅ 实现基本的中医问答接口
- ✅ 集成LangGraph工作流
- ✅ 支持Neo4j图数据库查询
- ✅ 支持FAISS向量相似度搜索
- ✅ 提供Swagger交互式文档

---

## 📞 技术支持

如有问题或建议，请联系：
- 📧 Email: [your-email@example.com]
- 💬 Issues: [GitHub Issues链接]
- 📖 文档: [项目Wiki链接]

---

## 📄 License

本项目仅供学习和研究使用。

---

## 🌟 附录

### A. 完整的请求-响应流程图

```
Client                          Server                         Backend
  |                               |                               |
  |-- POST /process ------------->|                               |
  |   {input: "人参功效"}          |                               |
  |                               |-- 意图识别 ------------------>| 
  |                               |                               |
  |                               |-- 实体抽取 ------------------>|
  |                               |                               |
  |                               |-- FAISS匹配 ---------------->|
  |                               |                               |
  |                               |-- 生成Cypher --------------->|
  |                               |                               |
  |                               |-- 执行Neo4j查询 ------------>|
  |                               |                               |
  |                               |-- LLM生成回答 -------------->|
  |                               |                               |
  |<-- 200 OK --------------------|                               |
  |   {input, output}             |                               |
  |                               |                               |
```

### B. 支持的实体类型

| 实体类型 | 英文标识 | 示例 |
|---------|---------|------|
| 症状 | Symptom | 咳嗽、发热、失眠 |
| 疾病 | Disease | 风寒感冒、脾虚泄泻 |
| 方剂 | Formula | 桂枝汤、麻黄汤 |
| 药材 | Herb | 人参、黄芪、当归 |
| 功效 | Effect | 清热、补气、祛湿 |
| 出处 | Source | 《伤寒论》《本草纲目》 |

### C. 常用测试问题

```
1. 人参有什么功效？
2. 桂枝汤的组成是什么？
3. 感冒了吃什么药？
4. 如何治疗失眠？
5. 黄芪和人参有什么区别？
6. 《伤寒论》中有哪些经典方剂？
7. 咳嗽有哪些中药可以治疗？
8. 补气的中药有哪些？
```

---

**最后更新时间**: 2024-01-01

**文档版本**: v1.0.0
