Conversational Agent Ollama
Conversational agent ollama example.
Conversational AI Agent using Ollama (Local LLM).
Run it
python examples/ai/conversational_agent_ollama.py
"""
Conversational AI Agent using Ollama (Local LLM).
This example uses Ollama for running local LLMs, which is great for:
- Development and testing without API costs
- Privacy-sensitive applications
- Offline environments
- Custom fine-tuned models
Prerequisites:
1. Install Ollama: https://ollama.ai
2. Pull a model: ollama pull llama3
3. Start Ollama service: ollama serve
Usage:
# Start a new conversation
flux workflow run conversational_agent_ollama '{"message": "Why is the sky blue?"}'
# Resume the conversation
flux workflow resume conversational_agent_ollama <execution_id> '{"message": "Why does the sky turn red and orange during sunset?"}'
# Use a different model
flux workflow run conversational_agent_ollama '{"message": "What color would the sky be on Mars?", "model": "qwen2.5:0.5b"}'
"""
from __future__ import annotations
from typing import Any
from ollama import AsyncClient
from flux import ExecutionContext, task, workflow
from flux.tasks import pause
@task.with_options(retry_max_attempts=3, retry_delay=1, retry_backoff=2, timeout=60)
async def call_ollama_api(
messages: list[dict[str, str]],
system_prompt: str,
model: str,
ollama_url: str,
) -> str:
"""Call Ollama API to generate a response using the official SDK."""
try:
client = AsyncClient(host=ollama_url)
# Prepare messages with system prompt
full_messages = [{"role": "system", "content": system_prompt}]
full_messages.extend(messages)
# Call Ollama chat API
response = await client.chat(model=model, messages=full_messages)
return response["message"]["content"]
except Exception as e:
raise RuntimeError(
f"Failed to call Ollama API: {str(e)}. "
"Make sure Ollama is running (ollama serve) and the model is available.",
) from e
@task
async def conversation_turn(
messages: list[dict[str, str]],
user_message: str,
system_prompt: str,
model: str,
ollama_url: str,
) -> tuple[list[dict[str, str]], str]:
"""
Process one turn of the conversation.
Args:
messages: Current conversation history
user_message: The user's message
system_prompt: System prompt for the LLM
model: Ollama model to use
ollama_url: Ollama server URL
Returns:
Tuple of (updated messages, assistant response)
"""
# Add user message to history
messages.append({"role": "user", "content": user_message})
# Call LLM to generate response
assistant_response = await call_ollama_api(
messages=messages,
system_prompt=system_prompt,
model=model,
ollama_url=ollama_url,
)
# Add assistant response to history
messages.append({"role": "assistant", "content": assistant_response})
return messages, assistant_response
@workflow
async def conversational_agent_ollama(ctx: ExecutionContext[dict[str, Any]]):
"""
A conversational AI agent using Ollama for local LLM inference.
Initial Input format:
{
"message": "User's message",
"system_prompt": "Optional system prompt",
"model": "llama3", # or other Ollama models
"max_turns": 10, # Optional: maximum conversation turns
"ollama_url": "http://localhost:11434" # Optional: Ollama server URL
}
Resume Input format:
{
"message": "User's next message"
}
"""
# Get initial configuration from input
initial_input = ctx.input or {}
system_prompt = initial_input.get(
"system_prompt",
"You are a helpful AI assistant. Be concise and informative.",
)
model = initial_input.get("model", "qwen3")
max_turns = initial_input.get("max_turns", 10)
ollama_url = initial_input.get("ollama_url", "http://localhost:11434")
# Initialize conversation state
messages: list[dict[str, str]] = []
# Process first turn
first_message = initial_input.get("message", "")
if not first_message:
return {"error": "No message provided in initial input", "execution_id": ctx.execution_id}
messages, _ = await conversation_turn(messages, first_message, system_prompt, model, ollama_url)
# Main conversation loop - pause between turns
for turn in range(1, max_turns):
# Pause and wait for next user input
# When resumed, pause returns the input provided during resume
# Use a unique label for each pause to allow Flux to differentiate between them
resume_input = await pause(f"waiting_for_user_input_turn_{turn}")
# Get next message from resume input
next_message = resume_input.get("message", "") if resume_input else ""
if not next_message:
return {
"error": "No message provided in resume input",
"turn_count": len(messages) // 2,
"execution_id": ctx.execution_id,
"conversation_history": messages,
}
# Process next turn
messages, _ = await conversation_turn(
messages,
next_message,
system_prompt,
model,
ollama_url,
)
# Conversation ended - return final result
return {
"status": "conversation_ended",
"reason": f"Maximum turns ({max_turns}) reached",
"conversation_history": messages,
"turn_count": len(messages) // 2,
"model": model,
"execution_id": ctx.execution_id,
}
if __name__ == "__main__": # pragma: no cover
import json
# Start conversation about atmospheric physics
initial_input = {"message": "Why is the sky blue?", "model": "llama3", "max_turns": 3}
try:
# Turn 1
result = conversational_agent_ollama.run(initial_input)
if result.has_failed:
raise Exception(f"Workflow failed: {result.output}")
# Turn 2
result = conversational_agent_ollama.resume(
result.execution_id,
{"message": "Why does the sky turn red and orange during sunset?"},
)
if result.has_failed:
raise Exception(f"Workflow failed: {result.output}")
# Turn 3
result = conversational_agent_ollama.resume(
result.execution_id,
{"message": "What color would the sky be on Mars?"},
)
if result.has_failed:
raise Exception(f"Workflow failed: {result.output}")
# Display conversation
print(json.dumps(result.output.get("conversation_history", []), indent=2))
except Exception as e:
print(f"Error: {e}")
print("Make sure Ollama is running: ollama serve")
print("And that you have pulled a model: ollama pull llama3")
Last verified against Flux 0.56.0.