arcus

module
v0.0.0-...-a000efe Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: MIT

README ยถ

โ„๏ธ Arcus ADK

A standard, easy-to-use Agent Development Kit for Go.

Go Reference Go Version Go Report Card Dependencies Status

OpenAI Anthropic DeepSeek

English ยท ็ฎ€ไฝ“ไธญๆ–‡


Arcus gives Go applications one clean way to talk to large language models. It pairs native, protocol-faithful clients for each provider with a unified chat layer that normalizes requests, responses, streaming, and tool calls โ€” so business code is written once and runs against OpenAI, Anthropic, or DeepSeek without change.

It is built on the database/sql driver model: the core imports no provider package, and applications wire providers in with blank imports. The whole kit is standard-library only โ€” zero third-party dependencies.

cli := chat.New()
_ = cli.Use(adapter.OpenAI, openai.Config{APIKey: key})

msg, _ := cli.Chat(ctx, adapter.Request{
    Provider: adapter.OpenAI,
    Data:     &openai.Request{Model: "gpt-4o", Messages: []openai.Message{openai.UserMessage("hi")}},
})
out, _ := chat.Result(msg)
fmt.Println(out.Text)

โœจ Why Arcus

  • Native packages, no leaky abstraction. pkg/openai, pkg/anthropic, and pkg/deepseek each speak their wire protocol directly (content blocks, SSE events, reasoning_content, โ€ฆ). Use them standalone, or through the unified layer โ€” your choice, not the framework's.
  • One entry point, three providers. pkg/chat routes a request by Provider and hands back a normalized result. Switching models is a config change, not a rewrite.
  • Driver-registry wiring. Providers register themselves from init(); you enable them with blank imports. No build tags, no central switch statement, no edits to the core to add a backend.
  • Tools that work everywhere. Declare a tool once against a tiny func(ctx, json.RawMessage) (*tool.Result, error) interface. Each driver renders it into that provider's native tool shape โ€” the same tool set drives function-calling on all three.
  • Streaming, normalized. A single channel of typed chunks (text, thinking, tool_call, stop, usage, error) regardless of backend.
  • Config-file friendly. Provider configs are plain structs with snake_case JSON tags; Use also accepts raw JSON decoded straight from your app config.

๐Ÿ“ฆ Install

go get github.com/arbureva/arcus

Requires Go 1.25+.

๐Ÿ—‚ Package layout

Package Role
pkg/chat Unified entry point โ€” Client, Chat, ChatStream, Result, chunk helpers. Imports no provider package.
pkg/chat/drivers/{openai,anthropic,deepseek} Provider bridges. Blank-import to enable; each registers itself from init().
pkg/adapter Neutral envelopes โ€” Request, MessageAdapter, ChunkMessageAdapter, and the Provider constants.
pkg/openai ยท pkg/anthropic ยท pkg/deepseek Native protocol clients, usable on their own.
pkg/tool Provider-agnostic tool abstraction โ€” Tool, Func, Reflect, Set, Result.
pkg/agent The agent loop โ€” Run / RunStream, hooks, sub-agents via AsTool. Imports no provider package.
pkg/agent/transcripts/{openai,anthropic,deepseek} Native-message transcripts. Blank-import to enable; each registers itself from init().
pkg/toolbox Progressive disclosure โ€” fold tool groups behind one meta-tool the model opens on demand.
pkg/skill Skills โ€” instruction packs with optional tools, from code (skill.New) or SKILL.md directories (skill.LoadDir).
pkg/mcp MCP client (stdio & Streamable HTTP) โ€” remote tools surfaced as ordinary tool.Tool values.
pkg/cli Command-line programs as tools โ€” cli.Command (argv, allowlisted) and cli.Shell.
pkg/ecode Shared sentinel errors.

๐Ÿš€ Quick start

Configure providers

Enable each backend with a blank import, then Use it:

