使用 Weaviate MCP 构建编码助手:对代码与文档进行 RAG 检索
本文介绍如何利用 Weaviate 内置的 MCP 服务器为编码助手(如 Claude Code、Cursor 和 VS Code)提供混合搜索能力,无需额外编写胶水代码。通过将代码库与文档分块并索引到 Weaviate,结合 BM25 精确匹配与向量语义检索,LLM 智能代理可以高效获取所需上下文,避免上下文过载与成本浪费。
上周,我让 Claude Code 在我的代码库中实现一个相对简单的功能。三轮对话后,上下文消耗超过 80K token,但 Claude 仍然缺少一些我忘记包含的关键信息。没有检索机制,你就会陷入这样的循环:粘贴太少,代理靠猜测;粘贴太多,又要为代理未使用的上下文付费。
大多数团队通过 RAG 对代码库进行检索来解决这个问题。典型的方案是采用向量数据库加上一个自定义的 MCP 服务器作为桥梁。Weaviate 简化了这一过程:MCP 服务器内建于数据库之中,位于与 REST API 同一端口的 /v1/mcp。仅需一个环境变量即可启用。你可能已经在 Weaviate 中使用的混合检索同样适用于代码检索,其中 BM25 部分可以匹配像 connect_to_local 这样的函数标识符,而向量部分则能发现语义意图,例如“如何初始化客户端”。
本文将指导你基于这一内建 MCP 服务器构建编码助手:包括如何摄取代码库、摄取文档、连接 Claude Code、Cursor 和 VS Code,并运行真实的查询。覆盖以下主题:
- 为什么你的编码助手需要超越训练数据
- 为什么 Weaviate MCP 适合这项工作
- 第一步:运行带 MCP 的 Weaviate
- 第二步:设计模式
- 第三步:代码库的分块与摄取
- 第四步:文档的分块与摄取
- 第五步:连接 Claude Code、Cursor 和 VS Code
- 尝试运行
- 代理运行手册:自动设置
为什么编码助手需要超越训练数据
LLM 具有固定的知识截止期,且对私有代码一无所知。简单的解决方法是直接把文件丢进提示词中。但这带来三个问题:
- 成本:上下文中的 token 按轮次计费。一个 200 个文件的 Python 项目无法完全放入提示词,即使部分放入,在代理推理过程中也会持续计费。
- 过时的上下文:一旦文件进入提示词,它就被冻结了。如果代理修改了一个函数后又需要重新读取,它必须重新加载整个文件。模型所见的视图与磁盘上的真实情况之间没有实时链接。
- 不恰当的粒度:即使文件能放入提示词,模型的注意力也会浪费在无关的部分——导入、模块级样板代码等。代理正在编辑的函数与上百行
from x import y争夺上下文。
检索解决了这三个问题。只需一次索引代码库,将分块存入向量数据库,让 LLM 客户端按需拉取所需内容。这就是 RAG。编码助手此前缺乏一种干净的方式与这类数据库通信,无需自定义适配层。这就是 MCP 发挥作用的地方。
为什么 Weaviate MCP 适合这项工作
模型上下文协议(MCP)是 Claude Code、Cursor 和 VS Code 等 LLM 客户端调用外部工具的标准方式。Weaviate v1.37.1 将其核心操作作为 MCP 工具暴露在 Streamable HTTP 端点 /v1/mcp 上。共提供四个工具:
weaviate-collections-get-config:让 LLM 检查可用的集合及其属性weaviate-tenants-list:在使用多租户时列出租户weaviate-query-hybrid:运行混合(BM25+向量)搜索weaviate-objects-upsert:仅在启用写权限时写入对象
混合搜索是该方案对编码助手最具体的好处。代码是标识符与意图的混合体。BM25 精确匹配标识符,向量捕获意图。像“我们在哪里处理 429 重试?”这样的查询需要两者同时作用:向量找到语义相关的重试代码,BM25 将 429 作为精确词条锚定。纯向量检索会丢失整数匹配,纯 BM25 会遗漏用户未提及的措辞。混合搜索在这种混合意图查询中胜出。
操作简便性是第二个原因。其他方案需要运行一个 MCP 服务器外加向量数据库,相当于在生产中监视两个服务。Weaviate 将 MCP 服务器放在数据库内部,同端口、同认证体系,你只需要监控 Weaviate 本身。
第三个原因是多租户。一个 Weaviate 实例可以承载多个代码库,每个库作为独立的租户隔离。对于拥有多个仓库的组织,一个集群即可,无需为每个团队单独部署。
关于 Weaviate MCP 与函数调用(function calling)的区别:函数调用是针对具体 LLM API 的,而 MCP 是传输层协议——任何支持 MCP 的客户端都可以调用任何支持 MCP 的服务器,无需重写。构建一次检索,即可在 Claude Code、Cursor 和 VS Code 中使用,无需转换。
第一步:运行带 MCP 的 Weaviate
MCP 服务器默认禁用。两个环境变量可启用:
services:
weaviate:
image: cr.weaviate.io/semitechnologies/weaviate:1.37.1
ports:
- '8080:8080'
- '50051:50051'
environment:
MCP_SERVER_ENABLED: 'true'
MCP_SERVER_WRITE_ACCESS_ENABLED: 'true'
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true'
DEFAULT_VECTORIZER_MODULE: 'text2vec-openai'
ENABLE_MODULES: 'text2vec-openai'
OPENAI_APIKEY: ${OPENAI_APIKEY}MCP_SERVER_ENABLED 暴露只读工具(集合查询、租户列表、混合查询)。MCP_SERVER_WRITE_ACCESS_ENABLED 添加 upsert 工具,允许代理写回结果。如果只需要检索,可以跳过写权限。建议先只开启只读模式,因为大多数编码代理的工作都只涉及检索,而且可以避免代理向知识库写入无意义内容的风险。
生产环境认证
本例使用匿名访问以保持聚焦于 MCP 配置。对于任何网络部署,应启用 API 密钥并在客户端配置中添加 Authorization: Bearer——Weaviate 的 MCP 服务器支持标准认证和 RBAC。
启动后,可通过 curl 发送 initialize 握手确认端点是否存活:
docker compose up -d
curl -sf -X POST http://localhost:8080/v1/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'如果 MCP 服务器存活,会返回一个包含服务器能力的 JSON-RPC 响应并设置 Mcp-Session-Id 头。
第二步:设计模式
创建两个集合:CodeChunks 和 DocChunks,共享同一个 Weaviate 实例。
client.collections.create(
name="CodeChunks",
properties=[
Property(name="content", data_type=DataType.TEXT, tokenization=Tokenization.WORD),
Property(name="symbol", data_type=DataType.TEXT, tokenization=Tokenization.LOWERCASE),
Property(name="file_path", data_type=DataType.TEXT, tokenization=Tokenization.FIELD),
Property(name="language", data_type=DataType.TEXT, tokenization=Tokenization.FIELD),
Property(name="repo", data_type=DataType.TEXT, tokenization=Tokenization.FIELD),
],
vector_config=Configure.Vectors.text2vec_openai(),
)symbol 使用小写分词,使 connect_to_local 作为一个 token 而非三个;file_path、language、repo 使用 FIELD 实现精确匹配;content 和 title 使用 WORD。
第三步:代码库的分块与摄取
基于行的朴素分块会破坏代码结构。应采用语法边界分块:每个函数、每个类、每个顶级语句一个块。Python 标准库 ast 即可胜任 Python 代码的分块:
import ast
from pathlib import Path
def chunk_python_file(path: Path) -> list[dict]:
source = path.read_text()
tree = ast.parse(source)
lines = source.splitlines()
chunks = []
for node in ast.iter_child_nodes(tree):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
start = node.lineno - 1
end = node.end_lineno
chunks.append({
"content": "\n".join(lines[start:end]),
"symbol": node.name,
"file_path": str(path),
"language": "python",
"repo": "my-service",
})
return chunks然后使用 Weaviate 的批量写入模式:
with code_chunks.batch.dynamic() as batch:
for py_file in Path("./").rglob("*.py"):
for chunk in chunk_python_file(py_file):
batch.add_object(properties=chunk)每个分块在插入时由 text2vec-openai 自动向量化。也可以替换为 text2vec-voyageai 或 text2vec-cohere。
第四步:文档的分块与摄取 文档分块应以标题为边界。推荐按 H2 分割,若某节过长再按 H3 细分:
import re
def chunk_markdown(text: str, max_chars: int = 1500) -> list[str]:
sections = re.split(r"(?m)^## ", text)
chunks = []
for section in sections:
if len(section) < max_chars:
chunks.append(section.strip())
else:
subsections = re.split(r"(?m)^### ", section)
for sub in subsections:
if sub.strip():
chunks.append(sub.strip()[:max_chars])
return chunks每个块同时保留 title 和 source_url 属性。
第五步:连接 Claude Code、Cursor 和 VS Code
- Claude Code 原生支持 MCP:在
~/.claude/settings.json中添加配置指向 Weaviate 端点,混合搜索工具自动可用。 - Curs 通过
.cursor/mcp.json配置:原理类似,只需指定 URL 和工具列表。 - VS Code 需安装 MCP 扩展后配置
mcp.json。
代理运行手册:自动设置
提供一个 shell 脚本 setup.sh,包含以下步骤:检查环境变量、拉取 Docker 镜像、启动 Weaviate、等待就绪、验证 MCP 端点,然后运行 ingest.py 摄取数据。脚本具有幂等性,可重复执行。
总之,Weaviate MCP 为编码助手提供了一条极简的路径来实现高效的混合检索。通过将 MCP 服务器内建在数据库内,该方案消除了额外的运维负担,同时提供了强大的多租户支持和开箱即用的混合搜索能力。无论是个人项目还是团队协作,都可以快速上手,让 LLM 代理真正理解私有代码库。