Skip to content

Ottavia-Luca/Nuist-OJ-Graphrag-Luca

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GraphRAG Service

面向 OJ 场景的图谱增强检索服务。项目使用 FastAPI + Neo4j + Redis + MySQL 组织题目、提交记录和知识点,提供题目讲解上下文接口,以及基于 webhook 的增量同步能力。

当前代码重点在于“构建和返回讲解上下文”,而不是直接生成自然语言答案。/explain/problem 会返回题面、知识点、推荐题目和经典 AC 代码,方便上层服务继续做回答生成或页面展示。

功能概览

  • 题目图谱:将 OJ 题目写入 Neo4j,保存题面向量、难度、提交统计等属性。
  • 提交图谱:将 AC 提交写入 Neo4j,建立 User -> Submission -> Problem 关系,并为代码生成向量。
  • 知识点抽取:批量 ETL 时调用 DeepSeek,从题面中提取知识点并建立 Problem -> KnowledgePoint 关系。
  • 增量同步:通过 /sync/webhook 接收事件,写入 Redis 队列,由 worker 异步消费并同步入图。
  • 讲解上下文:通过 /explain/problem 返回题面、知识点、推荐题和“经典 AC 代码”。

系统结构

MySQL(problem/submission/submission_misc)
        |
        | 批量 ETL / webhook 增量事件
        v
SentenceTransformers ----> Neo4j <---- DeepSeek(知识点抽取)
        ^                    |
        |                    |
      worker              FastAPI
        |                    |
      Redis queue      /explain/problem
                       /sync/webhook
                       /health

仓库结构

app/
  main.py               FastAPI 入口
  config.py             环境变量配置
  deps.py               Neo4j 连接生命周期
  models.py             请求/响应模型
  worker.py             Redis 队列消费者
  routers/
    explain.py          讲解上下文接口
    sync.py             webhook 接口
  services/
    embedding.py        文本/代码向量化
    graph.py            Neo4j 写入与查询
  etl/
    batch.py            批量导入 MySQL -> Neo4j
docker-compose.yml      Neo4j 本地开发环境
pyproject.toml          Python 依赖定义

依赖服务

项目运行依赖以下组件:

  • Python 3.11+
  • Neo4j 5.x
  • Redis 5+
  • MySQL / MariaDB
  • DeepSeek API Key(用于批量知识点抽取;未配置时 ETL 会跳过该能力)

注意:

  • 仓库内的 docker-compose.yml 只启动 Neo4j,不会启动 RedisMySQL
  • 向量模型默认配置为 BAAI/bge-m3BAAI/bge-code-v1。如果本地没有缓存,sentence-transformers 首次加载时可能会下载模型;也可以把环境变量改为本地模型目录。

环境变量

复制示例配置:

cp .env.example .env

.env 关键配置如下:

# Neo4j
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=password123

# MySQL / MariaDB
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASSWORD=your_mysql_password
MYSQL_DB=levoj

# DeepSeek
DEEPSEEK_API_KEY=your_deepseek_api_key
DEEPSEEK_BASE_URL=https://api.deepseek.com

# Embedding models
EMBEDDING_MODEL_NAME=BAAI/bge-m3
CODE_EMBEDDING_MODEL_NAME=BAAI/bge-code-v1

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_QUEUE_KEY=graphrag:sync_queue

本地启动

1. 安装 Python 依赖

python -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -e ".[dev]"

2. 启动 Neo4j

docker compose up -d

启动后可以打开 Neo4j Browser:

  • 地址:http://localhost:7474
  • 用户名:neo4j
  • 密码:password123

3. 准备 Redis 与 MySQL

确保以下资源已可用:

  • Redis:REDIS_HOST:REDIS_PORT
  • MySQL:包含 problemsubmissionsubmission_misc

当前代码默认依赖的字段如下:

  • problem: id, title, content, difficulty, submits, accepts, timeLimit, memoryLimit, status, closed
  • submission: id, userId, problemId, language, status, time, memory
  • submission_misc: submissionId, code

4. 启动 API 服务

uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

可用地址:

  • OpenAPI 文档:http://localhost:8000/docs
  • 健康检查:http://localhost:8000/health

5. 首次批量构图

python -m app.etl.batch

批量 ETL 会做这些事:

  • 从 MySQL 读取题目和 AC 提交
  • 用本地向量模型生成题面向量和代码向量
  • 将题目、提交、用户关系写入 Neo4j
  • 调用 DeepSeek 提取知识点并建立知识点关系
  • 创建 Neo4j 向量索引

6. 启动增量同步 worker

python -m app.worker

worker 会阻塞消费 Redis 队列中的 webhook 事件,并把新题目或新 AC 提交同步进 Neo4j。

数据流说明

批量 ETL

适用于首次构图或周期性补数。

  1. 从 MySQL 查询题目和 AC 提交。
  2. 对题面和代码做 embedding。
  3. ProblemSubmissionUser 节点及关系写入 Neo4j。
  4. 对题目调用 DeepSeek 抽取知识点,建立 REQUIRES 关系。

增量同步

适用于 OJ 主系统在题目变更、提交通过后即时推送事件。

  1. 外部系统向 /sync/webhook 发事件。
  2. API 将事件写入 Redis 列表队列。
  3. app.worker 消费队列,并调用图谱写入逻辑。

API 说明

GET /health

健康检查。

响应示例:

{"status":"ok"}

POST /sync/webhook

接收外部事件并推入 Redis 队列。

请求体:

{
  "event_type": "problem.created",
  "payload": {
    "id": 1001,
    "title": "Two Sum",
    "content": "给定一个数组...",
    "difficulty": 1,
    "submits": 10,
    "accepts": 5,
    "timeLimit": 1000,
    "memoryLimit": 134217728
  }
}

支持的事件类型:

  • problem.created
  • problem.updated
  • submission.ac

返回示例:

{
  "accepted": true,
  "message": ""
}

submission.ac 事件的 payload 至少应包含:

{
  "id": 2001,
  "userId": 7,
  "problemId": 1001,
  "language": "cpp",
  "time": 12,
  "memory": 65536
}

POST /explain/problem

返回题目的讲解上下文,不直接生成答案。

请求体:

{
  "problem_id": 5614,
  "user_id": 42
}

返回字段说明:

  • title: 题目标题
  • content: 题目内容
  • knowledge_points: 关联知识点
  • recommended_problems: 基于用户已做题知识点匹配出的推荐题
  • classic_code: 该题“最经典”的 AC 代码,可能为空

调用示例:

curl --noproxy localhost -X POST http://localhost:8000/explain/problem \
  -H "Content-Type: application/json" \
  -d '{"problem_id": 5614, "user_id": 42}'

响应示例:

{
  "title": "题目标题",
  "content": "题面内容",
  "knowledge_points": ["动态规划", "背包"],
  "recommended_problems": [
    {
      "problem_id": 123,
      "title": "相似题目",
      "difficulty": 2,
      "score": 1.0
    }
  ],
  "classic_code": "#include <bits/stdc++.h>\n..."
}

Neo4j 中的数据模型

节点:

  • Problem
  • Submission
  • User
  • KnowledgePoint

关系:

  • (User)-[:SUBMITTED]->(Submission)
  • (Submission)-[:FOR_PROBLEM]->(Problem)
  • (Problem)-[:REQUIRES]->(KnowledgePoint)

向量字段:

  • Problem.content_embedding
  • Submission.code_embedding

默认向量索引:

  • problem_content_idx
  • submission_code_idx

常用查询

查看题目节点:

MATCH (p:Problem) RETURN p LIMIT 25

查看提交与题目的关系:

MATCH (s:Submission)-[:FOR_PROBLEM]->(p:Problem)
RETURN s, p
LIMIT 20

查看题目和知识点:

MATCH (p:Problem)-[:REQUIRES]->(k:KnowledgePoint)
RETURN p, k
LIMIT 20

开发说明

  • FastAPI 启动时会初始化 Neo4j 连接。
  • Neo4j 向量索引是在批量 ETL 中创建的,因此首次使用前建议先执行一次 python -m app.etl.batch
  • classic_code 不存放在 Neo4j,而是在查询时回表 MySQL submission_misc 获取。
  • 推荐题逻辑当前基于“用户已掌握知识点重合 + 最近一次 AC 题目向量”筛出候选集,排序逻辑仍然比较基础。

已知注意事项

  • docker-compose.yml 当前不包含 Redis / MySQL,需要自行提供。
  • DeepSeek 主要用于知识点抽取,不参与 /explain/problem 的直接回答生成。
  • 如果 embedding 模型没有本地缓存,首次运行可能较慢。
  • 当前仓库没有自动化测试和一键初始化脚本,首次部署建议按本文步骤逐项验证。

License

仓库中暂未声明许可证;如需开源发布,请补充明确的 LICENSE 文件。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages