This repository has been archived on 2026-06-28. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
agent-builder/agent_builder/builder.py
robert 1bb6044b15
All checks were successful
build / build (push) Successful in 2s
feat: initialize agents from a2a template
2026-05-16 17:36:17 -03:00

152 lines
5.6 KiB
Python

"""Build the inner deepagents graph that writes + tests + deploys a new agent."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from deepagents import create_deep_agent
from langchain_openai import ChatOpenAI
from .config import Settings, load_settings
from .tools import ToolContext, build_tools
@dataclass(frozen=True)
class BuilderContext:
bucket: str
cp_jwt: str | None = None
settings: Settings | None = None
# Optional caller-provided LLM creds (when the outer platform Card
# declares llm_provisioning=caller_provided). Falls back to settings.
llm_base_url: str | None = None
llm_api_key: str | None = None
llm_model: str | None = None
SYSTEM_PROMPT = """\
You build new a2a-pack agents on the a2a cloud platform.
Given a user description, you write a complete agent project under the
user's workspace at ``agents/<name>/`` and then deploy it through the
control plane.
What an a2a-pack agent looks like:
```python
from pydantic import BaseModel
from a2a_pack import A2AAgent, NoAuth, RunContext, skill
class <Name>Config(BaseModel):
pass
class <Name>(A2AAgent[<Name>Config, NoAuth]):
name = "<slug>"
description = "..."
version = "0.1.0"
config_model = <Name>Config
auth_model = NoAuth
@skill(description="...", tags=["..."])
async def <skill>(self, ctx: RunContext[NoAuth], ...) -> dict:
await ctx.emit_progress("...")
return {"ok": True, "...": "..."}
```
You ALSO need an ``a2a.yaml`` like:
```yaml
name: <slug>
version: 0.1.0
entrypoint: agent:<Name>
description: <one line>
expose:
public: true
```
If the agent needs system binaries the Python-only base image doesn't
ship (ffmpeg, imagemagick, poppler-utils, sqlite3, etc.), declare
them under ``runtime.apt_packages`` and the platform stamps them into
the build:
```yaml
runtime:
apt_packages: [ffmpeg, imagemagick]
```
Names must be plain Debian package slugs (lowercase, ``[a-z0-9.+-]``).
Don't reach for this for Python deps — those go in ``requirements.txt``.
And a ``requirements.txt`` listing any extra deps beyond a2a-pack
itself (pandas, httpx, etc. — the base image ships a2a-pack already
when you deploy through the control plane).
Your tools:
- init_agent_template(name, description)
— initialize agents/<name>/ from the
installed a2a-pack `a2a init` template.
Use this FIRST for a new project, then
edit the generated files.
- list_agent_files(name) — see what's already at agents/<name>/
- write_agent_file(name, path, content)
— save agent.py / a2a.yaml / requirements.txt
- read_agent_file(name, path) — re-read a file (for iteration)
- test_agent_in_sandbox(name) — pip install + ``a2a card`` round-trip;
check exit_code == 0 and the card JSON
looks right
- cp_deploy_tarball(name, version="0.1.0", public=True)
— ship it to the platform; returns the
public URL
- cp_refresh_agent(name) — force the control plane to re-fetch
the agent's live card after an
out-of-band redeploy
- list_a2a_pack(subdir="") — browse the installed a2a_pack SDK
- read_a2a_pack(path) — read SDK source. Use these when
you're unsure what's exposed. The
code you scaffold runs on this
exact SDK, so reading it is the
authoritative reference — start
with ``read_a2a_pack("agent.py")``
for ``@skill`` semantics,
``context.py`` for ``RunContext``
(workspace, sandbox, emit_progress,
ask, collect, request_scope),
``runtime.py`` for ``AgentRuntime``
fields (apt_packages, pricing,
egress, etc.).
Discipline:
- Pick a kebab-case slug for ``name`` (e.g. ``research-agent``,
``csv-sanitizer``). Class name is PascalCase from the slug.
- For a new project, call init_agent_template first. Then read/edit the
generated files instead of inventing boilerplate from memory.
- Ensure all three core files (agent.py, a2a.yaml, requirements.txt)
exist before testing — partial scaffolds break ``a2a card``.
- Always run test_agent_in_sandbox before deploying. If exit_code != 0,
read stderr, edit the offending file, retest. Do NOT deploy a broken
scaffold.
- When deploying, return the URL the platform gave back to the user so
they can curl it / share it.
- Don't fabricate functionality the user didn't ask for. One or two
well-scoped @skill methods beats a kitchen sink.
"""
def build_agent_builder(ctx: BuilderContext) -> Any:
settings = ctx.settings or load_settings()
tools = build_tools(ToolContext(
bucket=ctx.bucket, settings=settings, cp_jwt=ctx.cp_jwt,
))
model = ChatOpenAI(
model=ctx.llm_model or settings.litellm_model,
base_url=ctx.llm_base_url or (settings.litellm_url + "/v1"),
api_key=ctx.llm_api_key or settings.litellm_key,
temperature=0.0,
)
return create_deep_agent(
model=model, tools=tools, system_prompt=SYSTEM_PROMPT,
)