Agentic Design Pattern: Clarification

Written by

in


    """
Pattern 129: Clarification & Disambiguation

Description:
    Detects ambiguous or unclear user inputs and asks targeted clarifying questions
    to resolve uncertainties before proceeding.

Components:
    - Ambiguity detection
    - Clarification question generation
    - Context tracking
    - Progressive refinement

Use Cases:
    - Conversational agents handling ambiguous queries
    - Complex instruction following
    - Reducing errors from misunderstanding

LangChain Implementation:
    Uses LangChain to detect ambiguity and generate appropriate clarification questions.
"""

import os
from typing import List, Dict, Any, Optional
from dotenv import load_dotenv

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import JsonOutputParser, StrOutputParser
from langchain_openai import ChatOpenAI
from langchain_core.pydantic_v1 import BaseModel, Field

load_dotenv()


class AmbiguityAnalysis(BaseModel):
    """Analysis of ambiguity in user input"""
    is_ambiguous: bool = Field(description="Whether input is ambiguous")
    ambiguity_type: str = Field(description="Type of ambiguity: lexical, syntactic, semantic, referential")
    confidence: float = Field(description="Confidence in ambiguity detection")
    ambiguous_elements: List[str] = Field(description="Specific ambiguous elements")
    clarification_needed: str = Field(description="What needs clarification")


class ClarificationQuestion(BaseModel):
    """A clarification question"""
    question: str = Field(description="The clarification question")
    options: Optional[List[str]] = Field(description="Possible options if applicable")
    priority: int = Field(description="Priority (1=high, 3=low)")


