🚀 RiMCP_hybrid
这个项目提供了一套面向 RimWorld 源代码与 XML 定义的检索与导航工具。其核心目标是结合词法检索、语义检索和图结构导航,构建一个可被 AI 助手调用的服务。该项目既可以作为命令行工具使用,也能以 MCP 服务器的形式集成到 Claude Desktop 或 VS Code Copilot 等助手中。当前源码实现了索引构建、混合检索策略、跨层图关系以及完整源码定位与返回等功能。
🚀 快速开始
要启动这个系统用于交互或集成,可直接运行 MCP 服务器组件,服务进程以标准输入输出或 JSON - RPC 协议暴露接口,外部的 AI 助手能向其发送检索与导航请求并接收结构化响应。也可在命令行模式下运行各个工具命令来进行索引构建、混合检索或图查询,便于离线测试和脚本化使用。
构建或更新索引
生成倒排索引、向量嵌入和图关系数据。
- 最小索引构建命令(在项目根或 RimWorldCodeRag 目录下运行):
cd src\RimWorldCodeRag
dotnet run -- index --root "..\..\RimWorldData"
cd src\RimWorldCodeRag
dotnet run -- index --root "..\..\RimWorldData" --python-batch 128 --embedding-server "http://127.0.0.1:5000"
cd src\RimWorldCodeRag
dotnet run -- index --root "..\..\RimWorldData" --embedding-server "http://127.0.0.1:5000"
- 新增功能:使用远程嵌入 API(例如 OpenAI):
cd src\RimWorldCodeRag
dotnet run -- index --root "..\..\RimWorldData" --embedding-server "https://api.openai.com/v1/embeddings" --api-key "sk-1234567890abcdefghijklmnopqrstuvwxyz" --model-name "text-embedding-3-small"
- 带强制重建(忽略增量判断,重新构建全部索引):
--force 参数现在支持更精细的控制,可以指定重建索引的特定部分,从而节省时间。
cd src\RimWorldCodeRag
dotnet run -- index --root "..\..\RimWorldData" --force all --embedding-server "http://127.0.0.1:5000" --python-batch 512
cd src\RimWorldCodeRag
dotnet run -- index --root "..\..\RimWorldData" --force lucene
cd src\RimWorldCodeRag
dotnet run -- index --root "..\..\RimWorldData" --force embed --embedding-server "http://127.0.0.1:5000" --python-batch 512
cd src\RimWorldCodeRag
dotnet run -- index --root "..\..\RimWorldData" --force graph
注意:--force 强制清空/刷新已有索引并从头构建,适用于修复字段存储或切分规则变更后的完全重建。常规更新可去掉 --force 以启动增量构建,更快且保留未变更的数据。
提示:最佳 batch 大小和 vram 有关。在 Geforce rtx4060 laptop + 16gb vram 上,256 ~ 512 的 batch 大小比较合适。超过这个值会导致部分数据被动态迁移至 CPU,极大降低嵌入效率。可多试验几次,找到最佳 batch 大小。
提示:使用 --embedding-server 可以连接到已运行的嵌入服务器,避免每次批次重新加载模型的开销。需要先运行嵌入服务器:.\scripts\start-embedding-server.ps1
CLI 查询命令
cd src\RimWorldCodeRag
dotnet run -- rough-search --query "weapon gun" --kind def --lexical-k 2000 --max-results 10
- 查找某个符号使用(返回该符号引用的其他符号,用
--kind 限制层):
dotnet run -- get-uses --symbol "xml:Gun_Revolver" --kind csharp
dotnet run -- get-used-by --symbol "RimWorld.CompProperties_Power" --kind xml
- 获取完整源码(按符号返回原始文件片段,可附带行数限制):
dotnet run -- get-item --symbol "RimWorld.Building_Door" --max-lines 200
dotnet run -- get-item --symbol "xml:Door"
常用参数说明
--kind 支持 csharp/cs 或 xml/def,用于只在某一层(C# 或 XML)查询
--max-results 控制返回候选数,--max-lines 控制源码返回行数上限
✨ 主要特性
- 提供面向 RimWorld 源代码与 XML 定义的检索与导航工具。
- 结合词法检索、语义检索和图结构导航,构建可被 AI 助手调用的服务。
- 既可以作为命令行工具使用,也能以 MCP 服务器的形式集成到其他助手中。
- 实现了索引构建、混合检索策略、跨层图关系以及完整源码定位与返回等功能。
📦 安装指南
MCP 服务器完整设置指南
前置要求
- .NET 8.0 SDK(从 microsoft.com/net 下载)
- Python 3.9+(从 python.org 下载)
- RimWorld 游戏文件:从 RimWorld 安装目录复制 Def 数据:C:\Program Files (x86)\Steam\steamapps\common\RimWorld\Data;C# 源码通过 ILSpy 或者 dnspy 导出,存进项目根目录/RimWorldData
- 跨平台支持:Windows (PowerShell)、Linux/macOS (Shell 脚本)
1. 设置项目结构
cd RiMCP_hybrid/
2. 设置嵌入环境(一次性,在构建前运行)
.\scripts\setup-embedding-env.ps1
./scripts/setup-embedding-env.sh
3. 构建项目
dotnet build
4. 构建索引(一次性,在实际服务前运行)
cd src/RimWorldCodeRag
dotnet run -- index --root "..\..\RimWorldData"
5. 启动服务
.\scripts\start-embedding-server.ps1
./scripts/start-embedding-server.sh
cd src\RimWorldCodeRag.McpServer
dotnet run
MCP 服务器详细配置
环境变量配置
在运行 MCP 服务器前设置这些变量:
$env:RIMWORLD_INDEX_ROOT = "c:\path\to\RiMCP_hybrid\index"
$env:EMBEDDING_SERVER_URL = "http://127.0.0.1:5000"
替代方案:appsettings.json 配置
编辑 src/RimWorldCodeRag.McpServer/appsettings.json:
{
"McpServer": {
"IndexRoot": "c:/path/to/RiMCP_hybrid/index",
"EmbeddingServerUrl": "http://127.0.0.1:5000"
}
}
💻 使用示例
第一次使用
先完整构建索引(无 --force 或用 --force 如果你想确保干净状态),然后运行若干典型查询确认结果。
代码或解析规则更新后
如果只是少量文件改动,使用增量索引;若修改了切分或存储字段,使用 --force 完整重建。
在将 MCP 服务交给 AI 助手前
在本地用 CLI 验证常用查询,确保 get-item 能返回正确源码片段,并检查图查询(get-uses/get-used-by)的结果合理性。
跨平台支持
- Windows: 使用 PowerShell 脚本(
.ps1)
- Linux/macOS: 使用 shell 脚本(
.sh)
- 两个平台都支持相同的功能和参数
日常维护
把索引构建或增量更新放入 CI 流程中,关键分支变更后运行验证脚本。
📚 详细文档
MCP 工具说明
- rough_search - 混合语义搜索:使用自然语言查询搜索 RimWorld 代码符号和 XML 定义。返回匹配项目名称列表及元数据。随后使用
get_item 工具获取任何感兴趣结果的完整源代码。如果搜索没有返回相关结果,请尝试简化查询以聚焦基本关键词。
- get_uses - 依赖分析(下游):查找符号依赖什么 - 显示调用关系和实现逻辑。非常适合通过追踪使用了哪些其他代码/符号来理解功能的工作原理。随后使用
get_item 工具检查任何感兴趣依赖项的完整源代码。
- get_used_by - 反向依赖分析(上游):查找什么使用了符号 - 显示反向依赖和调用关系。非常适合通过追踪谁调用或引用了符号来理解影响范围和使用模式。随后使用
get_item 工具检查任何感兴趣调用者的完整源代码。
- get_item - 精确源代码检索:检索特定符号的完整源代码和元数据。从
rough_search、get_uses 或 get_used_by 结果中找到感兴趣的符号后使用此工具。返回完整的类定义、方法实现或 XML 定义及详细元数据。
测试 MCP 服务器
基础测试
cd src\RimWorldCodeRag.McpServer
.\test-mcp.ps1
./test-mcp.sh
手动 JSON - RPC 测试
dotnet run
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05"}}' | dotnet run
VS Code 集成
创建或编辑 %APPDATA%\Code\User\globalStorage\mcp-servers.json:
{
"mcpServers": {
"rimworld-code-rag": {
"command": "dotnet",
"args": [
"run",
"--project",
"c:/path/to/RiMCP_hybrid/src/RimWorldCodeRag.McpServer"
],
"env": {
"RIMWORLD_INDEX_ROOT": "c:/path/to/RiMCP_hybrid/index",
"EMBEDDING_SERVER_URL": "http://127.0.0.1:5000"
}
}
}
}
🔧 技术细节
思路介绍
项目主要分为三部分:索引构建管线,检索工具,mcp 服务器相关。
索引构建管线
- 数据分类:索引过程从原始数据开始,原始数据主要包括 C# 源代码文件和 XML 定义文件(Defs)。索引的目标是提取可检索、可理解的最小单元,这些单元要保留足够上下文以便阅读,也应足够小以便精确匹配和生成语义向量。第一步是扫描文件系统,逐文件读取并分类,记录文件路径、编码、时间戳等基础元数据作为索引项的输入标识。元素有类,函数,变量定义和 xmldef。
- 文本切分:读取文件后,需把长文本切分成若干块。对 C# 代码,按语法符号切分,分片基于类、方法、属性或注释块,同时记录每个片段在源文件中的起止偏移,以便后续精确回溯。对 XML Def,通常保留整个定义节点的字符串表示为一个块,因为 Def 本身语义相对独立且自包含。切分时会生成每个块的标题、摘要性片段和用于检索的正文文本。切分策略会影响召回质量,块过大可能导致上下文腐化,块过小又可能丢失相关信息。
- 存储层写入:文本块生成后,索引流程并行地把每个块送入两个不同的存储层:用于传统词法检索的倒排索引(用 Lucene 实现)和用于语义检索的向量索引。写入倒排索引时,会把块的标题、类型、路径、源偏移、标签等结构化字段一起存储,这些字段既支持过滤,也支持把结果定位回源文件。向量部分要求先把文本块转换成向量表示,这通常由外部的嵌入服务完成。项目对嵌入生成支持批量参数配置,以便在不同显存或 CPU 条件下调整批处理大小。生成向量后,这些向量会被写入向量索引,并与对应的块 ID 建立映射。由于 C# 的 huggingface 支持不佳,专门起了一个 Python 服务器在后台 127.0.0.1:5000 进行嵌入服务,这样也能节省每次查询时冷启动的时间。项目用上了 .NET 的 SIMD 加速特性,通过
System.Numerics.Tensors 库让向量点积运算跑得飞快,在现代 CPU 上能发挥硬件并行计算的全部潜力。
- 图关系构建:索引的另一个关键部分是图关系构建。静态分析器会在解析源码与 XML 时寻找有意义的引用关系,如继承、方法调用、字段引用、XML 到 C# 的绑定等。每发现一条关系,都会把它作为图的有向边写入图存储。图的节点和边属性存储在 tsv 中,而额外使用了两份(行和列)压缩稀疏矩阵表示来存储,这样可以在 O(1) 时间写入,读取向下关系,读取向上关系。同时,由于使用二进制 bit 存储而不是完整的边信息文本,把原先 b 树数据库的 6.2gb 大小压缩到了 400mb。这些边补充了文本相似度的不足,使得跨层查询(如"哪些 Def 使用了这个 C# 组件")可以高效地回答。
检索召回部分
- 检索工具:检索召回一共有四个工具:粗搜
rough_search,两个方向的依赖树搜索 uses,used_by,和整段代码召回 get_item。核心思路是尽量减少无用源代码块的直接返回,以减少大模型上下文中的噪声。大模型 agent 的典型搜索路径是:先对一个问题粗搜,得到一个候选元素列表,然后大模型选择最感兴趣的元素名字,召回整块代码;或者通过 get_uses 和 get_used_by 找依赖关系,得到元素列表,选择最感兴趣的元素名字召回整块代码。
- 粗搜策略:粗搜分成两步:先使用 Lucene 模糊搜索和 BM25 字面相似度排序对所有大约八万个元素进行快速索引,选取最相似的 1000 个。然后对这 1000 个实施基于向量相似度的语义相似度打分,选出候选的五个内容。这种方式能保证快,并且并没有想象中的那么容易漏掉正确答案。一开始打算将字面相似度分数和向量语义相似度分数相加,但并没有得到两者各自的优点,反而使得噪声淹没了信息。
- 图检索设计:图检索没什么特别的设计,三个图数据库合在一起足够快,同时每条边属性的注册也能便利根据种类的结果预先筛选,减少噪音。测试时,曾因大模型搜索
Verse.Thing 的被引用关系,MCP 直接返回两万六千条数据,导致上下文溢出。后来重写了返回内容的排序,确保排序稳定性,以此基础做了一个简单的分页机制,用实际运行的请求次数和 token 量为代价确保了一些安全。
- 部署与性能:项目对嵌入生成与向量索引的批量大小提供了调整参数,用以在不同显存与硬件配置下平衡速度与资源占用。实际运行时,Lucene 的检索延迟通常很小,而语义重排序和向量搜索会随着候选集和向量模型大小有所变化,因此在资源受限的环境里可以选择关闭嵌入或调小批次来保证稳定运行。
后续迭代方向
- 检索架构调整:虽然粗搜阶段的 Lucene -> embedding 听起来不错,但实际测试发现,Lucene 筛掉的正确选项比预想中的更多。因此,决定放弃混合双阶段检索的架构,直接采用全量向量检索,相信 Faiss 可以提供足够的算法加速。当然,Lucene 和 BM25 的重排并不是完全没用,在剩余三个工具中,由于输入都需要是准确的元素名称,很多时候会导致召回失败,可以使用这一套高效快速的检索和字面相似度评分替代原先的直接匹配,提供更多的鲁棒性,提升 Agent 工作流中的流畅度。
- 权重算法设计:在测试
get_uses 和 get_used_by 工具时,发现由于关系提取太过细致,Rimworld 的源码又足够复杂,提取上下游关系时有可能返回超大量数据,为大模型的判断带来了不少噪声。因此,计划设计一套权重算法,为图的边附上优先级权重,查询依赖关系的结果按照倒序排序,就能更高概率将重要的链接排到前面,变相降低大模型获得的信息中的噪声。边的权重也许会和边的种类,节点的重要性指数(计划使用经典的谷歌 Pagerank 算法),甚至可能有加权因子是由两者名字的字面相似程度计算而来。但担心这套权重算法由于是人工直观设计,并不能反映理论上的重要性,甚至会对检索效率产生负面优化。若寻求非人工设计的最优解加权方式,却甚至无法定义问题所在,从算法角度更是无可下手。所以未来大量的实验应该是避免不了的。
- 功能更新:
- v1.1.0 更新:远程嵌入 API 的功能已经实现了。现在可以通过
--api-key 和 --model-name 参数来连接到像 OpenAI 这样的外部嵌入服务了。感谢 asvc_33 大佬的贡献!
- v1.1.2 更新:稍微改变了 bm25 的运行方式,现在 bm25 会在各元素代码全文(而不是原先的元素名称)中匹配字面相似度,这是更合理并且普遍的一种做法。经过试验,其实大约是发挥了应有的效果,同时召回并不算慢。这样看来,faiss 的必要性减弱了。
- v1.2.0 更新:给图生成设计了一套边的权重生成公式:
$$(Pr\cdot 10^7)\sqrt{d}\cdot w$$
其中 Pr 代表目标节点的 Pagerank 原始分数,d 代表同一个连接在这个元素中的出现次数,w 代表节点的属性所自带的权重(这个权重是自己编的,比如继承为 2.0,函数调用为 0.7,并没有什么特别的依据)。$10^7$ 的目的是防止浮点数误差,因为 Pr 值一般数量级都较小。
- **1.2.1 更新**:在不断的实验中发现图生成目前有非常大的问题,比如会对父类的关系也全盘继承导致返回大量结果,噪声很大,同时会漏捕捉静态成员和反射调用的情况(实际上 xml 和 c# 的连接大多数都是这种方式,然而此前全部漏分析了)。这需要对解析代码的方式做出一些深度的重构,目前先修复了重复抓取边的问题,将原先将近五千万条边降到了只有 23 万条,是一个对可用性比较大的提升。
📄 许可证
这个项目遵循 MIT 协议,意味着代码可以任意为太阳系内的所有生物自由使用。
相关文档
- 实现详情:
docs/ 目录
- MCP 协议:https://modelcontextprotocol.io/
故障排查
MCP 服务器故障排查
- "Index not found" 错误:确保运行了索引构建步骤:
dotnet run -- index --root "..\..\RimWorldData";检查 RIMWORLD_INDEX_ROOT 环境变量是否指向 index/ 文件夹。
- "Embedding server connection failed" 错误:先启动嵌入服务器,Windows PowerShell 用
.\scripts\start-embedding-server.ps1,Linux/macOS 用 ./scripts/start-embedding-server.sh;等待"Model loaded successfully"消息;检查是否在端口 5000 运行。
- 构建失败:确保安装了 .NET 8.0 SDK;从项目根目录运行
dotnet build。
- 无搜索结果:验证 RimWorldData 文件夹包含游戏文件;尝试更简单的搜索查询(例如 "pawn" 而不是 "pawn hunger system")。
故障排查速查
- 无法找到符号:检查符号格式,XML 用
xml:DefName,C# 用 Namespace.ClassName;必要时重新构建索引并验证。
- 嵌入生成失败或 OOM:减小
--python-batch,或在无 GPU 的环境下使用较小模型或仅使用倒排索引。
- MCP 无响应:检查启动目录与命令是否正确,确认
dotnet run 能在命令行单独启动服务器,并查看控制台日志以定位错误。
- 远程 API 错误:当使用
--api-key 时,请确保同时提供了 --embedding-server (API 的 URL) 和 --model-name。
性能说明
- 冷启动:~2 - 5 秒(加载索引)
- 热查询:0.5 - 1 秒
- 内存使用:向量索引约 300MB
- 推荐 GPU 用于嵌入服务器(显著加速)
更新说明
RimWorld 更新时:
- 使用新游戏文件更新
RimWorldData/ 文件夹。
- 重新构建索引:
dotnet run -- index --root "..\..\RimWorldData" --force。