Avi-17 commited on
Commit
cbd6ebe
Β·
1 Parent(s): 2fb15fb

readme modified

Browse files
Files changed (2) hide show
  1. README.md +304 -71
  2. server/client.py +7 -1
README.md CHANGED
@@ -15,89 +15,322 @@ tags:
15
 
16
  # Cashflow Multi-Agent RL Environment
17
 
18
- A high-fidelity business simulation designed for multi-agent interaction, focused on CFO decision-making under partial observability and hidden world dynamics.
19
 
20
- ## πŸ† Hackathon Alignment
21
- - **Theme #1: Multi-Agent Interactions**: Features Expenditure, Revenue, Risk, and Vendor agents.
22
- - **Theme #3.1: World Modeling / Professional Tasks**: Models complex cash-flow workflows with real tools (negotiation, credit lines).
23
- - **Outcome**: A realistic environment for training a "CFO LLM" to manage company finances.
24
 
25
- ## πŸ—οΈ System Architecture
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  ```mermaid
28
  graph TD
29
- subgraph "Hidden World (Ground Truth)"
30
- HW[World State: Shocks, Trust, True Inflows]
31
- end
32
-
33
- subgraph "OpenEnv Simulation"
34
- ENV[CashflowmanagerEnvironment]
35
- end
36
-
37
- subgraph "Advisory Agents (SFT/ICL)"
38
- EXP[Expenditure Agent]
39
- REV[Revenue Agent]
40
- RSK[Risk Agent]
41
- end
42
-
43
- subgraph "External Agents"
44
- VND[Vendor Agent]
45
- end
46
-
47
- subgraph "Decision Maker"
48
- CFO[CFO Agent - RL Trained]
49
- end
50
-
51
- HW --> ENV
52
- ENV -->|Partial Obs| EXP
53
- ENV -->|Partial Obs| REV
54
- ENV -->|Partial Obs| RSK
55
- EXP -->|Memo| CFO
56
- REV -->|Memo| CFO
57
- RSK -->|Memo| CFO
58
- CFO -->|Action: Pay/Negotiate| ENV
59
- ENV -->|Negotiation Call| VND
60
- VND -->|Response| ENV
61
- ENV -->|Reward| CFO
62
  ```
63
 
64
- ## πŸ“‚ Project Structure
65
 
66
- - `models.py`: Data models for Invoices, Receivables, and Actions.
67
- - `server/cashflowmanager_environment.py`: Core logic for `reset()`, `step()`, and `state()`.
68
- - `server/data_generator.py`: Synthetic JSON generator for realistic financial scenarios.
69
- - `server/agents.py`: System prompts and logic for advisory/vendor agents.
70
- - `scripts/train_sft.py`: Training script for sub-agents using **Unsloth**.
71
- - `scripts/train_rl.py`: RL training pipeline for the CFO using **HF TRL**.
72
 
73
- ## πŸš€ Getting Started
 
 
 
 
 
74
 
75
- ### 1. Build the Environment
76
- ```bash
77
- python3 -m server.app
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  ```
79
 
