LangChain Agents in .NET with ReAct and Tools

Building LLM Agents with the LangChain NuGet Package
This post is part 4 of 5 in the series Mastering LangChain in .NET

Ever feel like your AI apps could do more than just respond to prompts? What if your language model could decide which tool to use, when, and how? That’s exactly where agents come into play. In this post, I will explore the world of LLM agents using the LangChain NuGet package and show you how to empower your AI with decision-making capabilities.

What Are Agents (and How They Relate to Tools and Chains)?

Agents in LangChain are like the brains behind the operation. They don’t just generate text; they reason and plan. When paired with tools (functions that perform specific actions) and chains (linked sequences of prompts and LLM calls), agents can dynamically choose the best path to accomplish a goal.

For example, an agent might:

  • Search a document before answering.
  • Call a calculator tool to solve a math query.
  • Combine tools to plan a multi-step process.

Think of it as giving your LLM superpowers to interact with the world more intelligently.

Supported Agent Types in LangChain NuGet

The LangChain NuGet package (for C#/.NET devs) supports several agent types. The most common one is the ReActAgent, based on the ReAct (Reasoning + Acting) pattern. Here’s what you need to know:

ReActAgent

  • Use case: Ideal for reasoning-intensive tasks.
  • How it works: Alternates between thought, action, observation, and final answer.
  • Strengths: Great for multi-step workflows with intermediate steps.

Other agent types (like Zero-Shot Agents) may be introduced in future releases, but ReActAgent remains the best supported in .NET as of now.

Setting Up AgentExecutor

To run an agent, you need an AgentExecutor. This component manages the loop where the agent evaluates, picks a tool, runs it, and decides next steps.

Here’s how to set it up:

var tools = new List<ITool> { new CalculatorTool(), new SearchTool() };
var agent = new ReActAgent(
    new OpenAIModel("your-api-key"),
    tools
);

var executor = new AgentExecutor(agent);
var result = await executor.RunAsync("What is 13 * sqrt(144)?");
Console.WriteLine(result);

Explanation:

  • CalculatorTool and SearchTool are tools that the agent can call.
  • The agent uses OpenAI to interpret the query, decide it needs a square root calculation (which equals 12), then multiply it with 13.
  • RunAsync handles the agent loop: thought, action (use CalculatorTool), observe, and finally present the answer.

You can add tracing for deeper insight:

executor.EnableVerboseLogging = true;

This helps visualize step-by-step how the agent made decisions, which is essential for debugging and optimization.

Custom Tool Use Case: Support Bot That Picks Tools Dynamically

Imagine you’re building a customer support bot. You want it to:

  • Respond from a knowledge base.
  • Escalate to a human if it doesn’t know.
  • Log a ticket in an external system.

Here’s how to implement this scenario using custom tools.

Define Tools:

public class KnowledgeBaseTool : ITool {
    public string Name => "KnowledgeLookup";
    public async Task<string> RunAsync(string input) {
        // Simulate KB search
        return "Found relevant article: Resetting your password";
    }
}

public class EscalateTool : ITool {
    public string Name => "EscalateToHuman";
    public async Task<string> RunAsync(string input) {
        return "Your request has been escalated to a support representative.";
    }
}

public class TicketTool : ITool {
    public string Name => "CreateTicket";
    public async Task<string> RunAsync(string input) {
        return "Ticket created with ID #1234 for: " + input;
    }
}

Setup Agent with Custom Tools:

var tools = new List<ITool> {
    new KnowledgeBaseTool(),
    new EscalateTool(),
    new TicketTool()
};

var agent = new ReActAgent(new OpenAIModel("your-api-key"), tools);
var executor = new AgentExecutor(agent);
var response = await executor.RunAsync("I can't log in to my account, what should I do?");
Console.WriteLine(response);

Explanation:

  • If the KB tool has a match, it responds.
  • If not, the agent may escalate or create a ticket based on context.
  • This makes your bot adaptive and intelligent without hardcoding control logic.

Error Handling and Control Flow: Fallbacks, Retries, and Structured Outputs

Things go wrong. APIs fail. Answers are ambiguous. Good agents need control mechanisms.

Tool-Level try-catch Example:

public class ResilientSearchTool : ITool {
    public string Name => "SafeSearch";
    public async Task<string> RunAsync(string input) {
        try {
            // Simulate external API call
            throw new TimeoutException();
        } catch (Exception ex) {
            return "Search failed: " + ex.Message;
        }
    }
}

Agent-Level Fallback:

You can use a wrapper to provide fallback responses if a tool fails or throws:

public class FallbackTool : ITool {
    public string Name => "FallbackResponder";
    public async Task<string> RunAsync(string input) {
        return "Unable to complete your request right now. Please try again later.";
    }
}

Then use it as last resort in your tools list.

Retry Logic Example:

Implement retry inside your tool:

public class RetryTool : ITool {
    public string Name => "RetryableCall";
    public async Task<string> RunAsync(string input) {
        int attempts = 0;
        while (attempts < 3) {
            try {
                // Try the risky operation
                return "Success on attempt " + (attempts + 1);
            } catch {
                attempts++;
                await Task.Delay(500);
            }
        }
        return "All retry attempts failed.";
    }
}

Structured Output Pattern:

public class StructuredDataTool : ITool {
    public string Name => "CurrencyFetcher";
    public async Task<string> RunAsync(string input) {
        return JsonSerializer.Serialize(new {
            Rate = 1.08,
            Currency = "USD",
            Base = "EUR"
        });
    }
}

This allows downstream systems to parse and react to output predictably.


FAQ: Working with Agents in LangChain NuGet

Can I write my own agent logic?

Yes! You can implement the IAgent interface to define custom reasoning workflows.

Are tools reusable across agents?

Absolutely. Tools are plug-and-play and can be shared across multiple agents.

How do I debug agent behavior?

Enable verbose logging in AgentExecutor or trace execution steps in the console.


Conclusion: Agents Unlock the Real Power of LLMs in .NET

By using agents with LangChain NuGet, you’re not just generating text—you’re creating autonomous, decision-making entities that can use tools, handle uncertainty, and act in the real world. This takes your AI apps from static responses to dynamic problem-solving machines.

So, what tool will your agent choose next?

Leave a Reply

Your email address will not be published. Required fields are marked *