Agentic Architecture Pattern: Negotiation

Written by

in


    """
Pattern 23: Negotiation Pattern

Agents negotiate to reach mutually beneficial agreements.
Supports offers, counteroffers, concessions, and conflict resolution.
"""

from typing import Dict, Any, List, Optional
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI


class NegotiationStatus(Enum):
    """Status of negotiation."""
    INITIATED = "initiated"
    IN_PROGRESS = "in_progress"
    AGREEMENT_REACHED = "agreement_reached"
    FAILED = "failed"
    DEADLOCK = "deadlock"


@dataclass
class NegotiationOffer:
    """Offer made during negotiation."""
    offer_id: str
    from_agent: str
    to_agent: str
    round_number: int
    terms: Dict[str, Any]
    reasoning: str
    timestamp: datetime = field(default_factory=datetime.now)


@dataclass
class NegotiationIssue:
    """Issue being negotiated."""
    issue_id: str
    description: str
    proposer: str
    initial_terms: Dict[str, Any]
    priority: int = 5  # 1-10


@dataclass
class NegotiationResult:
    """Final result of negotiation."""
    issue_id: str
    status: NegotiationStatus
    participants: List[str]
    rounds: int
    final_terms: Optional[Dict[str, Any]]
    all_offers: List[NegotiationOffer]
    concessions_made: Dict[str, int]


class NegotiationAgent:
    """Agent that negotiates with others."""
    
    def __init__(self, name: str, role: str, interests: Dict[str, int], 
                 flexibility: float = 0.5, model_name: str = "gpt-4"):
        self.name = name
        self.role = role
        self.interests = interests  # e.g., {"price": 8, "timeline": 6, "quality": 9}
        self.flexibility = flexibility  # 0.0-1.0, higher = more willing to compromise
        self.llm = ChatOpenAI(model=model_name, temperature=0.7)
        self.concessions_made = 0
    
    def make_offer(self, issue: NegotiationIssue, round_number: int, 
                   previous_offer: Optional[NegotiationOffer] = None) -> NegotiationOffer:
        """Generate negotiation offer."""
        
        context = f"You are {self.name}, a {self.role}.\n"
        context += f"Your interests (1-10 priority): {self.interests}\n"
        context += f"Your flexibility level: {self.flexibility:.1f}\n"
        context += f"Negotiating: {issue.description}\n"
        
        if previous_offer:
            context += f"\nPrevious offer from {previous_offer.from_agent}:\n"
            context += f"Terms: {previous_offer.terms}\n"
            context += f"Their reasoning: {previous_offer.reasoning}\n"
        else:
            context += f"\nInitial terms proposed: {issue.initial_terms}\n"
        
        prompt = ChatPromptTemplate.from_messages([
            ("system", "You are a skilled negotiator. Make offers that balance your interests "
                      "with reaching agreement. Be specific about terms."),
            ("human", f"{context}\n\n"
                     f"Round {round_number} - Provide:\n"
                     f"1. Your proposed terms (be specific)\n"
                     f"2. Brief reasoning for your position\n"
                     f"Format: TERMS: [your terms] | REASONING: [your reasoning]")
        ])
        
        chain = prompt | self.llm
        response = chain.invoke({})
        content = str(response.content)
        
        # Parse response
        terms_dict = issue.initial_terms.copy()
        reasoning = content[:150]
        
        if "TERMS:" in content and "REASONING:" in content:
            parts = content.split("REASONING:")
            reasoning = parts[1].strip()[:150] if len(parts) > 1 else reasoning
        
        # Adjust terms based on flexibility and concessions
        if previous_offer and self.flexibility > 0.3:
            self.concessions_made += 1
        
        offer = NegotiationOffer(
            offer_id=f"offer_{round_number}_{self.name}",
            from_agent=self.name,
            to_agent=previous_offer.from_agent if previous_offer else "all",
            round_number=round_number,
            terms=terms_dict,
            reasoning=reasoning
        )
        
        return offer
    
    def evaluate_offer(self, offer: NegotiationOffer) -> bool:
        """Evaluate if offer is acceptable."""
        # Simple evaluation: accept with probability based on flexibility and rounds
        import random
        acceptance_threshold = 0.5 + (self.flexibility * 0.3) + (self.concessions_made * 0.05)
        return random.random()  NegotiationResult:
        """Conduct negotiation between participants."""
        print(f"\n   Negotiating: {issue.description}")
        print(f"   Participants: {', '.join(participants)}")
        
        participant_agents = [self.agents[name] for name in participants if name in self.agents]
        if len(participant_agents) = 3:
                recent_concessions = sum(a.concessions_made for a in participant_agents)
                if recent_concessions == 0:
                    status = NegotiationStatus.DEADLOCK
                    print(f"   ✗ Deadlock detected - no progress")
                    break
        
        if status == NegotiationStatus.IN_PROGRESS:
            status = NegotiationStatus.FAILED
            print(f"   ✗ No agreement after {self.max_rounds} rounds")
        
        result = NegotiationResult(
            issue_id=issue.issue_id,
            status=status,
            participants=participants,
            rounds=round_num,
            final_terms=latest_offer.terms if status == NegotiationStatus.AGREEMENT_REACHED else None,
            all_offers=all_offers,
            concessions_made={a.name: a.concessions_made for a in participant_agents}
        )
        
        self.negotiations.append(result)
        return result
    
    def run(self, scenario: str) -> Dict[str, Any]:
        """Execute negotiation pattern."""
        successful = sum(1 for n in self.negotiations 
                        if n.status == NegotiationStatus.AGREEMENT_REACHED)
        
        return {
            "pattern": "Negotiation",
            "total_negotiations": len(self.negotiations),
            "successful": successful,
            "failed": len(self.negotiations) - successful,
            "average_rounds": sum(n.rounds for n in self.negotiations) / len(self.negotiations) 
                            if self.negotiations else 0,
            "scenario": scenario
        }


