Skip to main content
Source Code: cpp/examples/wifi_agent.cpp — single-file, self-contained agent (~1,100 lines including 13 tools and a custom TUI).
Platform: Windows (PowerShell network commands). Compiles on Linux for CI but tools require Windows to return real data. Prerequisite: Lemonade Server running with a model loaded.

What This Agent Does

The Wi-Fi Troubleshooter is an AI agent that acts like an IT support specialist. When a user says “my internet isn’t working”, it runs a systematic diagnostic chain, reads real output from your machine, reasons about what’s wrong, and applies fixes automatically.
  • Runs real commands — every tool executes an actual PowerShell command and returns real output
  • LLM-driven reasoning — no hardcoded if/else tree; the LLM reads each result and decides the next step
  • Pure C++ — no Python, no MCP subprocess, no external dependencies. Just a compiled binary talking to a local LLM
  • Fully local — the LLM runs on your AMD hardware via Lemonade Server. No data leaves your machine

Demo

Full diagnostic run on a Ryzen AI PC: the agent calls 5 tools in sequence, reads real network output, and reports status — all powered by a local LLM on the NPU.

Quick Start

1

Build

cd cpp
cmake -B build -G "Visual Studio 17 2022" -A x64
cmake --build build --config Release
Binary: cpp\build\Release\wifi_agent.exe
2

Start Lemonade Server

lemonade-server serve
The agent connects to http://localhost:8000/api/v1 by default.
3

Run the agent (run as admin for fix tools)

cpp\build\Release\wifi_agent.exe
Try these prompts:
You: Run a full network diagnostic.
You: My Wi-Fi is connected but I can't browse the web.
You: Check if DNS is working and fix it if not.

Architecture

The agent loop is a conversation between your C++ code and the LLM: Your C++ code provides the tools (what the agent can do), the system prompt provides the strategy (what it should do), and the LLM connects the two.

How It Works

The agent subclasses gaia::Agent with three pieces: a config, registered tools, and a system prompt.
class WiFiTroubleshooterAgent : public gaia::Agent {
public:
    explicit WiFiTroubleshooterAgent(const std::string& modelId)
        : Agent(makeConfig(modelId)) {
        setOutputHandler(std::make_unique<CleanConsole>());  // custom TUI
        init();  // calls registerTools() + composes system prompt
    }

protected:
    std::string getSystemPrompt() const override {
        return R"(You are an expert Windows network troubleshooter...
            // Diagnostic sequence, fix instructions, output format
        )";
    }

    void registerTools() override {
        // 13 tools: 7 diagnostic + 6 fix (see tables below)
        toolRegistry().registerTool("check_adapter", ...);
        toolRegistry().registerTool("ping_host", ...);
        // ...
    }

private:
    static gaia::AgentConfig makeConfig(const std::string& modelId) {
        gaia::AgentConfig config;
        config.maxSteps = 20;   // up to 20 tool calls per query
        config.modelId = modelId;
        return config;
    }
};
Each tool is a C++ lambda that runs a PowerShell command and returns the output:
toolRegistry().registerTool(
    "check_adapter",                          // name the LLM calls
    "Show Wi-Fi adapter status including "    // description the LLM reads
    "SSID, signal strength, and state.",
    [](const gaia::json& /*args*/) -> gaia::json {
        std::string output = runShell("netsh wlan show interfaces");
        return {{"tool", "check_adapter"}, {"output", output}};
    },
    {}  // no parameters
);
Tools with parameters validate input before passing to the shell:
toolRegistry().registerTool(
    "ping_host",
    "Ping a specific host and return connection status.",
    [](const gaia::json& args) -> gaia::json {
        std::string host = args.value("host", "");
        if (host.empty()) return {{"error", "host parameter is required"}};
        if (!isSafeShellArg(host)) return {{"error", "Invalid host"}};
        std::string output = runShell("Test-NetConnection -ComputerName " + host + " | ConvertTo-Json");
        return {{"tool", "ping_host"}, {"output", output}};
    },
    {{"host", gaia::ToolParamType::STRING, true, "Hostname or IP to ping"}}
);
All string arguments from the LLM are validated with isSafeShellArg() to reject shell metacharacters before constructing commands. See the Custom Agent guide for details on tool registration patterns.

Tool Reference

Diagnostic Tools (Read-Only)

ToolWhat It RunsParameters
check_adapternetsh wlan show interfacesnone
check_wifi_driversnetsh wlan show driversnone
check_ip_configipconfig /allnone
test_dns_resolutionResolve-DnsNamehostname (optional)
test_internetTest-NetConnection 8.8.8.8 -Port 443none
test_bandwidthCloudflare CDN parallel download/uploadnone
ping_hostTest-NetConnection <host>host (required)

Fix Tools (Write Operations)

ToolWhat It RunsWhen to Use
flush_dns_cacheClear-DnsClientCacheStale DNS entries
set_dns_serversSet-DnsClientServerAddressDefault DNS not responding
renew_dhcp_leaseipconfig /release + /renewNo IP or APIPA address
toggle_wifi_radioWindows Radio Management APIAdapter shows “Software Off”
enable_wifi_adapterEnable-NetAdapterAdapter administratively disabled
restart_wifi_adapterDisable-NetAdapter + Enable-NetAdapterAdapter stuck in bad state

Diagnostic Flow

The system prompt teaches the LLM this decision tree. Each fix loops back to re-verify before declaring success:

Extending the Agent

Add a tool inside registerTools() and update the system prompt:
toolRegistry().registerTool(
    "check_firewall",
    "Check if Windows Firewall is blocking outbound connections.",
    [](const gaia::json& /*args*/) -> gaia::json {
        std::string output = runShell(
            "Get-NetFirewallProfile | Select-Object Name, Enabled | ConvertTo-Json"
        );
        return {{"tool", "check_firewall"}, {"output", output}};
    },
    {}
);
The framework handles everything else — the new tool appears in the LLM’s system prompt automatically.

Next Steps