04

Embedding 构建

向量化引擎、双编码器设计与内存向量存储

为什么代码需要 Embedding

代码图谱解决了"结构查询":给定一个函数名,找到它的调用链。但还有另一类需求—— 语义搜索:用自然语言描述意图,找到对应的代码,哪怕你不记得函数名。 Embedding 是这一能力的核心:把代码和自然语言都转换为 高维浮点数向量, 然后通过向量距离衡量语义相似度。

🧠
语义搜索
自然语言描述 → 找到相关函数,跨越字面差异,支持中英文混合查询
📐
2560 维向量
Qwen3-Embedding-4B 输出维度。维度越高,语义表达能力越强,代价是存储和计算量增加
API 驱动
无需本地 GPU,通过 DashScope API 调用。批处理最大 25 个文本/请求,指数退避重试
🔄
双编码器
查询和文档使用不同任务指令,这是 bi-encoder 检索模型的关键设计,提升跨语言匹配精度
💡 核心直觉

把 2560 维向量想象成每个函数在语义空间中的"坐标"。embed_query(query) 把用户问题也投影到同一个坐标系,然后用余弦相似度找最近邻。这就是为什么"解析命令行参数"能找到 parse_args()——两者在这个坐标系里很近。

Qwen3Embedder:双编码器实现

双编码器设计 是 Qwen3Embedder 的核心。embed_documents()embed_query() 使用不同的编码路径—— 前者直接嵌入代码文本,后者附加任务指令,引导模型从"检索意图"角度理解查询。

向量化建索引流程

cgb index 执行时,L3 函数文档被批量向量化并存入 MemoryVectorStore, 最终序列化为 vectors.pkl。下次启动直接加载,无需重新向量化。

📄
L3 Docs
✂️
文本组装
🌐
DashScope
🗄️
VectorStore
💾
vectors.pkl
点击"下一步"开始 Step 0 / 5
1
文本组装策略
每个 L3 函数文档提取:函数签名(精确语义锚点)+ 一句话描述(docstring 或 LLM 生成)+ 调用树摘要(结构上下文)+ 源码前 30 行(实现语义)。这4个维度共同构成函数的语义指纹。
2
VectorRecord 创建
每个函数创建一个 VectorRecord(node_id, qualified_name, embedding, metadata),metadata 存储 name/type/file_path 等字段,用于搜索后回填详情,无需再查图数据库。
3
Pickle 持久化
向量化完成后序列化为 vectors.pkl(Pickle 格式)。下次 cgb mcp 启动时直接 load() 恢复内存状态,跳过全量向量化,启动时间从分钟级降为秒级。

MemoryVectorStore:余弦相似度搜索

MemoryVectorStore 用 Python dict 存储向量,用暴力余弦相似度 搜索。对代码库(通常几千个函数)这完全够用——暴力搜索比维护 HNSW 索引简单得多,也不需要近似误差的trade-off。

组件对话 — 查询 "解析命令行参数" 的执行路径
0 / 4 messages
💡 关键词提权(Keyword Boost)

纯向量搜索有时对词语变形敏感——"拉短"和"缩短"可能检索分数差距较大。CGB 在向量搜索结果上叠加了关键词匹配:query 分词后,每有一个 token 出现在函数名/签名/模块名中,相似度分数 +0.05。这个小奖励不会颠覆向量搜索的排序,但能把本该排第2的正确答案推到第1。

VectorRecord 数据结构与配置

每个向量记录的结构设计决定了搜索后能回填哪些信息。metadata 字段的选择是工程权衡: 存太少则每次搜索后还要再查图数据库;存太多则 pkl 文件膨胀、加载变慢。

字段 类型 说明
node_id int 图数据库节点 ID,用于搜索后回查图获取完整信息
qualified_name str 函数 FQN(如 entrypoints.cli.cli.parse_args),关键词提权和去重依赖此字段
embedding list[float] 2560 维向量,约 20KB/函数(float64)。1000 个函数约 20MB 内存
metadata dict 存储 name、type、path、start_line、end_line、signature,搜索结果可直接展示无需回查
💡 为什么不用 Qdrant / Faiss

CGB 同时实现了 MemoryVectorStoreQdrantVectorStore 两个后端。对代码库(几百到几千个函数),暴力搜索延迟 <10ms,完全没有引入 Qdrant 的必要——那会多一个外部依赖、一个网络跳跃、以及近似搜索的精度损失。规模达到十万函数以上再考虑切换。