# RicherText Tutorial: Build Your First Enrichment Pipeline

This tutorial walks you through creating a custom enrichment pipeline from scratch. We'll build a pipeline that analyzes customer feedback to extract sentiment, topics, and actionable insights.

---

## What You'll Build

By the end of this tutorial, you'll have a pipeline that transforms this:

| id | feedback |
|----|----------|
| 1 | "Love the new dashboard! So much easier to find what I need." |
| 2 | "App crashes every time I try to export. Very frustrating." |

Into this:

| id | feedback | sentiment | topics | urgency | action_summary |
|----|----------|-----------|--------|---------|----------------|
| 1 | "Love the new..." | positive | ui; usability | low | No action needed... |
| 2 | "App crashes..." | negative | bugs; export | high | Investigate export crash... |

---

## Step 1: Set Up Your Project

First, create a new directory and initialize RicherText:

```bash
mkdir customer-feedback
cd customer-feedback
richertext init
```

This creates the standard project structure:

```
customer-feedback/
├── .env                    # API key goes here
├── taskflows/              # Pipeline configurations
├── prompts/                # Prompt templates
├── input/                  # Your CSV files
├── output/                 # Enriched results
└── docs/                   # Documentation (README, TUTORIAL, ENRICHMENTS)
```

Add your Gemini API key to `.env`:

```bash
echo 'GOOGLE_API_KEY=your-api-key-here' > .env
```

---

## Step 2: Prepare Your Input Data

Create a CSV file with your data. Save this as `input/feedback.csv`:

```csv
id,feedback,customer_tier
1,"Love the new dashboard! So much easier to find what I need now.",premium
2,"App crashes every time I try to export reports. Very frustrating.",free
3,"The mobile app is okay but could use dark mode.",premium
4,"Been waiting 3 days for support to respond. Unacceptable for a paid plan.",enterprise
5,"Quick question - how do I reset my password?",free
6,"Your API documentation is excellent. Integration took 30 minutes.",enterprise
7,"Price increase without notice is disappointing. Considering alternatives.",premium
8,"Feature request: please add bulk import for contacts.",free
```

---

## Step 3: Design Your Enrichments

Before writing config, plan what you want to extract:

| Enrichment | Type | Purpose |
|------------|------|---------|
| sentiment | classifier | Positive, negative, or neutral |
| topics | labeler | What the feedback is about (multiple) |
| urgency | classifier | How quickly we need to respond |
| action_summary | summarizer | What action to take (if any) |

---

## Step 4: Create Your Prompts File

Create `prompts/feedback_prompts.yaml`:

```yaml
# Sentiment classification prompt
sentiment_prompt: |
  Analyze the sentiment of this customer feedback.

  Customer tier: {customer_tier}
  Feedback: {feedback}

# Topic labeling prompt
topics_prompt: |
  What topics does this customer feedback discuss?
  Select all that apply.

  {feedback}

# Urgency classification prompt
urgency_prompt: |
  How urgently does this feedback need attention?
  Consider: Is there a blocking issue? Is the customer frustrated?
  Is this a paying customer?

  Customer tier: {customer_tier}
  Feedback: {feedback}

# Action summary prompt
action_prompt: |
  Based on this feedback, what action (if any) should we take?
  Be specific and actionable. If no action needed, say so.

  Customer tier: {customer_tier}
  Feedback: {feedback}
```

**Tips for writing prompts:**
- Use `{column_name}` to inject CSV columns
- Include relevant context (like customer_tier) to improve accuracy
- Be specific about what you want

---

## Step 5: Create Your Taskflow Config

Create `taskflows/feedback_tasks.yaml`:

```yaml
provider:
  type: gemini
  model: gemini-2.5-flash-lite

prompts_file: ../prompts/feedback_prompts.yaml

enrichments:
  # 1. Classify sentiment
  - name: sentiment
    type: classifier
    prompt_key: sentiment_prompt
    categories:
      - positive
      - negative
      - neutral
    input_columns: [feedback, customer_tier]
    include_reasoning: false

  # 2. Label topics (can have multiple)
  - name: topics
    type: labeler
    prompt_key: topics_prompt
    labels:
      - bugs
      - feature_request
      - pricing
      - support
      - ui
      - usability
      - documentation
      - performance
    input_columns: [feedback]

  # 3. Classify urgency
  - name: urgency
    type: classifier
    prompt_key: urgency_prompt
    categories:
      - critical    # Blocking issue, needs immediate attention
      - high        # Frustrated customer, respond within hours
      - medium      # Should address soon
      - low         # Nice to know, no action required
    input_columns: [feedback, customer_tier]
    include_reasoning: false

  # 4. Generate action summary
  - name: action
    type: summarizer
    prompt_key: action_prompt
    input_columns: [feedback, customer_tier]
    max_length: 100
```

