|
Download README.md from PrernaKu/LegalDoc: direct link, hf CLI and curl.
- Browser
- Download file 16.4 kB
-
https://huggingface.co/spaces/PrernaKu/LegalDoc/resolve/main/README.md
- Command line
-
hf download hf://spaces/PrernaKu/LegalDoc/README.md
-
curl -L -o README.md https://huggingface.co/spaces/PrernaKu/LegalDoc/resolve/main/README.md
16.4 kB
| title: Multi-Agent Autonomous Procurement System | |
| emoji: π’ | |
| colorFrom: blue | |
| colorTo: purple | |
| sdk: docker | |
| sdk_version: "3.11" | |
| python_version: "3.11" | |
| app_file: app.py | |
| pinned: false | |
| # Multi-Agent Autonomous Procurement System | |
| A production-ready implementation of a multi-agent procurement orchestration system using **LangGraph**, **LangChain**, and **LangSmith**. This system demonstrates enterprise-grade agent design patterns including human-in-the-loop interruptions, stateful graph execution, and comprehensive observability. | |
| ## π Overview | |
| This POC implements a complete procurement workflow where autonomous agents collaborate to: | |
| 1. **Discover vendors** through intelligent search | |
| 2. **Analyze pricing** against budget constraints | |
| 3. **Pause for human approval** with persistent state | |
| 4. **Generate legal contracts** post-approval | |
| The system showcases modern LLM orchestration patterns suitable for production deployment. | |
| ``` | |
| START β ResearchAgent β AnalysisAgent β [Budget Check] | |
| ββ OK: HumanApproval (INTERRUPTS) β LegalAgent β END | |
| ββ Exceeded: END (Reject) | |
| ``` | |
| ## π― Key Features | |
| ### Core Architecture | |
| - **Multi-Agent Orchestration**: 3 specialized agents with clear responsibilities | |
| - **Stateful Execution**: Persistent state management via `MemorySaver` (upgradable to PostgreSQL) | |
| - **Human-in-the-Loop**: Graph pauses at interruption points for external decision-making | |
| - **Production Patterns**: Uses modern LangGraph Command-based routing | |
| ### Observability | |
| - **Full LangSmith Tracing**: Every LLM call, tool invocation, and state transition logged | |
| - **Audit Trail**: Complete decision history in graph state | |
| - **Performance Metrics**: Latency, token usage, and cost tracking | |
| ### Enterprise-Ready | |
| - **Error Handling**: Graceful failure modes and retry logic | |
| - **Type Safety**: Strict TypedDict schema for all state | |
| - **Modular Design**: Agents, tools, and graph cleanly separated | |
| - **Async Support**: Non-blocking workflow execution | |
| ## π Project Structure | |
| ``` | |
| . | |
| βββ procurement_system.py # Main implementation (production-grade) | |
| βββ advanced_examples.py # Additional patterns and integrations | |
| βββ setup_and_deployment.md # Detailed setup & production guide | |
| βββ EXECUTION_WALKTHROUGH.md # Step-by-step execution trace | |
| βββ requirements.txt # Python dependencies | |
| βββ README.md # This file | |
| ``` | |
| ## π Quick Start | |
| ### 1. Setup Environment | |
| ```bash | |
| # Clone or download project | |
| cd /path/to/procurement_system | |
| # Create virtual environment | |
| python -m venv venv | |
| source venv/bin/activate # Windows: venv\Scripts\activate | |
| # Install dependencies | |
| pip install -r requirements.txt | |
| ``` | |
| ### 2. Configure API Keys | |
| Create a `.env` file in the project root: | |
| ```bash | |
| # Required: Anthropic API Key | |
| ANTHROPIC_API_KEY="sk-ant-xxx" | |
| # Optional but recommended: LangSmith for observability | |
| LANGSMITH_API_KEY="lsk_xxx" | |
| LANGSMITH_PROJECT="procurement_system" | |
| ``` | |
| ### 3. Run the System | |
| ```bash | |
| # Execute complete workflow with human approval | |
| python procurement_system.py | |
| # Output will show: | |
| # - Stage 1: Vendor research and analysis β Graph pauses | |
| # - Stage 2: Human reviews state at interruption | |
| # - Stage 3: State updated with approval β Graph resumes | |
| # - Stage 4: Contract generated and printed | |
| ``` | |
| ## π Usage Examples | |
| ### Basic Workflow Execution | |
| ```python | |
| from langchain_anthropic import ChatAnthropic | |
| from procurement_system import build_procurement_graph, ProcurementWorkflowExecutor | |
| # Initialize | |
| llm = ChatAnthropic(model="claude-3-5-sonnet-20241022") | |
| graph, memory, agents = build_procurement_graph(llm) | |
| executor = ProcurementWorkflowExecutor(graph, memory) | |
| # Start workflow (executes until interruption point) | |
| state = executor.start_workflow( | |
| procurement_request="Enterprise cloud infrastructure with auto-scaling", | |
| budget_limit=5500.0 | |
| ) | |
| # Inspect state for human review | |
| current_state = executor.inspect_state() | |
| # Human approves vendor | |
| final_state = executor.approve_vendor(approval=True) | |
| # Access generated contract | |
| print(final_state["contract_draft"]) | |
| ``` | |
| ### Programmatic State Inspection | |
| ```python | |
| from advanced_examples import WorkflowStateInspector | |
| inspector = WorkflowStateInspector(graph, thread_id="procurement_20250628_145230") | |
| # Get current state | |
| state = inspector.get_current_state() | |
| # Analyze decision trail | |
| decisions = inspector.get_decision_trail() | |
| # Generate audit report | |
| report = inspector.generate_audit_report() | |
| print(report) | |
| ``` | |
| ### Vendor Comparison | |
| ```python | |
| from advanced_examples import VendorComparison | |
| # Get vendor comparison matrix | |
| comparison = VendorComparison.build_comparison_matrix(state, llm) | |
| print(comparison) | |
| ``` | |
| ### Metrics & Analytics | |
| ```python | |
| from advanced_examples import ProcurementMetrics | |
| metrics = ProcurementMetrics() | |
| # Calculate KPIs | |
| cycle_time = metrics.calculate_procurement_cycle_time(state) | |
| budget_utilization = metrics.calculate_budget_utilization(state) | |
| vendor_variance = metrics.calculate_vendor_variance(state) | |
| print(f"Cycle Time: {cycle_time}s") | |
| print(f"Budget Utilization: {budget_utilization}%") | |
| print(f"Vendor Variance: {vendor_variance['variance_percentage']:.2f}%") | |
| ``` | |
| ## π System Components | |
| ### State Schema | |
| ```python | |
| class ProcurementState(TypedDict): | |
| procurement_request: str # What's being procured | |
| vendor_options: list[dict] # Candidate vendors from research | |
| selected_vendor: dict # Chosen vendor after analysis | |
| budget_limit: float # Maximum spending limit | |
| analysis_approved: bool # Budget validation result | |
| human_approved: bool # Human decision (set at interruption) | |
| contract_draft: str # Generated legal document | |
| logs: list[str] # Audit trail | |
| ``` | |
| ### Agent Nodes | |
| #### ResearchNode (Agent 1) | |
| **Responsibility**: Vendor discovery | |
| - **Input**: Procurement request | |
| - **Tool**: `mock_vendor_search` (mock for POC, integrate real APIs in production) | |
| - **Output**: `vendor_options` list with pricing and capabilities | |
| - **Logic**: Searches for vendors matching requirement | |
| #### AnalysisNode (Agent 2) | |
| **Responsibility**: Financial validation and recommendation | |
| - **Input**: Vendor list, budget limit | |
| - **LLM Call**: Claude analyzes vendors for best fit | |
| - **Tools**: | |
| - `validate_budget`: Checks if vendor price β€ budget | |
| - **Routing**: | |
| - Budget OK β Send to approval gate | |
| - Budget exceeded β Terminate (rejection) | |
| - **Output**: `selected_vendor`, `analysis_approved` flag | |
| #### ApprovalGate (Human-in-the-Loop) | |
| **Responsibility**: Interruption point for external approval | |
| - **Behavior**: Graph pauses before legal_node | |
| - **State Persisted**: Via `MemorySaver` checkpoint | |
| - **External Update**: System updates `human_approved` flag | |
| - **Resumption**: Graph continues with updated state | |
| - **Use Case**: Slack notification, email approval, web dashboard | |
| #### LegalNode (Agent 3) | |
| **Responsibility**: Contract generation | |
| - **Input**: Approved vendor details, procurement request | |
| - **LLM Call**: Claude generates markdown purchase agreement | |
| - **Output**: `contract_draft` with complete legal terms | |
| - **Template**: Includes parties, services, pricing, terms, warranties | |
| ### Tools | |
| #### `mock_vendor_search(query: str) β list[dict]` | |
| Mock vendor discovery returning 3-5 vendors with details. | |
| In production, replace with: | |
| ```python | |
| @tool | |
| def real_vendor_search(query: str) -> list[dict]: | |
| """Query vendor APIs or internal procurement catalog.""" | |
| response = requests.get("https://vendor-api.com/search", params={"q": query}) | |
| return response.json() | |
| ``` | |
| #### `validate_budget(vendor_price: float, budget_limit: float) β dict` | |
| Budget validation returning: | |
| ```python | |
| { | |
| "within_budget": True/False, | |
| "variance": 500.00, | |
| "variance_percentage": 9.09, | |
| "status": "APPROVED" or "REJECTED" | |
| } | |
| ``` | |
| ## π LangSmith Integration | |
| ### Setup | |
| 1. **Create Account**: https://smith.langchain.com | |
| 2. **Get API Key**: Settings β API Keys | |
| 3. **Set Environment**: | |
| ```bash | |
| export LANGSMITH_API_KEY="lsk_xxx" | |
| export LANGSMITH_PROJECT="procurement_system" | |
| ``` | |
| 4. **Run System**: All LLM calls automatically traced | |
| ### Observability Features | |
| **Trace Tree View**: | |
| ``` | |
| Procurement Workflow Run #1 | |
| ββ research_node (5ms) | |
| β ββ Tool: mock_vendor_search | |
| ββ analysis_node (10ms) | |
| β ββ LLM: ChatAnthropic (1.23s, 342 tokens in, 198 out) | |
| β ββ Tool: validate_budget | |
| ββ approval_gate (1ms) [INTERRUPTED] | |
| β ββ *** PAUSED FOR HUMAN APPROVAL *** | |
| ββ legal_node (8ms) | |
| ββ LLM: ChatAnthropic (1.89s, 287 tokens in, 856 out) | |
| ``` | |
| **Analytics Dashboard**: | |
| - Total cost tracking | |
| - LLM model performance | |
| - Tool invocation success rates | |
| - Latency by agent | |
| - Token usage trends | |
| ## π Workflow Execution Timeline | |
| ### Complete Execution Flow | |
| ``` | |
| T+0ms: Graph starts with initial_state | |
| T+5ms: ResearchNode executes β vendor_options populated | |
| T+10ms: AnalysisNode executes β LLM recommendation + budget check | |
| If within budget: goto approval_gate | |
| If exceeded: goto END | |
| T+15ms: ApprovalGate processes β Graph PAUSES (checkpoint created) | |
| *** EXTERNAL SYSTEM *** | |
| Human reviews state at interruption | |
| Updates state: human_approved = True | |
| T+60s: Graph resumes from checkpoint | |
| T+61ms: ApprovalDecision routes β goto legal_node | |
| T+70ms: LegalNode executes β contract_draft generated | |
| T+75ms: Graph reaches END | |
| Total: ~75ms execution + human approval time | |
| ``` | |
| ## π οΈ Production Deployment | |
| ### Checkpointer Upgrade | |
| **Development (current)**: | |
| ```python | |
| from langgraph.checkpoint.memory import MemorySaver | |
| checkpointer = MemorySaver() # In-memory only | |
| ``` | |
| **Production**: | |
| ```python | |
| from langgraph.checkpoint.postgres import PostgresSaver | |
| checkpointer = PostgresSaver( | |
| conn_string="postgresql://user:pwd@localhost/langgraph" | |
| ) | |
| # Provides: Persistence, recovery, multi-worker support | |
| ``` | |
| ### Real Tool Integration | |
| ```python | |
| # Replace mock tools with production implementations | |
| @tool | |
| def real_vendor_search(query: str) -> list[dict]: | |
| """Search live vendor database.""" | |
| db = VendorDatabase() | |
| return db.search(query) | |
| @tool | |
| def real_budget_validator(vendor_id: str, budget: float) -> dict: | |
| """Query actual ERP system.""" | |
| erp = ERPClient() | |
| return erp.check_budget_allocation(vendor_id, budget) | |
| ``` | |
| ### Error Handling | |
| ```python | |
| def research_node(state: ProcurementState) -> Command: | |
| try: | |
| vendors = self.vendor_search_tool(state["procurement_request"]) | |
| if not vendors: | |
| raise ValueError("No vendors found") | |
| except Exception as e: | |
| logger.error(f"Research failed: {str(e)}") | |
| return Command(update={"logs": [f"ERROR: {str(e)}"]}, goto=END) | |
| return Command(update={"vendor_options": vendors, ...}, goto="analysis_node") | |
| ``` | |
| ### Async Execution | |
| ```python | |
| # For non-blocking workflows in web applications | |
| async def run_procurement_async(): | |
| async for event in graph.astream(initial_state, config): | |
| logger.info(f"Event: {event}") | |
| ``` | |
| ## π§ͺ Testing | |
| ### Unit Tests | |
| ```bash | |
| # Install test dependencies | |
| pip install pytest pytest-asyncio | |
| # Run tests | |
| pytest tests/ -v | |
| # Example test: | |
| def test_analysis_node_budget_exceeded(): | |
| state = ProcurementState( | |
| procurement_request="", | |
| vendor_options=[{"price_per_month": 10000}], | |
| budget_limit=1000, | |
| ... | |
| ) | |
| result = agents.analysis_node(state) | |
| assert result.goto == END | |
| ``` | |
| ### Integration Tests | |
| ```python | |
| # Test complete workflow | |
| def test_complete_workflow(): | |
| executor = ProcurementWorkflowExecutor(graph, memory) | |
| state = executor.start_workflow( | |
| procurement_request="Cloud infrastructure", | |
| budget_limit=5000 | |
| ) | |
| assert state["selected_vendor"]["name"] is not None | |
| assert state["analysis_approved"] == True | |
| ``` | |
| ## π Monitoring & Metrics | |
| ### Key Metrics to Track | |
| ```python | |
| from advanced_examples import ProcurementMetrics | |
| metrics = ProcurementMetrics() | |
| # Cycle time: Time from request to contract generation | |
| cycle_time = metrics.calculate_procurement_cycle_time(state) | |
| # Budget utilization: Percentage of budget used | |
| utilization = metrics.calculate_budget_utilization(state) | |
| # Vendor variance: Price spread across options | |
| variance = metrics.calculate_vendor_variance(state) | |
| # Dashboard queries (with PostgreSQL) | |
| # - Weekly procurement throughput | |
| # - Avg approval time | |
| # - Vendor selection patterns | |
| # - Budget compliance rate | |
| ``` | |
| ## π Security Considerations | |
| ### Secrets Management | |
| ```bash | |
| # Never commit API keys | |
| echo ".env" >> .gitignore | |
| # Use environment variables or secret manager | |
| export ANTHROPIC_API_KEY=$(aws secretsmanager get-secret-value --secret-id anthropic-key | jq -r .SecretString) | |
| ``` | |
| ### State Security | |
| - Sensitive data (contracts, pricing) stored in checkpointer | |
| - **Production**: Use encrypted PostgreSQL with TLS | |
| - **Access Control**: RBAC for state updates | |
| - **Audit Logging**: All approvals timestamped and logged | |
| ### LLM Safety | |
| ```python | |
| llm = ChatAnthropic( | |
| model="claude-3-5-sonnet-20241022", | |
| temperature=0.1, # Lower for consistent decisions | |
| max_tokens=4096, | |
| timeout=30, | |
| max_retries=3 | |
| ) | |
| ``` | |
| ## π Learning Resources | |
| ### Included Documentation | |
| - **EXECUTION_WALKTHROUGH.md**: Detailed trace of every step | |
| - **setup_and_deployment.md**: Production deployment guide | |
| - **advanced_examples.py**: Patterns for escalation, async, metrics | |
| ### External Resources | |
| - [LangGraph Documentation](https://langchain-ai.github.io/langgraph/) | |
| - [LangSmith Tracing Guide](https://docs.smith.langchain.com/) | |
| - [Claude API Documentation](https://docs.anthropic.com/) | |
| - [LangChain Tools Reference](https://python.langchain.com/docs/modules/tools/) | |
| ## π€ Contributing | |
| This is a POC for demonstration. To extend: | |
| 1. **Add More Agents**: Extend `ProcurementAgents` class | |
| 2. **Custom Tools**: Create `@tool` decorated functions | |
| 3. **New Routing Logic**: Add conditional edges to graph | |
| 4. **Monitoring**: Integrate with Datadog, New Relic, etc. | |
| ## π License | |
| This implementation is provided as-is for educational and demonstration purposes. | |
| ## β FAQ | |
| ### Q: How does the graph pause for human approval? | |
| A: Uses `interrupt_before=["legal_node"]` in graph compilation. When the graph reaches this point, it pauses and returns control to the application. External systems update the state with human decision, then the graph resumes. | |
| ### Q: Can I run multiple workflows concurrently? | |
| A: Yes! Each workflow gets a unique `thread_id`. The checkpointer (especially PostgreSQL) supports concurrent execution. | |
| ### Q: What's the difference between this and a simple chain? | |
| A: This uses a **StateGraph** with persistent state, routing decisions, and interruptions. A chain would be linear without branching or pauses. | |
| ### Q: How do I integrate with real vendor APIs? | |
| A: Replace `mock_vendor_search` with a real `@tool` that calls your vendor API. Same for budget validation. | |
| ### Q: Can I deploy this as a REST API? | |
| A: Yes! Wrap the executor in FastAPI: | |
| ```python | |
| @app.post("/procurement/start") | |
| async def start_procurement(request: dict): | |
| executor = ProcurementWorkflowExecutor(graph, memory) | |
| state = executor.start_workflow(...) | |
| return {"thread_id": executor.thread_id, "state": state} | |
| ``` | |
| ## π¨ Troubleshooting | |
| ### Graph doesn't pause at approval point | |
| - Verify `interrupt_before=["legal_node"]` in graph compilation | |
| - Check that state reaches approval_gate node | |
| - Review logs for routing decisions | |
| ### LangSmith traces not appearing | |
| - Verify `LANGSMITH_API_KEY` is set: `echo $LANGSMITH_API_KEY` | |
| - Check project name: `LANGSMITH_PROJECT="procurement_system"` | |
| - Ensure `langsmith` package installed: `pip list | grep langsmith` | |
| ### State not persisting across sessions | |
| - MemorySaver only persists while process is running | |
| - For persistence: upgrade to PostgresSaver | |
| - Verify checkpointer is passed to `graph.compile(checkpointer=...)` | |
| ### LLM calls failing | |
| - Check `ANTHROPIC_API_KEY` is valid | |
| - Verify model name: `claude-3-5-sonnet-20241022` | |
| - Check rate limits: https://console.anthropic.com | |
| --- | |
| **Ready to build production-grade multi-agent systems?** Start with `python procurement_system.py` and explore the execution walkthrough! | |