import (
    "github.com/arbureva/arcus/pkg/adapter"
    "github.com/arbureva/arcus/pkg/chat"
    "github.com/arbureva/arcus/pkg/openai"

    _ "github.com/arbureva/arcus/pkg/chat/drivers/openai"   // registers the openai driver
    _ "github.com/arbureva/arcus/pkg/chat/drivers/anthropic"
    _ "github.com/arbureva/arcus/pkg/chat/drivers/deepseek"
)

cli := chat.New()
_ = cli.Use(adapter.OpenAI, openai.Config{APIKey: key, BaseURL: "https://api.openai.com/v1"})
Non-streaming
msg, _ := cli.Chat(ctx, adapter.Request{
    Provider: adapter.OpenAI,
    Data:     &openai.Request{Model: "gpt-4o", Messages: []openai.Message{openai.UserMessage("Introduce yourself.")}},
})
out, _ := chat.Result(msg) // *chat.Completion: Text / Reasoning / ToolCalls / StopReason / Usage / Raw
fmt.Println(out.Text)
Streaming
ch, _ := cli.ChatStream(ctx, adapter.Request{Provider: adapter.OpenAI, Data: req})
for c := range ch {
    switch c.Kind {
    case chat.ChunkText:
        fmt.Print(chat.MustText(&c))
    case chat.ChunkThinking:
        fmt.Print(chat.MustThinking(&c))
    case chat.ChunkUsage:
        fmt.Printf("\nUsage: %d\n", chat.MustUsage(&c).TotalTokens)
    case chat.ChunkError:
        return chat.MustError(&c)
    }
}
Tool calling

Declare a tool once; the same Set is advertised to the model and used to dispatch its calls:

type weatherArgs struct {
    City string `json:"city" desc:"City name, e.g. Shanghai"`
}

tools := tool.NewSet(tool.Func("get_weather", "Get the current weather for a city",
    tool.Reflect(weatherArgs{}),
    func(ctx context.Context, raw json.RawMessage) (*tool.Result, error) {
        var a weatherArgs
        if err := json.Unmarshal(raw, &a); err != nil {
            return tool.Errf("bad arguments: %v", err), nil
        }
        return tool.Textf("It is 24ยฐC and sunny in %s.", a.City), nil
    }))

msg, _ := cli.Chat(ctx, adapter.Request{
    Provider: adapter.OpenAI,
    Data:     &openai.Request{Model: "gpt-4o", Messages: msgs},
    Tools:    tools.RequestTools(),
})
out, _ := chat.Result(msg)
for _, call := range out.ToolCalls {
    res, _ := tools.Invoke(ctx, call.Name, call.Args)
    // feed res.Content back as the provider's tool / tool_result message, then call again
}

chat.Result normalizes tool calls into []chat.ToolCall for every provider, so your dispatch loop is identical across backends. Only the follow-up message reconstruction is provider-shaped (OpenAI/DeepSeek tool messages vs. Anthropic tool_use / tool_result blocks).

Agent

Or skip the manual loop entirely. pkg/agent runs the call โ†’ dispatch โ†’ append cycle until the model stops asking for tools. Message history lives in a Transcript โ€” a per-provider implementation that stores native messages (so Anthropic thinking blocks, DeepSeek reasoning_content, and tool-call turns all round-trip exactly), registered the same way chat drivers are:

import (
    "github.com/arbureva/arcus/pkg/agent"
    _ "github.com/arbureva/arcus/pkg/agent/transcripts/openai" // like drivers: blank-import to enable
)

bot := agent.New(cli, agent.WithTools(tools), agent.WithMaxSteps(8))

tr, _ := agent.NewTranscript(adapter.OpenAI, &openai.Request{
    Model:    "gpt-4o",
    Messages: []openai.Message{openai.SystemMessage("Be terse.")},
})

tr.User("What's 2+2, and what time is it?")
out, _ := bot.Run(ctx, tr)
fmt.Println(out.Text()) // out.Steps / out.Usage hold the full trace

