Series: Claude Code 自動化指南 (3/3)
- On-demand tool loading is RAG's index-then-load structure, but the LLM's reasoning replaces vector similarity as the retriever
- Lazy loading cuts context cost roughly 10x and keeps the prompt cache stable versus preloading every schema upfront
- Deferred tools arrive as names only; call one before fetching its schema and it fails
Table of Contents
If you’ve used Claude Code, you may have noticed something: it advertises dozens of skills and hundreds of tools, yet none of them are stuffed into the model’s context up front. They appear “on demand.” This post unpacks how that mechanism works, and answers a question I confused myself with at first — is this a form of RAG over the user’s query?
The short answer: in spirit, absolutely. But the retriever isn’t a vector database — it’s the LLM itself. Let’s unpack it.
The root problem: context is a scarce resource
Agent systems keep accumulating tools and instructions, but the context window is both limited and expensive. Suppose a skill’s full instructions average 800 tokens and a tool’s JSONSchema averages 300 tokens:
Preload everything: 60 skills × 800 + 50 tools × 300 ≈ 63,000 tokens
↑ burned before the conversation even starts
Lazy loading: index ≈ 2,000 tokens + what's actually used ≈ 2,500 tokens ≈ 4,500 tokens
That’s more than a 10x difference. Worse, preloading everything makes the prompt cache hard to maintain — the moment which skill you used changes, the leading content shifts and the cache invalidates.
So the right move isn’t “give everything,” it’s a two-tier approach: hand over a lightweight index first, and only expand the heavyweight full definition when it’s actually needed. That’s lazy loading.
Two kinds of on-demand loading
Claude Code has two categories that use the same strategy, with slightly different mechanics.
| Deferred Tools | Skills | |
|---|---|---|
| What it is | Callable functions (WebFetch, Notion API…) | A bundle of workflow instructions (/post, /code-review…) |
| What’s in the index | Just the name | Name + a one-line description |
| How to expand | Use ToolSearch to fetch the JSONSchema | Use the Skill tool to execute it |
| What you get after | Parameter definitions, now callable | Full prompt injected into the conversation |
The Deferred Tools flow
At the start of a session, the tool index the model receives looks like this (excerpt). Note each tool is just a name:
The following deferred tools are now available via ToolSearch.
Their schemas are NOT loaded — calling them directly will fail:
WebFetch
WebSearch
mcp__claude_ai_Notion__notion-search
... (~50 of them)
At this point WebFetch is just a string. The model doesn’t know what parameters it takes or what it returns; calling it blindly gets rejected with an error. The flow is:
flowchart LR
A[User request] --> B[Model decides: need WebFetch]
B --> C["ToolSearch(select:WebFetch)"]
C --> D[Fetch full JSONSchema]
D --> E["WebFetch(url, prompt)"]
E --> F[Got result, continue task]
The key is step three: those 50 tool schemas might total tens of thousands of tokens, but the model only pays the cost for the one it actually needs. The other 49 forever occupy nothing but “a name.”
The Skills flow
Each line in the skills index has only a description:
- post: Convert a conversation, notes... into a structured post
- code-review: Review the current diff for correctness bugs...
Behind post there might be hundreds of lines of instructions (how to categorize, how to fill frontmatter, article structure templates, commit format…), but none of it is in context until the user says “turn this into an article,” the model matches the description, and executes Skill(skill="post"). Only at that moment do the full instructions get injected. Before that, the model knows nothing about the details.
Back to the core question: is this RAG?
Many people equate RAG with “embeddings + vector database,” but that’s just one implementation of retrieval. RAG, broken down, is two things:
Retrieval + Augmentation (inject the result into context) → Generation
The real definition is “don’t stuff everything in — retrieve the relevant bits first, then inject.” By that definition, on-demand tool loading genuinely is RAG — it’s “don’t put all schemas in context; retrieve what’s needed first, then inject.”
But who is the retriever? Here are two modes, and this is the key difference.
Mode A: Vector retrieval (classic RAG)
query → embedding → compute cosine similarity → take top-k
decision-maker = math
Retrieval is automatic and upfront; the model passively receives already-retrieved content, and retrieval happens before the model “speaks.”
Mode B: Agentic retrieval (the LLM is the retriever)
query → LLM reads the index, reasons → actively calls search to fetch
decision-maker = the model's reasoning
Retrieval is an action the model actively initiates mid-conversation, not a background pre-processing step the system runs. The model looks at the index, uses reasoning to decide which to fetch, then goes and fetches it.
sequenceDiagram
participant U as User
participant L as LLM
participant R as Tool Registry
U->>L: Fetch this web page for me
L->>L: Reasoning: this needs WebFetch
L->>R: ToolSearch(select:WebFetch)
R-->>L: Returns schema
L->>L: Now I know the parameters
L->>R: WebFetch(url, ...)
R-->>L: Page content
The two modes compared
| Vector RAG (Mode A) | Tool loading (Mode B) | |
|---|---|---|
| Retrieves what | Document chunks | Tool schemas |
| Who decides | Cosine similarity (math) | The LLM’s reasoning |
| When it retrieves | ”Before” the model generates | ”During” the conversation, model-initiated |
| Retrieval method | Embedding vector match | Name match + model judgment |
| Model’s role | Passive recipient | Active initiator |
So the most precise framing is: on-demand tool loading is the concept of RAG, but with agentic retrieval replacing vector retrieval. It belongs to a broader family, “Retrieval-Augmented X”: retrieving documents is RAG, retrieving tools could be called Tool RAG, retrieving examples is dynamic few-shot. They all share the same parent philosophy — context is scarce; retrieve before you inject.
A detail that makes it click
ToolSearch actually supports both modes, and you can tell from the shape of the query:
ToolSearch("select:WebFetch") ← I know which one → exact fetch (like SQL WHERE name=)
ToolSearch("notion send message") ← I'm not sure of the name → keyword/semantic search (like RAG)
That second, fuzzy query is very likely backed by a keyword index or embeddings comparing against tool descriptions — and that part really is Mode A vector retrieval, just with the retrieval target swapped from articles to tools.
In other words, Modes A and B aren’t mutually exclusive; they stack: the model uses reasoning to decide “whether to retrieve and with what keywords” (the agentic layer), and underneath, vector similarity maps the fuzzy keywords to concrete tools (the vector layer).
Overall
If you’re building a RAG system, this observation is a useful reminder: RAG’s value isn’t the vector database — it’s the “index-then-load” structure. The same structure applies to documents (knowledge RAG), to tools (tool RAG), and to examples (dynamic few-shot).
What agent systems genuinely add is the layer of letting the model participate in the retrieval decision itself. When your agent has so many tools they won’t fit in context, rather than categorizing them by hand, build a minimal mechanism: a tool registry storing only name + description, with a search function that returns the full schema. It’s isomorphic to the document RAG you already know — just with a different retrieval target.
References
For a deeper look at the technologies and architecture mentioned here, see the following.
Answers come from this article only. Click any prompt below or open the chat at the bottom right.
🇺🇸 English
Ever noticed something about Claude Code? It brags about having dozens of skills and hundreds of tools — but if you peek at what's actually loaded into the model's context at the start, almost none of them are there. They show up only when they're needed. On demand. So today I want to pull apart how that mechanism works, and answer a question that genuinely tripped me up when I first ran into it: is this just RAG in disguise?
Here's the short version. In spirit? Absolutely, yes. But the thing doing the retrieving isn't a vector database — it's the language model itself. Let me walk you through why.
Start with the root problem, which is brutally simple: context is a scarce resource. Agent systems keep piling on tools and instructions, but the context window is both limited and expensive. Let's put numbers on it. Say a skill's full instructions run about 800 tokens, and a single tool's schema — its parameter definitions — runs about 300. Now imagine you preload everything: sixty skills, fifty tools. You're looking at roughly 63,000 tokens burned before the conversation has even started. Before the user has typed a single word.
Now compare that to lazy loading. You hand the model a lightweight index — about 2,000 tokens — plus whatever it actually ends up using, maybe another 2,500. Call it 4,500 tokens total. That's more than a ten-x difference. And there's a second, sneakier cost to preloading: it wrecks your prompt cache. The moment the set of skills in play changes, the leading content of your prompt shifts, and the cache invalidates.
So the smart move isn't "give the model everything." It's two tiers: hand over a lightweight index first, and only expand the heavyweight full definition when it's genuinely needed. That's lazy loading in a nutshell.
Now, Claude Code actually does this in two flavors, same strategy, slightly different plumbing. The first is what's called deferred tools — these are callable functions, things like WebFetch or a Notion API. The second is skills — bundles of workflow instructions, like a "turn this into a post" command. The difference is in what lives in the index and how you expand it. For deferred tools, the index holds literally just the name. To expand it, the model calls a tool-search function to pull down the full schema, and then it can actually call the thing. For skills, the index holds the name plus a one-line description, and to expand you invoke the skill, which injects its full prompt into the conversation.
Let me make the deferred tools flow concrete. At the start of a session, the model gets a list that's essentially just names — WebFetch, WebSearch, the Notion search tool, about fifty of them — with an explicit note: these schemas are NOT loaded, and if you try to call them directly, you'll get an error. So at that moment, "WebFetch" is just a string. The model has no idea what parameters it takes or what it returns. The flow goes: user makes a request, the model reasons "ah, I need WebFetch," it calls the search function to fetch WebFetch's full schema, now it knows the parameters, and only then does it actually call WebFetch and get its result. The beautiful part is step three. Those fifty schemas might add up to tens of thousands of tokens collectively — but the model only ever pays for the one it actually needs. The other forty-nine just sit there as names, costing essentially nothing.
The skills flow is the same idea. Each line in the skills index is just a name and a description — "post: convert a conversation or notes into a structured post," that kind of thing. Behind "post" there might be hundreds of lines of instructions: how to categorize, how to fill in the frontmatter, article templates, commit formats. But none of that is in context until the user actually says "turn this into an article," the model matches that intent against the description, and runs the skill. Only at that instant do the full instructions get injected. Before that moment, the model knows nothing about the details.
Okay — so back to the core question. Is this RAG?
A lot of people hear "RAG" and immediately think "embeddings plus a vector database." But that's just one implementation of retrieval. Strip RAG down to its bones and it's two moves: retrieval, then augmentation — meaning you inject the retrieved result into the context — and then generation. The real heart of it is this principle: don't stuff everything in; retrieve the relevant bits first, then inject. And by that definition, on-demand tool loading genuinely is RAG. Don't put all the schemas in context — retrieve what's needed, then inject it.
But here's where it gets interesting. Who is the retriever? Because there are two distinct modes, and the difference between them is the whole point.
Mode A is classic vector retrieval. Your query gets turned into an embedding, you compute cosine similarity against your stored chunks, you grab the top few matches. The decision-maker here is math. And critically, retrieval is automatic and it happens up front — before the model "speaks." The model just passively receives content that's already been fetched for it.
Mode B is what I'd call agentic retrieval, and here the LLM is the retriever. The query comes in, the model reads the index, reasons about it, and then actively goes and calls search to fetch what it decided it needs. The decision-maker is the model's own reasoning. Retrieval isn't a background pre-processing step the system runs for you — it's an action the model initiates in the middle of the conversation. It looks at the index, thinks "I need this one," and goes and gets it.
So let me put the two side by side. Vector RAG retrieves document chunks; tool loading retrieves tool schemas. In vector RAG, cosine similarity — math — decides what comes back; in tool loading, the model's reasoning decides. Vector RAG retrieves before generation; tool loading retrieves during the conversation, model-initiated. And the model's role flips completely: in vector RAG it's a passive recipient, in tool loading it's the active initiator.
So the most precise way to say it: on-demand tool loading is the concept of RAG, but with agentic retrieval swapped in for vector retrieval. It belongs to a bigger family you could call "retrieval-augmented anything." Retrieving documents is RAG. Retrieving tools — call it tool RAG. Retrieving examples to drop into your prompt — that's dynamic few-shot. Same parent philosophy across all of them: context is scarce, so retrieve before you inject.
And here's the detail that really made it click for me. That tool-search function actually supports both modes, and you can tell which one you're using just from the shape of your query. If you write something like "select WebFetch" — you already know exactly which tool you want — that's an exact fetch, basically like a SQL lookup by name. But if you write something fuzzy like "notion send message," because you're not sure of the tool's exact name, that triggers a keyword or semantic search against the tool descriptions. And that second path? That really is Mode A, honest-to-goodness vector retrieval — just with the target swapped from articles to tools.
Which means the two modes aren't mutually exclusive. They stack. The model uses reasoning to decide whether to retrieve at all and what keywords to use — that's the agentic layer on top — and underneath, vector similarity maps those fuzzy keywords onto concrete tools. Agentic on top, vector underneath, working together.
So let me leave you with the takeaways.
First, and this is the big one: RAG's value was never the vector database. It's the index-then-load structure. Hand over a lightweight index, expand the heavy definition only when needed. That same structure works for documents, for tools, and for examples — it's all the same shape.
Second: what agent systems genuinely add on top of classic RAG is letting the model participate in the retrieval decision itself. Not "the system fetches and the model receives," but "the model reasons about what it needs and goes and gets it."
And third, the practical takeaway: if your agent has so many tools they won't fit in context, don't sit there hand-categorizing them. Build the minimal mechanism instead — a registry that stores just name and description, plus a search function that returns the full schema on request. It's structurally identical to the document RAG you already know how to build. You're just pointing it at a different target.
🇹🇼 中文
如果你用過 Claude Code,可能注意到一件怪事。它號稱有幾十個 skill、上百個工具,但這些東西並不是一開始就全部塞進模型的腦袋裡,而是「按需」出現的。今天我想拆解這個機制到底怎麼運作,順便回答一個我自己一開始也搞混的問題——這到底算不算對使用者的提問做 RAG?
結論先給你:精神上,它完完全全就是 RAG。只是檢索器不是向量資料庫,而是 LLM 自己。我們慢慢往下講。
先講問題的根源:context 是稀缺資源。Agent 系統的工具跟指令越來越多,但 context window 又貴又有限。我們算一筆帳:假設一個 skill 的完整指令平均要八百個 token,一個工具的結構描述平均三百個 token。如果你把六十個 skill、五十個工具全部預先載入,光是還沒開始對話,就先燒掉六萬三千個 token。但如果改成惰性載入,只給一份輕量目錄,再加上實際用到的那幾個,大概四千五就夠了。差距超過十倍。
而且更麻煩的是,全部預載會讓 prompt cache 很難維持。只要今天用到的 skill 有變化,前面那一大段內容跟著變,快取就失效了。所以正確做法不是「全給」,而是分兩層:先給輕量的目錄,真的要用的時候,才把重量級的完整定義展開。這就是惰性載入。
那 Claude Code 裡有兩類東西用這套策略,機制有點不一樣。
第一類叫 Deferred Tools,就是可以呼叫的函式,像是抓網頁、呼叫 Notion API 這種。它在目錄裡只有一個名字,沒別的。第二類是 Skills,是一整包流程指令,像是幫你整理文章、做程式碼審查。它在目錄裡是名字加上一行描述。
先看 Deferred Tools 的流程。session 一開始,模型收到的工具目錄裡,每個工具真的就只有一個名字,比方說 WebFetch。模型此刻完全不知道這東西要什麼參數、會回傳什麼,你硬呼叫它,系統會直接擋下來報錯。流程是這樣:使用者提出請求,模型推理判斷「啊我需要 WebFetch」,於是先用一個搜尋動作把它的完整結構描述取回來,這時候才知道參數長怎樣,然後才真正呼叫它、拿到結果。關鍵在於,那五十個工具的描述加起來可能上萬 token,但模型只在真正要用的那一個上付出成本,其他四十九個永遠只佔「一個名字」的空間。
Skills 的流程也類似。目錄裡每個 skill 只有一行描述。但它背後可能藏著幾百行指令——怎麼分類、frontmatter 怎麼填、文章結構模板、commit 格式——這些東西完全不在 context 裡,直到使用者說「幫我整理成文章」、模型比對描述命中、執行那個 skill 的那一刻,完整指令才被注入。在那之前,模型對細節是一無所知的。
好,回到核心問題:這是 RAG 嗎?
很多人把 RAG 直接等於「embedding 加向量資料庫」,但那只是檢索的一種實作而已。RAG 拆開來其實是兩件事:檢索,加上把檢索結果塞進 context,最後才生成。它真正的定義是——不要全塞,先檢索相關的再塞。按這個定義,按需載入工具當然是 RAG,它就是「不把所有描述塞進 context,先檢索需要的再塞」。
但檢索器是誰?這裡有兩種模式,也是最關鍵的差別。
模式 A 是典型的向量檢索。提問先變成向量,算 cosine 相似度,取最相關的前幾名。決策者是數學。而且這個檢索是自動前置的,模型是被動收到已經檢索好的內容,整件事發生在模型「開口之前」。
模式 B 是 agentic 檢索,也就是 LLM 自己當檢索器。模型讀目錄、自己推理判斷,然後主動發起一個搜尋動作把東西抓回來。決策者是模型的推理。而且這個檢索是在對話「中途」由模型主動發起的動作,不是系統背後自動跑的前處理。
所以兩種模式對照下來,差別很清楚:向量 RAG 檢索的是文件片段,工具載入檢索的是工具描述;前者由 cosine 相似度這個數學決定,後者由 LLM 的推理決定;前者發生在生成之前,後者發生在對話之中;前者模型是被動接收,後者模型是主動發起。
所以最精準的說法是:按需載入工具,是 RAG 的概念,但用 agentic retrieval 取代了 vector retrieval。它屬於一個更大的家族,可以叫「檢索增強的 X」。檢索文件叫 RAG、檢索工具可以叫 Tool RAG、檢索範例叫 dynamic few-shot。它們共享同一套母體哲學——context 是稀缺資源,先檢索再注入。
接著講一個會讓你「啊,原來如此」的細節。那個搜尋工具,其實同時支援兩種模式,看你查詢的形式就知道。如果你已經確定要哪個工具,你就精確指名取回,這就像 SQL 裡用名字精確查詢。但如果你不確定它叫什麼,你就丟關鍵字進去做語意搜尋——而這背後,很可能就是用關鍵字索引或 embedding 去比對工具描述。這一段,就真的是模式 A 的向量檢索了,只是檢索對象從文章換成了工具。
換句話說,模式 A 跟模式 B 不是互斥的,是可以疊起來的。模型用推理決定「要不要檢索、用什麼關鍵字檢索」,這是 agentic 那一層;底層再用向量相似度,把模糊的關鍵字對應到具體的工具,這是 vector 那一層。
那整體來說,如果你正在做 RAG 系統,這個觀察其實是個提醒:RAG 的價值不在向量資料庫,而在「先建索引、再按需載入」這個結構。同一套結構,可以套在文件上、套在工具上、也可以套在範例上。
最後幫你收斂三個重點。第一,按需載入的本質就是 RAG,差別只在檢索器從向量相似度,換成了 LLM 自己的推理。第二,RAG 真正的價值是「index-then-load」這個結構,不是向量資料庫這個工具,所以它能套在文件、工具、範例任何東西上。第三,agent 系統真正多出來的那一層,是讓模型自己參與檢索決策的主動性。所以當你的工具多到塞不下 context,與其手動分類,不如建一個最小機制:registry 只存名字加描述,再提供一個搜尋函式回傳完整定義。這跟你已經會的文件 RAG,其實是同一回事,只是換了檢索對象而已。
Tags
Related Articles
RAG's Five Stages: From Pipeline to Reasoning Retrieval, and the Naive RAG on My Own Site
Over the past two years RAG evolved from a 'linear pipeline' to 'loop-based reasoning'. It maps cleanly to five stages: Naive, Advanced, Modular, Graph, Agentic. The real inflection point is control moving from pipeline to agent — a System 1 → System 2 shift. Looking back at engineer-news's own RAG stack, it's stuck at the Naive edge — so this post also lays out what to fix next.
Building a Real RAG: 5 Infra Lessons from InfiniFlow's 2024 Year-in-Review
The previous post zoomed out for a five-stage panorama of RAG. This one zooms in on the five infra lessons any real RAG has to face: document ingestion, contextualized chunking, three-lane hybrid search, tensor reranker, and GraphRAG's semantic gap. Each lesson is checked against engineer-news's current stack, ending with a priority list for a personal site.
Harness Engineering (2): Five Engineering Answers from OpenAI's Million-Line Experiment
Three OpenAI engineers, five months, one million lines of AI-generated code, zero hand-written. The real value of this experiment isn't the numbers — it's the proof that Harness design can be engineered. Five concrete practices: making the app legible to agents, treating the repo as the source of truth, mechanizing architectural constraints, rewriting merge philosophy, and background entropy management.