Skip to main content

Overview

The Agent Composer YAML lets you define executable agent workflows as graphs. Each workflow describes:
  • What inputs it accepts,
  • How data flows between steps (nodes),
  • What outputs it produces,
  • What is surfaced to the UI
A workflow is compiled into an ExecutableGraph and run via the /query/acl API.
The following resources can also aid you in building Agent Composer workflows via YAML:

A Minimal Workflow Example

The following is the smallest useful Agent Composer YAML. You can copy/paste this and run it immediately.
inputs:
  query: str

outputs:
  response: str

nodes:
  generate:
    type: ResponseGenerationStep
    input_mapping:
      query: __inputs__#query

  __outputs__:
    type: output
    input_mapping:
      response: generate#response
Here’s what it does:
  • Accepts a single input: query
  • Runs a generation step
  • Returns a single output: response
  • Automatically displays response in the UI
To run it, use the following Python script:
from sunrise.services.query.acl.graph import ExecutableGraph

graph = ExecutableGraph.from_yaml(yaml_content)
result = graph.execute_graph({"query": "Hello"})
The script should return the following value:
{
  "response": "..."
}

An Extended Workflow Example

The following workflow defines a retrieval-augmented question-answering pipeline that takes a user query, builds conversational context, performs structured agentic research over a vectorized document datastore, and generates a grounded response strictly based on retrieved evidence.
version: 0.1
inputs:
  query: str

outputs:
  response: str

nodes:
  create_message_history:
    type: CreateMessageHistoryStep
    input_mapping:
      query: __inputs__#query

  research:
    type: AgenticResearchStep
    ui_stream_types:
      retrievals: true
    config:
      tools_config:
        - name: search_docs
          description: |
            Search the datastore containing user-uploaded documents. This datastore is a vector database of text chunks which uses hybrid semantic and lexical search to find the most relevant chunks.
            Use this tool to find information within the uploaded documents.
          step_config:
            type: SearchUnstructuredDataStep
            config:
              top_k: 50 # Retrieve 50 chunks from vector search
              lexical_alpha: 0.1          # Weight for lexical (BM25) search
              semantic_alpha: 0.9         # Weight for semantic (vector) search
              reranker: "ctxl-rerank-v2-instruct-multilingual-FP8"
              rerank_top_k: 12
              reranker_score_filter_threshold: 0.2

      agent_config:
        agent_loop:
          num_turns: 10
          parallel_tool_calls: false
          model_name_or_path: "vertex_ai/claude-opus-4-5@20251101"
          identity_guidelines_prompt: |
            You are a retrieval-augmented assistant created by Contextual AI. You provide factual, grounded answers to user's questions by retrieving information via tools and then synthesizing a response based only on what you retrieved.

          research_guidelines_prompt: |
            You have access to the following tool:
            - `search_docs` — Search the document datastore. Returns SEARCH_RESULTS with CITE_ID for citation.
            
            You have access to the following data source:
            1. Document Datastore (Unstructured): 
                - Contains user-uploaded documents that have been parsed, extracted, and chunked for efficient retrieval.
                - Use the `search_docs` tool to query this datastore for relevant content, details, and information from the available documents.
            
            ## Research Strategy
            You MUST always explore unstructured datastore before answering. Do not skip the source.
            - Breadth, then depth strategy:
                1. INITIAL RETRIEVAL - FIRST (Mandatory):
                  - Documents: Use `search_docs` with your initial search terms.
                2. ANALYZE & PLAN: Review initial results and create a query plan:
                  - What information is still missing to fully answer the question?
                  - Create a specific plan: which queries to run next and in what order
                  - Identify dependencies: what do you need to find first before searching for related info?
                3. DEEP DIVE (Execute & Adapt): Execute your plan and adapt based on retrieved content:
                  - Run planned queries systematically
                  - Continue until you have a COMPLETE answer - don't stop early
            - EFFICIENCY: You have 10 turns. Be strategic:
                - Avoid redundant searches; prefer high-quality retrievals.
                - Batch related searches when possible
                - Don't repeat similar queries
                - Prioritize high-value retrievals first
                - But DO NOT sacrifice comprehensiveness for speed - gather ALL relevant information

    input_mapping:
      message_history: create_message_history#message_history

  generate:
    type: GenerateFromResearchStep
    ui_stream_types:
      generation: true
    config:
      model_name_or_path: "vertex_ai/claude-opus-4-5@20251101"

      identity_guidelines_prompt: |
        You are a retrieval-augmented assistant created by Contextual AI. Your purpose is to provide factual, grounded answers by retrieving information via tools and then synthesizing a response based only on what you retrieved. Always start immediately with the answer, don't begin with a preamble or thoughts. 

      response_guidelines_prompt: |
        ## Output
        - Write a concise, direct, well-structured answer in **Markdown** (use short headings, bullets, and brief paragraphs).
        - **START IMMEDIATELY WITH THE ANSWER.** Never begin with preamble like:
          - "Perfect!", "Great!", "Based on my research...", "I now have comprehensive information..."
          - "Let me provide...", "I can now provide...", "Here's what I found..."
          - Any meta-commentary about your search process or confidence level
        - Your first words should be the actual content (a heading, the direct answer, or the key fact).
        - If the required fact is missing from the latest **SEARCH_RESULTS**:
          - Partial or Related Information: Provide whatever relevant details you found, while clearly stating the limitations or gaps.
          - No Relevant Information: If nothing related was found, reply with: "I don't have specific information about [topic] in the available documents."
          - Maintain Engagement: Suggest related topics or alternative ways you can assist to keep the interaction productive.
        - DO NOT engage in character play. You must maintain a friendly, professional, and neutral tone at all times.

    input_mapping:
      message_history: create_message_history#message_history
      research: research#research

  __outputs__:
    type: output
    input_mapping:
      response: generate#response
