Skip to content

Tornado Agent Basics

Overview

TornadoAgent is the core agent class in LlmTornado.Agents that provides a high-level abstraction for building AI agents. It combines AI models with tools, instructions, and structured output to create agents capable of performing complex tasks.

Quick Start

csharp
using LlmTornado;
using LlmTornado.Agents;
using LlmTornado.Chat.Models;

TornadoApi api = new TornadoApi("your-api-key");

// Create a simple agent
TornadoAgent agent = new TornadoAgent(
    client: api,
    model: ChatModel.OpenAi.Gpt41.V41Mini,
    instructions: "You are a helpful assistant."
);

// Run the agent
Conversation result = await agent.RunAsync("Hello, what can you help me with?");
Console.WriteLine(result.Messages.Last().Content);

Prerequisites

  • LlmTornado.Agents package installed
  • Understanding of basic LlmTornado chat functionality
  • Familiarity with C# async/await patterns

TornadoAgent

TornadoAgent Architecture

A TornadoAgent consists of:

  • Client: TornadoApi instance for API communication
  • Model: The AI model powering the agent
  • Instructions: System-level guidance defining behavior
  • Tools: Functions the agent can call
  • Output Schema: Optional type for structured responses
  • Streaming: Real-time response capability

Agent Initialization

csharp
TornadoAgent agent = new TornadoAgent(
    client: api,                    // Required: API client
    model: ChatModel.OpenAi.Gpt4.O, // Required: AI model
    name: "CodeAssistant",          // Optional: Agent name
    instructions: "You are a coding expert specialized in C#", // Optional: Behavior instructions
    outputSchema: typeof(Response), // Optional: Structured output type
    tools: toolsList,               // Optional: Available functions
    mcpServers: mcpList,            // Optional: MCP servers
    streaming: true,                // Optional: Enable streaming
    toolPermissionRequired: perms    // Optional: Tool permissions
);

Basic Usage

Simple Agent

csharp
TornadoAgent agent = new TornadoAgent(
    client: api,
    model: ChatModel.OpenAi.Gpt41.V41Mini,
    instructions: "You are a helpful assistant."
);

Conversation result = await agent.RunAsync("What is 2+2?");
Console.WriteLine(result.Messages.Last().Content);

Agent with Custom Instructions

csharp
string instructions = @"
You are a senior software architect with expertise in:
- Microservices architecture
- Cloud-native design patterns
- System scalability and performance
- Security best practices

Provide detailed, actionable advice with code examples when appropriate.
";

TornadoAgent agent = new TornadoAgent(
    client: api,
    model: ChatModel.OpenAi.Gpt4.O,
    name: "ArchitectBot",
    instructions: instructions
);

Conversation result = await agent.RunAsync(
    "How should I design a scalable event-driven system?"
);

Agent with Tools

csharp
TornadoAgent agent = new TornadoAgent(
    client: api,
    model: ChatModel.OpenAi.Gpt41.V41Mini,
    instructions: "You are a helpful assistant that can check weather.",
    tools: [weathertools.GetWeather]
);

Conversation result = await agent.RunAsync("What's the weather like in Prague?");

Console.WriteLine(result.Messages.Last().Content);
// Agent will call GetWeather("Prague") and use the result

public class weathertools
{
    // Define a tool function
    [Description("get the weather")]
    public static string GetWeather([Description("cit to get weather from")] string city)
    {
        // Simulated weather data
        return $"The weather in {city} is sunny with 22°C.";
    }
}

Agent with Structured Output

csharp
TornadoAgent agent = new TornadoAgent(
    client: api,
    model: ChatModel.OpenAi.Gpt4.O,
    instructions: "Analyze tasks and provide structured breakdown.",
    outputSchema: typeof(TaskAnalysis)
);

Conversation result = await agent.RunAsync("Analyze: Build a REST API with authentication");
TaskAnalysis? analysis = JsonConvert.DeserializeObject<TaskAnalysis>(
    result.Messages.Last().Content
);

Console.WriteLine($"Task: {analysis?.Task}" +
    $" Complexity: {analysis?.ComplexityScore} " +
    $" Steps: {string.Join(", ", analysis?.Steps ?? Array.Empty<string>())} " +
    $" Estimated Time: {analysis?.EstimatedMinutes} minutes");

public class TaskAnalysis
{
    public string Task { get; set; }
    public int ComplexityScore { get; set; }
    public string[] Steps { get; set; }
    public int EstimatedMinutes { get; set; }
}

Advanced Usage

Maintaining Conversation Context

csharp
TornadoAgent agent = new TornadoAgent(api, ChatModel.OpenAi.Gpt41.V41Mini);

// First interaction
Conversation result = await agent.RunAsync("My name is Alice and I'm learning C#.");

