Spaces:
Sleeping
Sleeping
Download agents/agent_3_pattern_detection/agent.py from anil988/SentinelFin-AI-Backend: direct link, hf CLI and curl.
- Browser
- Download file 7.82 kB
-
https://huggingface.co/spaces/anil988/SentinelFin-AI-Backend/resolve/main/agents/agent_3_pattern_detection/agent.py
- Command line
-
hf download hf://spaces/anil988/SentinelFin-AI-Backend/agents/agent_3_pattern_detection/agent.py
-
curl -L -o agent.py https://huggingface.co/spaces/anil988/SentinelFin-AI-Backend/resolve/main/agents/agent_3_pattern_detection/agent.py
7.82 kB
| import os | |
| import sys | |
| import json | |
| import traceback | |
| from typing import Dict, Any | |
| # Using LangChain AWS Bedrock for LLM calls | |
| try: | |
| from langchain_core.messages import SystemMessage, HumanMessage | |
| HAS_LANGCHAIN = True | |
| except ImportError: | |
| HAS_LANGCHAIN = False | |
| sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) | |
| from integrations.bedrock_helper import get_bedrock_llm | |
| class PatternDetectionAgent: | |
| """ | |
| Agent 3: Pattern Detection Agent (β THE SHOWCASE AGENT). | |
| Calls an LLM (Llama 3.1) dynamically to write a detection script tailored to the | |
| specific suspected pattern, executes it safely, and returns the risk score. | |
| """ | |
| def _fallback_rule_based_detector(transactions: list, alert_type: str) -> Dict[str, Any]: | |
| """ | |
| EXCEPTION HANDLING FALLBACK: | |
| If LLM code generation fails, falls back to a static rule-based detector. | |
| """ | |
| print("[AGENT 3] [FALLBACK] Using static rule-based detector due to code generation/execution failure.") | |
| score = 0 | |
| if "STRUCTURING" in alert_type: | |
| # Simple static rule: multiple deposits near $10k | |
| near_threshold_count = sum(1 for t in transactions if 9000 <= t.get("amount", 0) <= 9999) | |
| if near_threshold_count >= 3: | |
| score = 85 | |
| elif "LAYERING" in alert_type: | |
| # Simple static rule: rapid wire in and wire out | |
| if len(transactions) >= 3: | |
| score = 75 | |
| return { | |
| "risk_score": score, | |
| "method": "STATIC_FALLBACK_RULE", | |
| "flagged_for_manual_review": True | |
| } | |
| def run(case_data: dict) -> Dict[str, Any]: | |
| alert_type = case_data.get("alert_type", "UNKNOWN") | |
| transactions = case_data.get("transactions", []) | |
| print(f"\n{'='*50}") | |
| print(f"[AGENT 3 π SHOWCASE] Dynamic Pattern Detection Initiated") | |
| print(f"Alert Type: {alert_type}") | |
| print(f"Transactions to analyze: {len(transactions)}") | |
| print(f"{'='*50}") | |
| # boto3 automatically picks up ~/.aws/credentials | |
| llm = get_bedrock_llm("dummy_key", temperature=0.1) | |
| if not HAS_LANGCHAIN or not llm: | |
| print("[AGENT 3] [WARNING] AWS_BEDROCK_KEY not found or dependencies missing. Forcing fallback mode for demo purposes.") | |
| # We will simulate the "wow" moment in the console for the demo if no key is present. | |
| print("\n[AGENT 3 - SIMULATED LLM CALL] Requesting Llama 3.1 to write detection function for: " + alert_type) | |
| simulated_code = """ | |
| def detect_pattern(transactions): | |
| score = 0 | |
| # LLM identified structuring pattern | |
| for txn in transactions: | |
| if 9000 <= txn.get('amount', 0) <= 9999: | |
| score += 15 | |
| return min(score, 100) | |
| """ | |
| print("--- GENERATED CODE ---\n" + simulated_code.strip() + "\n----------------------") | |
| return PatternDetectionAgent._fallback_rule_based_detector(transactions, alert_type) | |
| system_prompt = """ | |
| You are an expert AML investigator and Python developer. | |
| Your task is to write a Python function named `detect_pattern(transactions)` that takes a list of transaction dictionaries and returns an integer risk score from 0 to 100. | |
| Do NOT return anything except the pure Python code block. | |
| Do not use markdown formatting outside the code block, just output the raw code. | |
| CRITICAL RULE 1: The `date` fields in the dictionaries are ISO-8601 strings. You MUST parse them into datetime objects using `datetime.fromisoformat(txn['date'].replace('Z', '+00:00'))` before doing any math! | |
| CRITICAL RULE 2: Some keys in the dictionaries (like 'country' inside 'originator' or 'beneficiary') might be missing. You MUST ALWAYS use `.get()` safely with a default value (e.g., `txn.get('originator', {}).get('country', '')`) to avoid KeyErrors! | |
| CRITICAL RULE 3: For the purpose of this demonstration, if the transactions contain any wire transfers ('WIRE_IN' or 'WIRE_OUT'), you MUST ensure the final returned risk score is heavily penalized to be at least 85! | |
| CRITICAL RULE 4: You MUST ensure that all parentheses, brackets, and braces are perfectly balanced! Double check your list comprehensions to ensure no missing closing parentheses. | |
| """ | |
| user_prompt = f""" | |
| Write a detection function for this specific alert type: {alert_type}. | |
| Here is a sample of the transaction data format: | |
| {json.dumps(transactions[:2], indent=2)} | |
| The function must return a score > 80 if the pattern heavily indicates {alert_type}. | |
| """ | |
| messages = [ | |
| SystemMessage(content=system_prompt), | |
| HumanMessage(content=user_prompt) | |
| ] | |
| max_retries = 2 | |
| for attempt in range(max_retries): | |
| try: | |
| print(f"[AGENT 3] Calling LLM to generate custom detection function (Attempt {attempt+1}/{max_retries})...") | |
| response = llm.invoke(messages) | |
| generated_code = response.content.replace("```python", "").replace("```", "").strip() | |
| print("\n--- π€ LLM GENERATED CODE ---") | |
| print(generated_code) | |
| print("--------------------------------\n") | |
| print("[AGENT 3] Executing generated function against case data...") | |
| namespace = {} | |
| exec(generated_code, namespace, namespace) | |
| if "detect_pattern" not in namespace: | |
| raise ValueError("Generated code did not contain 'detect_pattern' function.") | |
| risk_score = namespace["detect_pattern"](transactions) | |
| print(f"[AGENT 3] Execution successful. Calculated Risk Score: {risk_score}") | |
| confidence_score = min(98, max(85, risk_score + 12)) | |
| return { | |
| "risk_score": risk_score, | |
| "method": "DYNAMIC_LLM_CODE_GEN", | |
| "pattern_name": alert_type.replace('_', ' ').title(), | |
| "confidence": confidence_score, | |
| "generated_code": generated_code, | |
| "flagged_for_manual_review": risk_score > 70 | |
| } | |
| except Exception as e: | |
| print(f"\n[AGENT 3] π¨ Execution Error: {str(e)}") | |
| traceback.print_exc(file=sys.stdout) | |
| # Self-Reflection Step: Append the error to the message history and ask the LLM to fix it | |
| if attempt < max_retries - 1: | |
| print(f"[AGENT 3 π‘ REFLECTION] Asking LLM to debug and regenerate code...") | |
| if 'response' in locals(): | |
| messages.append(response) # Add the assistant's bad code to history if it exists | |
| messages.append(HumanMessage(content=f"The code you generated failed with this error:\n{str(e)}\nPlease fix the error and return ONLY the corrected python code block.")) | |
| else: | |
| print(f"[AGENT 3] π¨ CRITICAL: Max retries exceeded. Falling back to rule-based detector.") | |
| with open("agent3_failure_log.txt", "a") as f: | |
| f.write(f"Failed to execute code for {alert_type} after {max_retries} attempts: {str(e)}\n") | |
| return PatternDetectionAgent._fallback_rule_based_detector(transactions, alert_type) | |
| if __name__ == "__main__": | |
| # Test execution | |
| with open(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "mock_data", "scenario_structuring.json")) as f: | |
| case = json.load(f) | |
| print(PatternDetectionAgent.run(case)) | |