Skip to content

Builder

LLM Agent Builder.

LLMAgentBuilder

A builder for LLM Agents.

Attributes:

Name Type Description
llm LLM | None

The backbone LLM for the agent.

tools list[Tool]

Tools to equip the agent with.

templates LLMAgentTemplates

Prompt templates for the agent.

mcp_providers list[MCPToolProvider]

MCP providers for tool discovery.

memories list[BaseMemory]

Memory backends for the agent.

Source code in src/llm_agents_from_scratch/agent/builder.py
class LLMAgentBuilder:
    """A builder for LLM Agents.

    Attributes:
        llm (LLM | None): The backbone LLM for the agent.
        tools (list[Tool]): Tools to equip the agent with.
        templates (LLMAgentTemplates): Prompt templates for the agent.
        mcp_providers (list[MCPToolProvider]): MCP providers for tool
            discovery.
        memories (list[BaseMemory]): Memory backends for the agent.
    """

    def __init__(
        self,
        llm: LLM | None = None,
        tools: list[Tool] | None = None,
        templates: LLMAgentTemplates = default_templates,
        mcp_providers: list[MCPToolProvider] | None = None,
        # added in ch07
        memories: list[BaseMemory] | None = None,
    ) -> None:
        """Initialize an LLMAgentBuilder.

        All parameters can also be set via fluent `with_*` builder methods for
        chained configuration.

        Examples:
            Fluent style::

                agent = await (
                    LLMAgentBuilder()
                    .with_llm(llm)
                    .with_tool(my_tool)
                    .with_mcp_provider(provider)
                    .with_memory(my_memory)
                    .build()
                )

            Direct params::

                agent = await LLMAgentBuilder(
                    llm=llm,
                    tools=[my_tool],
                    mcp_providers=[provider],
                    memories=[my_memory],
                ).build()

        Args:
            llm (LLM | None, optional): The backbone LLM for the agent.
                Required before calling `build()`. Defaults to None.
            tools (list[Tool] | None, optional): Initial list of tools to
                equip the agent with. Defaults to None.
            templates (LLMAgentTemplates, optional): Prompt templates for
                the agent. Defaults to `default_templates`.
            mcp_providers (list[MCPToolProvider] | None, optional): MCP
                providers for tool discovery. Tools are fetched during
                `build()`. Defaults to None.
            memories (list[BaseMemory] | None, optional): Memory backends
                for the agent. No default implementation is provided — the
                caller must supply a concrete subclass. Defaults to None.
        """
        self.llm = llm
        self.templates = templates
        self.mcp_providers = mcp_providers or []
        self.tools = tools or []
        # added in ch07
        self.memories: list[BaseMemory] = memories or []

    def with_llm(self, llm: LLM) -> Self:
        """Set llm of builder."""
        self.llm = llm
        return self

    def with_tool(self, tool: Tool) -> Self:
        """Add tool to builder."""
        self.tools.append(tool)
        return self

    def with_tools(self, tools: list[Tool]) -> Self:
        """Add tools to builder."""
        self.tools.extend(tools)
        return self

    def with_templates(self, templates: LLMAgentTemplates) -> Self:
        """Set templates of builder."""
        self.templates = templates
        return self

    def with_mcp_provider(self, provider: MCPToolProvider) -> Self:
        """Add mcp provider to builder."""
        self.mcp_providers.append(provider)
        return self

    def with_mcp_providers(self, providers: list[MCPToolProvider]) -> Self:
        """Add mcp providers to builder."""
        self.mcp_providers.extend(providers)
        return self

    def with_memory(self, memory: BaseMemory) -> Self:
        """Add a memory backend to builder. Added in Chapter 7."""
        self.memories.append(memory)
        return self

    def with_memories(self, memories: list[BaseMemory]) -> Self:
        """Add memory backends to builder. Added in Chapter 7."""
        self.memories.extend(memories)
        return self

    async def build(self) -> LLMAgent:
        """Build an LLMAgent with configured tools and MCP providers.

        Discovers tools from all registered MCP providers concurrently,
        combines them with manually added tools, and returns a configured
        LLMAgent.

        This is the recommended pattern for building agents with MCP tools.
        Alternatively, you can manually discover tools and pass them directly:

            provider = MCPToolProvider(name="github", url="...")
            tools = await provider.get_tools()
            agent = LLMAgent(llm=llm, tools=tools)

        Returns:
            LLMAgent: The configured agent with all tools.

        Raises:
            LLMAgentBuilderError: If `llm` is not set.
        """
        if not self.llm:
            raise LLMAgentBuilderError("`llm` must be set")

        # discover tools for mcp providers
        coros = []
        for provider in self.mcp_providers:
            coro = provider.get_tools()
            coros.append(coro)

        discovered_tools: list[list[MCPTool]] = await asyncio.gather(*coros)
        mcp_tools = list(chain.from_iterable(discovered_tools))

        return LLMAgent(
            llm=self.llm,
            tools=self.tools + mcp_tools,
            templates=self.templates,
            memories=self.memories,  # added in ch07
        )

