synthesis-econ 0.2.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.
- synthesis_econ-0.2.0/PKG-INFO +237 -0
- synthesis_econ-0.2.0/README.md +208 -0
- synthesis_econ-0.2.0/setup.cfg +4 -0
- synthesis_econ-0.2.0/setup.py +34 -0
- synthesis_econ-0.2.0/synthesis/__init__.py +7 -0
- synthesis_econ-0.2.0/synthesis/alignment/__init__.py +4 -0
- synthesis_econ-0.2.0/synthesis/alignment/meta.py +184 -0
- synthesis_econ-0.2.0/synthesis/alignment/qqa.py +165 -0
- synthesis_econ-0.2.0/synthesis/cli.py +208 -0
- synthesis_econ-0.2.0/synthesis/data/__init__.py +4 -0
- synthesis_econ-0.2.0/synthesis/data/fred.py +134 -0
- synthesis_econ-0.2.0/synthesis/data/worldbank.py +114 -0
- synthesis_econ-0.2.0/synthesis/extraction/__init__.py +3 -0
- synthesis_econ-0.2.0/synthesis/extraction/claims.py +97 -0
- synthesis_econ-0.2.0/synthesis/report/__init__.py +4 -0
- synthesis_econ-0.2.0/synthesis/report/generate.py +121 -0
- synthesis_econ-0.2.0/synthesis/report/html.py +398 -0
- synthesis_econ-0.2.0/synthesis/retrieval/__init__.py +6 -0
- synthesis_econ-0.2.0/synthesis/retrieval/arxiv.py +55 -0
- synthesis_econ-0.2.0/synthesis/retrieval/dedup.py +45 -0
- synthesis_econ-0.2.0/synthesis/retrieval/nber.py +56 -0
- synthesis_econ-0.2.0/synthesis/retrieval/openalex.py +54 -0
- synthesis_econ-0.2.0/synthesis_econ.egg-info/PKG-INFO +237 -0
- synthesis_econ-0.2.0/synthesis_econ.egg-info/SOURCES.txt +26 -0
- synthesis_econ-0.2.0/synthesis_econ.egg-info/dependency_links.txt +1 -0
- synthesis_econ-0.2.0/synthesis_econ.egg-info/entry_points.txt +2 -0
- synthesis_econ-0.2.0/synthesis_econ.egg-info/requires.txt +7 -0
- synthesis_econ-0.2.0/synthesis_econ.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: synthesis-econ
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: AI-powered economics research tool: QQA alignment of literature and empirical data.
|
|
5
|
+
Home-page: https://github.com/bsin-researcher/synthesis
|
|
6
|
+
Author: Blake Sinclair
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: anthropic>=0.109.0
|
|
15
|
+
Requires-Dist: requests>=2.34.0
|
|
16
|
+
Requires-Dist: pandas>=2.0.0
|
|
17
|
+
Requires-Dist: plotly>=5.20.0
|
|
18
|
+
Requires-Dist: rich>=13.0.0
|
|
19
|
+
Requires-Dist: typer>=0.9.0
|
|
20
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
21
|
+
Dynamic: author
|
|
22
|
+
Dynamic: classifier
|
|
23
|
+
Dynamic: description
|
|
24
|
+
Dynamic: description-content-type
|
|
25
|
+
Dynamic: home-page
|
|
26
|
+
Dynamic: requires-dist
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
Dynamic: summary
|
|
29
|
+
|
|
30
|
+
# Synthesis
|
|
31
|
+
|
|
32
|
+
**AI-powered economics research briefs combining qualitative literature and quantitative data.**
|
|
33
|
+
|
|
34
|
+
Synthesis retrieves papers from arXiv, OpenAlex, and NBER, extracts structured empirical claims using Claude, pulls FRED and World Bank time-series data, and scores theory against evidence using a novel **Quantitative-Qualitative Alignment (QQA)** method — all in a single command.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
synthesis "Does raising the minimum wage increase unemployment?"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
→ Generates an 8-section PhD-level research brief with interactive charts and a gap matrix in under 2 minutes.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## What It Produces
|
|
45
|
+
|
|
46
|
+
- **Literature consensus** across 20+ papers with identified fault lines
|
|
47
|
+
- **Extracted claims** structured by variable, direction, methodology, geography, and confidence
|
|
48
|
+
- **Empirical data** from FRED (US macro) and World Bank (cross-country)
|
|
49
|
+
- **QQA alignment scores** — each theoretical claim scored against empirical data
|
|
50
|
+
- **Research gap matrix** — unstudied methodology × geography combinations
|
|
51
|
+
- **Suggested research directions** with identification strategies
|
|
52
|
+
- **Interactive HTML report** with Plotly charts (zoomable, hoverable)
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## The Core Idea: QQA
|
|
57
|
+
|
|
58
|
+
Most research tools do one of two things: search literature (qualitative) or fetch data (quantitative). **Synthesis does both and aligns them.**
|
|
59
|
+
|
|
60
|
+
The Quantitative-Qualitative Alignment (QQA) method:
|
|
61
|
+
1. Extracts structured claims from paper abstracts: `variable_a → variable_b | direction | methodology | geography | confidence`
|
|
62
|
+
2. Fetches empirical data relevant to the question via Claude-identified FRED and World Bank series
|
|
63
|
+
3. Scores each claim against the data: `strongly_supported / supported / neutral / contradicted / strongly_contradicted / insufficient_data`
|
|
64
|
+
4. Identifies where the literature and data diverge — the most productive place for new research
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Quick Start
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install synthesis-econ
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Set your API keys (get them free):
|
|
75
|
+
- [Anthropic API key](https://console.anthropic.com/) — for Claude
|
|
76
|
+
- [FRED API key](https://fred.stlouisfed.org/docs/api/api_key.html) — for US macro data
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
export ANTHROPIC_API_KEY='sk-ant-...'
|
|
80
|
+
export FRED_API_KEY='your-fred-key'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Run a research brief:
|
|
84
|
+
```bash
|
|
85
|
+
synthesis "Does immigration lower wages for native workers?"
|
|
86
|
+
synthesis "What is the effect of quantitative easing on inflation?"
|
|
87
|
+
synthesis "Do charter schools improve student outcomes?"
|
|
88
|
+
synthesis "Does foreign aid promote economic growth?"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The HTML report opens automatically in your browser.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Install from Source
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
git clone https://github.com/blakesinclair/synthesis.git
|
|
99
|
+
cd synthesis
|
|
100
|
+
pip install -e .
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## How It Works
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
Your question
|
|
109
|
+
│
|
|
110
|
+
├── arXiv (economics: econ.GN, econ.EM, econ.LG, econ.TH, econ.HE, econ.IO)
|
|
111
|
+
├── OpenAlex (250M+ peer-reviewed works)
|
|
112
|
+
└── NBER (64K+ working papers)
|
|
113
|
+
│
|
|
114
|
+
▼
|
|
115
|
+
Claude extracts structured claims
|
|
116
|
+
(variable_a → variable_b, direction, methodology, geography, confidence)
|
|
117
|
+
│
|
|
118
|
+
├── FRED (US macro time-series — series IDs chosen by Claude)
|
|
119
|
+
└── World Bank (cross-country indicators — chosen by Claude)
|
|
120
|
+
│
|
|
121
|
+
▼
|
|
122
|
+
QQA Alignment Scoring
|
|
123
|
+
(theory vs. data, claim by claim)
|
|
124
|
+
│
|
|
125
|
+
▼
|
|
126
|
+
Research Gap Matrix
|
|
127
|
+
(unstudied methodology × geography combinations)
|
|
128
|
+
│
|
|
129
|
+
▼
|
|
130
|
+
8-section research brief + interactive Plotly HTML report
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Example Output
|
|
136
|
+
|
|
137
|
+
**Question:** Does raising the minimum wage increase unemployment?
|
|
138
|
+
|
|
139
|
+
**Papers:** 24 (arXiv: 8, OpenAlex: 8, NBER: 8)
|
|
140
|
+
**Claims extracted:** 10
|
|
141
|
+
**FRED series:** Federal Minimum Wage, Unemployment Rate, Nonfarm Payrolls, Labor Force Participation
|
|
142
|
+
**World Bank series:** Unemployment, total (% of labor force)
|
|
143
|
+
|
|
144
|
+
> **Consensus:** No detectable disemployment effect from realistic minimum wage increases — a 138-change US DiD study and the 2022 German 22% hike both find employment essentially unchanged. Adjustment occurs on the *hours* margin, not headcount.
|
|
145
|
+
>
|
|
146
|
+
> **Key tension:** Structural search-friction models predict disemployment by construction; DiD studies find none. The reconciliation is a monopsony nonlinearity — disemployment appears only in competitive markets (German IV result), vanishes under labor-market concentration.
|
|
147
|
+
>
|
|
148
|
+
> **Top gap:** A US IV study interacting minimum wage changes with local labor-market concentration (HHI) — the German monopsony result has never been replicated on US data.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Data Sources
|
|
153
|
+
|
|
154
|
+
| Source | Coverage | Key |
|
|
155
|
+
|--------|----------|-----|
|
|
156
|
+
| arXiv | Economics preprints (7 categories) | None required |
|
|
157
|
+
| OpenAlex | 250M+ peer-reviewed works | None required |
|
|
158
|
+
| NBER | 64K+ working papers | None required |
|
|
159
|
+
| FRED | 800K+ US macro time-series | Free API key |
|
|
160
|
+
| World Bank | Cross-country development indicators | None required |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Options
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
synthesis "question" [OPTIONS]
|
|
168
|
+
|
|
169
|
+
-o, --output TEXT Output directory [default: ./synthesis_output]
|
|
170
|
+
-p, --papers INT Max papers to retrieve [default: 24]
|
|
171
|
+
--no-fred Skip FRED data
|
|
172
|
+
--no-worldbank Skip World Bank data
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Requirements
|
|
178
|
+
|
|
179
|
+
- Python 3.10+
|
|
180
|
+
- Anthropic API key (uses Claude Opus 4.8 with adaptive thinking)
|
|
181
|
+
- FRED API key (free, optional but strongly recommended)
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
anthropic>=0.109.0
|
|
185
|
+
requests>=2.34.0
|
|
186
|
+
pandas>=2.0.0
|
|
187
|
+
plotly>=5.20.0
|
|
188
|
+
rich>=13.0.0
|
|
189
|
+
typer>=0.9.0
|
|
190
|
+
python-dotenv>=1.0.0
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Roadmap
|
|
196
|
+
|
|
197
|
+
- [x] arXiv + OpenAlex + NBER retrieval
|
|
198
|
+
- [x] Claude-powered structured claim extraction (adaptive thinking)
|
|
199
|
+
- [x] FRED + World Bank empirical data (series chosen by Claude)
|
|
200
|
+
- [x] QQA alignment scoring
|
|
201
|
+
- [x] Research gap matrix
|
|
202
|
+
- [x] Interactive Plotly HTML report
|
|
203
|
+
- [ ] Semantic deduplication across sources (sentence-transformers)
|
|
204
|
+
- [ ] Numerical effect size extraction and meta-analytic pooling
|
|
205
|
+
- [ ] Formal QQA with confidence intervals
|
|
206
|
+
- [ ] Citation importance weighting (h-index, citation count)
|
|
207
|
+
- [ ] PDF and LaTeX export
|
|
208
|
+
- [ ] Benchmark vs Elicit, Consensus, Semantic Scholar
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Citation
|
|
213
|
+
|
|
214
|
+
If you use Synthesis in your research:
|
|
215
|
+
|
|
216
|
+
```bibtex
|
|
217
|
+
@software{sinclair2026synthesis,
|
|
218
|
+
author = {Sinclair, Blake},
|
|
219
|
+
title = {Synthesis: AI-Powered Economics Research via Quantitative-Qualitative Alignment},
|
|
220
|
+
year = {2026},
|
|
221
|
+
url = {https://github.com/blakesinclair/synthesis},
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Contributing
|
|
228
|
+
|
|
229
|
+
Pull requests welcome. The highest-value open items are in the roadmap above.
|
|
230
|
+
|
|
231
|
+
To add a new data source, implement the pattern in `synthesis/data/` — a function `fetch_X(question: str, client: anthropic.Anthropic) -> list[DataSeries]` and wire it into `synthesis/cli.py`.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## License
|
|
236
|
+
|
|
237
|
+
MIT
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Synthesis
|
|
2
|
+
|
|
3
|
+
**AI-powered economics research briefs combining qualitative literature and quantitative data.**
|
|
4
|
+
|
|
5
|
+
Synthesis retrieves papers from arXiv, OpenAlex, and NBER, extracts structured empirical claims using Claude, pulls FRED and World Bank time-series data, and scores theory against evidence using a novel **Quantitative-Qualitative Alignment (QQA)** method — all in a single command.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
synthesis "Does raising the minimum wage increase unemployment?"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
→ Generates an 8-section PhD-level research brief with interactive charts and a gap matrix in under 2 minutes.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## What It Produces
|
|
16
|
+
|
|
17
|
+
- **Literature consensus** across 20+ papers with identified fault lines
|
|
18
|
+
- **Extracted claims** structured by variable, direction, methodology, geography, and confidence
|
|
19
|
+
- **Empirical data** from FRED (US macro) and World Bank (cross-country)
|
|
20
|
+
- **QQA alignment scores** — each theoretical claim scored against empirical data
|
|
21
|
+
- **Research gap matrix** — unstudied methodology × geography combinations
|
|
22
|
+
- **Suggested research directions** with identification strategies
|
|
23
|
+
- **Interactive HTML report** with Plotly charts (zoomable, hoverable)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## The Core Idea: QQA
|
|
28
|
+
|
|
29
|
+
Most research tools do one of two things: search literature (qualitative) or fetch data (quantitative). **Synthesis does both and aligns them.**
|
|
30
|
+
|
|
31
|
+
The Quantitative-Qualitative Alignment (QQA) method:
|
|
32
|
+
1. Extracts structured claims from paper abstracts: `variable_a → variable_b | direction | methodology | geography | confidence`
|
|
33
|
+
2. Fetches empirical data relevant to the question via Claude-identified FRED and World Bank series
|
|
34
|
+
3. Scores each claim against the data: `strongly_supported / supported / neutral / contradicted / strongly_contradicted / insufficient_data`
|
|
35
|
+
4. Identifies where the literature and data diverge — the most productive place for new research
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install synthesis-econ
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Set your API keys (get them free):
|
|
46
|
+
- [Anthropic API key](https://console.anthropic.com/) — for Claude
|
|
47
|
+
- [FRED API key](https://fred.stlouisfed.org/docs/api/api_key.html) — for US macro data
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
export ANTHROPIC_API_KEY='sk-ant-...'
|
|
51
|
+
export FRED_API_KEY='your-fred-key'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Run a research brief:
|
|
55
|
+
```bash
|
|
56
|
+
synthesis "Does immigration lower wages for native workers?"
|
|
57
|
+
synthesis "What is the effect of quantitative easing on inflation?"
|
|
58
|
+
synthesis "Do charter schools improve student outcomes?"
|
|
59
|
+
synthesis "Does foreign aid promote economic growth?"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The HTML report opens automatically in your browser.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Install from Source
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/blakesinclair/synthesis.git
|
|
70
|
+
cd synthesis
|
|
71
|
+
pip install -e .
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## How It Works
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
Your question
|
|
80
|
+
│
|
|
81
|
+
├── arXiv (economics: econ.GN, econ.EM, econ.LG, econ.TH, econ.HE, econ.IO)
|
|
82
|
+
├── OpenAlex (250M+ peer-reviewed works)
|
|
83
|
+
└── NBER (64K+ working papers)
|
|
84
|
+
│
|
|
85
|
+
▼
|
|
86
|
+
Claude extracts structured claims
|
|
87
|
+
(variable_a → variable_b, direction, methodology, geography, confidence)
|
|
88
|
+
│
|
|
89
|
+
├── FRED (US macro time-series — series IDs chosen by Claude)
|
|
90
|
+
└── World Bank (cross-country indicators — chosen by Claude)
|
|
91
|
+
│
|
|
92
|
+
▼
|
|
93
|
+
QQA Alignment Scoring
|
|
94
|
+
(theory vs. data, claim by claim)
|
|
95
|
+
│
|
|
96
|
+
▼
|
|
97
|
+
Research Gap Matrix
|
|
98
|
+
(unstudied methodology × geography combinations)
|
|
99
|
+
│
|
|
100
|
+
▼
|
|
101
|
+
8-section research brief + interactive Plotly HTML report
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Example Output
|
|
107
|
+
|
|
108
|
+
**Question:** Does raising the minimum wage increase unemployment?
|
|
109
|
+
|
|
110
|
+
**Papers:** 24 (arXiv: 8, OpenAlex: 8, NBER: 8)
|
|
111
|
+
**Claims extracted:** 10
|
|
112
|
+
**FRED series:** Federal Minimum Wage, Unemployment Rate, Nonfarm Payrolls, Labor Force Participation
|
|
113
|
+
**World Bank series:** Unemployment, total (% of labor force)
|
|
114
|
+
|
|
115
|
+
> **Consensus:** No detectable disemployment effect from realistic minimum wage increases — a 138-change US DiD study and the 2022 German 22% hike both find employment essentially unchanged. Adjustment occurs on the *hours* margin, not headcount.
|
|
116
|
+
>
|
|
117
|
+
> **Key tension:** Structural search-friction models predict disemployment by construction; DiD studies find none. The reconciliation is a monopsony nonlinearity — disemployment appears only in competitive markets (German IV result), vanishes under labor-market concentration.
|
|
118
|
+
>
|
|
119
|
+
> **Top gap:** A US IV study interacting minimum wage changes with local labor-market concentration (HHI) — the German monopsony result has never been replicated on US data.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Data Sources
|
|
124
|
+
|
|
125
|
+
| Source | Coverage | Key |
|
|
126
|
+
|--------|----------|-----|
|
|
127
|
+
| arXiv | Economics preprints (7 categories) | None required |
|
|
128
|
+
| OpenAlex | 250M+ peer-reviewed works | None required |
|
|
129
|
+
| NBER | 64K+ working papers | None required |
|
|
130
|
+
| FRED | 800K+ US macro time-series | Free API key |
|
|
131
|
+
| World Bank | Cross-country development indicators | None required |
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Options
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
synthesis "question" [OPTIONS]
|
|
139
|
+
|
|
140
|
+
-o, --output TEXT Output directory [default: ./synthesis_output]
|
|
141
|
+
-p, --papers INT Max papers to retrieve [default: 24]
|
|
142
|
+
--no-fred Skip FRED data
|
|
143
|
+
--no-worldbank Skip World Bank data
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Requirements
|
|
149
|
+
|
|
150
|
+
- Python 3.10+
|
|
151
|
+
- Anthropic API key (uses Claude Opus 4.8 with adaptive thinking)
|
|
152
|
+
- FRED API key (free, optional but strongly recommended)
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
anthropic>=0.109.0
|
|
156
|
+
requests>=2.34.0
|
|
157
|
+
pandas>=2.0.0
|
|
158
|
+
plotly>=5.20.0
|
|
159
|
+
rich>=13.0.0
|
|
160
|
+
typer>=0.9.0
|
|
161
|
+
python-dotenv>=1.0.0
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Roadmap
|
|
167
|
+
|
|
168
|
+
- [x] arXiv + OpenAlex + NBER retrieval
|
|
169
|
+
- [x] Claude-powered structured claim extraction (adaptive thinking)
|
|
170
|
+
- [x] FRED + World Bank empirical data (series chosen by Claude)
|
|
171
|
+
- [x] QQA alignment scoring
|
|
172
|
+
- [x] Research gap matrix
|
|
173
|
+
- [x] Interactive Plotly HTML report
|
|
174
|
+
- [ ] Semantic deduplication across sources (sentence-transformers)
|
|
175
|
+
- [ ] Numerical effect size extraction and meta-analytic pooling
|
|
176
|
+
- [ ] Formal QQA with confidence intervals
|
|
177
|
+
- [ ] Citation importance weighting (h-index, citation count)
|
|
178
|
+
- [ ] PDF and LaTeX export
|
|
179
|
+
- [ ] Benchmark vs Elicit, Consensus, Semantic Scholar
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Citation
|
|
184
|
+
|
|
185
|
+
If you use Synthesis in your research:
|
|
186
|
+
|
|
187
|
+
```bibtex
|
|
188
|
+
@software{sinclair2026synthesis,
|
|
189
|
+
author = {Sinclair, Blake},
|
|
190
|
+
title = {Synthesis: AI-Powered Economics Research via Quantitative-Qualitative Alignment},
|
|
191
|
+
year = {2026},
|
|
192
|
+
url = {https://github.com/blakesinclair/synthesis},
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Contributing
|
|
199
|
+
|
|
200
|
+
Pull requests welcome. The highest-value open items are in the roadmap above.
|
|
201
|
+
|
|
202
|
+
To add a new data source, implement the pattern in `synthesis/data/` — a function `fetch_X(question: str, client: anthropic.Anthropic) -> list[DataSeries]` and wire it into `synthesis/cli.py`.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="synthesis-econ",
|
|
5
|
+
version="0.2.0",
|
|
6
|
+
author="Blake Sinclair",
|
|
7
|
+
description="AI-powered economics research tool: QQA alignment of literature and empirical data.",
|
|
8
|
+
long_description=open("README.md").read(),
|
|
9
|
+
long_description_content_type="text/markdown",
|
|
10
|
+
url="https://github.com/bsin-researcher/synthesis",
|
|
11
|
+
packages=find_packages(),
|
|
12
|
+
python_requires=">=3.10",
|
|
13
|
+
install_requires=[
|
|
14
|
+
"anthropic>=0.109.0",
|
|
15
|
+
"requests>=2.34.0",
|
|
16
|
+
"pandas>=2.0.0",
|
|
17
|
+
"plotly>=5.20.0",
|
|
18
|
+
"rich>=13.0.0",
|
|
19
|
+
"typer>=0.9.0",
|
|
20
|
+
"python-dotenv>=1.0.0",
|
|
21
|
+
],
|
|
22
|
+
entry_points={
|
|
23
|
+
"console_scripts": [
|
|
24
|
+
"synthesis=synthesis.cli:main",
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
classifiers=[
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"License :: OSI Approved :: MIT License",
|
|
30
|
+
"Operating System :: OS Independent",
|
|
31
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
32
|
+
"Intended Audience :: Science/Research",
|
|
33
|
+
],
|
|
34
|
+
)
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Meta-analytic pooling for Synthesis.
|
|
3
|
+
|
|
4
|
+
Weights each extracted claim by methodology quality × confidence × log(citations+1),
|
|
5
|
+
then computes a pooled direction score and consensus statement.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import math
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from synthesis.extraction.claims import Claim
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Evidence hierarchy — standard in meta-analysis
|
|
14
|
+
METHOD_WEIGHTS: dict[str, int] = {
|
|
15
|
+
"RCT": 5,
|
|
16
|
+
"IV": 4,
|
|
17
|
+
"DiD": 4,
|
|
18
|
+
"natural_experiment": 4,
|
|
19
|
+
"meta-analysis": 3,
|
|
20
|
+
"structural": 2,
|
|
21
|
+
"OLS": 2,
|
|
22
|
+
"survey": 1,
|
|
23
|
+
"theoretical": 1,
|
|
24
|
+
"other": 1,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
CONFIDENCE_MULT: dict[str, float] = {
|
|
28
|
+
"high": 1.0,
|
|
29
|
+
"moderate": 0.6,
|
|
30
|
+
"low": 0.3,
|
|
31
|
+
"contested": 0.2,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
DIRECTION_SCORE: dict[str, float] = {
|
|
35
|
+
"positive": 1.0,
|
|
36
|
+
"negative": -1.0,
|
|
37
|
+
"no_effect": 0.0,
|
|
38
|
+
"ambiguous": 0.0,
|
|
39
|
+
"nonlinear": 0.0,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class PooledEvidence:
|
|
45
|
+
# Core pooled estimate
|
|
46
|
+
pooled_direction: str # "positive" / "negative" / "no_effect" / "mixed"
|
|
47
|
+
weighted_score: float # -1.0 to +1.0
|
|
48
|
+
total_weight: float
|
|
49
|
+
|
|
50
|
+
# Direction breakdown
|
|
51
|
+
n_positive: int
|
|
52
|
+
n_negative: int
|
|
53
|
+
n_no_effect: int
|
|
54
|
+
n_ambiguous: int
|
|
55
|
+
n_claims: int
|
|
56
|
+
|
|
57
|
+
# Quality
|
|
58
|
+
high_confidence_count: int
|
|
59
|
+
top_methods: list[str] # top 3 methods by total weight
|
|
60
|
+
|
|
61
|
+
# Effect sizes (raw strings from extraction)
|
|
62
|
+
effect_sizes: list[str]
|
|
63
|
+
|
|
64
|
+
# Human-readable summary sentence
|
|
65
|
+
consensus_statement: str
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def pool_evidence(claims: list[Claim]) -> PooledEvidence:
|
|
69
|
+
if not claims:
|
|
70
|
+
return PooledEvidence(
|
|
71
|
+
pooled_direction="insufficient_data", weighted_score=0.0,
|
|
72
|
+
total_weight=0.0, n_positive=0, n_negative=0, n_no_effect=0,
|
|
73
|
+
n_ambiguous=0, n_claims=0, high_confidence_count=0,
|
|
74
|
+
top_methods=[], effect_sizes=[], consensus_statement="No claims to pool.",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
weighted_sum = 0.0
|
|
78
|
+
total_w = 0.0
|
|
79
|
+
direction_counts: dict[str, int] = {"positive": 0, "negative": 0,
|
|
80
|
+
"no_effect": 0, "ambiguous": 0, "nonlinear": 0}
|
|
81
|
+
method_weights: dict[str, float] = {}
|
|
82
|
+
high_conf = 0
|
|
83
|
+
effect_sizes: list[str] = []
|
|
84
|
+
|
|
85
|
+
for c in claims:
|
|
86
|
+
mw = METHOD_WEIGHTS.get(c.methodology, 1)
|
|
87
|
+
cw = CONFIDENCE_MULT.get(c.confidence, 0.3)
|
|
88
|
+
cite_w = math.log(getattr(c, "citations", 0) + 1) + 1 # +1 floor so uncited claims still count
|
|
89
|
+
weight = mw * cw * cite_w
|
|
90
|
+
|
|
91
|
+
d = c.direction if c.direction in DIRECTION_SCORE else "ambiguous"
|
|
92
|
+
direction_counts[d] = direction_counts.get(d, 0) + 1
|
|
93
|
+
weighted_sum += DIRECTION_SCORE[d] * weight
|
|
94
|
+
total_w += weight
|
|
95
|
+
|
|
96
|
+
method_weights[c.methodology] = method_weights.get(c.methodology, 0.0) + weight
|
|
97
|
+
|
|
98
|
+
if c.confidence == "high":
|
|
99
|
+
high_conf += 1
|
|
100
|
+
|
|
101
|
+
es = getattr(c, "effect_size", "")
|
|
102
|
+
if es and es not in ("unclear", "", "not reported", "N/A"):
|
|
103
|
+
effect_sizes.append(f"{c.paper_title[:40]}… — {es}")
|
|
104
|
+
|
|
105
|
+
norm_score = weighted_sum / total_w if total_w > 0 else 0.0
|
|
106
|
+
|
|
107
|
+
# Determine pooled direction
|
|
108
|
+
if abs(norm_score) < 0.15:
|
|
109
|
+
pooled_dir = "no_effect"
|
|
110
|
+
elif norm_score > 0:
|
|
111
|
+
pooled_dir = "positive"
|
|
112
|
+
else:
|
|
113
|
+
pooled_dir = "negative"
|
|
114
|
+
|
|
115
|
+
# Check for genuine disagreement
|
|
116
|
+
n_directional = direction_counts["positive"] + direction_counts["negative"]
|
|
117
|
+
if n_directional >= 2:
|
|
118
|
+
minority = min(direction_counts["positive"], direction_counts["negative"])
|
|
119
|
+
if minority / n_directional >= 0.35:
|
|
120
|
+
pooled_dir = "mixed"
|
|
121
|
+
|
|
122
|
+
top_methods = sorted(method_weights, key=method_weights.get, reverse=True)[:3]
|
|
123
|
+
|
|
124
|
+
consensus_statement = _build_statement(
|
|
125
|
+
pooled_dir, norm_score, direction_counts, high_conf, len(claims), top_methods
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return PooledEvidence(
|
|
129
|
+
pooled_direction=pooled_dir,
|
|
130
|
+
weighted_score=round(norm_score, 3),
|
|
131
|
+
total_weight=round(total_w, 1),
|
|
132
|
+
n_positive=direction_counts["positive"],
|
|
133
|
+
n_negative=direction_counts["negative"],
|
|
134
|
+
n_no_effect=direction_counts["no_effect"],
|
|
135
|
+
n_ambiguous=direction_counts.get("ambiguous", 0) + direction_counts.get("nonlinear", 0),
|
|
136
|
+
n_claims=len(claims),
|
|
137
|
+
high_confidence_count=high_conf,
|
|
138
|
+
top_methods=top_methods,
|
|
139
|
+
effect_sizes=effect_sizes[:6],
|
|
140
|
+
consensus_statement=consensus_statement,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _build_statement(
|
|
145
|
+
direction: str,
|
|
146
|
+
score: float,
|
|
147
|
+
counts: dict[str, int],
|
|
148
|
+
high_conf: int,
|
|
149
|
+
n: int,
|
|
150
|
+
methods: list[str],
|
|
151
|
+
) -> str:
|
|
152
|
+
method_str = " + ".join(methods) if methods else "various methods"
|
|
153
|
+
conf_str = f"{high_conf} high-confidence" if high_conf else "no high-confidence"
|
|
154
|
+
|
|
155
|
+
if direction == "negative":
|
|
156
|
+
strength = "strongly" if score < -0.6 else "moderately"
|
|
157
|
+
return (
|
|
158
|
+
f"Pooled evidence ({conf_str} of {n} claims via {method_str}) "
|
|
159
|
+
f"{strength} supports a NEGATIVE effect (weighted score {score:+.2f}). "
|
|
160
|
+
f"{counts['positive']} claims find positive, {counts['negative']} negative, "
|
|
161
|
+
f"{counts['no_effect']} no effect."
|
|
162
|
+
)
|
|
163
|
+
elif direction == "positive":
|
|
164
|
+
strength = "strongly" if score > 0.6 else "moderately"
|
|
165
|
+
return (
|
|
166
|
+
f"Pooled evidence ({conf_str} of {n} claims via {method_str}) "
|
|
167
|
+
f"{strength} supports a POSITIVE effect (weighted score {score:+.2f}). "
|
|
168
|
+
f"{counts['positive']} claims find positive, {counts['negative']} negative, "
|
|
169
|
+
f"{counts['no_effect']} no effect."
|
|
170
|
+
)
|
|
171
|
+
elif direction == "no_effect":
|
|
172
|
+
return (
|
|
173
|
+
f"Pooled evidence ({conf_str} of {n} claims via {method_str}) "
|
|
174
|
+
f"finds NO CLEAR EFFECT (weighted score {score:+.2f}). "
|
|
175
|
+
f"{counts['no_effect']} claims find no effect, "
|
|
176
|
+
f"{counts['positive']} positive, {counts['negative']} negative."
|
|
177
|
+
)
|
|
178
|
+
else: # mixed
|
|
179
|
+
return (
|
|
180
|
+
f"Evidence is MIXED across {n} claims via {method_str} "
|
|
181
|
+
f"(weighted score {score:+.2f}, {conf_str} findings). "
|
|
182
|
+
f"{counts['positive']} positive vs {counts['negative']} negative — "
|
|
183
|
+
f"genuine disagreement in the literature."
|
|
184
|
+
)
|