Skip to main content

Building Production AI Agents & LLM Tool Calling in .NET with C#

Designing resilient AI agents in C# with Semantic Kernel, OpenAI & Claude APIs, strict JSON schema enforcement, and deterministic tool execution pipelines.

· 10 min read · Advanced

Key takeaways

  • Design deterministic tool-calling workflows that prevent agent hallucinations.
  • Implement strict schema enforcement for LLM JSON outputs.
  • Defend against indirect prompt injections and dangerous execution bugs.
  • Integrate AI agents into ASP.NET Core background services and Hangfire queues.

A chatbot that answers questions is one thing. An agent that queries your database, calls your APIs, and triggers business workflows on its own is a different problem entirely — and most of the difficulty isn't the LLM part.

1. The Core Agent Loop in C#

A production agent needs four subsystems, kept decoupled from each other: a Planner for the LLM reasoning step, a Tool Registry of C# functions the model can actually call, a Memory/Context Store, and Execution Guardrails — the sandbox and validation layer that stops a bad tool call from becoming a bad outcome.

// Registering native C# tools with Semantic Kernel
public class DatabaseQueryPlugin
{
    private readonly ISqlExecutor _sqlExecutor;

    public DatabaseQueryPlugin(ISqlExecutor sqlExecutor)
    {
        _sqlExecutor = sqlExecutor;
    }

    [KernelFunction, Description("Executes a read-only parameterized query against the product catalog.")]
    public async Task<string> QueryProductCatalogAsync(
        [Description("The category name to search")] string category,
        [Description("Maximum number of results (1-50)")] int limit = 10)
    {
        var results = await _sqlExecutor.GetProductsByCategoryAsync(category, Math.Clamp(limit, 1, 50));
        return JsonSerializer.Serialize(results);
    }
}

2. Defensive Engineering: Sanitizing Model Outputs

An LLM response is just text. Execute raw SQL or shell commands straight from that text without schema validation, and you've built an injection vector, not a feature. Typed DTOs and system-level capability boundaries aren't optional here.

Treat LLM tool arguments with the same level of distrust as untrusted external HTTP requests.

3. Connecting Agents to ASP.NET Core & Background Queues

Multi-step agent reasoning can run long — sometimes tens of seconds, sometimes minutes. That doesn't belong inside a synchronous HTTP request pipeline. Push it onto a background queue instead (Hangfire, or a .NET Channel<T> worker), and you get progress tracking for free along with avoiding the timeout entirely.

← All guides