dopple-ai
v0.1.0
Published
Synthetic user research engine. Generate psychometrically grounded personas from your product data, query them, validate them.
Downloads
26
Maintainers
Readme
dopple
Understand your users before you build. Dopple connects to your product data, creates psychometrically grounded synthetic users, and tells you what to build next — all from the terminal.
Every prediction gets smarter. Dopple tracks what it predicted, what actually happened, and uses that history to improve the next prediction. The more you use it, the more accurate it gets.
dopple insights --product "my SaaS" --posthog-key phx_... --pretty
# 5 Insights:
#
# 1. Power users export heavily but you have no export customization
# Confidence: ████████░░ 80% Impact: █████████░ 90%
# → Add CSV column selection and scheduled exports
#
# 2. 28% of users churn within 7 days — they never finish onboarding
# Confidence: ███████░░░ 70% Impact: ████████░░ 80%
# → Simplify onboarding step 3 or add a guided walkthroughUse Cases
"What should I build next?" Connect PostHog → Dopple discovers your user segments → generates targeted questions from your data → cross-references persona responses with real behavior → surfaces insights you didn't know.
"Will users pay for this?" Generate a panel of synthetic users grounded in your Stripe + PostHog data. Ask them about pricing, features, positioning. Every answer cites specific data.
"How will users react to this change?" Run a focus group. Inject a stimulus mid-discussion ("competitor just launched a free tier"). Watch personas debate, change their minds, disagree. Get a summary of themes, agreements, and opinion shifts.
"Does our design work?" Have personas review your Figma designs, landing pages, email copy, or pricing pages. Each persona reacts from their specific context — their usage patterns, their frustrations, their personality.
"Does our product match our positioning?" Calibrate personas against your policy docs, brand guidelines, or real survey results. Structured calibration with real math (MAE, Pearson correlation), not LLM vibes.
"Who are my users, really?" Build a knowledge graph from your data. Ask it natural language questions. "Why are users churning?" — get answers grounded in real behavioral patterns.
Install
bun add dopple-ai
# or
npm install dopple-aiQuick Start
Get insights (the main command)
dopple insights --product "my SaaS" --csv users.csv --pretty
dopple insights --product "my SaaS" --posthog-key phx_... --graph --pretty
dopple insights --product "my SaaS" --posthog-key phx_... --agent # JSON for AI agentsPredictions are automatically traced. When real outcomes arrive, record them — next predictions improve:
# Record what actually happened
dopple record-outcome \
--product "my SaaS" \
--trace-id abc123 \
--actual "22% churned (predicted 28%)" \
--mae 0.06 --direction-correct
# View prediction history and accuracy
dopple traces --product "my SaaS" --prettyAs a library
import { Dopple } from "dopple-ai";
const dopple = new Dopple({
model: "anthropic/claude-sonnet-4-20250514",
adapters: [{ type: "posthog", apiKey: "phx_..." }],
});
// Get actionable insights — automatically calibrated from past predictions
const report = await dopple.runInsights({ product: "my SaaS app" });
console.log(report.insights);
// Generate personas from data, documents, or both
const personas = await dopple.generate({
product: "budget tracking app for freelancers",
count: 5,
save: "budget-panel",
});
// Every response includes citations and reasoning
const response = await personas[0].ask("Would you pay $15/mo for this?");
console.log(response.response); // "Honestly, $15 feels steep..."
console.log(response.reasoning); // "High price sensitivity (neuroticism=0.72)"
console.log(response.citations); // [{ source: "trait-model", detail: "...", weight: 0.8 }]
// Record real outcomes to improve future predictions
await dopple.recordOutcome(
"my SaaS app", traceId, "22% churned", "stripe",
{ mae: 0.06, directionCorrect: true, notes: "Over-estimated by 6pp" }
);Persona generation (three modes)
// From data — clusters real users into segments
const personas = await dopple.generate({
product: "my SaaS", // + PostHog/Stripe adapters connected
});
// From documents — extracts stakeholder types
const personas = await dopple.generate({
product: "Singapore flood risk policy", // + PDF adapter: --doc policy.pdf
});
// Combined — segments from data, enriched with document knowledge
const personas = await dopple.generate({
product: "my SaaS", // + PostHog + --doc product-roadmap.pdf
});Each persona gets a rich narrative backstory, pain points, goals, and relationship to the product — not bullet-point demographics.
Focus groups
const group = dopple.createFocusGroup("pricing", {
topic: "Should we raise prices from $10 to $15/mo?",
product: "project management SaaS",
});
group.addPersonas(personas);
group.setPersonaContext("Sarah", ["You cancelled your subscription last month"]);
group.setPersonaContext("Mike", ["You upgraded to pro 2 weeks ago"]);
const round1 = await group.discuss(llm);
group.inject("A competitor just launched a free tier");
const round2 = await group.discuss(llm);
const summary = await group.summarize(llm);
// { themes, agreements, disagreements, insights, opinionShifts }Design and messaging review
import { reviewContent } from "dopple-ai";
// Personas review with full context — their usage, frustrations, personality
const review = await reviewContent(llm, personas, {
type: "pricing_page",
content: "https://myapp.com/pricing",
description: "New pricing page with Pro tier at $15/mo",
questions: ["Is the pricing clear?", "Would you upgrade?"],
});
console.log(review.consensus.wouldActRate); // 0.4 — only 40% would upgrade
console.log(review.consensus.weaknesses); // ["Pro tier value not clear"]
// Figma design review
import { reviewFigmaDesign } from "dopple-ai";
const review = await reviewFigmaDesign(llm, personas, {
accessToken: "fig_...",
fileKey: "abc123",
nodeId: "42:0",
});Surveys
const survey = dopple.createSurvey("pricing-research")
.freeText("pain", "What's the hardest part about managing expenses?")
.multipleChoice("switch", "Would you switch to a cheaper alternative?", [
"Yes, immediately", "Maybe, if same features", "No, I'm loyal",
])
.likert5("satisfaction", "How satisfied are you with your current tool?")
.numerical("budget", "Max monthly price?", 0, 100);
const results = await dopple.runSurvey(survey, personas);Calibration
// Qualitative — against policy docs, brand guidelines
const report = await dopple.calibrate(personas, [
{ type: "policy", name: "Return Policy", content: policyText },
]);
// Structured — real math against real survey data
const report = dopple.calibrateStructured(realSurveyData, syntheticResults);
console.log(report.overallMAE); // 0.073 (7.3pp average error)
console.log(report.correlation); // 0.847Knowledge graph
const graph = await dopple.buildGraph();
const answer = await graph.ask(llm, "Why are users churning?");
// { answer: "...", evidence: [...], confidence: 0.82 }CLI
18 commands. JSON output by default (for AI agents). --pretty for humans. --agent for strict envelope format.
# Insights (the main command — predictions auto-traced)
dopple insights --product "my SaaS" --posthog-key phx_... --pretty
# Personas (from data, documents, or both)
dopple generate --product "budget app" --count 5 --save my-panel --pretty
dopple generate --product "flood risk" --doc policy.pdf --pretty
dopple generate --product "my SaaS" --posthog-key phx_... --doc roadmap.pdf --pretty
dopple ask "Would you pay for dark mode?" --persona my-panel --pretty
# Research
dopple survey --product "my SaaS" --persona my-panel --pretty
dopple focus-group --topic "pricing" --product "my SaaS" --rounds 3 --pretty
dopple review "https://myapp.com" --type landing_page --persona my-panel --pretty
dopple review "Ship faster with AI" --type messaging --pretty
# Calibration
dopple calibrate --source policy.txt --source-type policy --persona my-panel --pretty
dopple calibrate-data --real survey.json --synthetic results.json --pretty
# Data & graph
dopple discover --posthog-key phx_... --product "my SaaS" --pretty
dopple graph --posthog-key phx_... --pretty
dopple query "Why are users churning?" --csv users.csv --pretty
# Prediction tracking (the compounding loop)
dopple traces --product "my SaaS" --pretty
dopple record-outcome --product "my SaaS" --trace-id abc123 --actual "22% churned"
# Management
dopple validate --persona panel.jsonl --pretty
dopple status --posthog-key phx_... --pretty
dopple panels
dopple providersHow It Works
Prediction trace accumulation
Every prediction Dopple makes is traced per product. When real outcomes arrive, they close the loop:
Week 1: dopple insights → "28% will churn" (trace saved)
Week 2: dopple record-outcome → "22% actually churned" (accuracy: 6pp error)
Week 3: dopple insights → calibration history injected → better prediction
Week 8: dopple insights → "For this product, Dopple's predictions have
r=0.84 correlation with real outcomes across 12 verified predictions"The LLM doesn't change. The world model expands. Each verified prediction makes the next one more accurate.
Persona grounding
Every persona is a mathematical object, not a prompt template. Built on the Big Five personality model (OCEAN) — the most validated framework in personality psychology.
Openness: 0.72 → tries new products early, values innovation
Conscientiousness: 0.45 → moderate planning, some impulse buying
Extraversion: 0.31 → decides independently, researches quietly
Agreeableness: 0.68 → trusts recommendations, loyal, avoids conflict
Neuroticism: 0.55 → somewhat price-sensitive, reads negative reviewsTrait vectors compile into deterministic behavioral rules. Same traits = same behavior, every time.
Three generation modes
| Input | Mode | Confidence | |-------|------|-----------| | PostHog, Stripe, CSV | Data — clusters real users → representative persona per segment | Medium-High | | PDF, Markdown, TXT | Documents — extracts stakeholder types → individual persona per stakeholder | Medium | | Both | Combined — segments from data, enriched with document knowledge | High |
Auto-selected based on what you connect. Each persona gets a rich narrative backstory, pain points, goals, and relationship to the product.
Sourced responses
Every response traces back to data:
{
"persona": "Sarah",
"response": "I'd probably cancel at $15.",
"confidence": "medium",
"reasoning": "High neuroticism drives price anxiety. Payment data shows 34% churn at current price.",
"citations": [
{ "source": "trait-model", "detail": "neuroticism 0.72 → price sensitivity", "weight": 0.6 },
{ "source": "payment-data", "detail": "34% churned after last price increase", "weight": 0.9 }
]
}Knowledge graph
Builds an in-memory graph from your data — users, events, features, complaints, payments, and their relationships. Query it with natural language or use it to ground personas in specific data neighborhoods.
Calibration (two modes)
Qualitative — calibrate against policy docs, brand guidelines, benchmarks. LLM evaluates alignment.
Structured — calibrate against real survey data. Pure math: Mean Absolute Error per question, Pearson correlation across distributions. No LLM judgment. Publishable.
Memory
Three layers that persist across sessions:
- Facts — immutable ground truths from your data. Can't be contradicted.
- Episodes — what the persona said before. Prevents flip-flopping.
- Stances — distilled positions ("AGAINST price increases"). Compact, always in prompt.
Data Sources
Adapters (read-only — Dopple never writes to your services)
| Adapter | What it provides | |---------|-----------------| | PostHog | User properties, events, behavioral patterns | | Stripe | Subscriptions, churn, MRR, cancel reasons | | Amplitude | User journeys, event export | | Mixpanel | User profiles, event analytics | | HubSpot | CRM contacts, deals, lifecycle stage | | Intercom | Contacts, conversation history, tags | | CSV / JSON | Any structured data from files | | Documents | PDF, Markdown, TXT, HTML files | | Context | Freeform text, URLs |
Integrations (output channels)
| Integration | What it does | |-------------|-------------| | Figma | Personas review your Figma designs — layout, messaging, usability, trust | | Slack | Post survey results, panel responses, insights to Slack channels | | Review | Personas review any content — landing pages, emails, ads, pricing pages |
Roadmap
| Layer | Adapter | What it adds | |-------|---------|-------------| | SAY | Formbricks | Survey responses, NPS, in-app feedback | | SAY | Chatwoot | Support conversations, tickets | | DO | Shopify | Orders, products, customer segments | | DO | RudderStack | Unified customer profiles | | HOW | OpenReplay | Session replays, heatmaps, rage clicks | | HOW | rrweb | Raw DOM recordings, scroll patterns |
The four layers of user understanding:
- SAY — what users tell you (surveys, support, reviews)
- DO — what users actually did (events, payments, signups)
- HOW — how users interacted (session replays, hesitation, scroll depth)
- WHY — why users behave that way (personality, motivation, beliefs) — Dopple
Custom adapters
Write your own in ~20 lines:
import { registerAdapter, type Adapter, type UserRecord } from "dopple-ai";
class MyAdapter implements Adapter {
readonly type = "my-source";
async fetch(): Promise<UserRecord[]> {
return [{ id: "user-1", properties: { plan: "pro" } }];
}
}
registerAdapter("my-source", (config) => new MyAdapter(config));LLM Providers
One string to switch models:
new Dopple({ model: "anthropic/claude-sonnet-4-20250514" }) // default
new Dopple({ model: "openai/gpt-4o" })
new Dopple({ model: "ollama/llama3" }) // local, free
new Dopple({ model: "groq/llama-3.3-70b-versatile" }) // fast
new Dopple({ model: "openrouter/anthropic/claude-sonnet-4-20250514" })8 providers: Anthropic, OpenAI, Google, Groq, Together, Fireworks, Ollama, OpenRouter.
Agent Integration
Dopple is designed to be called by AI agents. Every command returns structured JSON by default.
# Claude Code / Codex / any agent can run:
dopple insights --product "my SaaS" --csv users.csv --agent
# Returns single-line JSON envelope:
# {"ok":true,"command":"insights","data":{...}}Self-documenting: dopple --help --agent returns a JSON schema of all commands.
License
MIT
