There Is No Application · Chapter 14
Case Study: Building an Autonomous Discovery Agent
We've laid the philosophical groundwork, established our architecture, and mastered the tools. Now, let's put it all together. In this chapter, we will design our most ambitious agent yet: an Autonomous Discovery Agent.
This case study is not a step-by-step tutorial but an architectural blueprint. It will show you how the concepts of memory, tools, and chained prompts can be combined to create an agent that can perform complex research tasks with a high degree of autonomy. This is inspired by the vision of projects like .marlocDiscover, where agents are not just responding to requests but are actively exploring and making sense of the world.
The Goal: The Discovery Agent's competence is simple to state but complex to execute: Given a complex research question, autonomously browse the web, read documents, synthesize information, and produce a structured report.
To achieve this, we need to move beyond a simple, single-call LLMChain. We need to build an agent that can think, plan, and act in a loop.
The Agent's Architecture: The ReAct Loop
The core of our Discovery Agent will be a popular and powerful framework known as ReAct, which stands for Reason + Act. The agent operates in a continuous loop, and in each cycle, it performs two steps:
- Reason: The agent's "brain"—a powerful LLM—is prompted to think about the current state of its task. Given the original question and all the information it has gathered so far, it decides what it needs to do next. This is its "thought process."
- Act: Based on its thought process, the agent chooses to execute one of a predefined set of "tools" available to it. The output of this action is called an "observation."
This observation is then fed back into the prompt for the next "Reason" step, and the loop continues until the agent concludes that it has gathered enough information to provide a final answer.
This is a direct implementation of our core principle: AI plans, deterministic workers execute. The "Reason" step is the AI planning. The "Act" step is the execution of a deterministic tool (a function you wrote).
The Discovery Agent's Toolkit
To be effective, our agent needs a set of well-defined tools. These are just regular TypeScript functions that the agent's LLM can choose to call. The agent doesn't write the code for these tools; it just decides which one to use and what input to provide.
Here is the toolkit for our Discovery Agent:
webSearch(query: string): Promise<Array<{ title: string, url: string }>>
A tool that takes a search query, uses a search engine API (like Google or DuckDuckGo), and returns a list of promising URLs.
readURL(url: string): Promise<string>
A tool that takes a URL, fetches the content of the page, cleans the HTML, and returns the raw text content. This is our Vindria competence.
vectorizeAndStore(text: string, source_url: string): Promise<string>
A tool that takes a block of text, splits it into chunks, creates vector embeddings for each chunk, and stores them in its Cloudflare Vectorize database (its "long-term memory"). It tags the vectors with the source URL.
queryVectorDB(query: string): Promise<string>
A tool that takes a natural language query, creates an embedding, and retrieves the most relevant chunks of text from its Vectorize memory. This is our RAG competence.
finish(report: object): Promise<void>
A special tool that the agent calls when it believes it has a final answer. It takes a structured report object, which concludes the loop.
A Walkthrough: The Agent in Action
Let's trace the execution of the agent as it tackles a research question.
User Question: "What are the key differences between the Cloudflare AI Gateway and LangSmith for observability?"
---
Loop 1
- Prompt to LLM:
> You are a Discovery Agent. Your goal is to answer the user's question. You have access to the following tools: webSearch, readURL, vectorizeAndStore, queryVectorDB, finish.
>
> User Question: "What are the key differences between the Cloudflare AI Gateway and LangSmith for observability?"
>
> Thought: I need to understand what both Cloudflare AI Gateway and LangSmith are. I don't have any information yet, so my first step should be to search the web.
>
> Action:
> ```json
> { "tool": "webSearch", "args": { "query": "Cloudflare AI Gateway observability" } }
> ```
- System: Executes
webSearch(...). - Observation: A list of URLs is returned, including the official Cloudflare product page.
---
Loop 2
- Prompt to LLM:
> ... (previous context) ...
>
> Observation: I have found several URLs. The first one, https://www.cloudflare.com/developer-platform/ai-gateway/, looks like the most reliable source for the AI Gateway. I should read its content.
>
> Thought: I need to read the content of the official Cloudflare AI Gateway page to understand its features.
>
- Action:
> ```json
> { "tool": "readURL", "args": { "url": "https://www.cloudflare.com/developer-platform/ai-gateway/" } }
> ```
- System: Executes
readURL(...). - Observation: The full text content of the page is returned.
---
Loop 3
- Prompt to LLM:
> ... (previous context) ...
>
> Observation: I have the text content for the AI Gateway page. It's a lot of information. I should process this and store it in my long-term memory so I can ask questions about it later. This will help me synthesize my final answer.
>
> Thought: I will use the vectorizeAndStore tool to commit this knowledge to my Vectorize database.
>
> Action:
> ```json
> { "tool": "vectorizeAndStore", "args": { "text": "...", "source_url": "..." } }
> ```
- System: Executes
vectorizeAndStore(...). - Observation:
"Success: Content has been indexed."
---
Loop 4, 5, 6...
The agent would repeat this process for LangSmith: searching, reading the LangSmith website, and storing that content in its memory.
---
Final Loop
- Prompt to LLM:
> ... (previous context) ...
>
> Observation: I have now gathered and stored information about both Cloudflare AI Gateway and LangSmith. I have enough context in my vector database to formulate a final answer. I will now query my own memory to synthesize the key differences.
>
> Thought: I have sufficient information. I will now construct the final report and finish the task.
>
> Action:
> ```json
> {
> "tool": "finish",
> "args": {
> "report": {
> "comparison_summary": "Cloudflare AI Gateway is a proxy that provides observability (cost, latency), caching, and security for LLM calls at the infrastructure level. LangSmith is a developer-focused platform that provides deep, trace-level observability specifically for LangChain applications, showing the internal steps of an agent's execution.",
> "key_differences": [
> "Level of Abstraction: AI Gateway is infrastructure-level, LangSmith is application-level.",
> "Focus: AI Gateway focuses on metrics and security, LangSmith focuses on debugging and tracing chain execution.",
> "Integration: AI Gateway works with any LLM call, LangSmith is deeply integrated with the LangChain framework."
> ]
> }
> }
> }
> ```
- System: Receives the
finishaction and returns the structured report to the user. The loop terminates.
The Engineering Reality
This example seems magical, but it's grounded in the engineering principles we've established. The developer's job is to:
- Write the robust, deterministic code for each of the tools (
webSearch,readURL, etc.). - Craft the master prompt that orchestrates the ReAct loop, explaining the agent's goal and how to use its tools.
- Write the parser that interprets the
ActionJSON from the LLM and calls the correct tool function.
This is the pinnacle of the "No Application" architecture. The LLM acts as the central planner, but it operates within a world of tools, rules, and constraints that you, the architect, have built for it.
