AIMA (AI-Inference-Managed-by-AI): a Go binary that manages AI inference on edge devices. It detects hardware, resolves optimal configs from a YAML knowledge base, generates K3S Pod YAML, and exposes 56 MCP tools for AI Agents to operate everything. This project is 100% developed by Claude Code.
Tech: Go (no CGO), K3S, HAMi, SQLite (modernc.org/sqlite), MCP (JSON-RPC 2.0), Cobra CLI, log/slog.
Design docs: design/ARCHITECTURE.md (system architecture), design/PRD.md, design/MRD.md.
Current development line: v0.3. Latest product release: v0.3.1.
This project uses a develop-based release flow with a single declared development line.
The development line is recorded in internal/buildinfo/series.txt. Right now that value is v0.3.
master ──●──── tag v0.2.0 ───────────────── tag v0.2.1 ──
\ /
develop ───●──●──●──●──feature──●──●──●──●──●
\ /
feat/xxx──●
| Branch | Purpose | Merges to |
|---|---|---|
master |
Production releases only. Every product release tag is created here. | — |
develop |
Main integration branch for the current development line (v0.3). |
master (via release/*) |
feat/<name> |
New feature branch from develop. |
develop |
fix/<name> |
Bug fix branch from develop. |
develop |
docs/<name> |
Documentation-only branch from develop. |
develop |
release/<ver> |
Release preparation branch for a concrete SemVer release such as release/v0.2.1. |
master + develop |
hotfix/<ver> |
Urgent production fix from master for a concrete SemVer release. |
master + develop |
- Product version — SemVer release tag
vMAJOR.MINOR.PATCH. Only annotated tags in this exact format count as AIMA releases. - Development line — the active train for
developand feature work, currentlyv0.3. - Development build version —
<development-line>-dev, for examplev0.3-dev. The exact commit is carried separately in build metadata. - MCP protocol version — protocol compatibility only (for example
2024-11-05), not the AIMA release number. - DB/import schema version — internal compatibility counters (
PRAGMA user_version,schema_version), never product release numbers. - Catalog/component version — upstream dependency versions stored in YAML, not AIMA release numbers.
- Asset bundle tag — must use a separate namespace such as
assets/<date>orbundle/<name>/<date>, nevervX.Y.Z-*.
- Use exactly one product release tag per release commit.
- Product release tags must be annotated.
- Only
vX.Y.Ztags are product releases. - Do not create product-like suffix tags such as
v0.1.0-imagesor duplicate aliases such asv0.0.1-metax. - Codenames belong in the GitHub release title/notes, not in the tag name.
- 0.y.z minor — user-visible capability additions, new MCP tools, new runtime behavior, or intentional CLI/MCP contract changes.
- 0.y.z patch — bug fixes, catalog corrections, packaging fixes, and doc updates without intentional capability expansion.
- 1.0.0 — CLI/MCP contract and core deployment workflow are stable enough to be treated as a compatibility baseline.
internal/buildinfo/series.txtis the single source of truth for the active development line.- As long as the team is iterating inside the current line, keep it at
v0.3. - All non-tagged builds from
develop,feat/*,fix/*,docs/*, andrelease/*reportv0.3-dev. - When starting the next line, update
internal/buildinfo/series.txtindevelopfirst, for examplev0.3→v0.4. - Product releases remain exact SemVer tags such as
v0.2.1orv0.3.0.
# Start a new feature
git checkout develop
git pull origin develop
git checkout -b feat/my-feature
# ... develop, commit ...
# Push and create PR to develop
git push -u origin feat/my-feature
# Create PR: feat/my-feature → develop
# After PR merged, clean up
git checkout develop
git pull origin develop
git branch -d feat/my-feature# Prepare a concrete release from the current development line
git checkout develop
git pull origin develop
git checkout -b release/v0.2.1
# Final fixes, changelog update, validation
# Merge to master and tag the release
git checkout master
git merge --no-ff release/v0.2.1
git tag -a v0.2.1 -m "Release v0.2.1"
git push origin master --tags
# Back-merge release fixes into develop
git checkout develop
git merge --no-ff release/v0.2.1
git push origin developmake build
./build/aima version
make version-audit
make bundle-tagmake build injects Version, GitCommit, and BuildTime into internal/buildinfo.
Only exact vX.Y.Z tags are treated as releases. Non-tagged builds report
<development-line>-dev, and the current line comes from internal/buildinfo/series.txt.
make version-audit prints product tags, legacy pseudo-release tags, and known migration targets.
make bundle-tag creates the local replacement bundle tag bundle/stack/2026-02-26 without pushing it.
- Never force-push to
master. - Branch new work from
develop, not frommaster. - Keep
internal/buildinfo/series.txtatv0.3until the team explicitly starts the next line. - Only
vX.Y.Zannotated tags are product releases. - Do not invent new product-like suffix tags for assets, images, or vendor-specific bundles.
- Release through
release/<ver>and tag onmaster.
Every line of Go code is a liability. The goal is the smallest possible binary that glues mature external tools (K3S, HAMi, containerd, SQLite) together with YAML knowledge.
- Before writing code, ask: "Can this be a YAML knowledge file instead?"
- Before adding a function, ask: "Does an existing tool/library already do this?"
- Before adding an abstraction, ask: "Do I have 3+ concrete uses, or am I guessing?"
- Before adding error handling, ask: "Can this actually happen, or am I being defensive?"
- 80% of capability expansion = writing YAML, not Go code.
Read design/ARCHITECTURE.md §5 for full list. The critical ones:
- INV-1/2: No code branches for engine/model types. Engine behavior = YAML. Model metadata = YAML. Adding a new engine or model = writing YAML, zero Go code.
- INV-3: Don't manage container lifecycle. K3S does it. AIMA only does: apply / get / delete / logs.
- INV-5: MCP tools are the single source of truth. CLI wraps MCP tools. CLI never has logic that MCP tools don't. Agent and human always walk the same code path.
- INV-8: Offline-first. All core functions must work with zero network. Network = enhancement, not requirement.
cmd/aima/ # Entry point + dependency wiring split across domain files
internal/
hal/ # Hardware detection (nvidia-smi, /proc)
k3s/ # K3S client (kubectl wrapper)
proxy/ # HTTP inference proxy (OpenAI-compatible)
knowledge/ # go:embed YAML + SQLite relational loader + L0-L3 resolver
# + query engine (query.go) + vector similarity (similarity.go)
# + Pod YAML generator (dynamic GPU resource names)
runtime/ # Multi-Runtime: K3S (Pod) + Docker (container) + Native (exec + warmup)
sqlite.go # SQLite state store package (modernc.org/sqlite, zero CGO) — v2: 16 tables
model/ # Model scan/download/import + metadata detection
engine/ # Engine image scan/pull/import + native binary manager
stack/ # Tiered stack installer (Docker/CTK/K3S/HAMi, archive/binary/helm, airgap)
benchmark/ # Live benchmark runner (SSE streaming, concurrency, percentile stats)
mcp/ # MCP server + RegisterAllTools + tools_*.go implementations
agent/ # Go Agent (L3a) + Dispatcher + Explorer (PDCA agent planner, workspace, tools, harvester)
cli/ # Cobra commands (thin wrappers over MCP tools)
ui/ # Embedded Web UI (go:embed, Alpine.js SPA on :6188/ui/)
catalog/ # Knowledge assets (go:embed, compiled in)
embed.go
hardware/ # Hardware Profile YAML (incl. gpu.resource_name)
engines/ # Engine Asset YAML (incl. source, warmup)
models/ # Model Asset YAML
partitions/ # Partition Strategy YAML
stack/ # Stack Component YAML (K3S, HAMi — install config + airgap sources)
# Runtime overlay: ~/.aima/catalog/{hardware,engines,models,partitions,stack}/*.yaml
# Same metadata.name overrides go:embed, new names append. No recompilation needed.
make build # Versioned build (uses current development line / release tag)
go build ./cmd/aima # Quick local build without release metadata guarantees
go test ./... # Test all
go test -race ./... # Test with race detector
go vet ./... # Static analysis- Zero CGO. SQLite via
modernc.org/sqlite. No C dependencies, ever. - Standard library first.
net/httpnot gin/echo.log/slognot zap/logrus.encoding/jsonnot jsoniter. - Errors wrap with context:
fmt.Errorf("resolve config for %s: %w", model, err). - Context as first param. Every function that does I/O takes
context.Context. - Interfaces at consumer, not provider. Define interfaces where they're used, not where they're implemented.
- Functional options for config:
NewServer(addr, WithTimeout(5*time.Second)). - No init(), no global state. Everything is dependency-injected via struct constructors.
- Table-driven tests. Use
testdata/for fixtures.
Every CLI command is a thin wrapper: parse flags -> call MCP tool function -> format output. CLI never contains business logic. If you need new logic, add it as an MCP tool first.
// CORRECT: CLI calls MCP tool
func runDeploy(cmd *cobra.Command, args []string) error {
return mcpTools.DeployApply(ctx, engine, model, slot)
}
// WRONG: CLI contains logic
func runDeploy(cmd *cobra.Command, args []string) error {
hw := hal.Detect()
config := knowledge.Resolve(hw, model)
pod := knowledge.GeneratePod(config)
return k3s.Apply(pod) // This logic belongs in deploy.apply MCP tool
}Don't hardcode behaviors per engine/model. Load them from YAML:
// CORRECT: Knowledge-driven
engineAsset, _ := knowledge.FindEngine(engineType, gpuArch)
pod := podgen.Render(engineAsset, modelAsset, partitionSlot)
// WRONG: Code-driven
if engineType == "vllm" {
pod.Image = "vllm/vllm-openai:latest"
pod.Command = []string{"vllm", "serve"}
} else if engineType == "llamacpp" {
// ...more branches for each engine...
}Every feature must handle absence of its dependencies:
// L3a unavailable -> fall back to L2 -> fall back to L0
func (d *Dispatcher) Ask(ctx context.Context, query string) (string, error) {
if d.goAgent.Available() {
return d.goAgent.Ask(ctx, query)
}
return d.knowledgeResolve(ctx, query) // L2 deterministic
}- Don't write strategy/policy code in Go. That's the Agent's job via MCP tools.
- Don't add engine-specific or model-specific if/switch branches. Use YAML knowledge.
- Don't manage container lifecycle. K3S handles health checks, restarts, resource limits.
- Don't create abstractions "for the future." Three concrete uses before abstracting.
- Don't add comments to code you didn't change. Don't add docstrings unless the function is exported and non-obvious.
- Don't create wrapper types around standard library types. Use
*sql.DBdirectly, nottype Database struct { db *sql.DB }unless there's a real reason. - Don't add metrics/tracing/logging infrastructure preemptively.
slog.Info()is enough until proven otherwise. - Don't create separate files for single types or tiny functions. Keep related code together.
- Read before writing. Always read existing code before modifying. Understand the pattern first.
- Architecture doc is source of truth. When in doubt, consult
design/ARCHITECTURE.md. - Test what matters. Test business logic and edge cases. Don't test that Go's JSON marshaling works.
- One MCP tool = one function = one responsibility. Keep tool implementations focused.
- Commit atomically. Each commit should be a coherent, working unit.
- Branch from develop. Never commit directly to master. Feature branches merge to develop via PR.
| Term | Meaning |
|---|---|
| Engine Asset | YAML describing an inference engine (vLLM, llama.cpp, etc) on specific hardware |
| Model Asset | YAML describing a model's variants across hardware/engine combos |
| Hardware Profile | YAML describing a device's GPU/CPU/RAM capability vector |
| Partition Strategy | YAML describing how to split resources across multiple workloads |
| Knowledge Note | Structured record of Agent exploration results (trials + recommendation) |
| Configuration | A tested Hardware x Engine x Model x Config instance with derivation chain |
| BenchmarkResult | Multi-dimensional performance data for a Configuration under specific load |
| PerfVector | 6-dimensional normalized performance vector for similarity search |
| L0/L1/L2/L3a | Progressive intelligence levels: defaults -> human CLI -> knowledge -> Go Agent |
| ConfigResolver | Merges L0-L3 configs, higher layer overrides lower |
| Store | Knowledge query engine wrapping *sql.DB (Search/Compare/Gaps/Similar/Lineage/Aggregate) |
| MCP Tool | JSON-RPC function exposed to Agents (deploy.apply, model.scan, etc) |
After deploying a model, establish performance baselines using the benchmark tools:
1. benchmark.run — Single benchmark run against a deployed model
Auto-detects endpoint from proxy; measures TTFT/TPOT/TPS
Results auto-saved to DB when hardware + engine provided
2. benchmark.matrix — Test matrix: concurrency × input_len × output_len
Runs benchmark.run for each combination sequentially
Use for comprehensive performance characterization
3. benchmark.list — Query historical benchmark results
Filter by model, hardware, engine, or config ID
4. benchmark.record — Manually record external benchmark measurements
Share tuning knowledge across devices using export/import:
1. knowledge.export — Export configs + benchmarks + notes to JSON
Filter by --hardware, --model, --engine
No filter = full DB dump
Output to file (--output) or stdout
2. knowledge.import — Import from JSON file
Conflict: skip (default) | overwrite
Supports --dry-run preview
Atomic transaction (all-or-nothing)
Typical cross-device flow:
# On device A (has benchmark data)
aima knowledge export --hardware nvidia-gb10-arm64 -o gb10.json
# Transfer file to device B
scp gb10.json user@device-b:/tmp/
# On device B (import knowledge)
aima knowledge import -i /tmp/gb10.json --dry-run # preview first
aima knowledge import -i /tmp/gb10.json # commitExport JSON format (schema_version: 1):
{
"schema_version": 1,
"data": {
"configurations": [...], // Hardware×Engine×Model configs
"benchmark_results": [...], // Performance measurements (FK → configurations)
"knowledge_notes": [...] // Agent exploration notes
},
"stats": { "configurations": N, "benchmark_results": N, "knowledge_notes": N }
}