Skip to content

Human-Agent Dialogue with Clarification¤

This guide shows how to use the dialogue() function for 1:1 human-agent conversations where the agent can request clarification when it lacks sufficient data. The application manages the clarification loop — receive the question, collect the human's response, and resume the dialogue.

Concepts covered

What you'll build¤

A customer support agent for NovaTech that helps with product inquiries, account questions, and technical issues. When the user asks about a specific order or account without providing enough details, the agent requests clarification instead of guessing:

User:  "What's the status of my order?"
Agent: "I'd be happy to look that up! Could you provide your order number
        or the email address associated with your account?"
User:  "Order #NVT-2024-0842"
Agent: "Your order #NVT-2024-0842 shipped on July 8 and is expected to
        arrive by July 12. Tracking: 1Z999AA10123456784"
Dialogue vs. Operation tree

dialogue() is a standalone function — it does not use the Operation tree. It calls the runtime directly with assembled instructions and conversation history. Use it for 1:1 conversational patterns.


1 Define the agent¤

The agent's identity block describes its persona and when to request clarification:

from crewmaster.agents.agent import AgentConfig

support_agent = AgentConfig(
    name="support_agent",
    identity_blocks=[
        """You are a helpful customer support agent for NovaTech,
an AI & Developer Tools company. Assist customers with product
inquiries, technical questions, and account-related matters.

Guidelines:
- Be friendly, professional, and concise.
- If a customer asks about an order, account, or billing detail
  and hasn't provided enough information (order number, account ID,
  email), you MUST ask for clarification rather than guessing.
- When clarification is needed, use the structured clarification
  format to request exactly the information you need.
- If the question is general (product features, pricing), answer
  directly without requesting clarification.
- Always strive to be helpful and resolve the question in as few
  turns as possible.""",
    ],
)

2 Run a single dialogue turn¤

Call dialogue() with the agent, the human's message, and a runtime. Iterate over the async generator to collect events:

import asyncio

from crewmaster.conversation.dialogue import (
    AgentOutputClarification,
    DialogueAgentMessage,
    DialogueComplete,
    dialogue,
)


async def chat_turn(driver, agent, human_message, history=None):
    """Run one turn of dialogue and return parsed events."""
    events = []

    async for event in dialogue(
        agent=agent,
        human_message=human_message,
        history=history or [],
        runtime=driver,
    ):
        events.append(event)

    return events

3 Interpret the events¤

Each turn yields three possible event types in order:

Event Meaning
DialogueAgentMessage The agent produced a normal text response
AgentOutputClarification The agent needs more information
DialogueComplete Terminal event (always yielded last)
def interpret(events):
    """Extract the agent's intent from dialogue events."""
    agent_text = None
    clarification = None
    typed_output = None

    for event in events:
        if isinstance(event, DialogueAgentMessage):
            agent_text = event.content
        elif isinstance(event, AgentOutputClarification):
            clarification = event.question
        elif isinstance(event, DialogueComplete):
            typed_output = event.typed_output

    if clarification:
        return {"type": "clarification", "question": clarification}
    return {"type": "response", "text": agent_text, "output": typed_output}

4 Build the clarification loop¤

The application manages the full conversation loop — receive a clarification request, collect the human's response, and call dialogue() again with the accumulated history:

from crewmaster.conversation.channel import Message


async def support_session(driver, initial_message: str):
    """Run a multi-turn dialogue with clarification support."""
    print(f"User: {initial_message}")

    history: list[Message] = []
    current_message = initial_message

    for turn in range(5):  # Max 5 turns
        events = await chat_turn(
            driver, support_agent, current_message, history
        )

        for event in events:
            if isinstance(event, DialogueAgentMessage):
                print(f"\nAgent: {event.content}")
                history.append(Message(role="user", content=current_message))
                history.append(Message(role="assistant", content=event.content))
                return  # Done

            elif isinstance(event, AgentOutputClarification):
                print(f"\nAgent (needs info): {event.question}")
                history.append(Message(role="user", content=current_message))
                history.append(Message(
                    role="assistant",
                    content=f"[CLARIFICATION] {event.question}"
                ))

                # Collect human response (in a real app, prompt the user)
                current_message = input("\nYour response: ")
                print(f"User: {current_message}")
                break  # Restart the loop with updated history

    print("\n[Session ended — max turns reached]")

5 Execute with a real driver¤

from crewmaster.execution.drivers.pydantic_ai import PydanticAIDriver


