Should you use entity_extraction or conversation_summarizer to get the caller's name? When does chat_categorizer beat call_llm? This decision guide helps you pick the right Ema workflow tool for every scenario.

The Core Building Blocks

Ema workflows provide several specialized tools. Each is optimized for a specific purpose:

Tool Purpose Output Type
conversation_summarizer Convert messy conversation → clean search query Text
entity_extraction Pull structured fields FROM text JSON blob
chat_categorizer Classify and route Single enum value
knowledge_search Find relevant documents Search results
call_llm General LLM tasks Text (can be JSON)
custom_agent Complex tasks with role/instructions Text + optional JSON fields
JSON Extractor Pull individual values from JSON Individual variables

Key insight: Using the wrong tool works... poorly. Using the right tool is effortless.


Decision Tree: What Tool Do I Need?

flowchart TB
    A{"What are you trying to do?"}:::primary

    A -->|Extract values?| B[entity_extraction]:::accent
    A -->|Route by intent?| C[chat_categorizer]:::secondary
    A -->|Build search query?| D[conversation_summarizer]:::accent
    A -->|Find documents?| E[knowledge_search]:::accent
    A -->|Complex analysis?| F[custom_agent]:::agent
    A -->|Simple transform?| G[call_llm]:::secondary
    A -->|JSON field?| H[JSON Extractor]:::secondary

Scenario 1: "Get the Caller's Name"

Question: Should I use conversation_summarizer, entity_extraction, or call_llm?

❌ conversation_summarizer — WRONG TOOL

Input: "Hi, this is Michael Thompson, I want to check my portfolio"
Output: "Portfolio check request from client"

Problem: Summarizer is for search queries, not extraction.
         It might paraphrase away the exact name!

✅ entity_extraction — RIGHT TOOL

Input: Same conversation
Output: {
  "caller_name": "Michael Thompson",
  "request_type": "portfolio_check"
}

Why: Designed to pull specific fields, preserves exact values.

✅ call_llm with JSON — ALSO WORKS

call_llm:
  instructions: "Extract caller_name from conversation. Output JSON only."

Output: { "caller_name": "Michael Thompson" }

Why: Flexible, same result, more control over prompt.

Verdict

Tool Use for "get name"? Why
conversation_summarizer ❌ No Summarizes, may lose exact name
entity_extraction ✅ Yes Designed for this
call_llm + JSON ✅ Yes Works, more flexible

Scenario 2: "Build a Search Query"

Question: I have a messy conversation, need to search my knowledge base. What do I use?

✅ conversation_summarizer — RIGHT TOOL

Input conversation:
  User: "Hey so like I was wondering about that tech stock"
  Bot: "Which one?"
  User: "The NVIDIA one, what's happening with it"
  Bot: "Let me check"
  User: "Also for Michael's account"

Output: "NVIDIA NVDA stock update Michael client portfolio"

Why: Converts verbose conversation into searchable keywords.

❌ entity_extraction — WRONG TOOL

Output: {
  "ticker": "NVDA",
  "client_name": "Michael"
}

Problem: Gives you fields, but not a search query string.
         You'd need another step to combine them.

Better Pattern: entity_extraction → call_llm query builder

1. entity_extraction → {ticker: "NVDA", client: "Michael"}
2. call_llm → "Build query:   portfolio"
3. Output → "NVDA Michael portfolio holdings exposure"

Why: More precise than summarizer, uses extracted entities.

Verdict

Tool Use for search query? Why
conversation_summarizer ✅ Quick & easy Good for simple cases
entity_extraction alone ❌ No Outputs fields, not query
entity_extraction + call_llm ✅ Best Precise, entity-aware queries

Scenario 3: "Route Based on Request Type"

Question: User might be asking for portfolio review, compliance check, or market update. How do I route?

✅ chat_categorizer — RIGHT TOOL

chat_categorizer:
  categories:
    - PORTFOLIO_REVIEW: "Review my portfolio", "How am I doing"
    - COMPLIANCE_CHECK: "Am I compliant", "Check TMD"
    - MARKET_UPDATE: "What's happening with NVDA", "Market news"

Output: category = "PORTFOLIO_REVIEW"

Why: Designed for classification and routing.
     Works directly with runIf conditions.

❌ entity_extraction — WRONG TOOL

Output: {
  "request_type": "portfolio_review"
}

Problem: Can't use extraction_columns directly in runIf!
         runIf needs a single value, not JSON blob.

Workaround: entity-aware routing

# Step 1: Extract entities
entity_extraction:
  output: extraction_columns
    {needs_email: true, has_recipient: false}

