Research and competitive-intelligence agents
- Source
- From the RAG & Agents Engineering course
- By
- Sourav Banerjee
- Last updated
- 9 Nov 2026
The project: research and market-brief agents
What you'll build
- A research assistant that searches the web, reads the pages it finds and checks the current weather.
- Budgeted tools that return at most three pages of 4,000 characters per search.
- Conversation memory that lets a follow-up such as "And in Dubai?" refer to the earlier turn.
- A competitive-intelligence analyst that returns a typed brief with a source URL per competitor and a field for data it could not find.
- A grounding check in code that flags any cited URL the run never saw.
Lesson 1 of T8.3 guarded an agent that acts. These two agents only read. Their risk is a wrong claim rather than a wrong action. Both are create_agent agents from T7.2 with tools in the T7.1 style. The first answers general research questions. The second specialises the same idea into a retail analyst that watches clothing-store competitors in Bangalore. For Larkspur, the same build would watch competing hosts' pricing pages.
The project adds no new pattern. It shows what changes on the live web: each tool call costs tokens, the data changes between runs, and there is no fixed expected answer. Each check therefore compares the answer with the tool data from the same run.
Architecture
The two builds share one shape and differ in what comes out:
Figure 1: Pieces of both builds. The weather tool and memory belong to the assistant, the typed brief and its check to the analyst.
| Piece | What it does | Built on |
|---|---|---|
| Search-and-read tool | Up to three search results, each page fetched in parallel, converted to text and cut to 4,000 characters. After 15 seconds it falls back to the snippet | T7.1 tool schemas |
| Weather tool | Current weather, trimmed from about 40 fields to 7. An unknown city returns a sentence | T7.1 tool errors as messages |
| Tool-choice check | Reads which tool the model would call for three questions (earnings, weather, arithmetic), running none | T7.1 native tool calls |
| The assistant | A prompt with today's date: search first, cite URLs, admit empty results | T7.2 create_agent |
| Memory | An in-memory checkpointer keyed by a thread id | T4.4 checkpointers |
| Analyst persona | A prompt for owners, marketers, location analysts and investors, and one search tool (five results) | |
| Brief schema | Area, audience, up to five competitors with an evidence URL each, opportunities, risks and data gaps | T7.2 structured output |
| Grounding check | Tests each evidence URL against every URL the search returned in the run |
Table 1: The project's pieces.
Following a follow-up question
The assistant's memory test asks three questions on one thread. The last one only makes sense with the first two:
| Turn | Tool call | What the thread holds after |
|---|---|---|
| 1. How is the weather in Bangalore right now? | Weather for Bangalore | The question, the call, its result and the answer |
| 2. And in Dubai? | Weather for Dubai | Both cities' results |
| 3. Which of the two cities is hotter right now? | None needed | An answer drawn from turns 1 and 2 |
Table 2: Three turns on one thread. The data is live, and no values are fixed.
Ask turn 3 on a fresh thread and the agent cannot know which two cities you mean. It either asks, or picks two cities of its own and answers confidently. We call that a phantom referent: a follow-up answered about something the conversation never named.
The check after turn 3 reads the temperatures from the weather results stored in the thread, finds the hotter city and tests whether the answer's first line names it. It never trusts the model's own comparison.
The analyst's brief gets the same treatment for sources. To see the check fail, the notebook appends one invented competitor to a real brief:
| Competitor entry | Evidence URL | In this run's search results |
|---|---|---|
| Entries from the agent's brief | URLs the search returned | Grounded |
| "Urban Threads Koramangala", busiest 6 to 9 pm on weekends | An example.com footfall report |
Flagged as not from this run's searches |
Table 3: The grounding check on a planted entry. A plausible line from memory has no URL the run saw.
Design choices worth copying
| Choice | Why |
|---|---|
| Today's date in the system prompt | Without it, "most recent quarter" means the latest one in the training data |
| Budget every tool's output | A full page can be tens of thousands of tokens. Three pages of 4,000 characters cap a search at 12,000 |
| Return failures as text | The agent adapts instead of crashing. Lesson 5 of T7.1 names the cost: error monitoring never sees these |
| Check tool choice before the loop | One model call per question, and no tool runs |
| Make data gaps a field | The original prompt asked for "example insights" when data was missing, an invitation to invent footfall figures. The field makes "no data" a visible answer |
| One evidence URL per claim, checked in code | Separates claims from this run's searches from claims out of the model's memory |
Table 4: Six choices and what each one protects against.
Limits
- Live data. Answers differ on every run and there is no expected answer. Test behaviour instead: which tools were called, in what order, and whether the answer agrees with the tool results stored in the run.
- A URL proves sight, not support. The grounding check shows the agent saw the page in this run. It does not show the page says what the brief claims. That needs a person, or a second check that quotes the page.
- Memory lives in the process. The in-memory checkpointer loses every thread on restart.
- Snippets are weaker evidence. A blocked or slow page leaves the agent a two-line snippet.
The assistant needs OpenAI, Tavily and OpenWeatherMap keys, and the analyst needs OpenAI and Tavily.
Run the notebooks
The code for this lesson is in these notebooks. Colab runs one in your browser: install the packages its first cell imports and set the API keys it reads. To run every notebook as written, clone the repository and use its uv environment.
| Notebook | GitHub | Colab |
|---|---|---|
| Research assistant agent | Open on GitHub | Open in Colab |
| Competitive intelligence agent (optional) | Open on GitHub | Open in Colab |
Key Takeaways
- An agent over live sources needs budgets on what each tool returns and failures returned as readable text, or one call floods the context or ends the run.
- Give the agent today's date and memory per thread, then check its answers against the tool data stored in the same run rather than its own summary.
- Make missing data a field of the output and check every cited source against the URLs the run actually retrieved.
Check yourself
1. Why does the research assistant's system prompt include today's date?
The model does not know the date. Without it, "most recent quarter" means the most recent one in its training data, so the agent searches for an old quarter and reports it as current.
2. A brief lists a competitor whose evidence URL never appeared in the run's search results. What does the grounding check conclude, and what does it not prove?
It flags the entry as not from this run's searches. The URL came from the model's memory or was invented, and the claim needs checking by hand. A URL that passes proves only that the agent saw the page in this run, not that the page supports the claim.
3. A fresh thread receives the question of which of the two cities is hotter. What goes wrong, and what fixes it?
With no earlier turns, the agent asks which cities, or picks two of its own and answers confidently. A checkpointer with a thread id carries the Bangalore and Dubai weather results into the follow-up, and the check reads those stored results to confirm the answer.