toneforge / docs /agent_features.md
Kush-Singh-26
corrected models and added test prompts
ebefa8f
|
Raw
History Blame Contribute Delete
5.89 kB

Detailed Documentation: agent_features.py (Developer 2 Domain)

This file manages the Specialized AI Agents: Multi-Agent Negotiation and the Legal Parser.


1. Environment & API Key Guard (Best Practice)

At the top of the file, we handle environment loading and an API key guard.

import os
from dotenv import load_dotenv

# 0. Load the .env file immediately
load_dotenv()

# Guard: Check if the key exists BEFORE initializing models
if not os.getenv("GROQ_API_KEY"):
    raise ValueError("GROQ_API_KEY is missing. Check your .env file.")
  • Viva Point: This check ensures the LLM's constructor (which will look for the key) doesn't fail with a confusing error message later in the code.

2. Multi-Agent System Roles & Temperatures

We use the state-of-the-art OpenAI GPT-OSS 120B model for these tasks due to the high reasoning requirements of negotiation and legal analysis.

# Using openai/gpt-oss-120b for all tasks - high reasoning for negotiation and legal
proposer_llm = ChatGroq(model="openai/gpt-oss-120b", temperature=0.5)
responder_llm = ChatGroq(model="openai/gpt-oss-120b", temperature=0.6)
evaluator_llm = ChatGroq(model="openai/gpt-oss-120b", temperature=0.0)
legal_llm = ChatGroq(model="openai/gpt-oss-120b", temperature=0.0)
  • OpenAI GPT-OSS 120B: This model provides exceptional reasoning capabilities essential for multi-agent negotiation and legal analysis.
  • Proposer & Responder: Higher temperatures (0.5-0.6) allow for dynamic negotiation and creative counter-offers.
  • Evaluator & Legal: Set to 0.0 for maximum precision. These agents MUST be objective and never "hallucinate" or vary their logic.

3. The Cyclic Multi-Agent Graph

This is the Cyclic Logic that makes the agents talk to each other.

_nw = StateGraph(NegotiationState)
_nw.add_node("proposer", _node_proposer)
_nw.add_node("responder", _node_responder)
_nw.add_node("evaluator", _node_evaluator)

_nw.add_edge(START, "proposer")
_nw.add_edge("proposer", "responder")
_nw.add_edge("responder", "evaluator")

_nw.add_conditional_edges(
    "evaluator",
    _route_negotiation,
    {
        "continue": "proposer",
        "end": END,
    },
)
  • Step 1: The proposer makes an offer on our behalf.
  • Step 2: The responder simulates the other party, countering or accepting.
  • Step 3: The evaluator (the "referee") looks at the thread and decides if terms are agreed upon.
  • Step 4: _route_negotiation (the loop brain) repeats the process unless agreement is reached or the max_rounds limit is hit.

4. Legal Document Parser

This feature uses a highly detailed Pydantic model to extract structured data from complex legal text.

class LegalParseOutput(BaseModel):
    obligations: list[str] = Field(description="Explicit duties/commitments found")
    deadlines: list[str] = Field(description="Dates, deadlines, or time constraints")
    clauses: list[LegalClause] = Field(
        description="Identified legal clauses with risk levels"
    )
    risk_flags: list[str] = Field(description="High-risk words or phrases")
    overall_risk: Literal["low", "medium", "high"] = Field(
        description="Overall legal risk rating"
    )
    plain_summary: str = Field(description="Plain-English explanation of commitments")
  • Viva Point: This model forces the LLM to output structured risk data. Notice how overall_risk is constrained to a Literal of only three values, ensuring consistent data for our frontend UI.

5. API Endpoint Implementation

The bridge between HTTP and the AI graphs.

@router.post("/negotiate_email")
async def negotiate_email(request: NegotiationRequest):
    result = await neg_graph.ainvoke({
        "topic": request.topic,
        "our_position": request.our_position,
        "their_position": request.their_position,
        "category": request.category,
        "rounds": 0,
        "max_rounds": request.max_rounds,
        "history": [],
        "evaluator_decision": None,
        "agreement_reached": False,
    })
    return {
        "topic": request.topic,
        "rounds_completed": result["rounds"],
        "agreement_reached": result["agreement_reached"],
        "summary": result["evaluator_decision"].summary,
        "email_thread": [
            {"role": e.role, "subject": e.subject, "body": e.body}
            for e in result["history"]
        ],
    }
  • Asynchronous Execution: Like the formalizer, this uses ainvoke so that long negotiation loops don't block the entire server.
  • Summary Retrieval: Notice the check: result["evaluator_decision"].summary extracts the final verdict from the referee agent.

6. Available Tasks & Functional Logic

A. Multi-Agent Negotiation Simulation

Purpose: Helps users practice or simulate a difficult email negotiation before sending actual mail.

  • Proposer Agent: Acts as the user's advocate. It tries to achieve the user's "Our Position" using persuasive but professional language.
  • Responder Agent: Simulates the other party. It acts realistically—rejecting unfair offers and suggesting compromises.
  • Evaluator Agent: Acts as the "Referee." It reads the back-and-forth history and identifies when an agreement has been reached, providing a final summary of the agreed terms.

B. Legal & Contract Parser

Purpose: Simplifies "legalese" and identifies hidden risks in contracts.

  • Risk Scorecard: Identifies "Risk Flags" like indemnification, irrevocability, or sole discretion that might be dangerous for a user.
  • Commitment Extraction: Clearly lists all Obligations (what you MUST do) and Deadlines (WHEN you must do it).
  • Plain Summary: Translates complex legal clauses into 2-3 simple English sentences so anyone can understand what they are signing.