This workflow enforces a disciplined research strategy—mandatory document retrieval, iterative planning, and deep dives—using a hybrid semantic-lexical search with reranking for precision, followed by a constrained generation step that produces concise, well-structured Markdown answers without meta commentary or speculation.

YAML Schema (Root-level Fields)

Every Agent Composer YAML file defines a graph with the following root fields:
FieldRequiredDescription
inputsGraph inputs and their Python types
outputsGraph outputs (must be JSON-serializable)
nodesNodes and their connections
ui_output⚠️Required only if there are multiple outputs

Important Constraints

  • The root graph currently accepts only one input: query: str (unless the graph is used as a subgraph).
  • Graph outputs must be JSON-serializable, as they are returned by the API.
  • If more than one output exists, exactly one must be selected as ui_output.
Example:
ui_output: response

Wiring & Mapping Syntax

Nodes are connected using output references of the form:
node_name#output_key

Special Nodes

  • __inputs__ Represents graph inputs defined under inputs.
  • __outputs__ Explicitly maps node outputs to graph outputs.

Example Wiring

input_mapping:
  query: __inputs__#query
  processed_query: preprocess#output
Conceptually: inputs.query → preprocess.output → finalize.response → outputs.response

Nodes

A node represents a single operation in the graph with the following standard structure:
example_node:
  type: SomeStepType
  config:
    some_param: 0.5
  input_mapping:
    input_name: upstream_node#output

Node Fields

  • type (required): The step type (must be registered in the system).
  • input_mapping (required if inputs exist): Wires node inputs to outputs from other nodes.
  • config (optional): Static configuration passed at graph construction time.
  • config_overrides (optional): Dynamic configuration resolved at execution time.
  • ui_stream_types (optional): Controls what this node streams to the UI.
All declared inputs of a node must be connected, or graph validation will fail.

Dynamic Configuration Overrides

config_overrides allow node configuration to be set dynamically at runtime.
search:
  type: SearchUnstructuredDataStep
  config:
    filter_retrievals: true
  config_overrides:
    top_k: __inputs__#top_k
  input_mapping:
    query: __inputs__#query
The following rules apply to dynamic configuration overrides:
  • Overrides are merged with static config
  • Overrides can come from any node output
  • Nodes whose type has OUTPUTS = None cannot use config overrides

Output Node

Every graph must explicitly define how node outputs map to graph outputs.
__outputs__:
  type: output
  input_mapping:
    response: generate#response
The output node:
  • Has no config
  • Has no outputs of its own
  • Only maps values into graph outputs

UI Streaming

Some nodes can stream events to the UI (retrievals, generation, etc.).
generate:
  type: ResponseGenerationStep
  ui_stream_types:
    generation: true
Bear in mind the following in regards to UI streaming:
  • Each step type declares supported stream types and defaults
  • Multiple nodes may stream the same type
  • Streaming is independent of the final ui_output

Subgraphs

Subgraphs are reusable workflows that can be embedded as nodes in other graphs. You can think of a subgraph like a function:
  • inputs = parameters
  • outputs = return values
  • nodes = function body

Defining a Subgraph

document_retrieval:
  inputs:
    query: str
  outputs:
    documents: List[str]
  nodes:
    search:
      type: SearchStep
      input_mapping:
        query: __inputs__#query

    __outputs__:
      type: output
      input_mapping:
        documents: search#documents

