agent-ablation 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. agent_ablation-0.3.0/LICENSE +21 -0
  2. agent_ablation-0.3.0/PKG-INFO +275 -0
  3. agent_ablation-0.3.0/README.md +251 -0
  4. agent_ablation-0.3.0/agent_ablation/__init__.py +76 -0
  5. agent_ablation-0.3.0/agent_ablation/adapters/__init__.py +16 -0
  6. agent_ablation-0.3.0/agent_ablation/adapters/autogen.py +73 -0
  7. agent_ablation-0.3.0/agent_ablation/adapters/crewai.py +111 -0
  8. agent_ablation-0.3.0/agent_ablation/adapters/langgraph.py +109 -0
  9. agent_ablation-0.3.0/agent_ablation/adapters/vercel.py +96 -0
  10. agent_ablation-0.3.0/agent_ablation/core.py +583 -0
  11. agent_ablation-0.3.0/agent_ablation/models.py +162 -0
  12. agent_ablation-0.3.0/agent_ablation/reporters/__init__.py +10 -0
  13. agent_ablation-0.3.0/agent_ablation/reporters/formatters.py +130 -0
  14. agent_ablation-0.3.0/agent_ablation.egg-info/PKG-INFO +275 -0
  15. agent_ablation-0.3.0/agent_ablation.egg-info/SOURCES.txt +25 -0
  16. agent_ablation-0.3.0/agent_ablation.egg-info/dependency_links.txt +1 -0
  17. agent_ablation-0.3.0/agent_ablation.egg-info/requires.txt +3 -0
  18. agent_ablation-0.3.0/agent_ablation.egg-info/top_level.txt +1 -0
  19. agent_ablation-0.3.0/pyproject.toml +45 -0
  20. agent_ablation-0.3.0/setup.cfg +4 -0
  21. agent_ablation-0.3.0/setup.py +7 -0
  22. agent_ablation-0.3.0/tests/test_ablation.py +246 -0
  23. agent_ablation-0.3.0/tests/test_adapters.py +202 -0
  24. agent_ablation-0.3.0/tests/test_async_ablation.py +106 -0
  25. agent_ablation-0.3.0/tests/test_backward_elimination.py +56 -0
  26. agent_ablation-0.3.0/tests/test_new_adapters_and_reporters.py +117 -0
  27. agent_ablation-0.3.0/tests/test_roi_and_accuracy.py +86 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ayush Verma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,275 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-ablation