async def main():
    driver = PydanticAIDriver(model_name="openai:gpt-4o-mini")

    # Scenario 1: Direct answer (no clarification needed)
    print("=== Scenario 1: General question ===")
    await support_session(driver, "What are NovaTech's pricing plans?")

    # Scenario 2: Clarification needed
    print("\n=== Scenario 2: Missing order info ===")
    await support_session(driver, "What's the status of my order?")


asyncio.run(main())

Expected output:

=== Scenario 1: General question ===
User: What are NovaTech's pricing plans?

Agent: NovaTech offers three tiers: Starter ($29/mo), Professional
($99/mo), and Enterprise (custom). All plans include API access and
community support. Professional adds SSO and priority support.
Enterprise includes dedicated support and custom SLAs.

=== Scenario 2: Missing order info ===
User: What's the status of my order?

Agent (needs info): I'd be happy to check your order status! Could
you provide your order number or the email associated with your account?

Your response: Order #NVT-2024-0842
User: Order #NVT-2024-0842

Agent: Your order #NVT-2024-0842 shipped on July 8 via UPS. It's
expected to arrive by July 12. Tracking number: 1Z999AA10123456784.

6 How clarification works under the hood¤

When the agent detects insufficient information, it uses a structured format:

---CLARIFICATION---
question: What is your order number?
schema: OrderLookupInput
---END CLARIFICATION---

The dialogue() function parses this and yields an AgentOutputClarification event. The response_schema field (when set) tells the application what structured data the agent expects:

if isinstance(event, AgentOutputClarification):
    print(f"Question: {event.question}")
    if event.response_schema is not None:
        print(f"Expected format: {event.response_schema.__name__}")
        # e.g., Expected format: OrderLookupInput

The schema is a lightweight type reference — the application decides how to collect and validate the human's response.


7 Complete script¤

dialogue_support.py
"""Human-agent dialogue with clarification support."""
import asyncio

from crewmaster.agents.agent import AgentConfig
from crewmaster.conversation.channel import Message
from crewmaster.conversation.dialogue import (
    AgentOutputClarification,
    DialogueAgentMessage,
    DialogueComplete,
    dialogue,
)
from crewmaster.execution.drivers.pydantic_ai import PydanticAIDriver


# ── Agent ────────────────────────────────────────────────────────
support_agent = AgentConfig(
    name="support_agent",
    identity_blocks=[
        """You are a customer support agent for NovaTech.

If a customer asks about an order, account, or billing and hasn't
provided enough info (order number, account ID, email), request
clarification. Use the structured format:
---CLARIFICATION---
question: <your question>
---END CLARIFICATION---

For general questions, answer directly. Be friendly and concise."""
    ],
)


# ── Dialogue helpers ─────────────────────────────────────────────
async def chat_turn(driver, agent, human_message, history=None):
    events = []
    async for event in dialogue(
        agent=agent,
        human_message=human_message,
        history=history or [],
        runtime=driver,
    ):
        events.append(event)
    return events


async def support_session(driver, initial_message: str):
    print(f"User: {initial_message}")
    history: list[Message] = []
    current = initial_message

    for _ in range(5):
        events = await chat_turn(driver, support_agent, current, history)

        for event in events:
            if isinstance(event, DialogueAgentMessage):
                print(f"\nAgent: {event.content}")
                history.append(Message(role="user", content=current))
                history.append(Message(role="assistant", content=event.content))
                return

            elif isinstance(event, AgentOutputClarification):
                print(f"\nAgent (needs info): {event.question}")
                history.append(Message(role="user", content=current))
                history.append(Message(
                    role="assistant",
                    content=f"[CLARIFICATION] {event.question}"
                ))
                current = input("\nYour response: ")
                print(f"User: {current}")
                break

    print("\n[Session ended]")


# ── Execute ──────────────────────────────────────────────────────
async def main():
    driver = PydanticAIDriver(model_name="openai:gpt-4o-mini")

    print("=== General question ===")
    await support_session(driver, "What are your pricing plans?")

    print("\n=== Missing order info ===")
    await support_session(driver, "What's the status of my order?")


asyncio.run(main())

Next steps¤

  • Combine dialogue with a sequential channel for scenarios where a human converses with multiple specialized agents in sequence.
  • Add typed output models to the dialogue with DialogueComplete.typed_output for structured responses.
  • Read the Conversation API reference for full documentation on dialogue, AgentOutputClarification, and Message.