PD03 commited on
Commit
c9a0b1a
·
verified ·
1 Parent(s): 55a0e9d

Create agent.py

Browse files
Files changed (1) hide show
  1. agent.py +189 -0
agent.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import json
3
+ from datetime import datetime
4
+ import random
5
+
6
+ class ProcurementRLAgent:
7
+ def __init__(self, tools):
8
+ self.tools = {tool.__class__.__name__: tool for tool in tools}
9
+ self.q_table = self._load_or_initialize_q_table()
10
+ self.learning_rate = 0.1
11
+ self.discount_factor = 0.95
12
+ self.epsilon = 0.1 # exploration rate
13
+
14
+ def _load_or_initialize_q_table(self):
15
+ """Initialize Q-table with realistic values based on historical data"""
16
+ try:
17
+ with open("demo_space/historical_procurement_data.json", "r") as f:
18
+ historical_data = json.load(f)
19
+
20
+ # Initialize Q-table based on historical patterns
21
+ # States: (urgency, contract_status, external_disruption, inventory_sufficient)
22
+ # Actions: (auto_execute, human_review, transfer_inventory, extend_contract)
23
+
24
+ q_table = {}
25
+ for urgency in ["Low", "Medium", "High"]:
26
+ for contract in ["Valid", "Expired"]:
27
+ for disruption in [True, False]:
28
+ for inventory in [True, False]:
29
+ state = (urgency, contract, disruption, inventory)
30
+ # Initialize with reasonable values based on business logic
31
+ if contract == "Expired":
32
+ q_table[state] = {"auto_execute": 0.2, "human_review": 0.8}
33
+ elif disruption:
34
+ q_table[state] = {"auto_execute": 0.3, "human_review": 0.7}
35
+ elif urgency == "High" and not inventory:
36
+ q_table[state] = {"auto_execute": 0.6, "human_review": 0.4}
37
+ else:
38
+ q_table[state] = {"auto_execute": 0.8, "human_review": 0.2}
39
+
40
+ return q_table
41
+ except FileNotFoundError:
42
+ return self._default_q_table()
43
+
44
+ def _default_q_table(self):
45
+ """Default Q-table initialization"""
46
+ return {
47
+ ("Low", "Valid", False, True): {"auto_execute": 0.9, "human_review": 0.1},
48
+ ("Medium", "Valid", False, True): {"auto_execute": 0.8, "human_review": 0.2},
49
+ ("High", "Valid", False, True): {"auto_execute": 0.7, "human_review": 0.3},
50
+ ("High", "Expired", True, False): {"auto_execute": 0.1, "human_review": 0.9},
51
+ }
52
+
53
+ def _get_state(self, pr_data):
54
+ """Convert PR data to state representation"""
55
+ inventory_sufficient = pr_data["Current_Inventory"] >= pr_data["Quantity"]
56
+ return (
57
+ pr_data["Urgency"],
58
+ pr_data["Contract_Status"],
59
+ pr_data["External_Disruption"],
60
+ inventory_sufficient
61
+ )
62
+
63
+ def _select_action(self, state):
64
+ """Select action using epsilon-greedy policy"""
65
+ if state in self.q_table:
66
+ if random.random() < self.epsilon:
67
+ # Exploration: random action
68
+ return random.choice(["auto_execute", "human_review"])
69
+ else:
70
+ # Exploitation: best action
71
+ return max(self.q_table[state].items(), key=lambda x: x[1])[0]
72
+ else:
73
+ # Default action for unknown states
74
+ return "human_review"
75
+
76
+ def decide(self, pr_data):
77
+ """Main decision function"""
78
+ timestamp = datetime.now().strftime("%H:%M:%S")
79
+ logs = []
80
+
81
+ # Get current state
82
+ state = self._get_state(pr_data)
83
+ logs.append({
84
+ "timestamp": timestamp,
85
+ "tool": "StateAnalyzer",
86
+ "result": f"State: {state}"
87
+ })
88
+
89
+ # Execute tools to gather information
90
+ tool_results = {}
91
+
92
+ # Check inventory
93
+ inventory_result = self.tools["InventoryTool"].run(pr_data)
94
+ tool_results["inventory"] = inventory_result
95
+ logs.append({
96
+ "timestamp": timestamp,
97
+ "tool": "InventoryTool",
98
+ "result": inventory_result["message"]
99
+ })
100
+
101
+ # Check external risks
102
+ risk_result = self.tools["ExternalRiskTool"].run(pr_data)
103
+ tool_results["risk"] = risk_result
104
+ logs.append({
105
+ "timestamp": timestamp,
106
+ "tool": "ExternalRiskTool",
107
+ "result": risk_result["message"]
108
+ })
109
+
110
+ # Check contract status
111
+ contract_result = self.tools["ContractTool"].run(pr_data)
112
+ tool_results["contract"] = contract_result
113
+ logs.append({
114
+ "timestamp": timestamp,
115
+ "tool": "ContractTool",
116
+ "result": contract_result["message"]
117
+ })
118
+
119
+ # Select action using RL policy
120
+ selected_action = self._select_action(state)
121
+
122
+ # Execute decision logic
123
+ if selected_action == "auto_execute" and self._can_auto_execute(tool_results):
124
+ # Execute PO automatically
125
+ po_result = self.tools["POExecutionTool"].run(pr_data)
126
+ logs.append({
127
+ "timestamp": timestamp,
128
+ "tool": "POExecutionTool",
129
+ "result": po_result["message"]
130
+ })
131
+
132
+ confidence = self.q_table.get(state, {}).get("auto_execute", 0.5)
133
+
134
+ return {
135
+ "action_type": "AUTO_EXECUTE",
136
+ "action": "Purchase Order created and sent to supplier",
137
+ "reason": "All conditions met for automatic execution",
138
+ "confidence": confidence,
139
+ "logs": logs
140
+ }
141
+ else:
142
+ # Require human review
143
+ human_result = self.tools["HumanNotificationTool"].run(pr_data)
144
+ logs.append({
145
+ "timestamp": timestamp,
146
+ "tool": "HumanNotificationTool",
147
+ "result": human_result["message"]
148
+ })
149
+
150
+ reason = self._get_human_review_reason(tool_results)
151
+
152
+ return {
153
+ "action_type": "HUMAN_REVIEW",
154
+ "action": "Escalated to procurement specialist",
155
+ "reason": reason,
156
+ "recommendation": self._get_recommendation(tool_results),
157
+ "logs": logs
158
+ }
159
+
160
+ def _can_auto_execute(self, tool_results):
161
+ """Determine if automatic execution is possible"""
162
+ return (
163
+ not tool_results["contract"]["contract_expired"] and
164
+ not tool_results["risk"]["risk"] and
165
+ tool_results["inventory"]["inventory_available"]
166
+ )
167
+
168
+ def _get_human_review_reason(self, tool_results):
169
+ """Get reason for human review requirement"""
170
+ reasons = []
171
+ if tool_results["contract"]["contract_expired"]:
172
+ reasons.append("Contract expired - requires renewal")
173
+ if tool_results["risk"]["risk"]:
174
+ reasons.append("External supply chain disruption detected")
175
+ if not tool_results["inventory"]["inventory_available"]:
176
+ reasons.append("Insufficient inventory - supplier sourcing required")
177
+
178
+ return "; ".join(reasons) if reasons else "Complex procurement scenario requires expert review"
179
+
180
+ def _get_recommendation(self, tool_results):
181
+ """Get recommendation for human reviewer"""
182
+ if tool_results["contract"]["contract_expired"]:
183
+ return "Extend contract with current supplier or initiate competitive bidding"
184
+ elif tool_results["risk"]["risk"]:
185
+ return "Evaluate alternative suppliers to mitigate supply chain risk"
186
+ elif not tool_results["inventory"]["inventory_available"]:
187
+ return "Check inventory at other locations or place emergency order"
188
+ else:
189
+ return "Review procurement parameters and approve if acceptable"