3
+ Version: 0.3.0
4
+ Summary: Leave-one-out ablation testing, backward elimination, and ROI evaluation for multi-agent decision systems — find out which agents' findings actually change the outcome.
5
+ Author-email: Ayush Verma <ayushv3533e@gmail.com>
6
+ License: MIT
7
+ Keywords: multi-agent,ai-agents,ablation,explainability,llm-agents,agent-orchestration,interpretability
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Classifier: Topic :: Software Development :: Testing
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # agent-ablation (Python)
26
+
27
+ [![CI](https://github.com/AyushCipher/agent-ablation/actions/workflows/ci.yml/badge.svg)](https://github.com/AyushCipher/agent-ablation/actions/workflows/ci.yml)
28
+ [![PyPI](https://img.shields.io/pypi/v/agent-ablation.svg)](https://pypi.org/project/agent-ablation/)
29
+ [![Python Versions](https://img.shields.io/pypi/pyversions/agent-ablation.svg)](https://pypi.org/project/agent-ablation/)
30
+
31
+ Leave-one-out ablation testing, backward elimination, and ROI evaluation for multi-agent systems in Python. You have a set of per-agent findings (scores, confidences, telemetry) and a function or model that turns those findings into a verdict. `agent-ablation` answers the key production questions:
32
+
33
+ 1. **Load-Bearing Influence:** Which agents' findings actually changed the verdict, and which were along for the ride?
34
+ 2. **Cost & Token ROI:** How many dollars and tokens did each specialist burn per verdict flip? Does that small accuracy bump justify the API bill?
35
+ 3. **Correlated Agents & Pruning:** Which redundant agents can be safely pruned via greedy backward elimination without breaking the final verdict?
36
+ 4. **Protective vs. Harmful Signals:** When ground truth is provided, did an agent's presence prevent an error (protective), or did it cause a hallucination/false positive (harmful)?
37
+ 5. **Stochastic & Async LLM Judges:** Handles async decisions and repeated sampling with majority voting to filter out LLM temperature variance.
38
+
39
+ Zero runtime dependencies. Pure Python 3.9+.
40
+
41
+ ---
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ pip install agent-ablation
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Core Features & Usage
52
+
53
+ ### 1. Basic Leave-One-Out Ablation
54
+
55
+ ```python
56
+ from typing import List
57
+ from agent_ablation import Finding, run_ablation
58
+
59
+ def decide(findings: List[Finding]) -> str:
60
+ # Noisy-OR combination
61
+ survival = 1.0
62
+ for f in findings:
63
+ survival *= (1.0 - f.score / 100.0)
64
+ risk = 1.0 - survival
65
+
66
+ if risk >= 0.7:
67
+ return "decline"
68
+ if risk <= 0.3:
69
+ return "approve"
70
+ return "escalate"
71
+
72
+ findings = [
73
+ Finding(agent_id="transaction_pattern", score=25),
74
+ Finding(agent_id="identity_signal", score=90),
75
+ Finding(agent_id="network_analysis", score=20),
76
+ ]
77
+
78
+ result = run_ablation(findings, decide)
79
+
80
+ print(result.baseline) # "decline"
81
+ print(result.load_bearing_ratio) # 0.33 (1 out of 3 agents flipped the outcome)
82
+ for p in result.per_agent:
83
+ print(p.removed_agent_id, "->", p.verdict_without, "(load-bearing)" if p.changed else "")
84
+ ```
85
+
86
+ ---
87
+
88
+ ### 2. Cost & Token ROI Analysis ("Cost per Verdict Flip")
89
+
90
+ Pass telemetry (`cost`, `tokens`, `latency_ms`) inside your findings. `batch_ablation` computes the exact ROI metrics and identifies expensive agents with low decision impact:
91
+
92
+ ```python
93
+ from agent_ablation import Finding, batch_ablation, format_markdown_report
94
+
95
+ cases = [
96
+ [
97
+ Finding(agent_id="expensive_reasoner", score=90, cost=0.15, tokens=3000),
98
+ Finding(agent_id="cheap_heuristic", score=10, cost=0.002, tokens=50),
99
+ ],
100
+ [
101
+ Finding(agent_id="expensive_reasoner", score=20, cost=0.15, tokens=3000),
102
+ Finding(agent_id="cheap_heuristic", score=85, cost=0.002, tokens=50),
103
+ ],
104
+ ]
105
+
106
+ results, summary = batch_ablation(cases, decide)
107
+
108
+ print(summary.roi.agents["expensive_reasoner"].cost_per_verdict_flip) # Cost per decision flip
109
+ print(summary.roi.recommendations) # Automated pruning/downgrade advice
110
+
111
+ # Format into a Markdown report for PRs or documentation
112
+ print(format_markdown_report(summary))
113
+ ```
114
+
115
+ ---
116
+
117
+ ### 3. Async & Stochastic Decision Functions (LLM-as-a-Judge)
118
+
119
+ When your decision step is an async LLM call with temperature, use `run_ablation_async` or `batch_ablation_async`. Set `samples=k` to take a majority-vote consensus across runs to eliminate sampling noise:
120
+
121
+ ```python
122
+ import asyncio
123
+ from agent_ablation import Finding, run_ablation_async
124
+
125
+ async def llm_supervisor_decide(findings: List[Finding]) -> str:
126
+ res = await call_llm_judge(findings)
127
+ return res["verdict"]
128
+
129
+ result = await run_ablation_async(
130
+ findings,
131
+ llm_supervisor_decide,
132
+ samples=5, # Runs 5 samples per ablation to filter out temperature noise
133
+ )
134
+ ```
135
+
136
+ ---
137
+
138
+ ### 4. Greedy Backward Elimination & Minimal Viable Panel
139
+
140
+ If you have correlated or redundant agents (e.g., three critics looking at the same context), simple leave-one-out might mark all of them as not load-bearing because the others compensate.
141
+
142
+ `run_backward_elimination` iteratively eliminates agents one-by-one until removing any further agent flips the verdict, revealing the **minimal viable panel**:
143
+
144
+ ```python
145
+ from agent_ablation import run_backward_elimination
146
+
147
+ result = run_backward_elimination(all_specialists, decide)
148
+
149
+ print(result.minimal_agent_ids) # ["critic_1", "security_auditor"]
150
+ print(result.eliminated_agent_ids) # ["critic_2", "critic_3", "scout_noisy"]
151
+ print(result.steps) # Step-by-step elimination trace
152
+ ```
153
+
154
+ For detecting 2nd-order joint dependencies, `run_pairwise_ablation(findings, decide)` evaluates all pairs $(A, B)$ to catch cases where neither agent alone is load-bearing, but removing both together flips the outcome.
155
+
156
+ ---
157
+
158
+ ### 5. Ground-Truth & Net Accuracy Impact ("Protective vs. Harmful")
159
+
160
+ Supply ground truth labels in `batch_ablation` to measure whether an agent's load-bearing presence actually **improved** accuracy or **injected errors / hallucinations**:
161
+
162
+ ```python
163
+ results, summary = batch_ablation(
164
+ cases,
165
+ decide,
166
+ ground_truth=["approve", "decline", "approve", "escalate"],
167
+ )
168
+
169
+ # Per-agent stats:
170
+ # - Protective: removing the agent caused a correct verdict to become incorrect
171
+ # - Harmful: removing the agent fixed an incorrect verdict
172
+ print(summary.per_agent_stats["hallucinating_agent"].role) # "Harmful"
173
+ print(summary.per_agent_stats["hallucinating_agent"].net_accuracy_impact) # -0.25
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Framework Adapters
179
+
180
+ Zero-dependency adapters to map telemetry and messages from popular agent frameworks directly into `Finding`:
181
+
182
+ ### LangGraph / LangChain
183
+ ```python
184
+ from agent_ablation import from_langgraph_messages
185
+
186
+ findings = from_langgraph_messages(
187
+ state["messages"],
188
+ score_of=lambda msg: msg["content"]["score"],
189
+ confidence_of=lambda msg: msg["content"].get("confidence"),
190
+ )
191
+ ```
192
+
193
+ ### CrewAI
194
+ ```python
195
+ from agent_ablation import from_crewai_tasks
196
+
197
+ findings = from_crewai_tasks(
198
+ crew_output.tasks_output,
199
+ score_of=lambda task: task.json_dict.get("score", 0),
200
+ )
201
+ ```
202
+
203
+ ### AutoGen
204
+ ```python
205
+ from agent_ablation import from_autogen_messages
206
+
207
+ findings = from_autogen_messages(
208
+ chat_history,
209
+ score_of=lambda msg: msg["content"]["risk_score"],
210
+ )
211
+ ```
212
+
213
+ ### Vercel AI SDK / Trace Steps
214
+ ```python
215
+ from agent_ablation import from_ai_sdk_steps
216
+
217
+ findings = from_ai_sdk_steps(
218
+ steps,
219
+ score_of=lambda step: step["result"]["score"],
220
+ )
221
+ ```
222
+
223
+ ### Generic Custom Records
224
+ ```python
225
+ from agent_ablation import from_records
226
+
227
+ findings = from_records(
228
+ custom_audit_records,
229
+ agent_id=lambda r, idx: r.specialist_id,
230
+ score_of=lambda r, idx: r.risk_score,
231
+ confidence_of=lambda r, idx: r.confidence_level,
232
+ )
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Worked Example: SentryMesh 33% Multi-Signal Finding
238
+
239
+ [SentryMesh](https://github.com/AyushCipher/Sentry-Mesh) is a four-specialist multi-agent fraud investigation system. Its eval harness runs an ablation over its 23-case bank and reports: of 9 cases auto-resolved without human escalation, **only 3 survive removal of their single loudest specialist — 6 collapse to `escalate`.**
240
+
241
+ `tests/test_ablation.py` reproduces all 6 cases verbatim with `agent-ablation`.
242
+
243
+ ---
244
+
245
+ ## Architectural Note: Leaf Ablation vs. DAG Subgraph Replay
246
+
247
+ * **Leaf Finding Ablation (This Package):** Best for **parallel / fan-out / fan-in** panels where specialists independently produce findings that feed a decision gate. Because findings are generated independently, dropping an item at `decide()` measures causal weight with **zero LLM re-invocation cost**.
248
+ * **Sequential DAG Replay:** If your pipeline is sequential (Agent A feeds intermediate prompt context to Agent B), removing Agent A at the final decision gate misses that Agent B's output already reflects Agent A. Measuring sequential pipelines requires replaying downstream subgraphs or injecting mock messages into the trace.
249
+
250
+ ---
251
+
252
+ ## API Summary
253
+
254
+ | Function | Description |
255
+ | :--- | :--- |
256
+ | `run_ablation(findings, decide, equals=None)` | Synchronous leave-one-out ablation for a single case. |
257
+ | `run_ablation_async(findings, decide, equals=None, samples=1, aggregate_samples=None)` | Async leave-one-out ablation with optional $K$-sampling / majority voting. |
258
+ | `batch_ablation(cases, decide, equals=None, ground_truth=None)` | Batch ablation with telemetry ROI and ground-truth metrics. |
259
+ | `batch_ablation_async(cases, decide, equals=None, samples=1, aggregate_samples=None, ground_truth=None)` | Async batch ablation. |
260
+ | `run_backward_elimination(findings, decide, equals=None)` | Greedy backward elimination to find the minimal agent panel. |
261
+ | `run_backward_elimination_async(findings, decide, ...)` | Async greedy backward elimination. |
262
+ | `run_pairwise_ablation(findings, decide, equals=None)` | Evaluates all 2-agent pairs to detect interaction/redundancy effects. |
263
+ | `format_markdown_report(summary, title=..., include_roi=True, include_recommendations=True)` | Formats summary into a GitHub/Dev.to markdown report with ROI tables. |
264
+ | `format_ascii_table(summary)` | Formats summary into a clean terminal ASCII table. |
265
+ | `from_langgraph_messages(...)` | Adapter for LangGraph message arrays. |
266
+ | `from_crewai_tasks(...)` | Adapter for CrewAI task outputs. |
267
+ | `from_autogen_messages(...)` | Adapter for AutoGen chat histories. |
268
+ | `from_ai_sdk_steps(...)` | Adapter for AI SDK / tool execution traces. |
269
+ | `from_records(...)` | Generic record mapper. |
270
+
271
+ ---
272
+
273
+ ## License
274
+
275
+ MIT © Ayush Verma — ayushv3533e@gmail.com
@@ -0,0 +1,251 @@
1
+ # agent-ablation (Python)
2
+
3
+ [![CI](https://github.com/AyushCipher/agent-ablation/actions/workflows/ci.yml/badge.svg)](https://github.com/AyushCipher/agent-ablation/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/agent-ablation.svg)](https://pypi.org/project/agent-ablation/)
5
+ [![Python Versions](https://img.shields.io/pypi/pyversions/agent-ablation.svg)](https://pypi.org/project/agent-ablation/)
6
+
7
+ Leave-one-out ablation testing, backward elimination, and ROI evaluation for multi-agent systems in Python. You have a set of per-agent findings (scores, confidences, telemetry) and a function or model that turns those findings into a verdict. `agent-ablation` answers the key production questions:
8
+
9
+ 1. **Load-Bearing Influence:** Which agents' findings actually changed the verdict, and which were along for the ride?
10
+ 2. **Cost & Token ROI:** How many dollars and tokens did each specialist burn per verdict flip? Does that small accuracy bump justify the API bill?
11
+ 3. **Correlated Agents & Pruning:** Which redundant agents can be safely pruned via greedy backward elimination without breaking the final verdict?
12
+ 4. **Protective vs. Harmful Signals:** When ground truth is provided, did an agent's presence prevent an error (protective), or did it cause a hallucination/false positive (harmful)?
13
+ 5. **Stochastic & Async LLM Judges:** Handles async decisions and repeated sampling with majority voting to filter out LLM temperature variance.
14
+
15
+ Zero runtime dependencies. Pure Python 3.9+.
16
+
17
+ ---
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ pip install agent-ablation
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Core Features & Usage
28
+
29
+ ### 1. Basic Leave-One-Out Ablation
30
+
31
+ ```python
32
+ from typing import List
33
+ from agent_ablation import Finding, run_ablation
34
+
35
+ def decide(findings: List[Finding]) -> str:
36
+ # Noisy-OR combination
37
+ survival = 1.0
38
+ for f in findings:
39
+ survival *= (1.0 - f.score / 100.0)
40
+ risk = 1.0 - survival
41
+
42
+ if risk >= 0.7:
43
+ return "decline"
44
+ if risk <= 0.3:
45
+ return "approve"
46
+ return "escalate"
47
+
48
+ findings = [
49
+ Finding(agent_id="transaction_pattern", score=25),
50
+ Finding(agent_id="identity_signal", score=90),
51
+ Finding(agent_id="network_analysis", score=20),
52
+ ]
53
+
54
+ result = run_ablation(findings, decide)
55
+
56
+ print(result.baseline) # "decline"
57
+ print(result.load_bearing_ratio) # 0.33 (1 out of 3 agents flipped the outcome)
58
+ for p in result.per_agent:
59
+ print(p.removed_agent_id, "->", p.verdict_without, "(load-bearing)" if p.changed else "")
60
+ ```
61
+
62
+ ---
63
+
64
+ ### 2. Cost & Token ROI Analysis ("Cost per Verdict Flip")
65
+
66
+ Pass telemetry (`cost`, `tokens`, `latency_ms`) inside your findings. `batch_ablation` computes the exact ROI metrics and identifies expensive agents with low decision impact:
67
+
68
+ ```python
69
+ from agent_ablation import Finding, batch_ablation, format_markdown_report
70
+
71
+ cases = [
72
+ [
73
+ Finding(agent_id="expensive_reasoner", score=90, cost=0.15, tokens=3000),
74
+ Finding(agent_id="cheap_heuristic", score=10, cost=0.002, tokens=50),
75
+ ],
76
+ [
77
+ Finding(agent_id="expensive_reasoner", score=20, cost=0.15, tokens=3000),
78
+ Finding(agent_id="cheap_heuristic", score=85, cost=0.002, tokens=50),
79
+ ],
80
+ ]
81
+
82
+ results, summary = batch_ablation(cases, decide)
83
+
84
+ print(summary.roi.agents["expensive_reasoner"].cost_per_verdict_flip) # Cost per decision flip
85
+ print(summary.roi.recommendations) # Automated pruning/downgrade advice
86
+
87
+ # Format into a Markdown report for PRs or documentation
88
+ print(format_markdown_report(summary))
89
+ ```
90
+
91
+ ---
92
+
93
+ ### 3. Async & Stochastic Decision Functions (LLM-as-a-Judge)
94
+
95
+ When your decision step is an async LLM call with temperature, use `run_ablation_async` or `batch_ablation_async`. Set `samples=k` to take a majority-vote consensus across runs to eliminate sampling noise:
96
+
97
+ ```python
98
+ import asyncio
99
+ from agent_ablation import Finding, run_ablation_async
100
+
101
+ async def llm_supervisor_decide(findings: List[Finding]) -> str:
102
+ res = await call_llm_judge(findings)
103
+ return res["verdict"]
104
+
105
+ result = await run_ablation_async(
106
+ findings,
107
+ llm_supervisor_decide,
108
+ samples=5, # Runs 5 samples per ablation to filter out temperature noise
109
+ )
110
+ ```
111
+
112
+ ---
113
+
114
+ ### 4. Greedy Backward Elimination & Minimal Viable Panel
115
+
116
+ If you have correlated or redundant agents (e.g., three critics looking at the same context), simple leave-one-out might mark all of them as not load-bearing because the others compensate.
117
+
118
+ `run_backward_elimination` iteratively eliminates agents one-by-one until removing any further agent flips the verdict, revealing the **minimal viable panel**:
119
+
120
+ ```python
121
+ from agent_ablation import run_backward_elimination
122
+
123
+ result = run_backward_elimination(all_specialists, decide)
124
+
125
+ print(result.minimal_agent_ids) # ["critic_1", "security_auditor"]
126
+ print(result.eliminated_agent_ids) # ["critic_2", "critic_3", "scout_noisy"]
127
+ print(result.steps) # Step-by-step elimination trace
128
+ ```
129
+
130
+ For detecting 2nd-order joint dependencies, `run_pairwise_ablation(findings, decide)` evaluates all pairs $(A, B)$ to catch cases where neither agent alone is load-bearing, but removing both together flips the outcome.
131
+
132
+ ---
133
+
134
+ ### 5. Ground-Truth & Net Accuracy Impact ("Protective vs. Harmful")
135
+
136
+ Supply ground truth labels in `batch_ablation` to measure whether an agent's load-bearing presence actually **improved** accuracy or **injected errors / hallucinations**:
137
+
138
+ ```python
139
+ results, summary = batch_ablation(
140
+ cases,
141
+ decide,
142
+ ground_truth=["approve", "decline", "approve", "escalate"],
143
+ )
144
+
145
+ # Per-agent stats:
146
+ # - Protective: removing the agent caused a correct verdict to become incorrect
147
+ # - Harmful: removing the agent fixed an incorrect verdict
148
+ print(summary.per_agent_stats["hallucinating_agent"].role) # "Harmful"
149
+ print(summary.per_agent_stats["hallucinating_agent"].net_accuracy_impact) # -0.25
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Framework Adapters
155
+
156
+ Zero-dependency adapters to map telemetry and messages from popular agent frameworks directly into `Finding`:
157
+
158
+ ### LangGraph / LangChain
159
+ ```python
160
+ from agent_ablation import from_langgraph_messages
161
+
162
+ findings = from_langgraph_messages(
163
+ state["messages"],
164
+ score_of=lambda msg: msg["content"]["score"],
165
+ confidence_of=lambda msg: msg["content"].get("confidence"),
166
+ )
167
+ ```
168
+
169
+ ### CrewAI
170
+ ```python
171
+ from agent_ablation import from_crewai_tasks
172
+
173
+ findings = from_crewai_tasks(
174
+ crew_output.tasks_output,
175
+ score_of=lambda task: task.json_dict.get("score", 0),
176
+ )
177
+ ```
178
+
179
+ ### AutoGen
180
+ ```python
181
+ from agent_ablation import from_autogen_messages
182
+
183
+ findings = from_autogen_messages(
184
+ chat_history,
185
+ score_of=lambda msg: msg["content"]["risk_score"],
186
+ )
187
+ ```
188
+
189
+ ### Vercel AI SDK / Trace Steps
190
+ ```python
191
+ from agent_ablation import from_ai_sdk_steps
192
+
193
+ findings = from_ai_sdk_steps(
194
+ steps,
195
+ score_of=lambda step: step["result"]["score"],
196
+ )
197
+ ```
198
+
199
+ ### Generic Custom Records
200
+ ```python
201
+ from agent_ablation import from_records
202
+
203
+ findings = from_records(
204
+ custom_audit_records,
205
+ agent_id=lambda r, idx: r.specialist_id,
206
+ score_of=lambda r, idx: r.risk_score,
207
+ confidence_of=lambda r, idx: r.confidence_level,
208
+ )
209
+ ```
210
+
211
+ ---
212
+
213
+ ## Worked Example: SentryMesh 33% Multi-Signal Finding
214
+
215
+ [SentryMesh](https://github.com/AyushCipher/Sentry-Mesh) is a four-specialist multi-agent fraud investigation system. Its eval harness runs an ablation over its 23-case bank and reports: of 9 cases auto-resolved without human escalation, **only 3 survive removal of their single loudest specialist — 6 collapse to `escalate`.**
216
+
217
+ `tests/test_ablation.py` reproduces all 6 cases verbatim with `agent-ablation`.
218
+
219
+ ---
220
+
221
+ ## Architectural Note: Leaf Ablation vs. DAG Subgraph Replay
222
+
223
+ * **Leaf Finding Ablation (This Package):** Best for **parallel / fan-out / fan-in** panels where specialists independently produce findings that feed a decision gate. Because findings are generated independently, dropping an item at `decide()` measures causal weight with **zero LLM re-invocation cost**.
224
+ * **Sequential DAG Replay:** If your pipeline is sequential (Agent A feeds intermediate prompt context to Agent B), removing Agent A at the final decision gate misses that Agent B's output already reflects Agent A. Measuring sequential pipelines requires replaying downstream subgraphs or injecting mock messages into the trace.
225
+
226
+ ---
227
+
228
+ ## API Summary
229
+
230
+ | Function | Description |
231
+ | :--- | :--- |
232
+ | `run_ablation(findings, decide, equals=None)` | Synchronous leave-one-out ablation for a single case. |
233
+ | `run_ablation_async(findings, decide, equals=None, samples=1, aggregate_samples=None)` | Async leave-one-out ablation with optional $K$-sampling / majority voting. |
234
+ | `batch_ablation(cases, decide, equals=None, ground_truth=None)` | Batch ablation with telemetry ROI and ground-truth metrics. |
235
+ | `batch_ablation_async(cases, decide, equals=None, samples=1, aggregate_samples=None, ground_truth=None)` | Async batch ablation. |
236
+ | `run_backward_elimination(findings, decide, equals=None)` | Greedy backward elimination to find the minimal agent panel. |
237
+ | `run_backward_elimination_async(findings, decide, ...)` | Async greedy backward elimination. |
238
+ | `run_pairwise_ablation(findings, decide, equals=None)` | Evaluates all 2-agent pairs to detect interaction/redundancy effects. |
239
+ | `format_markdown_report(summary, title=..., include_roi=True, include_recommendations=True)` | Formats summary into a GitHub/Dev.to markdown report with ROI tables. |
240
+ | `format_ascii_table(summary)` | Formats summary into a clean terminal ASCII table. |
241
+ | `from_langgraph_messages(...)` | Adapter for LangGraph message arrays. |
242
+ | `from_crewai_tasks(...)` | Adapter for CrewAI task outputs. |
243
+ | `from_autogen_messages(...)` | Adapter for AutoGen chat histories. |
244
+ | `from_ai_sdk_steps(...)` | Adapter for AI SDK / tool execution traces. |
245
+ | `from_records(...)` | Generic record mapper. |
246
+
247
+ ---
248
+
249
+ ## License
250
+
251
+ MIT © Ayush Verma — ayushv3533e@gmail.com
@@ -0,0 +1,76 @@
1
+ """
2
+ agent-ablation: Leave-one-out ablation testing, backward elimination, and ROI evaluation for multi-agent systems.
3
+ """
4
+
5
+ from agent_ablation.adapters.autogen import from_autogen_messages
6
+ from agent_ablation.adapters.crewai import from_crewai_tasks
7
+ from agent_ablation.adapters.langgraph import from_langgraph_messages, from_records
8
+ from agent_ablation.adapters.vercel import from_ai_sdk_steps
9
+ from agent_ablation.core import (
10
+ aggregate_batch_results,
11
+ batch_ablation,
12
+ batch_ablation_async,
13
+ majority_vote,
14
+ run_ablation,
15
+ run_ablation_async,
16
+ run_backward_elimination,
17
+ run_backward_elimination_async,
18
+ run_pairwise_ablation,
19
+ )
20
+ from agent_ablation.models import (
21
+ AblationResult,
22
+ AgentRoiMetrics,
23
+ BackwardEliminationResult,
24
+ BackwardEliminationStep,
25
+ BatchAblationSummary,
26
+ Finding,
27
+ GroundTruthSummary,
28
+ PairwiseAblationItem,
29
+ PairwiseAblationResult,
30
+ PerAgentAblation,
31
+ PerAgentStats,
32
+ PruningRecommendation,
33
+ RoiSummary,
34
+ )
35
+ from agent_ablation.reporters.formatters import (
36
+ format_ascii_table,
37
+ format_markdown_report,
38
+ )
39
+
40
+ __version__ = "0.3.0"
41
+
42
+ __all__ = [
43
+ # Models
44
+ "Finding",
45
+ "PerAgentAblation",
46
+ "AblationResult",
47
+ "AgentRoiMetrics",
48
+ "PruningRecommendation",
49
+ "RoiSummary",
50
+ "GroundTruthSummary",
51
+ "PerAgentStats",
52
+ "BatchAblationSummary",
53
+ "BackwardEliminationStep",
54
+ "BackwardEliminationResult",
55
+ "PairwiseAblationItem",
56
+ "PairwiseAblationResult",
57
+ # Core Functions
58
+ "run_ablation",
59
+ "run_ablation_async",
60
+ "batch_ablation",
61
+ "batch_ablation_async",
62
+ "aggregate_batch_results",
63
+ "run_backward_elimination",
64
+ "run_backward_elimination_async",
65
+ "run_pairwise_ablation",
66
+ "majority_vote",
67
+ # Adapters
68
+ "from_langgraph_messages",
69
+ "from_records",
70
+ "from_crewai_tasks",
71
+ "from_autogen_messages",
72
+ "from_ai_sdk_steps",
73
+ # Reporters
74
+ "format_markdown_report",
75
+ "format_ascii_table",
76
+ ]
@@ -0,0 +1,16 @@
1
+ """
2
+ Framework adapters for agent-ablation.
3
+ """
4
+
5
+ from agent_ablation.adapters.autogen import from_autogen_messages
6
+ from agent_ablation.adapters.crewai import from_crewai_tasks
7
+ from agent_ablation.adapters.langgraph import from_langgraph_messages, from_records
8
+ from agent_ablation.adapters.vercel import from_ai_sdk_steps
9
+
10
+ __all__ = [
11
+ "from_langgraph_messages",
12
+ "from_records",
13
+ "from_crewai_tasks",
14
+ "from_autogen_messages",
15
+ "from_ai_sdk_steps",
16
+ ]