WeKnora 架构学习指南 🎓
写在前面:你好!我是你的技术教练。这份指南将带你从零开始,深入理解 WeKnora 这个优秀的开源项目。别担心,我会用最通俗的方式,像聊天一样,一步步带你探索这个项目的精妙之处。准备好了吗?让我们开始这段激动人心的学习之旅!💪
第一部分:项目架构深度解析(像架构师一样俯瞰全景)🔍
1. 项目架构概览
💡 用一个生活化的类比来理解
想象一下,WeKnora 就像一个超级智能的图书馆管理系统:
- 前台接待员(Frontend):漂亮的 Vue.js 界面,你可以在这里上传文档、提出问题
- 馆长助理(Go 后端):高效的 Go 服务,负责接待访客、分配任务、协调各部门
- 资料整理员(Python DocReader):专门负责把各种格式的文档(PDF、Word、图片)整理成统一的格式
- 索引系统(向量数据库):像图书馆的卡片目录,但更智能,能理解语义
- 智能顾问(LLM):当你问问题时,它能结合检索到的资料给你专业的回答
这个比喻虽然简化了,但抓住了核心:WeKnora 就是让机器能像人类图书馆员一样,理解你的问题,找到相关资料,然后给出专业的回答。
🏗️ 核心设计特征
WeKnora 采用了现代化的分层架构 + 微服务理念,虽然不是完全的微服务架构,但已经实现了关注点分离:
表现层(Presentation Layer)
- Vue 3 + TypeScript 前端
- RESTful API 接口
- SSE(Server-Sent Events)流式响应
应用层(Application Layer)
- Handler:HTTP 请求处理
- Service:业务逻辑编排
- Repository:数据访问抽象
领域层(Domain Layer)
- 核心业务模型
- 接口定义(interfaces)
- 业务规则
基础设施层(Infrastructure Layer)
- 数据库(PostgreSQL + pgvector)
- 缓存(Redis)
- 消息队列(Asynq)
- 对象存储(MinIO/COS)
- 链路追踪(Jaeger)
🌟 与同类项目的对比
让我给你看看 WeKnora 的独特之处:
对比维度 | WeKnora | Dify | FastGPT |
---|---|---|---|
核心语言 | Go(高性能) | Python | TypeScript |
文档处理 | Python gRPC 服务 | 内置 | Node.js |
并发能力 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
知识图谱 | ✅ GraphRAG | ❌ | 部分支持 |
链路追踪 | ✅ OpenTelemetry | 基础 | 基础 |
部署复杂度 | 中等 | 简单 | 简单 |
学习价值 | Go 最佳实践 | Python AI 应用 | TS 全栈 |
背书 | 腾讯开源 | 初创公司 | 开源社区 |
WeKnora 的优势:
- ✅ 性能卓越:Go 的高并发特性,轻松应对大量请求
- ✅ 架构清晰:严格的分层设计,适合学习企业级架构
- ✅ 可观测性强:完整的链路追踪,问题排查超级方便
- ✅ 技术栈现代:依赖注入、接口设计都是业界最佳实践
- ✅ 腾讯背书:代码质量有保障,长期维护有信心
🔧 技术栈深度分析
来看看这些技术选型背后的考量:
后端技术栈(Go):
Go 1.24+
├── Web 框架: Gin (轻量、高性能)
├── ORM: GORM (类型安全、功能完整)
├── 依赖注入: uber/dig (企业级 IoC 容器)
├── 配置管理: Viper (灵活的配置方案)
├── 日志: logrus (结构化日志)
├── 链路追踪: OpenTelemetry (云原生标准)
├── 任务队列: asynq (Redis 支持的异步任务)
└── gRPC: google.golang.org/grpc (与 Python 服务通信)
为什么选 Go?
- 并发天生强大:goroutine 让处理上千并发请求变得轻而易举
- 性能接近 C:比 Python 快 10-100 倍,比 Node.js 快 2-5 倍
- 部署简单:编译成单个二进制文件,没有依赖地狱
- 适合后端服务:标准库强大,生态成熟
文档处理(Python):
Python 3.x
├── gRPC 服务
├── 文档解析库 (PyPDF2, python-docx 等)
├── OCR (可选,处理图片中的文字)
├── 图像理解 (VLM 模型)
└── Poetry 依赖管理
为什么文档处理用 Python?
- 文档处理库生态成熟(PDF、Word、OCR)
- AI/ML 库丰富(处理图像、调用视觉模型)
- 开发效率高,适合快速迭代
前端技术栈(Vue 3):
Vue 3.5+
├── 构建工具: Vite 7
├── 状态管理: Pinia
├── 路由: Vue Router 4
├── UI 框架: TDesign (腾讯自家的 UI 库)
├── Markdown 渲染: marked
├── HTTP 客户端: axios
└── TypeScript (类型安全)
数据库和中间件:
数据存储
├── PostgreSQL (ParadeDB 版本)
│ ├── pgvector (向量存储和检索)
│ └── 全文搜索扩展
├── Redis (缓存 + 任务队列)
└── MinIO (对象存储,S3 兼容)
检索引擎
├── PostgreSQL pgvector (默认)
└── Elasticsearch (可选,全文检索强项)
🎯 外部系统集成
WeKnora 设计了灵活的外部系统集成方案:
graph LR
A[WeKnora 核心] --> B[LLM 服务]
A --> C[Embedding 服务]
A --> D[Rerank 服务]
A --> E[存储服务]
A --> F[向量数据库]
B --> B1[Ollama 本地]
B --> B2[OpenAI API]
B --> B3[其他兼容 API]
C --> C1[本地 Embedding]
C --> C2[BGE API]
C --> C3[GTE API]
E --> E1[本地文件系统]
E --> E2[MinIO]
E --> E3[腾讯云 COS]
F --> F1[PostgreSQL pgvector]
F --> F2[Elasticsearch]
这种设计的好处:
- 🔄 可替换性:不喜欢 OpenAI?换成 Ollama 或其他模型即可
- 🔒 私有化部署:可以完全本地化,数据不出门
- 💰 成本可控:本地模型免费,API 按需付费
- 🚀 灵活扩展:轻松接入新的模型和服务
📊 架构流程描述
让我带你走一遍完整的"用户提问 → 获得答案"的流程:
sequenceDiagram
autonumber
participant U as 👤 用户
participant F as 🖥️ 前端
participant A as 🔧 API网关
participant S as 🧠 Session服务
participant R as 🔍 检索器
participant V as 📚 向量DB
participant L as 🤖 LLM
participant M as 💾 消息存储
U->>F: 在对话界面输入问题
F->>A: POST /api/v1/knowledge-chat/:session_id
A->>A: 认证&授权检查
A->>S: 转发到 SessionHandler.KnowledgeQA
S->>S: 加载会话上下文
alt 启用了问题改写
S->>L: 基于历史对话改写问题
L-->>S: 返回改写后的问题
end
S->>R: 调用检索器检索相关知识
par 并行检索
R->>R: BM25 关键词检索
R->>V: 向量相似度检索
end
R->>R: 合并检索结果
alt 启用了重排序
R->>L: 调用 Rerank 模型重排序
L-->>R: 返回排序后的结果
end
R-->>S: 返回 Top-K 相关文档片段
S->>S: 构建提示词(问题+上下文+历史)
S->>L: 调用 LLM 生成答案(流式)
loop 流式返回
L-->>S: 返回生成的 token
S-->>F: SSE 推送 token
F-->>U: 实时显示答案
end
L-->>S: 生成完成
S->>M: 保存问题和答案到消息表
S-->>F: 返回完成信号
F-->>U: 显示完整答案
这个流程的关键点:
- 认证授权(步骤 3):中间件自动处理,基于 JWT token
- 问题改写(步骤 5-6):解决代词指代问题,如"它怎么样" → "微信支付怎么样"
- 混合检索(步骤 8-9):BM25 + 向量检索,结合关键词和语义匹配
- 重排序(步骤 11-12):用更精确的模型对初筛结果重新排序
- 流式响应(步骤 15-18):用户不用等全部生成完,边生成边看
- 持久化(步骤 20):保存对话历史,支持多轮对话
整个流程耗时分析(以典型场景为例):
- 检索阶段:50-200ms
- Rerank 阶段:100-300ms(如果启用)
- LLM 生成:1-5 秒(取决于答案长度)
- 总计:约 2-6 秒首 token 返回
2. 目录结构与核心流程
好,现在让我们深入项目的"骨架"——目录结构。理解了目录组织,你就能快速定位任何功能的代码位置。
📁 目录组织逻辑
WeKnora 采用了按功能模块 + 按技术层次的混合组织方式:
WeKnora/
│
├── cmd/ # 🚀 应用入口
│ └── server/
│ └── main.go # ⭐ 从这里开始!程序启动入口
│
├── internal/ # 🏠 核心业务逻辑(私有包)
│ ├── application/ # 应用层
│ │ ├── service/ # 🧠 业务逻辑服务
│ │ │ ├── chat_pipline/ # 对话流水线(核心!)
│ │ │ ├── knowledge.go # 知识管理
│ │ │ ├── retriever/ # 检索器实现
│ │ │ └── ...
│ │ └── repository/ # 📊 数据访问层
│ │ ├── knowledge.go
│ │ └── retriever/ # 不同数据库的检索实现
│ │ ├── postgres/
│ │ └── elasticsearch/
│ │
│ ├── handler/ # 🎮 HTTP 请求处理器
│ │ ├── auth.go
│ │ ├── knowledge.go
│ │ ├── session.go # ⭐ 对话相关的核心 handler
│ │ └── ...
│ │
│ ├── models/ # 🤖 外部模型交互
│ │ ├── chat/ # LLM 调用
│ │ ├── embedding/ # 向量化模型
│ │ └── rerank/ # 重排序模型
│ │
│ ├── types/ # 📝 类型定义
│ │ ├── interfaces/ # ⭐ 接口定义(依赖倒置)
│ │ ├── knowledge.go
│ │ └── ...
│ │
│ ├── config/ # ⚙️ 配置管理
│ ├── container/ # 🏭 依赖注入容器
│ ├── middleware/ # 🛡️ 中间件
│ ├── router/ # 🛤️ 路由定义
│ ├── tracing/ # 🔍 链路追踪初始化
│ └── utils/ # 🔧 工具函数
│
├── services/ # 🐍 独立微服务
│ └── docreader/ # Python 文档解析服务
│ └── src/
│ ├── grpc_server.py # gRPC 服务端
│ └── parsers/ # 各种文档解析器
│
├── frontend/ # 🎨 前端应用
│ └── src/
│ ├── views/ # 页面组件
│ ├── components/ # 可复用组件
│ ├── api/ # API 调用封装
│ └── stores/ # Pinia 状态管理
│
├── migrations/ # 🗄️ 数据库迁移脚本
│ ├── mysql/
│ └── paradedb/ # ⭐ 推荐使用这个
│
├── config/ # ⚙️ 配置文件
│ └── config.yaml # ⭐ 核心配置(提示词模板在这里)
│
├── scripts/ # 🛠️ 运维脚本
│ └── start_all.sh # ⭐ 一键启动脚本
│
├── client/ # 📦 Go SDK(可选)
├── mcp-server/ # 🔌 MCP 协议服务器
├── dataset/ # 📊 评估数据集工具
└── docs/ # 📚 项目文档
🎯 关键文件定位
如果你只有 30 分钟快速了解项目,按这个顺序阅读:
第一个必读 ⭐⭐⭐
README_CN.md
- 项目总览docker-compose.yml
- 了解系统组成cmd/server/main.go
- 程序入口
理解业务流程 ⭐⭐⭐
internal/router/router.go
- API 路由定义internal/handler/session.go
- 对话处理internal/application/service/chat_pipline/chat_pipline.go
- 对话流水线
理解数据模型 ⭐⭐
internal/types/knowledge.go
- 知识库相关类型internal/types/session.go
- 会话相关类型migrations/paradedb/00-init-db.sql
- 数据库表结构
理解配置 ⭐⭐
config/config.yaml
- 核心配置(提示词模板超重要!)internal/config/config.go
- 配置加载逻辑
🔗 模块依赖关系
让我用图表清晰展示模块间的依赖关系:
graph TB
subgraph 表现层
H[Handler]
end
subgraph 应用层
S[Service]
R[Repository]
end
subgraph 领域层
I[Interfaces]
T[Types]
end
subgraph 基础设施层
M[Models<br/>LLM/Embedding]
D[Database]
C[Cache/MQ]
end
H --> S
S --> R
S --> M
R --> D
S --> C
H -.依赖.-> I
S -.依赖.-> I
R -.依赖.-> I
H --> T
S --> T
R --> T
style I fill:#e1f5ff
style T fill:#fff3e0
这种依赖关系的优点:
- ✅ 单向依赖:高层模块依赖低层模块,但通过接口解耦
- ✅ 易于测试:可以轻松 mock 接口进行单元测试
- ✅ 易于替换:比如换数据库,只需实现新的 Repository 接口
- ✅ 符合 SOLID 原则:特别是依赖倒置原则(DIP)
依赖注入容器的作用:
看看 internal/container/container.go
,你会发现它使用 uber/dig
自动管理所有依赖:
// 伪代码示例
container := dig.New()
// 注册依赖
container.Provide(NewDatabase) // 数据库连接
container.Provide(NewRepository) // 仓储层
container.Provide(NewService) // 服务层
container.Provide(NewHandler) // 处理器层
// 自动解析依赖并调用
container.Invoke(func(handler *Handler) {
// handler 的所有依赖都已自动注入
})
这就像是一个智能工厂,你只需要声明"我需要什么",容器会自动帮你创建并注入依赖,不需要手动 new
一大堆对象。
🔄 典型业务流程:知识问答全链路
让我选择最核心的业务场景——用户上传文档并进行问答——来展示数据流和控制流:
场景描述:用户上传了一份《微信支付接入指南.pdf》,然后问"如何申请商户号?"
完整流程拆解:
第一阶段:文档上传与处理 📤
sequenceDiagram
autonumber
participant U as 用户
participant F as 前端
participant KH as KnowledgeHandler
participant KS as KnowledgeService
participant FS as FileStorage
participant DR as DocReader(gRPC)
participant CS as ChunkService
participant EM as Embedding模型
participant VDB as 向量数据库
U->>F: 选择文件并上传
F->>KH: POST /api/v1/knowledge-bases/:id/knowledge/file
KH->>FS: 保存文件到存储(MinIO/COS)
FS-->>KH: 返回文件 URL
KH->>DR: gRPC 调用:解析文档
DR->>DR: 识别文档类型并解析
DR->>DR: OCR 提取图片文字
DR-->>KH: 返回解析后的文本和图片
KH->>CS: 文本分块(ChunkService)
CS->>CS: 按策略切分(512字/块,重叠50字)
CS->>EM: 批量调用 Embedding 模型
EM-->>CS: 返回向量表示
CS->>VDB: 批量插入向量数据库
VDB-->>CS: 插入成功
CS-->>KH: 文档处理完成
KH-->>F: 返回成功状态
F-->>U: 显示"文档已处理"
核心代码位置:
- 文件上传:
internal/handler/knowledge.go
-CreateKnowledgeFromFile()
- 文档解析:
services/docreader/src/grpc_server.py
-ParseDocument()
- 文本分块:
internal/application/service/chunk.go
-CreateChunks()
- 向量化:
internal/models/embedding/
各实现 - 存储:
internal/application/repository/chunk.go
-CreateBatch()
这个阶段的关键技术点:
gRPC 跨语言调用:Go 调用 Python 服务
// Go 端 conn, _ := grpc.Dial("docreader:50051") client := pb.NewDocReaderClient(conn) response, _ := client.ParseDocument(ctx, &pb.ParseRequest{ ...})
文本分块策略:
- 默认 512 个字符一块
- 重叠 50 个字符(保持上下文连贯)
- 按段落、句子优先切分(不会切断句子)
批量向量化:
- 不是一条一条调用 Embedding API
- 而是批量调用(比如 32 条一批)
- 大幅提升效率,降低成本
异步处理:
- 文档上传后立即返回
- 后台使用 Asynq 任务队列异步处理
- 用户可以查看处理进度
第二阶段:用户问答 💬
sequenceDiagram
autonumber
participant U as 用户
participant F as 前端
participant SH as SessionHandler
participant CP as ChatPipeline
participant RW as Rewriter
participant RT as Retriever
participant RK as Reranker
participant LLM as LLM模型
participant MS as MessageService
U->>F: 输入"如何申请商户号?"
F->>SH: POST /knowledge-chat/:session_id
SH->>SH: 加载会话上下文
SH->>CP: 启动对话流水线
alt 问题需要改写
CP->>RW: 改写问题(补全指代)
RW->>LLM: 调用 LLM 改写
LLM-->>RW: 返回改写结果
RW-->>CP: 改写后的问题
end
CP->>RT: 混合检索
par BM25 关键词检索
RT->>RT: 分词:[申请, 商户号]
RT->>RT: BM25 算法打分
and 向量语义检索
RT->>EM: 问题向量化
EM-->>RT: 问题向量
RT->>VDB: 相似度检索
VDB-->>RT: Top-30 候选
end
RT->>RT: 合并去重
alt 启用重排序
RT->>RK: 重排序
RK->>LLM: 调用 Rerank 模型
LLM-->>RK: 重排后的结果
RK-->>RT: Top-5 最相关片段
end
RT-->>CP: 返回相关文档片段
CP->>CP: 构建完整提示词
CP->>LLM: 流式调用 LLM
loop 流式返回
LLM-->>CP: 返回 token
CP-->>SH: SSE 推送
SH-->>F: 推送给前端
F-->>U: 实时显示
end
CP->>MS: 保存消息记录
CP-->>SH: 完成信号
核心代码位置:
- 对话入口:
internal/handler/session.go
-KnowledgeQA()
- 流水线编排:
internal/application/service/chat_pipline/chat_pipline.go
- 问题改写:
internal/application/service/chat_pipline/rewrite.go
- 检索器:
internal/application/service/retriever/keywords_vector_hybrid_indexer.go
- LLM 调用:
internal/models/chat/
各实现
这个阶段的关键技术点:
问题改写(Query Rewrite):
原问题:"它安全吗?" 历史对话:"微信支付有哪些功能?" 改写后:"微信支付安全吗?"
这样做的好处是让检索更准确!
混合检索(Hybrid Retrieval):
- BM25(关键词):精确匹配,如"商户号"、"申请"
- 向量检索(语义):语义相似,如"注册"也能匹配"申请"
- 加权合并:
score = 0.3 * bm25_score + 0.7 * vector_score
重排序(Rerank):
为什么需要?因为初筛的 30 条结果质量参差不齐,用更强大的模型再精排一次,选出最佳的 5 条。流式响应(Streaming):
- 不是等 LLM 生成完整答案再返回
- 而是每生成一个 token 就立即推送给前端
- 用户体验更好,感觉更快
上下文窗口管理:
完整提示词 = 系统提示词 + 历史对话(最近 5 轮)+ 检索到的文档片段(Top 5)+ 用户当前问题
总长度不能超过模型的上下文限制(如 8k tokens)
📄 实现文件索引
为了方便你快速定位代码,这里给出一个功能 → 文件的映射表:
功能模块 | 核心文件 | 说明 |
---|---|---|
应用启动 | cmd/server/main.go |
程序入口 |
路由定义 | internal/router/router.go |
所有 API 路由 |
认证授权 | internal/middleware/auth.go |
JWT 验证 |
知识库管理 | internal/handler/knowledgebase.go |
创建、查询知识库 |
文档上传 | internal/handler/knowledge.go |
文件上传和处理 |
对话问答 | internal/handler/session.go |
问答核心逻辑 |
对话流水线 | internal/application/service/chat_pipline/chat_pipline.go |
编排各个步骤 |
文本分块 | internal/application/service/chunk.go |
分块策略 |
混合检索 | internal/application/service/retriever/keywords_vector_hybrid_indexer.go |
BM25 + 向量 |
PostgreSQL 检索 | internal/application/repository/retriever/postgres/repository.go |
pgvector 实现 |
Elasticsearch 检索 | internal/application/repository/retriever/elasticsearch/v8/repository.go |
ES 实现 |
LLM 调用(Ollama) | internal/models/chat/ollama.go |
本地模型 |
LLM 调用(API) | internal/models/chat/remote_api.go |
远程 API |
Embedding 模型 | internal/models/embedding/ |
各种实现 |
Rerank 模型 | internal/models/rerank/ |
重排序 |
文档解析服务 | services/docreader/src/grpc_server.py |
Python gRPC |
前端对话页面 | frontend/src/views/chat/ |
Vue 组件 |
3. 代码结构观察
现在让我们从代码质量的角度来审视这个项目。作为学习者,了解优秀代码的特征,也能帮你识别可以改进的地方。
✅ 代码组织模式
WeKnora 的代码组织体现了多个优秀的设计模式:
1. 依赖倒置原则(DIP)
看 internal/types/interfaces/
目录,你会发现大量的接口定义:
// internal/types/interfaces/knowledge_base.go
type KnowledgeBaseService interface {
Create(ctx context.Context, req *types.CreateKnowledgeBaseRequest) (*types.KnowledgeBase, error)
Get(ctx context.Context, id string) (*types.KnowledgeBase, error)
// ...更多方法
}
高层模块(Handler)依赖接口,而不是具体实现(Service)。这样的好处:
- ✅ 易于测试:可以轻松 mock
- ✅ 易于替换:换实现不影响调用方
- ✅ 解耦合:模块间独立开发
2. 工厂模式(Factory Pattern)
检索器的创建使用了工厂模式:
// internal/application/service/retriever/registry.go
func NewRetriever(dbType string, ...) Retriever {
switch dbType {
case "postgres":
return NewPostgresRetriever(...)
case "elasticsearch":
return NewElasticsearchRetriever(...)
default:
panic("unsupported database type")
}
}
通过配置文件就能切换不同的检索实现,无需修改代码!
3. 构建者模式(Builder Pattern)
LLM 请求的构建:
// 伪代码示例
request := NewChatRequest().
WithModel("qwen").
WithMessages(messages).
WithTemperature(0.7).
WithStream(true).
Build()
链式调用,代码优雅易读。
4. 策略模式(Strategy Pattern)
不同的检索策略可以灵活组合:
// 组合检索器
retriever := NewCompositeRetriever().
Add(NewBM25Retriever(), 0.3). // 30% 权重
Add(NewVectorRetriever(), 0.7). // 70% 权重
Build()
🎨 设计模式识别
让我帮你识别项目中的设计模式,这些都是面试高频考点!
设计模式 | 使用场景 | 代码位置 | 优势 |
---|---|---|---|
单例模式 | 数据库连接 | internal/container/ |
全局唯一实例 |
工厂模式 | 检索器创建 | internal/application/service/retriever/registry.go |
解耦创建逻辑 |
策略模式 | 检索策略 | internal/application/service/retriever/ |
算法可替换 |
装饰器模式 | 中间件链 | internal/middleware/ |
动态添加功能 |
观察者模式 | 流式响应 | SSE 推送 | 事件驱动 |
模板方法 | 对话流水线 | chat_pipline.go |
固定流程,灵活步骤 |
适配器模式 | 不同 LLM 接口 | internal/models/chat/ |
统一接口 |
责任链模式 | 中间件处理 | Gin 中间件 | 链式处理请求 |
📏 代码质量观察
作为学习者,让我们客观地评估代码质量:
优点 ✅:
- 目录结构清晰:按功能和层次组织,易于导航
- 接口设计良好:大量使用接口,符合 SOLID 原则
- 注释适度:关键逻辑有注释,不过度注释
- 错误处理规范:统一的错误处理和日志记录
- 配置灵活:YAML + 环境变量,适应不同环境
- 类型安全:充分利用 Go 的类型系统
可以改进的地方 💡(学习机会):
单元测试覆盖:
- 当前状态:部分核心模块有测试,但覆盖率不够高
- 学习机会:可以为关键服务补充测试用例
- 文件示例:
internal/application/service/chat_pipline/chat_pipline_test.go
文档注释:
- 当前状态:大部分函数有注释,但有些不够详细
- 学习机会:可以按 Go 的文档规范补充注释
- 参考:Go Doc Comments
配置管理:
- 当前状态:配置在 YAML 文件中,修改需重启
- 改进方向:可以支持配置热更新(如使用 Viper 的 WatchConfig)
- 学习机会:实现一个配置热加载功能
API 文档:
- 当前状态:有 docs/API.md,但不是自动生成
- 改进方向:可以集成 Swagger/OpenAPI
- 学习机会:添加 Swagger 注解并自动生成文档
性能监控:
- 当前状态:有链路追踪,但缺少详细的性能指标
- 改进方向:可以添加 Prometheus metrics
- 学习机会:集成 Prometheus + Grafana 监控
🔍 潜在改进点(学习机会)
作为学习者,你可以通过改进这些地方来深入理解项目:
初级练习 🌱:
补充单元测试:
- 选择一个 Service 层的函数
- 编写完整的测试用例(正常情况 + 异常情况)
- 学习:如何 mock 依赖、如何编写好测试
优化错误信息:
- 搜索代码中的
error
返回 - 确保每个错误都有清晰的上下文信息
- 学习:Go 的错误处理最佳实践
- 搜索代码中的
改进日志输出:
- 统一日志格式
- 添加结构化字段(如
user_id
,knowledge_base_id
) - 学习:结构化日志的重要性
中级练习 🌿:
实现一个新的检索器:
- 比如集成 Weaviate 向量数据库
- 实现
Retriever
接口 - 学习:如何扩展系统功能
添加缓存层:
- 对热门问题的答案进行缓存
- 使用 Redis 实现
- 学习:缓存策略和失效机制
实现配置热更新:
- 修改提示词模板无需重启服务
- 使用 Viper 的 WatchConfig
- 学习:配置管理和文件监听
高级练习 🌳:
实现分布式追踪的详细分析:
- 在每个关键步骤添加 Span
- 记录详细的性能指标
- 学习:OpenTelemetry 的深度使用
实现多租户隔离:
- 不同租户的数据物理隔离
- 实现租户级别的资源配额
- 学习:多租户架构设计
实现知识图谱可视化:
- 前端展示实体关系图
- 支持交互式探索
- 学习:图数据库和可视化技术
第二部分:技能需求清单(你的学习弹药库)📚
好了,了解了项目架构后,你可能在想:"我需要掌握哪些技能才能真正理解和贡献这个项目?"别着急,我帮你梳理了一份完整的技能清单。
1. 基础技能要求
这些是你必须掌握的基础技能,没有这些基础,后面的学习会很吃力。
🔤 编程语言和框架
Go 语言(核心,必须精通) ⭐⭐⭐⭐⭐
你需要掌握的 Go 特性:
基础语法:
- 变量、常量、类型系统
- 控制流(if、for、switch)
- 函数、方法、闭包
- 指针和值传递
数据结构:
- 数组、切片(slice)、map
- 结构体(struct)
- 接口(interface)—— ⭐ 超级重要!
并发编程 ⭐ 核心特性:
- Goroutine:轻量级线程
- Channel:goroutine 间通信
- Select:多路复用
- Mutex、RWMutex:互斥锁
- WaitGroup:等待多个 goroutine 完成
错误处理:
// Go 的错误处理模式 result, err := DoSomething() if err != nil { return fmt.Errorf("do something failed: %w", err) }
包管理:
- Go Modules(go.mod、go.sum)
- 导入和导出规则(大写字母开头才能被外部访问)
版本要求:Go 1.24+(项目使用了最新特性)
学习路径:
- 📘 官方教程:A Tour of Go
- 📘 进阶阅读:《Effective Go》(官方最佳实践)
- 📘 深入理解:《Go 语言高级编程》
Gin Web 框架 ⭐⭐⭐⭐
WeKnora 使用 Gin 作为 Web 框架,你需要掌握:
路由和处理器:
r := gin.Default() r.GET("/api/users/:id", handler.GetUser) r.POST("/api/users", handler.CreateUser)
中间件:
r.Use(middleware.Logger()) r.Use(middleware.Auth())
参数绑定:
var req CreateUserRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(400, gin.H{ "error": err.Error()}) return }
响应处理:JSON、文件下载、流式响应等
学习资源:Gin 官方文档
GORM(数据库 ORM) ⭐⭐⭐
WeKnora 使用 GORM 进行数据库操作:
模型定义:
type Knowledge struct { ID string `gorm:"primaryKey"` Title string `gorm:"type:varchar(255)"` CreatedAt time.Time }
CRUD 操作:
// 创建 db.Create(&knowledge) // 查询 db.Where("title = ?", "test").First(&knowledge) // 更新 db.Model(&knowledge).Update("status", "processed") // 删除 db.Delete(&knowledge)
关联查询:
db.Preload("Chunks").Find(&knowledges)
学习资源:GORM 官方文档
🌐 前端基础(了解即可)
如果你想修改前端界面,需要了解:
Vue 3 ⭐⭐⭐
组合式 API(Composition API):
<script setup lang="ts"> import { ref, onMounted } from 'vue' const message = ref('Hello') onMounted(() => { console.log('Component mounted') }) </script>
响应式数据:
ref
、reactive
、computed
- 路由:Vue Router
- 状态管理:Pinia(Vue 3 的官方状态管理)
TypeScript ⭐⭐
- 基本类型注解
- 接口和类型别名
- 泛型基础
学习资源:Vue 3 官方文档
🗄️ 数据库和 SQL
PostgreSQL ⭐⭐⭐⭐
基础 SQL:
- SELECT、INSERT、UPDATE、DELETE
- JOIN 查询
- 索引和性能优化
PostgreSQL 特性:
- JSON/JSONB 类型
- 全文搜索(to_tsvector、to_tsquery)
- pgvector 扩展 ⭐ 向量检索的核心!
pgvector 基本用法:
-- 创建向量列 ALTER TABLE chunks ADD COLUMN embedding vector(768); -- 创建向量索引 CREATE INDEX ON chunks USING ivfflat (embedding vector_cosine_ops); -- 向量相似度查询 SELECT * FROM chunks ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector LIMIT 10;
学习资源:
Redis(缓存与消息队列) ⭐⭐⭐
- 基本数据类型:String、Hash、List、Set、ZSet
- 常用命令:GET/SET、EXPIRE、LPUSH/RPOP 等
- 发布订阅:用于流式响应管理
- 持久化:RDB 和 AOF
学习资源:Redis 中文文档
🐳 容器化和部署
Docker ⭐⭐⭐⭐
基础概念:
- 镜像(Image)vs 容器(Container)
- Dockerfile 编写
- 容器网络和数据卷
常用命令:
docker build -t myapp . docker run -p 8080:8080 myapp docker ps docker logs -f container_id docker exec -it container_id bash
Docker Compose ⭐⭐⭐⭐
- docker-compose.yml 语法
- 服务编排:depends_on、networks、volumes
- 常用命令:
docker compose up -d docker compose down docker compose logs -f service_name docker compose restart service_name
学习资源:Docker 官方文档
🔧 基础工具和概念
Git 版本控制 ⭐⭐⭐⭐
- 基本操作:clone、add、commit、push、pull
- 分支管理:branch、checkout、merge
- 查看历史:log、diff、blame
- 协作流程:PR、Code Review
HTTP 协议 ⭐⭐⭐
- 请求方法:GET、POST、PUT、DELETE
- 状态码:2xx、4xx、5xx
- 请求头和响应头
- RESTful API 设计规范
JSON 数据格式 ⭐⭐⭐
- JSON 语法
- 序列化和反序列化
- Go 中的
encoding/json
包
2. 进阶技能要求
这些技能会帮助你深入理解项目的核心技术。
🤖 AI/ML 相关概念
RAG(Retrieval-Augmented Generation) ⭐⭐⭐⭐⭐
这是 WeKnora 的核心理念!
什么是 RAG?
传统 LLM:问题 → LLM → 答案(可能幻觉) RAG 模式:问题 → 检索相关文档 → 问题+文档 → LLM → 答案(基于事实)
RAG 的三个阶段:
- 索引阶段:文档分块 → 向量化 → 存储到向量数据库
- 检索阶段:问题向量化 → 相似度搜索 → 返回 Top-K 文档
- 生成阶段:问题+检索文档 → LLM → 生成答案
为什么需要 RAG?
- ❌ LLM 的知识有截止日期,不知道最新信息
- ❌ LLM 容易"胡编乱造"(幻觉问题)
- ❌ LLM 不知道你的私有数据
- ✅ RAG 让 LLM 基于你的文档回答,准确可控
学习资源:
- LangChain RAG 教程
- 论文:《Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks》
Embedding(文本向量化) ⭐⭐⭐⭐
什么是 Embedding?
- 把文本转换成高维向量(如 768 维)
- 语义相似的文本,向量也相似
"苹果手机" → [0.2, -0.5, 0.8, ..., 0.1] (768维) "iPhone" → [0.3, -0.4, 0.7, ..., 0.2] (相似!) "香蕉" → [0.1, 0.9, -0.3, ..., -0.5] (不相似)
常用 Embedding 模型:
- 中文:BGE、GTE、M3E
- 英文:OpenAI text-embedding-3、Sentence-BERT
- 多语言:M-BERT、XLM-RoBERTa
向量维度选择:
- 384维:速度快,精度一般
- 768维:平衡(WeKnora 默认)
- 1024维+:精度高,但更慢更耗资源
向量检索 ⭐⭐⭐⭐
相似度计算:
- 余弦相似度(Cosine Similarity):最常用
- 欧氏距离(Euclidean Distance)
- 点积(Dot Product)
向量索引算法:
- 暴力搜索(Brute Force):精确但慢
- HNSW:速度快,精度高(推荐)
- IVF(Inverted File Index):适合大规模数据
- PQ(Product Quantization):压缩向量,节省内存
实际使用:
# 问题向量化 query_vector = embedding_model.encode("如何申请商户号?") # 向量检索 results = vector_db.search( vector=query_vector, top_k=10, similarity_threshold=0.7 )
BM25 算法 ⭐⭐⭐
什么是 BM25?
- 一种基于关键词的检索算法
- TF-IDF 的改进版
- 考虑词频、逆文档频率、文档长度
BM25 vs 向量检索:
| 维度 | BM25 | 向量检索 |
| -------- | -------------- | -------- |
| 精确匹配 | ✅ 很好 | ❌ 较差 |
| 语义理解 | ❌ 不行 | ✅ 很好 |
| 速度 | 🚀 极快 | 🐢 较慢 |
| 适用场景 | 专业术语、代码 | 自然语言 |混合检索(WeKnora 的做法):
最终得分 = 0.3 × BM25得分 + 0.7 × 向量相似度
综合两者优势!
Rerank(重排序) ⭐⭐⭐
为什么需要 Rerank?
- 初筛(BM25+向量)召回 30 条候选
- 但质量参差不齐
- 用更精确的模型重新排序,选出最佳 5 条
Rerank 模型:
- BGE Reranker
- Cohere Rerank
- 自定义交叉编码器
性能对比:
只用向量检索:准确率 60% 向量检索 + Rerank:准确率 85%+
提升显著!
LLM API 调用 ⭐⭐⭐
主流 LLM 提供商:
- OpenAI(GPT-4、GPT-3.5)
- 阿里云(通义千问)
- 字节跳动(豆包)
- 本地部署(Ollama + Qwen/Llama)
API 基本参数:
{ "model": "qwen-plus", "messages": [...], "temperature": 0.7, // 创造性(0-2) "max_tokens": 2000, // 最大生成长度 "stream": true // 流式响应 }
流式响应(Streaming):
// 服务器推送事件(SSE) stream, _ := client.CreateChatCompletion(req) for { response, err := stream.Recv() if err == io.EOF { break } fmt.Print(response.Choices[0].Delta.Content) }
🏗️ 架构模式和设计原则
分层架构(Layered Architecture) ⭐⭐⭐⭐
表现层(Presentation Layer) ← HTTP 请求处理
↓
应用层(Application Layer) ← 业务逻辑编排
↓
领域层(Domain Layer) ← 核心业务模型
↓
基础设施层(Infrastructure) ← 数据库、外部服务
SOLID 原则 ⭐⭐⭐⭐
WeKnora 严格遵循 SOLID 原则:
S - 单一职责原则(SRP):
- 一个类只做一件事
- 示例:
ChunkService
只负责分块,不处理存储
O - 开闭原则(OCP):
- 对扩展开放,对修改关闭
- 示例:新增检索器不需要修改现有代码
L - 里氏替换原则(LSP):
- 子类可以替换父类
- 示例:
PostgresRetriever
可以替换Retriever
接口
I - 接口隔离原则(ISP):
- 不依赖不需要的接口
- 示例:只定义必要的方法
D - 依赖倒置原则(DIP) ⭐ 最重要!
- 依赖接口,不依赖实现
- WeKnora 大量使用接口
依赖注入(Dependency Injection) ⭐⭐⭐⭐
什么是依赖注入?
// ❌ 不好:直接创建依赖 type UserService struct { repo *UserRepository // 硬编码依赖 } func NewUserService() *UserService { return &UserService{ repo: &UserRepository{ }, // 紧耦合! } } // ✅ 好:注入依赖 type UserService struct { repo UserRepositoryInterface // 依赖接口 } func NewUserService(repo UserRepositoryInterface) *UserService { return &UserService{ repo: repo} // 解耦! }
WeKnora 使用 uber/dig:
container := dig.New() // 注册依赖 container.Provide(NewDatabase) container.Provide(NewRepository) container.Provide(NewService) // 自动注入 container.Invoke(func(svc *Service) { svc.DoSomething() })
领域驱动设计(DDD) ⭐⭐⭐
WeKnora 的部分设计借鉴了 DDD:
- 实体(Entity):如
Knowledge
、Chunk
- 值对象(Value Object):如
Embedding
- 聚合根(Aggregate Root):如
KnowledgeBase
- 仓储(Repository):数据访问抽象
- 服务(Service):业务逻辑编排
🔍 可观测性技术
链路追踪(Distributed Tracing) ⭐⭐⭐⭐
OpenTelemetry:
- 云原生标准
- 支持多种后端(Jaeger、Zipkin)
基本概念:
- Trace:一次完整的请求链路
- Span:链路中的一个操作
- Context Propagation:跨服务传递追踪信息
实际使用:
ctx, span := tracer.Start(ctx, "operation-name") defer span.End() // 添加属性 span.SetAttributes( attribute.String("knowledge_id", id), attribute.Int("chunk_count", count), )
查看追踪:
- 访问 http://localhost:16686 查看 Jaeger UI
- 可以看到每个请求的完整调用链
- 定位性能瓶颈超级方便!
结构化日志 ⭐⭐⭐
为什么需要结构化日志?
❌ 纯文本:User 123 created knowledge ✅ 结构化:{"level":"info","user_id":"123","action":"create","resource":"knowledge"}
结构化日志易于搜索和分析!
WeKnora 使用 logrus:
log.WithFields(log.Fields{ "user_id": userID, "knowledge_id": knowledgeID, }).Info("Knowledge created")
消息队列(Message Queue) ⭐⭐⭐
Asynq:
- 基于 Redis 的异步任务队列
- 支持任务重试、定时任务
使用场景:
- 文档解析(耗时操作)
- 向量化处理(批量任务)
- 定时清理(周期任务)
基本用法:
// 生产者 client := asynq.NewClient(redisOpt) task := asynq.NewTask("parse_document", payload) client.Enqueue(task) // 消费者 srv := asynq.NewServer(redisOpt, asynq.Config{ Concurrency: 10, }) srv.HandleFunc("parse_document", handleParseDocument) srv.Run()
3. 技能掌握程度建议
根据你的学习目标,我给出不同阶段的建议:
🌱 初学者(刚接触 Go 和 Web 开发)
学习侧重:
- Go 语言基础 - 必须精通
- Gin 框架 - 理解路由和中间件
- PostgreSQL - 基本 SQL 操作
- Docker - 会用 docker compose 启动项目
- Git - 基本的 clone、commit、push
能达到的目标:
- ✅ 能成功运行项目
- ✅ 能修改简单的业务逻辑
- ✅ 能添加新的 API 接口
- ✅ 能调整配置参数
不需要掌握(暂时):
- ❌ RAG 原理(知道概念即可)
- ❌ 向量检索算法细节
- ❌ OpenTelemetry 使用
- ❌ 复杂的设计模式
学习时间估计:2-3 个月
🌿 有经验的开发者(熟悉一门后端语言)
学习侧重:
- Go 特有特性 - 并发编程、接口设计
- 分层架构 - 理解各层职责
- RAG 原理 - 深入理解检索增强生成
- 向量检索 - 理解 Embedding 和相似度计算
- 依赖注入 - 理解 uber/dig 的用法
能达到的目标:
- ✅ 能理解核心业务流程
- ✅ 能实现新的检索策略
- ✅ 能集成新的 LLM 模型
- ✅ 能优化性能瓶颈
- ✅ 能修复大部分 Bug
应该掌握:
- ✅ SOLID 原则
- ✅ 设计模式(工厂、策略、装饰器)
- ✅ 单元测试编写
- ✅ 链路追踪基本使用
学习时间估计:4-6 周
🌳 进阶贡献者(想深度参与开发)
学习侧重:
- 系统设计 - 理解整体架构权衡
- 性能优化 - 识别和解决性能问题
- 可观测性 - OpenTelemetry 深度使用
- 分布式系统 - 理解分布式追踪、一致性等
- 领域知识 - RAG、NLP、向量检索的最新进展
能达到的目标:
- ✅ 能设计和实现大功能
- ✅ 能做架构级别的优化
- ✅ 能指导其他开发者
- ✅ 能解决复杂的系统问题
- ✅ 能贡献高质量的 PR
应该掌握:
- ✅ DDD(领域驱动设计)
- ✅ CQRS(命令查询职责分离)
- ✅ 事件驱动架构
- ✅ 性能调优技巧
- ✅ 生产环境最佳实践
学习时间估计:持续学习
第三部分:学习路径规划(你的专属教练计划)🎯
现在到了最激动人心的部分——让我们开始动手!我会带你一步步从零开始,逐渐深入。
1. 项目运行入口定位(快速上手)
🚀 一键启动指南(目标:30 分钟内运行起来)
前置条件检查 ✅
在开始之前,确保你的环境满足以下要求:
工具 | 最低版本 | 检查命令 | 安装链接 |
---|---|---|---|
Docker | 20.10+ | docker --version |
docker.com |
Docker Compose | 2.0+ | docker compose version |
随 Docker Desktop 安装 |
Git | 2.30+ | git --version |
git-scm.com |
磁盘空间 | 10GB+ | - | 清理空间 |
内存 | 8GB+ | - | 关闭其他应用 |
具体操作步骤 📝
# 步骤 1:克隆项目(约 1-2 分钟)
git clone https://github.com/Tencent/WeKnora.git
cd WeKnora
# 步骤 2:配置环境变量(约 2-3 分钟)
# Windows用户
copy .env.example .env
# Mac/Linux用户
cp .env.example .env
# 用记事本或VS Code打开 .env 文件,进行以下关键配置:
# DB_USER=weknora
# DB_PASSWORD=your_strong_password ← 改成你自己的密码
# DB_NAME=weknora
# REDIS_PASSWORD=your_redis_password ← 改成你自己的密码
# 步骤 3:一键启动所有服务(约 10-15 分钟,首次需要下载镜像)
./scripts/start_all.sh
# 或者 Windows 用户(如果上面不行)
docker compose up -d
# 步骤 4:查看服务状态
docker compose ps
预期输出(所有服务都应该是 Up
状态):
NAME STATUS PORTS
WeKnora-app Up (healthy) 0.0.0.0:8080->8080/tcp
WeKnora-frontend Up 0.0.0.0:80->80/tcp
WeKnora-postgres Up (healthy) 0.0.0.0:5432->5432/tcp
WeKnora-redis Up 0.0.0.0:6379->6379/tcp
WeKnora-minio Up 0.0.0.0:9000-9001->9000-9001/tcp
WeKnora-docreader Up (healthy) 0.0.0.0:50051->50051/tcp
jaeger Up 0.0.0.0:16686->16686/tcp
步骤 5:验证成功 ✨
打开浏览器,访问以下地址:
前端页面:http://localhost
- 应该看到 WeKnora 的登录/初始化页面
- 首次访问会自动跳转到初始化配置页面
后端 API:http://localhost:8080/health
- 应该返回:
{"status":"ok"}
- 应该返回:
Jaeger 追踪:http://localhost:16686
- 应该看到 Jaeger UI 界面
MinIO 控制台(可选):http://localhost:9001
- 用户名:
minioadmin
- 密码:
minioadmin
- 用户名:
如果一切正常,恭喜你!🎉 你已经成功运行起 WeKnora 了!
⚠️ 常见环境配置陷阱及解决方案
根据社区反馈和我的经验,这里是最容易踩的坑:
陷阱 1:端口被占用 🔴
现象:
Error: bind: address already in use
原因:默认端口(80、8080、5432等)已被其他程序占用
解决方案:
# 方案1:找出并关闭占用端口的程序
# Windows
netstat -ano | findstr :80
taskkill /PID <进程ID> /F
# Mac/Linux
lsof -i :80
kill -9 <进程ID>
# 方案2:修改 .env 文件中的端口
APP_PORT=8081
FRONTEND_PORT=8080
DB_PORT=5433
陷阱 2:Docker 内存不足 🔴
现象:
容器频繁重启,或卡在 "Starting" 状态
原因:Docker Desktop 默认内存限制太低(2GB)
解决方案:
- 打开 Docker Desktop
- Settings → Resources → Advanced
- 将 Memory 调整到 至少 8GB(推荐 12GB)
- 点击 "Apply & Restart"
陷阱 3:网络问题导致镜像下载失败 🔴
现象:
Error response from daemon: Get https://registry-1.docker.io/...
原因:国内访问 Docker Hub 可能很慢或失败
解决方案:
配置国内镜像加速器(推荐阿里云):
- 打开 Docker Desktop
- Settings → Docker Engine
- 添加镜像加速器:
{ "registry-mirrors": [ "https://docker.mirrors.ustc.edu.cn", "https://hub-mirror.c.163.com" ] }
- 点击 "Apply & Restart"
陷阱 4:向量维度不匹配 🟡
现象:
ERROR: vector dimension mismatch
原因:Embedding 模型的向量维度与数据库配置不一致
解决方案:
- 确认你使用的 Embedding 模型的维度(常见:384、768、1024)
- 如果不是 768 维,需要修改:
# 重新初始化数据库 make clean-db # 在初始化页面配置正确的向量维度
陷阱 5:Ollama 连接失败 🟡
现象:
failed to connect to ollama: connection refused
原因:Ollama 未运行或连接地址配置错误
解决方案:
# 方案1:安装并启动 Ollama
# 访问 https://ollama.com 下载安装
ollama serve
# 方案2:使用远程 API(如 OpenAI)
# 在初始化页面选择 "远程 API",填入 API 密钥
2. 循序渐进学习计划(四阶段法)
现在项目已经运行起来了,让我们开始有计划地深入学习!
阶段一:环境搭建和项目启动(1-2 天)⭐
目标:成功运行项目并能打个断点
学习任务:
✅ 任务 1.1:完整体验核心功能(30分钟)
跟着我一步步操作:
注册并登录:
访问 http://localhost → 填写管理员账号信息 → 登录进入系统
完成初始化配置:
→ 配置 LLM 模型(推荐先用 Ollama + qwen2.5:7b) → 配置 Embedding 模型 → (可选)配置 Rerank 模型
创建第一个知识库:
→ 点击"创建知识库" → 命名为"测试知识库" → 上传一个简单的 TXT 文件(比如一篇文章) → 等待处理完成(状态变为"已完成")
进行第一次问答:
→ 点击知识库的"对话"按钮 → 提一个文档中相关的问题 → 观察系统如何检索并回答
✅ 任务 1.2:理解 Docker 容器组成(1小时)
打开终端,执行以下命令并理解每个服务的作用:
# 查看所有运行中的容器
docker compose ps
# 查看各服务的日志
docker compose logs app # Go 后端日志
docker compose logs docreader # Python 文档处理服务日志
docker compose logs postgres # 数据库日志
# 进入容器内部探索
docker exec -it WeKnora-app sh # 进入 Go 后端容器
ls -la # 查看文件结构
exit # 退出容器
理解每个服务的角色:
app
:Go 后端,核心业务逻辑frontend
:Vue 前端,用户界面postgres
:数据库,存储知识、对话等数据redis
:缓存和任务队列minio
:对象存储,保存文档文件docreader
:Python 服务,解析各种格式的文档jaeger
:链路追踪,性能监控
✅ 任务 1.3:在 Go 代码中打断点调试(1-2小时)
这是学习的关键一步!
安装 VS Code + Go 扩展
停止 Docker 中的 app 服务:
docker compose stop app
本地运行 Go 后端:
# 安装依赖 go mod download # 配置环境变量(复制 docker-compose.yml 中的 app 部分) export DB_HOST=localhost export DB_PORT=5432 export DB_USER=weknora export DB_PASSWORD=your_password export DB_NAME=weknora # ... 其他环境变量 # 运行 cd cmd/server go run main.go
在关键位置打断点:
internal/handler/session.go
的KnowledgeQA
方法- 在前端发起问答请求
- 观察代码执行流程
验收标准:
- ✅ 能成功运行项目
- ✅ 能完成一次完整的文档上传和问答流程
- ✅ 理解各个容器的作用
- ✅ 能在本地调试 Go 代码
阶段二:核心流程理解(3-5 天)⭐⭐
目标:追踪一个完整业务流程,画出自己的流程图
学习任务:
✅ 任务 2.1:追踪"文档上传"完整流程(4-6小时)
步骤 1:从前端开始
打开 frontend/src/views/knowledge/index.vue
,找到文档上传的代码:
// 找到上传文件的方法
const handleUpload = async (file) => {
// 这里会调用 API
const response = await uploadKnowledgeFile(knowledgeBaseId, file)
// ...
}
步骤 2:跟踪 API 调用
打开 frontend/src/api/knowledge-base/index.ts
,看 API 定义:
export const uploadKnowledgeFile = (kbId: string, file: File) => {
return request.post(`/api/v1/knowledge-bases/${
kbId}/knowledge/file`, formData)
}
步骤 3:后端路由
打开 internal/router/router.go
,找到路由定义:
kb.POST("/file", handler.CreateKnowledgeFromFile)
步骤 4:Handler 处理
打开 internal/handler/knowledge.go
,阅读 CreateKnowledgeFromFile
方法
步骤 5:Service 层业务逻辑
继续追踪到 Service 层,理解:
- 文件如何保存到 MinIO
- 如何调用 DocReader 解析
- 如何分块
- 如何向量化
- 如何存储到数据库
练习:画出流程图
用你喜欢的工具(draw.io、Excalidraw 等)画出完整流程图,包括:
- 前端 → 后端 → gRPC → 数据库 → 向量数据库
- 标注每个步骤的关键函数名
✅ 任务 2.2:追踪"知识问答"完整流程(4-6小时)
重复类似的过程,但这次追踪问答流程:
- 找到前端发送问题的代码
- 找到后端的
KnowledgeQA
handler - 深入
chat_pipline.go
,理解流水线编排 - 理解检索器如何工作(BM25 + 向量)
- 理解如何调用 LLM
- 理解流式响应如何实现
练习:修改提示词模板
- 打开
config/config.yaml
- 找到
conversation.summary.context_template
- 修改提示词,加入你的个性化内容
- 重启服务,观察效果变化
✅ 任务 2.3:使用 Jaeger 查看链路追踪(2小时)
- 访问 http://localhost:16686
- 在 Service 下拉框选择 "WeKnora"
- 点击 "Find Traces"
- 选择一条最近的 trace
- 观察完整的调用链路和耗时
分析性能瓶颈:
- 哪个步骤最慢?
- 有没有并发执行的地方?
- 数据库查询用了多长时间?
验收标准:
- ✅ 能画出文档上传和问答的完整流程图
- ✅ 能找到任意功能对应的代码位置
- ✅ 能通过 Jaeger 分析性能
- ✅ 能独立修改提示词模板
阶段三:模块深入和定制开发(1-2 周)⭐⭐⭐
目标:能修改或扩展一个现有功能
学习任务:
✅ 任务 3.1:实现一个新的检索器(难度:中等)
目标:集成 Weaviate 向量数据库
步骤:
学习接口定义:
阅读internal/types/interfaces/retriever.go
参考现有实现:
阅读internal/application/repository/retriever/postgres/repository.go
实现新的 Retriever:
// internal/application/repository/retriever/weaviate/repository.go type WeaviateRetriever struct { client *weaviate.Client } func (r *WeaviateRetriever) Search(ctx context.Context, req *types.SearchRequest) ([]*types.SearchResult, error) { // 实现 Weaviate 检索逻辑 // ... }
注册到工厂:
修改internal/application/service/retriever/registry.go
测试:
编写单元测试并验证功能
✅ 任务 3.2:添加缓存层(难度:中等)
目标:对热门问题缓存答案,减少 LLM 调用
实现思路:
- 在
ChatPipeline
中,调用 LLM 前先查缓存 - 使用 Redis,key 为问题的 hash,value 为答案
- 设置合理的过期时间(如 1 小时)
代码位置:internal/application/service/chat_pipline/chat_pipline.go
✅ 任务 3.3:实现新的文档格式支持(难度:较难)
目标:支持 Markdown 文件的解析
实现思路:
- 在
services/docreader/src/parsers/
下新建markdown_parser.py
- 实现解析逻辑(提取标题、内容、代码块等)
- 注册解析器到
grpc_server.py
- 测试上传 Markdown 文件
验收标准:
- ✅ 成功实现至少一个新功能
- ✅ 代码通过 Code Review 标准
- ✅ 编写了相应的测试用例
- ✅ 更新了相关文档
阶段四:架构理解和贡献指南(2 周+)⭐⭐⭐⭐
目标:能理解技术选型原因,并尝试修复一个简单 issue
学习任务:
✅ 任务 4.1:深入理解依赖注入容器(3-5天)
- 阅读
internal/container/container.go
和internal/runtime/container.go
- 理解 uber/dig 的工作原理
- 尝试添加一个新的 Service 并注入
✅ 任务 4.2:理解 OpenTelemetry 实现(3-5天)
- 阅读
internal/tracing/init.go
- 学习如何在新的地方添加 Span
- 学习如何记录自定义属性
✅ 任务 4.3:贡献代码到开源项目(持续)
找到合适的 Issue:
- 访问 https://github.com/Tencent/WeKnora/issues
- 筛选
good first issue
标签 - 选择一个你感兴趣的
Fork 项目并创建分支:
git clone https://github.com/你的用户名/WeKnora.git cd WeKnora git checkout -b feature/your-feature-name
实现功能并提交:
git add . git commit -m "feat: add your feature" git push origin feature/your-feature-name
创建 Pull Request:
- 在 GitHub 上创建 PR
- 填写清晰的描述
- 等待 Code Review
验收标准:
- ✅ 理解整体架构设计的权衡
- ✅ 能解释为什么选择 Go 而不是 Python
- ✅ 能解释依赖注入的好处
- ✅ 成功提交至少一个 PR
3. 学习路径流程图
graph TD
A[开始学习 WeKnora] --> B{你的基础如何?}
B -->|完全新手| C[阶段一:环境搭建<br/>1-2天]
B -->|有 Web 开发经验| D[阶段二:核心流程<br/>3-5天]
B -->|后端开发老手| E[阶段三:模块深入<br/>1-2周]
C --> F[✅ 成功运行项目<br/>✅ 完成第一次问答]
F --> G{遇到问题?}
G -->|是| H[查看常见陷阱部分<br/>或提Issue求助]
G -->|否| D
D --> I[✅ 画出流程图<br/>✅ 修改提示词]
I --> J{想深入吗?}
J -->|是| E
J -->|先学基础| K[补充 Go/RAG 知识]
K --> E
E --> L[✅ 实现新功能<br/>✅ 通过测试]
L --> M{想贡献代码?}
M -->|是| N[阶段四:开源贡献<br/>2周+]
M -->|暂时不| O[继续深入其他模块]
N --> P[✅ 提交 PR<br/>✅ Code Review]
P --> Q[🎉 成为 Contributor]
O --> E
style A fill:#e1f5ff
style Q fill:#c8e6c9
style F fill:#fff9c4
style I fill:#fff9c4
style L fill:#fff9c4
style P fill:#fff9c4
第四部分:实践建议和进阶指导(从会用到精通)💡
1. 调试技巧和常见陷阱
🐛 调试技巧
技巧 1:善用日志
WeKnora 的日志非常详细,学会看日志能解决 80% 的问题:
# 实时查看所有服务日志
docker compose logs -f
# 只看特定服务
docker compose logs -f app
# 查看最近 100 行
docker compose logs --tail=100 app
# 搜索关键词
docker compose logs app | grep "error"
技巧 2:使用 Jaeger 定位性能问题
- 访问 http://localhost:16686
- 找到慢的请求(Duration 排序)
- 点击查看详细的 Span
- 找到耗时最长的操作
技巧 3:数据库调试
# 进入 PostgreSQL 容器
docker exec -it WeKnora-postgres psql -U weknora -d weknora
# 查看所有表
\dt
# 查看知识库
SELECT * FROM knowledge_bases LIMIT 10;
# 查看文档块
SELECT id, knowledge_id, content, embedding <=> '[0.1, 0.2, ...]'::vector AS distance
FROM chunks
ORDER BY distance
LIMIT 5;
# 退出
\q
技巧 4:gRPC 调试
使用 grpcurl 调试 DocReader 服务:
# 安装 grpcurl
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
# 列出所有服务
grpcurl -plaintext localhost:50051 list
# 调用解析服务
grpcurl -plaintext -d '{"file_path": "/path/to/file.pdf"}' \
localhost:50051 docreader.DocReaderService/ParseDocument
⚠️ 常见陷阱(Top 10)
根据社区反馈,这是最容易踩的10个坑:
- 忘记启动 Ollama
- 向量维度不匹配
- Redis 内存溢出(默认配置太小)
- 文件权限问题(volumes 挂载)
- 网络问题(容器间通信)
- 配置文件未更新(修改后忘记重启)
- 端口冲突
- Docker 内存不足
- 数据库连接数耗尽
- 提示词模板格式错误
详细解决方案请参见"阶段一"的"环境配置陷阱"部分。
2. 扩展练习建议
🌱 入门级练习
修改欢迎消息
- 位置:
config/config.yaml
- 修改默认的回答模板
- 难度:⭐
- 位置:
调整检索参数
- 修改
embedding_top_k
、rerank_top_k
- 观察对结果的影响
- 难度:⭐
- 修改
添加新的 API 接口
- 实现一个"统计知识库数量"的接口
- 涉及:Router → Handler → Service → Repository
- 难度:⭐⭐
🌿 中级练习
实现答案评价功能
- 前端添加"👍/👎"按钮
- 后端记录评价到数据库
- 用于后续优化
- 难度:⭐⭐⭐
集成新的 Embedding 模型
- 比如使用 text2vec-large-chinese
- 实现
EmbeddingModel
接口 - 难度:⭐⭐⭐
实现多轮对话摘要
- 当对话超过 10 轮时,自动摘要前面的内容
- 减少上下文长度
- 难度:⭐⭐⭐
🌳 高级练习
实现分布式部署
- 将各服务拆分到不同机器
- 配置服务发现
- 难度:⭐⭐⭐⭐
性能优化挑战
- 目标:将问答响应时间从 3 秒降到 1 秒
- 思路:缓存、并发、索引优化
- 难度:⭐⭐⭐⭐⭐
实现知识图谱可视化
- 前端使用 D3.js 或 ECharts
- 展示实体和关系
- 支持交互式探索
- 难度:⭐⭐⭐⭐⭐
3. 参与贡献的途径
🤝 如何成为 Contributor
步骤 1:了解项目规范
阅读以下文档:
README_CN.md
- 项目介绍CONTRIBUTING.md
- 贡献指南(如果有)- Code of Conduct - 行为准则
步骤 2:找到合适的 Issue
在 GitHub Issues 页面筛选:
- 标签:
good first issue
、help wanted
- 难度:选择适合自己水平的
- 兴趣:选择你感兴趣的方向
步骤 3:认领 Issue
在 Issue 下评论:
Hi, I'd like to work on this issue. Could you assign it to me?
等待维护者回复确认。
步骤 4:开发和测试
# Fork 并克隆
git clone https://github.com/你的用户名/WeKnora.git
cd WeKnora
# 创建功能分支
git checkout -b feature/issue-123-add-new-feature
# 开发...
# 编写测试...
# 提交
git add .
git commit -m "feat: add new feature for #123"
git push origin feature/issue-123-add-new-feature
步骤 5:创建 Pull Request
在 GitHub 上创建 PR,标题格式:
feat: add new feature (#123)
描述模板:
## 变更说明
简要描述你做了什么改动
## 相关 Issue
Closes #123
## 测试
- [x] 添加了单元测试
- [x] 本地测试通过
- [x] 更新了文档
## 截图(如适用)
贴上前后对比图
步骤 6:Code Review
- 耐心等待 Review
- 根据反馈修改代码
- 保持礼貌和专业
步骤 7:合并和庆祝 🎉
当 PR 被合并后,你就成为了 WeKnora 的 Contributor!
第五部分:技术栈学习指引(你的知识地图)🌐
1. 官方文档定位(学习的基石)
核心技术栈文档
技术 | 官方文档 | 重点章节 | 学习时长 |
---|---|---|---|
Go 语言 | go.dev/doc | Tour of Go、Effective Go | 1-2 周 |
Gin 框架 | gin-gonic.com/docs | Quickstart、Middleware | 2-3 天 |
GORM | gorm.io/zh_CN | CRUD、关联查询 | 3-5 天 |
PostgreSQL | postgresql.org/docs | SQL 语法、索引、性能调优 | 1 周 |
pgvector | GitHub | 安装、使用、性能 | 1-2 天 |
Redis | redis.io/docs | 数据类型、持久化 | 3-5 天 |
Docker | docs.docker.com | 容器基础、Compose | 3-5 天 |
Vue 3 | vuejs.org | Composition API、Router | 1 周 |
OpenTelemetry | opentelemetry.io/docs | 链路追踪基础 | 2-3 天 |
项目自身文档
文档 | 位置 | 内容 | 优先级 |
---|---|---|---|
README | README_CN.md |
项目介绍、快速开始 | ⭐⭐⭐⭐⭐ |
API 文档 | docs/API.md |
接口说明 | ⭐⭐⭐⭐ |
问题排查 | docs/QA.md |
常见问题 | ⭐⭐⭐⭐ |
详细设计 | docs/WeKnora.md |
架构设计文档 | ⭐⭐⭐ |
数据库结构 | migrations/paradedb/ |
数据库 Schema | ⭐⭐⭐ |
权威技术书籍
Go 语言:
- 《Go 程序设计语言》- 官方推荐
- 《Go 语言高级编程》- 深入并发和接口
- 《Effective Go》- 官方最佳实践(免费)
系统设计:
- 《设计数据密集型应用》- 分布式系统必读
- 《微服务架构设计模式》
- 《领域驱动设计》
AI/ML:
- 《动手学深度学习》- 理解 Embedding
- 《自然语言处理综论》
- 《大规模语言模型》
2. 学习路径建议(社区智慧)
技能学习顺序
graph LR
A[Go 基础语法] --> B[Gin Web 框架]
B --> C[GORM 数据库操作]
C --> D[Docker 容器化]
D --> E[RAG 原理]
E --> F[向量检索]
F --> G[LLM 集成]
G --> H[分布式追踪]
style A fill:#ffcdd2
style E fill:#c8e6c9
style H fill:#bbdefb
核心概念优先级
必须掌握 ⭐⭐⭐⭐⭐:
- Go 并发编程(goroutine、channel)
- RESTful API 设计
- SQL 和数据库索引
- Docker 基本使用
- RAG 基本原理
重要 ⭐⭐⭐⭐:
- SOLID 原则
- 依赖注入
- 向量检索算法
- LLM API 调用
- 链路追踪
进阶 ⭐⭐⭐:
- 设计模式
- 性能调优
- 分布式系统
- OpenTelemetry 深度使用
实践项目推荐
在学习 WeKnora 的同时,可以参考这些项目:
- Dify(Python):另一个知名的 LLM 应用框架
- FastGPT(TypeScript):类似的知识库问答系统
- Langchain-Go:Go 版本的 LangChain
- Milvus:专业的向量数据库
3. 工具与环境配置指南
开发环境搭建
推荐配置:
工具 | 推荐版本 | 用途 |
---|---|---|
IDE | VS Code 或 GoLand | 代码编辑 |
Go | 1.24+ | 后端开发 |
Node.js | 18+ | 前端开发 |
Docker Desktop | 最新版 | 容器管理 |
Git | 2.30+ | 版本控制 |
Postman | 最新版 | API 测试 |
VS Code 必装扩展:
- Go(官方)
- Docker
- GitLens
- REST Client
- Database Client
- Mermaid Preview
GoLand 配置:
- 启用 Go Modules
- 配置远程调试
- 安装 Database Tools
常用工具使用
Postman 测试 API:
- 导入 API 集合(如果有)
配置环境变量:
{ "base_url": "http://localhost:8080", "token": "your_jwt_token" }
测试登录:
POST { {base_url}}/api/v1/auth/login Body: { "username": "admin", "password": "password" }
Git 工作流:
# 拉取最新代码
git pull origin main
# 创建功能分支
git checkout -b feature/my-feature
# 提交代码(遵循 Conventional Commits)
git commit -m "feat: add new feature"
git commit -m "fix: resolve bug #123"
git commit -m "docs: update README"
# 推送到远程
git push origin feature/my-feature
4. 进阶拓展方向
技术博客与专家观点
推荐关注的技术博客:
Go 语言相关:
- Go 官方博客:blog.golang.org
- Dave Cheney 的博客:dave.cheney.net
- Go 语言中文网:studygolang.com
RAG 和 LLM:
- LangChain Blog
- OpenAI Cookbook
- Hugging Face Blog
系统设计:
- Martin Fowler 的博客
- High Scalability Blog
相关技术大会
- GopherCon:Go 语言年度盛会
- KubeCon:云原生技术
- AI 大会:关注 LLM 和 RAG 技术进展
社区与论坛
平台 | 链接 | 适用场景 |
---|---|---|
GitHub Discussions | WeKnora 项目页 | 项目相关讨论 |
Go 语言中文网 | studygolang.com | Go 语言学习 |
V2EX | v2ex.com | 技术讨论 |
知乎 | 搜索相关话题 | 经验分享 |
Stack Overflow | stackoverflow.com | 技术问答 |
结语 🎓
恭喜你看到这里!这份指南凝聚了我对 WeKnora 项目的深入分析和学习路径设计。
记住几个关键点:
- 不要试图一次性掌握所有内容。按照四阶段学习路径,循序渐进。
- 实践是最好的老师。多动手,多调试,多尝试。
- 遇到问题不要怕。查文档、看日志、问社区,总能解决。
- 保持好奇心。思考"为什么这样设计",而不是只看"怎么用"。
- 分享你的收获。写博客、提 PR、帮助他人,这也是学习的一部分。
下一步行动建议:
✅ 立即行动:按照"阶段一"的指南,今天就把项目跑起来
✅ 设定目标:选择一个你感兴趣的方向深入学习
✅ 加入社区:在 GitHub Discussions 上介绍自己
✅ 分享进展:记录学习笔记,帮助后来者
祝你学习愉快,早日成为 WeKnora 的 Contributor!💪