{"id":104495,"date":"2026-07-13T19:49:12","date_gmt":"2026-07-13T19:49:12","guid":{"rendered":"https:\/\/www.europesays.com\/ai\/104495\/"},"modified":"2026-07-13T19:49:12","modified_gmt":"2026-07-13T19:49:12","slug":"agentic-rag-let-the-agent-search","status":"publish","type":"post","link":"https:\/\/www.europesays.com\/ai\/104495\/","title":{"rendered":"Agentic RAG: Let the Agent Search"},"content":{"rendered":"<p class=\"wp-block-paragraph\"> application we build is a RAG app.<\/p>\n<p class=\"wp-block-paragraph\">The recipe is simple: chunk, embed, retrieve, then answer.<\/p>\n<p class=\"wp-block-paragraph\">It looks clean on paper. But once you use it on real cases, things get messy very quickly: similarity search finds similar wordings but not necessarily useful chunks, the right evidence never shows up in the retrieved context as it ranks too low, or important context may be split across chunk boundaries.<\/p>\n<p class=\"wp-block-paragraph\">With insufficient context, the LLM has little room to recover.<\/p>\n<p class=\"wp-block-paragraph\">So, how about we make retrieval iterative? What if the model can search, read, decide whether it has enough evidence, and search again when needed? Probably we don\u2019t even need the vector embeddings in the first place.<\/p>\n<p class=\"wp-block-paragraph\">That\u2019s the premise of agentic RAG.<\/p>\n<p class=\"wp-block-paragraph\">In this post, we\u2019ll build a mini agentic RAG workflow with the OpenAI Agents SDK. We\u2019ll examine how the agent iteratively searches, reads, and grounds its answer.<\/p>\n<p class=\"wp-block-paragraph\">At the end, we\u2019ll take a step back and briefly discuss the considerations for building a practical agentic RAG solution.<\/p>\n<p>1. Case Study: Answering a Policy Question with Agentic RAG<\/p>\n<p class=\"wp-block-paragraph\">For our case study, we\u2019ll build a policy RAG agent over a company policy document collection. <\/p>\n<p>1.1 Curating The Document Collection<\/p>\n<p class=\"wp-block-paragraph\">Here, I created six synthetic company policy docs. They are all markdown files. Each one has a title, an effective date, a short summary, and the policy text.<\/p>\n<p class=\"wp-block-paragraph\">To be realistic, those docs cover 6 common company policy areas:<\/p>\n<p>approval_matrix.md, containing approval levels for common business travel decisions, effective on July 1, 2025.<\/p>\n<p>conference_guidelines.md, containing rules for attending external events, effective on May 15, 2025.<\/p>\n<p>faq.md, containing informal answers to common travel questions, effective on September 1, 2025.<\/p>\n<p>policy_updates_2026.md, containing updates to lodging, conference travel, and approval timing for 2026, effective on January 1, 2026.<\/p>\n<p>remote_work_policy.md, containing rules for remote work, effective on February 1, 2026.<\/p>\n<p>travel_policy.md, containing standard travel booking rules for flights, lodging, meals, and transportation, effective on March 1, 2025.<\/p>\n<p class=\"wp-block-paragraph\">We made it intentional that the answer to a policy question may not live in one document. This allows us to see the desired agentic behavior.<\/p>\n<p class=\"wp-block-paragraph\">You can find the full synthetic documents and the agentic RAG implementation notebook <a href=\"https:\/\/github.com\/ShuaiGuo16\/agentic_RAG\" rel=\"nofollow noopener\" target=\"_blank\">here<\/a>.<\/p>\n<p>1.2 Defining The Agent<\/p>\n<p class=\"wp-block-paragraph\">Next, we configure the agent. For that, we use the OpenAI Agents SDK. <\/p>\n<p class=\"wp-block-paragraph\">At a high level, the agent is just this:<\/p>\n<p># pip install openai-agents<br \/>\nfrom agents import Agent<\/p>\n<p>agent = Agent(<br \/>\n    name=&#8221;Policy research assistant&#8221;,<br \/>\n    instructions=INSTRUCTIONS,<br \/>\n    model=&#8221;gpt-5.4&#8243;,<br \/>\n    tools=[list_docs, search_docs, read_doc],<br \/>\n)<\/p>\n<p class=\"wp-block-paragraph\">Two parts we need to go through: the agent instruction, and the tools it has access to.<\/p>\n<p class=\"wp-block-paragraph\">First, the instruction. This is where we define the desired search behavior:<\/p>\n<p># Note: This instruction is iterated with AI<br \/>\nINSTRUCTIONS = &#8220;&#8221;&#8221;<br \/>\n[Role]<br \/>\nYou are a careful internal policy research assistant.<\/p>\n<p>[Research behavior]<br \/>\nAnswer employee policy questions using the document tools.<br \/>\nFind enough relevant evidence to support the answer.<br \/>\nKeep conclusions grounded in the policy documents.<\/p>\n<p>[Expected output]<br \/>\nGive a direct answer first.<br \/>\nThen briefly explain the evidence.<br \/>\nCite the document filenames used for each important claim.<br \/>\n&#8220;&#8221;&#8221;.strip()<\/p>\n<p class=\"wp-block-paragraph\">For this case study, we require that the agent can only touch the docs via three pre-defined tools:<\/p>\n<p class=\"wp-block-paragraph\">The first one is a tool that gives the agent a quick overview of what documents exist:<\/p>\n<p>@function_tool<br \/>\ndef list_docs() -&gt; list[dict]:<br \/>\n    &#8220;&#8221;&#8221;List available policy documents without returning their body text.&#8221;&#8221;&#8221;<br \/>\n    return [<br \/>\n        {<br \/>\n            &#8220;doc_name&#8221;: doc[&#8220;doc_name&#8221;],<br \/>\n            &#8220;title&#8221;: doc[&#8220;title&#8221;],<br \/>\n            &#8220;effective&#8221;: doc[&#8220;effective&#8221;],<br \/>\n            &#8220;summary&#8221;: doc[&#8220;summary&#8221;],<br \/>\n        }<br \/>\n        for doc in docs.values()<br \/>\n    ]<\/p>\n<p class=\"wp-block-paragraph\">The second tool is a keyword-search tool. We keep it simple here: each document is split into paragraph chunks, and each query is matched against those chunks by token overlap:<\/p>\n<p>@function_tool<br \/>\ndef search_docs(query: str) -&gt; list[dict]:<br \/>\n    &#8220;&#8221;&#8221;Search policy documents and return the top three short snippets.&#8221;&#8221;&#8221;<br \/>\n    query_tokens = tokenize(query)<br \/>\n    scored = []<\/p>\n<p>    for chunk in chunks:<br \/>\n        score = len(query_tokens &amp; chunk[&#8220;tokens&#8221;])<br \/>\n        if score:<br \/>\n            scored.append((score, chunk))<\/p>\n<p>    scored.sort(key=lambda item: item[0], reverse=True)<\/p>\n<p>    results = []<br \/>\n    for score, chunk in scored[:3]:<br \/>\n        snippet = chunk[&#8220;text&#8221;].replace(&#8220;\\n&#8221;, &#8221; &#8220;)<br \/>\n        if len(snippet) &gt; 420:<br \/>\n            snippet = snippet[:417].rstrip() + &#8220;&#8230;&#8221;<br \/>\n        results.append({<br \/>\n            &#8220;doc_name&#8221;: chunk[&#8220;doc_name&#8221;],<br \/>\n            &#8220;title&#8221;: chunk[&#8220;title&#8221;],<br \/>\n            &#8220;section&#8221;: chunk[&#8220;section&#8221;],<br \/>\n            &#8220;snippet&#8221;: snippet,<br \/>\n            &#8220;score&#8221;: round(score, 2),<br \/>\n        })<\/p>\n<p>    return results<\/p>\n<p class=\"wp-block-paragraph\">The last tool is what allows the agent to open one document by filename:<\/p>\n<p>@function_tool<br \/>\ndef read_doc(doc_name: str) -&gt; str:<br \/>\n    &#8220;&#8221;&#8221;Read one policy document by filename.&#8221;&#8221;&#8221;<br \/>\n    if doc_name not in docs:<br \/>\n        valid = &#8220;, &#8220;.join(sorted(docs))<br \/>\n        return f&#8221;Unknown document: {doc_name}. Valid documents: {valid}&#8221;<\/p>\n<p>    return docs[doc_name][&#8220;text&#8221;]<\/p>\n<p class=\"wp-block-paragraph\">That\u2019s the full RAG agent.<\/p>\n<p>1.3 Running One Policy Question<\/p>\n<p class=\"wp-block-paragraph\">Now we test the agent with one concrete question:<\/p>\n<p>\u201cI am attending a conference in Berlin. The conference organizer lists an official hotel, but the nightly rate is above the normal hotel cap. Can I book that hotel, and what approval do I need before booking?\u201c<\/p>\n<p class=\"wp-block-paragraph\">We run the agent with:<\/p>\n<p>from agents import Runner<\/p>\n<p>result = await Runner.run(agent, PROMPT, max_turns=12)<\/p>\n<p class=\"wp-block-paragraph\">The agent produced the right answer: yes, the employee can book the official conference hotel if there is a practical business reason. It got that information from conference_guidelines.md.<\/p>\n<p class=\"wp-block-paragraph\">For the approval part, the agent first identified that approval is needed as the hotel is above the normal cap. Then it gave the corresponding approval conditions. The agent used travel_policy.md, approval_matrix.md, and policy_updates_2026.md to support its answer, which is exactly what we would expect.<\/p>\n<p class=\"wp-block-paragraph\">The more interesting part is the trace, from which we can learn how the agent thinks. We can show the trace in the following way:<\/p>\n<p>for item in result.new_items:<br \/>\n    print(type(item).__name__, item)<\/p>\n<p class=\"wp-block-paragraph\">result.new_items contains the intermediate tool calls and tool outputs produced by the agent. In my run, I can see that the agent first called search_docs() with keywords like conference hotel, hotel cap, approval, and Berlin. Then, it called list_docs() to inspect the available policy documents. After that, it opened the relevant files with read_doc(). Only then did it produce the final answer.<\/p>\n<p class=\"wp-block-paragraph\">This is exactly the agentic loop we wanted to see.<\/p>\n<p>3. What to Decide Before Building Agentic RAG<\/p>\n<p class=\"wp-block-paragraph\">The case study we just went through only scratched the surface. To really build a practical agentic RAG solution, based on my experience, I suggest you answer the following 5 questions:<\/p>\n<p class=\"wp-block-paragraph\">Q1: How much freedom should the agent have?<\/p>\n<p class=\"wp-block-paragraph\">One common option is exactly what we have done in the earlier case study: we exposed a couple of carefully curated tools, and the agent is only allowed to use those tools to do the investigation. This is straightforward in terms of controlling, testing, and auditing.<\/p>\n<p class=\"wp-block-paragraph\">But we can also give the agent broader access, such as shell and file system. This way, the agent can directly run scripts to search and inspect files, and maybe even do further data processing to generate useful artifacts, all on its own. <\/p>\n<p class=\"wp-block-paragraph\">This pattern can be much more powerful, but it also increases risk and makes behavior harder to predict.<\/p>\n<p class=\"wp-block-paragraph\">So for most RAG applications, I\u2019d start with curated tools first, and only add shell\/file-system access when the task complexity justifies it.<\/p>\n<p class=\"wp-block-paragraph\">Q2: Should the agent search raw text only?<\/p>\n<p class=\"wp-block-paragraph\">Most RAG projects might start with plain text like PDFs, wiki pages, manuals, etc. That\u2019s fine.<\/p>\n<p class=\"wp-block-paragraph\">But in practice, we can often make retrieval easier by deriving a knowledge layer on top of the raw texts.<\/p>\n<p class=\"wp-block-paragraph\">Those derived knowledge artifacts can be document metadata, summaries, cross-document links, or we can go further and implement a proper knowledge graph.<\/p>\n<p class=\"wp-block-paragraph\">These derived knowledge artifacts help the agent navigate the corpus, while the raw texts remain as the source of truth.<\/p>\n<p class=\"wp-block-paragraph\">Q3: Do we still need embeddings?<\/p>\n<p class=\"wp-block-paragraph\">Agentic RAG doesn\u2019t necessarily mean embeddings are gone.<\/p>\n<p class=\"wp-block-paragraph\">Vector embeddings are still an efficient way to find semantically relevant texts, and it often outperforms a pure keyword search strategy. <\/p>\n<p class=\"wp-block-paragraph\">In agentic RAG, what changed essentially is that the retrieval becomes an \u201caction\u201d the agent can take. Under this framing, \u201caction\u201d can still be powered by an embedding-based retriever, a keyword-based one, or even a hybrid one.<\/p>\n<p class=\"wp-block-paragraph\">So embeddings can still be useful. They are just one possible way to power the agent\u2019s search tool.<\/p>\n<p class=\"wp-block-paragraph\">Q4: Should one agent handle everything?<\/p>\n<p class=\"wp-block-paragraph\">The simplest agentic RAG setup is just one agent that does the search, read, and answer.<\/p>\n<p class=\"wp-block-paragraph\">But as the task gets more complex, you might want to split the work among multiple agents. More concretely, you might need to adopt a multi-agent strategy.<\/p>\n<p class=\"wp-block-paragraph\">You can split the work by role. For example, the planner-retriever-writer split, where the planner decides what evidence is needed, the retriever collects it, and the writer produces the final answer by using the collected evidence.<\/p>\n<p class=\"wp-block-paragraph\">You can also split by source type, where each agent is equipped with customized tools and focuses on one specific type of source.<\/p>\n<p class=\"wp-block-paragraph\">Just keep in mind: A multi-agent setup adds coordination complexity, and there is no guarantee that it will perform better than a single-agent setup. Empirical testing is very important.<\/p>\n<p class=\"wp-block-paragraph\">Q5: Should we always use agentic RAG?<\/p>\n<p class=\"wp-block-paragraph\">Maybe not always.<\/p>\n<p class=\"wp-block-paragraph\">Just because agentic RAG becomes a trendy topic does not necessarily mean you should always default to it.<\/p>\n<p class=\"wp-block-paragraph\">Agentic RAG gives more flexibility, but that comes with costs. That cost is not only about latency or token cost, but also less predictable agent behavior.<\/p>\n<p class=\"wp-block-paragraph\">Always start simple, then add agentic loops when the question actually needs iterative retrieval.<\/p>\n","protected":false},"excerpt":{"rendered":"application we build is a RAG app. The recipe is simple: chunk, embed, retrieve, then answer. It looks&hellip;\n","protected":false},"author":2,"featured_media":104496,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[179,7493,54196,24,405,415,30539],"class_list":["post-104495","post","type-post","status-publish","format-standard","has-post-thumbnail","category-agentic-ai","tag-agentic-ai","tag-agentic-artificial-intelligence","tag-agentic-rag","tag-ai","tag-ai-agents","tag-llm","tag-rag"],"_links":{"self":[{"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/posts\/104495","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/comments?post=104495"}],"version-history":[{"count":0,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/posts\/104495\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/media\/104496"}],"wp:attachment":[{"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/media?parent=104495"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/categories?post=104495"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.europesays.com\/ai\/wp-json\/wp\/v2\/tags?post=104495"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}