Conversational Agent Anthropic
Conversational agent anthropic example.
Conversational AI Agent using Anthropic Claude.
Run it
python examples/ai/conversational_agent_anthropic.py
"""
Conversational AI Agent using Anthropic Claude.
This example uses Anthropic's Claude API which excels at:
- Long-form conversations with extended context
- Complex reasoning and analysis
- Following detailed instructions
Prerequisites:
1. Get an Anthropic API key: https://console.anthropic.com/
2. Set the API key as a secret:
flux secrets set ANTHROPIC_API_KEY "your-api-key"
Usage:
# Start a new conversation
flux workflow run conversational_agent_anthropic '{"message": "Why is the sky blue?"}'
# Resume the conversation
flux workflow resume conversational_agent_anthropic <execution_id> '{"message": "Why does the sky turn red and orange during sunset?"}'
# Specify the model explicitly (uses Claude Sonnet 4.5 by default)
flux workflow run conversational_agent_anthropic '{"message": "What color would the sky be on Mars?", "model": "claude-sonnet-4-5-20250929"}'
"""
from __future__ import annotations
from typing import Any
from anthropic import AsyncAnthropic
from flux import ExecutionContext, task, workflow
from flux.tasks import pause
@task.with_options(
secret_requests=["ANTHROPIC_API_KEY"],
retry_max_attempts=3,
retry_delay=1,
retry_backoff=2,
timeout=60,
)
async def call_anthropic_api(
messages: list[dict[str, str]],
system_prompt: str,
model: str,
temperature: float,
max_tokens: int,
secrets: dict[str, Any] | None = None,
) -> tuple[str, int, int]:
"""
Call Anthropic API to generate a response using the official SDK.
Args:
messages: Conversation history
system_prompt: System instructions for the model
model: The Claude model to use
temperature: Sampling temperature (0.0 to 1.0)
max_tokens: Maximum tokens in the response
secrets: Dictionary containing ANTHROPIC_API_KEY
Returns:
Tuple of (assistant_response, input_tokens, output_tokens)
"""
if not secrets or "ANTHROPIC_API_KEY" not in secrets:
raise ValueError(
"ANTHROPIC_API_KEY not found. Set it using: "
"flux secrets set ANTHROPIC_API_KEY 'your-key'",
)
try:
# Create Anthropic client
client = AsyncAnthropic(api_key=secrets["ANTHROPIC_API_KEY"])
# Call Anthropic messages API
response = await client.messages.create(
model=model,
max_tokens=max_tokens,
temperature=temperature,
system=system_prompt,
messages=messages,
)
assistant_message = response.content[0].text
input_tokens = response.usage.input_tokens
output_tokens = response.usage.output_tokens
return assistant_message, input_tokens, output_tokens
except Exception as e:
raise RuntimeError(f"Failed to call Anthropic API: {str(e)}") from e
@task
async def conversation_turn(
messages: list[dict[str, str]],
user_message: str,
system_prompt: str,
model: str,
temperature: float,
max_tokens: int,
) -> tuple[list[dict[str, str]], str, int, int]:
"""
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: Anthropic model to use
temperature: Sampling temperature
max_tokens: Maximum tokens per response
Returns:
Tuple of (updated messages, assistant response, input tokens, output tokens)
"""
# Add user message to history
messages.append({"role": "user", "content": user_message})
# Call LLM to generate response
assistant_response, input_tokens, output_tokens = await call_anthropic_api(
messages=messages,
system_prompt=system_prompt,
model=model,
temperature=temperature,
max_tokens=max_tokens,
)
# Add assistant response to history
messages.append({"role": "assistant", "content": assistant_response})
return messages, assistant_response, input_tokens, output_tokens
@workflow
async def conversational_agent_anthropic(ctx: ExecutionContext[dict[str, Any]]):
"""
A conversational AI agent using Anthropic's Claude models.
Initial Input format:
{
"message": "User's message",
"system_prompt": "Optional system prompt",
"model": "claude-sonnet-4-5-20250929", # Latest Claude model
"temperature": 1.0, # Optional: 0.0 to 1.0
"max_tokens": 1024, # Optional: max tokens per response
"max_turns": 10 # Optional: maximum conversation turns
}
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", "claude-sonnet-4-5-20250929")
temperature = initial_input.get("temperature", 1.0)
max_tokens = initial_input.get("max_tokens", 1024)
max_turns = initial_input.get("max_turns", 10)
# Initialize conversation state
messages: list[dict[str, str]] = []
total_input_tokens = 0
total_output_tokens = 0
# 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, _, input_tokens, output_tokens = await conversation_turn(
messages,
first_message,
system_prompt,
model,
temperature,
max_tokens,
)
total_input_tokens += input_tokens
total_output_tokens += output_tokens
# 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,
"total_input_tokens": total_input_tokens,
"total_output_tokens": total_output_tokens,
"total_tokens": total_input_tokens + total_output_tokens,
}
# Process next turn
messages, _, input_tokens, output_tokens = await conversation_turn(
messages,
next_message,
system_prompt,
model,
temperature,
max_tokens,
)
total_input_tokens += input_tokens
total_output_tokens += output_tokens
# Conversation ended - return final result
return {
"status": "conversation_ended",
"reason": f"Maximum turns ({max_turns}) reached",
"conversation_history": messages,
"turn_count": len(messages) // 2,
"total_input_tokens": total_input_tokens,
"total_output_tokens": total_output_tokens,
"total_tokens": total_input_tokens + total_output_tokens,
"model": model,
"execution_id": ctx.execution_id,
}
if __name__ == "__main__": # pragma: no cover
import json
import os
from flux.secret_managers import SecretManager
# Check if API key is available
api_key = os.getenv("ANTHROPIC_API_KEY")
if not api_key:
print("Error: ANTHROPIC_API_KEY not found in environment.")
print("Set it using: export ANTHROPIC_API_KEY='your-key'")
exit(1)
# Set secret for local execution
secret_manager = SecretManager.current()
secret_manager.save("ANTHROPIC_API_KEY", api_key)
# Start conversation about atmospheric physics
initial_input = {
"message": "Why is the sky blue?",
"model": "claude-sonnet-4-5-20250929",
"max_turns": 3,
}
try:
# Turn 1
result = conversational_agent_anthropic.run(initial_input)
if result.has_failed:
raise Exception(f"Workflow failed: {result.output}")
# Turn 2
result = conversational_agent_anthropic.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_anthropic.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 and token usage
print(
f"Total Tokens: {result.output.get('total_tokens', 0)} "
f"(in: {result.output.get('total_input_tokens', 0)}, "
f"out: {result.output.get('total_output_tokens', 0)})",
)
print(json.dumps(result.output.get("conversation_history", []), indent=2))
except Exception as e:
print(f"Error: {e}")
Last verified against Flux 0.56.0.