80
- ### 2. Generate Synthetic Data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  ```bash
82
- python3 server/data_generator.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  ```
84
 
85
- ### 3. Training Pipeline
86
- - **SFT (Sub-Agents)**: Use `scripts/train_sft.py` in Colab/Kaggle to train advisory agents on expert financial datasets.
87
- - **RL (CFO Agent)**: Use `scripts/train_rl.py` to train the CFO decision-maker using rewards from bill settlement and liquidity preservation.
88
-
89
- ## πŸ€– Agent Roles
90
-
91
- | Agent | Focus | Input | Output |
92
- | :--- | :--- | :--- | :--- |
93
- | **Expenditure** | Liabilities | Invoices, Cash | Advice on payment priority |
94
- | **Revenue** | Inflows | Receivables | Cash projection & risk warnings |
95
- | **Risk** | Shocks | Credit, Hidden Events | Buffer recommendations |
96
- | **Vendor** | External | Negotiation Requests | Accept/Reject/Counteroffer |
97
- | **CFO** | Strategic | Advisor Memos, Stats | Final Action (Pay, Defer, Neg, etc.) |
98
-
99
- ## βš–οΈ Reward Logic
100
- - `+ Cash Balance`: Small reward for liquidity.
101
- - `- Late Fees / Interest`: Heavy penalties for compounding debt.
102
- - `+ Vendor Trust`: Rewards for consistent payments and successful negotiations.
103
- - `+ Shock Absorption`: Rewards for surviving probabilistic cash shocks.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  # Cashflow Multi-Agent RL Environment
17
 
18
+ A high-fidelity business simulation in which an AI agent plays the **CFO** of a company, deciding which invoices to pay, defer, negotiate, or settle on credit each day β€” under partial observability and a probabilistic world model that can throw cash shocks, payment delays, and audits at it without warning.
19
 
20
+ Built on top of **OpenEnv**, with a Gradio dashboard for inspection and a multi-agent advisory system (Expenditure / Revenue / Risk β†’ CFO).
 
 
 
21
 
22
+ ---
23
+
24
+ ## Quick start
25
+
26
+ ```bash
27
+ # 1. Install
28
+ pip install -r requirements.txt
29
+
30
+ # 2. Configure API keys (see "Environment variables" below)
31
+ cp .env.example .env # then edit
32
+
33
+ # 3. Run the server (Gradio dashboard + OpenEnv HTTP endpoints)
34
+ python -m server.app
35
+ ```
36
+
37
+ The dashboard is at <http://0.0.0.0:7860/ui>. OpenEnv endpoints (`/reset`, `/step`, `/state`) are mounted on the same FastAPI app.
38
+
39
+ ---
40
+
41
+ ## UI navigation
42
+
43
+ The dashboard has a sidebar (settings + actions) and three tabs in the main panel.
44
+
45
+ ### Sidebar β€” Settings & Actions
46
+
47
+ - **Difficulty**: `easy` / `medium` / `hard`. Controls starting cash, invoice density, interest rates, and receivable reliability.
48
+ - **Seed**: `0` for a random scenario, or a fixed integer for reproducibility.
49
+ - **Preview Scenario** β€” peek at the initial state without running.
50
+ - **Run Full Simulation** β€” execute the entire `sim_window` end-to-end.
51
+ - **Start New / Next Day** β€” initialize a day-by-day run and step through it manually.
52
+ - **Theme toggle** (top-right `πŸŒ“`) β€” switches between dark and light mode. Buttons stay blue regardless.
53
+
54
+ ### Tab 1 β€” Full Simulation
55
+
56
+ Runs every day in one shot. Two columns:
57
+
58
+ - **Left**: Simulation Summary table (final cash, credit used, invoices paid, late fees, etc.).
59
+ - **Right**: Day-by-Day Metrics chart, an Agent Score banner (with letter grade), and a per-dimension score breakdown.
60
+
61
+ ### Tab 2 β€” Day-by-Day
62
+
63
+ Manually step through the simulation one day at a time.
64
+
65
+ - **Left**: Status table after the most recent day.
66
+ - **Right**: Running Metrics table that grows row-by-row. When the sim completes, the score banner + breakdown appear directly below it.
67
+
68
+ ### Tab 3 β€” Logs
69
+
70
+ A scrollable log of every day, rendered as **cards** with rounded backgrounds and a blue accent border. Each card contains:
71
+
72
+ - Opening cash / credit balance and invoice counts.
73
+ - World events triggered that day (cash shocks, payment delays, etc.).
74
+ - Each advisor's memo, formatted as a blockquote.
75
+ - The CFO's action plan, one line per invoice with an icon for the action type.
76
+ - Closing balances, reward, late fees, and interest.
77
+
78
+ Page-level scrolling is locked β€” only the logs container scrolls internally, so the sidebar and tab headers stay visible.
79
+
80
+ ---
81
+
82
+ ## Simulation logic
83
+
84
+ A simulation is a sequence of `sim_window` business days (default `3`). Each day follows the same fixed cycle:
85
+
86
+ ```
87
+ Day N:
88
+ 1. Activate any incoming invoices scheduled to appear today
89
+ 2. Age all unpaid invoices (decrement due_in; mark overdue if past 0)
90
+ 3. Collect receivables expected today (probability roll per receivable)
91
+ 4. Decide actions:
92
+ (a) Fast path β€” rule-based, no LLM
93
+ (b) Full path β€” three advisors in parallel + CFO sequentially
94
+ 5. Apply CFO actions (pay / partial / credit / defer)
95
+ 6. Charge daily interest on remaining balances
96
+ 7. Trigger world events for the day (shocks, delays, fraud, revenue miss)
97
+ 8. Compute reward via CashflowRubric β†’ write DayLog
98
+ ```
99
+
100
+ ### State the CFO sees
101
+
102
+ | Component | Description |
103
+ |---|---|
104
+ | **Cash** | Current balance. Can go negative up to `-credit_limit` before bankruptcy. |
105
+ | **Active Invoices** | Vendor bills with `amount`, `due_in`, `late_fee`, `interest`, `status`. |
106
+ | **Receivables** | Expected customer payments with `expected_in` day and arrival `probability`. |
107
+ | **Credit** | A fixed credit line. Drawing on it boosts cash now but hurts the final score. |
108
+ | **Vendor Profiles** | `trust_score` and `negotiation_flexibility`, used by the `negotiate` action. |
109
+ | **Advisor Memos** | Structured notes from Expenditure / Revenue / Risk agents. |
110
+ | **World Events** | Hidden, probabilistic shocks. The CFO doesn't see them in advance β€” only the Risk agent gets vague hints (`market_stress`, `upcoming_risk_level`). |
111
+
112
+ ### World model β€” hidden dynamics
113
+
114
+ A `WorldModel` instance is initialized per scenario and generates a private timeline of events:
115
+
116
+ - **Cash shocks** β€” equipment failure, tax audits, regulatory fines, supplier hikes (8–25% of starting cash each).
117
+ - **Payment delays** β€” receivables get pushed out by 1–3 days.
118
+ - **Revenue miss** β€” raises market stress for subsequent days.
119
+ - **Fraud anomalies** β€” rare, scaled-down cash deductions.
120
+
121
+ Events fire *after* CFO actions each day (the agent decides without seeing tomorrow's shock). The Risk Agent receives partial hints via `world_model.get_risk_hints(day)` so it can warn the CFO of elevated stress without revealing exact amounts.
122
+
123
+ ### Dynamic data generation
124
+
125
+ Every scenario is fresh β€” no fixed test set. The generator:
126
+
127
+ 1. Samples vendors, invoices, receivables, and incoming-invoice timelines based on difficulty.
128
+ 2. Runs a **solvability check** to guarantee the agent has a feasible path to break-even, then applies a difficulty-specific buffer.
129
+ 3. Hands the scenario to the env, which wraps it in a `CashflowmanagerObservation` and a `WorldModel`.
130
+
131
+ | Difficulty | Cash | Credit | Invoices | Receivables |
132
+ |---|---|---|---|---|
133
+ | `easy` | β‚Ή40k–50k | β‚Ή20k | 3–4 | 3–4 (85–95% prob) |
134
+ | `medium` | β‚Ή25k–35k | β‚Ή10k | 4–6 | 2–3 (70–85% prob) |
135
+ | `hard` | β‚Ή2k–5k | β‚Ή2k | 6–8 | 2–3 (60–80% prob) |
136
+
137
+ ---
138
+
139
+ ## Multi-agent architecture
140
 
141
  ```mermaid
