This document provides guidelines for AI agents working with the Aetheris codebase.
Aetheris (CoRag) is an execution runtime for intelligent agents — a durable, replayable, and observable environment where AI agents can plan, execute, pause, resume, and recover long-running tasks.
Key technologies:
- Go 1.26.1 (see
go.modand CI) - Go module:
github.com/Colin4k1024/Aetheris/v2(import path for all internal packages) - Cloudwego eino: Workflow/DAG execution, Agent scheduling, Pipeline orchestration
- Hertz: HTTP framework for REST APIs
- Viper: Configuration management
- PostgreSQL: JobStore (event-sourced durable history)
- Redis: Cache, RAG, Vector Index
# Build all binaries
go build ./...
# Build specific binary
go build -o bin/api ./cmd/api
go build -o bin/worker ./cmd/worker
go build -o bin/aetheris ./cmd/cli
# Build with race detector
go build -race ./...# API service (default :8080)
go run ./cmd/api
# Worker service
go run ./cmd/worker
# CLI tool
go run ./cmd/cli
# With custom config
CONFIG_PATH=/path/to/config.yaml go run ./cmd/api# Run all tests
go test ./...
# Run tests with coverage
go test -coverprofile=coverage.out ./...
# View coverage in browser
go tool cover -html=coverage.out
# Run single test file
go test ./internal/pipeline/query/... -v
# Run single test function
go test -run TestQueryPipeline_ValidQuery ./internal/pipeline/query/...
# Run tests with race detector
go test -race ./...
# Run key integration tests (runtime + http)
go test -v ./internal/agent/runtime/executor ./internal/api/http# Go vet
go vet ./...
# Format code
gofmt -w .
gofmt -d . # Show diff
# Static analysis
go run golang.org/x/tools/go/analysis/cmd/vet@latest ./...# Download dependencies
go mod download
# Tidy dependencies
go mod tidy
# Verify dependencies
go mod verify
# List dependencies
go list -m allThe project provides a Makefile for convenient build and startup:
make help # Show help
make build # Build api, worker, and cli into bin/
make run # Build and start API + Worker in background (one-command startup)
make run-api # Build and start only API in background
make run-worker # Build and start only Worker in background
make stop # Stop API and Worker started by make run
make clean # Remove bin/
make test # Run tests
make test-integration # Run key integration tests (runtime + http)
make docker-build # Build runtime container image
make docker-run # Start local 2.0 stack via Compose
make docker-stop # Stop local 2.0 stack
make vet # go vet
make fmt # gofmt -w
make tidy # go mod tidyOrganize imports in three groups with blank lines between:
- Standard library
- External packages (github.com/xxx)
- Internal packages (github.com/Colin4k1024/Aetheris/v2/xxx)
import (
"context"
"fmt"
"time"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/common/hlog"
appcore "github.com/Colin4k1024/Aetheris/v2/internal/app"
"github.com/Colin4k1024/Aetheris/v2/internal/runtime/eino"
)- Use
gofmtfor automatic formatting - Indent with tabs, not spaces
- No trailing whitespace
- Max line length: ~120 characters (soft limit)
- Packages: lowercase, concise, meaningful (e.g.,
app,pipeline,storage) - Files: lowercase with underscores only if needed for naming (e.g.,
workflow.go) - Exported types/functions: PascalCase (e.g.,
Workflow,CreateWorkflow) - Unexported: camelCase (e.g.,
engine,parseDefaultKey) - Interfaces: Simple noun or verb+noun pattern (e.g.,
Client,Retriever) - Constants: PascalCase or SCREAMING_SNAKE_CASE for constants (e.g.,
ErrNotFound,MaxRetries) - Variables: camelCase, avoid single letters except loop indices
- Use
pkg/errorsfor error wrapping:errors.Wrap(err, "message") - Use
errors.Wrapffor formatted error messages - Sentinel errors in
pkg/errors/errors.go:ErrNotFound,ErrInvalidArg - Return meaningful errors with context
- Handle errors at the appropriate level (don't ignore with
_) - Use
context.Contextfor cancellation and timeouts - Use
hlog.CtxErrorffor logging errors in handlers
if err != nil {
return nil, fmt.Errorf("compile workflow failed: %w", err)
}
return nil, errors.Wrap(err, "failed to create client")- Use struct tags for JSON serialization
- Use
bindingtags for Hertz request validation - Keep structs focused and small
type Query struct {
ID string `json:"id"`
Text string `json:"text"`
Metadata map[string]interface{} `json:"metadata"`
CreatedAt time.Time `json:"created_at"`
}- Pass
context.Contextas first parameter - Use named context variables for clarity
- Check context cancellation in long-running operations
func (h *Handler) Query(ctx context.Context, c *app.RequestContext) error {
// ...
}- Use Chinese or English comments for public APIs and documentation (team preference)
- Comment exported types and functions
- Use sentence case for comments
- No commented-out code
// Workflow 工作流
type Workflow struct {
// ...
}
// CreateWorkflow 创建工作流
func CreateWorkflow(name, description string) *Workflow {
// ...
}- Use
consts.StatusXXXfor status codes - Return consistent JSON response format
- Log errors with
hlog.CtxErrorf - Validate request parameters with
bindingtags
func (h *Handler) Query(ctx context.Context, c *app.RequestContext) {
var request struct {
Query string `json:"query" binding:"required"`
TopK int `json:"top_k"`
}
if err := c.BindJSON(&request); err != nil {
c.JSON(consts.StatusBadRequest, map[string]string{
"error": "请求参数错误",
})
return
}
// ...
}- Use table-driven tests when appropriate
- Test file naming:
xxx_test.go - Test function naming:
TestXxx - Use
t.Runfor sub-tests - Prefer
requireoverassertfor clarity on failures
cmd/ # Entry points (api, worker, cli, devops)
internal/ # Private application code
agent/ # Agent runtime (execution, scheduling, recovery)
agent.go # Deprecated: legacy Agent struct; use eino.AgentFactory instead
api/ # HTTP/gRPC API
app/ # Application core (bootstrap, services)
einoext/ # Cloudwego eino extensions
ingestqueue/ # Document ingestion queue
model/ # LLM, embedding, vision abstractions
pipeline/ # Domain pipelines (query, specialized)
runtime/ # Runtime core (eino workflow orchestration)
eino/
engine.go # Eino Engine: workflow compilation, runner management
agent_factory.go # AgentFactory: config-driven Eino ADK agent creation (recommended)
tool_bridge.go # Tool Bridge: converts Aetheris tools → Eino InvokableTool
workflow.go # Workflow definition and compilation
tools.go # Built-in Eino tools (retriever, generator, etc.)
splitter/ # Text splitting implementations
storage/ # Data storage implementations
tool/ # Tool definitions and implementations
pkg/ # Public libraries
config/ # Configuration
log/ # Logging
tracing/ # Tracing utilities
configs/ # Configuration files
agents.yaml # Agent definitions (loaded by AgentFactory at startup)
examples/ # Example code
design/ # Design documentation (public in root; internal/ for implementation details)
deployments/ # Docker, K8s configurations
- Use Viper for configuration management
- YAML configuration files in
configs/ - Support environment variable overrides
- Use
${VAR_NAME}syntax in config for env var substitution
- All pipelines orchestrated via eino
- Workflows: DAG-based execution with nodes and edges
- Use
compose.NewGraphfor workflow definition - Register workflows with the Engine
go.mod: Module definition and dependenciesconfigs/*.yaml: Configuration filesconfigs/agents.yaml: Agent definitions (loaded byAgentFactoryat startup)internal/runtime/eino/engine.go: Eino Engine — workflow compilation, runner managementinternal/runtime/eino/agent_factory.go: AgentFactory — config-driven Eino ADK agent creation (recommended entry point for all agent construction)internal/runtime/eino/tool_bridge.go: Tool Bridge — converts AetherisRuntimeToolto EinoInvokableTool(resolves import cycle via interface abstraction)internal/runtime/eino/workflow.go: Workflow implementationinternal/api/http/handler.go: HTTP handlersinternal/app/api/app.go: API assembly — createsAgentFactory, wires tools, registers agents from configinternal/app/bootstrap.go: Bootstrap and shared initializationpkg/config/config.go: Configuration types (includesAgentDefConfigwithToolsfield)pkg/errors/errors.go: Error utilitiesinternal/agent/agent.go: Deprecated — legacy Agent struct; useeino.AgentFactoryinsteadinternal/agent/runtime/executor.go: Agent execution runtime (DAG compiler + runner)Makefile: Build and run commands (usemake runto start all services)
- Create
internal/pipeline/newpipeline/ - Implement
NewPipeline()function - Register with Engine in
internal/app/bootstrap.go - Add handler in
internal/api/http/handler.go
- Implement interface in
internal/model/llm/or similar - Register provider in config
- Use
NewLLMClientFromConfigpattern
All agent construction goes through AgentFactory using Eino ADK. The legacy runtime.NewAgent() is deprecated.
- Define the agent in
configs/agents.yaml:
agents:
my_agent:
type: "react"
description: "My custom agent"
llm: "default"
max_iterations: 10
tools: # Optional: filter available tools; empty = all
- "web_search"
- "calculator"
system_prompt: |
You are a helpful assistant.AgentFactory.GetOrCreateFromConfig()loads all agents at startup (ininternal/app/api/app.go)- Access the runner via
agentFactory.GetRunner("my_agent") - For programmatic creation, use
AgentFactory.CreateAgent(ctx, eino.AgentBuildConfig{...})
- Implement the
RuntimeToolinterface in your tool package:
type MyTool struct{}
func (t *MyTool) Name() string { return "my_tool" }
func (t *MyTool) Description() string { return "Description" }
func (t *MyTool) Schema() map[string]any { return map[string]any{...} }
func (t *MyTool) Execute(ctx context.Context, sess *session.Session, input map[string]any, state interface{}) (any, error) {
// Tool logic here
return "result", nil
}- Register with the tool registry:
registry.Register("my_tool", &MyTool{}) - The
RegistryToolBridgeautomatically converts registered tools to EinoInvokableTool AgentFactory.collectTools()merges registry tools + engine built-in tools for each agent- Optionally limit which agents see this tool via
tools:list inagents.yaml
- Define request/response types in handler
- Implement handler method
- Register route in
internal/api/http/router.go
Legend: 🎯session 🔴bugfix 🟣feature 🔄refactor ✅change 🔵discovery ⚖️decision 🚨security_alert 🔐security_note Format: ID TIME TYPE TITLE Fetch details: get_observations([IDs]) | Search: mem-search skill
Stats: 50 obs (17,283t read) | 440,696t work | 96% savings
2912 2:54p 🔵 Aetheris Go 框架适配器生态 — 8 种外部框架作为 TaskGraph 节点支持 2913 " 🔵 Aetheris 自定义 Agent 迁移模式 — 命令式代码→Tool+TaskGraph 标准化转换 2914 2:55p 🔵 AgentFactory 实现细节 — Checkpoint Runner 不缓存,默认 agents.yaml 定义 6 种 Agent 2916 3:03p 🔵 Aetheris 战略定位与 SDK 完整上下文确认 2917 " ⚖️ Aetheris "用户已有 Agent 接入"方向三项核心决策确立 2918 " 🔵 Aetheris SDK + NodeAdapter 执行层完整结构确认 2923 3:08p ⚖️ CoRag/Aetheris MVP 黑盒 Agent 接入策略确定 2928 3:12p 🔵 CoRag/Aetheris 黑盒 Agent 接入架构全景探索 2929 " 🔵 CoRag/Aetheris app.go 完整工具链初始化路径 2930 " 🔵 CoRag/Aetheris Runner 执行契约 — PlanGenerated 强制前置 2931 3:13p 🟣 openclaw-adapter 黑盒 HTTP Agent 接入 TDD RED 阶段 — 测试先行 2933 3:14p 🔵 CoRag 开发环境 Go 二进制路径需手动指定 2934 3:15p 🔵 TDD RED 状态确认 — external_http Agent 接入编译失败 2938 3:19p 🔵 CoRag/Aetheris TDD GREEN Phase — Session Resumed After Code Pull 2939 " 🟣 pkg/config: AgentExternalConfig + ValidateExternalAgents 实现落地 2940 " 🟣 runtime.Manager.Register() — 稳定 ID agent 注册方法新增 2941 " ✅ external_agent_tool_test.go — 返回类型断言从 map[string]any 修正为 tools.ToolResult 2943 3:21p 🟣 ExternalAgentCallTool 完整实现 — internal/app/api/external_agent_tool.go 新建 2944 " 🟣 RegisterConfiguredAgents + PlanGoalForJobFuncWithExternalAgents 实现落地 2946 3:22p 🟣 app.go 启动时完整接入 external_http agent — 工具注册、Agent 注册、Planner 路由三段全部接通 2947 3:23p 🟣 collectExternalAgentConfigs 辅助函数添加至 external_agent_tool.go 2948 " 🔴 app.go nil-safety fix — bootstrap.Config nil guard before AgentsConfig dereference 2949 " ✅ agent_dag.go loadLocalAgents — external_http case guard prevents unknown-type warning 2950 3:24p 🟣 AppendJobCompleted 携带 external agent 答案 — extractAnswerFromCommittedEvents 实现 2951 " 🟣 TDD GREEN 阶段完成 — pkg/config 和 internal/app/api 测试全部通过 2952 3:29p 🟣 external_http Agent Type — Phase 1 HTTP Blackbox Adapter 2953 " 🔴 app.go nil guard before AgentsConfig dereference 2954 " 🔴 ExternalAgentCallTool test type assertion fixed from map to tools.ToolResult 2955 " ⚖️ external_http reliability boundary: at-most-once only for outer tool call 2956 3:31p 🟣 node_sink_test.go — AppendJobCompleted answer extraction integration test 2957 3:32p 🟣 external_http full test suite GREEN — all 5 packages pass including new node_sink_test 2958 3:35p 🔵 CoRag/Aetheris working branch is main tracking origin/main 2959 4:16p 🔵 CoRag Project Review — Branch State and Directory Structure Confirmed 2960 4:17p 🔵 CoRag/Aetheris Project Structure — Multi-Language Monorepo with SLSA Release Pipeline 2961 " 🔵 CoRag/Aetheris Internal Design Documentation — Extensive Formal Spec Coverage 2962 " 🔵 Aetheris v2.3.0 Status Snapshot — Production-Ready Runtime, Integrated Compliance, Prototype Enterprise Lane 2963 " 🔵 Aetheris CI Pipeline — Go 1.26.1, 30% Coverage Threshold, Postgres Integration Tests 2964 4:18p 🔵 Aetheris Full Test Suite — All Packages Pass, Complete Package Layout Confirmed 2965 " 🔵 Local Build Environment — Go Module Cache Permission Issue and Linter Not Installed 2966 4:19p 🔵 Aetheris go.mod Dependency Stack — CloudWeGo Eino + Hertz, Full OpenTelemetry, Dual DB Support 2967 4:22p 🔵 Aetheris Technical Debt Scan — gRPC Unimplemented, Milvus/Pinecone Stubs, SAML Not Supported 2968 " 🔵 AGENTS.md Go Version Stale — Documents 1.25.7 While go.mod and CI Use 1.26.1 2969 4:24p 🔵 Aetheris Runtime Configuration — Key Environment Variables Including PLANNER_TYPE Switcher 2970 " 🔵 Go Version Documentation Drift Is Pervasive — 1.25.7 Appears in 10+ Files vs Actual 1.26.1 2971 " 🔵 Aetheris Latest Release is v2.5.3 — docs/STATUS.md Stale at v2.3.0+ 2975 4:35p ✅ CoRag/Aetheris Go version requirement bumped to 1.26.1 across all docs 2976 " ✅ Milvus/Pinecone vector adapters clarified as non-production prototype placeholders 2977 " ✅ CoRag/Aetheris STATUS.md updated to v2.5.3 and CURRENT-STATUS-AND-FOCUS.md demoted to historical snapshot 2978 " 🔵 Remote branch codex/external-http-agent-intake diverged with 2 PR-review fix commits during local work 2994 5:15p ✅ hermes-agent-go v2.1.0 代码推送 GitHub 启动
Access 441k tokens of past work via get_observations([IDs]) or mem-search skill.