Skip to content

14. Search the web

Build a research agent: ask it anything, and it searches the web, reads what it finds, and answers with its sources. In tutorial 13 the agent searched your own documents. This one looks up everything else. Maitento has web search and page reading built in, so there’s no connector to write.

You’ll learn

  • how to give an agent web search and page reading, with two switches
  • where the pages it reads are kept, and for how long
  • what the agent does when a page can’t be read

New concepts: web tools · temporary knowledge bases

Builds on: 13. Your documents

Time: ~10 min · Cost: under $0.01 on DeepSeek per question (about ten thousand tokens), plus each search and page read, which Maitento meters on the Usage page · Needs: nothing beyond Get started

Two switches on the agent give it the web:

maitento/agents/researcher.mtr
model = @ref("deepseek.deepseek-v4-flash")
prompt = "You are a careful researcher. Search the web, read the most relevant pages, and only state what the pages support."
summary = "Researches questions on the web"
// Give the agent Maitento's built-in web tools. Every search and page fetch is metered.
allowWebSearch = true
allowWebFetch = true
SwitchThe agent gets
allowWebSearch = trueWebSearch: up to 5 queries a call, with titles, links and snippets back
allowWebFetch = trueWebFetch, which reads up to 10 pages a call as text, and WebRead, which reads on through a long one

These are tools like the ones you wrote in tutorials 8 and 13, but Maitento provides them. WebFetch reads PDFs, Office documents and images too, not only web pages.

Research takes a while, so the interaction warns the agent at two minutes and stops it at four. Its task, in prompts/research-task.md, asks for one paragraph and the list of pages used:

maitento/interactions/one-shot/research.mtr
agent = {
name = "Researcher"
agent = @ref("researcher")
}
task = @prompt("research-task")
inputs.prompts = [
{
name = "question"
description = "What to research"
required = true
}
]
// Research can take a while: warn the agent at 2 minutes and stop at 4.
duration.warn = "00:02:00"
duration.max = "00:04:00"

The agent needs somewhere to keep the pages it reads, so it can read a long one in parts. That’s a knowledge base, like the handbook in tutorial 13, but a temporary one: its files are never indexed, and each is deleted after fileLifetimeHours (here, 24). A run whose agent can read pages must have one, so the script makes it once and passes it as webDownloadPartitionId:

import { Maitento } from "@maitento/sdk";
const mt = new Maitento();
await mt.deploy("../maitento");
// Pages the agent reads are kept for a day in a temporary knowledge base. Make one once and reuse it.
const partitions = await mt.partitions.list();
const downloads = partitions.find((p) => p.name === "web-downloads" && p.isTemporary)
?? (await mt.partitions.create({ name: "web-downloads", isTemporary: true, fileLifetimeHours: 24 }));
const question = process.argv[2] ?? "What is the tallest building in Lisbon, and how tall is it?";
const run = await mt.interactions.run("research", { inputs: { question }, webDownloadPartitionId: downloads.id });
console.log(run.output);
// Every search and every page read is a tool call in the run.
console.log("\nWhat the agent did:");
for (const call of run.context.toolCall) console.log(`- ${call.functionName} ${call.argumentsJson}`);
The tallest building in Lisbon is the Vasco da Gama Tower (Torre Vasco da Gama), a 160-metre (525 ft) lattice tower
with a 22-storey skyscraper section in Parque das Nações, built for Expo '98; a 72-metre hotel was added to its base
in 2012. …
- https://en.wikipedia.org/wiki/Vasco_da_Gama_Tower
- https://en.wikipedia.org/wiki/List_of_tallest_buildings_in_Lisbon
- https://en.wikipedia.org/wiki/List_of_tallest_buildings_in_Portugal
What the agent did:
- WebSearch {"queries":"tallest building in Lisbon height","page":"1"}
- WebFetch {"urls":"https://en.wikipedia.org/wiki/List_of_tallest_buildings_in_Lisbon\nhttps://en.wikipedia.org/wiki/Vasco_da_Gama_Tower","maxChars":"4000"}

What happened:

  1. The agent searched the web once, and got back titles, links and snippets.
  2. It read the two most useful pages in one WebFetch call. Maitento downloaded them, turned them into text and kept them in web-downloads.
  3. It answered from those pages, and listed them.

