> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Message Steering

> Send real-time guidance to agents during long-running execution

Message Steering lets you send guidance to a running agent without restarting it.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(
    name="researcher",
    instructions="You are a research assistant",
    message_steering=True,
)

agent.start("Write a brief overview of agentic RAG.")
agent.steer("Focus on business impact, keep under 200 words", priority=10)
```

The user steers a long-running task mid-flight; guidance is queued and applied at the next safe boundary.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Message Steering"
        U[👤 User] -->|steer message| Q[📬 Priority Queue]
        Q -->|polled at chat/tool boundary| A[🤖 Running Agent]
        A -->|adjusted response| R[✅ Response]
    end
    
    classDef user fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef queue fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff
    
    class U user
    class Q queue
    class A agent
    class R result
```

<Note>
  Mid-tool-loop steering is fully wired as of [PR #2997](https://github.com/MervinPraison/PraisonAI/pull/2997) (release after 2026-07-14). Earlier releases only checked pending steering at chat/tool boundaries, so a long multi-tool run could not be steered until the loop returned to a boundary — and `INTERRUPT`/`URGENT` priorities were silently downgraded on any mid-run injection path. Upgrade to pick up per-iteration steering + preserved priorities across LiteLLM and OpenAI-native providers, sync and async.
</Note>

## Quick Start

<Steps>
  <Step title="Simple Usage">
    Enable with `message_steering=True` and use `agent.steer()` to send guidance.

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent

    agent = Agent(
        name="researcher",
        instructions="You are a research assistant",
        message_steering=True,
        llm="gpt-4o-mini",
    )

    agent.steer("Focus on business impact, keep under 200 words", priority=10)

    agent.start("Summarise the latest AI trends")
    ```
  </Step>

  <Step title="Threaded Pattern">
    Send steering messages while the agent is actively running.

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import threading, time
    from praisonaiagents import Agent

    agent = Agent(name="writer", instructions="You write articles", message_steering=True)

    def run():
        return agent.start("Write a comprehensive article about quantum computing")

    t = threading.Thread(target=run); t.start()

    time.sleep(0.5); agent.steer("Make it beginner-friendly")
    time.sleep(1.0); agent.steer("Include practical applications", priority=15)
    time.sleep(1.5); agent.steer("Keep under 300 words total", priority=20)

    t.join()
    ```
  </Step>

  <Step title="Async Pattern">
    Use async execution with real-time steering.

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import asyncio
    from praisonaiagents import Agent

    async def main():
        agent = Agent(name="analyst", instructions="You analyse data", message_steering=True)
        task = asyncio.create_task(agent.astart("Analyse impact of AI on job markets"))

        await asyncio.sleep(0.3); agent.steer("Focus on positive opportunities")
        await asyncio.sleep(0.7); agent.steer("Include specific examples", priority=12)

        print(await task)

    asyncio.run(main())
    ```
  </Step>

  <Step title="Steering a Long Multi-Tool Run">
    Steer an agent while it is deep inside a multi-step tool loop — the guidance lands on the next loop iteration, not only when a chat step returns.

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import threading, time
    from praisonaiagents import Agent
    from praisonaiagents.tools import duckduckgo  # any tool works — this is illustrative

    agent = Agent(
        name="researcher",
        instructions="Research topics and write a structured report.",
        tools=[duckduckgo],
        message_steering=True,
    )

    def run():
        return agent.start("Research solar market trends and write a 500-word summary")

    t = threading.Thread(target=run); t.start()

    # Agent is now in a multi-step tool loop (search → read → search → summarise).
    # The steer lands on the next loop iteration, not only when the chat step returns.
    time.sleep(1.0); agent.steer("Focus on the EU market only", priority=20)   # URGENT
    time.sleep(2.0); agent.steer("Skip anything before 2024", priority=30)     # INTERRUPT

    t.join()
    ```

    Example user flow:

    ```
    User: research solar market trends and write a 500-word summary
    Agent: (tool loop: searching → reading → summarising …)
    User: (mid-run) focus on the EU market only          [URGENT]
    Agent: (next iteration picks up guidance, re-scopes the next search)
    User: (mid-run) skip anything before 2024            [INTERRUPT]
    Agent: ✅ 500-word EU Solar Market Trends (2024+) summary
    ```
  </Step>
</Steps>

***

## How It Works

Message steering injects guidance at the top of every chat step **and every tool-loop iteration**, so a long multi-tool run picks up new user guidance on its very next model call — without restarting the run.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Agent
    participant Loop as Tool Loop
    participant LLM
    participant Tools

    User->>Agent: agent.start("long task")
    Agent->>LLM: prompt
    LLM-->>Agent: tool_calls
    Agent->>Loop: enter tool-calling loop

    Note over User,Loop: user steers mid-run
    User->>Agent: agent.steer("focus on EU market", priority=20)

    Loop->>Loop: drain pending steering notes
    Loop->>LLM: prompt + [URGENT USER GUIDANCE]: focus on EU market
    LLM-->>Loop: revised tool_calls
    Loop->>Loop: cancel_token.is_set()? (before tool dispatch)
    Loop->>Tools: dispatch tools (only if not cancelled)
    Tools-->>Loop: results
    Loop-->>Agent: final response
    Agent-->>User: result
```

Steering messages are drained at the start of each chat step and each tool-loop iteration, then injected as `user` messages with a priority-aware header. The agent picks them up on its very next model call, whether it is between chat turns or deep inside a multi-tool loop.

***

## Mid-Tool-Loop Steering

Steering messages land on the very next tool-loop iteration, so multi-step tool runs pick up new guidance without a restart.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Iter[🔁 Next tool-loop iteration] --> CancelChk{🛑 /stop received?}
    CancelChk -->|Yes| Halt[✅ Abort — skip tools]
    CancelChk -->|No| Drain{📬 Pending steering<br/>messages?}
    Drain -->|Yes| Inject[🧭 Inject as user messages<br/>with priority-aware headers]
    Drain -->|No| Continue[▶ Continue as normal]
    Inject --> Continue
    Continue --> LLMCall[🤖 Next LLM call]

    classDef step fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef decision fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef ok fill:#10B981,stroke:#7C90A0,color:#fff
    classDef halt fill:#F59E0B,stroke:#7C90A0,color:#fff

    class Iter,LLMCall,Continue,Inject step
    class CancelChk,Drain decision
    class Halt halt
```

* Steering messages queued via `agent.steer(...)` are checked at the top of every tool-loop iteration, so multi-step tool runs pick them up without a restart.
* `INTERRUPT` and `URGENT` priorities keep their headers when injected mid-loop, so the model sees `[INTERRUPT USER GUIDANCE]` / `[URGENT USER GUIDANCE]` instead of being silently downgraded to plain guidance.
* `/stop` (via `cancel_token` or `agent.interrupt_controller`) is re-checked immediately before dispatching tool calls, so a cancel mid-iteration skips running the pending tools instead of executing them first.

<Note>
  The between-iteration check on the OpenAI-native default sync path became genuinely operative in [PR #4301](https://github.com/MervinPraison/PraisonAI/pull/4301). Earlier releases ran the check but the `cancel_token` was dropped before reaching `OpenAIClient`, so `is_set()` was always false.
</Note>

***

## Priority Levels

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Q{Need to change agent behaviour?} -->|Style nudge| N[NORMAL — priority=5]
    Q -->|Important course correction| H[HIGH — priority=10]
    Q -->|Must affect next tool call| U[URGENT — priority=20]
    Q -->|Stop current tool NOW| I[INTERRUPT — priority=30]

    classDef question fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef normal fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef high fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef urgent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef interrupt fill:#10B981,stroke:#7C90A0,color:#fff

    class Q question
    class N normal
    class H high
    class U urgent
    class I interrupt
```

| Priority  | Int value | Behaviour                                                                                                                              |
| --------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| LOW       | 1         | Whispered guidance                                                                                                                     |
| NORMAL    | 5         | Default priority                                                                                                                       |
| HIGH      | 10        | Acknowledged urgently in next chat step                                                                                                |
| URGENT    | 20        | Injected mid tool-loop as `[URGENT USER GUIDANCE]` — the model acknowledges and adjusts on its very next model call                    |
| INTERRUPT | 30        | Injected mid tool-loop as `[INTERRUPT USER GUIDANCE]` — the model is told to stop current work and follow the new guidance immediately |

***

## CLI Usage

Enable message steering from command line:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai --message-steering "Write a detailed report about AI trends"
```

The `--message-steering` flag sets `message_steering=True` on the agent.

***

## YAML Usage

Enable steering per role in YAML configuration:

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
framework: praisonai
roles:
  researcher:
    role: Research Assistant
    goal: Conduct thorough research
    message_steering: true   # Enable per-role
    tasks:
      research_task:
        description: Research AI trends and write report
        expected_output: Comprehensive research report
```

***

## Configuration Options

| Option             | Type                                | Default | Description                     |
| ------------------ | ----------------------------------- | ------- | ------------------------------- |
| `message_steering` | `bool` \| `MessageSteeringProtocol` | `False` | Enable steering                 |
| `max_messages`     | `int`                               | `50`    | Queue capacity (in constructor) |
| `check_interval`   | `float`                             | `0.1`   | Poll interval in seconds        |

When `message_steering=False` (default), there's zero overhead - the feature has no performance impact when disabled.

***

## Agent Methods

| Method                                | Signature                                             | Description                                     |
| ------------------------------------- | ----------------------------------------------------- | ----------------------------------------------- |
| `steer`                               | `agent.steer(message: str, priority: int = 5) -> str` | Queue a steering message. Returns tracking ID.  |
| `get_steering_status`                 | `agent.get_steering_status() -> Dict[str, Any]`       | Returns `{enabled, pending_count, has_pending}` |
| `message_steering_enabled` (property) | `agent.message_steering_enabled -> bool`              | Whether steering is active                      |

***

## Custom Steering Backends

Implement `MessageSteeringProtocol` to plug custom backends:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, MessageSteeringProtocol, SteeringMessage, SteeringPriority

class RedisSteering:  # Implements MessageSteeringProtocol structurally
    def queue_message(self, message: str, priority: int = 5) -> str: ...
    def get_pending_messages(self): ...
    def process_steering(self, context=None) -> bool: ...
    def clear_messages(self) -> int: ...
    def has_pending_messages(self) -> bool: ...
    @property
    def enabled(self) -> bool: ...

agent = Agent(name="bg", message_steering=RedisSteering())
```

Mid-loop drains rely on repeated `has_pending_messages()` + dequeue calls, so a custom backend must implement both correctly for mid-run injection to work.

***

## Best Practices

<AccordionGroup>
  <Accordion title="Use threading for long-running tasks">
    Start agents in background threads to enable concurrent steering. The threaded pattern is the primary use case for message steering.
  </Accordion>

  <Accordion title="Choose appropriate priorities">
    Use NORMAL (5) for style guidance, HIGH (10) for course corrections, URGENT (20) to interrupt tools, and INTERRUPT (30) only for immediate stops.
  </Accordion>

  <Accordion title="Keep messages concise">
    Steering messages are injected into prompts. Keep them brief and actionable for best results.
  </Accordion>

  <Accordion title="Monitor steering status">
    Use `get_steering_status()` to check pending messages and ensure your guidance is being processed.
  </Accordion>
</AccordionGroup>

***

<Info>
  When wrapping a steering-enabled agent in a chat bot, set the bot's `busy_mode="steer"` to surface this capability to end users. See [Bot Run Control](/docs/features/bot-run-control).
</Info>

***

## Related

<CardGroup cols={2}>
  <Card icon="webhook" href="/docs/features/hooks">Pre/post execution hooks (compile-time interception)</Card>
  <Card icon="keyboard" href="/docs/features/bot-run-control">Interactive input vs background steering</Card>
</CardGroup>