---

## Step 6: Run Your Pipeline

Run the enrichment:

```bash
richertext run input/feedback.csv --config taskflows/feedback_tasks.yaml -v
```

You'll see output like:

```
Processing 8 records...
Provider: gemini-2.5-flash-lite
Workers: 133
Enrichments: ['sentiment', 'topics', 'urgency', 'action']
Processing 8 records with 133 workers...
...
Completed 8 records in 1.2s (6.67 rec/s, 26.7 API calls/s)

Output saved to: output/feedback_enriched.csv
```

---

## Step 7: Review Your Results

Open `output/feedback_enriched.csv`. You'll see new columns:

| Column | Description |
|--------|-------------|
| sentiment_category | positive, negative, or neutral |
| topics_labels | Semicolon-separated list of topics |
| urgency_category | critical, high, medium, or low |
| action_summary | Recommended action |

---

## Chaining Enrichments

You can use output from earlier enrichments in later ones. Just include the output column in `input_columns`.

### Simple Example: Language Detection → Summary

Create `input/multilingual.csv`:

```csv
id,text
1,"Bonjour! Je suis très content de ce produit. Il fonctionne parfaitement."
2,"Hola, necesito ayuda con mi pedido. No ha llegado todavía."
3,"Great product, fast shipping. Would definitely recommend to friends!"
4,"Das ist nicht was ich bestellt habe. Ich möchte eine Rückerstattung."
```

Create `taskflows/multilingual.yaml`:

```yaml
provider:
  type: gemini
  model: gemini-2.5-flash-lite

enrichments:
  # First: detect language
  - name: language
    type: classifier
    categories: [english, spanish, french, german]
    input_columns: [text]
    prompt: |
      What language is this text written in?

      {text}

  # Second: summarize using detected language
  - name: summary
    type: summarizer
    input_columns: [text, language_category]  # Include previous output!
    prompt: |
      Summarize this {language_category} text in English:

      {text}
```

Run it:

```bash
richertext run input/multilingual.csv --config taskflows/multilingual.yaml -v
```

**Output columns:** `language_category`, `summary_summary`

---

### Advanced Example: Multi-Stage Analysis Pipeline

This example shows a sophisticated pipeline where:
1. **Classifiers with reasoning** analyze sentiment and urgency
2. **Reasoner** synthesizes those analyses into a comprehensive assessment
3. **Scorer** evaluates the quality of the feedback based on everything

Create `taskflows/advanced_feedback.yaml`:

