Skip to content

Commit bb1e72e

Browse files
release: v1.4.1 — Sprint 3 additive follow-up
New surface (additive kwargs only; all v1.4.0 APIs stable): - sp.shift_share_political_panel(cluster='shock') — panel-extended Adão-Kolesár-Morales (2019) shock-clustered SE recommended by Park-Xu (2026) §4.2; exposes diagnostics['akm_se'] + human-readable diagnostics['cluster'] label. - sp.causal_llm.anthropic_client(thinking_budget=N) — Claude 4.5 / Opus 4.7 extended-thinking API. Reasoning trace stored on client.history[-1]['thinking'] for auditability; NOT returned as part of the public answer. Handles thinking + redacted_thinking content blocks. Eagerly validates budget >= 1024 and < max_tokens. Test suites: - tests/reference_parity/test_assimilation_parity.py — 10 checks on Kalman / particle backends (already tracked since 1.4.0 wire-up). - tests/integration/test_causal_mas_with_fake_llm.py — 11 e2e MAS tests + 3 Claude thinking block-splitter tests mocking Anthropic SDK. Fixed stale dag.edges() call (it's an attribute, not a method). Docs: - docs/guides/shift_share_political_panel.md — full panel-IV recipe incl. AKM shock-cluster guidance + pretrend workflow. - docs/guides/causal_mas.md — multi-agent LLM causal discovery walkthrough, real-SDK integration, Claude thinking mode, pipe into sp.dag / sp.identify. Both wired into mkdocs.yml nav. Housekeeping: - Version bumped to 1.4.1 across pyproject.toml, __init__.py, README, README_CN, docs/index.md, BibTeX citations. - CHANGELOG: [1.4.0] entry restored to its as-shipped text; new [1.4.1] section consolidates the Sprint 3 additions above. Sanity: 21 passed on integration + assimilation parity; 21/3-skipped on the bartik/shift_share keyword slice.
1 parent c78b68f commit bb1e72e

12 files changed

Lines changed: 739 additions & 70 deletions

File tree

CHANGELOG.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,87 @@
22

33
All notable changes to StatsPAI will be documented in this file.
44

5+
## [1.4.1] — 2026-04-21 — v3-frontier sprint 3 (AKM SE + Claude thinking + parity suites + docs)
6+
7+
Additive follow-up to v1.4.0. All v1.4.0 APIs remain stable; new
8+
functionality is exposed through additive kwargs on existing entry
9+
points.
10+
11+
### Added — shock-clustered SE for panel shift-share
12+
13+
- **`sp.shift_share_political_panel(..., cluster='shock')`** — new
14+
option computes the panel-extended Adão-Kolesár-Morales (2019)
15+
variance estimator recommended by Park-Xu (2026) §4.2:
16+
17+
```text
18+
u_k = Σ_{i, t} s_{ikt} · Z̃_{it} · ε̂_{it}
19+
Var(β̂) = Σ_k u_k² / (D̂'_fit · D̃)²
20+
```
21+
22+
Typically 3× tighter than unit-clustered SEs in settings with 10–100
23+
industries. `diagnostics['akm_se']` exposes the value alongside the
24+
chosen cluster type, and `diagnostics['cluster']` is now a
25+
human-readable label (`"shock (AKM 2019)"` when the shock estimator
26+
is active).
27+
[`bartik/political.py`]
28+
29+
### Added — Claude extended-thinking support for Causal MAS
30+
31+
- **`sp.causal_llm.anthropic_client(..., thinking_budget=N)`** — opt
32+
into the Claude 4.5 / Opus 4.7 **extended-thinking** API. The
33+
reasoning trace is captured on `client.history[-1]['thinking']` for
34+
auditability but is NOT included in the public answer parsed by
35+
`causal_mas`. Compatible with Anthropic's `thinking` /
36+
`redacted_thinking` content blocks; both are handled cleanly.
37+
Validates `thinking_budget >= 1024` and `< max_tokens` eagerly, so
38+
misconfiguration fails loudly before the first API call.
39+
[`causal_llm/llm_clients.py`]
40+
41+
### Added — parity + integration test suites
42+
43+
- **`tests/reference_parity/test_assimilation_parity.py`** — 10 checks
44+
on the Kalman / particle backends:
45+
- static-effect posterior recovery (both backends)
46+
- Kalman ↔ particle agreement on three seeds (point + SD within 15%)
47+
- monotone posterior variance under `process_var = 0`
48+
- particle-filter ESS stays above threshold after resampling
49+
- Student-t particle beats Kalman on a contaminated stream
50+
- drift tracking without variance blow-up
51+
- `assimilative_causal(backend=...)` matches direct-backend calls
52+
53+
- **`tests/integration/test_causal_mas_with_fake_llm.py`** — 11
54+
end-to-end integration tests using the deterministic `echo_client`
55+
to drive the proposer / critic / domain-expert / synthesiser loop:
56+
proposer parsing (newlines + bullets), critic rejection,
57+
domain-expert endorsement lifting confidence, transcript
58+
auditability, confidence scaling with rounds, role overrides, DAG
59+
interop via `sp.dag(...)`, plus three Claude-thinking content-block
60+
splitter tests that mock Anthropic responses without requiring the
61+
`anthropic` SDK at test time.
62+
63+
### Documentation
64+
65+
Two new MkDocs guides, wired into `mkdocs.yml` nav under
66+
*DID & Panel Methods* / guides:
67+
68+
- `docs/guides/shift_share_political_panel.md` — full panel-IV recipe
69+
including AKM shock-cluster guidance and pretrend workflow.
70+
- `docs/guides/causal_mas.md` — multi-agent LLM causal discovery,
71+
real-SDK integration, Claude thinking-mode walkthrough, and
72+
end-to-end pipe into `sp.dag` / `sp.identify`.
73+
74+
### Fixed
75+
76+
- Integration test used `dag.edges()` but `DAG.edges` is a list-of-
77+
tuples **attribute** (not a method). Corrected to `dag.edges`.
78+
79+
### Backwards compatibility
80+
81+
- All v1.4.0 APIs remain stable. The only new surface is additive
82+
kwargs:
83+
- `sp.shift_share_political_panel(cluster='shock')`
84+
- `sp.causal_llm.anthropic_client(thinking_budget=N)`
85+
586
## [1.4.0] — 2026-04-21 — v3-frontier sprint 2 (extensions + LLM SDK + docs)
687

788
Follow-up to v1.3.0 covering the four secondary items flagged at the

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,20 @@ StatsPAI is the **agent-native** Python package for causal inference and applied
1515

1616
It brings R's [Causal Inference Task View](https://cran.r-project.org/web/views/CausalInference.html) (fixest, did, rdrobust, gsynth, DoubleML, MatchIt, CausalImpact, ...) and Stata's core econometrics commands into a single, consistent Python API.
1717

18-
**🎉 NEW in v1.4.0 — v3-frontier Sprint 2: panel shift-share, real-LLM adapters, particle-filter assimilation, 3 new guides**
18+
**🎉 NEW in v1.4.1 — v3-frontier Sprint 3: AKM shock-clustered SE, Claude extended thinking, parity + integration suites, 2 new guides**
1919

20-
StatsPAI 1.4 is Sprint 2 of the 知识地图 v3 roadmap. Closes the four secondary items flagged at the end of Sprint 1: multi-period Park-Xu political shift-share, real OpenAI / Anthropic LLM adapters for the Causal MAS discovery agent, a particle-filter backend for `causal_kalman` to handle non-Gaussian priors and nonlinear dynamics, and three new MkDocs guides covering the v3 frontier. 20 unused-import cleanups across Sprint 1 modules. One CI flake (CausalForest ATE parity test) deflaked by seeding the forest explicitly.
20+
StatsPAI 1.4.1 is an additive follow-up to 1.4.0 that closes the Sprint 3 items:
21+
22+
- **AKM shock-clustered SE**`sp.shift_share_political_panel(cluster='shock')` computes the panel-extended Adão-Kolesár-Morales (2019) variance estimator recommended by Park-Xu (2026) §4.2 — typically 3× tighter than unit-clustered SEs in settings with 10–100 industries. `diagnostics['akm_se']` and a human-readable `diagnostics['cluster']` label surface the result.
23+
- **Claude extended thinking for Causal MAS**`sp.causal_llm.anthropic_client(thinking_budget=N)` opts into the Claude 4.5 / Opus 4.7 extended-thinking API. The reasoning trace is captured on `client.history[-1]['thinking']` for auditability but is not returned to `causal_mas`. Handles both `thinking` and `redacted_thinking` content blocks.
24+
- **Parity + integration test suites**`tests/reference_parity/test_assimilation_parity.py` (10 checks on the Kalman / particle backends, incl. Kalman↔particle agreement and Student-t contamination robustness) and `tests/integration/test_causal_mas_with_fake_llm.py` (11 end-to-end MAS tests using `echo_client` + 3 Claude thinking block-splitter tests mocking the Anthropic SDK).
25+
- **Two new MkDocs guides**`docs/guides/shift_share_political_panel.md` (full panel-IV recipe incl. AKM shock-cluster) and `docs/guides/causal_mas.md` (multi-agent LLM causal discovery walkthrough).
26+
27+
All v1.4.0 APIs remain stable; the new surface is strictly additive kwargs.
28+
29+
**Previously in v1.4.0 — v3-frontier Sprint 2: panel shift-share, real-LLM adapters, particle-filter assimilation, 3 new guides**
30+
31+
StatsPAI 1.4.0 is Sprint 2 of the 知识地图 v3 roadmap. Closes the four secondary items flagged at the end of Sprint 1: multi-period Park-Xu political shift-share, real OpenAI / Anthropic LLM adapters for the Causal MAS discovery agent, a particle-filter backend for `causal_kalman` to handle non-Gaussian priors and nonlinear dynamics, and three new MkDocs guides covering the v3 frontier. 20 unused-import cleanups across Sprint 1 modules. One CI flake (CausalForest ATE parity test) deflaked by seeding the forest explicitly.
2132

2233
| Area | v1.4 Highlights |
2334
| --- | --- |
@@ -970,7 +981,7 @@ pytest
970981
author={Wang, Biaoyue},
971982
year={2026},
972983
url={https://github.com/brycewang-stanford/statspai},
973-
version={1.4.0}
984+
version={1.4.1}
974985
}
975986
```
976987

README_CN.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,20 @@ StatsPAI 是一个**面向 AI Agent** 的 Python 因果推断与应用计量经
1515

1616
它将 R 的 [Causal Inference Task View](https://cran.r-project.org/web/views/CausalInference.html)(fixest、did、rdrobust、gsynth、DoubleML、MatchIt、CausalImpact、sfaR、lme4、oaxaca、ddecompose……)和 Stata 的核心计量命令(`frontier``xtfrontier``mixed``meglm``mixlogit``ivqreg`……),统一到一个一致的 Python API 中。
1717

18-
**🎉 v1.4.0 新版本 — v3 前沿 Sprint 2:面板 shift-share、真·LLM 适配器、粒子滤波同化、3 篇新指南**
18+
**🎉 v1.4.1 新版本 — v3 前沿 Sprint 3:AKM 冲击聚类 SE、Claude 扩展思考、对齐与集成测试套件、2 篇新指南**
1919

20-
StatsPAI 1.4 是知识地图 v3 路线图的 Sprint 2,关闭了 Sprint 1 末尾的 4 个次要项:Park-Xu 政治 shift-share 多期扩展、Causal MAS 发现 Agent 的真·OpenAI/Anthropic 适配器、`causal_kalman` 的粒子滤波后端(处理非高斯先验与非线性动力学),以及 3 篇覆盖 v3 前沿的 MkDocs 指南。Sprint 1 模块的 20 处 unused-import 清理。CI 上 1 个 CausalForest ATE parity 测试的 flake 通过显式播种修复。
20+
StatsPAI 1.4.1 在 1.4.0 基础上做增量更新,关闭 Sprint 3 的 4 项工作:
21+
22+
- **AKM 冲击聚类 SE**`sp.shift_share_political_panel(cluster='shock')` 实现 Park-Xu(2026)§4.2 推荐的 Adão-Kolesár-Morales(2019)面板扩展方差估计,在 10–100 个行业的设定下通常比单元聚类 SE 紧 3×。`diagnostics['akm_se']` 与可读 `diagnostics['cluster']` 标签同时返回。
23+
- **Causal MAS 的 Claude 扩展思考**`sp.causal_llm.anthropic_client(thinking_budget=N)` 接入 Claude 4.5 / Opus 4.7 扩展思考 API。推理轨迹写入 `client.history[-1]['thinking']` 以便审计,但不会出现在 `causal_mas` 解析的正文里。同时处理 `thinking``redacted_thinking` 两类 content block。
24+
- **对齐与集成测试套件**`tests/reference_parity/test_assimilation_parity.py`(10 项 Kalman / 粒子后端校验,含 Kalman↔粒子一致性与 Student-t 污染鲁棒性)与 `tests/integration/test_causal_mas_with_fake_llm.py`(11 个基于 `echo_client` 的 MAS 端到端测试 + 3 个 mock Anthropic SDK 的 Claude thinking block 解析测试)。
25+
- **两篇新 MkDocs 指南**`docs/guides/shift_share_political_panel.md`(含 AKM 冲击聚类的完整面板 IV 流程)与 `docs/guides/causal_mas.md`(多智能体 LLM 因果发现完整走查)。
26+
27+
v1.4.0 的所有公开 API 保持稳定;新增面只是附加 kwargs。
28+
29+
**v1.4.0 — v3 前沿 Sprint 2:面板 shift-share、真·LLM 适配器、粒子滤波同化、3 篇新指南**
30+
31+
StatsPAI 1.4.0 是知识地图 v3 路线图的 Sprint 2,关闭了 Sprint 1 末尾的 4 个次要项:Park-Xu 政治 shift-share 多期扩展、Causal MAS 发现 Agent 的真·OpenAI/Anthropic 适配器、`causal_kalman` 的粒子滤波后端(处理非高斯先验与非线性动力学),以及 3 篇覆盖 v3 前沿的 MkDocs 指南。Sprint 1 模块的 20 处 unused-import 清理。CI 上 1 个 CausalForest ATE parity 测试的 flake 通过显式播种修复。
2132

2233
| 模块 | v1.4 亮点 |
2334
| --- | --- |
@@ -364,7 +375,7 @@ pytest
364375
author={Wang, Biaoyue},
365376
year={2026},
366377
url={https://github.com/brycewang-stanford/statspai},
367-
version={1.4.0}
378+
version={1.4.1}
368379
}
369380
```
370381

docs/guides/causal_mas.md

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
# Causal MAS — Multi-Agent LLM Causal Discovery
2+
3+
> arXiv:2509.00987 (September 2025): *Causal MAS — A Survey of Large
4+
> Language Model Architectures for Discovery and Effect Estimation.*
5+
6+
## 1. Why multi-agent?
7+
8+
Single-prompt LLM causal-discovery pipelines (Kiciman-Sharma 2025) are
9+
brittle: one model run produces one DAG, and you have no audit trail
10+
to tell a confident but wrong edge from a cautious correct one.
11+
12+
Causal MAS flips the architecture:
13+
14+
```
15+
Proposer ──► list of candidate edges
16+
Critic ──► rejects edges violating time-order / exclusion / DAG axioms
17+
Domain ──► endorses edges consistent with substantive knowledge
18+
Synthesiser ─► accumulates per-edge votes over N rounds
19+
```
20+
21+
After `N` rounds every edge has a confidence in `[0, 1]`: the fraction
22+
of rounds in which it survived critic rejection plus a half-point per
23+
domain endorsement. The final DAG keeps edges above
24+
`final_threshold`.
25+
26+
## 2. Quick-start (offline, no API key)
27+
28+
```python
29+
import statspai as sp
30+
31+
res = sp.causal_llm.causal_mas(
32+
variables=['age', 'sex', 'income', 'treatment', 'mortality'],
33+
domain='observational mortality cohort',
34+
rounds=3,
35+
final_threshold=0.5,
36+
)
37+
print(res.summary())
38+
print(res.to_dag_string())
39+
```
40+
41+
The default backend is the **deterministic heuristic** shipped with
42+
StatsPAI — it uses the same variable-name pattern matcher as
43+
`sp.causal_llm.llm_dag_propose`. You get reproducible output with no
44+
API key, no network, and no surprises.
45+
46+
## 3. Plugging in a real LLM
47+
48+
```python
49+
# OpenAI-compatible (default gpt-4o-mini, set OPENAI_API_KEY)
50+
openai_cli = sp.causal_llm.openai_client(
51+
model='gpt-4o-mini',
52+
temperature=0.0, # deterministic for reproducibility
53+
)
54+
55+
# Anthropic (default claude-opus-4-7, set ANTHROPIC_API_KEY)
56+
claude_cli = sp.causal_llm.anthropic_client(
57+
model='claude-opus-4-7',
58+
temperature=0.0,
59+
)
60+
61+
res = sp.causal_llm.causal_mas(
62+
variables=['age', 'sex', 'treatment', 'ldl', 'mi'],
63+
domain='cardiovascular prevention in type-2 diabetes',
64+
client=claude_cli,
65+
rounds=4,
66+
)
67+
```
68+
69+
Both clients expose the `LLMClient.chat(role, prompt) -> str`
70+
interface, so you can supply any other model by wrapping it yourself:
71+
72+
```python
73+
class MyClient(sp.causal_llm.LLMClient):
74+
name = 'my-local-llm'
75+
def chat(self, role, prompt):
76+
return my_inference_server(role, prompt) # whatever you have
77+
```
78+
79+
## 4. Claude extended thinking
80+
81+
Claude 4.5 / Opus 4.7 support extended thinking — the model reasons
82+
privately for a configurable token budget before producing its public
83+
answer. For causal discovery this frequently lifts edge accuracy on
84+
ambiguous domains (Kiciman-Sharma 2025 §5).
85+
86+
```python
87+
claude_thinking = sp.causal_llm.anthropic_client(
88+
model='claude-opus-4-7',
89+
thinking_budget=4096, # reasoning tokens
90+
max_tokens=8192, # total (must exceed thinking_budget)
91+
)
92+
res = sp.causal_llm.causal_mas(
93+
variables=[...], client=claude_thinking, rounds=3,
94+
)
95+
96+
# Inspect the private reasoning trace for audit:
97+
for entry in claude_thinking.history:
98+
if 'thinking' in entry:
99+
print(f"[{entry['role']}] thought for "
100+
f"{len(entry['thinking'])} chars before answering.")
101+
```
102+
103+
The reasoning text is stored on `client.history[-1]['thinking']`; it is
104+
**never** included in the public answer that `causal_mas` parses — so
105+
your DAG construction is unaffected by the thinking text, you just get
106+
optional auditability.
107+
108+
## 5. The debate transcript
109+
110+
Every MAS run produces a full `transcript` for auditability:
111+
112+
```python
113+
for entry in res.transcript[:8]:
114+
print(f"[round={entry['round']}] {entry['agent']}:{entry['action']}: "
115+
f"{entry['payload']}")
116+
```
117+
118+
Four entries per round: `propose` (proposer), `reject` (critic),
119+
`endorse` (domain expert), `score` (synthesiser). The `score` entry
120+
records the running edge-count map, so you can replay how confidence
121+
accumulated.
122+
123+
## 6. Reading the result
124+
125+
```python
126+
res.edges # -> list[(parent, child)] surviving final_threshold
127+
res.confidence # -> dict{edge -> float in [0, 1]}
128+
res.roles # -> dict{variable -> 'treatment' | 'outcome' | 'confounder' | ...}
129+
res.transcript # -> list of round-by-round debate events
130+
res.backend # -> 'heuristic' | 'openai' | 'anthropic' | 'echo' | user's name
131+
res.to_dag_string() # -> 'A -> B; C -> D' for sp.dag(...)
132+
```
133+
134+
## 7. Pipe into `sp.dag` for identification
135+
136+
```python
137+
dag_str = res.to_dag_string()
138+
dag = sp.dag(dag_str)
139+
140+
# Identify the causal effect of 'treatment' on 'mortality':
141+
ident = sp.identify(dag, treatment='treatment', outcome='mortality')
142+
print(ident.summary())
143+
```
144+
145+
If the MAS DAG identifies the effect, `ident` will include an
146+
adjustment set. If the DAG has cycles or incomplete roles, the
147+
`identify` call will tell you which edges to challenge — typically a
148+
useful signal that the critic round didn't reject enough.
149+
150+
## 8. Role overrides vs. the heuristic
151+
152+
The heuristic variable-name classifier is a starting guess. Override
153+
it whenever you know better:
154+
155+
```python
156+
res = sp.causal_llm.causal_mas(
157+
variables=['A', 'B', 'C', 'D'], # all opaque names
158+
treatment='B',
159+
outcome='D',
160+
confounders=['A'],
161+
instruments=['C'],
162+
client=my_client,
163+
rounds=3,
164+
)
165+
```
166+
167+
Explicit kwargs always win over the name heuristic.
168+
169+
## 9. Integration with the rest of StatsPAI
170+
171+
| Use case | Pipeline |
172+
|-----------------------------------------------------|------------------------------------------------------------------|
173+
| Propose a DAG, identify effects, estimate, robustness | `causal_mas``sp.dag``sp.identify``sp.smart.recommend``sp.dml` |
174+
| Build an E-value under unobserved confounding | `causal_mas``sp.causal_llm.llm_unobserved_confounders``sp.evalue` |
175+
| Cross-examine against a domain DAG | `causal_mas` → diff `res.edges` with the experts' DAG |
176+
| Streaming update of the DAG as new data arrive | Run `causal_mas` per batch, combine via `sp.dag` edge intersection |
177+
178+
## 10. When *not* to use this
179+
180+
- **High-stakes clinical or legal decisions.** The heuristic backend
181+
is not a substitute for a domain-expert DAG review. Treat the
182+
output as a *proposal* to be audited, not a ground truth.
183+
- **Very large variable sets (≥ 50 nodes).** Token budgets and
184+
combinatorial explosion of edge proposals degrade quality; cluster
185+
the variables first or use a structured DAG-learning algorithm
186+
like `sp.pcalg` or `sp.ges`.
187+
- **Quantitative effect estimation.** Causal MAS only produces a DAG.
188+
For effect magnitudes pipe the DAG into `sp.dml`, `sp.causal_forest`,
189+
`sp.metalearner`, etc.
190+
191+
## 11. References
192+
193+
- arXiv:2509.00987 (2025/09).
194+
*Causal MAS: A Survey of Large Language Model Architectures for
195+
Discovery and Effect Estimation.*
196+
- Kiciman, E. & Sharma, A. (2025).
197+
*Causal Reasoning with Large Language Models.* arXiv:2402.11068.
198+
- Anthropic (2025).
199+
*Extended Thinking in Claude 4.5 / Opus 4.7.* Technical report.

0 commit comments

Comments
 (0)