DATA — Public reference datasets for methodology:
- data/README.md: schema + format definitions for brand catalogs
- data/swiece-sojowe-pl/brand_catalog.json: 35 tracked brands (33 manufacturers + 2 importers) + 5 excluded marketplaces/resellers
- data/swiece-sojowe-pl/brand_catalog.md: human-readable companion
- data/swiece-sojowe-pl/market_metadata.json: GMV estimate, personas, seasonality, expected dynamics
TOOLS — 6-stage prompt curation pipeline (Python 3.12+):
- tools/prompt_curation/README.md: process documentation + cost estimates
- tools/prompt_curation/config.py: tunable parameters per stage
- tools/prompt_curation/.env.example: required API keys template
- tools/prompt_curation/requirements.txt: dependencies
- tools/prompt_curation/1_persona_generator.py: Claude generates 7 buyer personas
- tools/prompt_curation/2_prompt_brainstormer.py: per persona × 30 prompts in voice
- tools/prompt_curation/3_reality_checker.py: Google Trends + Reddit cross-check
- tools/prompt_curation/4_validation_agents.py: 3 critic agents async (real_buyer/methodology/exploit_hunter)
- tools/prompt_curation/5_pilot_test_runner.py: sample × 3 LLM models pre-flight
- tools/prompt_curation/6_human_review_export.py: CSV export for founder approval
- tools/prompt_curation/7_finalize.py: post-approval → closed prompts/{cat}/v{N}.json
- tools/prompt_curation/pipeline.py: orchestrator (stages 1–6, then human review, then 7)
GITIGNORE — Fixed .env.* exclusion to allow .env.example.
This commit completes Faza 1. Stages outputs (data/{cat}/personas.json,
raw_prompts.json, validated_prompts.json, critic_review.json, pilot_test_results.json,
for_human_review.csv) are runtime artifacts — public when committed, derived from
public methodology + public brand catalog. Final approved prompt strings in
prompts/{cat}/v{N}.json remain CLOSED (gitignored, anti-Goodhart's Law).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
86 lines
2.8 KiB
Python
86 lines
2.8 KiB
Python
"""Pipeline configuration — tunable per category and stage."""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Literal
|
|
|
|
PromptType = Literal["buying", "comparison", "specific_need", "informational", "brand_direct"]
|
|
|
|
|
|
@dataclass
|
|
class PipelineConfig:
|
|
"""Default config — override via command-line args or per-category YAML."""
|
|
|
|
# Stage 1 — Persona Generator
|
|
num_personas: int = 7
|
|
persona_model: str = "claude-sonnet-4-6"
|
|
|
|
# Stage 2 — Prompt Brainstormer
|
|
prompts_per_persona: int = 30
|
|
brainstormer_model: str = "claude-sonnet-4-6"
|
|
type_distribution: dict[PromptType, float] = field(
|
|
default_factory=lambda: {
|
|
"buying": 0.30,
|
|
"comparison": 0.25,
|
|
"specific_need": 0.20,
|
|
"informational": 0.15,
|
|
"brand_direct": 0.10,
|
|
}
|
|
)
|
|
|
|
# Stage 3 — Reality Checker
|
|
google_trends_min_volume: int = 1 # PL queries per month, minimum signal
|
|
reddit_min_organic_mentions: int = 3
|
|
fallback_to_quora_if_no_signal: bool = True
|
|
|
|
# Stage 4 — Validation Agents
|
|
flagged_by_n_critics_to_remove: int = 2 # Remove if 2+ agents flag it
|
|
critic_models: dict[str, str] = field(
|
|
default_factory=lambda: {
|
|
"real_buyer_critique": "claude-sonnet-4-6",
|
|
"methodology_critic": "claude-sonnet-4-6",
|
|
"vendor_exploit_hunter": "claude-sonnet-4-6",
|
|
}
|
|
)
|
|
|
|
# Stage 5 — Pilot Test Runner
|
|
pilot_sample_size: int = 10
|
|
pilot_models: list[str] = field(
|
|
default_factory=lambda: [
|
|
"gpt-4o-search",
|
|
"perplexity-sonar-pro",
|
|
"gemini-pro",
|
|
]
|
|
)
|
|
repetitions_per_prompt: int = 1 # In pilot test only, production uses 2+
|
|
|
|
# Final pool size after all filtering
|
|
final_pool_size: int = 100
|
|
|
|
# Output paths
|
|
data_dir: str = "data" # Public stage outputs
|
|
prompts_dir: str = "../../prompts" # Closed final prompts (gitignored)
|
|
|
|
|
|
CONFIG = PipelineConfig()
|
|
|
|
|
|
# Type distribution as integer counts for final pool
|
|
def get_target_counts(config: PipelineConfig = CONFIG) -> dict[PromptType, int]:
|
|
"""Return integer counts per prompt type for final pool of `final_pool_size`."""
|
|
counts = {
|
|
ptype: int(round(config.final_pool_size * pct))
|
|
for ptype, pct in config.type_distribution.items()
|
|
}
|
|
# Adjust rounding to ensure sum == final_pool_size
|
|
total = sum(counts.values())
|
|
if total != config.final_pool_size:
|
|
# Adjust the largest category to absorb difference
|
|
largest_type = max(counts, key=lambda k: counts[k])
|
|
counts[largest_type] += config.final_pool_size - total
|
|
return counts
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print(f"Final pool size: {CONFIG.final_pool_size}")
|
|
print(f"Target counts per type: {get_target_counts()}")
|