Practical patterns for building agentic and multi-agent systems on top of cascadeflow:
- 🔁 Tool loops (multi-turn tool calling)
- 🧩 Multi-agent orchestration (planner/executor/researcher)
- 🧰 Agent-as-a-tool delegation
- 🧱 Message list best practices (tool history + system prompts)
- What cascadeflow Gives You (and What You Still Own)
- Message Format (Tool Loops)
- Tool Loop: Fast DX With
tool_executor - Multi-Agent: Two Proven Orchestration Patterns
- System Prompts (Important)
- Example (Runnable)
- Troubleshooting
cascadeflow handles:
- ✅ Model cascading (cheap first, escalate when needed)
- ✅ Tool-capable model filtering (when tools are present)
- ✅ Tool intent/risk routing (choose strong tool models when needed)
- ✅ Streaming helpers (
stream_events) for UIs and observability
You still implement:
- 🔁 Tool implementations (your functions, your side effects)
- 🧠 Multi-agent orchestration (agent graph, delegation, memory, state)
For multi-turn tool calling, your message history will contain:
role="assistant"withtool_calls(the model asking to call tools)role="tool"withtool_call_id(your tool results, one per tool call)- Regular
user/assistantmessages
Example:
messages = [
{"role": "user", "content": "Compute 2+2"},
{
"role": "assistant",
"content": "",
"tool_calls": [
{"id": "call_1", "type": "function", "function": {"name": "calculate", "arguments": "{\"expression\":\"2+2\"}"}}
],
},
{"role": "tool", "tool_call_id": "call_1", "content": "{\"result\": 4}"},
{"role": "user", "content": "Explain the result briefly."},
]If you pass a tool_executor to CascadeAgent, cascadeflow can automatically:
- Execute tool calls when the model emits them
- Append tool results
- Continue until the model stops requesting tools (or
max_stepsis reached)
from cascadeflow import CascadeAgent, ModelConfig
from cascadeflow.tools import ToolConfig, ToolExecutor
def calculate(expression: str) -> dict:
return {"expression": expression, "result": 4}
executor = ToolExecutor([
ToolConfig(
name="calculate",
description="Calculator",
parameters={
"type": "object",
"properties": {"expression": {"type": "string"}},
"required": ["expression"],
},
function=calculate,
)
])
agent = CascadeAgent(
models=[
ModelConfig(name="gpt-4o-mini", provider="openai", cost=0.00015, supports_tools=True),
ModelConfig(name="gpt-4o", provider="openai", cost=0.00625, supports_tools=True),
],
tool_executor=executor,
)
tools = [
{
"name": "calculate",
"description": "Calculator",
"parameters": {
"type": "object",
"properties": {"expression": {"type": "string"}},
"required": ["expression"],
},
}
]
result = await agent.run(
"Compute 2+2 using the calculate tool.",
tools=tools,
max_steps=5,
)
print(result.content)planner_agentproduces a planresearch_agentgathers facts/toolswriter_agentproduces final output
Use multiple CascadeAgent instances with different prompts, models, and tool sets.
Expose a tool like delegate_to_researcher({question}) where the tool implementation calls a second agent.
This scales well because delegation becomes just another tool call, with separate rate-limits and tracing if needed.
Python providers typically accept system prompts as role="system" messages in the message list.
Recommendation:
- Put your system prompt either in
system_prompt=...(agent-level) or as the first message withrole="system". - Avoid duplicating the same instruction in multiple places.
See:
examples/agentic_multi_agent.py
It demonstrates:
- Tool loop with
tool_executor - Multi-agent delegation as a tool
Tool loop stops early
- Ensure your tools are passed as
tools=[...]. - Ensure the agent is created with
tool_executor=....
Tool results are not matched to tool calls
- Ensure each tool result message includes the correct
tool_call_id.