// Continue conversation
result = await agent.RunAsync(
    "What should I learn next?",
    appendMessages: result.Messages.ToList()
);

// Agent remembers context
result = await agent.RunAsync(
    "What's my name again?",
    appendMessages: result.Messages.ToList()
);

Dynamic Output Schema Changes

csharp
TornadoAgent agent = new TornadoAgent(
    api, 
    ChatModel.OpenAi.Gpt4.O,
    outputSchema: typeof(PersonInfo)
);

// Use initial schema
Conversation result = await agent.RunAsync("Extract: John, 30, NYC");

// Change schema dynamically
agent.UpdateOutputSchema(typeof(CompanyInfo));

// Use new schema
result = await agent.RunAsync("Extract: TechCorp, founded 2010, 500 employees");

Adding Tools Dynamically

csharp
TornadoAgent agent = new TornadoAgent(api, ChatModel.OpenAi.Gpt41.V41);

// Add tool later
Tool weatherTool = new Tool(
    (string city) => $"Weather in {city}: Sunny",
    "get_weather",
    "Gets weather for a city"
);

agent.AddTornadoTool(weatherTool);

Conversation result = await agent.RunAsync("What's the weather in London?");

Best Practices

Clear Instructions

  • Be specific about the agent's role
  • Define boundaries and limitations
  • Provide examples when helpful
  • Use structured format for complex instructions

Tool Design

  • Keep tools focused and single-purpose
  • Provide clear descriptions
  • Handle errors gracefully
  • Return meaningful results

Error Handling

csharp
try
{
    Conversation result = await agent.RunAsync(userInput);
    
    if (!result.Messages.Any())
    {
        Console.WriteLine("No response generated");
        return;
    }
    
    string response = result.Messages.Last().Content;
    Console.WriteLine(response);
}
catch (Exception ex)
{
    Console.WriteLine($"Agent error: {ex.Message}");
    // Implement retry or fallback logic
}

Memory Management

  • Trim conversation history for long sessions
  • Save important context to external storage
  • Use conversation serialization for persistence

Common Issues

Agent Not Using Tools

Solutions:

  • Ensure instructions mention tool availability
  • Use models with strong function calling (GPT-4, Claude 3+)
  • Provide clear tool descriptions
  • Test tools individually first

Context Loss

Solutions:

  • Always use appendMessages for continuity
  • Implement conversation persistence
  • Monitor token usage and trim history

Poor Output Quality

Solutions:

  • Improve instruction clarity
  • Use more capable models
  • Provide examples in instructions
  • Adjust temperature and other parameters

API Reference

Constructor

csharp
public TornadoAgent(
    TornadoApi client,
    ChatModel model,
    string name = "Assistant",
    string instructions = "You are a helpful assistant",
    Type? outputSchema = null,
    List<Delegate>? tools = null,
    List<MCPServer>? mcpServers = null,
    bool streaming = false,
    Dictionary<string, bool>? toolPermissionRequired = null
)

TornadoAgent Methods

  • RunAsync(string input, ...) - Execute agent with input
  • UpdateOutputSchema(Type? schema) - Change output format after initilization

TornadoAgent Properties

TornadoApi Client

TornadoApi required with authentication for api access to run the agent.

ChatModel Model

ChatModel desired to run on the agent while performing task. Close consideration should be taken when considering which model to use for each job.

ChatRequest Options

ChatRequest Used to configure any option within the specific api's to get a more advanced control. (Please set this after initilization)

ResponseRequest ResponseOptions

ResponseRequest Response Options to support OpenAI response Tools (Please set this after initilization)

string Name

Use to differentiate the agent when using as a tool or within the logging.

string Instructions

System message the agent will use as instructions for how to process users prompts

string Id

Unique generated GUID to differentiate agents on the backend

Type OutputSchema

The Type used to define the json stuctured output the agent should respond with.

List<Delegate> Tools

List of delegate functions used to extract tools from on initilization.

Dictionary<string, bool> ToolPermissionRequired

Set the Dictionary string Key as the tool name = true if you want the agent loop to send event for permission request.

Dictionary<string, Tool> ToolList

List of available Tornado tools that have been converted and ready to use within the agent loop

Dictionary<string, TornadoAgentTool> AgentTools

List of available Agents converted into Tornado tools and ready to be use within the agent loop

Dictionary<string, MCPServer> McpTools

List of available MCP tools as keys = MCP servers that host the tool required for the agent loop

List<MCPServer> MpcServers

List of MCP Servers to extract Tools from on initilization

bool Streaming

Used to enable to the default runner streaming capability if paired with a streaming handler. (or else it will not run streaming).

⚡ Event ⚡ Func<AgentRunnerEvents, ValueTask>? OnAgentRunnerEvent

Used to get Streaming Results, debugging information, tool results, ects.