
RAG 很好用,直到它不再好用。
我们的助手只能检索与查询匹配的文本片段。如果答案分散在多个页面里,或者用户需要的精确语法没有落在 top-K 结果中,它就卡住了。我们希望它能像你逛代码库一样逛文档。
智能体正在把文件系统当作主要接口,因为对智能体来说,grep、cat、ls、find 就够了。只要把每个文档页面看作一个文件,把每个章节看作一个目录,它就能精确查字符串、读整页内容、自己在结构里穿行。我们只差一个能镜像线上文档站点的文件系统。
容器瓶颈
最直接的做法就是给智能体一个真实文件系统。大多数 harness 会通过启动隔离沙箱并克隆仓库来解决。我们已经在异步后台智能体上这么做了,那种场景里延迟不是问题;但对前端助手而言,用户正盯着加载转圈,这套方法就崩了。我们的 p90 会话创建时间(包括 GitHub clone 和其他初始化)是 约 46 秒。
除去延迟,为了读静态文档而专门跑微型虚拟机,也带来了不小的基础设施账单。
按每月 85 万次对话来算,即便是最小配置(1 vCPU、2 GiB RAM、会话生命周期 5 分钟),按 Daytona 的按秒沙箱计费($0.0504/h per vCPU,$0.0162/h per GiB RAM)换算下来,一年也要超过 $70,000。会话时间更长,这个数字会翻倍。(这只是最朴素的估算;真正的生产方案可能会有 warm pools 和 container sharing,但结论依然成立)
文件系统这条路必须又快又便宜,所以得从文件系统本身重新想。
假装有个 Shell
智能体不需要一个真实的文件系统,它只需要一个幻象。我们的文档早就为了搜索而做过索引、切分,并存进了 Chroma 数据库,所以我们做了 ChromaFs:一个虚拟文件系统,用来拦截 UNIX 命令,并把它们翻译成对同一套数据库的查询。会话创建从约 46 秒降到 约 100 毫秒;而且 ChromaFs 复用了我们本来就在付费的基础设施,因此每次对话的边际计算成本为零。
export class ChromaFs implements IFileSystem {
private files = new Set<string>();
private dirs = new Map<string, string[]>();
async readFile(path: string): Promise<string> {
this.assertInit();
const normalized = normalizePath(path);
// Serve from cache or fetch from Chroma
const slug = normalized.replace(/\\.mdx$/, '').slice(1);
// Pages are chunked in Chroma. Reassemble them on the fly:
const results = await this.collection.get<ChunkMetadata>({
where: { page: slug },
include: [IncludeEnum.documents, IncludeEnum.metadatas],
});
const chunks = results.ids
.map((id, i) => ({
document: results.documents[i] ?? '',
chunkIndex: parseInt(String(results.metadatas[i]?.chunk_index ?? 0), 10),
}))
.sort((a, b) => a.chunkIndex - b.chunkIndex);
return chunks.map((c) => c.document).join('');
}
// Enforce completely stateless, read-only interaction
async writeFile(): Promise<void> { throw erofs(); }
async appendFile(): Promise<void> { throw erofs(); }
async mkdir(): Promise<void> { throw erofs(); }
async rm(): Promise<void> { throw erofs(); }
}
ChromaFs 基于 Vercel Labs 的 just-bash(致敬 Malte!)构建。just-bash 是用 TypeScript 重写的 bash,支持 grep、cat、ls、find、cd 等等。just-bash 提供可插拔的 IFileSystem 接口,因此解析、管道、参数标志这些逻辑都由它处理,而 ChromaFs 负责把每一次底层文件系统调用翻译成 Chroma 查询。
https://arxiv.org/abs/2601.11672
工作原理
引导构建目录树
在智能体执行第一条命令之前,ChromaFs 必须先知道有哪些文件存在。我们把完整的文件树以 gzip 压缩的 JSON 文档(path_tree)形式存放在 Chroma collection 里:

