Skip to content

Tools and return types

Give an agent tools to call. One of them can be its reporting tool: the agent calls it to hand back a result, and Run.step() returns it.

Reporting tool

@dataclass(frozen=True)
class Review:
    issues: list[str] = describe("Every problem you found.")

@role(model=Claude.OPUS, accepts=(Request,))
def reviewer_role() -> Role[Review]:
    return Role(
        name="reviewer",
        instructions=prompt_file("prompts/review.md"),
        tools=[reporting_tool("report_review", "Report what you found.", Review)],
    )
review = await run.step(reviewer, request)

describe() tells the agent what a field is for. A field can be a str, bool, int, float or another dataclass, a list or tuple of them, or optional, as X | None. A field with a default can be left out.

If the agent sends a payload that doesn't fit, AGL sends it the problems and asks it to try again. The first payload that fits counts. If the agent never reports, the step raises RoleIncompleteError, and its work is wiped. A role has at most one reporting tool. Without one, Run.step() returns None.

Tools

async def look_up(query: Query) -> ToolResult:
    return ToolResult(text=search_docs(query.text))

tool("look_up", "Search the project's docs.", Query, look_up)

AGL checks the agent's arguments against the payload before your function runs, and sends the agent the problems if they don't fit. Return rejected=True to tell the agent the call failed. If your function raises, the step stops, its work is wiped, and Run.step() raises the same error.

Asking questions

A tool can ask a question in the terminal. Its function shows the question and returns the answer as the tool's result, while the agent waits in the same session. To reach the terminal, the role's function takes the Run as an argument.

def asking(text: str) -> Screen[str]:
    return Screen(text, [TextInput("Answer", str)])

@role(model=Claude.OPUS, accepts=(Request,))
def planner_role(run: Run) -> Role[Plan]:
    async def ask(question: Question) -> ToolResult:
        answer = await run.terminal.show(asking, text=question.text)
        return ToolResult(text=answer)

    return Role(
        name="planner",
        instructions=prompt_file("prompts/plan.md"),
        tools=[
            tool("ask", "Ask a question in the terminal.", Question, ask),
            reporting_tool("report_plan", "Report the plan.", Plan),
        ],
    )

Call the role's function in the workflow, with its Run.

plan = await run.step(planner_role(run), request)

A step that replays runs no agent, so its tools aren't called and their questions aren't asked again.

Reference

agl.sdk.reporting_tool(name, description, payload)

Declares the tool an agent reports a step's result through.

Parameters:

  • name (str) –

    The name the agent calls it by, unique among the role's tools.

  • description (str) –

    The whole of what the agent reads to decide this is the tool it wants.

  • payload (type[P]) –

    The dataclass the step's result is returned as.

Returns:

Raises:

  • InputError –

    An empty name or description, or a payload that can't be saved as JSON.

agl.sdk.ReportingTool dataclass

The tool an agent reports a step's result through.

agl.sdk.describe

describe(text: str, *, default: T) -> T
describe(text: str) -> Any

Describes a payload field to the agent.

Parameters:

  • text (str) –

    The description of the field, shown to the agent beside its name.

  • default (Any, default: MISSING ) –

    The field's default. If omitted, the field is required.

Returns:

  • Any –

    The dataclasses.field to assign to the annotated field.

Raises:

  • InputError –

    text is empty or only whitespace.

agl.sdk.tool(name, description, payload, handler)

Builds a tool an agent can call.

Parameters:

  • name (str) –

    The name the agent calls it by, unique within a Role.

  • description (str) –

    The whole of what the agent reads to decide this is the tool it wants.

  • payload (type[P]) –

    The dataclass the agent's arguments are checked against and built into.

  • handler (Callable[[P], Awaitable[ToolResult]]) –

    The coroutine awaited with the built payload; its result goes back to the agent.

Returns:

  • Tool –

    The tool to put on a Role.

Raises:

  • InputError –

    An empty name or description, or a payload that can't be saved as JSON.

agl.sdk.ToolResult dataclass

What a tool's handler returns to the agent.

text instance-attribute

What the agent reads back from the call.

rejected = False class-attribute instance-attribute

True tells the agent the call failed, False that it succeeded.

agl.sdk.Tool dataclass

A tool an agent can call.

agl.sdk.JsonValue = None | bool | int | float | str | list[JsonValue] | dict[str, JsonValue]

Any value JSON can hold, at any depth.

agl.sdk.RoleIncompleteError

Bases: UpstreamUnexpected

The agent stopped without reporting a result.