Custom Workflow¶
In the custom workflow pattern, you define your own execution flow using Spring AI Alibaba StateGraph. You have full control over the graph structure—sequential steps, conditional branches, loops, and parallel execution. Nodes can be deterministic (e.g. vector search, DB query), LLM-based (e.g. rewrite, classify), or agentic (an agent with tools). State is passed between nodes via a shared state map with configurable strategies (replace, append).
This pattern is useful when standard patterns (Pipeline, Routing, Subagents, etc.) do not fit, when you need to mix deterministic logic with agentic behavior, or when your use case requires multi-stage processing with explicit control over the flow.
Key characteristics¶
Full control over graph structure: You define nodes and edges; the framework does not impose a fixed topology.
Mix deterministic and agentic steps: Some nodes run without an LLM (e.g. retrieve, list_tables); others call a model or an agent with tools.
Explicit state: Each node reads and updates state (e.g.
question,rewritten_query,documents,messages). Key strategies (replace vs append) control how state is merged.Composable: You can use other patterns as nodes—for example, an AgentScopeAgent (or a Pipeline) as a single node in the graph.
When to use¶
Consider a custom workflow when:
You need a multi-stage pipeline that is not a simple sequence of identical agents (e.g. rewrite → retrieve → agent, or list_tables → get_schema → generate_query).
You want to combine non-LLM steps (retrieval, DB, APIs) with LLM or agent steps in a fixed order.
Standard patterns (Pipeline, Routing, Subagents, Supervisor) do not match your flow, or you need conditional branches or loops that those patterns do not express naturally.
Implementation¶
The project uses Spring AI Alibaba StateGraph and CompiledGraph. Core steps:
Define state and strategies: Create a
StateGraphwith a name and a supplier ofMap<String, KeyStrategy>. UseReplaceStrategyfor single-value keys (e.g.question,rewritten_query) andAppendStrategyfor lists or message history (e.g.messages).Add nodes: Each node is either:
A function node:
NodeAction(sync or async vianode_async(...)). It receivesOverAllState, returns aMap<String, Object>of state updates. Can be pure logic (e.g. vector search) or an ad-hoc LLM call (e.g. rewrite).An agent node:
AgentScopeAgent.asNode(). The agent receives state (e.g. viainstructionwith{input}or from state contents) and returns updates (e.g.messages).
Add edges:
addEdge(START, "first_node"),addEdge("node_a", "node_b"),addEdge("last_node", END). For conditional routing you use conditional edges (not shown in the examples below).Compile and invoke:
graph.compile()returns aCompiledGraph; you invoke it with an initial state map and read the final state (e.g.messages,answer) from the result.
Example: RAG workflow (conceptual)
START → rewrite → retrieve → prepare_agent → rag_agent → END
rewrite: LLM rewrites the query for better retrieval (function node with Model).
retrieve: Vector similarity search, no LLM (function node using Knowledge).
prepare_agent: Formats context and question into a prompt (deterministic function node).
rag_agent: AgentScopeAgent (ReActAgent) with context and optional tools (e.g.
get_latest_news).
Example: SQL workflow (conceptual)
START → list_tables → call_get_schema → get_schema → generate_query → END
list_tables: Lists DB tables (function node using SqlTools).
call_get_schema: LLM decides which table(s) to get schema for (function node).
get_schema: Executes schema lookup (function node).
generate_query: AgentScopeAgent with
sql_db_list_tables,sql_db_schema,sql_db_querytools; generates and runs SELECT.
Example project¶
Location: agentscope-examples/multiagent-patterns/workflow/
Package |
Flow |
Description |
|---|---|---|
ragagent |
Query → Rewrite → Retrieve → Prepare → Agent → Response |
RAG: rewrite query, vector retrieve, then ReActAgent with context and |
sqlagent |
START → list_tables → get_schema → generate_query → END |
SQL: list tables, get schema for relevant tables, then ReActAgent with SQL tools (list_tables, schema, query). Uses H2 in-memory and AgentScopeAgent. |
Build and run (from repo root):
./mvnw -pl agentscope-examples/multiagent-patterns/workflow -am -B package -DskipTests
Run RAG workflow (with optional demo on startup):
./mvnw -pl agentscope-examples/multiagent-patterns/workflow spring-boot:run \
-Dspring-boot.run.arguments="--workflow.rag.enabled=true --workflow.runner.enabled=true"
Run SQL workflow (with optional demo on startup):
./mvnw -pl agentscope-examples/multiagent-patterns/workflow spring-boot:run \
-Dspring-boot.run.arguments="--workflow.sql.enabled=true --workflow.runner.enabled=true"
Configuration:
workflow.rag.enabled– Enable RAG workflow beans.workflow.sql.enabled– Enable SQL workflow beans.workflow.runner.enabled– Whentrue, run a one-shot demo on startup (use with one of the above).DashScope API key:
AI_DASHSCOPE_API_KEYorspring.ai.dashscope.api-key(required for RAG and SQL; RAG also needs an embedding model).
Custom workflow vs other patterns¶
Pipeline: Pipeline uses flow agents (SequentialAgent, ParallelAgent, LoopAgent) with a fixed topology (sequence, fan-out, loop). Custom workflow uses StateGraph and arbitrary nodes/edges; you can implement pipeline-like flows manually and add conditional or mixed deterministic/agentic steps.
Routing: Routing is classify → specialist(s) → merge, often with a single router node. In a custom workflow you could implement the same with your own graph (e.g. router node → branch → specialists → merge node).
Handoffs: Handoffs use state (e.g.
active_agent) to route between agent nodes. Custom workflow can do the same with conditional edges and state; Handoffs is a dedicated pattern for “who owns the conversation” transitions.
For implementation details and code, see the workflow example (agentscope-examples/multiagent-patterns/workflow/) and the RAG/SQL config classes (RagAgentConfig, SqlAgentConfig).