Open the run in the portal’s Runs. The Conversation tab shows each search and page read with what came back, and the Web tab shows which knowledge base the downloads went to.

Done when: the answer names the tower and lists the pages it used.

The pages are files in the temporary knowledge base. List the newest:

import { Maitento } from "@maitento/sdk";
const mt = new Maitento();
const downloads = (await mt.partitions.list()).find((p) => p.name === "web-downloads" && p.isTemporary);
if (!downloads) throw new Error("Run research.ts first.");
// The newest files first. Each one is a page an agent read, with the run that read it.
const page = await mt.partitions.files(downloads.id, { limit: 5 });
for (const file of page.items) {
console.log(`${file.webSource?.url}\n read by run ${file.webSource?.processId}, deleted after ${file.expiresAt?.toISOString()}`);
}
https://en.wikipedia.org/wiki/List_of_tallest_buildings_in_Lisbon
read by run 42b519e3-0def-4eec-9cea-2775d23607da, deleted after 2026-10-27T19:31:41.452Z
https://en.wikipedia.org/wiki/Vasco_da_Gama_Tower
read by run 42b519e3-0def-4eec-9cea-2775d23607da, deleted after 2026-10-27T19:31:40.337Z
…

Each file remembers its address and the run that read it. A page read again within the hour comes from here, at no charge, so if you ask twice, the second run reuses the first one’s files. In the portal, open Knowledge, then web-downloads.

Done when: you see the pages from step 1, each deleted a day after it was read.

On GitHub: examples/14-web-research · TypeScript · Python · C#

All the files in this project (3)
model = @ref("deepseek.deepseek-v4-flash")
prompt = "You are a careful researcher. Search the web, read the most relevant pages, and only state what the pages support."
summary = "Researches questions on the web"
// Give the agent Maitento's built-in web tools. Every search and page fetch is metered.
allowWebSearch = true
allowWebFetch = true

Ask about a page that doesn’t exist:

node research.ts "Read https://www.maitento-tutorial-no-such-site.com/pricing and tell me what it says."
I attempted to read the page at https://www.maitento-tutorial-no-such-site.com/pricing, but it could not be reached,
so I cannot report what it says; the address appears not to resolve to a live site. …
What the agent did:
- WebFetch {"urls":"https://www.maitento-tutorial-no-such-site.com/pricing"}

The fetch failed, and the tool told the agent so. In the run’s Conversation, the WebFetch result says Failed (target_unreachable): The site could not be reached. The agent reported that, instead of inventing a price list, because its prompt says to state only what the pages support.

Addresses on private networks fail the same way. Maitento never fetches from them, so an agent can’t read your own localhost: the same reason tutorial 9 needed a tunnel.

  • The run won’t start: [api:web:download-partition-required]. An agent that can read pages needs a temporary knowledge base for its run. Pass webDownloadPartitionId (--web-download-partition in the CLI), or set a default once: in the portal’s Settings → Web tools, with mt web-access-set web-downloads, or with PUT /tenants/self/web-access and {"defaultWebDownloadPartitionId": "<id>"}.
  • Creating web-downloads fails with a 403. As in tutorial 13, only tenant admins can create knowledge bases. Ask one to create it, or to set the default.
  • Searches fail or time out. Web search goes through a search provider, which can be slow. The agent gets the error, and may read pages it already knows about instead. Try again later. Reading pages still works.
  • The run stops at four minutes. That’s duration.max in the interaction. Ask a narrower question, or raise it.

You built:

  • a research agent that searches the web, reads the best pages and cites them;
  • a script that shows every search and page read the agent made;
  • a temporary knowledge base that keeps those pages for a day.

The concepts:

  • Web tools: allowWebSearch and allowWebFetch give an agent WebSearch, WebFetch and WebRead, with no connector to write. Every search and page is metered.
  • Temporary knowledge base: where a run keeps the pages it read. Its files are never indexed, and are deleted after fileLifetimeHours.

Challenge: make a page summariser. Run the same research interaction on “Summarise https://en.wikipedia.org/wiki/Vasco_da_Gama_Tower in three sentences.”, with web search switched off for that run only, so the agent can read but not search. Don’t change the project. Hint: webAccessOverrides, in Web tools. The solution is in examples/14-web-research/solution.

Next: 15. Files for agents lets an agent read and write files.