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.