Skip to content

Subagents

This tutorial is a continuation of the subagent doc.

In the MapReduce paradigm, parallel workers are recombined in a "reduce" or "aggregation" step. That is no problem for parallem:

from dotenv import load_dotenv

import parallem as pllm
import polars as pl


def subagent_app(orch: pllm.AgentOrchestrator):
    collector = []
    with orch.agent("main-agent") as agt:
        conv = agt.get_msg_state()
        conv.ask_llm(
            "Please name 10 tourist destinations, separated by commas, with no explanation."
        )
        places = [x.strip() for x in conv[-1].final_answer.split(",") if x.strip()]

        # Subagent pattern: simply create another agent from within the agent
        # No special syntax required.
        for i, item in enumerate(places):
            with orch.agent(f"subagent-{i}") as subagt:
                subconv = subagt.get_msg_state()
                subconv.extend(conv)  # Give subagent the parent agent conversation
                subconv.ask_llm(
                    f"Please write 2 paragraphs about {item}.",
                    tools=[pllm.tools.WebSearchTool()],
                )
                collector.append(
                    {
                        "place": item,
                        "description": subconv[-1].final_answer,
                    }
                )
    return pl.DataFrame(collector)


def aggregation_app(orch: pllm.AgentOrchestrator, df: pl.DataFrame):
    all_descriptions = "\n\n".join(df["description"].to_list())
    with orch.agent("agg-agent") as agt:
        conv = agt.get_msg_state()
        conv.ask_llm(
            "I like both urban and countryside exploration. Can you pick the top 3 destinations from the choices below? Please give a brief explanation.",
            all_descriptions,
        )
        return conv[-1].final_answer


if __name__ == "__main__":
    load_dotenv()
    with pllm.resume_directory(".pllm/example/subagents") as orch:
        df = subagent_app(orch)
        with pl.Config(set_ascii_tables=True):
            print(df)
        summary = aggregation_app(orch, df)
        print(summary)

Output:

[INFO] Resuming with session_id=7
shape: (10, 2)
+-----------+-----------------------------------+
| place     | description                       |
| ---       | ---                               |
| str       | str                               |
+===============================================+
| Paris     | Paris is a city of light and l... |
| Tokyo     | Tokyo is a city where centurie... |
| New York  | New York City, often simply ca... |
| Rome      | Rome, the capital of Italy, is... |
| Barcelona | Barcelona sits on the northeas... |
| Istanbul  | Istanbul is a city where two c... |
| Bangkok   | Bangkok, Thailand's energetic ... |
| Sydney    | Sydney is Australia's largest ... |
| Kyoto     | Kyoto, in Japan's Kansai regio... |
| Bali      | Bali, Indonesia's famed island... |
+-----------+-----------------------------------+
Top 3 destinations for a mix of urban and countryside exploration:

- Kyoto, Japan — A city steeped in tradition with easy access to nature: Arashiyama’s bamboo grove, temple gardens, and the Philosopher’s Path let you wander from refined urban culture into serene countryside scenes, all in one trip.

- Sydney, Australia — A world-class city with immediate nature on your doorstep: iconic harbors and beaches (Bondi, Manly) plus coastal trails and nearby national parks give you city vibes and outdoor escapes in one headlining destination.

- Bali, Indonesia — Rich rural landscapes paired with vibrant culture: terraced rice paddies in Ubud, volcanic hikes (Mount Batur), and stunning temples sit alongside beach towns and lively markets, offering both countryside immersion and island-city flavor.