# Step 2: Categorizer evaluates extraction
chat_categorizer:
  custom_data: entity_extraction.extraction_columns
  categories:
    - NEEDS_EMAIL: "needs_email is true AND recipient_email is null"
    - READY: "has all required data"

Why: Categorizer can evaluate the extraction in custom_data.

Verdict

Tool Use for routing? Why
chat_categorizer ✅ Yes Designed for this, works with runIf
entity_extraction ❌ Alone no Output can't drive runIf directly
entity_extraction + categorizer ✅ Yes Categorizer uses extraction for decisions

Scenario 4: "Wire Extracted Email to Send Email Node"

Question: I extracted recipient_email, how do I connect it to the email node's to field?

❌ entity_extraction alone — CAN'T WIRE DIRECTLY

entity_extraction outputs: extraction_columns (blob)
{
  "recipient_email": "m.t@corp.com",
  "subject": "...",
  ...
}

Problem: Can't do extraction_columns.recipient_email in wiring!
         The platform treats it as an opaque blob.

✅ JSON Extractor — RIGHT TOOL

entity_extraction → JSON Extractor
                    Variable: recipient_email
                    Path: $.recipient_email

Output: "m.t@corp.com" (single string)

Now you can wire: JSON_Extractor.recipient_email → Email.to

✅ call_llm as value extractor — ALSO WORKS

call_llm:
  named_inputs:
    - Entities: entity_extraction.extraction_columns
  instructions: |
    Output ONLY the recipient email from Entities.
    Just the email, nothing else.

Output: "m.t@corp.com"

Wire: call_llm.response → Email.to

Verdict

Tool Use for direct wiring? Why
entity_extraction alone ❌ No Can't index into blob
JSON Extractor ✅ Yes Extracts individual values
call_llm ✅ Yes LLM extracts the value

Scenario 5: "Complex Analysis with Specific Expertise"

Question: I need a "Senior Portfolio Analyst" to review holdings and make recommendations.

✅ custom_agent — RIGHT TOOL

custom_agent:
  role_instructions: |
    You are a Senior Portfolio Analyst specializing in...

  task_instructions: |
    Analyze the portfolio data and produce:
    1. Performance summary
    2. Risk assessment
    3. Recommendations

  output_fields:
    - summary (string)
    - risk_score (number)
    - recommendations (array)

Why: Role defines expertise, task defines job, output_fields structure result.

⚠️ call_llm — TOO SIMPLE

call_llm:
  instructions: "Analyze this portfolio..."

Problem: No role definition, no structured output.
  Works but less organized.

Verdict

Tool Use for complex analysis? Why
custom_agent ✅ Yes Role + task + structured output
call_llm ⚠️ Okay Works, but less structured

Quick Reference: Tool Selection

Need to... Use
Extract specific fields (name, email, type) entity_extraction
Convert conversation to search query conversation_summarizer
Route/branch based on intent chat_categorizer
Find documents knowledge_search
Complex analysis with persona custom_agent
Simple text transformation call_llm
Get individual value from JSON JSON Extractor
Combine routing + entity logic categorizer with custom_data from extraction

Common Mistakes

Mistake Why It's Wrong Fix
Using summarizer to extract names May paraphrase away exact values Use entity_extraction
Using extraction for routing Can't use blob in runIf Use categorizer
Expecting extraction to wire to fields Platform treats as blob Use JSON Extractor
Using call_llm for everything Less structured, harder to manage Use appropriate specialized tool

The Golden Pattern

When in doubt, this architecture handles most conversational AI needs:

flowchart TB
    A[trigger.chat_conversation]:::primary --> B["entity_extraction<br/>(structured fields)"]:::accent
    A --> C["conversation_summarizer<br/>(search query)"]:::accent

    C --> D[knowledge_search]:::accent

    B --> E["chat_categorizer<br/>(routing)"]:::secondary
    B --> F["custom_agent<br/>(analysis)"]:::agent
    D --> F

    E --> G[runIf branches]:::secondary
    F --> H((WORKFLOW_OUTPUT)):::accent
    G --> H

This combines the right tools for each job while sharing data effectively:

  • Entity extraction captures structured data
  • Summarizer optimizes for search
  • Categorizer drives routing decisions
  • Agent does the heavy analytical lifting

TL;DR

If you need... Use this
Exact values from conversation entity_extraction
Search-optimized query conversation_summarizer
Intent-based routing chat_categorizer
Complex reasoning custom_agent
Simple transform call_llm
Individual JSON field JSON Extractor

Rule of thumb: Each tool does one thing well. Match the tool to the task.


For more on routing patterns, see Ema Workflows: Routing and Branching.