> For the complete documentation index, see [llms.txt](https://docs.midio.com/midio-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.midio.com/midio-docs/tutorials/concurrent-research-agent.md).

# Concurrent Research Agent

In this guide we will create a **Research Agent** that takes a given user question and automatically does several things:

* Breaks the question into discrete research tasks
* Launches multiple sub‑agents that search the web in parallel
* Collects and deduplicates their findings
* Produces a concise, well‑structured final answer

We leverage the built‑in concurrency primitives such as **`Std.Spawn Processes from List`** and **`Std.MergeAll`**.

{% embed url="<https://youtu.be/_KOrmVXIfpc@>" %}

***

## High‑Level Architecture

Our solution is split into three main phases:

| Phase                          | Responsibility                                                                                                                                                             |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **1) Task Planning**           | Turn the user prompt into a list of concrete research tasks                                                                                                                |
| **2) Concurrent Research**     | <p>Spawn one child process per task, each of which:<br>• Transforms its task into a search engine query<br>• Queries the Tavily Search API<br>• Summarizes the results</p> |
| **3) Aggregation & Synthesis** | Merge all child outputs, filter failures, and craft the master answer                                                                                                      |

<figure><picture><source srcset="/files/HFNgP5kHnd6xm1tShJkC" media="(prefers-color-scheme: dark)"><img src="/files/wuEETR4kX2UV7itdrY5t" alt=""></picture><figcaption></figcaption></figure>

## Phase 1: Task Planning

The master agent needs a **machine‑readable** list of subtasks. Midio’s `Chat Complete` (avilable in the open-ai [package](/midio-docs/package-manager.md)) node supports [**structured output**](/midio-docs/guides/building-agents.md#structured-output) which lets an LLM return valid JSON that is guaranteed to match a user‑defined schema.

1. We can force the LLM to output a custom schema by providing an object like the following to the `response format` input. We also need to mention in the system prompt that we expect JSON output.

   ```json
   {
       name: "AgentDeployments",
       description: "A list of agents to deploy, which agents and what their task is.",
       schema: {
           agents: [
               {
                   name: "WebResearchAgent",
                   task: "A description of what the agent should research."
               }
           ]
       }
   }
   ```
2. To the `system message` input, we pass the following

   ```
   You are a senior researcher.
   Break the following question into distinct, non‑overlapping web research tasks.
   Spawn at least 3 tasks, at most 6.
   Return an array of objects that conforms to the provided JSON Schema.
   ```
3. And the users query is passed directly to the `user message` input.

<figure><picture><source srcset="/files/bHi8bMb11SVA3lCQy5NJ" media="(prefers-color-scheme: dark)"><img src="/files/w3AHvJUdbT1M4IEhW2w8" alt=""></picture><figcaption></figcaption></figure>

## Phase 2: Concurrent Research

### 1. Spawning Child Processes

We can spawn a process for each generated task by using the [**`Std.Spawn Processes from List`**](/midio-docs/built-in-nodes/core-std.md#spawn-processes-from-list) node, and connect the agents field from the response of the `Generate Research Tasks` function.

We can then connect the `child spawned` trigger (which is triggered once per item) to the `Search Web and Summarize` function.

### Inside the Research Sub‑Agent

The sub‑agent receives a single task object. It first searches the web, and then summarizes its findings. Here is an example of a task object. Generated by the previous step.

```json
{ "task": "Compare the energy density of li‑ion vs. sodium‑ion batteries" }
```

### 2. Craft a Search Query

A `Chat Complete` call turns the (sometimes verbose) task into a concise search‑engine query using the following prompt:

{% code overflow="wrap" %}

```
You are given a task, and you should convert it into a search query that you might put into a google search, and expect resources that provide information to answer the task.
```

{% endcode %}

The response is then piped directly into the query input of a [`Tavily.Search`](http://tavily.search) node. You can add the Tavily package using the package manager in the editor.

<figure><picture><source srcset="/files/yWhFSuMOBYd1Wyfwtffn" media="(prefers-color-scheme: dark)"><img src="/files/gFRC3byRtsb3n3BjScDY" alt=""></picture><figcaption></figcaption></figure>

### 3. Convert Pages to Markdown

For each search result we get back from Tavily, we convert it to Markdown using the `Html to Markdown` function in the **web-tools** [package](/midio-docs/package-manager.md). We use `Spawn Processes from List` here as well to do this task concurrently.

{% hint style="info" %}
Tavily supports returning a summary of the page results automatically, but we do it manually here for demo purposes.
{% endhint %}

<figure><picture><source srcset="/files/4S4cFhWRBc7aNEw9eZEK" media="(prefers-color-scheme: dark)"><img src="/files/5hIYSLSnOZ2kcU5Et84H" alt=""></picture><figcaption></figcaption></figure>

### 4. Summarize Findings

Another `Chat Complete` node receives both the **task** and **raw search results**, and is prompted to combine them into a task summary.

{% code overflow="wrap" %}

```
You are a web research summarizer agent.

The user will provide you the result of a web search, as well a description of a research task that you are tasked with addressing.

Use the web search results and task description to create a summary. Expect the output of your summary to be used by another agent which will combine it with other search results performed by other agents similar to you.
```

{% endcode %}

## Phase 3: Aggregation & Synthesis

The final step is to combine all the research data and produce a single response. We do this with another LLM call, with the following prompt:

{% code overflow="wrap" %}

```
You are a web research summarizer agent.

The user will provide you the result of a web search, as well a description of a research task that you are tasked with addressing.

Use the web search results and task description to create a summary. Expect the output
of your summary to be used by another agent which will combine it with other search results performed by other agents similar to you.
```

{% endcode %}

For its user message we pass the search results and the original user query using the following expression string template:

```
"<data>
  ${data}
</data>
<user_query>
  ${userQuery}
</user_query>"
```

<figure><picture><source srcset="/files/L73VfO7y4NqUiAhwP8OU" media="(prefers-color-scheme: dark)"><img src="/files/xeaEAKx5iwcEb5lJ0I1r" alt=""></picture><figcaption></figcaption></figure>

{% hint style="info" %}
We also make sure to remove any null values from the inputs list, which might appear if a research agent fails for some reason.
{% endhint %}

### Exposing the Agent as a REST API

To make the agent callable from any frontend (e.g. Lovable, curl, or your own app), we can hook it up to an `Endpoint` event node like this:

<figure><picture><source srcset="/files/RimQeDTYFoNFYKwhbBxX" media="(prefers-color-scheme: dark)"><img src="/files/wJmTuIoogbHUqa6WhoF6" alt=""></picture><figcaption></figcaption></figure>

And return a response like this:

<figure><picture><source srcset="/files/qyRT5GHtD2kNiTSh2Z7L" media="(prefers-color-scheme: dark)"><img src="/files/KXxN6PHFJ1Zz5CDZnZtz" alt=""></picture><figcaption></figcaption></figure>

Your research micro service is now live at: [`https://<your-project-name>.midio.dev:3001/research`](https://github.com/midio-code/midio-docs/blob/master/tutorials/https:/%3Cyour-project-name%3E.midio.dev:3001/research?query/README.md) and expecting a single query parameter named `query`. You can call it like this using curl, for example:

{% code overflow="wrap" %}

```
curl https://<your-project-name>.midio.dev:3001/research?query=what+is+going+on+in+tech
```

{% endcode %}
