a, err := agent.NewAgent( agent.WithLLMClient(llmClient), agent.WithSystemPrompt("You are a helpful assistant."), agent.WithStream(true),)
Then call Stream instead of Run:
eventCh, err := a.Stream(ctx, "What's 17 * 23?", nil)if err != nil { return err}for ev := range eventCh { if ev == nil { continue } switch ev.Type() { case agent.AgentEventTypeTextMessageContent: if t, ok := ev.(*agent.AgentTextMessageContentEvent); ok { fmt.Print(t.Delta) } case agent.AgentEventTypeToolCallStart: if t, ok := ev.(*agent.AgentToolCallStartEvent); ok { fmt.Printf("\n[tool] %s\n", t.ToolCallName) } case agent.AgentEventTypeRunFinished: fmt.Println("\n[done]") case agent.AgentEventTypeRunError: if t, ok := ev.(*agent.AgentRunErrorEvent); ok { fmt.Printf("\n[error] %s\n", t.Message) } }}
Always check if ev == nil { continue } before calling ev.Type(). The channel can deliver a nil sentinel to signal the end of a batch.
Parse approval events with ParseCustomEventApproval / ParseCustomEventDelegation, then call OnApproval with the ApprovalToken. See Approvals for the full flow.
TEXT_MESSAGE_CONTENT deltas and AgentRunFinishedEvent.Result.Content carry the same text. Do not print both. If you rendered deltas live, skip printing result.Content from RUN_FINISHED.
When sub-agents are configured, events from delegated runs fan in to the parent stream. Use AgentName on typed events to identify which agent emitted them. Multiple RUN_FINISHED events may appear before the root run finishes — each corresponds to one completed sub-agent run.
for ev := range eventCh { if ev == nil { continue } if finished, ok := ev.(*agent.AgentRunFinishedEvent); ok && finished.Result != nil { if finished.Result.LLMUsage != nil { fmt.Printf("tokens: input=%d output=%d total=%d\n", finished.Result.LLMUsage.PromptTokens, finished.Result.LLMUsage.CompletionTokens, finished.Result.LLMUsage.TotalTokens, ) } }}
RunAsync starts the run in a goroutine and returns a channel that delivers one AgentRunAsyncResult when the run completes. Use it when you want a final result without blocking the caller, but do not need live token events.
Stream events follow the AG-UI open protocol. Serialize any event with event.ToJSON() and forward over SSE or WebSocket to a compatible frontend. See AG-UI Protocol.