class ClarificationAgent:
    """Agent for handling ambiguity and generating clarifications"""
    
    def __init__(self, model: str = "gpt-4o-mini", temperature: float = 0.3):
        self.llm = ChatOpenAI(model=model, temperature=temperature)
        self.clarification_history: List[Dict[str, Any]] = []
        
        # Ambiguity detection prompt
        self.ambiguity_prompt = ChatPromptTemplate.from_messages([
            ("system", """Analyze the user's input for ambiguity or unclear elements.

Types of ambiguity:
- Lexical: Multiple meanings of a word (e.g., "bank" - financial or river)
- Syntactic: Sentence structure unclear (e.g., "I saw the man with the telescope")
- Semantic: Vague or imprecise meaning (e.g., "nearby", "soon")
- Referential: Unclear what something refers to (e.g., "it", "that thing")

Return JSON:
{{
  "is_ambiguous": true/false,
  "ambiguity_type": "type",
  "confidence": 0.9,
  "ambiguous_elements": ["element1", "element2"],
  "clarification_needed": "what specifically needs clarification"
}}"""),
            ("user", "Input: {input}\n\nPrevious context: {context}")
        ])
        
        # Clarification generation prompt
        self.clarification_prompt = ChatPromptTemplate.from_messages([
            ("system", """Generate a helpful clarification question to resolve the ambiguity.

Make the question:
1. Specific and targeted
2. Easy to answer
3. Include options when helpful
4. Natural and conversational

Return JSON:
{{
  "question": "the clarification question",
  "options": ["option1", "option2"] or null,
  "priority": 1-3
}}"""),
            ("user", """Input: {input}
Ambiguity: {ambiguity}
Elements needing clarification: {elements}

Generate a clarification question.""")
        ])
        
        self.json_parser = JsonOutputParser()
    
    def detect_ambiguity(self, user_input: str, context: str = "") -> AmbiguityAnalysis:
        """Detect if input is ambiguous"""
        try:
            chain = self.ambiguity_prompt | self.llm | self.json_parser
            result = chain.invoke({"input": user_input, "context": context})
            
            return AmbiguityAnalysis(**result)
        except Exception as e:
            print(f"Ambiguity detection error: {e}")
            return AmbiguityAnalysis(
                is_ambiguous=False,
                ambiguity_type="unknown",
                confidence=0.0,
                ambiguous_elements=[],
                clarification_needed=""
            )
    
    def generate_clarification(
        self,
        user_input: str,
        analysis: AmbiguityAnalysis
    ) -> ClarificationQuestion:
        """Generate clarification question"""
        try:
            chain = self.clarification_prompt | self.llm | self.json_parser
            result = chain.invoke({
                "input": user_input,
                "ambiguity": analysis.ambiguity_type,
                "elements": ", ".join(analysis.ambiguous_elements)
            })
            
            return ClarificationQuestion(**result)
        except Exception as e:
            print(f"Clarification generation error: {e}")
            return ClarificationQuestion(
                question="Could you please provide more details?",
                options=None,
                priority=2
            )
    
    def process_input(self, user_input: str, context: str = "") -> Dict[str, Any]:
        """Process input and return clarification if needed"""
        print(f"\n{'='*70}")
        print(f"šŸ“ Input: {user_input}")
        
        # Detect ambiguity
        analysis = self.detect_ambiguity(user_input, context)
        
        print(f"\nšŸ” Analysis:")
        print(f"   Ambiguous: {analysis.is_ambiguous}")
        print(f"   Confidence: {analysis.confidence:.2f}")
        
        result = {
            "input": user_input,
            "is_ambiguous": analysis.is_ambiguous,
            "analysis": analysis,
            "clarification": None
        }
        
        if analysis.is_ambiguous and analysis.confidence > 0.6:
            print(f"   Type: {analysis.ambiguity_type}")
            print(f"   Ambiguous elements: {', '.join(analysis.ambiguous_elements)}")
            
            # Generate clarification
            clarification = self.generate_clarification(user_input, analysis)
            result["clarification"] = clarification
            
            print(f"\nā“ Clarification needed:")
            print(f"   {clarification.question}")
            if clarification.options:
                print(f"   Options:")
                for i, opt in enumerate(clarification.options, 1):
                    print(f"     {i}. {opt}")
            
            # Store in history
            self.clarification_history.append({
                "input": user_input,
                "analysis": analysis.dict(),
                "clarification": clarification.dict()
            })
        else:
            print(f"\nāœ… Input is clear, no clarification needed")
        
        return result
    
    def resolve_with_clarification(
        self,
        original_input: str,
        clarification_response: str
    ) -> str:
        """Process clarified input"""
        resolution_prompt = ChatPromptTemplate.from_messages([
            ("system", "Combine the original input with the clarification to form a complete, unambiguous statement."),
            ("user", """Original: {original}
Clarification response: {clarification}

Generate complete, unambiguous statement:""")
        ])
        
        chain = resolution_prompt | self.llm | StrOutputParser()
        resolved = chain.invoke({
            "original": original_input,
            "clarification": clarification_response
        })
        
        print(f"\nāœ… Resolved statement:")
        print(f"   {resolved}")
        
        return resolved