You always know exactly which native types you injected โ€” the seed request is yours, and tr.Messages() hands the history back in the provider's own message type. agent.RunStream does the same loop over the normalized chunk channel, emitting tool_result chunks between turns.

Multi-agent is one line: agent.AsTool(name, desc, subAgent, seedFn) wraps a specialist agent as a tool.Tool; every delegation gets a fresh transcript from seedFn, so contexts stay isolated.

Progressive disclosure, skills, MCP, CLI

Everything above the loop is still just tool.Tool:

// Fold tool groups behind one meta-tool; the model opens what it needs.
box := toolbox.New().
    Add(clock).                                  // always visible
    Namespace("git", "Read-only git inspection", // folded until opened
        toolbox.Tools(gitTool), toolbox.Instructions("Prefer --stat over full diffs.")).
    AddSkills(skills)                            // each skill folds into its own namespace
session := box.Clone()                           // per-conversation open/closed state
bot := agent.New(cli, agent.WithTools(session))  // *toolbox.Box satisfies agent.Tools

// Skills: instruction packs, from code or from SKILL.md directories.
skills, _ := skill.LoadDir("skills") // each subdir with a SKILL.md becomes one skill

// MCP servers: remote tools indistinguishable from local ones.
srv, _ := mcp.Dial(ctx, mcp.Stdio("npx", "-y", "@modelcontextprotocol/server-filesystem", "."))
defer srv.Close()
remote, _ := srv.ToolSet(ctx) // *tool.Set โ€” plug straight into an agent

// Command-line programs: argv in, stdout/stderr back, no shell in between.
git := cli.Command("git", "Inspect the repo.",
    cli.AllowFirstArg("status", "log", "diff"), cli.Timeout(30*time.Second))

๐Ÿ“‚ Examples

Runnable examples live under example/:

  • example/chat โ€” non-streaming chat, one file per provider.
  • example/chat-stream โ€” streaming, one file per provider.
  • example/chat-tool โ€” the two-turn tool-calling loop, one file per provider.
  • example/agent โ€” an interactive REPL around agent.Run, with hooks narrating every tool round-trip.
  • example/agent-multi โ€” multi-agent coordination: a specialist wrapped by agent.AsTool.
  • example/mcp โ€” an agent driving the MCP filesystem server over stdio.
  • example/skill-toolbox โ€” skills loaded from SKILL.md plus toolbox progressive disclosure.
  • example/http โ€” the integration template: arcus inside an HTTP service. An OpenAI-compatible layer (/v1/chat/completions โ€” point Cherry Studio, LobeChat, or any OpenAI client at it; the "model" name selects the service) plus a session-owning API, four services (skills-only, cli-only, mcp-only, multi-agent team), config-file startup, SSE streaming. Start here if you're wiring the SDK into your own backend.

๐Ÿ—บ Roadmap

Arcus aims to be a complete, standard ADK for Go. Every higher-level capability wraps the same tool.Tool interface and chat entry point โ€” adopting them requires no change to code already written against Arcus.

  • Native provider clients โ€” OpenAI ยท Anthropic ยท DeepSeek
  • Unified chat entry point with driver registry
  • Streaming with normalized chunks
  • Tool calling
  • Agent โ€” the tool-calling loop with native-message transcripts, hooks, streaming, and sub-agents (pkg/agent)
  • MCP โ€” Model Context Protocol tools as first-class tool.Tool values, stdio & Streamable HTTP (pkg/mcp)
  • Skills โ€” instruction packs with tools, from code or SKILL.md directories (pkg/skill), foldable via pkg/toolbox
  • CLI โ€” command-line programs as tools (pkg/cli), plus a REPL example for running and inspecting agents
  • Structured output helpers
  • More providers (Gemini, Qwen, โ€ฆ)

๐Ÿ“„ License

Released under the MIT License.


Directories ยถ

