Skip to content

读别人写的仓库代码,一直是件让人上头的事。文件几十上百个,函数调用关系七拐八绕,README 又只讲了个大概。GPT-Code-Learner 就是针对这个痛点做的工具——把一个完整的 Git 仓库"喂"给 GPT,然后你可以用自然语言提问,它会带着代码上下文来回答。

项目地址:JinghaoZhao/GPT-Code-Learner

核心思路

整体是一个 RAG (Retrieval-Augmented Generation) 架构,分三层:

  1. Knowledge Base — 把源码文件切片 → 向量化 → 存入向量数据库
  2. Tool Planner — GPT 根据用户问题选择合适的检索工具
  3. 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 EmbeddingOpenAIEmbeddings()精度高,需要 API Key
本地 EmbeddingSentenceTransformer("all-mpnet-base-v2")离线可用,隐私安全

本地方案封装了一个 LocalHuggingFaceEmbeddings 类,继承 LangChain 的 Embeddings 基类,适配 embed_documentsembed_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 方案虽然实用,但也有明显的短板:

  1. 切片粒度的矛盾 — chunk 太大检索精度下降,太小丢失上下文。代码文件不同于自然语言文档,函数之间有调用关系,简单的固定长度切片会打断这种关系
  2. 跨文件理解能力弱 — 如果一个流程横跨 5 个文件,单次检索很难覆盖完整链路
  3. 代码语义 vs 自然语言语义 — 通用的 Embedding 模型对代码的理解不如专门训练的代码模型(如 CodeBERT、StarCoder Embedding)

可能的改进方向:

  • 用 AST(抽象语法树)做智能切片,按函数/类为单位
  • 构建调用关系图,检索时沿着调用链扩展上下文
  • 换用代码专用的 Embedding 模型

小结

GPT-Code-Learner 麻雀虽小五脏俱全,把 RAG 流程从知识库构建、工具选择到上下文增强的完整链路都跑通了。核心代码加起来也就几百行,非常适合想快速理解 RAG 实战落地的同学拿来学习和改造。