142
  graph TD
143
+ WM[World Model<br/>hidden events]
144
+ ENV[CashflowmanagerEnvironment]
145
+ EXP[Expenditure Agent<br/>llama-3.1-8b key=0]
146
+ REV[Revenue Agent<br/>llama-3.1-8b key=1]
147
+ RSK[Risk Agent<br/>llama-3.1-8b key=2]
148
+ CFO[CFO Agent<br/>llama-3.1-8b key=2]
149
+ GRADIO[Gradio Dashboard /ui]
150
+ HTTP[OpenEnv /reset /step /state]
151
+
152
+ WM --> ENV
153
+ ENV -->|partial obs| EXP
154
+ ENV -->|partial obs| REV
155
+ ENV -->|partial obs + risk hints| RSK
156
+ EXP -->|memo| CFO
157
+ REV -->|memo| CFO
158
+ RSK -->|memo| CFO
159
+ CFO -->|actions| ENV
160
+ ENV --> GRADIO
161
+ ENV --> HTTP
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  ```
163
 
164
+ The three advisors run **in parallel** via `ThreadPoolExecutor(max_workers=3)`. The CFO runs sequentially after all three memos arrive, on the Risk agent's key (since Risk has the smallest prompt β†’ most leftover TPM headroom).
165
 
166
+ ### Agent roles
 
 
 
 
 
167
 
168
+ | Agent | Reads | Outputs |
169
+ |---|---|---|
170
+ | **Expenditure** | Active invoices, cash | Payment priority list, critical-invoice flags, recommended action |
171
+ | **Revenue** | Receivables, expected timing | Total expected inflow, reliable vs at-risk receivables, 3-day cash projection |
172
+ | **Risk** | Debt-to-cash ratio, credit util, market stress hints | Risk level (low/moderate/elevated/critical), recommended cash buffer, threats |
173
+ | **CFO** | Full state, history, advisor memos, invoice list | One action per invoice with a confidence score |
174
 
175
+ ---
176
+
177
+ ## Reward & scoring
178
+
179
+ Two distinct concepts:
180
+
181
+ ### Per-step reward β€” OpenEnv `Rubric` pattern
182
+
183
+ `CashflowRubric` plugs into `Environment.rubric` and composes independently-weighted sub-rubrics rather than collapsing reward into a single scalar:
184
+
185
+ - on-time payment bonus
186
+ - late-fee / interest penalty
187
+ - credit-draw penalty
188
+ - cash-buffer health
189
+
190
+ This avoids the monolithic-reward trap where one number accidentally rewards the wrong behavior (e.g. an agent that hoards cash to dodge fees but never pays anyone). With independent components, you can see which signal is dominating and tune weights surgically.
191
+
192
+ ### End-of-simulation score β€” five dimensions
193
+
194
+ | Dimension | Weight | Question |
195
+ |---|---|---|
196
+ | Solvency | 25% | Did the company survive without going deeply negative? |
197
+ | Debt Clearance | 30% | What fraction of invoices were fully paid? |
198
+ | Fiscal Discipline | 20% | Were late fees + interest avoided? |
199
+ | Credit Prudence | 10% | Was the credit line used sparingly? |
200
+ | Cash Management | 15% | Did the agent end with more cash than it started? |
201
+
202
+ Final score in `[0, 1]` plus a letter grade (A β‰₯ 0.90, B β‰₯ 0.75, C β‰₯ 0.55, D β‰₯ 0.35, F otherwise).
203
+
204
+ ---
205
+
206
+ ## OpenEnv integration
207
+
208
+ The FastAPI app is built via OpenEnv's `create_app(...)` factory:
209
+
210
+ ```python
211
+ from openenv.core.env_server.http_server import create_app
212
+ from server.cashflowmanager_environment import (
213
+ CashflowmanagerEnvironment,
214
+ CashflowmanagerAction,
215
+ CashflowmanagerObservation,
216
+ )
217
+
218
+ app = create_app(
219
+ CashflowmanagerEnvironment,
220
+ CashflowmanagerAction,
221
+ CashflowmanagerObservation,
222
+ env_name="cashflowmanager",
223
+ max_concurrent_envs=1,
224
+ )
225
  ```
226
 
227
+ This automatically wires `/reset`, `/step`, and `/state` HTTP endpoints. The Gradio dashboard mounts on top via `gr.mount_gradio_app(app, ..., path="/ui")`.
228
+
229
+ A remote client can drive the env via `server/client.py`, which extends `openenv.core.EnvClient`.
230
+
231
+ ---
232
+
233
+ ## Performance optimizations
234
+
235
+ The ICL flow makes 4 LLM calls per complex day. Without optimization, that easily blows past Groq's free-tier rate limits. The following changes brought a 3-day hard-difficulty sim down to ~10s with zero rate-limit failures.
236
+
237
+ ### 1. Rule-based fast path
238
+
239
+ `_try_fast_path()` short-circuits the LLM entirely on trivial days (no overdue invoices, ≀3 unpaid bills, enough cash to cover all). On easy / medium difficulty, this skips 30–50% of LLM calls.
240
+
241
+ ### 2. Slim prompts
242
+
243
+ Advisor prompts no longer include `serialize_history(past_logs)` (only the CFO does). Each advisor receives a minimal one-line state header plus its own data slice. Prompt size dropped ~55% per call, eliminating `json_validate_failed` truncation errors.
244
+
245
+ ### 3. Parallel advisors
246
+
247
+ The three advisors run concurrently via `ThreadPoolExecutor(max_workers=3)`. Per-day wall-clock dropped from ~12s (sequential) to ~4s.
248
+
249
+ ### 4. Multi-account API keys
250
+
251
+ Groq's free tier rate-limits at the **organization** level (~6,000 TPM for `llama-3.1-8b-instant`). Multiple keys from the same account share one bucket β€” they don't multiply capacity.
252
+
253
+ The fix: three keys from **three separate Groq accounts**, with per-agent key isolation (Expenditure β†’ key 0, Revenue β†’ key 1, Risk β†’ key 2, CFO β†’ reuses key 2). Triples the effective TPM ceiling.
254
+
255
+ ---
256
+
257
+ ## Environment variables
258
+
259
+ Set these in `.env` (or pass at the shell):
260
+
261
  ```bash
262
+ # API key (single-key mode β€” fallback)
263
+ GROQ_API_KEY=gsk_...
264
+
265
+ # Multi-key pool (recommended). Comma-separated, no quotes around individual keys.
266
+ # These MUST be from 3 separate Groq accounts to actually multiply TPM.
267
+ GROQ_API_KEYS=gsk_key1,gsk_key2,gsk_key3
268
+
269
+ # Models
270
+ MODEL_NAME=llama-3.1-8b-instant
271
+ CFO_MODEL_NAME=llama-3.1-8b-instant
272
+ ADVISOR_MODEL_NAME=llama-3.1-8b-instant
273
+ EXPENDITURE_MODEL_NAME=llama-3.1-8b-instant
274
+ REVENUE_MODEL_NAME=llama-3.1-8b-instant
275
+ RISK_MODEL_NAME=llama-3.1-8b-instant
276
+
277
+ # Per-agent key indices (override if you have a different mapping)
278
+ EXPENDITURE_KEY_INDEX=0
279
+ REVENUE_KEY_INDEX=1
280
+ RISK_KEY_INDEX=2
281
+ CFO_KEY_INDEX=2
282
+
283
+ # Confidence gate threshold (currently informational)
284
+ CFO_CONFIDENCE_THRESHOLD=0.85
285
+
286
+ # Retry / timeout knobs
287
+ LLM_MAX_RETRIES=5
288
+ LLM_TIMEOUT_SECONDS=20
289
+
290
+ # Set to "true" to load a local Hugging Face model instead of hitting Groq
291
+ USE_LOCAL_HF=false
292
+ LOCAL_MODEL_PATH=unsloth/Llama-3.2-1B-Instruct
293
  ```
294
 
295
+ ---
296
+
297
+ ## Project structure
298
+
299
+ ```
300
+ cashflow manager/
301
+ β”œβ”€β”€ server/
302
+ β”‚ β”œβ”€β”€ app.py # FastAPI + Gradio dashboard, mounts on /ui
303
+ β”‚ β”œβ”€β”€ cashflowmanager_environment.py # CashflowmanagerEnvironment class + sim loop
304
+ β”‚ β”œβ”€β”€ agents.py # Advisor + CFO prompts, per-agent key indices
305
+ β”‚ β”œβ”€β”€ client.py # Groq/OpenAI client wrapper, multi-key parsing
306
+ β”‚ β”œβ”€β”€ data_generator.py # Dynamic scenario generation
307
+ β”‚ β”œβ”€β”€ world_model.py # Hidden event timeline + risk hints
308
+ β”‚ β”œοΏ½οΏ½β”€ reward.py # CashflowRubric (OpenEnv Rubric pattern)
309
+ β”‚ β”œβ”€β”€ scoring.py # End-of-sim five-dimension score
310
+ β”‚ β”œβ”€β”€ state_serializer.py # Prompt-friendly state / history formatters
311
+ β”‚ └── tasks.py
312
+ β”œβ”€β”€ models.py # Pydantic models (Action, Observation, Invoice, ...)
313
+ β”œβ”€β”€ inference.py # Single-episode CLI runner that writes transitions.jsonl
314
+ β”œβ”€β”€ client.py # OpenEnv EnvClient subclass for remote use
315
+ β”œβ”€β”€ scripts/
316
+ β”‚ β”œβ”€β”€ train_sft.py # SFT training (Unsloth)
317
+ β”‚ β”œβ”€β”€ train_rl.py # RL training (HF TRL)
318
+ β”‚ β”œβ”€β”€ generate_sft_data.py # Expert demonstration data
319
+ β”‚ └── generate_rl_transitions.py # RL transition generator
320
+ β”œβ”€β”€ data/ # Generated SFT datasets (gitignored)
321
+ β”œβ”€β”€ images_blog/ # Diagrams used by the blog post
322
+ └── .env # API keys + model config (gitignored)
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Training pipeline (offline)
328
+
329
+ The runtime app uses ICL only. Training scripts live in `scripts/` and are run separately (typically in Colab / Kaggle for the GPU access):
330
+
331
+ - **SFT** β€” `scripts/train_sft.py` fine-tunes per-agent SFT models on `data/{agent}_sft.jsonl`. Uses Unsloth for 4-bit quantization.
332
+ - **RL** β€” `scripts/train_rl.py` runs PPO on top of the SFT-tuned CFO using `transitions.jsonl` from `inference.py`.
333
+
334
+ Set `USE_LOCAL_HF=true` and `LOCAL_MODEL_PATH=...` to point the runtime at a fine-tuned model instead of Groq.
335
+
336
+ ---
server/client.py CHANGED
@@ -2,7 +2,6 @@ import os
2
  import json
3
  import random
4
  import sys
5
- import torch
6
  from dotenv import load_dotenv
7
  import time as _time
8
 
@@ -19,6 +18,13 @@ try:
19
  except ImportError:
20
  OpenAI = None
21
 
 
 
 
 
 
 
 
22
  try:
23
  from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
24
  except ImportError:
 
2
  import json
3
  import random
4
  import sys
 
5
  from dotenv import load_dotenv
6
  import time as _time
7
 
 
18
  except ImportError:
19
  OpenAI = None
20
 
21
+ # torch + transformers are only needed when USE_LOCAL_HF=true. Keep them
22
+ # optional so the production Groq-API path doesn't pull in a multi-GB dep.
23
+ try:
24
+ import torch
25
+ except ImportError:
26
+ torch = None
27
+
28
  try:
29
  from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
30
  except ImportError: