Appearance
读别人写的仓库代码,一直是件让人上头的事。文件几十上百个,函数调用关系七拐八绕,README 又只讲了个大概。GPT-Code-Learner 就是针对这个痛点做的工具——把一个完整的 Git 仓库"喂"给 GPT,然后你可以用自然语言提问,它会带着代码上下文来回答。
项目地址:JinghaoZhao/GPT-Code-Learner
核心思路
整体是一个 RAG (Retrieval-Augmented Generation) 架构,分三层:
- Knowledge Base — 把源码文件切片 → 向量化 → 存入向量数据库
- Tool Planner — GPT 根据用户问题选择合适的检索工具
- Code Learner — 将检索到的代码上下文 + 用户问题一起发给 GPT 生成回答
用户提问
↓
Tool Planner (GPT选择工具)
├── Code_Searcher → 关键字精确搜索,定位具体函数/变量
├── Repo_Parser → 向量相似度搜索,回答宏观架构问题
└── No_Tool → 通用编程问题,直接回答
↓
拼接代码上下文 + 用户问题
↓
GPT 生成最终回答Knowledge Base:构建向量知识库
知识库是整个系统的基座。knowledge_base.py 做了这几件事:
文档切片
用 LangChain 的 RecursiveCharacterTextSplitter 对源码文件做切片,每个 chunk 1500 字符,overlap 200 字符。overlap 的作用是保留上下文衔接,避免函数被硬切到两个 chunk 里导致语义断裂。
python
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1500,
chunk_overlap=200,
length_function=len,
)对于代码文件来说,1500 字符差不多是一个中等函数的长度,不会太短丢失上下文,也不会太长超过 GPT 的处理效率。
向量化
支持两种 Embedding 方式:
| 方式 | 实现 | 适用场景 |
|---|---|---|
| OpenAI Embedding | OpenAIEmbeddings() | 精度高,需要 API Key |
| 本地 Embedding | SentenceTransformer("all-mpnet-base-v2") | 离线可用,隐私安全 |
本地方案封装了一个 LocalHuggingFaceEmbeddings 类,继承 LangChain 的 Embeddings 基类,适配 embed_documents 和 embed_query 接口:
python
class LocalHuggingFaceEmbeddings(Embeddings):
def __init__(self, model_id="all-mpnet-base-v2"):
self.model = SentenceTransformer(model_id)
def embed_documents(self, texts):
return self.model.encode(texts)
def embed_query(self, text):
return list(map(float, self.model.encode(text)))向量存储
支持两种向量数据库:
- FAISS — Facebook 开源的向量检索库,纯本地,不需要额外服务
- Supabase — 云端 PostgreSQL + pgvector 扩展,适合需要持久化的场景
FAISS 的持久化直接用 pickle 序列化本地文件。简单粗暴,但对于单机学习场景够用。
Tool Planner:让 GPT 自己选工具
这是项目里比较巧妙的设计。用户输入一个提问后,不是直接搜索,而是先让 GPT 判断该用哪个工具。
tool_planner.py 的核心是一段 few-shot prompt:
python
def tool_selection(input):
user_prompt = """
你需要根据用户的问题选择一个工具。
- Code_Searcher: 针对具体函数或变量的搜索
- Repo_Parser: 针对宏观流程和架构的模糊搜索
- No_Tool: 与代码仓库无关的通用问题
示例:
- "如何使用 extract_function_name 函数?" → Code_Searcher
- "知识库是怎么构建的?" → Repo_Parser
- "Python 的 asyncio 怎么用?" → No_Tool
""" + f'用户问题: {input}'
return get_chat_response(system_prompt, user_prompt)选定工具后的处理流程:
- Code_Searcher:再用 GPT 从用户问题中提取函数名/变量名 → 在源码中做关键字检索 → 拿到该函数的上下文代码
- Repo_Parser:在向量数据库中做相似度搜索 → 返回最相关的代码片段
- No_Tool:直接把用户问题交给 GPT 回答
最后把检索到的代码上下文拼接到原始问题后面,发给 GPT 生成回答。这样 GPT 既有了"记忆"(代码片段),又有了"任务"(用户问题),生成的答案自然就靠谱很多。
工程上的几个细节
热重载
run.py 用了 hupper 库实现热重载,改代码后不用手动重启:
python
import hupper
reloader = hupper.start_reloader('code_learner.main')前端交互
用 Gradio 搭建了一个简单的 Web UI(端口 7860),输入仓库地址就能开始提问。对于这种工具型项目,Gradio 是性价比很高的选择——几行代码就有一个可用的界面。
本地 LLM 支持
通过 LocalAI 兼容 OpenAI API 格式,在本地跑大模型。改一下 .env 配置即可切换:
bash
LLM_TYPE="local"
EMBEDDING_TYPE="local"RAG 的局限性和改进方向
这种 RAG 方案虽然实用,但也有明显的短板:
- 切片粒度的矛盾 — chunk 太大检索精度下降,太小丢失上下文。代码文件不同于自然语言文档,函数之间有调用关系,简单的固定长度切片会打断这种关系
- 跨文件理解能力弱 — 如果一个流程横跨 5 个文件,单次检索很难覆盖完整链路
- 代码语义 vs 自然语言语义 — 通用的 Embedding 模型对代码的理解不如专门训练的代码模型(如 CodeBERT、StarCoder Embedding)
可能的改进方向:
- 用 AST(抽象语法树)做智能切片,按函数/类为单位
- 构建调用关系图,检索时沿着调用链扩展上下文
- 换用代码专用的 Embedding 模型
小结
GPT-Code-Learner 麻雀虽小五脏俱全,把 RAG 流程从知识库构建、工具选择到上下文增强的完整链路都跑通了。核心代码加起来也就几百行,非常适合想快速理解 RAG 实战落地的同学拿来学习和改造。