> ## 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.

## Quick Start

**1. 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")
```

**2. 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()
```

**3. 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())
```

**4. 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
```

***

## 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.

***

## 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

> [accordion] 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] 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] Keep messages concise
>
> Steering messages are injected into prompts. Keep them brief and actionable for best results.

> [accordion] Monitor steering status
>
> Use `get_steering_status()` to check pending messages and ensure your guidance is being processed.

***

> [!NOTE]
>
> 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](https://praison.ai/docs/features/bot-run-control.md).
>
> `busy_mode="steer"` folds the new message into the *same* turn, so it does **not** trigger the supersede path — a pending approval on the running turn stays live. `busy_mode="interrupt"` is the mode that cancels a pending approval when the turn is superseded. See [Approval Turn Liveness](https://praison.ai/docs/features/gateway-approval-turn-liveness.md).
>
> You can also steer or interrupt a live warm-runtime turn from a second terminal with `praisonai attach -m "..." <id>` and `praisonai attach --interrupt <id>` — the CLI uses this same `message_steering` channel under the hood. See [Attach](https://praison.ai/docs/cli/attach.md).

***

## Related

> [card]
>
> Pre/post execution hooks (compile-time interception)
>
> [Open](https://praison.ai/docs/features/hooks.md)

> [card]
>
> Interactive input vs background steering
>
> [Open](https://praison.ai/docs/features/bot-run-control.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.