Path Synopsis
example
agent command
Command agent is a minimal REPL around agent.Run: type a line, watch the model think / call tools / answer, repeat.
Command agent is a minimal REPL around agent.Run: type a line, watch the model think / call tools / answer, repeat.
agent-multi command
Command agent-multi shows multi-agent coordination the arcus way: a sub-agent is just another tool.
Command agent-multi shows multi-agent coordination the arcus way: a sub-agent is just another tool.
chat command
chat-stream command
chat-tool command
http command
Command http ๆผ”็คบๅฆ‚ไฝ•ๆŠŠ arcus ๆŽฅ่ฟ›ไฝ ่‡ชๅทฑ็š„ HTTP ๆœๅŠกใ€‚
Command http ๆผ”็คบๅฆ‚ไฝ•ๆŠŠ arcus ๆŽฅ่ฟ›ไฝ ่‡ชๅทฑ็š„ HTTP ๆœๅŠกใ€‚
mcp command
Command mcp connects to an MCP server and hands its tools to an agent.
Command mcp connects to an MCP server and hands its tools to an agent.
skill-toolbox command
Command skill-toolbox demonstrates progressive disclosure.
Command skill-toolbox demonstrates progressive disclosure.
pkg
agent
Package agent implements a provider-agnostic ReAct loop on top of pkg/chat and pkg/tool: the model is called, its tool calls are dispatched through a tool source, the results are folded back into the conversation, and the loop repeats until the model produces a final answer (or the step budget runs out).
Package agent implements a provider-agnostic ReAct loop on top of pkg/chat and pkg/tool: the model is called, its tool calls are dispatched through a tool source, the results are folded back into the conversation, and the loop repeats until the model produces a final answer (or the step budget runs out).
agent/transcripts/anthropic
Package anthropic registers the Anthropic transcript for Arcus's agent layer.
Package anthropic registers the Anthropic transcript for Arcus's agent layer.
agent/transcripts/deepseek
Package deepseek registers the DeepSeek transcript for Arcus's agent layer.
Package deepseek registers the DeepSeek transcript for Arcus's agent layer.
agent/transcripts/openai
Package openai registers the OpenAI transcript for Arcus's agent layer.
Package openai registers the OpenAI transcript for Arcus's agent layer.
anthropic
Package anthropic implements the Anthropic Messages API natively.
Package anthropic implements the Anthropic Messages API natively.
chat
Package chat is Arcus's unified chat entry point.
Package chat is Arcus's unified chat entry point.
chat/drivers/anthropic
Package anthropic registers the Anthropic driver for Arcus's chat layer.
Package anthropic registers the Anthropic driver for Arcus's chat layer.
chat/drivers/deepseek
Package deepseek registers the DeepSeek driver for Arcus's chat layer.
Package deepseek registers the DeepSeek driver for Arcus's chat layer.
chat/drivers/openai
Package openai registers the OpenAI driver for Arcus's chat layer.
Package openai registers the OpenAI driver for Arcus's chat layer.
cli
Package cli exposes command-line programs to the model as tool.Tool values โ€” the third execution boundary next to in-process functions (tool.Func) and remote servers (mcp).
Package cli exposes command-line programs to the model as tool.Tool values โ€” the third execution boundary next to in-process functions (tool.Func) and remote servers (mcp).
deepseek
Package deepseek implements the DeepSeek chat completions API natively.
Package deepseek implements the DeepSeek chat completions API natively.
mcp
Package mcp is a zero-dependency Model Context Protocol client that surfaces remote tools as first-class tool.Tool values.
Package mcp is a zero-dependency Model Context Protocol client that surfaces remote tools as first-class tool.Tool values.
openai
Package openai implements the OpenAI Chat Completions API natively.
Package openai implements the OpenAI Chat Completions API natively.
skill
Package skill packages reusable capabilities as prompt + tools bundles.
Package skill packages reusable capabilities as prompt + tools bundles.
tool
Package tool is Arcus's single tool abstraction.
Package tool is Arcus's single tool abstraction.
toolbox
Package toolbox implements progressive disclosure for tool injection.
Package toolbox implements progressive disclosure for tool injection.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL