TL;DR
File-Native Context Engineering is a principle for organizing an LLM's context: instead of cramming everything into one prompt, you spread knowledge across files (data schemas, documentation, rules), and the model finds what it needs through file operations. The study tested this on SQL generation: 9,649 experiments, 11 models, 4 formats (YAML, Markdown, JSON, TOON), schemas from 10 to 10,000 tables. Two approaches were compared: File Agent (the model reads files on demand via grep/read) vs. Prompt Baseline (the entire database schema in the prompt up front).
The main finding torpedoes one-size-fits-all recipes: the file-based approach works differently for different models. Frontier models (Claude, GPT, Gemini) got +2.7% more accurate when they searched files instead of reading from the prompt. Open-source models showed mixed results: some (DeepSeek, Llama Scout) performed about the same, others (Qwen, Llama Maverick) fell flat with files and lost up to 22% accuracy. The reason: differing quality of tool-use training. Format barely affects accuracy (YAML 75.4%, Markdown 74.9%, JSON 72.3%), but it does affect efficiency: YAML used 28-60% fewer tokens than the rest.
The method tackles scale through domain partitioning: instead of one file with 10,000 tables, several files of ~250 tables each (sales, customers, inventory). The model first figures out which domain holds the table it needs, then reads only that file. Navigation accuracy stayed high even at 10,000 tables. An unexpected effect: the compact TOON format (25% smaller file size) burned 7x more tokens on large schemas — a "grep tax". Models don't know TOON syntax, so they make lots of failed search attempts, and every attempt adds tokens to the context.
Want this for your own case? Apply this study to your task: AI adapts its method to what you are working on and writes the prompt.
Method Blueprint
File-Native Architecture (as applied to ChatGPT/Claude Projects):
SETUP:
1. Structure your knowledge → files by topic/domain
- schema.yaml — data structure
- navigator.md — overview and descriptions
- rules.md — business rules
2. Pick a format → YAML for compactness, MD for readability
3. Partition if >100 objects → files by domain (~250 objects per file)
USING IT IN A CHAT:
1. Upload the files to a project/chat
2. Give a task → the model finds the right files on its own
3. The model reads only what's relevant → less "lost in the middle"
For frontier models (GPT-4+, Claude Opus/Sonnet, Gemini Pro) it works out of the box. For open-source, test on small data first.
Example
Task: You run an online store with 300+ products across 15 categories. You need to answer questions about products, stock and specs quickly — but stuffing the whole catalog into the prompt is clunky.
Prepare the files:
catalog_navigator.md:
# Catalog navigator
## Structure
- electronics.yaml — electronics (smartphones, laptops, headphones)
- clothing.yaml — apparel (T-shirts, jeans, shoes)
- home.yaml — home goods (cookware, textiles, decor)
...
## How to search
If the question is about specs → look in the matching .yaml
If it's about stock → the stock_quantity field
If it's about prices → the price_usd field
electronics.yaml:
category: Electronics
tables:
- name: smartphones
description: Smartphones from all brands
columns:
- name: model
type: string
description: Phone model
- name: price_usd
type: integer
description: Price in US dollars
- name: stock_quantity
type: integer
description: Units in stock
- name: ram_gb
type: integer
description: RAM in GB
- name: laptops
description: Laptops
columns:
- name: model
- name: price_usd
- name: processor
- name: screen_inches
Upload the files to a project → ask your questions:
"Which smartphones with 8+ GB of RAM are in stock for under $500?"
Result: The model reads navigator.md → realizes it needs electronics.yaml → opens it → finds the smartphones table → reads its structure → returns results filtered by ram_gb ≥ 8, price_usd ≤ 500, stock_quantity > 0. Instead of processing all 300 products in the prompt, it read only the relevant chunk.
Why It Works
The LLM's weakness: "Lost in the middle" — models make worse use of information from the middle of a long context. Cram a 10,000-table schema into the prompt and the model may miss the table it needs if it lands mid-list. Plus, a big context = lots of tokens = expensive and slow.
The LLM's strength: Frontier models are excellent at using tools (file read, search) — it's part of their training. They know how to break a task into steps: "find which file it's in → read the file → find what's needed in it → use it".
How the method exploits this: The file-native approach turns one big context into a navigable knowledge base. The model reads only what it needs right now. Navigator.md provides a map → the model orients itself → reads a specific file → gets the exact slice of data. That solves "lost in the middle" and saves tokens. But it only works if the model is trained for tool use — which is why open-source models stumble.
Control levers:
- File granularity: one file for 10 domains vs. 10 files, one per domain → more files = more precise search, but more navigation steps
- Format: YAML eats fewer tokens during retrieval (compact key-value), Markdown is friendlier for humans
- Navigator.md: the more detailed the domain descriptions → the more accurately the model picks the file on the first try
- Partition size: 50 vs. 500 objects per file → smaller partitions = less noise in each file, but more navigation steps
Prompt Template
File structure for file-native context:
1. Navigator file (navigator.md or _index.yaml):
# Navigator: {system name}
## Knowledge structure
- {file_1}.yaml — {what it contains}: {short description}
- {file_2}.yaml — {what it contains}: {short description}
...
## How to search
If the question is about {task_type_1} → look in {file_1}
If the question is about {task_type_2} → look in {file_2}
## Formats
All .yaml files use the structure:
{description of the data format}
2. Data files (YAML for compactness):
domain: {domain name}
description: {what it describes}
entities:
- name: {entity name}
description: {what it is}
fields:
- name: {field}
type: {type}
description: {what it means}
- name: {field_2}
type: {type}
description: {what it means}
- name: {entity_name_2}
fields:
...
How to use it: 1. Create navigator.md — a map of all files 2. Split the data by domain/topic into separate .yaml/.md files 3. Upload all files to a ChatGPT Project / Claude Project / via file upload 4. Write your tasks — the model will find the right file through the navigator
Partitioning for large volumes (>500 objects):
- Split by domain: ~250 objects per file
- Give files descriptive names: sales_schema.yaml, customers_schema.yaml
- In navigator.md, define clear domain boundaries
🚀 Quick start — paste into a chat with your files uploaded:
I have {description of the data/system}. Help me structure it in a file-native format:
1. Split it by domain (ideally ~250 objects per file)
2. Create a navigator.md with a map of the files
3. For each domain, create a .yaml file using the template above
My system: {description}
The model will ask about your data structure, relationships and how to split things up — because it needs to understand the domain boundaries to partition effectively. It'll take the template and build a structure for your data.
Limitations
⚠️ Model-specific: Open-source models (especially Qwen, Llama Maverick) fall flat with the file-based approach and lose up to 22% accuracy. Always test on small data before scaling up. Frontier models (Claude 3.5+, GPT-4+, Gemini 1.5+) work reliably.
⚠️ Application domain: The study was tested on SQL generation — a structured, unambiguous task. For creative work where context = inspiration (write in author X's style), the effect may differ. It works best for reference information: schemas, documentation, rules, catalogs.
⚠️ Grep tax for custom formats: If you use your own format (not YAML/JSON/Markdown), the model will spend many times more tokens searching because it doesn't know the patterns. At 10,000 tables, TOON (a custom format) burned 7x more tokens than YAML.
⚠️ Platform barrier: Works great in ChatGPT Projects, Claude Projects and APIs with file search. Doesn't work in a plain chat without file upload (you'll have to copy-paste).
How They Tested It
The team took TPC-DS — a standard database schema benchmark (24 tables, retail domain: customers, sales, inventory). They created 100 SQL queries of varying difficulty: from simple (L1: "find a customer") to complex (L5: multi-step queries with subqueries and window functions). The model's job: generate correct SQL from a natural-language question.
They tested 11 models (Claude Opus/Haiku, GPT-4.5/mini, Gemini Pro/Flash, DeepSeek, Kimi, Llama 4, Qwen) in two modes: (1) File Agent — the model searches schema.yaml via grep/read, (2) Prompt Baseline — the whole schema in the prompt at once. Across 4 formats: YAML, Markdown, JSON, TOON. Scoring used Jaccard similarity of query results: if the model got the same result as the ground truth, it's a success.
Why the results came out this way: Frontier models (+2.7% with files) won because they avoided "lost in the middle" — they didn't read the whole schema but picked out what was relevant. Open-source models stumbled because they're poorly trained for tool use: Qwen made extra grep attempts, lost context, got confused. DeepSeek did better — apparently it saw more file-operation examples in training.
The surprise: TOON (a compact format, 25% smaller file) burned 7x more tokens at 10,000 tables. The logic: models don't know TOON syntax → they try YAML/JSON patterns → every failed attempt adds tokens → on large schemas the "grep tax" piles up. Meanwhile, format didn't affect accuracy — only efficiency. This showed that file size ≠ runtime tokens: what matters isn't how compact the file is, but how familiar the patterns are for efficient search.
Practical insight: Don't optimize your format for file size — optimize for a grep-friendly structure. YAML won not because it's compact but because models know the patterns (tables:, columns:, name:). Scaling works not by shrinking the file but by partitioning: at 10,000 tables the model read only ~250 from the relevant domain — the context stayed bounded.
Original from the Paper
YAML Schema Format (used in the experiment):
tables:
- name: store_sales
description: Sales transactions at physical stores
columns:
- name: ss_sold_date_sk
type: integer
description: Date key for sale transaction
- name: ss_item_sk
type: integer
description: Item key, references item table
- name: ss_customer_sk
type: integer
description: Customer key
- name: ss_quantity
type: integer
description: Quantity sold
- name: ss_sales_price
type: decimal
description: Price at which item was sold
foreign_keys:
- column: ss_sold_date_sk
references: date_dim.d_date_sk
- column: ss_item_sk
references: item.i_item_sk
- column: ss_customer_sk
references: customer.c_customer_sk
- name: customer
description: Customer dimension
columns:
- name: c_customer_sk
type: integer
description: Customer surrogate key
- name: c_current_addr_sk
type: integer
description: Current address key
- name: c_first_name
type: string
- name: c_last_name
type: string
Navigator.md structure (from the paper):
# Schema Navigator
## Overview
TPC-DS retail data warehouse schema with 24 tables across 4 domains:
- Sales (store_sales, web_sales, catalog_sales)
- Customers (customer, customer_address, customer_demographics)
- Items (item, warehouse, inventory)
- Time (date_dim, time_dim)
## How to search
For questions about:
- Sales transactions → store_sales, web_sales, catalog_sales
- Customer information → customer, customer_address
- Product details → item, warehouse
- Time periods → date_dim, time_dim
## Relationships
All sales tables link to:
- date_dim via *_sold_date_sk
- item via *_item_sk
- customer via *_customer_sk
Context: The researchers used this structure for all YAML experiments. Navigator.md was identical across all formats; only the structure of the main schema files changed.
Adaptations and Extrapolations
💡 Adaptation for business processes: Not just for database schemas — it applies to any structured knowledge.
Example: a customer support knowledge base
support_navigator.md:
# Support knowledge base
## Structure
- billing_rules.yaml — pricing rules, subscriptions, refunds
- technical_specs.yaml — product specs, compatibility
- common_issues.yaml — common problems and fixes
- escalation_rules.yaml — when to escalate to L2/L3
## How to search
If the question is about payment/refunds → billing_rules.yaml
If it's "it doesn't work" → common_issues.yaml, the section for that product
If it's about specs → technical_specs.yaml
common_issues.yaml:
product: iOS mobile app
issues:
- symptom: "Push notifications aren't arriving"
causes:
- Turned off in iOS settings
- Permission not granted on first launch
- Beta version of the app
solution: |
1. Settings → {App name} → Notifications → turn on
2. If that doesn't help → reinstall the app (it will ask for permission again)
3. If beta → update to the stable version from the App Store
escalate_if: "The problem persists after steps 1-3"
- symptom: "The interface is laggy"
causes:
- iOS version below 15
- Low storage on the device
solution: |
1. Check iOS: Settings → General → About
2. If iOS 14 → suggest updating
3. Check storage → Settings → General → Storage
Upload it to a project → the chat becomes L1 support with instant access to procedures.
🔧 Technique: partition by usage frequency → token optimization
Instead of partitioning only by domain, split into hot (frequently needed) and cold (rarely needed):
_index.yaml:
partitions:
hot:
file: frequent_queries.yaml
description: The 20% of entities that show up in 80% of questions
use_case: ["pricing", "availability", "top 10 products"]
cold:
file: full_catalog.yaml
description: Full catalog for rare queries
use_case: ["niche specs", "rare categories"]
Effect: most queries read only the hot file (smaller, more relevant) → fewer tokens → faster → cheaper. Fall back to cold only if nothing turns up in hot.
💡 Extrapolation: combine with Chain-of-Thought for complex queries
Task: {a complex query that needs data from several domains}
Step 1: Decomposition
Break the task into subtasks. For each subtask, determine which files are needed.
Step 2: Navigation
For each file:
- Read the navigator.md section about this file
- Find the needed entity via grep
- Read only the relevant section
Step 3: Synthesis
Combine the data from all sources and answer the original question.
Available files: {list from navigator.md}
This combines file-native retrieval with explicit reasoning — for tasks where you need to pull data from 3+ domains.
Resources
Structured Context Engineering for File-Native Agentic Systems: Evaluating Schema Accuracy, Format Effectiveness, and Multi-File Navigation at Scale (2025)
- TPC-DS Benchmark: https://www.tpc.org/tpcds/
- Model Context Protocol: https://modelcontextprotocol.io/
- TOON Format: https://toonformat.dev/
- Spider Benchmark (text-to-SQL): arXiv:1809.08887
- "Lost in the Middle" (Liu et al., 2024): arXiv:2307.03172
- Context Engineering Survey (Mei et al., 2025): arXiv:2507.13334
Author: Damon McMillan, HxAI Australia