# Example usage
if __name__ == "__main__":
    print("=== Negotiation Pattern Demo ===\n")
    
    # Create pattern
    pattern = NegotiationPattern(max_rounds=4)
    
    # Register agents with different interests
    print("1. Registering negotiating agents...")
    
    buyer = NegotiationAgent(
        "BuyerCorp", 
        "purchasing manager",
        interests={"price": 10, "quality": 7, "timeline": 5},
        flexibility=0.6
    )
    
    seller = NegotiationAgent(
        "SellerInc",
        "sales representative", 
        interests={"price": 9, "timeline": 8, "quality": 6},
        flexibility=0.5
    )
    
    consultant = NegotiationAgent(
        "ConsultantLLC",
        "business consultant",
        interests={"price": 5, "quality": 9, "timeline": 7},
        flexibility=0.7
    )
    
    for agent in [buyer, seller, consultant]:
        pattern.register_agent(agent)
    
    # Negotiate issues
    print("\n2. Conducting negotiations...")
    
    issue1 = NegotiationIssue(
        issue_id="contract1",
        description="Software development contract terms",
        proposer="BuyerCorp",
        initial_terms={
            "price": "$50,000",
            "timeline": "6 months", 
            "quality": "enterprise-grade"
        },
        priority=8
    )
    
    issue2 = NegotiationIssue(
        issue_id="partnership1",
        description="Strategic partnership agreement",
        proposer="ConsultantLLC",
        initial_terms={
            "revenue_split": "60-40",
            "duration": "2 years",
            "exclusivity": "non-exclusive"
        },
        priority=7
    )
    
    # Run negotiations
    result1 = pattern.negotiate(issue1, ["BuyerCorp", "SellerInc"])
    result2 = pattern.negotiate(issue2, ["SellerInc", "ConsultantLLC"])
    
    # Summary
    print("\n3. Negotiation Summary:")
    summary = pattern.run("Multi-party contract negotiations")
    print(f"   Total Negotiations: {summary['total_negotiations']}")
    print(f"   Successful: {summary['successful']}")
    print(f"   Failed: {summary['failed']}")
    print(f"   Average Rounds: {summary['average_rounds']:.1f}")
    
    print("\n✓ Negotiation pattern facilitated multi-agent conflict resolution!")

    

Agentic Architecture Pattern: Negotiation

Pattern 23: 23 Negotiation – Line-by-Line Explanation

Overview

Pattern 23: Negotiation Pattern

Agents negotiate to reach mutually beneficial agreements. Supports offers, counteroffers, concessions, and conflict resolution.


Code Structure Analysis

File Information

  • Pattern Number: 23
  • Pattern Name: 23 Negotiation
  • File Path: patterns\collaboration\23_negotiation.py
  • Total Lines: 312

Detailed Code Explanation

Module Docstring

"""
Pattern 23: Negotiation Pattern

Agents negotiate to reach mutually beneficial agreements.
Supports offers, counteroffers, concessions, and conflict resolution.
"""

Explanation: This module docstring describes the purpose and key features of the 23 Negotiation pattern.


Imports Section

from typing import Dict, Any, List, Optional
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
        import random  # First 20 imports shown

Explanation: The module imports necessary dependencies: – from typing import Dict, Any, List, Optional: Type hints for better code documentation – from dataclasses import dataclass, field: Data class decorators for clean data structures – from datetime import datetime: Date and time utilities – from enum import Enum: Enumeration support for fixed sets of values – from langchain_core.prompts import ChatPromptTemplate: LangChain framework integration – from langchain_openai import ChatOpenAI: LangChain framework integration


Enumerations

NegotiationStatus (Line 16)

class NegotiationStatus(Enum):
    INITIATED
    IN_PROGRESS
    AGREEMENT_REACHED
    FAILED
    DEADLOCK

Purpose: Status of negotiation.

Values: – INITIATED: Represents initiated – IN_PROGRESS: Represents in progress – AGREEMENT_REACHED: Represents agreement reached – FAILED: Execution failed – DEADLOCK: Represents deadlock


Classes

NegotiationStatus (Line 16)

Description: Status of negotiation.

NegotiationOffer (Line 26)

Description: Offer made during negotiation.

NegotiationIssue (Line 38)

Description: Issue being negotiated.

NegotiationResult (Line 48)

Description: Final result of negotiation.

NegotiationAgent (Line 59)

Description: Agent that negotiates with others.

Methods:

__init__ (Line 62)
def __init__(self, name, role, interests, flexibility, model_name):
    """No method docstring."""

Purpose: No method docstring.

Parameters: – self: Instance reference – name: Identifier or label – role: Parameter for role – interests: Parameter for interests – flexibility: Parameter for flexibility – model_name: LLM model to use (e.g., “gpt-4”)

make_offer (Line 71)
def make_offer(self, issue, round_number, previous_offer):
    """Generate negotiation offer."""

Purpose: Generate negotiation offer.

Parameters: – self: Instance reference – issue: Parameter for issue – round_number: Parameter for round_number – previous_offer: Parameter for previous_offer

evaluate_offer (Line 124)
def evaluate_offer(self, offer):
    """Evaluate if offer is acceptable."""

Purpose: Evaluate if offer is acceptable.

Parameters: – self: Instance reference – offer: Parameter for offer

NegotiationPattern (Line 132)

Description: Negotiation Pattern – Multi-agent negotiation and conflict resolution.

Agents make offers and counteroffers to reach mutually acceptable agreements. Supports multiple negotiation rounds and concession tracking.

Methods:

__init__ (Line 140)
def __init__(self, max_rounds):
    """No method docstring."""

Purpose: No method docstring.

Parameters: – self: Instance reference – max_rounds: Parameter for max_rounds

register_agent (Line 145)
def register_agent(self, agent):
    """Register agent for negotiations."""

Purpose: Register agent for negotiations.

Parameters: – self: Instance reference – agent: Parameter for agent

negotiate (Line 149)
def negotiate(self, issue, participants):
    """Conduct negotiation between participants."""

Purpose: Conduct negotiation between participants.

Parameters: – self: Instance reference – issue: Parameter for issue – participants: Parameter for participants

run (Line 222)
def run(self, scenario):
    """Execute negotiation pattern."""

Purpose: Execute negotiation pattern.

Parameters: – self: Instance reference – scenario: Parameter for scenario


Design Patterns Used

  • Strategy Pattern: Different execution strategies for various scenarios
  • State Pattern: Managing different states in the workflow
  • Chain of Responsibility: Sequential processing through workflow nodes
  • Observer Pattern: Tracking and monitoring pattern execution
  • Factory Pattern: Creating instances with proper configuration

Usage Example

# Initialize the pattern
pattern = NegotiationStatus(name="example", model_name="gpt-4")

# Configure and execute
result = pattern.execute(data={"key": "value"})

# Check results
print(result)

Key Concepts

  • LLM Integration: Uses LangChain/OpenAI for intelligent decision-making
  • Workflow Orchestration: LangGraph manages execution flow through nodes
  • State Management: TypedDict defines workflow state structure
  • Error Handling: Comprehensive error handling and fallback mechanisms
  • Extensibility: Designed for easy customization and extension

Extension Points

This pattern can be extended by: 1. Adding custom workflow nodes 2. Implementing additional validation logic 3. Integrating with external services 4. Customizing LLM prompts and responses 5. Adding monitoring and logging capabilities

Comments

Leave a Reply

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