山东大学软件学院项目实训-创新实训-计科智伴(一)——知识库底座的设计与构建
作为"计科智伴"项目中负责多模态模块、教学与交互 Agent、Neo4j 与 pgvector 数据库及 RAG 功能的成员,我的任务是为整个 AI Agent 体系构建一个稳固、可扩展的知识库底座。
本篇博客将系统性地阐述我对计科智伴知识库底座的设计思路与构建实践
一、 核心设计思路:为什么要"双库协同"
教学场景需要的是对某个问题的语义检索——学生问"快速排序的时间复杂度为什么是 O(n log n)",Agent 从海量资料中找出语义最相关文字生成回答。这是典型的向量检索场景,适合用 pgvector。
但诊断场景不同。学生做错一道题,Agent 需要判断错误根源并据此溯源到对应的知识漏洞、再找到前置薄弱点。这是关系推理问题,向量做不了,必须用图数据库 Neo4j 来承载课程-章节-知识点之间的结构化关系。
由此确立了双库协同的底层架构:
- Neo4j 图谱层:承载知识点的结构骨架与语义关系,服务诊断 Agent
- pgvector 向量层:承载教材/博客切片的高维表示,服务 RAG 教学 Agent
这两层之间通过 doc_id(文章唯一标识)和 topic_name(知识点名称)做跨库关联,各司其职又能互通。
二、 分层架构:从原始数据到 Agent 可调用资产
将一篇博客变成 AI Agent 可直接调用的知识,要经过四层处理。
第一层 · 爬取层(crawlers/)
从 CSDN、GitHub 开源仓库等多源采集原始文本,输出统一结构的 JSON
第二层 · 处理层(processors/)
清洗噪声、按 Markdown 标题和滑动窗口两级切分文本块、调用向量模型生成嵌入
第三层 · 入库层(db_builders/)
分别写入 Neo4j 图谱和 pgvector 向量库,两边幂等、可重复执行
第四层 · 调度层(scripts/)
一键全流程入口,支持分阶段跳过(--skip-crawl、--skip-embed 等)。
这种分层让整个流程既能一键跑完,也能在任何一个环节出问题时单独重跑该环节
三、 关键技术环节详解
1. 多源爬取策略:使用CSDN和Github优质资源
在爬取阶段我确立了两条互补的采集通道:CSDN 做广度覆盖,GitHub 做深度质量。
CSDN 作为中文技术社区的最大内容池,与学生日常的学习习惯对齐,适合作为语料的主体
GitHub 则承担高质量种子内容的角色。我精选了社区口碑较好的几个 CS 学习仓库——CyC2018/CS-Notes、Snailclimb/JavaGuide、labuladong/fucking-algorithm、arkingc/note 等,将其中核心文档的 raw URL 硬编码成清单直接抓取。
不论数据来自哪个来源,最终都会被归一化为统一的 ArticleItem 结构,并以 URL 的 MD5 作为 doc_id 进行去重。这种设计让下游的清洗、切片、向量化环节完全无需区分数据来源。
最终两个通道的采集结果如下:
| 来源 | 文章数(去重后) | 切片数 | 占比 |
|---|---|---|---|
| CSDN | 约 1,450 篇 | 16,178 条 | 85.6% |
| GitHub | 约 130 篇 | 2,730 条 | 14.4% |
| 合计 | 1,578 篇 | 18,908 条 | 100% |
CSDN 在数量上占主导,GitHub 虽然文章数仅占 8%,但由于其笔记篇幅更长、结构更完整,贡献的切片占比达到 14%,二者形成了符合预期的**"广度 + 深度"**互补格局。
2. 文本切片:两级分块保留语义完整性
RAG 效果的好坏,很大程度取决于文本切片质量。单纯按固定长度切,容易把一个完整概念切成两半;切得太大又会稀释单个切片的语义。我采用了两级分块策略:
- 一级切分:按 Markdown 标题
#########粗切,保留段落语义边界 - 二级切分:对超过 512 字符的块,按段落做 64 字符重叠的滑动窗口细切
- 质量过滤:小于 80 字符的碎片直接丢弃(通常是标题、引用、代码注释等无效内容)
CHUNK_SIZE = 512 这个值是反复调整后的折中:太小会损失上下文(比如一段代码示例会被拦腰截断),太大会导致向量语义被稀释,检索时相关性下降。
3. 向量化:选择中文特化的 BGE 模型
向量模型选型上,对比了 paraphrase-multilingual-mpnet-base-v2(多语言通用)和 BAAI/bge-large-zh-v1.5(中文特化)。最终选后者,原因有三:
- 我们的语料 95% 以上是中文技术博客,中文特化模型在检索任务上召回率显著更高
- BGE 模型官方推荐归一化后使用,归一化向量的余弦相似度等于点积,可简化计算
- 1024 维的嵌入维度在 pgvector 上走 HNSW 索引性能足够
向量化时采用批处理(batch_size=32),一次对一批文本做嵌入,在 GPU 上吞吐量显著提升。
4. 知识图谱建模:静态骨架 + 动态关联
Neo4j 的图谱模型有三层骨架加两类关键语义关系:
(Subject) -[:HAS_CHAPTER]-> (Chapter) -[:HAS_TOPIC]-> (Topic)
(Topic) -[:PREREQUISITE_OF]-> (Topic) 前置关系
(Topic) -[:RELATED_TO] -> (Topic) 关联关系
(Topic) -[:HAS_ARTICLE] -> (Article) 文章关联
(Article) -[:TAGGED_WITH] -> (Tag) 标签
Subject → Chapter → Topic 三层骨架由 config/subjects.json 配置文件驱动,里面定义了 7 门课程、每门课的章节结构、以及每个章节下的核心知识点。这是"静态"部分,由课程专家定义,图谱加载时一次性写入。
PREREQUISITE_OF(前置)和 RELATED_TO(关联)是关键的"语义关系",我手工标注了 40+ 对核心关系,例如:
链表 → 栈 → 队列(前置递进)进程同步 → 死锁(概念前置)KMP 算法 ↔ 字符串算法(语义关联)死锁 ↔ 银行家算法(应用关联)
这些关系直接关系到诊断 Agent 的错因溯源能力。学生做错一道关于死锁的题目,诊断 Agent 定位到"死锁"这个 Topic 节点后,沿 PREREQUISITE_OF 反向查找"进程同步"、"互斥"等前置知识点,就能判断学生究竟是在哪一环节出的问题,再推送对应的补练题。
5. 双库写入的幂等设计
整个工具链要能反复运行而不产生脏数据。Neo4j 侧用 MERGE 语句(存在则更新、不存在则新建);pgvector 侧用 ON CONFLICT (chunk_id) DO UPDATE 实现 UPSERT。每批 500 条用 UNWIND 批量写入,既保证性能又保证幂等性。
四、 部署环节:容器化与踩过的三个坑
数据库选择用 Docker Compose 部署,pgvector/pgvector:pg16 官方镜像已经内置扩展、neo4j:5.19-community 是 LTS 版本。理论上一条 docker compose up -d 就能启动,实际却因为时序问题花了不少时间调通:
坑一:容器密码幽灵
Docker 的 POSTGRES_PASSWORD 和 NEO4J_AUTH 环境变量只在数据卷首次初始化时生效。
坑二:Neo4j 握手时间差
Neo4j 的 HTTP 端口(7474)先起来,Bolt 端口(7687)要晚 30-60 秒。如果脚本启动太快,会收到错误
坑三:认证限流
Neo4j 对短时间内多次认证失败会触发 AuthenticationRateLimit。
这三步是容器化应用永远绕不过去的问题:端口监听 ≠ 服务可用 ≠ 认证通过。
五、 今日成果数据
最终跑完后的实际数据量远超任务书指标:
| 指标 | 任务书要求 | 实际完成 | 完成度 |
|---|---|---|---|
| 教材切片数 | ≥ 1000 条 | 18,908 条 | 18.9 倍 |
| 覆盖课程数 | ≥ 5 门 | 7 门 | 140% |
| 原始文章数(去重后) | - | 1,578 篇 | - |
| Neo4j 图谱节点 | - | 358 个 | - |
| Neo4j 关系 | - | 378 条 | - |
按课程分布看,数据结构类内容最多(6,042 条切片),这是专业核心课;其次是算法(2,832 条)、操作系统(2,402 条)、数据库(2,147 条)、计算机网络(1,897 条)、高数(1,798 条)、线代(1,595 条)。分布符合课程权重预期。
六、 为后续 Agent 开发铺设的接口
知识库最终要服务于教学、诊断、规划、交互四个 Agent。因此我设计了如下对外接口设计好
教学 Agent 需要的接口(我在 pgvector_builder.py 中实现):
1.search_similar(query_vector, subject, top_k):按课程过滤的向量检索
2.hybrid_search(query, query_vector, subject, top_k):向量 + 全文混合检索,解决短术语召回问题
诊断 Agent 需要的接口(在 neo4j_builder.py 中实现):
1.find_related_topics(topic_name, depth):给定错因知识点,溯源前置/关联知识点
2.find_articles_for_topic(topic_name, limit):给定知识点,查找关联的讲解文章
3.update_topic_mastery(topic_name, mastery):更新用户对某知识点的掌握度
4.get_weak_topics(subject, threshold):查询某课程下掌握度低于阈值的知识点
七、 总结与下一步
本次数据爬取工作收集了18,908 条切片加上 358 个图谱节点,为之后开发奠定了坚实的基础
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)