RAG Replacement Virtual Filesystem AI Assistant: A Step-by-Step Build Guide

Key Takeaways
- Virtual filesystem approach achieved 84% accuracy vs 62% with traditional RAG
- Document hierarchy preservation is critical for multilingual APAC documentation
- Session initialization dropped from 38 seconds to under 200 milliseconds
- Token costs increase ~15% but accuracy gains justify the investment
- Agentic file navigation outperforms chunk retrieval for structured documents
Quick Answer: A virtual filesystem AI assistant replaces traditional RAG by letting an LLM navigate documentation as a directory tree using ls, read, and search tools. This preserves document hierarchy, improves multilingual accuracy, and reduces session initialization from tens of seconds to milliseconds.
Most teams I talk to across Hong Kong and Singapore treat RAG as the default architecture for any AI documentation assistant. They spend weeks tuning chunk sizes, fiddling with embedding models, and still end up with retrieval results that miss the mark on complex multi-document queries. Here's the contrarian take: RAG might be the wrong abstraction entirely. The RAG replacement virtual filesystem AI assistant approach — pioneered publicly by Mintlify and now gaining traction in APAC enterprise circles — treats your documentation corpus as a navigable filesystem rather than a bag of vector embeddings. And for documentation-heavy SaaS companies and enterprise implementations across Asia-Pacific, the performance difference is measurable.
Related reading: Customer Lifetime Value Modelling: APAC Retail Benchmarks That Should Shape Your 2025 Spend
Related reading: Post-Purchase Automation Playbook for APAC Retail Brands: 7 Steps
Related reading: Digital Operations Maturity Model for APAC Retailers: A 5-Stage Framework
Related reading: Claude Code AI Vulnerability Detection on Linux: What APAC Teams Need to Know
Related reading: MR DIY Malaysia Adobe Commerce Shopify Migration: What Mid-Market Retailers Get Wrong
At Branch8, we ran into this exact wall when building an internal knowledge assistant for a Taiwanese fintech client with 4,000+ pages of regulatory documentation across three languages. Traditional RAG with chunking produced an answer accuracy rate hovering around 62% on our internal benchmarks. After switching to a virtual filesystem architecture using ChromaFs as the storage layer, that number climbed to 84% — and session initialization dropped from 38 seconds to under 200 milliseconds. That's the kind of operational metric that matters when your support team handles 800+ queries per day.
This tutorial walks you through building a RAG replacement virtual filesystem AI assistant from scratch, oriented toward the realities of APAC tech teams managing multilingual, high-volume documentation.
Prerequisites
Before you start, make sure you have the following ready:
Environment Requirements
- Python 3.10+ installed (3.11 recommended for performance improvements)
- Node.js 18+ if you plan to integrate with frontend tooling
- Docker running locally for ChromaDB container
- An OpenAI API key (GPT-4o or GPT-4o-mini; Claude 3.5 Sonnet also works)
- At least 8GB RAM available — ChromaDB can be memory-hungry with large collections
Knowledge Requirements
- Basic familiarity with Python async patterns
- Understanding of how traditional RAG pipelines work (so you know what you're replacing)
- Comfort with command-line tooling
Install Core Dependencies
1pip install chromadb openai pydantic fastapi uvicorn tiktoken2pip install chromafs # The virtual filesystem layer over ChromaDB
If chromafs isn't available via pip at your time of reading (the project is evolving rapidly — check the ChromaFs GitHub repo for the latest install instructions), you can clone and install from source:
1git clone https://github.com/nichochar/chromafs.git2cd chromafs3pip install -e .
Step 1: Understand Why Traditional RAG Fails for APAC Documentation
Before writing code, it's worth understanding the architectural problem. Traditional RAG works in three stages: chunk your documents, embed the chunks into vectors, then retrieve the top-k most similar chunks at query time. This works reasonably well for English-language corpora with uniform structure.
But APAC enterprise documentation breaks this model in three specific ways:
- Multilingual content — Embedding models like
text-embedding-3-smallperform unevenly across Traditional Chinese, Simplified Chinese, Japanese, and Bahasa. According to the MTEB Leaderboard maintained by Hugging Face, most embedding models show a 12-18% accuracy drop on CJK languages compared to English. - Hierarchical regulatory documents — Compliance documentation in financial services (common across HK, SG, and AU) has deep nesting. Chunking flattens this hierarchy, losing critical parent-child relationships between sections.
- Cross-reference density — Enterprise docs in APAC markets reference other documents constantly. A chunk from Document A that says "as specified in Section 4.2 of the Capital Requirements Directive" is useless without that context.
The virtual filesystem approach solves these by preserving the document tree structure. Instead of chunks, your AI assistant navigates directories and reads files — just like a developer would.
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
Step 2: Launch ChromaDB and Initialize the Virtual Filesystem
Start ChromaDB as a Docker container:
1docker run -d --name chromadb \2 -p 8000:8000 \3 -v chroma_data:/chroma/chroma \4 chromadb/chroma:0.5.23
Now create your Python project structure:
1mkdir vfs-assistant && cd vfs-assistant2mkdir -p docs/en docs/zh-hant docs/zh-hans3touch main.py filesystem.py ingest.py
In filesystem.py, set up the virtual filesystem connection:
1import chromadb2from chromafs import ChromaFs34def get_filesystem() -> ChromaFs:5 """Initialize ChromaFs with a persistent ChromaDB backend."""6 client = chromadb.HttpClient(host="localhost", port=8000)7 fs = ChromaFs(8 client=client,9 collection_name="docs_vfs",10 embedding_function=chromadb.utils.embedding_functions11 .OpenAIEmbeddingFunction(12 api_key="YOUR_OPENAI_KEY",13 model_name="text-embedding-3-small"14 )15 )16 return fs
Expected output when you test the connection:
1# Quick test2fs = get_filesystem()3print(fs.ls("/"))4# Output: []
An empty list confirms your virtual filesystem is connected and ready.
Step 3: Ingest Documentation While Preserving Hierarchy
This is where the architecture diverges fundamentally from RAG. Instead of chunking documents into 500-token fragments, you're writing them as files into a virtual directory tree that mirrors their original structure.
Create ingest.py:
1import os2from pathlib import Path3from filesystem import get_filesystem45def ingest_docs(source_dir: str, vfs_root: str = "/"):6 """Ingest a directory of markdown/text docs into the virtual filesystem."""7 fs = get_filesystem()8 source_path = Path(source_dir)910 files_ingested = 01112 for file_path in source_path.rglob("*.md"):13 # Preserve relative path structure14 relative = file_path.relative_to(source_path)15 vfs_path = f"{vfs_root}{str(relative)}"1617 # Create parent directories in VFS18 parent_dir = str(Path(vfs_path).parent)19 fs.mkdir(parent_dir, exist_ok=True)2021 # Read and write file content22 content = file_path.read_text(encoding="utf-8")2324 # For large files, split by heading sections (not arbitrary chunks)25 sections = split_by_headings(content)2627 if len(sections) <= 1:28 fs.write(vfs_path, content)29 files_ingested += 130 else:31 # Create a subdirectory for multi-section documents32 doc_dir = vfs_path.replace(".md", "/")33 fs.mkdir(doc_dir, exist_ok=True)34 for i, (heading, body) in enumerate(sections):35 section_path = f"{doc_dir}{i:02d}_{slugify(heading)}.md"36 fs.write(section_path, f"# {heading}\n\n{body}")37 files_ingested += 13839 return files_ingested4041def split_by_headings(content: str) -> list[tuple[str, str]]:42 """Split markdown content by H2 headings, preserving structure."""43 import re44 parts = re.split(r"^## (.+)$", content, flags=re.MULTILINE)4546 if len(parts) <= 1:47 return [("Overview", content)]4849 sections = []50 # First part is content before any H251 if parts[0].strip():52 sections.append(("Overview", parts[0].strip()))5354 # Pair headings with their content55 for i in range(1, len(parts), 2):56 heading = parts[i]57 body = parts[i + 1].strip() if i + 1 < len(parts) else ""58 sections.append((heading, body))5960 return sections6162def slugify(text: str) -> str:63 import re64 return re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-")6566if __name__ == "__main__":67 count = ingest_docs("./docs/en", vfs_root="/en/")68 print(f"Ingested {count} files into virtual filesystem")
Run the ingestion:
1python ingest.py2# Output: Ingested 47 files into virtual filesystem
The critical difference: your documents retain their tree structure. A query about "capital requirements for Hong Kong licensed corporations" can navigate to /en/regulations/hk/capital-requirements/ rather than hoping the right chunk surfaces from a vector similarity search.
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
Step 4: Build the Filesystem-Aware AI Assistant
Now for the core assistant. Instead of giving the LLM pre-retrieved chunks, you give it filesystem tools — ls, read, search — and let it navigate the documentation tree autonomously.
Create main.py:
1import json2from openai import OpenAI3from filesystem import get_filesystem45client = OpenAI(api_key="YOUR_OPENAI_KEY")6fs = get_filesystem()78TOOLS = [9 {10 "type": "function",11 "function": {12 "name": "ls",13 "description": "List files and directories at a given path in the documentation filesystem.",14 "parameters": {15 "type": "object",16 "properties": {17 "path": {18 "type": "string",19 "description": "The directory path to list, e.g. /en/api/"20 }21 },22 "required": ["path"]23 }24 }25 },26 {27 "type": "function",28 "function": {29 "name": "read",30 "description": "Read the full content of a file in the documentation filesystem.",31 "parameters": {32 "type": "object",33 "properties": {34 "path": {35 "type": "string",36 "description": "The file path to read, e.g. /en/api/authentication.md"37 }38 },39 "required": ["path"]40 }41 }42 },43 {44 "type": "function",45 "function": {46 "name": "search",47 "description": "Semantic search across all documentation files. Returns top matches with paths.",48 "parameters": {49 "type": "object",50 "properties": {51 "query": {52 "type": "string",53 "description": "Natural language search query"54 },55 "n_results": {56 "type": "integer",57 "description": "Number of results to return (default 5)"58 }59 },60 "required": ["query"]61 }62 }63 }64]6566SYSTEM_PROMPT = """You are a documentation assistant with access to a virtual filesystem67containing all product and regulatory documentation.6869Before answering any question:701. Use `search` to find relevant file paths712. Use `ls` to explore the directory structure around those paths723. Use `read` to get the full content of relevant files734. Synthesize your answer from the actual file contents7475Always cite which files you referenced. If information spans multiple files,76read all of them before responding. Support Traditional Chinese, Simplified Chinese,77and English queries."""7879def execute_tool(name: str, args: dict) -> str:80 """Execute a filesystem tool and return the result."""81 try:82 if name == "ls":83 result = fs.ls(args["path"])84 return json.dumps(result)85 elif name == "read":86 content = fs.read(args["path"])87 return content88 elif name == "search":89 results = fs.search(90 args["query"],91 n_results=args.get("n_results", 5)92 )93 return json.dumps([94 {"path": r["path"], "preview": r["content"][:200]}95 for r in results96 ])97 except Exception as e:98 return f"Error: {str(e)}"99100def chat(user_message: str, history: list = None) -> str:101 """Run a multi-turn conversation with filesystem tool use."""102 messages = [{"role": "system", "content": SYSTEM_PROMPT}]103 if history:104 messages.extend(history)105 messages.append({"role": "user", "content": user_message})106107 # Allow up to 10 tool-use rounds108 for _ in range(10):109 response = client.chat.completions.create(110 model="gpt-4o",111 messages=messages,112 tools=TOOLS,113 tool_choice="auto"114 )115116 msg = response.choices[0].message117 messages.append(msg)118119 if not msg.tool_calls:120 return msg.content121122 # Execute each tool call123 for tool_call in msg.tool_calls:124 result = execute_tool(125 tool_call.function.name,126 json.loads(tool_call.function.arguments)127 )128 messages.append({129 "role": "tool",130 "tool_call_id": tool_call.id,131 "content": result132 })133134 return "Reached maximum tool-use iterations."135136if __name__ == "__main__":137 answer = chat("What are the API rate limits for the enterprise tier?")138 print(answer)
Test it:
1python main.py2# Output: Based on /en/api/03_rate-limits.md, the enterprise tier allows...
The assistant doesn't just retrieve chunks — it navigates, reads, cross-references, and then synthesizes. That's the fundamental shift.
Step 5: Add Multilingual Path Routing for APAC Markets
For APAC deployments, you need language-aware routing. Our Branch8 project for the Taiwanese fintech client required the assistant to handle queries in Traditional Chinese and route to the correct language documentation subtree.
Add a language detection layer:
1from openai import OpenAI23def detect_language(query: str) -> str:4 """Fast language detection using the LLM itself."""5 # For production, use a dedicated library like langdetect6 # This is a simple approach for prototyping7 import re89 # Check for CJK characters10 cjk_chars = len(re.findall(r'[\u4e00-\u9fff]', query))11 if cjk_chars > len(query) * 0.3:12 # Distinguish Traditional vs Simplified13 # Traditional Chinese has more complex characters14 traditional_indicators = re.findall(15 r'[\u9577\u8acb\u8a2a\u8b93\u6a23\u689d\u7d93\u904e]',16 query17 )18 if traditional_indicators:19 return "zh-hant"20 return "zh-hans"2122 return "en"2324# Modify the system prompt to include language routing25SYSTEM_PROMPT_MULTILINGUAL = """You are a documentation assistant.26Documentation is organized by language:27- /en/ — English documentation28- /zh-hant/ — Traditional Chinese (繁體中文)29- /zh-hans/ — Simplified Chinese (简体中文)3031When a user asks in a specific language, search the corresponding32directory first, then fall back to /en/ if the content isn't available33in their language.3435Always respond in the same language as the user's query."""
This matters operationally. According to CSA Research's "Can't Read, Won't Buy" study, 76% of online consumers prefer to buy products with information in their native language. For enterprise documentation assistants, the same principle applies to internal adoption rates.
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
Step 6: Expose the Assistant via a FastAPI Endpoint
Wrap everything in a production-ready API:
1# api.py2from fastapi import FastAPI, HTTPException3from pydantic import BaseModel4from main import chat5import time67app = FastAPI(title="VFS Documentation Assistant")89class QueryRequest(BaseModel):10 message: str11 session_id: str | None = None1213class QueryResponse(BaseModel):14 answer: str15 latency_ms: float16 session_id: str1718# Simple in-memory session store (use Redis in production)19sessions: dict[str, list] = {}2021@app.post("/query", response_model=QueryResponse)22async def query_assistant(request: QueryRequest):23 session_id = request.session_id or str(time.time())24 history = sessions.get(session_id, [])2526 start = time.perf_counter()27 answer = chat(request.message, history=history)28 latency = (time.perf_counter() - start) * 10002930 # Update session history31 history.append({"role": "user", "content": request.message})32 history.append({"role": "assistant", "content": answer})33 sessions[session_id] = history[-20:] # Keep last 20 messages3435 return QueryResponse(36 answer=answer,37 latency_ms=round(latency, 2),38 session_id=session_id39 )4041@app.get("/health")42async def health():43 return {"status": "ok"}
Run it:
1uvicorn api:app --host 0.0.0.0 --port 8080 --reload
Test with curl:
1curl -X POST http://localhost:8080/query \2 -H "Content-Type: application/json" \3 -d '{"message": "How do I configure SSO for the enterprise plan?"}'45# Output:6# {7# "answer": "Based on /en/admin/sso-configuration.md, SSO setup requires...",8# "latency_ms": 1243.56,9# "session_id": "1719484823.456"10# }
Performance Benchmarks: Virtual Filesystem vs Traditional RAG
We tracked performance across our Branch8 deployment for the Taiwanese client over a four-week pilot with 3,200 real user queries:
- Answer accuracy (human-evaluated on 200 random samples): VFS assistant scored 84.2% vs 62.1% for the previous RAG setup
- Average latency: 1.3 seconds (VFS) vs 2.8 seconds (RAG with re-ranking)
- Session initialization: 180ms (VFS) vs 38 seconds (RAG with embedding computation). Mintlify reported a similar improvement, dropping from 46 seconds to 100ms according to their engineering blog
- Token usage per query: 15% higher with VFS (because the model reads full files), but the accuracy improvement more than justified the cost increase
- Multilingual query accuracy: 79% for Traditional Chinese queries (VFS) vs 51% (RAG) — this was the biggest win for the APAC use case
The tradeoff is real: you spend more tokens per query because the LLM reads full documents rather than short chunks. For our 800 queries/day volume, that translated to roughly USD $45/day more in API costs. Against the support team productivity gains — 2.1 fewer escalations per agent per day — the ROI was obvious within the first week.
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
Common Pitfalls and How to Avoid Them
Don't flatten your directory structure
The entire value of this approach depends on meaningful hierarchy. If you dump all files into /docs/, you've built a worse version of RAG. Spend time designing your directory tree to mirror how your team actually thinks about the documentation.
Watch your context window budget
GPT-4o gives you 128K tokens of context. A virtual filesystem assistant that reads 15 files per query can burn through that quickly. Implement a token budget tracker:
1import tiktoken23enc = tiktoken.encoding_for_model("gpt-4o")45def count_tokens(text: str) -> int:6 return len(enc.encode(text))78# In your execute_tool function, track cumulative tokens9total_tokens_read = 010MAX_BUDGET = 80000 # Leave room for system prompt and output1112def execute_tool_with_budget(name: str, args: dict) -> str:13 global total_tokens_read14 result = execute_tool(name, args)1516 if name == "read":17 tokens = count_tokens(result)18 total_tokens_read += tokens19 if total_tokens_read > MAX_BUDGET:20 return "Token budget exceeded. Summarize what you know and respond."2122 return result
Handle stale documentation gracefully
Set up a file watcher or CI/CD hook that re-ingests documentation when it changes. For teams using Mintlify CLI or similar AI documentation tools, you can trigger re-ingestion on git push:
1# .github/workflows/reingest.yml2on:3 push:4 paths: ["docs/**"]5jobs:6 reingest:7 runs-on: ubuntu-latest8 steps:9 - uses: actions/checkout@v410 - run: pip install -r requirements.txt11 - run: python ingest.py
What to Do Next
You now have a working RAG replacement virtual filesystem AI assistant that preserves document hierarchy, supports multilingual APAC queries, and gives your LLM the ability to navigate rather than just retrieve.
Here's where to go from here:
- Add AgentFS or a similar agent-native filesystem layer if you need write-back capabilities (letting the assistant update documentation)
- Implement caching with Redis for repeated queries — in our deployment, roughly 35% of queries were near-duplicates that could be served from cache
- Evaluate open-source alternatives: if you're looking for a Mintlify open-source alternative for the documentation layer, tools like Docusaurus paired with this VFS approach give you full control
- Scale to multi-tenant: if you're serving multiple clients, partition your virtual filesystem by tenant (
/tenant-a/en/,/tenant-b/zh-hant/) with access controls
The direction this space is heading is clear: agentic file exploration is replacing retrieval-augmented generation for any use case where document structure carries meaning. As LLM context windows continue expanding — Gemini 1.5 Pro already offers 2M tokens according to Google's developer documentation — the economics shift even further in favor of reading whole files over chunking. For APAC enterprise teams managing complex, multilingual, regulation-heavy documentation, this isn't a nice-to-have architecture. It's becoming the competitive baseline.
If your team is wrestling with documentation assistant accuracy across multiple Asian languages and regulatory frameworks, reach out to Branch8. We've shipped this architecture in production for fintech and SaaS clients across Hong Kong, Singapore, and Taiwan — and we can help you skip the months of trial-and-error we've already been through.
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
Sources
- Mintlify Engineering Blog — How we built a virtual filesystem for our Assistant: https://www.mintlify.com/blog/how-we-built-a-virtual-filesystem-for-our-assistant
- ChromaDB Documentation — Getting Started: https://docs.trychroma.com/getting-started
- OpenAI Function Calling Documentation: https://platform.openai.com/docs/guides/function-calling
- MTEB Leaderboard — Multilingual Embedding Benchmarks: https://huggingface.co/spaces/mteb/leaderboard
- CSA Research — Can't Read, Won't Buy (2020): https://csa-research.com/Featured-Content/For-Global-Businesses/Cant-Read-Wont-Buy
- Google Gemini 1.5 Pro Context Window: https://ai.google.dev/gemini-api/docs/models/gemini
- Subramanya.ai — The Filesystem Is the Database: https://subramanya.ai/2025/05/29/filesystem-is-the-database/
FAQ
Not entirely, but it shifted the economics. The Assistants API abstracts away much of the manual RAG plumbing, but a virtual filesystem approach gives you finer control over document structure and multilingual routing. For enterprise APAC deployments with complex compliance documentation, manual control still matters.
About the Author
Matt Li
Co-Founder & CEO, Branch8 & Second Talent
Matt Li is Co-Founder and CEO of Branch8, a Y Combinator-backed (S15) Adobe Solution Partner and e-commerce consultancy headquartered in Hong Kong, and Co-Founder of Second Talent, a global tech hiring platform ranked #1 in Global Hiring on G2. With 12 years of experience in e-commerce strategy, platform implementation, and digital operations, he has led delivery of Adobe Commerce Cloud projects for enterprise clients including Chow Sang Sang, HomePlus (HKBN), Maxim's, Hong Kong International Airport, Hotai/Toyota, and Evisu. Prior to founding Branch8, Matt served as Vice President of Mid-Market Enterprises at HSBC. He serves as Vice Chairman of the Hong Kong E-Commerce Business Association (HKEBA). A self-taught software engineer, Matt graduated from the University of Toronto with a Bachelor of Commerce in Finance and Economics.