synthpanel 0.5.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.
- synthpanel-0.5.0/LICENSE +21 -0
- synthpanel-0.5.0/PKG-INFO +413 -0
- synthpanel-0.5.0/README.md +375 -0
- synthpanel-0.5.0/pyproject.toml +117 -0
- synthpanel-0.5.0/setup.cfg +4 -0
- synthpanel-0.5.0/src/synth_panel/__init__.py +3 -0
- synthpanel-0.5.0/src/synth_panel/__main__.py +9 -0
- synthpanel-0.5.0/src/synth_panel/cli/__init__.py +1 -0
- synthpanel-0.5.0/src/synth_panel/cli/commands.py +1011 -0
- synthpanel-0.5.0/src/synth_panel/cli/output.py +59 -0
- synthpanel-0.5.0/src/synth_panel/cli/parser.py +308 -0
- synthpanel-0.5.0/src/synth_panel/cli/repl.py +87 -0
- synthpanel-0.5.0/src/synth_panel/cli/slash.py +139 -0
- synthpanel-0.5.0/src/synth_panel/conditions.py +182 -0
- synthpanel-0.5.0/src/synth_panel/cost.py +305 -0
- synthpanel-0.5.0/src/synth_panel/instrument.py +285 -0
- synthpanel-0.5.0/src/synth_panel/llm/__init__.py +32 -0
- synthpanel-0.5.0/src/synth_panel/llm/aliases.py +18 -0
- synthpanel-0.5.0/src/synth_panel/llm/client.py +137 -0
- synthpanel-0.5.0/src/synth_panel/llm/errors.py +65 -0
- synthpanel-0.5.0/src/synth_panel/llm/models.py +207 -0
- synthpanel-0.5.0/src/synth_panel/llm/providers/__init__.py +16 -0
- synthpanel-0.5.0/src/synth_panel/llm/providers/_openai_format.py +253 -0
- synthpanel-0.5.0/src/synth_panel/llm/providers/anthropic.py +288 -0
- synthpanel-0.5.0/src/synth_panel/llm/providers/base.py +53 -0
- synthpanel-0.5.0/src/synth_panel/llm/providers/gemini.py +116 -0
- synthpanel-0.5.0/src/synth_panel/llm/providers/openai_compat.py +105 -0
- synthpanel-0.5.0/src/synth_panel/llm/providers/xai.py +106 -0
- synthpanel-0.5.0/src/synth_panel/main.py +78 -0
- synthpanel-0.5.0/src/synth_panel/mcp/__init__.py +7 -0
- synthpanel-0.5.0/src/synth_panel/mcp/data.py +465 -0
- synthpanel-0.5.0/src/synth_panel/mcp/server.py +929 -0
- synthpanel-0.5.0/src/synth_panel/orchestrator.py +768 -0
- synthpanel-0.5.0/src/synth_panel/packs/__init__.py +3 -0
- synthpanel-0.5.0/src/synth_panel/packs/developer.yaml +82 -0
- synthpanel-0.5.0/src/synth_panel/packs/enterprise-buyer.yaml +79 -0
- synthpanel-0.5.0/src/synth_panel/packs/general-consumer.yaml +81 -0
- synthpanel-0.5.0/src/synth_panel/packs/healthcare-patient.yaml +81 -0
- synthpanel-0.5.0/src/synth_panel/packs/instruments/__init__.py +1 -0
- synthpanel-0.5.0/src/synth_panel/packs/instruments/churn-diagnosis.yaml +73 -0
- synthpanel-0.5.0/src/synth_panel/packs/instruments/feature-prioritization.yaml +75 -0
- synthpanel-0.5.0/src/synth_panel/packs/instruments/landing-page-comprehension.yaml +75 -0
- synthpanel-0.5.0/src/synth_panel/packs/instruments/name-test.yaml +74 -0
- synthpanel-0.5.0/src/synth_panel/packs/instruments/pricing-discovery.yaml +80 -0
- synthpanel-0.5.0/src/synth_panel/packs/startup-founder.yaml +63 -0
- synthpanel-0.5.0/src/synth_panel/persistence.py +449 -0
- synthpanel-0.5.0/src/synth_panel/plugins/__init__.py +30 -0
- synthpanel-0.5.0/src/synth_panel/plugins/hooks.py +159 -0
- synthpanel-0.5.0/src/synth_panel/plugins/manager.py +176 -0
- synthpanel-0.5.0/src/synth_panel/plugins/manifest.py +100 -0
- synthpanel-0.5.0/src/synth_panel/plugins/registry.py +66 -0
- synthpanel-0.5.0/src/synth_panel/prompts.py +50 -0
- synthpanel-0.5.0/src/synth_panel/routing.py +84 -0
- synthpanel-0.5.0/src/synth_panel/runtime.py +444 -0
- synthpanel-0.5.0/src/synth_panel/structured/__init__.py +5 -0
- synthpanel-0.5.0/src/synth_panel/structured/output.py +152 -0
- synthpanel-0.5.0/src/synth_panel/synthesis.py +247 -0
- synthpanel-0.5.0/src/synth_panel/templates.py +122 -0
- synthpanel-0.5.0/src/synthpanel.egg-info/PKG-INFO +413 -0
- synthpanel-0.5.0/src/synthpanel.egg-info/SOURCES.txt +84 -0
- synthpanel-0.5.0/src/synthpanel.egg-info/dependency_links.txt +1 -0
- synthpanel-0.5.0/src/synthpanel.egg-info/entry_points.txt +2 -0
- synthpanel-0.5.0/src/synthpanel.egg-info/requires.txt +16 -0
- synthpanel-0.5.0/src/synthpanel.egg-info/top_level.txt +1 -0
- synthpanel-0.5.0/tests/test_acceptance.py +625 -0
- synthpanel-0.5.0/tests/test_aliases.py +17 -0
- synthpanel-0.5.0/tests/test_cli.py +1471 -0
- synthpanel-0.5.0/tests/test_client.py +155 -0
- synthpanel-0.5.0/tests/test_compat_matrix.py +251 -0
- synthpanel-0.5.0/tests/test_conditions.py +289 -0
- synthpanel-0.5.0/tests/test_cost.py +222 -0
- synthpanel-0.5.0/tests/test_errors.py +27 -0
- synthpanel-0.5.0/tests/test_instrument.py +425 -0
- synthpanel-0.5.0/tests/test_mcp_data.py +442 -0
- synthpanel-0.5.0/tests/test_mcp_server.py +460 -0
- synthpanel-0.5.0/tests/test_models.py +67 -0
- synthpanel-0.5.0/tests/test_orchestrator.py +1204 -0
- synthpanel-0.5.0/tests/test_persistence.py +342 -0
- synthpanel-0.5.0/tests/test_plugins.py +464 -0
- synthpanel-0.5.0/tests/test_providers.py +907 -0
- synthpanel-0.5.0/tests/test_repl_wiring.py +246 -0
- synthpanel-0.5.0/tests/test_routing.py +116 -0
- synthpanel-0.5.0/tests/test_runtime.py +516 -0
- synthpanel-0.5.0/tests/test_structured_output.py +178 -0
- synthpanel-0.5.0/tests/test_synthesis.py +336 -0
- synthpanel-0.5.0/tests/test_templates.py +177 -0
synthpanel-0.5.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DataViking Tech
|
|
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,413 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: synthpanel
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Run synthetic focus groups and user research panels using AI personas. CLI tool, Python library, any LLM.
|
|
5
|
+
Author-email: DataViking <openclaw@dataviking.tech>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/DataViking-Tech/synthpanel
|
|
8
|
+
Project-URL: Repository, https://github.com/DataViking-Tech/synthpanel
|
|
9
|
+
Project-URL: Issues, https://github.com/DataViking-Tech/synthpanel/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/DataViking-Tech/synthpanel/releases
|
|
11
|
+
Keywords: llm,research,focus-group,synthetic,personas,user-research,ai
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: httpx>=0.27
|
|
26
|
+
Requires-Dist: pyyaml>=6.0
|
|
27
|
+
Requires-Dist: importlib_resources>=5.0; python_version < "3.9"
|
|
28
|
+
Provides-Extra: mcp
|
|
29
|
+
Requires-Dist: mcp>=1.0; extra == "mcp"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov>=6.0; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
36
|
+
Requires-Dist: pip-audit>=2.7; extra == "dev"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# synthpanel
|
|
40
|
+
|
|
41
|
+
Open-source synthetic focus groups. Any LLM. Your terminal or your agent's tool call.
|
|
42
|
+
|
|
43
|
+
Define personas in YAML. Define your research instrument in YAML. Run against any LLM — from your terminal, from a pipeline, or from an AI agent's MCP tool call. Get structured, reproducible output with full cost transparency.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install synthpanel
|
|
47
|
+
synthpanel panel run --personas personas.yaml --instrument survey.yaml
|
|
48
|
+
|
|
49
|
+
# For MCP server support (Claude Code, Cursor, Windsurf, etc.)
|
|
50
|
+
pip install synthpanel[mcp]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Why
|
|
54
|
+
|
|
55
|
+
Traditional focus groups cost $5,000-$15,000 and take weeks. Synthetic panels cost pennies and take seconds. They don't replace real user research, but they're excellent for:
|
|
56
|
+
|
|
57
|
+
- **Pre-screening** survey instruments before spending budget on real participants
|
|
58
|
+
- **Rapid iteration** on product names, copy, and positioning
|
|
59
|
+
- **Hypothesis generation** across demographic segments
|
|
60
|
+
- **Concept testing** at the speed of thought
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Install from PyPI (v0.4.0+)
|
|
66
|
+
pip install synthpanel
|
|
67
|
+
|
|
68
|
+
# For MCP server support (agent integration)
|
|
69
|
+
pip install synthpanel[mcp]
|
|
70
|
+
|
|
71
|
+
# Or install from source for the latest unreleased changes
|
|
72
|
+
pip install git+https://github.com/DataViking-Tech/synthpanel.git@main
|
|
73
|
+
|
|
74
|
+
# Set your API key (Claude, OpenAI, Gemini, xAI, or any OpenAI-compatible provider)
|
|
75
|
+
export ANTHROPIC_API_KEY="sk-..."
|
|
76
|
+
|
|
77
|
+
# Run a single prompt
|
|
78
|
+
synthpanel prompt "What do you think of the name Traitprint for a career app?"
|
|
79
|
+
|
|
80
|
+
# Run a full panel
|
|
81
|
+
synthpanel panel run \
|
|
82
|
+
--personas examples/personas.yaml \
|
|
83
|
+
--instrument examples/survey.yaml
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## What You Get
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
============================================================
|
|
90
|
+
Persona: Sarah Chen (Product Manager, 34)
|
|
91
|
+
============================================================
|
|
92
|
+
Q: What is the most frustrating part of your workflow?
|
|
93
|
+
A: Version control on documents that aren't in a proper system...
|
|
94
|
+
|
|
95
|
+
Cost: $0.0779
|
|
96
|
+
|
|
97
|
+
============================================================
|
|
98
|
+
Persona: Marcus Johnson (Small Business Owner, 52)
|
|
99
|
+
============================================================
|
|
100
|
+
Q: What is the most frustrating part of your workflow?
|
|
101
|
+
A: I'll send my manager a menu update in an email, she makes
|
|
102
|
+
her changes, sends it back...
|
|
103
|
+
|
|
104
|
+
Cost: $0.0761
|
|
105
|
+
|
|
106
|
+
============================================================
|
|
107
|
+
Total: estimated_cost=$0.2360
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Each persona responds in character with distinct voice, concerns, and perspective. Cost is tracked and printed per-panelist and in aggregate.
|
|
111
|
+
|
|
112
|
+
## Defining Personas
|
|
113
|
+
|
|
114
|
+
```yaml
|
|
115
|
+
# personas.yaml
|
|
116
|
+
personas:
|
|
117
|
+
- name: Sarah Chen
|
|
118
|
+
age: 34
|
|
119
|
+
occupation: Product Manager
|
|
120
|
+
background: >
|
|
121
|
+
Works at a mid-size SaaS company. 8 years in tech,
|
|
122
|
+
previously a software engineer. Manages a team of 5.
|
|
123
|
+
personality_traits:
|
|
124
|
+
- analytical
|
|
125
|
+
- pragmatic
|
|
126
|
+
- detail-oriented
|
|
127
|
+
|
|
128
|
+
- name: Marcus Johnson
|
|
129
|
+
age: 52
|
|
130
|
+
occupation: Small Business Owner
|
|
131
|
+
background: >
|
|
132
|
+
Runs a family-owned restaurant chain with 3 locations.
|
|
133
|
+
Not tech-savvy but recognizes the need for digital tools.
|
|
134
|
+
personality_traits:
|
|
135
|
+
- practical
|
|
136
|
+
- skeptical of technology
|
|
137
|
+
- values personal relationships
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Defining Instruments
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
# survey.yaml
|
|
144
|
+
instrument:
|
|
145
|
+
questions:
|
|
146
|
+
- text: >
|
|
147
|
+
What is the most frustrating part of your current
|
|
148
|
+
workflow when collaborating with others?
|
|
149
|
+
response_schema:
|
|
150
|
+
type: text
|
|
151
|
+
follow_ups:
|
|
152
|
+
- "Can you describe a specific recent example?"
|
|
153
|
+
|
|
154
|
+
- text: >
|
|
155
|
+
If you could fix one thing about how you work with
|
|
156
|
+
technology daily, what would it be?
|
|
157
|
+
response_schema:
|
|
158
|
+
type: text
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Adaptive Research (0.5.0): Branching Instruments
|
|
162
|
+
|
|
163
|
+
A v3 instrument is a small DAG of *rounds*. After each round, a routing
|
|
164
|
+
predicate decides which round runs next based on the synthesizer's themes
|
|
165
|
+
and recommendation. The panel chooses its own probe path — no human in the
|
|
166
|
+
loop, no hand-coded conditional flows.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# The Show HN demo: ~$0.20, one command, the panel decides
|
|
170
|
+
# whether to dig into pain, pricing, or alternatives.
|
|
171
|
+
synthpanel panel run \
|
|
172
|
+
--personas examples/personas.yaml \
|
|
173
|
+
--instrument pricing-discovery
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`pricing-discovery` is one of five bundled v3 packs (`pricing-discovery`,
|
|
177
|
+
`name-test`, `feature-prioritization`, `landing-page-comprehension`,
|
|
178
|
+
`churn-diagnosis`). List them with `synthpanel instruments list`.
|
|
179
|
+
|
|
180
|
+
The output now carries a `path` array recording the routing decisions
|
|
181
|
+
that actually fired:
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
discovery -> probe[themes contains price] -> probe_pricing -> validation
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Render the DAG of any instrument:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
synthpanel instruments graph pricing-discovery --format mermaid
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Predicate Reference
|
|
194
|
+
|
|
195
|
+
`route_when` is a list of clauses evaluated in order. The first matching
|
|
196
|
+
clause wins; an `else` clause is **mandatory** as the last entry.
|
|
197
|
+
|
|
198
|
+
```yaml
|
|
199
|
+
route_when:
|
|
200
|
+
- if: { field: themes, op: contains, value: price }
|
|
201
|
+
goto: probe_pricing
|
|
202
|
+
- if: { field: recommendation, op: matches, value: "(?i)wait|delay" }
|
|
203
|
+
goto: probe_objections
|
|
204
|
+
- else: __end__
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
| Field | Source |
|
|
208
|
+
|-------|--------|
|
|
209
|
+
| `themes` | `SynthesisResult.themes` (list, substring match) |
|
|
210
|
+
| `recommendation` | `SynthesisResult.recommendation` (string) |
|
|
211
|
+
| `disagreements`, `agreements`, `surprises` | `SynthesisResult` (lists) |
|
|
212
|
+
| `summary` | `SynthesisResult.summary` (string) |
|
|
213
|
+
|
|
214
|
+
| Op | Meaning |
|
|
215
|
+
|----|---------|
|
|
216
|
+
| `contains` | Substring match against any list entry or the string |
|
|
217
|
+
| `equals` | Exact string match |
|
|
218
|
+
| `matches` | Python regex match (use `(?i)` for case-insensitive) |
|
|
219
|
+
|
|
220
|
+
The reserved target `__end__` terminates the run; the path so far feeds
|
|
221
|
+
final synthesis.
|
|
222
|
+
|
|
223
|
+
### Theme Matching: The R3 Caveat
|
|
224
|
+
|
|
225
|
+
> **Predicates match against the synthesizer's *exact* theme strings.**
|
|
226
|
+
|
|
227
|
+
`themes contains price` only fires if the synthesizer actually emitted a
|
|
228
|
+
theme containing the substring `price`. LLM synthesizers paraphrase —
|
|
229
|
+
"cost concerns" or "sticker shock" will not match. The bundled packs
|
|
230
|
+
mitigate this with a comment block at the top of the instrument that
|
|
231
|
+
hints at the canonical theme tags the synthesizer should prefer:
|
|
232
|
+
|
|
233
|
+
```yaml
|
|
234
|
+
# Synthesizer guidance: when emitting `themes`, prefer the short
|
|
235
|
+
# canonical tags below so route_when predicates match reliably:
|
|
236
|
+
# - "pain" (workflow pain, frustration, broken status quo)
|
|
237
|
+
# - "price" (cost concerns, perceived value, sticker shock)
|
|
238
|
+
# - "alternative" (existing tools, workarounds, competitors)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
When you author your own v3 packs, **always** add a similar tag-hint
|
|
242
|
+
block. The synthesizer reads it and tends to use the canonical tags;
|
|
243
|
+
your `contains` predicates then route reliably. If you skip this step,
|
|
244
|
+
expect routes to silently fall through to `else` because the
|
|
245
|
+
synthesizer's prose theme labels won't match your predicate values.
|
|
246
|
+
|
|
247
|
+
Prefer short, lowercase, single-token tags (`price`, `pain`, `confusion`)
|
|
248
|
+
over long phrases. `contains` does substring matching, so `price` will
|
|
249
|
+
also match `pricing`, `priced`, etc.
|
|
250
|
+
|
|
251
|
+
### `instruments` Subcommand
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
synthpanel instruments list # bundled + installed packs
|
|
255
|
+
synthpanel instruments show pricing-discovery # full YAML body
|
|
256
|
+
synthpanel instruments install ./my-pack.yaml # add a local pack
|
|
257
|
+
synthpanel instruments graph pricing-discovery # text DAG
|
|
258
|
+
synthpanel instruments graph pricing-discovery \
|
|
259
|
+
--format mermaid # mermaid flowchart
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The unified instrument resolver (used by `panel run --instrument`) accepts
|
|
263
|
+
either a YAML path *or* an installed pack name, so you can iterate on a
|
|
264
|
+
local file and then `install` it once it's stable.
|
|
265
|
+
|
|
266
|
+
## LLM Provider Support
|
|
267
|
+
|
|
268
|
+
synthpanel works with any LLM provider. Set the appropriate environment variable:
|
|
269
|
+
|
|
270
|
+
| Provider | Environment Variable | Model Flag |
|
|
271
|
+
|----------|---------------------|------------|
|
|
272
|
+
| Anthropic (Claude) | `ANTHROPIC_API_KEY` | `--model sonnet` |
|
|
273
|
+
| Google (Gemini) | `GOOGLE_API_KEY` or `GEMINI_API_KEY` | `--model gemini` |
|
|
274
|
+
| OpenAI | `OPENAI_API_KEY` | `--model gpt-4o` |
|
|
275
|
+
| xAI (Grok) | `XAI_API_KEY` | `--model grok` |
|
|
276
|
+
| Any OpenAI-compatible | `OPENAI_API_KEY` + `OPENAI_BASE_URL` | `--model <model-id>` |
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
# Use Claude (default)
|
|
280
|
+
synthpanel panel run --personas p.yaml --instrument s.yaml
|
|
281
|
+
|
|
282
|
+
# Use GPT-4o
|
|
283
|
+
synthpanel panel run --personas p.yaml --instrument s.yaml --model gpt-4o
|
|
284
|
+
|
|
285
|
+
# Use a local model via Ollama
|
|
286
|
+
OPENAI_BASE_URL=http://localhost:11434/v1 \
|
|
287
|
+
synthpanel panel run --personas p.yaml --instrument s.yaml --model llama3
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Architecture
|
|
291
|
+
|
|
292
|
+
synthpanel is a research harness, not an LLM wrapper. It orchestrates the research workflow:
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
personas.yaml ──┐
|
|
296
|
+
├──> Orchestrator ──> Panelist 1 ──> LLM ──> Response
|
|
297
|
+
instrument.yaml ─┘ ├──> Panelist 2 ──> LLM ──> Response
|
|
298
|
+
└──> Panelist N ──> LLM ──> Response
|
|
299
|
+
│
|
|
300
|
+
Aggregated Report <──┘
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Components
|
|
304
|
+
|
|
305
|
+
| Module | Purpose |
|
|
306
|
+
|--------|---------|
|
|
307
|
+
| `llm/` | Provider-agnostic LLM client (Anthropic, Google, OpenAI, xAI) |
|
|
308
|
+
| `runtime.py` | Agent session loop (turns, tool calls, compaction) |
|
|
309
|
+
| `orchestrator.py` | Parallel panelist execution with worker state tracking |
|
|
310
|
+
| `structured/` | Schema-validated responses via tool-use forcing |
|
|
311
|
+
| `cost.py` | Token tracking, model-specific pricing, budget enforcement |
|
|
312
|
+
| `persistence.py` | Session save/load/fork (JSON + JSONL) |
|
|
313
|
+
| `plugins/` | Manifest-based extension system with lifecycle hooks |
|
|
314
|
+
| `mcp/` | MCP server for agent-native invocation (stdio transport) |
|
|
315
|
+
| `cli/` | CLI framework with slash commands, output formatting |
|
|
316
|
+
|
|
317
|
+
### Design Principles
|
|
318
|
+
|
|
319
|
+
- **Minimal dependencies** — Python 3.10+ with `httpx` for HTTP and `pyyaml` for YAML parsing. Optional: `mcp` for the MCP server
|
|
320
|
+
- **Agent-native** — invoke from your terminal or from an AI agent's MCP tool call
|
|
321
|
+
- **Provider agnostic** — swap LLMs without changing research definitions
|
|
322
|
+
- **Cost transparent** — every API call is tracked and priced
|
|
323
|
+
- **Reproducible** — same personas + same instrument = comparable output
|
|
324
|
+
- **Structured by default** — responses conform to declared schemas
|
|
325
|
+
|
|
326
|
+
## MCP Server (Agent Integration)
|
|
327
|
+
|
|
328
|
+
synthpanel includes an MCP server so AI agents can run panels as tool calls:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
synthpanel mcp-serve
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Add to your editor's MCP config (Claude Code, Cursor, Windsurf, etc.):
|
|
335
|
+
|
|
336
|
+
```json
|
|
337
|
+
{
|
|
338
|
+
"mcpServers": {
|
|
339
|
+
"synth_panel": {
|
|
340
|
+
"command": "synthpanel",
|
|
341
|
+
"args": ["mcp-serve"],
|
|
342
|
+
"env": { "ANTHROPIC_API_KEY": "sk-..." }
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Tools exposed (12): `run_prompt`, `run_panel`, `run_quick_poll`, `extend_panel`, `list_persona_packs`, `get_persona_pack`, `save_persona_pack`, `list_instrument_packs`, `get_instrument_pack`, `save_instrument_pack`, `list_panel_results`, `get_panel_result`.
|
|
349
|
+
|
|
350
|
+
`run_panel` accepts an inline `instrument` dict or an `instrument_pack`
|
|
351
|
+
name, so an agent can offload research-design judgment in a single tool
|
|
352
|
+
call. v3 responses include `rounds`, `path`, `terminal_round`, and
|
|
353
|
+
`warnings` alongside the back-compat `results` array.
|
|
354
|
+
|
|
355
|
+
`extend_panel` appends a single ad-hoc round to a saved panel result —
|
|
356
|
+
it is **not** a re-entry into the authored DAG. Use it for follow-up
|
|
357
|
+
probes that the original instrument didn't anticipate.
|
|
358
|
+
|
|
359
|
+
## Output Formats
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
# Human-readable (default)
|
|
363
|
+
synthpanel panel run --personas p.yaml --instrument s.yaml
|
|
364
|
+
|
|
365
|
+
# JSON (pipe to jq, store in database)
|
|
366
|
+
synthpanel panel run --personas p.yaml --instrument s.yaml --output-format json
|
|
367
|
+
|
|
368
|
+
# NDJSON (streaming, one event per line)
|
|
369
|
+
synthpanel panel run --personas p.yaml --instrument s.yaml --output-format ndjson
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Budget Control
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
# Set a dollar budget for the panel
|
|
376
|
+
synthpanel panel run --personas p.yaml --instrument s.yaml --config budget.yaml
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
The cost tracker enforces soft budget limits — the current panelist completes, but no new panelists start if the budget is exceeded.
|
|
380
|
+
|
|
381
|
+
## Methodology Notes
|
|
382
|
+
|
|
383
|
+
Synthetic research is useful for exploration, hypothesis generation, and rapid iteration. It is **not** a replacement for talking to real humans.
|
|
384
|
+
|
|
385
|
+
Known limitations:
|
|
386
|
+
- Synthetic responses tend to cluster around means
|
|
387
|
+
- LLMs exhibit sycophancy (tendency to please)
|
|
388
|
+
- Cultural and demographic representation has blind spots
|
|
389
|
+
- Higher-order correlations between variables are poorly replicated
|
|
390
|
+
|
|
391
|
+
Use synthpanel to pre-screen and iterate, then validate with real participants.
|
|
392
|
+
|
|
393
|
+
## Versions
|
|
394
|
+
|
|
395
|
+
| Version | Highlights |
|
|
396
|
+
|---------|-----------|
|
|
397
|
+
| 0.5.0 | v3 branching instruments, router predicates, 5 bundled instrument packs, `instruments` subcommand (list/show/install/graph), MCP `*_instrument_pack` tools, rounds-shaped panel output, `extend_panel` ad-hoc round tool |
|
|
398
|
+
| 0.4.0 | `--var KEY=VALUE` and `--vars-file` for instrument templates, fail-loud on all-provider errors, default `--model` respects available credentials, `pack show <id>` alias, publish workflow fix |
|
|
399
|
+
| 0.3.0 | Structured output via tool-use forcing, cost tracking, MCP server (stdio), persona-pack persistence |
|
|
400
|
+
|
|
401
|
+
See [CHANGELOG.md](CHANGELOG.md) for detailed release notes.
|
|
402
|
+
|
|
403
|
+
## Contributing
|
|
404
|
+
|
|
405
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and how to submit changes.
|
|
406
|
+
|
|
407
|
+
## MCP Server Documentation
|
|
408
|
+
|
|
409
|
+
For detailed MCP server documentation (all 12 tools, 4 resources, 3 prompt templates), see [docs/mcp.md](docs/mcp.md).
|
|
410
|
+
|
|
411
|
+
## License
|
|
412
|
+
|
|
413
|
+
MIT
|