def demonstrate_clarification():
    """Demonstrate clarification and disambiguation"""
    print("=" * 80)
    print("Pattern 129: Clarification & Disambiguation")
    print("=" * 80)
    
    agent = ClarificationAgent()
    
    # Example 1: Lexical ambiguity
    print("\n" + "=" * 80)
    print("EXAMPLE 1: Lexical Ambiguity")
    print("=" * 80)
    
    ambiguous_inputs = [
        "I need to go to the bank",
        "Can you get me the bat?",
        "Show me the rock music"
    ]
    
    for input_text in ambiguous_inputs:
        result = agent.process_input(input_text)
        
        if result["clarification"]:
            # Simulate user providing clarification
            print(f"\n   šŸ’¬ User provides clarification: Financial bank")
            resolved = agent.resolve_with_clarification(
                input_text,
                "The financial institution"
            )
    
    # Example 2: Referential ambiguity
    print("\n" + "=" * 80)
    print("EXAMPLE 2: Referential Ambiguity")
    print("=" * 80)
    
    referential_inputs = [
        ("John told Mike he should leave", "Previous: John and Mike were arguing"),
        ("Put it on the table", "Previous: User has a box and a book"),
        ("I want the blue one", "Previous: Looking at shirts")
    ]
    
    for input_text, context in referential_inputs:
        agent.process_input(input_text, context)
    
    # Example 3: Semantic vagueness
    print("\n" + "=" * 80)
    print("EXAMPLE 3: Semantic Vagueness")
    print("=" * 80)
    
    vague_inputs = [
        "Schedule a meeting soon",
        "I need a lot of storage",
        "Find a nearby restaurant"
    ]
    
    for input_text in vague_inputs:
        agent.process_input(input_text)
    
    # Example 4: Complex ambiguity
    print("\n" + "=" * 80)
    print("EXAMPLE 4: Complex Ambiguous Request")
    print("=" * 80)
    
    complex_input = "Book me a nice hotel room with a good view for my trip"
    result = agent.process_input(complex_input)
    
    if result["clarification"]:
        print(f"\n   šŸ’¬ User response: Paris, June 15-20, budget up to $200/night")
        resolved = agent.resolve_with_clarification(
            complex_input,
            "Paris, France from June 15-20, 2024, budget $200 per night"
        )
    
    # Example 5: Clear input (no clarification needed)
    print("\n" + "=" * 80)
    print("EXAMPLE 5: Clear Input (No Clarification)")
    print("=" * 80)
    
    clear_inputs = [
        "Set an alarm for 7:00 AM tomorrow",
        "What is the capital of France?",
        "Calculate 15% tip on $85.50"
    ]
    
    for input_text in clear_inputs:
        agent.process_input(input_text)
    
    # Statistics
    print("\n" + "=" * 80)
    print("CLARIFICATION STATISTICS")
    print("=" * 80)
    
    print(f"\nTotal clarifications asked: {len(agent.clarification_history)}")
    
    if agent.clarification_history:
        print(f"\nAmbiguity types encountered:")
        types = {}
        for item in agent.clarification_history:
            amb_type = item['analysis']['ambiguity_type']
            types[amb_type] = types.get(amb_type, 0) + 1
        
        for amb_type, count in types.items():
            print(f"  - {amb_type}: {count}")
    
    # Summary
    print("\n" + "=" * 80)
    print("SUMMARY")
    print("=" * 80)
    print("""
Clarification & Disambiguation Pattern:
- Detects ambiguous or unclear user inputs
- Generates targeted clarification questions
- Resolves ambiguity through progressive refinement
- Improves accuracy and user satisfaction

Types of Ambiguity Handled:
āœ“ Lexical - Multiple word meanings
āœ“ Syntactic - Unclear sentence structure
āœ“ Semantic - Vague or imprecise terms
āœ“ Referential - Unclear references

Key Benefits:
āœ“ Reduces errors from misunderstanding
āœ“ Improves task completion accuracy
āœ“ Better user experience
āœ“ Handles complex, real-world inputs
āœ“ Proactive problem prevention

Best Practices:
• Ask specific, targeted questions
• Provide options when helpful
• Keep questions natural and conversational
• Prioritize most important clarifications
• Track clarification history for learning
    """)


if __name__ == "__main__":
    demonstrate_clarification()

    

Agentic Design Pattern: Clarification

Pattern 129: Clarification – Line-by-Line Explanation

Overview

This document provides a detailed line-by-line explanation of the Clarification pattern implementation.


Pattern Description

Pattern 129: Clarification & Disambiguation

Description: Detects ambiguous or unclear user inputs and asks targeted clarifying questions to resolve uncertainties before proceeding.

Components: – Ambiguity detection – Clarification question generation – Context tracking – Progressive refinement

Use Cases: – Conversational agents handling ambiguous queries – Complex instruction following – Reducing errors from misunderstanding

LangChain Implementation: Uses LangChain to detect ambiguity and generate appropriate clarification questions.


File Statistics

  • Total Lines: 354
  • Classes: 3
  • Functions: 1
  • Constants: 0

Module Documentation

"""
Pattern 129: Clarification & Disambiguation

Description:
    Detects ambiguous or unclear user inputs and asks targeted clarifying questions
    to resolve uncertainties before proceeding.

Components:
    - Ambiguity detection
    - Clarification question generation
    - Context tracking
    - Progressive refinement

Use Cases:
    - Conversational agents handling ambiguous queries
    - Complex instruction following
    - Reducing errors from misunderstanding

LangChain Implementation:
    Uses LangChain to detect ambiguity and generate appropriate clarification questions.
"""

