`BidiAgent` manages a live conversation with a streaming model. It keeps the model connection open across responses, runs tools, and maintains conversation history. Your application can send new input while the model responds or tools run.

## Create an agent

Configure the agent’s instructions and tools when you create it:

```python
from strands.bidi.agent import BidiAgent
from strands.vended_tools import notebook

agent = BidiAgent(
    system_prompt="You are a helpful assistant. Keep your answers brief.",
    tools=[notebook],
)
```

By default, `BidiAgent` uses [Amazon Nova Sonic](/pr-cms-5317/docs/user-guide/sdk/bidi/models/bedrock/index.md). Pass a model instance through `model` to customize its configuration or choose [Google Gemini Live](/pr-cms-5317/docs/user-guide/sdk/bidi/models/google/index.md), [OpenAI Realtime](/pr-cms-5317/docs/user-guide/sdk/bidi/models/openai/realtime/index.md), or experimental [OpenAI Live](/pr-cms-5317/docs/user-guide/sdk/bidi/models/openai/live/index.md). See the [`BidiAgent` API reference](/pr-cms-5317/docs/api/python/strands.bidi.agent#strands.bidi.agent.BidiAgent.__init__) for constructor options.

`BidiAgent` and `Agent` share the [`LocalAgent`](/pr-cms-5317/docs/api/python/strands.types.agent#strands.types.agent.LocalAgent) interface for common capabilities such as `messages` and registered tools. Tools and hooks can use this interface to work with either agent type. See [Hooks](/pr-cms-5317/docs/user-guide/sdk/bidi/hooks/index.md) for callback annotations.

## Send and receive

Creating the agent configures it; entering `async with` opens the model connection. Inside the block, `send()` submits input without waiting for a model response, and `receive()` yields output events as they arrive.

This example sends a text prompt and prints the completed response, then ends the conversation. Follow the [OpenAI Realtime setup](/pr-cms-5317/docs/user-guide/sdk/bidi/models/openai/realtime/index.md) before running it:

```python
import asyncio

from strands.bidi.agent import BidiAgent
from strands.bidi.models import OpenAIRealtimeModel
from strands.bidi.types import BidiResponseStopEvent, BidiTextBlockEvent


async def main() -> None:
    model = OpenAIRealtimeModel(
        model_id="gpt-realtime-2.1",
        transcription_model_id=None,
        params={"output_modalities": ["text"]},
    )
    async with BidiAgent(model=model) as agent:
        await agent.send("Tell me about Lisbon in one sentence.")

        async for event in agent.receive():
            if isinstance(event, BidiTextBlockEvent):
                print(event.text)
            elif isinstance(event, BidiResponseStopEvent):
                break


asyncio.run(main())
```

For an ongoing conversation, keep consuming events while another coroutine supplies input through `send()`. [Input Content](/pr-cms-5317/docs/user-guide/sdk/bidi/content/index.md) covers the formats you can send, and [Stream Events](/pr-cms-5317/docs/user-guide/sdk/bidi/events/index.md) explains the events you receive and their ordering.

You can also let `agent.run()` manage this exchange: it reads from input streams, forwards events to output streams, and starts and stops the agent. See [I/O Streams](/pr-cms-5317/docs/user-guide/sdk/bidi/io/index.md) for built-in streams and custom integrations.

## End a conversation

Exiting the `async with` block calls `stop()`, which cancels background model and tool tasks and closes the model connection. You can also manage the lifecycle explicitly with `start()` and `stop()`.

To end the conversation when a user asks to stop, give the model a tool that requests cancellation with `agent.cancel()`:

```python
from strands import ToolContext, tool
from strands.bidi.agent import BidiAgent


@tool(context=True)
def end_conversation(tool_context: ToolContext[BidiAgent]) -> str:
    """End the conversation when the user asks to stop or says goodbye."""
    tool_context.agent.cancel()
    return "Ending conversation."


agent = BidiAgent(tools=[end_conversation])
```

The agent acts on the request after a tool group finishes, ending `receive()` so the surrounding `async with` block can stop the agent.

This is currently the only cancellation checkpoint. Requests made outside a tool also remain pending until a tool group finishes. Additional checkpoints are planned.

## Conversation history

Give a new agent conversation history through `messages`:

```python
from strands.bidi.agent import BidiAgent

agent = BidiAgent(
    messages=[
        {"role": "user", "content": [{"text": "I'm planning a trip to Lisbon."}]},
    ],
)
```

The agent sends the initial history to the model on startup and updates `agent.messages` as the conversation progresses. See [Messages](/pr-cms-5317/docs/user-guide/sdk/bidi/events/index.md#messages) for how streamed content and tool results become history.

## Connection restarts

Model providers limit how long a streaming connection can stay open. Each connection carries continuous input and output within a live session, which can expire while the conversation is still ongoing.

By default, `BidiAgent` replaces the connection on a configured schedule or after a connection timeout. It preserves conversation history across replacements so the conversation can continue across multiple connections.

To customize the restart timing, set `restart_after_s` on the model. This example sets Nova Sonic to restart six minutes after each connection opens:

```python
from strands.bidi.models import BedrockNovaSonicModel

model = BedrockNovaSonicModel(
    model_id="amazon.nova-2-5-sonic",
    connection={"restart_after_s": 360},
)
```

A scheduled restart waits briefly for a turn boundary before proceeding. Leave enough time before the provider’s connection limit for that wait and for opening the new connection. See [`ConnectionConfig`](/pr-cms-5317/docs/api/python/strands.bidi.models#strands.bidi.models.ConnectionConfig) for the configuration reference.

The model adapter restores context by replaying history or resuming its server-side session. Calls to `send()` wait while the connection is replaced, and background tools can continue running.

To track the transition, handle [`BidiConnectionRestartEvent`](/pr-cms-5317/docs/user-guide/sdk/bidi/events/index.md#connection-restarts). Its `reason` identifies a scheduled restart or timeout, and `turn_interrupted` indicates whether the restart interrupted an unfinished turn. If the restart fails, the error propagates through `receive()` or `run()`.

## Related pages

- [Bidirectional Streaming](/pr-cms-5317/docs/user-guide/sdk/bidi/index.md) (1 shared tag)
- [Bidirectional Streaming Models](/pr-cms-5317/docs/user-guide/sdk/bidi/models/index.md) (1 shared tag)
- [Google Gemini Live](/pr-cms-5317/docs/user-guide/sdk/bidi/models/google/index.md) (1 shared tag)
- [I/O Streams](/pr-cms-5317/docs/user-guide/sdk/bidi/io/index.md) (1 shared tag)
- [Input Content](/pr-cms-5317/docs/user-guide/sdk/bidi/content/index.md) (1 shared tag)
- [Interrupts](/pr-cms-5317/docs/user-guide/sdk/bidi/interrupts/index.md) (1 shared tag)
- [OpenAI Live](/pr-cms-5317/docs/user-guide/sdk/bidi/models/openai/live/index.md) (1 shared tag)
- [OpenAI Realtime](/pr-cms-5317/docs/user-guide/sdk/bidi/models/openai/realtime/index.md) (1 shared tag)
- [Stream Events](/pr-cms-5317/docs/user-guide/sdk/bidi/events/index.md) (1 shared tag)
- [Tools](/pr-cms-5317/docs/user-guide/sdk/bidi/tools/index.md) (1 shared tag)


## Implementation

### Python

- [harness-sdk/strands-py/src/strands/bidi/agent/agent.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/bidi/agent/agent.py)
- [harness-sdk/strands-py/src/strands/bidi/agent/loop.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/bidi/agent/loop.py)
- [harness-sdk/strands-py/src/strands/bidi/types/agent.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/bidi/types/agent.py)
- [harness-sdk/strands-py/src/strands/bidi/models/configs.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/bidi/models/configs.py)
- [harness-sdk/strands-py/src/strands/types/agent.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/types/agent.py)