Using a Subgraph

retriever:
  type: subgraph:document_retrieval
  input_mapping:
    query: __inputs__#query
Bear in mind the following regarding subgraphs:
  • Subgraphs can be nested
  • Subgraphs can be reused multiple times
  • Subgraphs have isolated input/output scopes

Conditional Execution

Conditional nodes execute exactly one branch based on a condition.
conditional:
  type: ConditionalStep
  config:
    inputs:
      score: float
    outputs:
      result: str
    variable: score
    branches:
      - condition: ">= 0.8"
        executable: HighQualityProcessor
        output_mapping:
          __outputs__#result: processed
      - condition: "< 0.8"
        executable: LowQualityProcessor
        output_mapping:
          __outputs__#result: processed
  input_mapping:
    score: evaluator#score
Bear in mind the following rules regarding conditional execution:
  • Conditions are evaluated top-to-bottom
  • First matching branch is executed
  • __inputs__ inside a branch refers to conditional inputs, not graph inputs

Agentic Research & Tool Usage

Agentic behavior in Agent Composer is implemented using explicit agentic steps that separate research (tool use and planning) from final response generation. This pattern makes agent workflows easier to reason about, debug, and control, while ensuring responses remain grounded in retrieved information. A typical agentic workflow consists of three phases:
  1. CreateMessageHistoryStep – Initialize structured conversation state
  2. AgenticResearchStep – Perform multi-turn research using tools
  3. GenerateFromResearchStep – Generate a final response grounded in research results

Initializing Message History

The following step converts the raw user input into a structured message_history object. This standardized message format is required by agentic steps and ensures consistent conversational context throughout the workflow.
create_message_history:
  type: CreateMessageHistoryStep
  input_mapping:
    query: __inputs__#query
This step:
  • Transforms the user’s query into a structured message format
  • Establishes a consistent conversation state
  • Produces message_history, which is reused by both research and generation steps

Agentic Research Step

The following step runs an internal agent loop that plans, executes, and adapts retrieval strategies over multiple turns. It is responsible for all tool usage and information gathering.
research:
  type: AgenticResearchStep
  ui_stream_types:
    retrievals: true
This step:
  • Controls the agent’s reasoning and decision-making process
  • Determines when and how tools are invoked
  • Iteratively retrieves information until sufficient coverage is achieved
  • Produces a structured research output used downstream
Enabling retrievals streaming allows retrieved documents and search activity to be surfaced in the UI.

Defining Tools for the Agent

The following configuration defines tools that the agent may use during research. Each tool wraps a standard step or subgraph and is exposed to the agent as a callable capability.
tools_config:
  - name: search_docs
    description: Search the datastore containing user-uploaded documents.
    step_config:
      type: SearchUnstructuredDataStep
      config:
        top_k: 50
        lexical_alpha: 0.1
        semantic_alpha: 0.9
        reranker: ctxl-rerank-v2-instruct-multilingual-FP8
        rerank_top_k: 12
        reranker_score_filter_threshold: 0.2
Bear in mind the following key points:
  • Tools are defined inline using either step_config or graph_config
  • Each tool encapsulates a retrieval or processing capability
  • Tool inputs must use basic Python types
  • Tool outputs are returned to the agent as structured retrieval results
  • Exactly one of the following must be provided for each tool: step_config graph_config

Configuring the Agent Loop

The following configuration controls how the agent reasons and how many turns it may take when performing research.
agent_config:
  agent_loop:
    num_turns: 10
    parallel_tool_calls: false
    model_name_or_path: vertex_ai/claude-opus-4-5@20251101
This configuration determines:
  • The maximum number of reasoning and tool-use iterations
  • Whether the agent can invoke multiple tools in parallel
  • The model used for agent planning and decision-making

Guiding Agent Behavior with Prompts

The following prompts define who the agent is and how it should conduct research. These prompts guide internal reasoning and are not exposed to the user. Identity Guidelines
The identity guidelines describe the agent’s role and grounding expectations, ensuring it behaves as a factual, retrieval-augmented assistant.
identity_guidelines_prompt: |
            You are a retrieval-augmented assistant created by Contextual AI. You provide factual, grounded answers to user's questions by retrieving information via tools and then synthesizing a response based only on what you retrieved.
Research Guidelines
The research guidelines define the agent’s strategy, including:
  • Mandatory use of retrieval tools before answering
  • A breadth-first, then depth-first research approach
  • Explicit planning between retrieval phases
  • Adaptive execution based on retrieved content
  • Efficiency constraints without sacrificing completeness