Purpose: This module implements the Clarification pattern, providing a structured approach to agent design and implementation.


Import Statements

Standard Library Imports

import langchain_core.output_parsers.JsonOutputParser
import langchain_core.output_parsers.StrOutputParser
import langchain_core.prompts.ChatPromptTemplate
import langchain_core.pydantic_v1.BaseModel
import langchain_core.pydantic_v1.Field
import os
import typing.Any
import typing.Dict
import typing.List
import typing.Optional

Purpose: Core Python functionality: – langchain_core.output_parsers.JsonOutputParser: Regular expression operations – langchain_core.output_parsers.StrOutputParser: Regular expression operations – langchain_core.prompts.ChatPromptTemplate: Regular expression operations – langchain_core.pydantic_v1.BaseModel: Regular expression operations – langchain_core.pydantic_v1.Field: Regular expression operations – os: Environment variable access and system operations – typing.Any: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Dict: Type hints for better code documentation (List, Dict, Any, Optional) – typing.List: Type hints for better code documentation (List, Dict, Any, Optional) – typing.Optional: Type hints for better code documentation (List, Dict, Any, Optional)

LangChain Imports

from langchain_openai.ChatOpenAI

Purpose: LangChain framework components: – LLM Interface: Wrapper for OpenAI models – Chains: Composable building blocks for complex workflows

Other Imports

from dotenv.load_dotenv

Purpose: Additional dependencies for specialized functionality.


Classes

Class: AmbiguityAnalysis

Lines 35-41

Inherits from: BaseModel

Purpose:

Analysis of ambiguity in user input

Class: ClarificationQuestion

Lines 44-48

Inherits from: BaseModel

Purpose:

A clarification question

Class: ClarificationAgent

Lines 51-212

Purpose:

Agent for handling ambiguity and generating clarifications

Methods:

__init__(self, model, temperature)

Lines 54-102

Purpose: Initialize the class instance with required parameters.

Parameters: – model: Language model instance – temperature: Sampling temperature (0-1)

detect_ambiguity(self, user_input, context)

Lines 104-119

Purpose:

Detect if input is ambiguous

Parameters: – user_input: Input data – context: Context

Returns: AmbiguityAnalysis

generate_clarification(self, user_input, analysis)

Lines 121-142

Purpose:

Generate clarification question

Parameters: – user_input: Input data – analysis: Analysis

Returns: ClarificationQuestion

process_input(self, user_input, context)

Lines 144-187

Purpose:

Process input and return clarification if needed

Parameters: – user_input: Input data – context: Context

Returns: <ast.Subscript object at 0x000001A06D7EEA90>

resolve_with_clarification(self, original_input, clarification_response)

Lines 189-212

Purpose:

Process clarified input

Parameters: – original_input: Input data – clarification_response: Clarification response

Returns: str


Functions

demonstrate_clarification()

Lines 215-349

Purpose:

Demonstrate clarification and disambiguation

Key Concepts Summary

Pattern Overview

The Clarification pattern provides a structured approach to building AI agents with specific capabilities and behaviors. This implementation leverages LangChain’s composable components to create a robust and maintainable solution.

Core Components

  1. Agent Class: Main implementation of the pattern logic
  2. Prompt Engineering: Carefully crafted prompts to guide LLM behavior
  3. Tool Integration: Optional tools for extended functionality
  4. Memory Management: State handling across interactions
  5. Output Parsing: Structured response processing

When to Use

āœ… Good for: – Scenarios requiring this specific pattern’s strengths – Applications needing structured agent behavior – Systems where explainability is important – Production environments requiring reliability

āŒ Not ideal for: – Simple single-prompt tasks – Scenarios requiring different pattern characteristics – Resource-constrained environments (if computationally intensive)


Best Practices Demonstrated

1. Environment Configuration

  • āœ… Use .env files for API keys
  • āœ… Never hardcode credentials
  • āœ… Validate environment variables on startup

2. Error Handling

  • āœ… Graceful degradation on failures
  • āœ… Informative error messages
  • āœ… Logging for debugging

