Install any skill in seconds. Free to start, no credit card required.
Get Started Free →LLM-driven hypothesis generation/testing on tabular data. Three methods: HypoGeniC (data-driven), HypoRefine (literature+data), Union. Iterative refinement, Redis caching, multi-hypothesis inference. Manual: hypothesis-generation; ideation: scientific-brainstorming.
.claude/skills/jaechang-hits-hypogenic-hypothesis-generation/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 156% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 15% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 94% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 372% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 100% | 0% |
HypoGeniC automates scientific hypothesis generation and testing using LLMs on tabular datasets. Given labeled data (e.g., deception detection, AI-content identification), it generates testable hypotheses, iteratively refines them against validation performance, and runs inference to classify new samples. It supports three approaches: purely data-driven (HypoGeniC), literature-integrated (HypoRefine), and mechanistic union of both.
hypogenicbashpip install hypogenic # Optional: clone example datasets git clone https://github.com/ChicagoHAI/HypoGeniC-datasets.git ./data git clone https://github.com/ChicagoHAI/Hypothesis-agent-datasets.git ./data_lit
pythonfrom hypogenic import BaseTask import re # Custom label extractor (must match dataset label format) def extract_label(text: str) -> str: match = re.search(r'final answer:\s+(.*)', text, re.IGNORECASE) return match.group(1).strip() if match else text.strip() # 1. Load task from config task = BaseTask( config_path="./data/your_task/config.yaml", extract_label=extract_label ) # 2. Generate hypotheses (data-driven) task.generate_hypotheses( method="hypogenic", num_hypotheses=20, output_path="./output/hypotheses.json" ) # 3. Run inference on test set results = task.inference( hypothesis_bank="./output/hypotheses.json", test_data="./data/your_task/your_task_test.json" ) print(f"Accuracy: {results['accuracy']:.3f}")
Create train/val/test JSON files with text features and labels.
pythonimport json # Dataset: each key maps to a list of equal length dataset = { "headline_1": [ "What Up, Comet? You Just Got *PROBED*", "Scientists Made a Breakthrough in Quantum Computing" ], "headline_2": [ "Scientists Were Holding Their Breath Today. Here's Why.", "New Quantum Computer Achieves Milestone" ], "label": [ "Headline 2 has more clicks than Headline 1", "Headline 1 has more clicks than Headline 2" ] } # All lists must have equal length; labels must match extract_label output for split in ["train", "val", "test"]: with open(f"my_task_{split}.json", "w") as f: json.dump(dataset, f, indent=2) print(f"Created dataset with {len(dataset['label'])} samples")
Write a config.yaml defining dataset paths and prompt templates.
python# config.yaml structure (write as YAML file) config = """ task_name: my_task train_data_path: ./my_task_train.json val_data_path: ./my_task_val.json test_data_path: ./my_task_test.json prompt_templates: observations: | Feature 1: ${text_features_1} Feature 2: ${text_features_2} Observation: ${label} batched_generation: system: "You are a research scientist generating hypotheses." user: "Generate ${num_hypotheses} testable hypotheses from these observations." inference: system: "You are evaluating a hypothesis against data." user: "Hypothesis: ${hypothesis}\\nSample: ${sample_text}\\nFinal answer: ${label}" is_relevant: system: "Check hypothesis relevance." user: "Is this hypothesis relevant? ${hypothesis}" """ with open("config.yaml", "w") as f: f.write(config) print("Configuration written to config.yaml")
Define a custom extract_label function matching your label format.
pythonimport re def extract_label(llm_output: str) -> str: """Parse LLM output to extract predicted label. Must return labels matching the 'label' field values in the dataset. Default: searches for 'final answer: <label>' pattern. """ match = re.search(r'final answer:\s+(.*)', llm_output, re.IGNORECASE) if match: return match.group(1).strip() # Domain-specific fallback if "Final prediction:" in llm_output: return llm_output.split("Final prediction:")[-1].strip() return llm_output.strip() # Test against expected labels assert extract_label("Final answer: Headline 1") == "Headline 1" print("Label extractor validated")
Run data-driven hypothesis generation with iterative refinement.
pythonfrom hypogenic import BaseTask task = BaseTask( config_path="./config.yaml", extract_label=extract_label ) # Generate hypotheses: initializes from data subset, iteratively refines task.generate_hypotheses( method="hypogenic", # Data-driven generation num_hypotheses=20, # Target number of hypotheses output_path="./output/hypotheses.json" ) # CLI equivalent: # hypogenic_generation --config config.yaml --method hypogenic --num_hypotheses 20 print("Hypothesis bank saved to ./output/hypotheses.json")
Test generated hypotheses against the test set.
pythonresults = task.inference( hypothesis_bank="./output/hypotheses.json", test_data="./my_task_test.json" ) print(f"Test accuracy: {results['accuracy']:.3f}") print(f"Predictions: {results['predictions'][:5]}") # CLI equivalent: # hypogenic_inference --config config.yaml --hypotheses output/hypotheses.json
Combine literature insights with data-driven hypotheses.
python# Requires GROBID setup and preprocessed PDFs # bash ./modules/setup_grobid.sh # first time # bash ./modules/run_grobid.sh # start GROBID service # python pdf_preprocess.py --task_name my_task task.generate_hypotheses( method="hyporefine", num_hypotheses=15, literature_path="./literature/my_task/", output_path="./output/" ) # Generates 3 hypothesis banks: # - HypoRefine (integrated literature+data) # - Literature-only hypotheses # - Literature union HypoRefine print("HypoRefine generation complete: 3 hypothesis banks created")
Test multiple hypotheses simultaneously for ensemble classification.
pythonfrom examples.multi_hyp_inference import run_multi_hypothesis_inference results = run_multi_hypothesis_inference( config_path="./config.yaml", hypothesis_bank="./output/hypotheses.json", test_data="./my_task_test.json" ) print(f"Multi-hypothesis accuracy: {results['accuracy']:.3f}")
| Parameter | Default | Range / Options | Effect | |-----------|---------|-----------------|--------| | method | "hypogenic" | "hypogenic", "hyporefine", "union" | Generation strategy | | num_hypotheses | 20 | 5-50 | Number of hypotheses to generate | | batch_size | 5 | 3-10 | Samples per generation batch | | max_iterations | 10 | 1-50 | Refinement iterations | | temperature | 0.7 | 0.0-1.0 | LLM sampling temperature | | confidence_threshold | 0.7 | 0.5-0.95 | Inference confidence cutoff | | num_papers | 10 | 5-30 | Papers for HypoRefine literature extraction | | inference_method | "voting" | "voting", "weighted", "ensemble" | How multiple hypotheses combine predictions |
HypoGeniC expects JSON files with parallel lists:
json{ "text_features_1": ["sample_1_feat1", "sample_2_feat1"], "text_features_2": ["sample_1_feat2", "sample_2_feat2"], "label": ["class_A", "class_B"] }
review_text, post_content, etc.)extract_label() output format exactly<TASK>_train.json, <TASK>_val.json, <TASK>_test.json| Method | Input | Process | Best For | |--------|-------|---------|----------| | HypoGeniC | Data only | Init from subset, iteratively refine on validation | Exploratory research, novel datasets without literature | | HypoRefine | Data + PDFs | Extract literature insights, merge with data patterns, refine both | Extending or validating existing theories | | Union | Literature + HypoGeniC | Mechanistic combination, deduplication | Maximum hypothesis diversity and coverage |
Minimal required config.yaml structure:
yamltask_name: my_task train_data_path: ./my_task_train.json val_data_path: ./my_task_val.json test_data_path: ./my_task_test.json model: name: "gpt-4" # or claude-3, gpt-3.5-turbo api_key_env: "OPENAI_API_KEY" temperature: 0.7 generation: method: "hypogenic" num_hypotheses: 20 batch_size: 5 max_iterations: 10 cache: enabled: true # Redis on localhost:6832 host: "localhost" port: 6832 prompt_templates: observations: | Feature 1: ${text_features_1} Observation: ${label} batched_generation: system: "Generate testable hypotheses." user: "Generate ${num_hypotheses} hypotheses." inference: system: "Evaluate hypothesis against sample." user: "Hypothesis: ${hypothesis}\nSample: ${sample_text}" is_relevant: system: "Check relevance." user: "Is ${hypothesis} relevant?"
When to use: creating a new classification task with domain-specific data.
pythonimport json from hypogenic import BaseTask # 1. Prepare data splits for split_name, data in [("train", train_data), ("val", val_data), ("test", test_data)]: with open(f"my_task_{split_name}.json", "w") as f: json.dump(data, f) # 2. Define domain-specific label extractor def my_extractor(text): if "positive" in text.lower(): return "positive" elif "negative" in text.lower(): return "negative" return text.strip() # 3. Create task and run full pipeline task = BaseTask(config_path="./my_task/config.yaml", extract_label=my_extractor) task.generate_hypotheses(method="hypogenic", num_hypotheses=15, output_path="./output/") results = task.inference(hypothesis_bank="./output/hypotheses.json") print(f"Custom task accuracy: {results['accuracy']:.3f}")
When to use: setting up GROBID for PDF-to-structured-text conversion before HypoRefine.
bash# 1. Setup GROBID (first time only) bash ./modules/setup_grobid.sh # 2. Place PDFs in literature directory mkdir -p literature/my_task/raw/ cp papers/*.pdf literature/my_task/raw/ # 3. Start GROBID and process bash ./modules/run_grobid.sh cd examples && python pdf_preprocess.py --task_name my_task # Output: structured text files in literature/my_task/processed/
When to use: combining literature and data-driven hypotheses for comprehensive coverage.
python# Generate literature hypotheses first (via HypoRefine) task.generate_hypotheses( method="hyporefine", num_hypotheses=15, literature_path="./literature/my_task/", output_path="./output/" ) # Union combines and deduplicates both banks # CLI alternative: # hypogenic_generation --config config.yaml --method union \ # --literature_hypotheses output/lit_hypotheses.json # Compare all three approaches for bank in ["hypogenic", "hyporefine", "union"]: r = task.inference(hypothesis_bank=f"./output/{bank}_hypotheses.json") print(f"{bank}: accuracy={r['accuracy']:.3f}")
hypotheses.json -- hypothesis bank with ranked, testable hypotheses (typically 10-20)| Problem | Cause | Solution | |---------|-------|----------| | ModuleNotFoundError: hypogenic | Package not installed | pip install hypogenic | | Generic/untestable hypotheses | Prompt templates too vague | Add domain-specific context to batched_generation prompt | | Poor inference accuracy | Few training examples or bad label extraction | Increase training data; verify extract_label matches dataset labels | | GROBID PDF processing fails | GROBID service not running | bash ./modules/run_grobid.sh; ensure PDFs are valid papers | | Label extraction mismatches | extract_label output differs from dataset labels | Print both formats and align; test with assert extract_label(sample) == expected | | Redis connection errors | Redis not running or wrong port | Start Redis on port 6832 or set cache.enabled: false | | API rate limit errors | Too many concurrent LLM calls | Reduce batch_size; enable Redis caching to avoid duplicate calls | | Empty hypothesis bank | Config missing required prompt templates | Include all four templates: observations, batched_generation, inference, is_relevant |
This entry is self-contained. The original references/config_template.yaml (151 lines) has been consolidated into the Key Concepts "Configuration Template" subsection, retaining the essential YAML structure, model/cache/generation parameters, and prompt template patterns. Omitted from the template: evaluation metrics block, logging configuration, task-specific feature/label metadata descriptions -- these are standard YAML patterns users can add as needed.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-09 | pass→pass | 17,115 | 8,420 | -51% | 1 | 1 | 0% | 3,180 | 5,795 | +82% | 0 | 0 | — |
case-01 | fail→pass | 12,544 | 13,052 | +4% | 1 | 1 | 0% | 2,441 | 6,238 | +156% | 0 | 0 | — |
case-02 | fail→pass | 29,837 | 13,986 | -53% | 1 | 1 | 0% | 6,151 | 7,077 | +15% | 0 | 0 | — |
case-03 | fail→fail | 41,349 | 12,891 | -69% | 1 | 1 | 0% | 4,854 | 6,910 | +42% | 0 | 0 | — |
case-04 | pass→pass | 8,923 | 7,497 | -16% | 1 | 1 | 0% | 1,780 | 5,820 | +227% | 0 | 0 | — |
case-05 | pass→pass | 16,062 | 12,842 | -20% | 1 | 1 | 0% | 2,810 | 6,516 | +132% | 0 | 0 | — |
case-06 | pass→pass | 11,338 | 9,769 | -14% | 1 | 1 | 0% | 2,316 | 6,190 | +167% | 0 | 0 | — |
case-07 | fail→pass | 13,471 | 5,176 | -62% | 1 | 1 | 0% | 2,606 | 5,051 | +94% | 0 | 0 | — |
case-08 | fail→pass | 21,291 | 5,336 | -75% | 1 | 1 | 0% | 1,096 | 5,170 | +372% | 0 | 0 | — |
case-10 | fail→pass | 17,618 | 8,680 | -51% | 1 | 1 | 0% | 2,962 | 5,911 | +100% | 0 | 0 | — |
case-11 | fail→pass | 4,161 | 1,889 | -55% | 1 | 1 | 0% | 723 | 4,557 | +530% | 0 | 0 | — |
case-12 | fail→pass | 10,508 | 2,813 | -73% | 1 | 1 | 0% | 1,779 | 4,685 | +163% | 0 | 0 | — |
case-13 | fail→pass | 14,124 | 2,437 | -83% | 1 | 1 | 0% | 2,543 | 4,651 | +83% | 0 | 0 | — |
case-14 | pass→pass | 6,440 | 2,319 | -64% | 1 | 1 | 0% | 1,148 | 4,622 | +303% | 0 | 0 | — |
case-15 | fail→pass | 9,872 | 1,795 | -82% | 1 | 1 | 0% | 1,702 | 4,504 | +165% | 0 | 0 | — |
case-16 | fail→pass | 9,907 | 2,039 | -79% | 1 | 1 | 0% | 1,719 | 4,501 | +162% | 0 | 0 | — |
case-17 | fail→pass | 35,195 | 7,199 | -80% | 1 | 1 | 0% | 4,201 | 5,724 | +36% | 0 | 0 | — |
case-18 | fail→fail | 10,793 | 2,010 | -81% | 1 | 1 | 0% | 2,052 | 4,567 | +123% | 0 | 0 | — |
case-19 | fail→pass | 9,585 | 1,690 | -82% | 1 | 1 | 0% | 1,606 | 4,544 | +183% | 0 | 0 | — |
case-20 | fail→pass | 8,349 | 1,958 | -77% | 1 | 1 | 0% | 1,414 | 4,541 | +221% | 0 | 0 | — |
case-21 | pass→pass | 5,915 | 1,563 | -74% | 1 | 1 | 0% | 1,081 | 4,406 | +308% | 0 | 0 | — |
case-22 | pass→pass | 7,570 | 1,743 | -77% | 1 | 1 | 0% | 1,358 | 4,513 | +232% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +59 percentage points is the difference between those two pass rates over the 21 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.