Repository Public
1# Running agents23> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.45Defining an agent is only the setup step. The runtime questions are what a single run does, how the next turn continues, and how the workflow behaves when it pauses for approvals or tool work.67## The agent loop89One SDK run is one application-level turn. The runner keeps looping until it reaches a real stopping point:10111. Call the current agent's model with the prepared input.122. Inspect the model output.133. If the model produced tool calls, execute them and continue.144. If the model handed off to another specialist, switch agents and continue.155. If the model produced a final answer with no more tool work, return a result.1617That loop is the core concept behind the SDK. Tools, handoffs, approvals, and streaming all build on top of it rather than replacing it.1819## Choose one conversation strategy2021There are four common ways to carry state into the next turn:2223| Strategy | Where state lives | Best for | What you pass on the next turn |24| ------------------------------------------------------------------------------------------------------------------ | ------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------- |25| `result.history` in TypeScript or `result.to_input_list()` in Python | Your application | Small chat loops and maximum control | The replay-ready history |26| `session` | Your storage plus the SDK | Persistent chat state, resumable runs, and storage you control | The same session |27| `conversationId` | OpenAI Conversations API | Shared server-managed state across workers or services | The same conversation ID and only the new turn |28| `previousResponseId` in TypeScript or `previous_response_id` in Python | OpenAI Responses API | The lightest server-managed continuation from one response to the next | The last response ID and only the new turn |2930In most applications, pick one strategy per conversation. Mixing local replay with server-managed state can duplicate context unless you are deliberately reconciling both layers.3132Persist multi-turn state with sessions3334```javascript35import { Agent, MemorySession, run } from "@openai/agents";3637const agent = new Agent({38 name: "Tour guide",39 instructions: "Answer with compact travel facts.",40});4142const session = new MemorySession();4344const firstTurn = await run(agent, "What city is the Golden Gate Bridge in?", {45 session,46});47console.log(firstTurn.finalOutput);4849const secondTurn = await run(agent, "What state is it in?", { session });50console.log(secondTurn.finalOutput);51```5253```python54import asyncio5556from agents import Agent, Runner, SQLiteSession5758agent = Agent(59 name="Tour guide",60 instructions="Answer with compact travel facts.",61)6263session = SQLiteSession("conversation_123")646566async def main() -> None:67 first_turn = await Runner.run(68 agent,69 "What city is the Golden Gate Bridge in?",70 session=session,71 )72 print(first_turn.final_output)7374 second_turn = await Runner.run(75 agent,76 "What state is it in?",77 session=session,78 )79 print(second_turn.final_output)808182if __name__ == "__main__":83 asyncio.run(main())84```858687Sessions are the best default when you want durable memory, resumable approval flows, or storage that your application controls.8889Continue with server-managed state9091```javascript92import { Agent, run } from "@openai/agents";93import OpenAI from "openai";9495const agent = new Agent({96 name: "Assistant",97 instructions: "Reply very concisely.",98});99100const client = new OpenAI();101const { id: conversationId } = await client.conversations.create({});102103const first = await run(agent, "What city is the Golden Gate Bridge in?", {104 conversationId,105});106console.log(first.finalOutput);107108const second = await run(agent, "What state is it in?", {109 conversationId,110});111console.log(second.finalOutput);112```113114```python115import asyncio116117from agents import Agent, Runner118119agent = Agent(120 name="Assistant",121 instructions="Reply very concisely.",122)123124125async def main() -> None:126 first = await Runner.run(127 agent,128 "What city is the Golden Gate Bridge in?",129 )130 print(first.final_output)131132 second = await Runner.run(133 agent,134 "What state is it in?",135 previous_response_id=first.last_response_id,136 )137 print(second.final_output)138139140if __name__ == "__main__":141 asyncio.run(main())142```143144145Use `conversationId` when multiple systems should share one named conversation. Use `previousResponseId` in TypeScript or `previous_response_id` in Python when you want the cheapest response-to-response continuation option.146147## Stream runs incrementally148149Streaming uses the same agent loop and the same state strategies. The only difference is that you consume events while the run is still happening.150151Stream a run as text arrives152153```javascript154import { Agent, run } from "@openai/agents";155156const agent = new Agent({157 name: "Planet guide",158 instructions: "Answer with short facts.",159});160161const stream = await run(agent, "Give me three short facts about Saturn.", {162 stream: true,163});164165for await (const event of stream) {166 if (167 event.type === "raw_model_stream_event" &&168 event.data.type === "output_text_delta"169 ) {170 process.stdout.write(event.data.delta);171 }172}173174await stream.completed;175console.log("\nFinal:", stream.finalOutput);176```177178```python179import asyncio180181from openai.types.responses import ResponseTextDeltaEvent182183from agents import Agent, Runner184185agent = Agent(186 name="Planet guide",187 instructions="Answer with short facts.",188)189190191async def main() -> None:192 stream = Runner.run_streamed(193 agent,194 "Give me three short facts about Saturn.",195 )196197 async for event in stream.stream_events():198 if event.type == "raw_response_event" and isinstance(199 event.data, ResponseTextDeltaEvent200 ):201 print(event.data.delta, end="", flush=True)202203 print(f"\nFinal: {stream.final_output}")204205206if __name__ == "__main__":207 asyncio.run(main())208```209210211Three practical rules matter:212213- Wait for the stream to finish before treating the run as settled.214- If the run pauses for approval, resolve `interruptions` and resume from `state` rather than starting a fresh user turn.215- If you cancel a stream mid-turn, resume the unfinished turn from `state` if you want the same turn to continue later.216217## Handle pauses and failures deliberately218219Two broad classes of non-happy-path outcomes matter:220221- **Runtime or validation failures** such as max-turn limits, guardrail exceptions, or tool errors.222- **Expected pauses** such as human approval requests, where the run is intentionally interrupted and should later resume from the same state.223224Treat approvals as paused runs, not as new turns. That distinction keeps turn counts, history, and server-managed continuation IDs consistent.225226## Next steps227228Once the runtime loop is clear, move to the guide that matches the next workflow boundary you need to design.229230231232 [Results and state233234235236 Learn which result surfaces your application should carry into the next237 turn.](https://developers.openai.com/api/docs/guides/agents/results)238 [Orchestration and handoffs239240241242 Decide how multiple specialists behave inside the same runtime loop.](https://developers.openai.com/api/docs/guides/agents/orchestration)243 [Guardrails and human review244245246247 Add validation and approval pauses without breaking turn continuity.](https://developers.openai.com/api/docs/guides/agents/guardrails-approvals)