3. Prompt Engineering

  • āœ… Clear, specific instructions
  • āœ… Structured output formats
  • āœ… Few-shot examples when beneficial

4. Code Organization

  • āœ… Separation of concerns (class-based design)
  • āœ… Type hints for clarity
  • āœ… Docstrings for documentation

5. Performance Optimization

  • āœ… Appropriate temperature settings
  • āœ… Token budget management
  • āœ… Caching where applicable

LangChain Components Used

  • ChatOpenAI: OpenAI model interface
  • Chains: Sequential component composition

Why LangChain? – Composable abstractions for LLM applications – Rich ecosystem of integrations – Production-ready patterns – Active community and development


Execution Flow

1. Initialize agent with configuration
   ↓
2. Load environment and dependencies
   ↓
3. Prepare prompt with input data
   ↓
4. Invoke LLM through LangChain
   ↓
5. Parse and validate response
   ↓
6. Return formatted result

Key Decision Points: – Model selection (temperature, model type) – Prompt structure (few-shot vs zero-shot) – Error handling strategy – Output format validation


Performance Considerations

Computational Cost

  • LLM Calls: Primary cost factor (based on tokens)
  • Latency: Depends on model and input size
  • Memory: Minimal for most patterns

Optimization Strategies

  1. Prompt Optimization: Minimize tokens while maintaining clarity
  2. Caching: Cache repeated queries/responses
  3. Model Selection: Use smaller models when appropriate
  4. Batch Processing: Group similar requests
  5. Streaming: For long responses, stream tokens

Cost Estimates (Approximate)

  • GPT-3.5-turbo: ~$0.002 per request
  • GPT-4: ~$0.03 per request
  • GPT-4-turbo: ~$0.01 per request

Actual costs vary based on input/output token counts


Common Pitfalls to Avoid

1. Prompt Design Issues

āŒ Vague or ambiguous instructions
āœ… Clear, specific, testable prompts

2. Error Handling

āŒ Ignoring API errors or rate limits
āœ… Implement retry logic and graceful degradation

3. Token Management

āŒ Exceeding model token limits
āœ… Truncate inputs, chunk processing

4. Security

āŒ Exposing API keys in code
āœ… Use environment variables

5. Testing

āŒ Only testing happy paths
āœ… Test edge cases, failures, malformed inputs

6. Cost Management

āŒ Unmonitored API usage
āœ… Implement budgets, alerts, and monitoring


Further Improvements

Production Enhancements

  1. Monitoring: Add logging, metrics, tracing
  2. Validation: Input/output schema validation
  3. Rate Limiting: Implement request throttling
  4. Caching: Redis or in-memory cache for responses
  5. Async Support: Non-blocking operations for scalability

Feature Extensions

  1. Multi-Model Support: Switch between providers
  2. Fallback Models: Graceful degradation strategy
  3. Custom Tools: Domain-specific function calling
  4. Advanced Memory: Vector stores for long-term memory
  5. Evaluation: Automated testing and benchmarking

Code Quality

  1. Testing: Unit tests, integration tests
  2. Documentation: API docs, usage examples
  3. Type Safety: Stricter type hints
  4. Configuration: Externalized config files

Related Patterns

  • Pattern 128
  • Pattern 130

Pattern Families: – Reasoning Patterns (001-011) – Multi-Agent Patterns (012-019) – Tool Use Patterns (020-025) – Memory Patterns (026-031) – And many more…


Summary

The Clarification pattern provides a powerful approach to building AI agents with specific capabilities. This implementation demonstrates:

āœ… Key Strengths: – Clean, maintainable code structure – LangChain integration for composability – Type hints and documentation – Production-ready patterns

šŸŽÆ Best Use Cases: – Applications requiring this pattern’s characteristics – Systems needing structured agent behavior – Production environments requiring reliability

šŸ“š Learning Resources: – LangChain Documentation: https://python.langchain.com/ – Pattern Documentation: See related patterns – OpenAI API Reference: https://platform.openai.com/docs/


File: 129_clarification.py
Total Lines: 354
Pattern Category: AI Agent Design Pattern #129


Generated from actual implementation code
For educational and reference purposes

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *