TheAarvee05 commited on
Commit
599b561
Β·
verified Β·
1 Parent(s): b511f2a

Upload README.md with huggingface_hub

Browse files
Files changed (1) hide show
  1. README.md +369 -371
README.md CHANGED
@@ -1,371 +1,369 @@
1
- ---
2
- title: Meta Ads Attribution Environment
3
- emoji: πŸ“ˆ
4
- colorFrom: blue
5
- colorTo: indigo
6
- sdk: docker
7
- app_port: 8000
8
- base_path: /web
9
- pinned: true
10
- ---
11
-
12
- # Meta Ads Attribution Recovery Environment
13
-
14
- [![OpenEnv](https://img.shields.io/badge/OpenEnv-Compliant-brightgreen)](https://openenv.dev)
15
- [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
16
-
17
- **An OpenEnv-compliant reinforcement learning environment that models Meta Ads attribution recovery under iOS tracking constraints, narrow attribution windows, and incomplete conversion signals.**
18
-
19
- > **The Problem**: Meta advertisers lose significant revenue because iOS privacy changes, narrow attribution windows, and browser tracking restrictions leave **40-70% of conversions** untracked. As a result, Meta's optimization system learns from incomplete signals, often overvaluing short-lag outcomes while undervaluing high-performing ads with delayed conversions.
20
-
21
- ---
22
-
23
- ## The Attribution Crisis Explained
24
-
25
- ### What's Breaking Attribution?
26
-
27
- 1. **Narrow Attribution Windows**: Defaults shifted from 28-day to 7-day (or 1-day), so later conversions are excluded.
28
- 2. **iOS 14.5+ Privacy**: Apple ATT suppresses a large share of iOS conversion tracking via Meta Pixel.
29
- 3. **Browser Restrictions**: Safari ITP, Firefox protections, and ad blockers further reduce signal quality.
30
- 4. **Missing Server-Side Tracking**: Many advertisers still lack Conversions API (CAPI), limiting server-side recovery.
31
-
32
- ### The Impact
33
-
34
- **Example Campaign:**
35
- - **Reported Metrics**: 59 conversions, $76 CPA, 0.98x ROAS -> *Appears unprofitable*
36
- - **True Performance**: 180 conversions, $25 CPA, 3.0x ROAS -> *Actually highly profitable!*
37
- - **Attribution Gap**: **67% of conversions untracked**
38
-
39
- **Result**: The optimization loop can pause profitable inventory and over-allocate spend to weaker ad sets.
40
-
41
- ### This Environment Teaches Agents To:
42
- 1. Diagnose attribution failures from campaign-level and ad set-level signals.
43
- 2. Apply technical remediations (window expansion, CAPI, AEM).
44
- 3. Reallocate budget using true performance rather than biased observed metrics.
45
- 4. Recover signal quality so optimization decisions become reliable again.
46
-
47
- ---
48
-
49
- ## Environment Overview
50
-
51
- ### OpenEnv Compliance
52
- - **Typed Pydantic models** for Observation, Action, Reward, State
53
- - **Standard API**: `reset()`, `step(action)`, `state()`
54
- - **Three difficulty levels** with programmatic graders (0.0–1.0 scoring)
55
- - **Realistic simulator** modeling attribution degradation
56
- - **Multi-component rewards** that capture incremental progress
57
-
58
- ### Key Metrics
59
- - **Episode Length**: 5-10 steps
60
- - **Success Threshold**: Score β‰₯0.60
61
- - **Action Space**: 10 discrete actions (window adjustment, CAPI, budget optimization)
62
- - **Observation Space**: Campaign metrics + diagnostic signals + natural language context
63
-
64
- ---
65
-
66
- ## Action Space
67
-
68
- | Action | Parameters | Use Case |
69
- |--------|-----------|----------|
70
- | `adjust_attribution_window` | `{"window": "7d_click"}` | When window is too narrow (1d_click) |
71
- | `enable_conversions_api` | `{}` | When iOS >40% and Pixel signal <60% |
72
- | `enable_aggregated_event_measurement` | `{}` | After CAPI, for additional iOS recovery |
73
- | `add_utm_tracking` | `{}` | Improve cross-domain attribution |
74
- | `pause_underperforming_adsets` | `{"roas_threshold": 1.0}` | When true ROAS <1.0 |
75
- | `reallocate_to_top_performers` | `{"amount": 2000}` | Shift budget to high-ROAS adsets |
76
- | `adjust_budget_allocation` | `{"shifts": {...}}` | Fine-grained budget control |
77
- | `change_bid_strategy` | `{"strategy": "value_optimisation"}` | Optimize for ROAS vs CPA |
78
- | `segment_audience` | `{}` | Create better-targeted segments |
79
- | `no_op` | `{}` | No action needed |
80
-
81
- ---
82
-
83
- ## Observation Space
84
-
85
- ```python
86
- {
87
- # Campaign Performance
88
- "reported_conversions": 59, # What Meta sees
89
- "true_conversions": 180, # Ground truth (hidden from algorithm)
90
- "attribution_gap_pct": 0.672, # 67% untracked!
91
- "reported_roas": 0.98, # Appears unprofitable
92
- "true_roas": 3.0, # Actually profitable
93
-
94
- # Attribution Setup
95
- "attribution_window": "1d_click", # Too narrow
96
- "pixel_signal_quality": 0.86, # 14% signal loss
97
- "ios_traffic_pct": 0.25, # 25% iOS users
98
- "conversions_api_enabled": false, # Missing CAPI
99
- "aem_enabled": false, # Missing AEM
100
-
101
- # Natural Language Context (for LLM agents)
102
- "context": "Campaign 'Spring Sale' | Objective: CONVERSIONS\n..."
103
- }
104
- ```
105
-
106
- ---
107
-
108
- ## Tasks & Difficulty
109
-
110
- ### Easy: Attribution Window Fix
111
- **Problem**: A 1-day attribution window excludes most delayed conversions.
112
- **Solution**: Adjust to 7-day click window
113
- **Baseline Score**: 0.893
114
-
115
- ### Medium: iOS Signal Recovery
116
- **Problem**: High iOS share without CAPI/AEM causes substantial signal loss.
117
- **Solution**: Enable CAPI β†’ Enable AEM
118
- **Baseline Score**: 0.850
119
-
120
- ### Hard: Full Attribution Audit
121
- **Problem**: Narrow window, high iOS exposure, missing tracking stack, and misallocated budget.
122
- **Solution**: Multi-step optimization (5+ actions)
123
- **Baseline Score**: 0.794
124
-
125
- ---
126
-
127
- ## Quick Start
128
-
129
- ### Installation
130
-
131
- ```bash
132
- # Clone repository
133
- git clone https://github.com/yourusername/meta-ads-openenv.git
134
- cd meta-ads-openenv
135
-
136
- # Install dependencies
137
- pip install -r requirements.txt
138
-
139
- # Set up API key (copy .env.example to .env and add your key)
140
- cp .env.example .env
141
- # Edit .env with required values:
142
- # API_BASE_URL=https://router.huggingface.co/v1
143
- # MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
144
- # HF_TOKEN=hf_your_token_here
145
- ```
146
-
147
- ### Run Baseline Agent
148
-
149
- ```bash
150
- # Required (for LLM-backed paths):
151
- export API_BASE_URL=https://router.huggingface.co/v1
152
- export MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
153
- export HF_TOKEN=hf_your_token_here
154
-
155
- # Run baseline across all 3 tasks
156
- python baseline/run_baseline.py
157
- ```
158
-
159
- **Expected Output:**
160
- ```
161
- TASK: EASY_ATTRIBUTION_WINDOW
162
- Score: 0.8926 (PASS) | Steps: 5/5
163
-
164
- TASK: MEDIUM_PIXEL_RECOVERY
165
- Score: 0.8500 (PASS) | Steps: 4/7
166
-
167
- TASK: HARD_FULL_ATTRIBUTION_AUDIT
168
- Score: 0.7942 (PASS) | Steps: 7/10
169
-
170
- Average Score: 0.8456
171
- ```
172
-
173
- ### Launch Web UI
174
-
175
- ```bash
176
- python -m server.app
177
- # Open browser to http://127.0.0.1:8000/web
178
- ```
179
-
180
- ### Use Programmatically
181
-
182
- ```python
183
- from meta_ads_env import MetaAdsAttributionEnv
184
- from meta_ads_env.models import Action
185
-
186
- # Initialize
187
- env = MetaAdsAttributionEnv(task_id="easy_attribution_window")
188
- obs = env.reset()
189
-
190
- # Check initial state
191
- print(f"Attribution gap: {obs.attribution_gap_pct:.1%}")
192
- print(f"Reported ROAS: {obs.roas_reported:.2f}x")
193
- print(f"True ROAS: {obs.roas_true:.2f}x")
194
-
195
- # Take action
196
- action = Action(
197
- action_type="adjust_attribution_window",
198
- parameters={"window": "7d_click"}
199
- )
200
- obs, reward, done, info = env.step(action)
201
-
202
- print(f"Reward: {reward.total:.4f}")
203
- print(f"New gap: {obs.attribution_gap_pct:.1%}")
204
-
205
- # Grade episode
206
- if done:
207
- result = env.grade_episode()
208
- print(f"Score: {result.score:.4f} - {'PASS' if result.passed else 'FAIL'}")
209
- ```
210
-
211
- ---
212
-
213
- ## Baseline Results
214
-
215
- **Model**: Qwen/Qwen2.5-72B-Instruct (OpenAI-compatible interface) | **Temperature**: 0.0
216
-
217
- | Task | Score | Pass | Steps | Key Actions |
218
- |------|-------|------|-------|-------------|
219
- | Easy | 0.893 | Yes | 5/5 | Investigate + window fix + convergence handling |
220
- | Medium | 0.850 | Yes | 4/7 | Investigate + CAPI + AEM + modeled reporting |
221
- | Hard | 0.794 | Yes | 7/10 | Investigate + window + CAPI + AEM + pause + reallocate |
222
- | **Average** | **0.846** | **100%** | - | **All passing** |
223
-
224
- ---
225
-
226
- ## Reward Function
227
-
228
- Multi-component reward designed to reward meaningful progress:
229
-
230
- ```python
231
- reward = (
232
- 0.35 Γ— attribution_accuracy # Gap closure
233
- + 0.25 Γ— roas_improvement # True ROAS increase
234
- + 0.25 Γ— signal_quality_gain # Pixel recovery
235
- + 0.10 Γ— action_validity # Right action for context
236
- + 0.05 Γ— step_efficiency # Fewer steps bonus
237
- - trajectory_penalty # Harmful action penalty
238
- )
239
- ```
240
-
241
- **Range**: -1.0 to 1.0 per step
242
-
243
- ---
244
-
245
- ## Docker Deployment
246
-
247
- ### Build and Run Locally
248
- ```bash
249
- docker build -t meta-ads-env .
250
- docker run -p 7860:7860 -e API_BASE_URL=https://router.huggingface.co/v1 -e MODEL_NAME=Qwen/Qwen2.5-72B-Instruct -e HF_TOKEN=hf_your_token_here meta-ads-env
251
- ```
252
-
253
- ### Deploy to Hugging Face Spaces
254
- 1. Create new Space (Docker SDK)
255
- 2. Add `API_BASE_URL`, `MODEL_NAME`, and `HF_TOKEN` as Space secrets
256
- 3. Push code to the Space repository
257
- 4. Space auto-builds and deploys
258
-
259
- ---
260
-
261
- ## Inference Workflow
262
-
263
- ### Inference Script
264
-
265
- Use `inference.py` at the repository root to run standardized task inference with structured logs.
266
-
267
- **Set environment variables:**
268
- ```bash
269
- export API_BASE_URL=https://router.huggingface.co/v1
270
- export MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
271
- export HF_TOKEN=hf_your_token_here
272
- ```
273
-
274
- **Run inference:**
275
- ```bash
276
- python inference.py
277
- ```
278
-
279
- **Output format:**
280
- ```
281
- [START] task=easy_attribution_window env=meta_ads_attribution_openenv model=Qwen/Qwen2.5-72B-Instruct
282
- [STEP] step=1 action=investigate_attribution reward=0.09 done=false error=null
283
- [END] success=true steps=3 score=0.900 rewards=0.09,0.76,0.67
284
- ...
285
- ```
286
-
287
- This structured output is designed for easy monitoring, reproducible evaluation, and downstream parsing.
288
-
289
- ### Validate Submission
290
- ```bash
291
- bash validate-submission.sh <your_space_url> .
292
- ```
293
-
294
- ---
295
-
296
- ## Project Structure
297
-
298
- ```
299
- meta-ads-openenv/
300
- β”œβ”€β”€ openenv.yaml # OpenEnv metadata
301
- β”œβ”€β”€ inference.py # Required hackathon inference script
302
- β”œβ”€β”€ requirements.txt # Dependencies
303
- β”œβ”€β”€ Dockerfile # Container definition
304
- β”‚
305
- β”œβ”€β”€ meta_ads_env/ # Core environment
306
- β”‚ β”œβ”€β”€ env.py # Main environment class
307
- β”‚ β”œβ”€β”€ models.py # Pydantic models
308
- β”‚ β”œβ”€β”€ simulator.py # Attribution simulator
309
- β”‚ β”œβ”€β”€ reward.py # Reward function
310
- β”‚ β”œβ”€β”€ grader.py # Task graders
311
- οΏ½οΏ½οΏ½ └── tasks.py # Task definitions
312
- β”‚
313
- β”œβ”€β”€ baseline/ # Baseline agent
314
- β”‚ β”œβ”€β”€ baseline_agent.py # LLM-powered agent
315
- β”‚ └── run_baseline.py # Evaluation script
316
- β”‚
317
- β”œβ”€β”€ evaluation/ # Evaluation tools
318
- β”‚ β”œβ”€β”€ llm_grader.py # Optional LLM-as-judge
319
- β”‚ └── metrics.py # Aggregate metrics
320
- β”‚
321
- └── validate-submission.sh # Submission validator
322
- ```
323
-
324
- ---
325
-
326
- ## Advanced Usage
327
-
328
- ### Validate OpenEnv Compliance
329
- ```bash
330
- pip install openenv
331
- openenv validate .
332
- ```
333
-
334
- ### Custom Training Loop
335
- ```python
336
- for episode in range(100):
337
- obs = env.reset()
338
- done = False
339
-
340
- while not done:
341
- action = your_policy.select_action(obs)
342
- obs, reward, done, info = env.step(action)
343
- your_policy.update(obs, action, reward)
344
- ```
345
-
346
- ### LLM Scoring
347
- ```python
348
- from evaluation.llm_grader import LLMGrader
349
-
350
- grader = LLMGrader(model="Qwen/Qwen2.5-72B-Instruct")
351
- result = grader.grade_trajectory(
352
- task_id="hard_full_attribution_audit",
353
- history=env.state().history,
354
- initial_context=initial_obs.context,
355
- final_context=final_obs.context
356
- )
357
- ```
358
-
359
- ---
360
-
361
- ## Acknowledgments
362
-
363
- Built to demonstrate how AI agents can solve high-impact marketing optimization problems in realistic attribution environments. Inspired by real Meta Ads attribution challenges faced by performance teams at scale.
364
-
365
- **OpenEnv**: RL environment specification
366
- **Meta Ads Manager**: Real-world attribution dynamics and constraints
367
- **Digital Marketing Community**: Practical insights from attribution and measurement operations
368
-
369
- ---
370
-
371
- **Making attribution-aware AI optimization practical and measurable**
 
1
+ ---
2
+ title: Meta Ads Attribution Environment
3
+ emoji: πŸ“ˆ
4
+ colorFrom: blue
5
+ colorTo: indigo
6
+ sdk: docker
7
+ pinned: true
8
+ ---
9
+
10
+ # Meta Ads Attribution Recovery Environment
11
+
12
+ [![OpenEnv](https://img.shields.io/badge/OpenEnv-Compliant-brightgreen)](https://openenv.dev)
13
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
14
+
15
+ **An OpenEnv-compliant reinforcement learning environment that models Meta Ads attribution recovery under iOS tracking constraints, narrow attribution windows, and incomplete conversion signals.**
16
+
17
+ > **The Problem**: Meta advertisers lose significant revenue because iOS privacy changes, narrow attribution windows, and browser tracking restrictions leave **40-70% of conversions** untracked. As a result, Meta's optimization system learns from incomplete signals, often overvaluing short-lag outcomes while undervaluing high-performing ads with delayed conversions.
18
+
19
+ ---
20
+
21
+ ## The Attribution Crisis Explained
22
+
23
+ ### What's Breaking Attribution?
24
+
25
+ 1. **Narrow Attribution Windows**: Defaults shifted from 28-day to 7-day (or 1-day), so later conversions are excluded.
26
+ 2. **iOS 14.5+ Privacy**: Apple ATT suppresses a large share of iOS conversion tracking via Meta Pixel.
27
+ 3. **Browser Restrictions**: Safari ITP, Firefox protections, and ad blockers further reduce signal quality.
28
+ 4. **Missing Server-Side Tracking**: Many advertisers still lack Conversions API (CAPI), limiting server-side recovery.
29
+
30
+ ### The Impact
31
+
32
+ **Example Campaign:**
33
+ - **Reported Metrics**: 59 conversions, $76 CPA, 0.98x ROAS -> *Appears unprofitable*
34
+ - **True Performance**: 180 conversions, $25 CPA, 3.0x ROAS -> *Actually highly profitable!*
35
+ - **Attribution Gap**: **67% of conversions untracked**
36
+
37
+ **Result**: The optimization loop can pause profitable inventory and over-allocate spend to weaker ad sets.
38
+
39
+ ### This Environment Teaches Agents To:
40
+ 1. Diagnose attribution failures from campaign-level and ad set-level signals.
41
+ 2. Apply technical remediations (window expansion, CAPI, AEM).
42
+ 3. Reallocate budget using true performance rather than biased observed metrics.
43
+ 4. Recover signal quality so optimization decisions become reliable again.
44
+
45
+ ---
46
+
47
+ ## Environment Overview
48
+
49
+ ### OpenEnv Compliance
50
+ - **Typed Pydantic models** for Observation, Action, Reward, State
51
+ - **Standard API**: `reset()`, `step(action)`, `state()`
52
+ - **Three difficulty levels** with programmatic graders (0.0–1.0 scoring)
53
+ - **Realistic simulator** modeling attribution degradation
54
+ - **Multi-component rewards** that capture incremental progress
55
+
56
+ ### Key Metrics
57
+ - **Episode Length**: 5-10 steps
58
+ - **Success Threshold**: Score β‰₯0.60
59
+ - **Action Space**: 10 discrete actions (window adjustment, CAPI, budget optimization)
60
+ - **Observation Space**: Campaign metrics + diagnostic signals + natural language context
61
+
62
+ ---
63
+
64
+ ## Action Space
65
+
66
+ | Action | Parameters | Use Case |
67
+ |--------|-----------|----------|
68
+ | `adjust_attribution_window` | `{"window": "7d_click"}` | When window is too narrow (1d_click) |
69
+ | `enable_conversions_api` | `{}` | When iOS >40% and Pixel signal <60% |
70
+ | `enable_aggregated_event_measurement` | `{}` | After CAPI, for additional iOS recovery |
71
+ | `add_utm_tracking` | `{}` | Improve cross-domain attribution |
72
+ | `pause_underperforming_adsets` | `{"roas_threshold": 1.0}` | When true ROAS <1.0 |
73
+ | `reallocate_to_top_performers` | `{"amount": 2000}` | Shift budget to high-ROAS adsets |
74
+ | `adjust_budget_allocation` | `{"shifts": {...}}` | Fine-grained budget control |
75
+ | `change_bid_strategy` | `{"strategy": "value_optimisation"}` | Optimize for ROAS vs CPA |
76
+ | `segment_audience` | `{}` | Create better-targeted segments |
77
+ | `no_op` | `{}` | No action needed |
78
+
79
+ ---
80
+
81
+ ## Observation Space
82
+
83
+ ```python
84
+ {
85
+ # Campaign Performance
86
+ "reported_conversions": 59, # What Meta sees
87
+ "true_conversions": 180, # Ground truth (hidden from algorithm)
88
+ "attribution_gap_pct": 0.672, # 67% untracked!
89
+ "reported_roas": 0.98, # Appears unprofitable
90
+ "true_roas": 3.0, # Actually profitable
91
+
92
+ # Attribution Setup
93
+ "attribution_window": "1d_click", # Too narrow
94
+ "pixel_signal_quality": 0.86, # 14% signal loss
95
+ "ios_traffic_pct": 0.25, # 25% iOS users
96
+ "conversions_api_enabled": false, # Missing CAPI
97
+ "aem_enabled": false, # Missing AEM
98
+
99
+ # Natural Language Context (for LLM agents)
100
+ "context": "Campaign 'Spring Sale' | Objective: CONVERSIONS\n..."
101
+ }
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Tasks & Difficulty
107
+
108
+ ### Easy: Attribution Window Fix
109
+ **Problem**: A 1-day attribution window excludes most delayed conversions.
110
+ **Solution**: Adjust to 7-day click window
111
+ **Baseline Score**: 0.893
112
+
113
+ ### Medium: iOS Signal Recovery
114
+ **Problem**: High iOS share without CAPI/AEM causes substantial signal loss.
115
+ **Solution**: Enable CAPI β†’ Enable AEM
116
+ **Baseline Score**: 0.850
117
+
118
+ ### Hard: Full Attribution Audit
119
+ **Problem**: Narrow window, high iOS exposure, missing tracking stack, and misallocated budget.
120
+ **Solution**: Multi-step optimization (5+ actions)
121
+ **Baseline Score**: 0.794
122
+
123
+ ---
124
+
125
+ ## Quick Start
126
+
127
+ ### Installation
128
+
129
+ ```bash
130
+ # Clone repository
131
+ git clone https://github.com/yourusername/meta-ads-openenv.git
132
+ cd meta-ads-openenv
133
+
134
+ # Install dependencies
135
+ pip install -r requirements.txt
136
+
137
+ # Set up API key (copy .env.example to .env and add your key)
138
+ cp .env.example .env
139
+ # Edit .env with required values:
140
+ # API_BASE_URL=https://router.huggingface.co/v1
141
+ # MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
142
+ # HF_TOKEN=hf_your_token_here
143
+ ```
144
+
145
+ ### Run Baseline Agent
146
+
147
+ ```bash
148
+ # Required (for LLM-backed paths):
149
+ export API_BASE_URL=https://router.huggingface.co/v1
150
+ export MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
151
+ export HF_TOKEN=hf_your_token_here
152
+
153
+ # Run baseline across all 3 tasks
154
+ python baseline/run_baseline.py
155
+ ```
156
+
157
+ **Expected Output:**
158
+ ```
159
+ TASK: EASY_ATTRIBUTION_WINDOW
160
+ Score: 0.8926 (PASS) | Steps: 5/5
161
+
162
+ TASK: MEDIUM_PIXEL_RECOVERY
163
+ Score: 0.8500 (PASS) | Steps: 4/7
164
+
165
+ TASK: HARD_FULL_ATTRIBUTION_AUDIT
166
+ Score: 0.7942 (PASS) | Steps: 7/10
167
+
168
+ Average Score: 0.8456
169
+ ```
170
+
171
+ ### Launch Web UI
172
+
173
+ ```bash
174
+ python -m server.app
175
+ # Open browser to http://127.0.0.1:8000/web
176
+ ```
177
+
178
+ ### Use Programmatically
179
+
180
+ ```python
181
+ from meta_ads_env import MetaAdsAttributionEnv
182
+ from meta_ads_env.models import Action
183
+
184
+ # Initialize
185
+ env = MetaAdsAttributionEnv(task_id="easy_attribution_window")
186
+ obs = env.reset()
187
+
188
+ # Check initial state
189
+ print(f"Attribution gap: {obs.attribution_gap_pct:.1%}")
190
+ print(f"Reported ROAS: {obs.roas_reported:.2f}x")
191
+ print(f"True ROAS: {obs.roas_true:.2f}x")
192
+
193
+ # Take action
194
+ action = Action(
195
+ action_type="adjust_attribution_window",
196
+ parameters={"window": "7d_click"}
197
+ )
198
+ obs, reward, done, info = env.step(action)
199
+
200
+ print(f"Reward: {reward.total:.4f}")
201
+ print(f"New gap: {obs.attribution_gap_pct:.1%}")
202
+
203
+ # Grade episode
204
+ if done:
205
+ result = env.grade_episode()
206
+ print(f"Score: {result.score:.4f} - {'PASS' if result.passed else 'FAIL'}")
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Baseline Results
212
+
213
+ **Model**: Qwen/Qwen2.5-72B-Instruct (OpenAI-compatible interface) | **Temperature**: 0.0
214
+
215
+ | Task | Score | Pass | Steps | Key Actions |
216
+ |------|-------|------|-------|-------------|
217
+ | Easy | 0.893 | Yes | 5/5 | Investigate + window fix + convergence handling |
218
+ | Medium | 0.850 | Yes | 4/7 | Investigate + CAPI + AEM + modeled reporting |
219
+ | Hard | 0.794 | Yes | 7/10 | Investigate + window + CAPI + AEM + pause + reallocate |
220
+ | **Average** | **0.846** | **100%** | - | **All passing** |
221
+
222
+ ---
223
+
224
+ ## Reward Function
225
+
226
+ Multi-component reward designed to reward meaningful progress:
227
+
228
+ ```python
229
+ reward = (
230
+ 0.35 Γ— attribution_accuracy # Gap closure
231
+ + 0.25 Γ— roas_improvement # True ROAS increase
232
+ + 0.25 Γ— signal_quality_gain # Pixel recovery
233
+ + 0.10 Γ— action_validity # Right action for context
234
+ + 0.05 Γ— step_efficiency # Fewer steps bonus
235
+ - trajectory_penalty # Harmful action penalty
236
+ )
237
+ ```
238
+
239
+ **Range**: -1.0 to 1.0 per step
240
+
241
+ ---
242
+
243
+ ## Docker Deployment
244
+
245
+ ### Build and Run Locally
246
+ ```bash
247
+ docker build -t meta-ads-env .
248
+ docker run -p 7860:7860 -e API_BASE_URL=https://router.huggingface.co/v1 -e MODEL_NAME=Qwen/Qwen2.5-72B-Instruct -e HF_TOKEN=hf_your_token_here meta-ads-env
249
+ ```
250
+
251
+ ### Deploy to Hugging Face Spaces
252
+ 1. Create new Space (Docker SDK)
253
+ 2. Add `API_BASE_URL`, `MODEL_NAME`, and `HF_TOKEN` as Space secrets
254
+ 3. Push code to the Space repository
255
+ 4. Space auto-builds and deploys
256
+
257
+ ---
258
+
259
+ ## Inference Workflow
260
+
261
+ ### Inference Script
262
+
263
+ Use `inference.py` at the repository root to run standardized task inference with structured logs.
264
+
265
+ **Set environment variables:**
266
+ ```bash
267
+ export API_BASE_URL=https://router.huggingface.co/v1
268
+ export MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
269
+ export HF_TOKEN=hf_your_token_here
270
+ ```
271
+
272
+ **Run inference:**
273
+ ```bash
274
+ python inference.py
275
+ ```
276
+
277
+ **Output format:**
278
+ ```
279
+ [START] task=easy_attribution_window env=meta_ads_attribution_openenv model=Qwen/Qwen2.5-72B-Instruct
280
+ [STEP] step=1 action=investigate_attribution reward=0.09 done=false error=null
281
+ [END] success=true steps=3 score=0.900 rewards=0.09,0.76,0.67
282
+ ...
283
+ ```
284
+
285
+ This structured output is designed for easy monitoring, reproducible evaluation, and downstream parsing.
286
+
287
+ ### Validate Submission
288
+ ```bash
289
+ bash validate-submission.sh <your_space_url> .
290
+ ```
291
+
292
+ ---
293
+
294
+ ## Project Structure
295
+
296
+ ```
297
+ meta-ads-openenv/
298
+ β”œβ”€β”€ openenv.yaml # OpenEnv metadata
299
+ β”œβ”€β”€ inference.py # Required hackathon inference script
300
+ β”œβ”€β”€ requirements.txt # Dependencies
301
+ β”œβ”€β”€ Dockerfile # Container definition
302
+ β”‚
303
+ β”œβ”€β”€ meta_ads_env/ # Core environment
304
+ β”‚ β”œβ”€β”€ env.py # Main environment class
305
+ β”‚ β”œβ”€β”€ models.py # Pydantic models
306
+ β”‚ β”œβ”€β”€ simulator.py # Attribution simulator
307
+ β”‚ β”œβ”€β”€ reward.py # Reward function
308
+ β”‚ β”œβ”€β”€ grader.py # Task graders
309
+ β”‚ └── tasks.py # Task definitions
310
+ β”‚
311
+ β”œβ”€β”€ baseline/ # Baseline agent
312
+ β”‚ β”œβ”€β”€ baseline_agent.py # LLM-powered agent
313
+ β”‚ └── run_baseline.py # Evaluation script
314
+ β”‚
315
+ β”œβ”€β”€ evaluation/ # Evaluation tools
316
+ β”‚ β”œβ”€β”€ llm_grader.py # Optional LLM-as-judge
317
+ β”‚ └── metrics.py # Aggregate metrics
318
+ β”‚
319
+ └── validate-submission.sh # Submission validator
320
+ ```
321
+
322
+ ---
323
+
324
+ ## Advanced Usage
325
+
326
+ ### Validate OpenEnv Compliance
327
+ ```bash
328
+ pip install openenv
329
+ openenv validate .
330
+ ```
331
+
332
+ ### Custom Training Loop
333
+ ```python
334
+ for episode in range(100):
335
+ obs = env.reset()
336
+ done = False
337
+
338
+ while not done:
339
+ action = your_policy.select_action(obs)
340
+ obs, reward, done, info = env.step(action)
341
+ your_policy.update(obs, action, reward)
342
+ ```
343
+
344
+ ### LLM Scoring
345
+ ```python
346
+ from evaluation.llm_grader import LLMGrader
347
+
348
+ grader = LLMGrader(model="Qwen/Qwen2.5-72B-Instruct")
349
+ result = grader.grade_trajectory(
350
+ task_id="hard_full_attribution_audit",
351
+ history=env.state().history,
352
+ initial_context=initial_obs.context,
353
+ final_context=final_obs.context
354
+ )
355
+ ```
356
+
357
+ ---
358
+
359
+ ## Acknowledgments
360
+
361
+ Built to demonstrate how AI agents can solve high-impact marketing optimization problems in realistic attribution environments. Inspired by real Meta Ads attribution challenges faced by performance teams at scale.
362
+
363
+ **OpenEnv**: RL environment specification
364
+ **Meta Ads Manager**: Real-world attribution dynamics and constraints
365
+ **Digital Marketing Community**: Practical insights from attribution and measurement operations
366
+
367
+ ---
368
+
369
+ **Making attribution-aware AI optimization practical and measurable**