# 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. ```python 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. ```python # 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. ```python _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. ```python 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. ```python @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.