-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Question
I'm defining a test state and a route inside our FastAPI backend via pydantic_ai.ag_ui.run_ag_ui
class TestState(BaseModel):
userName: str
counter: int
class TestStateSnapshot(BaseModel):
testState: TestState = Field(
default_factory=lambda: TestState(userName="Test User", counter=1),
description="The current test state",
)@router.post("/chat")
async def run_agent(request: Request) -> Response:
# [boilerplate]
state = (
TestStateSnapshot.model_validate(run_input.state)
if run_input.state
else TestStateSnapshot()
)
event_stream = run_ag_ui(
main_agent,
run_input,
accept=accept,
on_complete=on_request_complete,
deps=StateDeps(state),
)
return StreamingResponse(event_stream, media_type=accept)and I have a dummy-agent defining some tools and a sub-agent for agent orchestration:
main_agent = Agent(
name="Routing Agent",
model=model,
system_prompt=MAIN_AGENT_PROMPT,
deps_type=StateDeps[TestStateSnapshot],
)
@main_agent.instructions
def beg_llm_to_update_state(ctx: RunContext[StateDeps[TestStateSnapshot]]) -> str:
return "always call update_state when you're done"
@main_agent.tool
async def roll_dice(ctx: RunContext[StateDeps[TestStateSnapshot]]) -> DiceAgentResponse:
"""Roll a six-sided die and return the random result."""
result = 6
ctx.deps.state.testState.counter = result
return DiceAgentResponse(roll=result)
@main_agent.tool
async def update_state(
ctx: RunContext[StateDeps[TestStateSnapshot]],
) -> StateSnapshotEvent:
return StateSnapshotEvent(type=EventType.STATE_SNAPSHOT, snapshot=ctx.deps.state)
sub_agent = Agent(
name="Pink Fluffy Unicorn agent",
model=model,
deps_type=StateDeps[TestStateSnapshot],
)
@main_agent.tool
async def pink_fluffy_unicorns(ctx: RunContext[StateDeps[TestStateSnapshot]]):
'''Test agent that changes the count to 42 in one of it's tools'''
response = await sub_agent.run(
message_history=ctx.messages,
deps=ctx.deps,
)
return response
@sub_agent.tool
async def get_pink_fluffy_unicorn_info(
ctx: RunContext[StateDeps[TestStateSnapshot]], increment: int
):
state = ctx.deps.state
state.testState.counter = 42In order to update the state and send it back to my frontend, main_agent needs to call the tool update_state at the end of it's execution. Here's the correct call stack:
Routing Agent run
chat gpt-4o
running 2 tools
running tool: roll_dice
running tool: update_state
chat gpt-4oHowever, the update_state call is missing more often than not, even if I prompt the agent to do it. It seems to work more or less consistently if I specifically ask it in the chat. Here are some example screenshots:
Asking the agent to roll the dice, update_state is not called:
running 1 tool
running tool: roll_dice
chat gpt-4o
Asking the agent to roll the dice and then update the state:
Routing Agent run
chat gpt-4o
running 2 tools
running tool: roll_dice
running tool: update_state
chat gpt-4o
Obviously not what you'd want for a user-facing application
It gets even flimsier when I ask it to call a sub-agent. In this example update_state is called before the tool call that changes the state to 42.
Routing Agent run
chat gpt-4o
running 2 tools
running tool: pink_fluffy_unicorns
Pink Fluffy Unicorn agent run
running tool: update_state
running tool: pink_fluffy_unicorns
Pink Fluffy Unicorn agent run
running 2 tools
running tool: pink_fluffy_unicorns
running tool: update_state
chat gpt-4o
running 1 tool
running tool: get_pink_fluffy_unicorn_info
chat gpt-4o
chat gpt-4oMy Question(s):
- Is updating the state via a tool call the only way to do it? Couldn't I send a custom event via
run_ag_ui'son_completecallback parameter? Send custom events from tool function, for AG-UI and event_stream_handler #2382 (comment) <- this workaround seems to be the closest thing I've found so far - CopilotKit Inserts a SystemMessage at the beginning of the message history. How does that interact with Agents (and sub-agents)?
- What's the best practice for this type of problem? (I don't really mind highly-opinionated takes on this)
If there's any input you could give me on this issue, I'd be stoked.
Additional Context
No response