__init__

__init__(
    llm=None,
    tools=None,
    templates=default_templates,
    mcp_providers=None,
    memories=None,
)

Initialize an LLMAgentBuilder.

All parameters can also be set via fluent with_* builder methods for chained configuration.

Examples:

Fluent style::

agent = await (
    LLMAgentBuilder()
    .with_llm(llm)
    .with_tool(my_tool)
    .with_mcp_provider(provider)
    .with_memory(my_memory)
    .build()
)

Direct params::

agent = await LLMAgentBuilder(
    llm=llm,
    tools=[my_tool],
    mcp_providers=[provider],
    memories=[my_memory],
).build()

Parameters:

Name Type Description Default
llm LLM | None

The backbone LLM for the agent. Required before calling build(). Defaults to None.

None
tools list[Tool] | None

Initial list of tools to equip the agent with. Defaults to None.

None
templates LLMAgentTemplates

Prompt templates for the agent. Defaults to default_templates.

default_templates
mcp_providers list[MCPToolProvider] | None

MCP providers for tool discovery. Tools are fetched during build(). Defaults to None.

None
memories list[BaseMemory] | None

Memory backends for the agent. No default implementation is provided — the caller must supply a concrete subclass. Defaults to None.

None
Source code in src/llm_agents_from_scratch/agent/builder.py
def __init__(
    self,
    llm: LLM | None = None,
    tools: list[Tool] | None = None,
    templates: LLMAgentTemplates = default_templates,
    mcp_providers: list[MCPToolProvider] | None = None,
    # added in ch07
    memories: list[BaseMemory] | None = None,
) -> None:
    """Initialize an LLMAgentBuilder.

    All parameters can also be set via fluent `with_*` builder methods for
    chained configuration.

    Examples:
        Fluent style::

            agent = await (
                LLMAgentBuilder()
                .with_llm(llm)
                .with_tool(my_tool)
                .with_mcp_provider(provider)
                .with_memory(my_memory)
                .build()
            )

        Direct params::

            agent = await LLMAgentBuilder(
                llm=llm,
                tools=[my_tool],
                mcp_providers=[provider],
                memories=[my_memory],
            ).build()

    Args:
        llm (LLM | None, optional): The backbone LLM for the agent.
            Required before calling `build()`. Defaults to None.
        tools (list[Tool] | None, optional): Initial list of tools to
            equip the agent with. Defaults to None.
        templates (LLMAgentTemplates, optional): Prompt templates for
            the agent. Defaults to `default_templates`.
        mcp_providers (list[MCPToolProvider] | None, optional): MCP
            providers for tool discovery. Tools are fetched during
            `build()`. Defaults to None.
        memories (list[BaseMemory] | None, optional): Memory backends
            for the agent. No default implementation is provided — the
            caller must supply a concrete subclass. Defaults to None.
    """
    self.llm = llm
    self.templates = templates
    self.mcp_providers = mcp_providers or []
    self.tools = tools or []
    # added in ch07
    self.memories: list[BaseMemory] = memories or []

with_llm

with_llm(llm)

Set llm of builder.

