Deterministic Workflow
Learn how to create a deterministic multi-step workflow with agents.
Multi-Step Workflow Example
This example demonstrates how to create a deterministic workflow with agentic steps, combining native control flow and a "Reasoning and Acting" (ReAct) agent framework.
Unlike fully autonomous agent workflows, a deterministic workflow gives you control over the sequence of steps while leveraging the power of agents for specific tasks.
Python
multi-step-workflow.py
from oci.addons.adk import Agent, AgentClient
from custom_functon_tools import ResearcherToolkit, WriterToolkit
"""
This examples shows how you can build "deterministically orchestrated workflows with agentic steps".
"""
# Your (existing) vanilla python code to be integrated into this agentic workflow
def get_user_preferences():
# Simulate result you fetched from a DB
return {
"email": "your@email.com",
"style": ["casual", "humorous"],
"topics": ["ai"]
}
def main():
client = AgentClient(
auth_type="api_key",
profile="DEFAULT",
region="us-chicago-1"
)
researcher = Agent(
client=client,
agent_endpoint_id="ocid1.genaiagentendpoint...",
name="Researcher",
instructions="You are a researcher. You research trending keywords based on the user preferences.",
tools=[ResearcherToolkit()]
)
writer = Agent(
client=client,
agent_endpoint_id="ocid1.genaiagentendpoint...",
name="Writer",
instructions="You are a writer. You write a blog post based on the trending keywords and the user preferences.",
tools=[WriterToolkit()]
)
researcher.setup()
writer.setup()
# Step 1: Fetch user preferences or any pre-processing information. (non-agentic step)
user_preferences = get_user_preferences()
# Step 2: Research trending keywords using outputs from the previous steps as input. (agentic step)
topics = user_preferences['topics']
researcher_prompt = f"Research trending keywords for the following topics: {topics}"
last_run_response = researcher.run(researcher_prompt)
# Step 3: Write a blog post using outputs from last two steps as input. (agentic step)
keywords = last_run_response.output
style = user_preferences['style']
email = user_preferences['email']
writer_prompt = f"Write a 5 sentences blog post and email it to {email}. Use style: {style}. Blog post should be based on: {keywords}."
last_run_response = writer.run(writer_prompt)
# Step 4: Do whatever you want with the last step output. Here we just print it.
last_run_response.pretty_print()
if __name__ == "__main__":
main()
Java
AgenticWorkflowDemo.java
package demos.determinsticWorkflow;
import com.oracle.bmc.ConfigFileReader;
import com.oracle.bmc.adk.client.AgentClient;
import com.oracle.bmc.adk.run.RunResponse;
import com.oracle.bmc.auth.BasicAuthenticationDetailsProvider;
import com.oracle.bmc.auth.SessionTokenAuthenticationDetailsProvider;
import com.oracle.bmc.adk.agent.Agent;
import com.oracle.bmc.adk.agent.RunOptions;
import com.oracle.bmc.adk.examples.determinsticWorkflow.tools.ResearcherToolkit;
import com.oracle.bmc.adk.examples.determinsticWorkflow.tools.WriterToolkit;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AgenticWorkflowDemo {
public static void main(String[] args) throws IOException {
// Configuration parameters
final String configLocation = "~/.oci/config";
final String configProfile = "DEFAULT";
final String researchAgentEndpointId =
"ocid1.genaiagentendpoint...";
final String writerAgentEndpointId =
"ocid1.genaiagentendpoint...";
// Initialize AgentClient
BasicAuthenticationDetailsProvider authProvider =
new SessionTokenAuthenticationDetailsProvider(
ConfigFileReader.parse(configLocation, configProfile));
AgentClient client =
AgentClient.builder()
.authProvider(authProvider)
.region("us-chicago-1")
.build();
// Create researcher agent with its toolkit
Agent researcher = Agent.builder()
.client(client)
.agentEndpointId(researchAgentEndpointId)
.instructions(
"You are a researcher. You research trending keywords based on the user preferences.")
.tools(Arrays.asList(new ResearcherToolkit()))
.name("Researcher")
.build();
// Create writer agent with its toolkit
Agent writer = Agent.builder()
.client(client)
.agentEndpointId(writerAgentEndpointId)
.instructions(
"You are a writer. You write a blog post based on the trending keywords and the user preferences.")
.tools(Arrays.asList(new WriterToolkit()))
.name("Writer")
.build();
// Set up both agents
researcher.setup();
writer.setup();
// Step 1: fetch user preferences (a non-agentic step)
Map<String, Object> userPreferences = getUserPreferences();
// Step 2: research trending keywords (agentic step)
List<String> topics = (List<String>) userPreferences.get("topics");
String researcherPrompt = "Research trending keywords for the following topics: " + topics;
final Integer maxSteps = 3;
RunOptions runOptions = RunOptions.builder().maxSteps(maxSteps).build();
RunResponse researcherResponse = researcher.run(researcherPrompt, runOptions);
String keywords = researcherResponse.getOutput();
// Step 3: write a blog post (agentic step)
List<String> style = (List<String>) userPreferences.get("style");
String email = (String) userPreferences.get("delivery_email");
String writerPrompt =
"Write a 5 sentences blog post and email it to "
+ email
+ ". Use style: "
+ style
+ ". Blog post should be based on: "
+ keywords;
RunResponse writerResponse = writer.run(writerPrompt, runOptions);
// Step 4: print the final output
writerResponse.prettyPrint();
}
// Simulate fetching user preferences (for example, from a database)
private static Map<String, Object> getUserPreferences() {
Map<String, Object> preferences = new HashMap<>();
preferences.put("delivery_email", "j.jing.y.yang@oracle.com");
preferences.put("style", Arrays.asList("casual", "humorous"));
preferences.put("topics", Arrays.asList("ai"));
return preferences;
}
}
Custom Function Tools
This example uses custom toolkits for the researcher and writer agents. Here's a simplified version of what these might look like:
Python
custom_functon_tools.py
from typing import Dict, List
from oci.addons.adk import tool, Toolkit
@tool
def get_trending_keywords(topic: str) -> Dict[str, List[str]]:
"""Get trending keywords for a given topic"""
# In a real implementation, this might call an API or database.
if topic == "ai":
return {"topic": topic, "keywords": ["generative AI", "multi-agent systems", "LLM agents"]}
return {"topic": topic, "keywords": ["unknown"]}
@tool
def send_email(recipient: str, subject: str, body: str) -> str:
"""Send an email with the given subject and body to the recipient"""
# In a real implementation, this would send an actual email.
print(f"Sending email to {recipient}")
print(f"Subject: {subject}")
print(f"Body: {body}")
return "Email sent successfully"
class ResearcherToolkit(Toolkit):
"""Toolkit for researching trending topics"""
def __init__(self):
super().__init__(name="ResearcherToolkit", description="Tools for researching trending topics")
self.add_tool(get_trending_keywords)
class WriterToolkit(Toolkit):
"""Toolkit for writing content and sending emails"""
def __init__(self):
super().__init__(name="WriterToolkit", description="Tools for writing content and sending emails")
self.add_tool(send_email)
Java
MultiAgentTools.java
package demos.tools;
import com.oracle.bmc.adk.tools.Param;
import com.oracle.bmc.adk.tools.Tool;
import com.oracle.bmc.adk.tools.Toolkit;
public class MultiAgentTools extends Toolkit {
@Tool(name = "get_trending_keywords", description = "Get the trending keywords for a given topic")
public static String getTrendingKeywords(
@Param(description = "The topic to get trending keywords for") String topic) {
return "{\"topic\": \"" + topic + "\", \"keywords\": [\"agent\", \"stargate\", \"openai\"]}";
}
@Tool(name = "send_email", description = "Send an email to a recipient")
public static String sendEmail(
@Param(description = "The recipient email address") String recipient,
@Param(description = "The email subject") String subject,
@Param(description = "The email body content") String body) {
System.out.println(
"Sending email to " + recipient + " with subject " + subject + " and body " + body);
return "Sent!";
}
}
When to use this pattern
This pattern is particularly useful when:
- You need predictable, repeatable execution.
- Parts of your workflow depend on existing systems or databases.
- You need to control exactly when and how agents are invoked.
- Business logic demands specific sequences that shouldn't be left to agent decision-making.