An LLM agent architecture written entirely in native Zig.
First and foremost, this project was built as a hands-on way to learn Zig. I wanted a project where I couldn't just slide by on basic syntax but also wasn't so complex it would take forever to get working. Building an LLM agent framework fit the bill. An agent has to deal with nested dynamic JSON, HTTP connections, and complex memory lifetimes—forcing me to jump straight into the deep end of how Zig handles memory and compile-time evaluation.
Sure, writing LLM agent loops is a solved problem and it's completely unnecessary to do this in Zig... but it was fun to figure out and it runs incredibly fast.
- Type-safe Comptime Tool Registration: Register standard Zig functions as agent tools. The compiler checks that your function parameters match the tool's descriptor structure using Zig's compile-time reflection.
- ACP Server Integration: Built-in JSON-RPC stdio server supporting the Agent Connection Protocol (ACP) for external client/IDE integration (
--acp). - Context-Aware Tools & State Management: Support for custom user-data contexts (
Tool.initWithContext), session state tracking, and dynamic context injections across turns. - Built-in Agent Tools: Standardized built-in tools (such as
agent.Tool.BuiltIn.Todofor planning and tracking multi-step execution). - Incremental Streaming & Response Accumulator: Built-in support for streaming model responses chunk-by-chunk with ANSI terminal markdown formatting.
- Asynchronous & Concurrent Tool Execution: Built on top of Zig's native non-blocking I/O event loop (
std.Io). Tools run concurrently viaIo.Select. - Async Zero-Dependency HTTP Client: Direct communication with LLM backends asynchronously using Zig's native
std.http.Client. - Explicit Memory Control: Everything uses Zig's explicit allocator pattern with minimal memory footprint (~5MB overhead).
COMA uses Zig's comptime capabilities to make tool registration type-safe and boilerplate-free:
// Define a standard Zig function
fn getWeather(allocator: std.mem.Allocator, zip_code: i64) ![]const u8 {
return try std.fmt.allocPrint(allocator, "Weather in {d}: Sunny, 72°F", .{zip_code});
}
// Register it with the Agent
const weather_tool = Tool.init(.{
.name = "get_weather",
.description = "Get the current weather for a given zip code.",
.parameters = &.{
.{
.name = "zip_code",
.type = .integer,
.required = true,
.description = "The 5-digit zip code.",
},
},
}, getWeather);You can also attach custom context to tools using Tool.initWithContext or include built-in tools like agent.Tool.BuiltIn.Todo.
To use COMA in your own project, initialize a provider and the agent loop:
var http_client: std.http.Client = .{ .allocator = allocator, .io = io };
var gemini_client = try provider.Gemini.init(allocator, &http_client, api_key);
var client = gemini_client.provider();
const session_config: types.SessionConfig = .{
.model = selected_model,
.system_prompt = "You are a helpful assistant.",
.tools = &[_]Tool{ weather_tool, agent.Tool.BuiltIn.Todo },
};
var session = try Session.init(allocator, io, client, session_config);
defer session.deinit();
const result = try session.executeTurn(.{ .prompt = "What is the weather in 90210?" });src/acp/: Implementation of the Agent Connection Protocol (ACP) JSON-RPC server and session storage.src/agent/: Core agent logic, session state management, tool execution, and built-in tools.src/llm/: Generic LLM provider interfaces and message schemas.src/provider/: Concrete implementations for LLM services (e.g., Google Gemini).src/testing/: Mocks and utilities for testing without network calls.src/MarkdownRendering.zig: Dedicated module for ANSI-formatted streaming markdown output in the terminal.src/main.zig: CLI entry point, supporting interactive chat and ACP server mode.
COMA is tested and built with Zig 0.16.0.
Get an API key from Google AI Studio. You can set it in your environment:
export GEMINI_API_KEY="your-api-key"Or create a .env file in the root of the project:
GEMINI_API_KEY="your-api-key"To build and start the CLI agent interface:
zig build runTo run COMA as an ACP server over stdio for external client integrations:
zig build run -- --acpYou can test the ACP server with coma-acp-repl.
To compile and execute all tests:
zig build testTo generate a test coverage report (requires kcov):
zig build coverage- ACP Server Integration: Build an ACP server into the agent for external client control.
- Agents.md / SKILLS: Define agent behaviors in Markdown files and load them into context automatically.
- More Providers: Support for Anthropic, OpenAI, Ollama, etc.
- Vector DB Client: Persistent long-term memory.
Distributed under the Apache License 2.0. See LICENSE for more information.