Source code in src/llm_agents_from_scratch/agent/builder.py
def with_llm(self, llm: LLM) -> Self:
    """Set llm of builder."""
    self.llm = llm
    return self

with_tool

with_tool(tool)

Add tool to builder.

Source code in src/llm_agents_from_scratch/agent/builder.py
def with_tool(self, tool: Tool) -> Self:
    """Add tool to builder."""
    self.tools.append(tool)
    return self

with_tools

with_tools(tools)

Add tools to builder.

Source code in src/llm_agents_from_scratch/agent/builder.py
def with_tools(self, tools: list[Tool]) -> Self:
    """Add tools to builder."""
    self.tools.extend(tools)
    return self

with_templates

with_templates(templates)

Set templates of builder.

Source code in src/llm_agents_from_scratch/agent/builder.py
def with_templates(self, templates: LLMAgentTemplates) -> Self:
    """Set templates of builder."""
    self.templates = templates
    return self

with_mcp_provider

with_mcp_provider(provider)

Add mcp provider to builder.

Source code in src/llm_agents_from_scratch/agent/builder.py
def with_mcp_provider(self, provider: MCPToolProvider) -> Self:
    """Add mcp provider to builder."""
    self.mcp_providers.append(provider)
    return self

with_mcp_providers

with_mcp_providers(providers)

Add mcp providers to builder.

Source code in src/llm_agents_from_scratch/agent/builder.py
def with_mcp_providers(self, providers: list[MCPToolProvider]) -> Self:
    """Add mcp providers to builder."""
    self.mcp_providers.extend(providers)
    return self

with_memory

with_memory(memory)

Add a memory backend to builder. Added in Chapter 7.

Source code in src/llm_agents_from_scratch/agent/builder.py
def with_memory(self, memory: BaseMemory) -> Self:
    """Add a memory backend to builder. Added in Chapter 7."""
    self.memories.append(memory)
    return self

with_memories

with_memories(memories)

Add memory backends to builder. Added in Chapter 7.

Source code in src/llm_agents_from_scratch/agent/builder.py
def with_memories(self, memories: list[BaseMemory]) -> Self:
    """Add memory backends to builder. Added in Chapter 7."""
    self.memories.extend(memories)
    return self

build async

build()

Build an LLMAgent with configured tools and MCP providers.

Discovers tools from all registered MCP providers concurrently, combines them with manually added tools, and returns a configured LLMAgent.

This is the recommended pattern for building agents with MCP tools. Alternatively, you can manually discover tools and pass them directly:

provider = MCPToolProvider(name="github", url="...")
tools = await provider.get_tools()
agent = LLMAgent(llm=llm, tools=tools)

Returns:

Name Type Description
LLMAgent LLMAgent

The configured agent with all tools.

Raises:

Type Description
LLMAgentBuilderError

If llm is not set.

Source code in src/llm_agents_from_scratch/agent/builder.py
async def build(self) -> LLMAgent:
    """Build an LLMAgent with configured tools and MCP providers.

    Discovers tools from all registered MCP providers concurrently,
    combines them with manually added tools, and returns a configured
    LLMAgent.

    This is the recommended pattern for building agents with MCP tools.
    Alternatively, you can manually discover tools and pass them directly:

        provider = MCPToolProvider(name="github", url="...")
        tools = await provider.get_tools()
        agent = LLMAgent(llm=llm, tools=tools)

    Returns:
        LLMAgent: The configured agent with all tools.

    Raises:
        LLMAgentBuilderError: If `llm` is not set.
    """
    if not self.llm:
        raise LLMAgentBuilderError("`llm` must be set")

    # discover tools for mcp providers
    coros = []
    for provider in self.mcp_providers:
        coro = provider.get_tools()
        coros.append(coro)

    discovered_tools: list[list[MCPTool]] = await asyncio.gather(*coros)
    mcp_tools = list(chain.from_iterable(discovered_tools))

    return LLMAgent(
        llm=self.llm,
        tools=self.tools + mcp_tools,
        templates=self.templates,
        memories=self.memories,  # added in ch07
    )