State shapes
Coevolved steps accept and return a “state” object. In practice, you’ll use one of:
- A Python
dict (most common)
- A Pydantic
BaseModel (when you want strong contracts)
- A custom object (supported, but easier to get wrong)
Many prebuilt utilities (like the ReAct agent) assume dict-like state.
Step supports validating both inputs and outputs via Pydantic models:
- input_schema: validate the incoming state
- output_schema: validate what your function returns
If validation fails, Step raises a ValueError with details from Pydantic.
Updating state safely
When your state is a dict, a simple pattern keeps steps predictable:
def step_fn(state: dict) -> dict:
# Avoid in-place mutation; return a new dict.
return {**state, "field": "value"}
When your state is a Pydantic model, return an updated copy:
from pydantic import BaseModel
class State(BaseModel):
count: int = 0
def inc(state: State) -> State:
return state.model_copy(update={"count": state.count + 1})
Treat “state” as immutable by convention. This makes parallel composition safer and debugging easier.
Schema design guidelines
- Prefer a small number of stable fields rather than dozens of loosely named keys.
- Use nested models when fields naturally group (e.g.
usage, inputs, outputs).
- Make “optional until set” fields explicitly optional (
Optional[...]) rather than missing keys.
- For agent loops, define a clear contract for “stop condition fields” (e.g.
final).
Next steps