A technical guide to controlling RAG flow in Ema workflows. Understanding trigger outputs, conversation handling, and query construction is the difference between a chatbot that retrieves relevant context and one that searches with noise. This guide demystifies the core concepts.

The Core Problem

When building conversational AI workflows, you face a fundamental challenge: what text do you send to your knowledge search?

Send the wrong thing, and your RAG retrieval returns irrelevant results. Send the right thing, and your AI Employee becomes remarkably accurate.

This guide covers the primitives you need to master:

Concept Purpose
user_query Current message only
chat_conversation Full conversation history
additional_context External context from your app
conversation_summarizer Smart query extraction
call_llm query builder Custom query construction
entity_extraction Structured data from conversation

Trigger Outputs Explained

Every Ema workflow starts with a trigger node. The trigger provides several outputs that you can wire to downstream agents.

The Three Trigger Outputs

Output Type What It Contains
user_query TEXT_WITH_SOURCES Current message only—just what the user said this turn
chat_conversation CHAT_CONVERSATION Full history—entire back-and-forth with all messages
additional_context TEXT_WITH_SOURCES Custom context passed from API/SDK when invoking the persona

When to Use Each

flowchart TB
    subgraph user_query["user_query"]
        U1["Single-turn Q&A, simple lookups"]:::secondary
        U2["When history doesn't matter"]:::secondary
        U3["'What's the PTO policy?'"]:::secondary
    end

    subgraph chat_conversation["chat_conversation"]
        C1["Multi-turn with references"]:::secondary
        C2["When you need full context for agent reasoning"]:::secondary
        C3["NOT directly for search queries"]:::secondary
    end

    subgraph additional_context["additional_context"]
        A1["App-provided context"]:::accent
        A2["Pre-populated from your integration"]:::accent
        A3["The 'hidden gem' for contextual awareness"]:::accent
    end

How Search Query Works

When you connect something to search.query, that text becomes the semantic search query against your knowledge base.

Good query:

"I need portfolio update for Michael Thompson"

This searches your knowledge base for documents related to portfolios and Michael Thompson. Clean, specific, effective.

Bad query:

User: Hi
Bot: Hello! How can I help?
User: What's Michael's portfolio?
Bot: Let me check...
User: Also add his email

This makes a terrible search query—too much noise, conversation artifacts, and irrelevant tokens diluting the semantic signal.

Key insight: What goes into search.query directly determines retrieval quality. Garbage in, garbage out.


The Problem with Raw Conversation

If you wire trigger.chat_conversation directly to search.query, you're sending the entire conversation history as your search text.

Why this fails:

  1. Noise accumulation — Every "Hi", "Thanks", and "Let me check" pollutes the query
  2. Token dilution — The actual search intent gets lost in conversational filler
  3. Semantic drift — Embeddings get pulled toward conversation artifacts, not the user's intent

Example of what happens:

Conversation History:
├── User: "Hi there"
├── Bot: "Hello! How can I help you today?"
├── User: "I'm looking for info on Michael Thompson"
├── Bot: "Sure, let me look that up."
├── User: "Specifically his portfolio performance"
├── Bot: "I found some information..."
└── User: "What about compliance status?"

Search query (if using raw conversation):
"Hi there Hello! How can I help you today? I'm looking for info on
Michael Thompson Sure, let me look that up. Specifically his portfolio
performance I found some information... What about compliance status?"

This is not what you want to search for.


Solution: conversation_summarizer

The conversation_summarizer agent transforms messy conversation history into clean, search-optimized text.

Input:

User: Hi
Bot: Hello! How can I help?
User: What's Michael's portfolio?
Bot: Let me check...
User: Also add his email

Output:

"Portfolio information and email for client Michael Thompson"

Much better for semantic search.

How It Works

flowchart LR
    A[Trigger]:::primary --> B[chat_conversation]:::secondary
    B --> C[conversation_summarizer]:::accent
    C --> D[summarized_conversation]:::secondary
    D --> E[search.query]:::accent

Basic Configuration

name: "conversation_summarizer"
action: "actions.emainternal.conversation_summarizer"
inputs:
  conversation: ""
outputs:
  - summarized_conversation

additional_context: The Hidden Gem

This is context you pass from outside when calling the persona via API. It's information your app knows that the user didn't explicitly say.

How to Use It

When invoking the persona programmatically:

// When calling the persona from your app
ema.chat({
  persona_id: "58bea418...",
  message: "What's their portfolio?",
  additional_context:
    "Client: Michael Thompson (c_102), Advisor: Sarah Mitchell",
});

Use Cases

Scenario What to Pass
User context Current logged-in user, their role, permissions
Page context What page/screen they're viewing
Entity context Account/client they're looking at
Session context Recent actions, workflow state

Why This Matters

Without additional_context:

User: "What's their portfolio?"
AI: "Whose portfolio would you like to see?"  ← Needs clarification

With additional_context: "Client: Michael Thompson":

User: "What's their portfolio?"
AI: "Here's Michael Thompson's portfolio..."  ← Already knows context

The AI Employee can answer immediately because your app pre-populated the context.

Accessing in Workflows

name: "search"
action: "actions.emainternal.search"
inputs:
  query: " "

Combine additional_context with summarized conversation for maximum relevance.


Better Query Tuning Patterns

Sometimes conversation_summarizer with default settings isn't enough. Here are three patterns for more control.

Pattern 1: conversation_summarizer with Custom Instructions

Instead of generic summarization, customize what it extracts:

name: "conversation_summarizer"
action: "actions.emainternal.conversation_summarizer"
inputs:
  conversation: ""
  user_instructions: |
    Extract:
    1. Client name (if mentioned)
    2. Specific request type (portfolio, compliance, market)
    3. Any specific holdings or topics

    Output as: "{request_type} for {client_name}: {details}"
outputs:
  - summarized_conversation

Result: Instead of generic summaries, you get structured, search-optimized queries.

Pattern 2: call_llm as Query Builder

More control than conversation_summarizer—use call_llm to construct the exact query you need:

name: "query_builder"
action: "actions.emainternal.call_llm"
inputs:
  query: ""
  named_inputs:
    - name: "Intent"
      value: ""
    - name: "History"
      value: ""
  user_instructions: |
    Build a search query for the knowledge base.

    Rules:
    - If intent is CLIENT_REVIEW: focus on portfolio, holdings, performance
    - If intent is COMPLIANCE: focus on TMD, concentration, limits
    - Include client name if mentioned
    - Be specific, avoid generic terms

    Output ONLY the search query, nothing else.
outputs:
  - response # This becomes the search query

When to use: When you need intent-aware query construction with specific formatting rules.

Pattern 3: Entity Extraction → Query Construction

Extract structured entities first, then build the query:

# Step 1: Extract entities
name: "entity_extraction"
action: "actions.emainternal.entity_extraction"
inputs:
  text: ""
  columns:
    - client_name
    - topic
    - time_period
outputs:
  - columns

# Step 2: Build query from entities
name: "query_constructor"
action: "actions.emainternal.call_llm"
inputs:
  named_inputs:
    - name: "Entities"
      value: ""
    - name: "Intent"
      value: ""
  user_instructions: |
    Build query: "{client_name} {topic} {intent_context}"
outputs:
  - response

When to use: When you need maximum control and the conversation contains structured data you can extract.


Handling "The Last Question Was About..." References

Multi-turn conversations often include references to previous topics:

  • "What about that client?"
  • "The same portfolio"
  • "Add to the ticket we discussed"

The Simple Approach: Use Full History

The chat_conversation already contains this context. Pass it to agents that need to reason about history:

name: "custom_agent"
action: "actions.emainternal.custom_agent"
inputs:
  named_inputs:
    - name: "Conversation"
      value: "" # Full history
    - name: "Current"
      value: "" # Just this message
  task_instructions: |
    The user may reference previous topics.
    Use the Conversation history to understand context.
    Current query: 

The Explicit Approach: Extract Context

For more control, explicitly extract what was discussed:

name: "context_extractor"
action: "actions.emainternal.call_llm"
inputs:
  query: ""
  user_instructions: |
    Extract any entities/topics discussed in previous messages.
    Output JSON:
    {
      "previous_client": "...",
      "previous_topic": "...",
      "current_request": "..."
    }
outputs:
  - response

Now you have structured data about conversation history that downstream agents can use precisely.


When to Use What: Decision Matrix

By Scenario

Scenario Best Input Why
Simple Q&A, no history needed user_query Clean, single-turn, no noise
Need to know what was discussed before chat_conversation Full context for reasoning
Semantic search query conversation_summarizer output Optimized for retrieval
You know context from your app additional_context Pre-populated, no clarification needed
Multi-turn with entity references ("that client") chat_conversation → entity extraction Structured extraction from history
Intent-aware search intent_classifier + call_llm query builder Custom query per intent