初始化时,服务端会拉取并解压这份文档,生成两份内存结构:一个保存文件路径的 Set<string>,以及一个把目录映射到子项列表的 Map
目录树一旦建好,ls、cd、find 都能在本地内存里完成解析,不需要任何网络调用。目录树会被缓存,同一站点的后续会话甚至可以完全跳过从 Chroma 拉取。
访问控制
注意 path tree 里的 isPublic 和 groups 字段。在构建文件树之前,ChromaFs 会根据当前用户的权限裁剪文件树,并把同样的过滤条件应用到后续所有 Chroma 查询中。
如果在真实沙箱里做到这种按用户粒度的访问控制,要么得管理 Linux 用户组、chmod 权限,要么按客户等级维护隔离的容器镜像。到了 ChromaFs,这只是 buildFileTree 运行前几行过滤。
从分块重组页面
Chroma 里的页面为了 embedding 会被拆成 chunks,所以当智能体运行 cat /auth/oauth.mdx 时,ChromaFs 会抓取所有 page slug 匹配的 chunks,按 chunk_index 排序,再拼接成完整页面。结果会被缓存,因此在 grep 工作流里重复读取时不会二次命中数据库。
并不是每个文件都必须存在于 Chroma 里。我们注册了按需解析的 lazy file pointers,用于指向存放在客户 S3 buckets 里的大型 OpenAPI specs。智能体在 /api-specs/ 里能看到 v2.json,但内容只有在它运行 cat 时才会拉取。
所有写操作都会抛出 EROFS(Read-Only File System)错误。智能体可以自由探索,但永远无法修改文档,这让系统保持无状态,不需要做会话清理,也没有一个智能体污染另一个智能体视图的风险。
优化 Grep
cat 和 ls 很容易虚拟化,但如果 grep -r 真的按最朴素的方式在网络上逐个扫描文件,那会慢到不可用。我们拦截 just-bash 的 grep,用 yargs-parser 解析 flags,然后把它们翻译成 Chroma 查询(固定字符串用 $contains,模式匹配用 $regex)。
Chroma 充当 粗过滤器,先找出哪些文件可能包含命中,然后我们把这些匹配 chunks 批量 bulkPrefetch 到 Redis 缓存里。接着,我们改写 grep 命令,让它只针对命中的文件,再把它交回 just-bash 做 精细过滤 in-memory execution,这样一来,大型递归查询也能在毫秒级完成。
const chromaFilter = toChromaFilter(
scannedArgs.patterns,
scannedArgs.fixedStrings,
scannedArgs.ignoreCase
);
// 1. Coarse Filter: Ask Chroma for slugs matching the string/regex
const matchedSlugs = await chromaFs.findMatchingFiles(chromaFilter, slugsUnderDirs);
if (matchedSlugs.length === 0) return { stdout: ‘’, exitCode: 1 };
// 2. Prefetch: Pull the chunked files into local cache concurrently
await chromaFs.bulkPrefetch(matchedSlugs);
// 3. Fine Filter: Narrow the arguments to ONLY the resolved hits
const matchedPaths = matchedSlugs.map((s) => ‘/’ + s + ‘.mdx’);
const narrowedArgs = [...args, ...matchedPaths]; // e.g. ["-i", "OAuth", "/docs/auth.mdx"]
// 4. Exec: Let the in-memory RegExp engine format the final output
return execBuiltin(narrowedArgs, ctx);
结论
ChromaFs 为数十万用户的文档助手提供支持,每天覆盖 30,000+ 次对话。通过用现有 Chroma 数据库上的虚拟文件系统替代沙箱,我们实现了即时会话创建、零边际计算成本,并且在不新增任何基础设施的情况下内建了 RBAC。
在任意 Mintlify 文档站点上都能试用,或者直接访问 mintlify.com/docs。
[在这里阅读完整文章:https://www.mintlify.com/blog/how-we-built-a-virtual-filesystem-for-our-assistant]