These instructions ensure consistent and repeatable agent behavior across runs.
 research_guidelines_prompt: |
            You have access to the following tool:
            - `search_docs` — Search the document datastore. Returns SEARCH_RESULTS with CITE_ID for citation.
            
            You have access to the following data source:
            1. Document Datastore (Unstructured): 
                - Contains user-uploaded documents that have been parsed, extracted, and chunked for efficient retrieval.
                - Use the `search_docs` tool to query this datastore for relevant content, details, and information from the available documents.
            
            ## Research Strategy
            You MUST always explore unstructured datastore before answering. Do not skip the source.
            - Breadth, then depth strategy:
                1. INITIAL RETRIEVAL - FIRST (Mandatory):
                  - Documents: Use `search_docs` with your initial search terms.
                2. ANALYZE & PLAN: Review initial results and create a query plan:
                  - What information is still missing to fully answer the question?
                  - Create a specific plan: which queries to run next and in what order
                  - Identify dependencies: what do you need to find first before searching for related info?
                3. DEEP DIVE (Execute & Adapt): Execute your plan and adapt based on retrieved content:
                  - Run planned queries systematically
                  - Continue until you have a COMPLETE answer - don't stop early
            - EFFICIENCY: You have 10 turns. Be strategic:
                - Avoid redundant searches; prefer high-quality retrievals.
                - Batch related searches when possible
                - Don't repeat similar queries
                - Prioritize high-value retrievals first
                - But DO NOT sacrifice comprehensiveness for speed - gather ALL relevant information

Generating the Final Response

The following step synthesizes a final user-facing response using the structured research output produced by the agent. generate:
  type: GenerateFromResearchStep
  ui_stream_types:
    generation: true
This step:
  • Consumes message_history and research
  • Produces the final response returned by the graph
  • Ensures the answer is grounded strictly in retrieved information
  • Enabling generation streaming allows partial responses to be shown in the UI as they are produced.

Controlling Response Style and Grounding

The response configuration enforces formatting, tone, and grounding constraints for the final output.
config:
  model_name_or_path: vertex_ai/claude-opus-4-5@20251101
Response guidelines typically enforce:
  • Immediate answers with no preamble or meta-commentary
  • Clear, concise Markdown formatting
  • Explicit handling of missing or incomplete information
  • A professional and neutral tone
This ensures consistent output quality and prevents internal reasoning or research details from leaking into the final response.

End-to-End Data Flow

The overall execution flow of an agentic workflow is as follows:
inputs.query
  → CreateMessageHistoryStep
    → message_history
      → AgenticResearchStep
        → research
          → GenerateFromResearchStep
            → outputs.response
Each phase has a clearly defined responsibility, making the workflow easier to understand, debug, and extend.

Design Principles

The agentic research pattern in Agent Composer is guided by a small set of core design principles. These principles ensure agent workflows remain understandable, reliable, and easy to operate as they grow in complexity.
  • Separation of concerns: research and generation are handled by different steps
  • Grounded responses: final answers are derived only from retrieved content
  • Observability: retrievals and generation can be streamed independently
  • Determinism: agent behavior is fully specified by configuration

Discovering Step Types

To see all available steps, their inputs, outputs, config fields, and UI streaming support, run the following curl command:
curl -X GET "https://api.staging.ctxl.dev/v1/steps?return_all=true"
This endpoint is the source of truth for step schemas.

Creating New Steps

To add a new step:
  1. Implement a PipelineStep subclass
  2. Define INPUTS, OUTPUTS, and optional UI metadata
  3. Register it in registrations.py
  4. Add it to the step catalog for discoverability

Running Graphs Programmatically

To run graphs programmatically, use the following Python code:
graph = ExecutableGraph.from_yaml(yaml_string)
graph.execute_graph({"query": "Hello"})
Inputs map to inputs, and outputs are returned as a dict.

API Response

ACL workflows are executed via /query/acl, with the following response fields returned:
  • outputs: graph outputs (JSON-serializable)
  • workflow_trace: executed nodes and durations
  • dynamic_agent_trace: agent reasoning and tool usage (if applicable)

Common Validation Errors

The following are common validation errors and how to fix them:
Validation ErrorFix
Unsupplied node inputsEnsure every required input is mapped.
Missing __outputs__ nodeExplicitly map node outputs to graph outputs.
Multiple outputs without ui_outputDesignate exactly one UI output.
Non-serializable outputsConvert outputs to basic types before output.

Additional Resources