By Pattern Complexity

flowchart LR
    A["user_query<br/>Simplest"]:::accent --> B[conversation_summarizer]:::secondary
    B --> C[call_llm builder]:::secondary
    C --> D["entity extraction<br/>+ query construction<br/>Most Control"]:::primary

Start simple. Add complexity only when retrieval quality demands it.


Recommended Workflow Patterns

Basic Pattern: Summarizer → Search

For most use cases, this is sufficient:

flowchart TB
    A[Trigger]:::primary --> B[conversation_summarizer]:::accent
    B --> C[knowledge_search]:::accent
    C --> D[respond_with_sources]:::secondary

    B -.->|"query: summarized_conversation"| C

Advanced Pattern: Intent-Aware Query Building

When different intents need different search strategies:

flowchart TB
    A[Trigger]:::primary --> B[intent_classifier]:::secondary
    A --> C[entity_extraction]:::accent
    A --> D[conversation_summarizer]:::accent

    B --> E[call_llm: query_builder]:::accent
    C --> E
    D --> E

    E --> F[knowledge_search]:::primary
    F --> G[respond_with_sources]:::secondary

Why this works:

  1. Intent classifier tells you what kind of search to perform
  2. Entity extraction pulls out structured data (client name, topic)
  3. Conversation summarizer gives you the condensed query
  4. Query builder combines all three into an optimal search query

With additional_context

When your app can provide context:

flowchart TB
    A[Trigger]:::primary -->|"additional_context"| B[query_builder]:::accent
    B -->|"Combines context + query"| C[knowledge_search]:::accent

The AI Employee knows who you're asking about before the user even specifies.


Common Mistakes to Avoid

Mistake 1: Raw Conversation to Search

# ❌ DON'T DO THIS
search:
  query: "" # Full conversation = noisy search
# ✅ DO THIS
search:
  query: ""

Mistake 2: Ignoring additional_context

If you control the integration, you have the power to pre-populate context. Use it!

// ❌ Missing opportunity
ema.chat({
  persona_id: "...",
  message: "What's their status?",
});

// ✅ Rich context
ema.chat({
  persona_id: "...",
  message: "What's their status?",
  additional_context: "Viewing: Client #1234 (Acme Corp), Tab: Compliance",
});

Mistake 3: Over-Engineering Simple Use Cases

If users ask simple, single-turn questions, you don't need entity extraction pipelines:

# ✅ Simple use case = simple solution
search:
  query: ""

Add complexity when retrieval quality suffers, not preemptively.

Mistake 4: Forgetting Token Limits

Long conversations can exceed context windows. Use conversation_summarizer to condense:

conversation_summarizer:
  max_turns: 10 # Limit history depth
  user_instructions: "Focus on the current request, not pleasantries"

Putting It All Together

Scenario: Financial Advisor Chatbot

Requirements:

  • Answer questions about client portfolios
  • Handle references to previous conversation ("that client")
  • App knows which advisor is logged in

Workflow:

flowchart TB
    A["Chat Trigger"]:::primary --> B{"intent_classifier"}:::secondary
    A --> C[entity_extraction]:::accent

    B -->|"CLIENT_REVIEW"| D[query_builder: call_llm]:::accent

    C -->|"client_name, topic"| D

    D -->|"constructed query"| E[knowledge_search]:::primary

    E -->|"Returns docs"| F[respond_with_sources]:::secondary

    F --> G((Output)):::accent

Why this works:

  • additional_context provides advisor context without asking
  • Entity extraction catches "Michael Thompson" even if mentioned turns ago
  • Intent-aware query builder optimizes search for the specific request type
  • The search query is clean, specific, and relevant

TL;DR

Need Solution
Current message only trigger.user_query
Full conversation history trigger.chat_conversation
App-provided context trigger.additional_context
Clean search query conversation_summarizer output
Intent-aware search intent_classifiercall_llm query builder
Reference resolution ("that client") chat_conversation → agent reasoning or entity extraction
Maximum control Entity extraction + custom query construction

Start simple: conversation_summarizerknowledge_search

Add complexity when: Retrieval quality suffers, or you need intent-specific search strategies.

Always consider: Can your app provide additional_context? It's often the easiest win for relevance.


For more on Ema workflows and concepts, see Ema Platform: A Complete Guide to Agentic AI Concepts.