ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

XERJ Second Brain 实战指南:一条命令把笔记文件夹变成可查询、可重放、带证据的链接索引

XERJ Second Brain 实战指南:一条命令把笔记文件夹变成可查询、可重放、带证据的链接索引
  • 人工智能
  • 搜索引擎
  • 本地部署
  • 后端
  • MCP 服务

【免费下载链接】xerj

XERJ is the new way for AI to search data. Its autoindex capability activates agents to know your data without the token waste of grep and sed. One command indexes code, docs, logs and PDFs for search, RAG, security audits and agent memory, using 40x fewer tokens than grep. Elasticsearch compatible, so existing clients just work.

项目地址:https://gitcode.com/GitHub_Trending/xe/xerj
点击查看免费下载

XERJ 的xerj brain命令能把任意笔记文件夹一步变成运行中的"第二大脑":本地服务器自动索引语料、用五个确定性边检测器提取笔记之间的链接关系、生成带仪表盘的图谱视图,并通过 REST API 与 MCP 接口向 Agent 开放。本指南以仓库中的 second-brain 演示目录 为骨架,结合引擎侧源码深入讲解语料设计、检测器原理、API 语义与验证脚本,读完后你能完整复现这套"一键建脑 + 可验证"的流程,并理解其确定性、双向时间戳与诚实记账的底层设计。

这个 Demo 是什么:搜索索引上的图状关系层

目录 demo/usecases/second-brain 存放着一套"语料生成器 + 验证装置":一条命令把一文件夹笔记变成可查询、可重放、带仪表盘的链接索引。其核心理念在 README 中讲得很直白——

XERJ doesn't understand your notes — it makes the structure you wrote queryable, timestamped, and evidence-backed, deterministically. It is a search engine with a graph-shaped index over its own documents,not a graph databaseand not a brain.

也就是说:它不是图数据库,更不是"大脑",而是一个"对自己的文档带有图状索引的搜索引擎"。每条边都是普通文档,存储在保留索引.xerj-memory-{brain}-edges中,走既有的 WAL → memtable → segment → doc-values 路径,零存储格式改动(见 graph_api.rs 模块头注释)。目录里没有任何 mock——脚本打印的每个数字都来自对真实运行服务器的实时响应。

演示语料库:39 个文件,每个检测器都有诚实的活儿干

gen-corpus.sh 生成一个真实的 39 文件个人知识库(酸面包烘焙 + 面包店启动项目),语料形状经过刻意设计,让每个链接检测器都有事可做、且测试覆盖到边界:

