AI Code Review Image

In Part 1, we built a simple AI-powered code review system using Git diff and an LLM.

That approach works well for basic automation, but modern AI systems are evolving into something much more powerful: AI Agents.

Instead of sending one large prompt to an LLM, AI Agents:

In this article, we’ll build a modular AI Agent in C# capable of:

What Is an AI Agent?

Traditional LLM workflow:

Prompt → LLM → Response

AI Agent workflow:

Task
  ↓
Planner
  ↓
Skills / Tools
  ↓
LLM Reasoning
  ↓
Actions
  ↓
Validation
  ↓
Final Output

This makes the system far more intelligent and extensible.

What Are Skills?

Skills are modular capabilities an AI Agent can execute.

Examples:

SkillPurpose
Git Diff SkillExtract code changes
Security Review SkillDetect vulnerabilities
Performance Review SkillFind inefficient code
Fix Generator SkillGenerate fixes
Unit Test SkillCreate test cases
Validation SkillVerify generated code

Instead of one giant prompt, the agent orchestrates multiple focused operations.

Agent Architecture

Developer Request
        ↓
AI Agent
        ↓
Skill Orchestrator
   ┌──────────────┐
   │ Git Diff     │
   │ Code Review  │
   │ Fix Generator│
   │ Validation   │
   └──────────────┘
        ↓
LLM
        ↓
Review + Fix Output

Step 1 — Define a Skill Contract

public interface IAiSkill
{
    string Name { get; }

    Task<string> ExecuteAsync(AiContext context);
}

Every skill follows a standard contract.

Step 2 — Create Shared Context

public class AiContext
{
    public string GitDiff { get; set; }

    public List<string> Findings { get; set; }
        = new();
}

This shared context acts like agent memory.

Step 3 — Implement Git Diff Skill

public class GitDiffSkill : IAiSkill
{
    public string Name => "GitDiff";

    public async Task<string> ExecuteAsync(AiContext context)
    {
        string diff = await GitHelper.GetDiffAsync();

        context.GitDiff = diff;

        return diff;
    }
}

Step 4 — Implement Review Skill

public class CodeReviewSkill : IAiSkill
{
    private readonly IChatClient _chatClient;

    public string Name => "CodeReview";

    public CodeReviewSkill(IChatClient chatClient)
    {
        _chatClient = chatClient;
    }

    public async Task<string> ExecuteAsync(AiContext context)
    {
        string prompt = $@"
Review this C# git diff.

Focus on:
- Bugs
- Security
- Performance

Diff:
{context.GitDiff}
";

        string result =
            await _chatClient.GetResponseAsync(prompt);

        context.Findings.Add(result);

        return result;
    }
}

Step 5 — Add Fix Generation Skill

public class FixSuggestionSkill : IAiSkill
{
    private readonly IChatClient _chatClient;

    public string Name => "FixSuggestion";

    public FixSuggestionSkill(IChatClient chatClient)
    {
        _chatClient = chatClient;
    }

    public async Task<string> ExecuteAsync(AiContext context)
    {
        string findings =
            string.Join("\n", context.Findings);

        string prompt = $@"
Generate corrected C# code fixes
for these findings:

{findings}
";

        return await _chatClient.GetResponseAsync(prompt);
    }
}

Step 6 — Build Agent Orchestrator

public class CodeReviewAgent
{
    private readonly List<IAiSkill> _skills;

    public CodeReviewAgent(List<IAiSkill> skills)
    {
        _skills = skills;
    }

    public async Task RunAsync()
    {
        var context = new AiContext();

        foreach (var skill in _skills)
        {
            Console.WriteLine(
                $"Running: {skill.Name}");

            var result =
                await skill.ExecuteAsync(context);

            Console.WriteLine(result);
        }
    }
}

Final Thoughts

Part 2 introduced a modular AI Agent architecture.

Instead of:

we now have:

However, prompts are still embedded inside C# code.

In Part 3, we’ll externalize intelligence into reusable markdown-based skills.md files and dynamically load them into the agent.