找不到位置
按名称 grep 会得到几十个候选,而目标并不排在第一。VulnGym 研究:在调用链超过 9 步的任务中,56.5% 的失败是代理没找到文件(26.8%),或找到了文件却没找到行(29.7%)。一半以上的失败出在导航,而不是分析。
来源: 实测数据
开源 · MIT · v0.1.0
RAGRAF 为代码仓库建立索引,通过固定操作回答 LLM 代理的问题:定义在哪里、谁调用了它、修改会影响什么。每个事实都带有可验证的 file:line 引用。
$ ragraf -p config/portal.yaml query write_check DocPayments --columns "No,Status,AuthorUserID,UpdatedBy"
{
"op": "write_check",
"found": true,
"problem": "колонки отсутствуют в таблице: AuthorUserID, UpdatedBy",
"result": {
"table": "DocPayments",
"unknown_columns": [
{ "name": "AuthorUserID", "nearest": ["ID"] },
{ "name": "UpdatedBy", "nearest": ["UpdatedAt", "DeletedBy"] }
],
"primary_key": ["ID"],
"column_count": 32,
"source": "migration"
}
}错误止步于运行前——SQLSTATE[42S22] 从未发生,代码一次写对。
92%
首次查询命中目标文件
13 中 12,实测
100%
事实带有 file:line 引用
实测 M6
1,0
每个任务的工具调用次数
此前 2,0–3,1
0
完整读取的文件数
此前 14
在真实 PHP 门户项目上测得(14 个导航任务):见项目的 bench/results/ 与 README。调用数与命中率为实测值;「实际约 43 次」为记录在测量工件中的模型修正值。
问题
凡是让 LLM 在现有项目中改过代码的人,都熟悉这三个症状。全部经过实测,而非杜撰。
按名称 grep 会得到几十个候选,而目标并不排在第一。VulnGym 研究:在调用链超过 9 步的任务中,56.5% 的失败是代理没找到文件(26.8%),或找到了文件却没找到行(29.7%)。一半以上的失败出在导航,而不是分析。
来源: 实测数据
字段名照着隔壁模块的变量写——直到运行时才发现:首次保存即报 SQLSTATE[42S22] Unknown column。迁移文件里的表结构应当在代码执行前完成校验。
来源: 真实缺陷
索引建好之后代码又被修改——代理拿到的是关于已不存在状态的事实。「没有这个函数」和「函数是索引之后新增的」在回答中无法区分,决策便建立在错误事实之上。
来源: 核心风险
给代理 Cypher/CPGQL?LLMxCPG(USENIX Security \u{27}25):基础模型在 1278 次中仅生成 19–132 条有效的代码图查询。自由查询语言会沦为错误生成器。
来源: 文献
RAGRAF 由此得出的结论:代理一半以上的失败不在分析,而在导航。这部分工作应当从模型中剥离,交给一个用事实回答、并且知道自己边界的外部索引。
功能特性
用固定契约取代自由检索:22 个门面操作、23 个 MCP 工具、三个 SQLite 存储。没有多余的东西——也没有会悄悄坏掉的东西。
symbol、callers、impact、slice、schema、write_check、policy_trace……代理只需选择「问什么」,不必操心「怎么问」:不提供图查询语言——这是基于实测的决定,而非主观偏好。
门面:22 个 op_* · MCP:23 个工具
path:line、来源标记(stated——分析器证明;derived——启发式)、置信度、index_rev。没有证据的边不会作为事实存在于图中。
引用可验证率:100%(M6 实测)
修订版本不可变;代码改动累积在只追加的差异日志中;受影响的事实在每个回答中标记 stale;校验和守护进程可自动重建索引。
drift 日志 · 守护进程 · 自动重建
BM25(FTS5)+ 384 维向量(paraphrase-multilingual-MiniLM-L12-v2,经 fastembed/ONNX)。无 torch、无云端、无订阅。模型仅下载一次(约 240 MB)。
SQLite:图 + FTS5 + vec0,共三个文件
表和字段直接解析自 migrations/*.sql,无需连接数据库。write_check 在执行前将写入计划与表结构核对,并给出最接近的现有字段名建议。
试点项目:44 张表 / 453 个字段
通过 stdio 提供 23 个 ragraf_* 工具(JSON-RPC)。缺少 Joern 层时 capability=degraded:slice 会说明原因并拒绝,其余操作照常。绝不静默降级。
可选 CPG 层:Joern + JDK 21
16 种语言按语法树解析(PHP、Python、JS、TS、Java、C#、C、C++、Ruby、Lua、Go、Perl、bash、PowerShell、CSS、HTML),另有 10 种按纯文本收录(Markdown、SQL、YAML、JSON、TOML、INI、XML、RST、batch、纯文本)。
工作原理
索引阶段生成不可变的修订快照;应答阶段读取快照并校验其新鲜度。工作期间的代码改动进入日志,而不是索引。
架构
索引的全部状态都存放在项目内的 .ragraf/ 目录中。没有服务、没有守护进程、没有云端。删掉目录,索引即消失。
节点(类、函数、方法)、带证据的边(调用、继承、导入)、数据库结构、路由。全部按 rev_id 标记。
FTS5 词法(BM25)与 vec0 向量(384 维)同库存储;分块原文可直接返回,无需读盘。
修订历史、只追加的差异日志、重建归档、调用遥测。永不删除。
# ragraf init <项目目录> — 自动生成配置。
# 索引位于项目内的 .ragraf/(请加入 .gitignore)。
project: myproject
root: C:/path/to/your/project
languages: [python, javascript, markdown]
include: ["**/*.py", "**/*.js", "**/*.md"]
exclude: ["**/node_modules/**", "**/build/**"]
secrets_exclude: # 不索引、不读取
- "**/.env"
- "**/*.pem"
embedding:
model: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
dim: 384
index:
retain_revs: 3
joern_enabled: true # 无 Joern 时 capability=degraded{
"op": "impact",
"index_rev": "rev-0007",
"capability": "full",
"drift": { "files": 3, "symbols": 7, "entries": 4 },
"provenance": "index",
"limits": ["статический анализ; динамика не покрыта"],
"stale_warnings": [
{ "path": "src/Pipeline/PipelineEngine.php",
"reason": "modify после rev-0007",
"suggest": "live_read" }
],
"result": [ "…" ]
}{
"mcpServers": {
"ragraf": {
"command": "python",
"args": [
"/path/to/RAGRAF/tools/ragraf-mcp.py",
"--project", "/path/to/RAGRAF/config/mycorpus.yaml",
"--agent", "my-agent",
"--agent-kind": "external"
]
}
}
}演示
以下场景基于产品的真实输出(项目测量与文章)。这些是界面模拟图而非截图:CLI 与 MCP 有意不做图形界面——这也是设计哲学的一部分。
代理的请求
ragraf -p config/portal.yaml query symbol --name z_agr_acl_audit
此前代理仅凭签名「array $data」构造调用,结果得到 TypeError:array 类型并不能说明键的组成。
代理的请求
ragraf -p config/portal.yaml query impact --target "DocJournal::submit" --mode hard --depth 2
已证明与启发式的结果绝不混排在同一区块:基于 derived 的结论必须标注「待验证」。
修订版本
rev-0016
capability: full · 索引为最新
语料
123 个文件
487 节点 · 7 292 边 · 27.5 MB
质量
分块:457
最大 5 543 token · 超预算:0
调用
本次会话 46 次
运行错误 0 · 节省约 5 200 token(估算)
零依赖的本地面板:仅监听回环地址,修改操作需控制台令牌。
模拟界面。数据取自产品文章与调用日志中的真实输出。
快速开始
无需管理员权限:一切安装在用户目录中。已验证平台为 Windows + PowerShell 7.6;通用 pip 方式适用于任何装有 Python 3.13+ 的系统。
脚本会在 ~/.ragraf/venv 创建虚拟环境、安装依赖,并以汇总表逐步验证结果。日志位于 ~/.ragraf/logs/。
git clone https://github.com/paulmann/RAGRAF.git
cd RAGRAF
pwsh -NoLogo -NoProfile -File scripts/install.ps1ensure 会找到语料,为未知目录自动创建,并重建过时的索引——一条命令完成。
ragraf ensure C:\work\myproject全局参数 -p 与 --json 放在子命令之前。selftest 输出 11 个组件的检查报告。
ragraf -p <配置> query search "порог переиндексации"
ragraf -p <配置> query symbol --name corpus_hash
ragraf -p <配置> selftest通用方式:创建虚拟环境并以 editable 模式安装。ragraf 控制台命令会出现在 venv 中。
git clone https://github.com/paulmann/RAGRAF.git
cd RAGRAF
python -m venv .venv
. .venv/bin/activate
pip install -e .同样是 ensure 命令;索引会生成在项目内的 .ragraf/ 目录——请将其加入该仓库的 .gitignore。
ragraf ensure /work/myproject不安装包也可以运行:包装脚本会自动把 src/ 加入路径。
ragraf -p <配置> query search "порог переиндексации"
# 或不安装直接运行:
python tools/ragraf.py -p <配置> status参数顺序
全局参数 --project/-p 与 --json 必须放在子命令之前(ragraf -p cfg query search …)。例外:ensure 有自己的 --json,放在其后(ragraf ensure <dir> --json)。
首次带向量索引
会将嵌入模型(约 240 MB)下载到 ~/.ragraf/models(仅一次)。无网络或使用 --no-vectors 时,BM25 词法检索照常工作。
selftest 不是门禁
该命令永远返回退出码 0:它是报告而非检查。请阅读报告本身:9 个必需组件 + joern/java(可选)。
企业 TLS 代理
若模型下载报 certificate verify failed——请安装 truststore:install.ps1 -WithTruststore 或 pip install truststore。
完整命令列表见 ragraf --help.
文档与测量协议位于 代码仓库.
FAQ
对高频问题的简短回答。细节请查阅代码仓库中的文档。
代码索引不是「更快的搜索」,而是把一部分工作从模型转移到一个知道自己不知道什么的外部结构。RAGRAF 最可贵的特质:在没有把握时,它拒绝给出自信的回答。
签名 array $data 诚实却无用;写明键组成的 docblock 才有用;事实旁边的 stale: true 标记则是必需。