```yaml
provider:
  type: gemini
  model: gemini-2.5-flash-lite

enrichments:
  # Stage 1: Classify with reasoning enabled
  - name: sentiment
    type: classifier
    categories: [positive, negative, neutral, mixed]
    input_columns: [feedback, customer_tier]
    include_reasoning: true  # Captures WHY it chose this sentiment
    prompt: |
      Analyze the emotional tone of this customer feedback.
      Consider word choice, punctuation, and overall message.

      Customer tier: {customer_tier}
      Feedback: {feedback}

  - name: urgency
    type: classifier
    categories: [critical, high, medium, low]
    input_columns: [feedback, customer_tier]
    include_reasoning: true  # Captures urgency factors
    prompt: |
      How urgently does this need attention?
      Consider: blocking issues, customer frustration level, business impact.

      Customer tier: {customer_tier}
      Feedback: {feedback}

  - name: topics
    type: labeler
    labels: [bugs, feature_request, pricing, support, ui, performance, docs]
    input_columns: [feedback]
    include_reasoning: true  # Enable to get reasoning for chaining

  # Stage 2: Reasoner synthesizes all previous analyses
  - name: assessment
    type: reasoner
    input_columns:
      - feedback
      - customer_tier
      - sentiment_category
      - sentiment_category_reasoning    # Feed in the reasoning!
      - urgency_category
      - urgency_category_reasoning      # And this reasoning too!
      - topics_labels
      - topics_labels_reasoning
    prompt: |
      Based on the following analysis of customer feedback, provide a comprehensive
      assessment and recommended action plan.

      ## Original Feedback
      Customer tier: {customer_tier}
      Feedback: {feedback}

      ## Analysis Results
      Sentiment: {sentiment_category}
      Sentiment reasoning: {sentiment_category_reasoning}

      Urgency: {urgency_category}
      Urgency reasoning: {urgency_category_reasoning}

      Topics: {topics_labels}
      Topics reasoning: {topics_labels_reasoning}

      ## Your Task
      Synthesize the above into:
      1. A one-sentence summary of the situation
      2. Root cause (if identifiable)
      3. Recommended action with specific next steps
      4. Who should handle this (support, engineering, product, etc.)

  # Stage 3: Score the feedback on multiple dimensions (one scorer each)
  - name: actionability
    type: scorer
    scale_min: 1
    scale_max: 10
    input_columns:
      - feedback
      - assessment_analysis
    prompt: |
      How actionable is this feedback? Can we take concrete steps?

      Feedback: {feedback}
      Assessment: {assessment_analysis}

  - name: specificity
    type: scorer
    scale_min: 1
    scale_max: 10
    input_columns:
      - feedback
    prompt: |
      How specific is this feedback? (1=vague, 10=very detailed)

      {feedback}

  - name: business_impact
    type: scorer
    scale_min: 1
    scale_max: 10
    input_columns:
      - feedback
      - customer_tier
      - urgency_category
    prompt: |
      What is the potential business impact of addressing this feedback?

      Feedback: {feedback}
      Customer tier: {customer_tier}
      Urgency: {urgency_category}
```

### What This Produces

Each record will have these columns:

| Column | Source | Description |
|--------|--------|-------------|
| sentiment_category | Stage 1 | positive/negative/neutral/mixed |
| sentiment_category_reasoning | Stage 1 | Why this sentiment was chosen |
| urgency_category | Stage 1 | critical/high/medium/low |
| urgency_category_reasoning | Stage 1 | Factors affecting urgency |
| topics_labels | Stage 1 | Semicolon-separated topics |
| topics_labels_reasoning | Stage 1 | Why each topic applies |
| assessment_analysis | Stage 2 | Full synthesized analysis |
| actionability_score | Stage 3 | Score 1-10 |
| specificity_score | Stage 3 | Score 1-10 |
| business_impact_score | Stage 3 | Score 1-10 |

### Why This Pattern Works

1. **Reasoning as data** - The `_reasoning` columns become valuable inputs for downstream tasks
2. **Progressive refinement** - Each stage builds on previous insights
3. **Final scoring is informed** - The scorer sees the full picture, not just raw feedback
4. **Transparent decisions** - You can trace how conclusions were reached

---

## Inline Prompts

For simple enrichments, you can skip the prompts file and write prompts inline:

```yaml
enrichments:
  - name: is_spam
    type: classifier
    categories: [spam, not_spam]
    input_columns: [message]
    include_reasoning: false
    prompt: |
      Is this message spam or not?

      {message}
```

---

## Tips for Better Results

1. **Be specific in prompts** - Tell the LLM exactly what you want
2. **Include context** - More relevant columns = better accuracy
3. **Use appropriate types** - Classifier for one choice, labeler for multiple
4. **Test on small data first** - Use 10 records before running 1000
5. **Check reasoning** - Enable `include_reasoning` to debug unexpected results

---

## Troubleshooting

**"Prompts file not found"**
- Path is relative to the taskflow file location
- Use `../prompts/` if prompts folder is a sibling of taskflows

**Unexpected classifications**
- Enable `include_reasoning: true` to see why
- Make prompts more specific
- Add more context columns

**Rate limit errors**
- Reduce workers: `--workers 20`
- The default is auto-calculated, but you can override

**Resume interrupted runs**
- Just re-run the same command with `--pk-field id`
- Already-processed records will be skipped

---

## Next Steps

- Explore the [Python API](README.md#python-api) for programmatic use
- Check out the job postings example in `taskflows/job_postings_tasks.yaml`
- Build your own enrichments for your specific use case

Happy enriching!
