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:
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:
| Field | Required | Description |
|---|
inputs | ✅ | Graph inputs and their Python types |
outputs | ✅ | Graph outputs (must be JSON-serializable) |
nodes | ✅ | Nodes 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:
Wiring & Mapping Syntax
Nodes are connected using output references of the form:
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 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:
CreateMessageHistoryStep – Initialize structured conversation state
AgenticResearchStep – Perform multi-turn research using tools
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.
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:
- Implement a
PipelineStep subclass
- Define
INPUTS, OUTPUTS, and optional UI metadata
- Register it in
registrations.py
- 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 Error | Fix |
|---|
| Unsupplied node inputs | Ensure every required input is mapped. |
Missing __outputs__ node | Explicitly map node outputs to graph outputs. |
Multiple outputs without ui_output | Designate exactly one UI output. |
| Non-serializable outputs | Convert outputs to basic types before output. |
Additional Resources