Agents¶
The agentic_v2.agents package contains typed agent classes for code
generation, review, testing, architecture, and task delegation. Use these
classes when a workflow step needs a model-and-tool loop. Use a normal
StepDefinition for deterministic work that does not need one.
Available classes¶
| Class | Input | Output | Purpose |
|---|---|---|---|
CoderAgent |
CodeGenerationInput |
CodeGenerationOutput |
Generate or revise code |
ReviewerAgent |
CodeReviewInput |
CodeReviewOutput |
Return structured review findings |
TestAgent |
TestGenerationInput |
TestGenerationOutput |
Generate test files |
ArchitectAgent |
ArchitectureInput |
ArchitectureOutput |
Produce a structured system design |
OrchestratorAgent |
OrchestratorInput |
OrchestratorOutput |
Decompose and delegate a task |
agentic_v2.agents exports the agent classes and the architecture, orchestrator and
test-generation models. CodeGenerationInput/Output and CodeReviewInput/Output
live in agentic_v2.contracts.
BaseAgent contract¶
BaseAgent[TInput, TOutput] owns:
- initialization and cleanup;
- state transitions and events;
- bounded model iterations;
- conversation memory;
- model routing;
- tool discovery, validation, approval, and dispatch;
- optional streaming;
- the last result and failure information.
A subclass must implement:
async def _call_model(
self,
messages: list[dict[str, object]],
tools: list[dict[str, object]] | None = None,
) -> dict[str, object]: ...
def _format_task_message(self, task: TInput) -> str: ...
async def _is_task_complete(self, task: TInput, response: str) -> bool: ...
async def _parse_output(self, task: TInput, response: str) -> TOutput: ...
_call_model() returns a mapping with content and, when applicable,
tool_calls. The output parser should reject invalid required data instead of
silently treating incomplete text as success.
Configuration¶
AgentConfig has these defaults:
| Field | Default |
|---|---|
name |
agent |
default_tier |
ModelTier.TIER_2 |
max_tier |
ModelTier.TIER_4 |
max_iterations |
10 |
max_tool_calls_per_turn |
5 |
timeout_seconds |
300.0 |
max_memory_messages |
50 |
max_memory_tokens |
8000 |
enable_streaming |
False |
verbose |
False |
max_iterations prevents an endless model/tool loop.
max_tool_calls_per_turn limits calls from one model response. Neither limit
replaces a timeout on the model client or tool implementation.
Constructor arguments allow injection of a model router, tool registry, and LLM client:
Use dependency injection in tests so a test cannot reach a real provider by accident.
Lifecycle¶
The normal state sequence is:
run() initializes a newly created agent, adds the task to memory, and starts
the bounded execution loop. A model response with tool calls is dispatched,
recorded as tool-role messages, and followed by another iteration. A final
response is parsed into the declared output model.
Cancellation is re-raised after the state changes to cancelled. Other
exceptions set the state to failed and remain visible to the caller.
Call cleanup() when an agent instance is no longer needed. It clears memory,
bound tools, and the current execution context.
Tools¶
During initialization, the base class binds registered tools whose tier is at
or below the agent's default_tier. You can also call bind_tool() or
unbind_tool() explicitly.
Tool calls follow the shared engine path:
- normalize the provider's call shape;
- resolve the bound tool;
- apply the approval gate;
- validate parameters;
- execute the tool;
- serialize and size-limit the result;
- add the result to conversation memory.
Unknown tools, invalid parameters, and execution errors are returned to the model as failed tool results. They are not reported as successful calls.
Tool tier is an availability filter, not an authorization boundary. Configure approval policy and keep tool-side validation in place for operations that read sensitive data or change state.
Memory¶
ConversationMemory stores system, user, assistant, and tool messages. Its
message and token limits are estimates used to control context growth.
Summarization can reduce older content when the limits are exceeded.
Do not use conversation memory as an audit store. Persist workflow events or domain records separately when the data must survive the agent process.
Events¶
Register a synchronous handler with on_event() to observe:
- state changes;
- sent and received messages;
- tool calls and results;
- model activity;
- streaming chunks;
- errors.
Handlers are suitable for metrics and UI updates. Handler exceptions are logged and do not stop the agent, so critical persistence should happen in the workflow or event-storage layer instead.
Capabilities and orchestration¶
Capability mixins declare what an agent is designed to do.
get_agent_capabilities() combines the declarations and
CapabilitySet.score_match() compares them with a subtask's requirements.
OrchestratorAgent uses those scores to assign work. Its plan includes
dependencies, which allow ready subtasks to run in parallel up to
OrchestratorInput.max_parallel. Fallback attempts and exhausted assignments
are recorded rather than converted into successful results.
This assignment is heuristic. Validate the plan, required capabilities, and final output for important work.
The CLI's agentic orchestrate command does not currently expose this dynamic
path through the default LangGraph workflow route.
Put an agent in a workflow¶
agent_to_step() wraps a BaseAgent as a StepDefinition:
from agentic_v2.agents import AgentConfig, CoderAgent, agent_to_step
agent = CoderAgent(config=AgentConfig(name="generate_code"))
step = agent_to_step(agent)
The wrapper reads task from ExecutionContext and writes the agent output as
result. Check that this generic context contract fits the surrounding
workflow before using the wrapper.
Anthropic implementations¶
agentic_v2.agents.implementations contains two optional integrations:
ClaudeAgentextendsBaseAgentand calls the Anthropic Messages API.ClaudeSDKAgentwrapsclaude-agent-sdkand does not extendBaseAgent.
Install the optional dependencies with:
Because ClaudeSDKAgent has a separate lifecycle and permission model, do not
treat it as a drop-in BaseAgent. Its loader reads Markdown definitions with
YAML frontmatter from the bundled definitions directory or
AGENTIC_EXTERNAL_AGENTS_DIR.
Adding an agent¶
- Define Pydantic input and output models.
- Subclass
BaseAgentwith those types. - Implement the four required methods.
- Set finite iteration, tool-call, and timeout limits.
- Add capability mixins only for behavior the class implements.
- Write tests for valid output, malformed model output, tool failure, cancellation, and the iteration limit.
- Add a workflow example only if the agent's context contract is clear.
Source directory:
agentic-workflows-v2/agentic_v2/agents/.