文件夹文件数覆盖的检测器与边界
notes/15[[wikilink]]——zettel 之间的作者链接;其中故意留一个悬空链接([[banana-bread]]没有对应笔记),它必须被计数为未解析,绝不能凭空发明一条边
projects/bakery-launch/6相对 Markdown 链接(mdlink),包含绝不能变成边的外部 URL(如https://www.rofco.be、https://example.gov/...)
clippings/4保存的 HTML 页面,含指向语料文件的<a href>(href 检测器,同时测试外部链接被跳过)
journal/12完全没有作者链接——每条边都是结构性的(文件放在同一目录),这是让仪表盘上 AUTHORED 与 STRUCTURAL 的区分保持诚实的角落
reading/1一篇超过 2KB 分段目标的长文,自动索引将其拆分,产生 section 顺序边

语料生成器还做了一件决定整个演示确定性的事:文件字节固定,且每个 mtime 都用touch -d钉死。因为检测器边的valid_at取自 mtime(src_file的 mtime,绝不取墙钟,见 detect/mod.rs 中EdgeDraft.valid_at_ms的注释),所以重新生成语料并重跑xerj brain会重现完全相同的 edge_id。只有created_at(大脑"学会"每条链接的时刻)按契约使用墙钟。

一键运行:从语料到运行中的大脑

三个脚本构成完整流程,均幂等可重跑:

./boot-and-brain.sh # 生成语料库(若不存在),运行 xerj brain, # 验证服务器有响应 → 端口 9331-9333 ./transcript.sh # API 证明(见下文) → PASS/FAIL 汇总 ./mcp-smoke.sh # MCP 上的 Agent 界面 → PASS/FAIL 汇总

boot-and-brain.sh 默认值与环境变量覆盖:

  • 工作根目录:${TMPDIR:-/tmp}/xerj-second-brain-demo(用XERJ_BRAIN_DEMO_ROOT覆盖)
  • es-compat 端口:9331(用XERJ_BRAIN_DEMO_PORT覆盖;启动的服务器还会占用 +1/+2 即 9331-9333 给 rest/grpc,处于 9330-9345 演示区间内,绝不占用 9200/9310)
  • 二进制:<repo>/engine/target/release/xerj(用XERJ_BIN覆盖;若未编译,脚本会提示cargo build --release -p xerj-server)
  • 停止服务器:kill $(cat <root>/data/server.pid)

xerj brain这一命令本身做了三件事的组合(见 brain.rs 模块头注释):组合而非重实现——索引与链接检测复用xerj_autoindex::run_index_report完整管线(图检测器默认开启);控制台鉴权复用服务器自身的首次启动引导(xerj-console-api::bootstrap,铸造一次性 30 分钟 setup 链接并打印在启动横幅里,该命令只负责转发,从不自行铸造凭据);服务器就是普通的xerj二进制,以分离进程组 detached 方式拉起(输出到<data-dir>/server.log),因此大脑比这条命令活得更久。boot 脚本等待就绪的超时为 120 秒、轮询间隔 150ms。

xerj brain完整选项(帮助文本来自 brain.rs):

选项含义
--brain <NAME>大脑名(默认取文件夹名,是 URL 的一部分)
--url <U>使用的服务器(默认http://localhost:9200);仅当 localhost URL 无人监听时才引导新服务器
--data-dir <PATH>新引导的服务器存放数据与日志的位置(默认~/.xerj/brain)
--api-key <K>已运行的安全服务器的 API 密钥(或环境变量XERJ_API_KEY;本命令引导的服务器无需——密钥写在<data-dir>/admin.key)
--fresh忽略恢复日志、就地重建计划并重走全部文件(id 保持幂等;绝不会删除你移除的笔记的文档)
--no-open打印链接但不打开浏览器
--disable-feedback不打印反馈邀请

退出码:0就绪;3就绪但有垃圾(有不可读文件被记录,非致命);1无内容可索引或服务器失败;2用法错误。端到端测试 brain_cli.rs 验证了"首次运行引导服务器 → 打印your second brain is ready与控制台深链/_xerj-console/#/second-brain?brain=notes→ 二次运行附着到已运行服务器且边集合收敛到相同 edge_id → 三次运行不带--no-open时打开控制台视图"的完整闭环,同时断言 5 文件 fixture 恰好产出 13 条边(4 wikilink + 4 same_dir + 5 sequence openers)。

五个边检测器:确定性、带版本、带证据

演示依赖的五个检测器在 detect/mod.rs 中通过EdgeDetectortrait 统一抽象:无网络、无 LLM、无时钟读取——相同输入必须产出相同草稿。每条边都携带教会它的精确引文(evidence.quote,裁剪到 240 字符)和detector标签(如wikilink@1);任何行为变更都必须 bump@N,让旧边始终可归属到产生它的规则。以下是演示语料实际用到的五个(源码中的权重/置信度常量均为实测参数):

检测器标签边类型权重置信度检测内容与设计意图
Wikilinkwikilink@2wikilink1.00.95[[Target]]/[[Target|alias]],最强信号:人类刻意点名另一篇笔记;置信度不为 1.0 是因为按名字解析,同 stem 多文件歧义会被计数、绝不隐藏(wikilink.rs)
Mdlinkmdlink@2mdlink0.90.9text指向语料文件;内联 Markdown 链接仍是一次明确的作者行为,但也是引用外部材料的方式,本地解析命中携带略弱的"归属同一主题"意图(mdlink.rs)
Hrefhref@2href0.70.85<a href>指向语料文件,仅作用于被 sniff 为htmlfamily 的文件(扩展名是 .html 不算数,sniffing 说了算);有 raw 源扫描与 section 文本双通道(href.rs)
Sequencesequence@2sequence0.80.99文件卡片开启链,然后每个 section 在同一文件内按顺序前置;autoindex 把长文档拆成分段,没有这些边拆分就会毁掉文档自身的叙事邻接("答案之后的那段"正是 Agent 需要的)(sequence.rs)
SameDirsamedir@2same_dir0.30.4同目录文件链式连接而非团式:n 个文件恰好 n-1 条边(500 文件的目录产出 499 条而非 124,750 条),是唯一对完全无链接语料也触发的检测器,防止无链接文件夹变成孤立点组成的图(samedir.rs)

几个值得一提的确定性细节:

  • 悬空链接被计数而非发明:Wikilink 对无法解析到语料文件的[[target]]递增unresolved计数器并跳过,演示语料里的[[banana-bread]]正是这个测试点;运行摘要必须展示它,绝不凭空造边。
  • 外部链接被跳过而非悬空:mdlink/href 共用的resolve_local(detect/mod.rs)识别带 scheme 或//前缀的目标为 External——外部 URL 是 Markdown/HTML 链接的本职用途,静默跳过是正确的,只有本地 miss 才计入 dangling。
  • samedir 链天然是命名顺序序列:按 rel 排序使链确定,且自然跟随01-intro, 02-setup, …的编号约定,文件级"按命名排序"信号顺带成立。
  • edge_id 确定性:edge_id = xxh3_128("xg1\0" src "\0" type "\0" dst "\0" decimal(valid_at_ms))渲染成 32 位小写 hex(detect/mod.rs)。xerj-api与xerj-autoindex两个 crate 各持一份字节相同的实现,用同一个 pin vector 测试锁死(edge_id("note-alpha", "wikilink", "note-beta", 1753600000000) == "bef814a75bd3d914c3e561f610154304"),两个写入方永远无法漂移。相同 (src, type, dst, valid_at) → 相同_id→ 重跑时 bulkindex就地覆盖而非重复;不同的 valid_at 会创建不同的边——这是双向时间设计,不是 bug。

transcript.sh:六个 API 证明,每个数字都来自真实响应

transcript.sh 是 HTTP 面上的"证据装置",覆盖了案例研究触及的每一个断言,脚本打印的每个数字都从实时响应中读取、绝不硬编码。它证明六件事:

1. Overview——大脑级真相

GET /_graph/{brain}/overview返回契约字符串xerj-second-brain/1、存在性、live/retired 计数(断言invalidated == total - live)、五个检测器都有 live 边(按检测器家族匹配而非钉死@N——版本号被设计为会变动,钉死它会在每次刻意 bump 时误报)、lexical-feature-hash诚实标记,以及not_shown记账(types_not_listed、hubs_out_not_listed)。实现上,overview 是对 edges 索引的四次组合搜索:总数(exists src,meta 文档因此永远不参与计数)、as_of 时刻的 live 切片(bool.filter的valid_at lte as_of+must_not invalid_at lte as_of,同时给出 type/detector/hub 的 terms 聚合)、创建时间直方图(date_histogram)、节点总数计数——每个 top-N 列表的尾巴都通过not_shown上报(graph_api.rs)。

2. Ego——单篇笔记的标注邻域

GET /_graph/{brain}/ego?node=<hub>&hops=2&include_nodes=true返回 hub 的 2 跳邻域:每条边带hop与direction(out/in,按 hop 前沿集合判定)、检测出的边带教会它的evidence.quote、节点摘要被水合(nodes对象含title/preview/path/index,preview 取text/body前 160 字符,ax_path是 autoindex 写入者的诚实文件标签)、剪裁的边/过期排除/悬空节点全部进入not_shown。多 seed 形式是nodes=(最多 64 个,超出部分计入not_shown.frontier_clipped);深入探索的合规迭代方式是"把上一响应reachable的一部分喂回去再展开"。

ego 完整参数(源码注释见 graph_api.rs):

参数说明
node/nodes二选一必填;nodes是逗号分隔的多 seed 形式,去重保序、上限 64
hops1(默认)或 2,其他一律 400 + "not a graph database" 措辞
directionout/in/both(默认)
types逗号分隔的边类型白名单,缺省为全部
limit返回边数上限,钳制在 1..=1000(默认 100)
as_of双向时间切点(epoch-ms 或 RFC3339,默认 now)
include_expired是否同时返回软失效边(默认 false)
include_nodes是否水合节点摘要(默认 false)
nodes_index节点索引覆盖(默认 meta 文档的nodes_index,否则.xerj-memory-{brain})
include_evidence是否为返回边水合 evidence(默认 true)

3. Link——带引文的幂等断言

POST /_graph/{brain}/link手工断言一条边,请求体包含src、dst、type与可选weight(默认 1.0,钳制 [0,1])、valid_at(默认服务器 now)、created_at、confidence、detector(默认manual@1)、evidence({quote, source, offset})。未知字段被 400 拒绝——打错的键绝不允许悄悄丢掉一条断言细节。首次断言返回 201,相同 (src, type, dst, valid_at) 重复断言返回 200 且created:false、edge_id 相同——确定性身份意味着安全重试;写路径会惰性创建带 §2.1 mapping 与 §2.5 meta 文档的 edges 索引。

4. Unlink——退休,永不删除

DELETE /_graph/{brain}/link/{edge_id}?invalid_at=...是软失效:同一个文档在相同_id下重新索引,加上invalid_at(事实停止为真的时刻)与expired_at(系统记录这一点的时刻,总是服务器 now)——响应同时携带两个时钟,这就是双向时间对。已失效的边再次 DELETE 是幂等 no-op,报告invalidated:false与既有already_invalid_at。边上invalid_at/expired_at未设置时必须省略而非置 null——null_bitmap 本身就是"仍然有效"的信号(§2.2 类型纪律)。

5. Replay——用 as_of 回到过去

在信念区间内用as_of=2026-07-10再次询问:被退休的边在过去仍然存在;不带as_of询问当下它已缺席;并且这次排除被计数进not_shown.expired_excluded;加include_expired=true后它仍可查询、带着invalid_at。autoindex 侧的替换失效(文件重写后旧代边被软失效)同样复用这套机制:invalidate_edges_by_field按src_file或dst逐轮 search → re-index → refresh,直到收敛(detect/mod.rs)。

6. Refusals——规范化的拒绝

  • hops=3→ 400,正文含"hops is capped at 2"与"not a graph"措辞(GRAPH_HOPS_CAP_REASON);
  • 自环{"src":"a","dst":"a"}→ 400,"self-edges are not allowed (src == dst)";
  • 未知大脑 → 404,正文exists:false。

这些拒绝不是"错误处理瑕疵",而是设计规范:引擎的图展开是列式、有界、批量读(≤2 跳/次),无查询语言、无最短路径、无 PageRank、无无界遍历。

MCP 冒烟:Agent 界面上的同一套大脑

mcp-smoke.sh 验证 Agent 界面:xerj-mcpstdio 代理通过XERJ_URL指向演示大脑,用换行分隔的 JSON-RPC 完成initialize → notifications/initialized → tools/list → tools/call真实往返。断言包括:

  • 四个大脑工具被列出:xerj_brain_ego、xerj_brain_link、xerj_brain_unlink、xerj_brain_overview;
  • 每个大脑工具的描述都携带 "not a graph database" 诚实字符串——这是设计强制的,大脑工具缺席即报 FAIL、绝不打马虎眼;
  • 一次真实的tools/call xerj_brain_overview成功,返回exists:true且带 live 边计数。

这意味着同样的双向时间语义、同样not_shown记账、同样的 hops 上限,Agent 通过 MCP 获得的是与 REST 完全一致的接口契约。

大脑即安全边界:授权模型

graph_api.rs 明确了大边界:一个 brain 就是一个安全边界(issue #79)。每个端点在做事之前——包括在检查大脑是否存在之前——先对"这个 brain"做授权,因此"不是你的大脑"与"不存在的大脑"统一返回 403,响应码无法用于枚举。授权资源是大脑的 edges 索引.xerj-memory-{brain}-edges:

主体可达范围
开放模式(--insecure、指向文件夹)所有大脑——单用户、零配置
配置的 admin 密钥所有大脑
铸造时role_descriptors命名了 edges 索引的密钥该大脑、授予的权限
铸造时没有role_descriptors的密钥没有任何大脑
无/无效凭据无

ego/overview需要read;link/unlink需要write(惰性创建 edges 索引属于"写一个大脑"的一部分,不是单独的manage步骤)。大脑名校验规则与 agent-memory 命名空间一致(小写/数字开头、[a-z0-9._-]、≤200 字符、不含..),并额外拒绝-edges后缀——否则名为kb-edges的大脑会与大脑kb的边索引冲突。这层检查只是授权决策的一半:边住在普通索引里,.xerj-memory-{brain}-edges的.前缀通过IndexName::validate、可被通用 ES-compat 路由触达,因此_search、_doc/{id}、DELETE、GET /_mapping等所有旁路都在authz中关闭,本模块内的检查是同一决策的 handler 内半边,即使脱离中间件单独挂载也保持安全。

这个 Demo 明确不声称什么

README 的诚实边界(也是设计边界):

  • 无语义理解:检测器是词法级的,embedder 是 feature hashing(lexical-feature-hash),绝不标榜 "neural";
  • 无隐藏连接发现:作者链接是从你写的内容里重新索引出来的;同目录/顺序链接是结构先验且被如实标注为 STRUCTURAL;
  • 无图数据库功能:无路径查询、无 PageRank、无无界遍历——每次调用展开上限为 2 跳,这是设计使然;
  • 无性能数字:这套装置测的是正确性,不是速度;凡未在此测量的一切,都是"未测量"。

这套诚实边界在响应里是可见的:overview报告embedder标识、not_shown逐项计数被隐藏的内容(剪掉的边/前沿、双向时间排除的边、跳过的 segment、悬空节点引用、聚合尾巴),MCP 工具描述里强制携带 "not a graph database" 字符串。演示运行摘要同样如实呈现edges_unresolved等计数器——对悬空链接的记录,本身就是对语料的一个事实。

与 CI 的关系

README 特别说明:这三个脚本也正是 CI 通过 .github/scripts/usecase-smoke.sh 运行的——每个 PR 上的门禁就是这套精确的装置,而不是它的另一份拷贝。因此你本地重跑boot-and-brain.sh、transcript.sh、mcp-smoke.sh的 PASS/FAIL,与项目 CI 对每个提交的判定完全同构。

快速上手清单

  1. 构建二进制:(cd engine && cargo build --release -p xerj-server -p xerj-mcp)
  2. 进入 demo/usecases/second-brain,运行./boot-and-brain.sh(首次会自动生成 39 文件语料库并引导服务器,端口 9331-9333)
  3. 运行./transcript.sh查看 6 组 API 证明的 PASS/FAIL 汇总
  4. 运行./mcp-smoke.sh验证四个 MCP 大脑工具与真实 tools/call 往返
  5. 浏览器打开控制台深链(http://localhost:9331/_xerj-console/#/second-brain?brain=vault)查看 AUTHORED vs STRUCTURAL 图谱
  6. 收尾:kill $(cat /tmp/xerj-second-brain-demo/data/server.pid)(或你覆盖的根目录)

整个过程零 mock、可重跑、幂等收敛——重新运行gen-corpus.sh与xerj brain会产生相同的 edge_id,唯一随时间漂移的是created_at。这正是"第二大脑"应该有的性质:你对笔记写下结构,XERJ 让它可查询、可回溯、有据可查。

  • 人工智能
  • 搜索引擎
  • 本地部署
  • 后端
  • MCP 服务

【免费下载链接】xerj

XERJ is the new way for AI to search data. Its autoindex capability activates agents to know your data without the token waste of grep and sed. One command indexes code, docs, logs and PDFs for search, RAG, security audits and agent memory, using 40x fewer tokens than grep. Elasticsearch compatible, so existing clients just work.

项目地址:https://gitcode.com/GitHub_Trending/xe/xerj
点击查看免费下载

相关推荐

上一篇:WebdriverIO 日志工具 @wdio/logger 完全指南:安装、自定义日志级别与敏感信息脱敏
下一篇:Building [What You're Building]

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进