diff --git a/README.md b/README.md index 207e9258..6f19a4e3 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ Flo AI is a Python framework that makes building production-ready AI agents and - [Create a Tool-Using Agent](#create-a-tool-using-agent) - [Create an Agent with Structured Output](#create-an-agent-with-structured-output) - [šŸ“ YAML Configuration](#-yaml-configuration) +- [šŸ”§ Variables System](#-variables-system) - [šŸ› ļø Tools](#ļø-tools) - [šŸŽÆ @flo_tool Decorator](#-flo_tool-decorator) - [🧠 Reasoning Patterns](#-reasoning-patterns) @@ -269,6 +270,261 @@ agent: Agent = builder.build() result: Any = await agent.run(email_thread) ``` +## šŸ”§ Variables System + +Flo AI supports dynamic variable resolution in agent prompts and inputs using `` syntax. Variables are automatically discovered, validated at runtime, and can be shared across multi-agent workflows. + +### ✨ Key Features + +- **šŸ” Automatic Discovery**: Variables are extracted from system prompts and inputs at runtime +- **āœ… Runtime Validation**: Missing variables are reported with detailed error messages +- **šŸ¤ Multi-Agent Support**: Variables can be shared across agent workflows +- **šŸ›”ļø JSON-Safe Syntax**: `` format avoids conflicts with JSON content + +### Basic Usage + +```python +import asyncio +from typing import Any, Dict +from flo_ai.builder.agent_builder import AgentBuilder +from flo_ai.llm import OpenAI +from flo_ai.models.agent import Agent + +async def main() -> None: + # Create agent with variables in system prompt + agent: Agent = ( + AgentBuilder() + .with_name('Data Analyst') + .with_prompt('Analyze and focus on . Generate insights for .') + .with_llm(OpenAI(model='gpt-4o-mini')) + .build() + ) + + # Define variables at runtime + variables: Dict[str, str] = { + 'dataset_path': '/data/sales_q4_2024.csv', + 'key_metric': 'revenue growth', + 'target_audience': 'executive team' + } + + # Run agent with variable resolution + result: Any = await agent.run( + 'Please provide a comprehensive analysis with actionable recommendations.', + variables=variables + ) + + print(f'Analysis: {result}') + +asyncio.run(main()) +``` + +### Variables in User Input + +Variables can also be used in the user input messages: + +```python +import asyncio +from typing import Any, Dict +from flo_ai.models.agent import Agent +from flo_ai.llm import OpenAI + +async def input_variables_example() -> None: + agent: Agent = Agent( + name='content_creator', + system_prompt='You are a content creator specializing in .', + llm=OpenAI(model='gpt-4o-mini') + ) + + variables: Dict[str, str] = { + 'content_type': 'technical blog posts', + 'topic': 'machine learning fundamentals', + 'word_count': '1500', + 'target_level': 'intermediate' + } + + # Variables in both system prompt and user input + result: Any = await agent.run( + 'Create a -word article about for readers.', + variables=variables + ) + + print(f'Content: {result}') + +asyncio.run(input_variables_example()) +``` + +### Multi-Agent Variable Sharing + +Variables can be shared and passed between agents in workflows: + +```python +import asyncio +from typing import Any, Dict, List +from flo_ai.arium import AriumBuilder +from flo_ai.models.agent import Agent +from flo_ai.llm import OpenAI + +async def multi_agent_variables() -> List[Any]: + llm: OpenAI = OpenAI(model='gpt-4o-mini') + + # Agent 1: Research phase + researcher: Agent = Agent( + name='researcher', + system_prompt='Research and focus on analysis.', + llm=llm + ) + + # Agent 2: Writing phase + writer: Agent = Agent( + name='writer', + system_prompt='Write a based on the research for .', + llm=llm + ) + + # Agent 3: Review phase + reviewer: Agent = Agent( + name='reviewer', + system_prompt='Review the for and provide feedback.', + llm=llm + ) + + # Shared variables across all agents + shared_variables: Dict[str, str] = { + 'research_topic': 'sustainable energy solutions', + 'research_depth': 'comprehensive', + 'document_type': 'white paper', + 'target_audience': 'policy makers', + 'review_criteria': 'accuracy and policy relevance' + } + + # Run multi-agent workflow with shared variables + result: List[Any] = await ( + AriumBuilder() + .add_agents([researcher, writer, reviewer]) + .start_with(researcher) + .connect(researcher, writer) + .connect(writer, reviewer) + .end_with(reviewer) + .build_and_run( + ['Begin comprehensive research and document creation process'], + variables=shared_variables + ) + ) + + return result + +asyncio.run(multi_agent_variables()) +``` + +### YAML Configuration with Variables + +Variables work seamlessly with YAML-based agent configuration: + +```yaml +apiVersion: flo/alpha-v1 +kind: FloAgent +metadata: + name: personalized-assistant + version: 1.0.0 + description: "Personalized assistant with variable support" +agent: + name: PersonalizedAssistant + kind: llm + role: assistant specialized in + model: + provider: openai + name: gpt-4o-mini + settings: + temperature: 0.3 + max_retries: 2 + reasoning_pattern: DIRECT + job: > + You are a focused on . + Your expertise includes and you should + tailor responses for users. + Always consider in your recommendations. +``` + +```python +import asyncio +from typing import Any, Dict +from flo_ai.builder.agent_builder import AgentBuilder +from flo_ai.models.agent import Agent + +async def yaml_with_variables() -> None: + yaml_config: str = """...""" # Your YAML configuration + + # Variables for YAML agent + variables: Dict[str, str] = { + 'user_role': 'data scientist', + 'domain_expertise': 'machine learning and statistical analysis', + 'primary_objective': 'deriving actionable insights from data', + 'experience_level': 'senior', + 'priority_constraints': 'computational efficiency and model interpretability' + } + + # Create agent from YAML with variables + builder: AgentBuilder = AgentBuilder.from_yaml(yaml_str=yaml_config) + agent: Agent = builder.build() + + result: Any = await agent.run( + 'Help me design an ML pipeline for with ', + variables={ + **variables, + 'use_case': 'customer churn prediction', + 'data_constraints': 'limited labeled data' + } + ) + + print(f'ML Pipeline Advice: {result}') + +asyncio.run(yaml_with_variables()) +``` + +### Error Handling and Validation + +The variables system provides comprehensive error reporting for missing or invalid variables: + +```python +import asyncio +from typing import Any, Dict +from flo_ai.models.agent import Agent +from flo_ai.llm import OpenAI + +async def variable_validation_example() -> None: + agent: Agent = Agent( + name='validator_example', + system_prompt='Process and for analysis.', + llm=OpenAI(model='gpt-4o-mini') + ) + + # Incomplete variables (missing 'another_param') + incomplete_variables: Dict[str, str] = { + 'required_param': 'dataset.csv' + # 'another_param' is missing + } + + try: + result: Any = await agent.run( + 'Analyze the data in ', + variables=incomplete_variables # Missing 'another_param' and 'data_source' + ) + except ValueError as e: + print(f'Variable validation error: {e}') + # Error will list all missing variables with their locations + +asyncio.run(variable_validation_example()) +``` + +### Best Practices + +1. **Descriptive Variable Names**: Use clear, descriptive names like `` instead of `` +2. **Consistent Naming**: Use consistent variable names across related agents and workflows +3. **Validation**: Always test your variable resolution before production deployment +4. **Documentation**: Document expected variables in your agent configurations + +The variables system makes Flo AI agents highly reusable and configurable, enabling you to create flexible AI workflows that adapt to different contexts and requirements. + ## šŸ› ļø Tools Create custom tools easily with async support: diff --git a/flo_ai/examples/variables_workflow_example.py b/flo_ai/examples/variables_workflow_example.py new file mode 100644 index 00000000..4b8b0609 --- /dev/null +++ b/flo_ai/examples/variables_workflow_example.py @@ -0,0 +1,242 @@ +""" +Simple variables workflow example with separate functions for complete and incomplete variables. + +This example shows: +1. Simple function to test complete variables (success case) +2. Simple function to test incomplete variables (error case) +""" + +import os +import asyncio +from flo_ai.builder.agent_builder import AgentBuilder +from flo_ai.arium.builder import AriumBuilder +from flo_ai.models.agent import Agent +from flo_ai.llm.gemini_llm import Gemini + +from dotenv import load_dotenv + +load_dotenv() + +api_key = os.getenv('GOOGLE_API_KEY') + + +async def test_single_agent_complete_variables(): + """Test single agent with complete variables""" + print('\n🟢 TESTING SINGLE AGENT - COMPLETE VARIABLES') + print('-' * 50) + + llm = Gemini(model='gemini-2.5-flash', temperature=0.7, api_key=api_key) + + # Create agent that uses variables (automatically extracted) + translator = ( + AgentBuilder() + .with_name('translator') + .with_prompt('You are a translator. Use this tone: ') + .with_llm(llm) + .build() + ) + + # Complete variables + variables = { + 'target_language': 'Spanish', + 'tone': 'formal', + 'text_to_translate': 'Welcome to our application', + } + + print('Variables provided:') + for key, value in variables.items(): + print(f' āœ“ {key}: {value}') + + try: + result = await translator.run( + 'Translate the following text: to ', + variables=variables, + ) + print('\nāœ… SUCCESS: Single agent executed successfully!') + print(f'Result: {result}') + except Exception as e: + print(f'āŒ Unexpected error: {e}') + + +async def test_single_agent_incomplete_variables(): + """Test single agent with incomplete variables""" + print('\nšŸ”“ TESTING SINGLE AGENT - INCOMPLETE VARIABLES') + print('-' * 50) + + llm = Gemini(model='gemini-2.5-flash', temperature=0.7, api_key=api_key) + + # Create agent that uses variables (automatically extracted) + calculator = ( + AgentBuilder() + .with_name('calculator') + .with_prompt('You are a calculator. Show the result in format.') + .with_llm(llm) + .build() + ) + + # Incomplete variables - missing some required variables + incomplete_variables = { + 'operation': 'addition', + 'number1': '15', + # Missing: 'number2' and 'format' + } + + print('Variables provided:') + for key, value in incomplete_variables.items(): + print(f' āœ“ {key}: {value}') + + print('\nMissing variables:') + print(' āŒ number2') + print(' āŒ format') + + try: + await calculator.run( + 'Please calculate the result of of and .', + variables=incomplete_variables, + ) + print("āŒ ERROR: Should have failed but didn't!") + except ValueError as e: + print('\nāœ… SUCCESS: Expected error caught!') + print(f'Error message: {e}') + + +async def test_multi_agent_complete_variables(): + """Test workflow with complete variables - should succeed""" + print('🟢 TESTING MULTI AGENT - COMPLETE VARIABLES') + print('-' * 40) + + llm = Gemini(model='gemini-2.5-flash', temperature=0.7, api_key=api_key) + + # Agent 1: Content Creator - uses 'topic' and 'style' variables + content_creator = Agent( + name='content_creator', + system_prompt='You are a content creator. Write in a