diff --git a/docs/architecture/README.md b/docs/architecture/README.md new file mode 100644 index 000000000..2da0f7a81 --- /dev/null +++ b/docs/architecture/README.md @@ -0,0 +1,28 @@ +# PicoClaw Multi-Agent Architecture + +This directory contains C4 model diagrams (rendered with Mermaid) documenting the multi-agent collaboration framework for PicoClaw. + +## Documents + +| Document | Scope | Description | +|----------|-------|-------------| +| [C1 - System Context](./c1-system-context.md) | Highest level | PicoClaw in its ecosystem: users, channels, LLM providers | +| [C2 - Container](./c2-container.md) | Runtime containers | Gateway, Agent Loop, Provider Layer, Channels | +| [C3 - Component](./c3-component-multi-agent.md) | Multi-agent internals | Blackboard, Handoff, Routing, Registry, Fallback | +| [C4 - Code](./c4-code-detail.md) | Key structs/interfaces | Go interfaces, data flow, tool execution | +| [Sequence Diagrams](./sequences.md) | Runtime flows | Handoff, Blackboard sync, Fallback chain | +| [Roadmap](./roadmap.md) | Phased plan | What's done, what's next, dependencies | + +## Related Issues + +- [#294 - Base Multi-agent Collaboration Framework & Shared Context](https://github.com/sipeed/picoclaw/issues/294) +- [#283 - Refactor Provider Architecture: By Protocol Instead of By Vendor](https://github.com/sipeed/picoclaw/issues/283) +- [Discussion #122 - Provider Architecture Proposal](https://github.com/sipeed/picoclaw/discussions/122) + +## Status + +| Phase | Status | PR | +|-------|--------|----| +| Provider Protocol Refactor | Merged | [#213](https://github.com/sipeed/picoclaw/pull/213) | +| Model Fallback + Multi-agent Routing | Merged | [#131](https://github.com/sipeed/picoclaw/pull/131) | +| Multi-agent Collaboration Framework | WIP | [#423](https://github.com/sipeed/picoclaw/pull/423) | diff --git a/docs/architecture/c1-system-context.md b/docs/architecture/c1-system-context.md new file mode 100644 index 000000000..69d92945d --- /dev/null +++ b/docs/architecture/c1-system-context.md @@ -0,0 +1,59 @@ +# C1 - System Context Diagram + +PicoClaw as a multi-agent platform within its ecosystem. + +```mermaid +C4Context + title System Context - PicoClaw Multi-Agent Platform + + Person(user, "User", "Interacts via messaging channels or CLI") + Person(dev, "Developer", "Configures agents, skills, and providers") + + System(picoclaw, "PicoClaw", "Multi-agent AI platform that routes user messages to specialized agents backed by multiple LLM providers") + + System_Ext(discord, "Discord", "Chat platform") + System_Ext(telegram, "Telegram", "Chat platform") + System_Ext(slack, "Slack", "Workspace messaging") + System_Ext(whatsapp, "WhatsApp", "Messaging") + System_Ext(cli, "CLI", "Direct terminal access") + + System_Ext(openai, "OpenAI API", "GPT models, Codex") + System_Ext(anthropic, "Anthropic API", "Claude models") + System_Ext(gemini, "Google Gemini", "Gemini models") + System_Ext(openrouter, "OpenRouter", "Multi-model gateway") + System_Ext(groq, "Groq", "Fast inference") + System_Ext(ollama, "Ollama", "Local LLM") + System_Ext(claude_cli, "Claude Code CLI", "Subprocess provider") + System_Ext(codex_cli, "Codex CLI", "Subprocess provider") + + Rel(user, discord, "Sends messages") + Rel(user, telegram, "Sends messages") + Rel(user, slack, "Sends messages") + Rel(user, whatsapp, "Sends messages") + Rel(user, cli, "Direct input") + Rel(dev, picoclaw, "Configures via config.json") + + Rel(discord, picoclaw, "Webhook/Bot events") + Rel(telegram, picoclaw, "Bot API") + Rel(slack, picoclaw, "Events API") + Rel(whatsapp, picoclaw, "Webhook") + Rel(cli, picoclaw, "Stdin/Stdout") + + Rel(picoclaw, openai, "HTTPS/REST") + Rel(picoclaw, anthropic, "HTTPS/REST") + Rel(picoclaw, gemini, "HTTPS/REST") + Rel(picoclaw, openrouter, "HTTPS/REST") + Rel(picoclaw, groq, "HTTPS/REST") + Rel(picoclaw, ollama, "HTTP localhost") + Rel(picoclaw, claude_cli, "Subprocess stdio") + Rel(picoclaw, codex_cli, "Subprocess stdio") +``` + +## Key interactions + +| Boundary | Protocol | Direction | +|----------|----------|-----------| +| User -> Channels | Platform-native (Discord bot, Telegram bot, etc.) | Inbound | +| Channels -> PicoClaw | Go channel bus (`pkg/bus`) | Internal | +| PicoClaw -> LLM Providers | HTTPS REST / Subprocess stdio | Outbound | +| Developer -> PicoClaw | `~/.picoclaw/config.json` + workspace files | Config | diff --git a/docs/architecture/c2-container.md b/docs/architecture/c2-container.md new file mode 100644 index 000000000..7276b7ad1 --- /dev/null +++ b/docs/architecture/c2-container.md @@ -0,0 +1,58 @@ +# C2 - Container Diagram + +Runtime containers inside PicoClaw. + +```mermaid +C4Container + title Container Diagram - PicoClaw Runtime + + Person(user, "User") + + System_Boundary(picoclaw, "PicoClaw Process") { + Container(gateway, "Gateway", "Go HTTP server", "Exposes health/ready endpoints, manages lifecycle") + Container(channel_mgr, "Channel Manager", "pkg/channels", "Manages Discord, Telegram, Slack, WhatsApp, CLI connections") + Container(msg_bus, "Message Bus", "pkg/bus", "Pub/sub event bus routing messages between channels and agents") + Container(agent_loop, "Agent Loop", "pkg/agent", "Core orchestrator: routes messages to agents, manages tool loops, sessions") + Container(registry, "Agent Registry", "pkg/agent", "Stores AgentInstance configs, resolves agent by ID or route") + Container(router, "Route Resolver", "pkg/routing", "Matches incoming messages to agents based on channel/chat/peer bindings") + Container(multiagent, "Multi-Agent Framework", "pkg/multiagent", "Blackboard shared context, Handoff mechanism, Agent discovery tools") + Container(tools, "Tool Registry", "pkg/tools", "Shell, file, web, session, message, spawn, exec tools") + Container(providers, "Provider Layer", "pkg/providers", "LLM provider abstraction: HTTP, CLI, OAuth, Fallback chain") + Container(session, "Session Store", "pkg/session", "Per-agent session persistence with conversation history") + Container(skills, "Skills Engine", "pkg/skills", "Loads SKILL.md files, provides skill tools to agents") + Container(config, "Config", "pkg/config", "Loads config.json, agent definitions, model_list") + } + + System_Ext(llm, "LLM Providers", "OpenAI, Anthropic, Gemini, Groq, Ollama, Claude CLI, Codex CLI") + System_Ext(channels_ext, "Messaging Platforms", "Discord, Telegram, Slack, WhatsApp") + + Rel(user, channels_ext, "Sends message") + Rel(channels_ext, channel_mgr, "Delivers event") + Rel(channel_mgr, msg_bus, "Publishes message") + Rel(msg_bus, agent_loop, "Delivers to agent") + Rel(agent_loop, router, "Resolves target agent") + Rel(agent_loop, registry, "Gets AgentInstance") + Rel(agent_loop, multiagent, "Blackboard sync, Handoff") + Rel(agent_loop, tools, "Executes tool calls") + Rel(agent_loop, session, "Load/save history") + Rel(agent_loop, skills, "Resolves skill tools") + Rel(agent_loop, providers, "LLM Chat()") + Rel(providers, llm, "API calls") + Rel(gateway, agent_loop, "Lifecycle management") + Rel(config, agent_loop, "Agent definitions") + Rel(config, providers, "Provider config") + Rel(config, registry, "AgentConfig list") +``` + +## Container responsibilities + +| Container | Package | Key types | +|-----------|---------|-----------| +| Agent Loop | `pkg/agent` | `AgentLoop`, `RunToolLoop()` | +| Agent Registry | `pkg/agent` | `AgentRegistry`, `AgentInstance` | +| Route Resolver | `pkg/routing` | `RouteResolver`, `SessionKeyBuilder` | +| Multi-Agent | `pkg/multiagent` | `Blackboard`, `HandoffTool`, `ListAgentsTool` | +| Provider Layer | `pkg/providers` | `LLMProvider`, `FallbackChain`, `HTTPProvider` | +| Tool Registry | `pkg/tools` | `Tool`, `ContextualTool`, `AsyncTool` | +| Session Store | `pkg/session` | `SessionStore`, conversation history | +| Config | `pkg/config` | `Config`, `AgentConfig`, `ModelConfig` | diff --git a/docs/architecture/c3-component-multi-agent.md b/docs/architecture/c3-component-multi-agent.md new file mode 100644 index 000000000..ca0155ce7 --- /dev/null +++ b/docs/architecture/c3-component-multi-agent.md @@ -0,0 +1,264 @@ +# C3 - Component Diagram: Multi-Agent Framework + +Detailed view of the multi-agent collaboration components. + +## Core Multi-Agent Components + +```mermaid +C4Component + title Component Diagram - Multi-Agent Collaboration (pkg/multiagent + pkg/agent) + + Container_Boundary(agent_pkg, "pkg/agent") { + Component(loop, "AgentLoop", "loop.go", "Core orchestrator: tool loop, LLM calls, session management") + Component(registry, "AgentRegistry", "registry.go", "Stores AgentInstance map, resolves by ID, lists all agents") + Component(instance, "AgentInstance", "instance.go", "Per-agent config: ID, Name, Role, SystemPrompt, tools, workspace") + Component(resolver_adapter, "registryResolver", "loop.go", "Adapter: bridges AgentRegistry to multiagent.AgentResolver interface") + } + + Container_Boundary(multiagent_pkg, "pkg/multiagent") { + Component(blackboard, "Blackboard", "blackboard.go", "Thread-safe shared key-value store with author/scope/timestamp metadata") + Component(bb_tool, "BlackboardTool", "blackboard_tool.go", "LLM tool: read/write/list/delete on shared context") + Component(handoff, "ExecuteHandoff", "handoff.go", "Resolves target agent, writes context to blackboard, delegates via RunToolLoop") + Component(handoff_tool, "HandoffTool", "handoff_tool.go", "LLM tool: delegates sub-task to another agent with optional context") + Component(list_tool, "ListAgentsTool", "list_agents_tool.go", "LLM tool: returns all registered agents with ID/Name/Role") + Component(agent_resolver, "AgentResolver", "handoff.go", "Interface: GetAgentInfo(id), ListAgents() - decouples from pkg/agent") + } + + Container_Boundary(routing_pkg, "pkg/routing") { + Component(route_resolver, "RouteResolver", "route.go", "Matches message to agent based on channel/chat/peer bindings") + Component(session_key, "SessionKeyBuilder", "session_key.go", "Builds per-agent session keys from channel+chat+agent") + Component(agent_id, "AgentID", "agent_id.go", "Normalizes agent identifiers") + } + + Container_Boundary(providers_pkg, "pkg/providers") { + Component(fallback, "FallbackChain", "fallback.go", "Tries candidates in order, skips cooled-down, classifies errors") + Component(cooldown, "CooldownTracker", "cooldown.go", "Per-model failure tracking with exponential backoff") + Component(error_cls, "ErrorClassifier", "error_classifier.go", "Maps HTTP errors to FailoverReason: rate_limit, billing, auth, etc.") + Component(factory, "CreateProvider", "factory.go", "Resolves config to provider: HTTP, CLI, OAuth, Fallback") + } + + Rel(loop, registry, "GetInstance(agentID)") + Rel(loop, resolver_adapter, "Creates on init") + Rel(resolver_adapter, registry, "Delegates to") + Rel(resolver_adapter, agent_resolver, "Implements") + + Rel(loop, blackboard, "getOrCreateBlackboard(sessionKey)") + Rel(loop, bb_tool, "Registers when >1 agent") + Rel(loop, handoff_tool, "Registers when >1 agent") + Rel(loop, list_tool, "Registers when >1 agent") + + Rel(handoff_tool, handoff, "Calls ExecuteHandoff()") + Rel(handoff, agent_resolver, "GetAgentInfo(targetID)") + Rel(handoff, blackboard, "Writes handoff context") + Rel(handoff, loop, "Calls RunToolLoop() for target agent") + + Rel(bb_tool, blackboard, "CRUD operations") + Rel(list_tool, agent_resolver, "ListAgents()") + + Rel(loop, route_resolver, "ResolveAgent(msg)") + Rel(loop, session_key, "BuildKey(channel, chat, agent)") + Rel(loop, fallback, "Chat() with fallback") + Rel(fallback, cooldown, "Check/update cooldown") + Rel(fallback, error_cls, "ClassifyError()") +``` + +## Blackboard Data Model + +```mermaid +classDiagram + class Blackboard { + -entries map[string]BlackboardEntry + -mu sync.RWMutex + +Set(key, value, author, scope) + +Get(key) string + +GetEntry(key) BlackboardEntry + +Delete(key) + +List() []BlackboardEntry + +Snapshot() string + +Size() int + +MarshalJSON() []byte + +UnmarshalJSON([]byte) + } + + class BlackboardEntry { + +Key string + +Value string + +Author string + +Scope string + +Timestamp time.Time + } + + class BlackboardTool { + -board *Blackboard + -agentID string + +Name() string + +Execute(args) string + } + + class HandoffRequest { + +TargetAgentID string + +Task string + +Context map[string]string + +SessionKey string + } + + class HandoffResult { + +AgentID string + +Response string + +Success bool + +Error string + } + + class AgentResolver { + <> + +GetAgentInfo(agentID) *AgentInfo + +ListAgents() []AgentInfo + } + + class AgentInfo { + +ID string + +Name string + +Role string + +SystemPrompt string + } + + Blackboard "1" --> "*" BlackboardEntry : stores + BlackboardTool --> Blackboard : operates on + HandoffRequest ..> AgentResolver : resolved via + AgentResolver --> AgentInfo : returns +``` + +## Agent Registry & Instance Model + +```mermaid +classDiagram + class AgentRegistry { + -agents map[string]*AgentInstance + -defaultID string + +Register(instance) + +GetInstance(id) *AgentInstance + +GetDefault() *AgentInstance + +ListAgentIDs() []string + } + + class AgentInstance { + +ID string + +Name string + +Role string + +SystemPrompt string + +Workspace string + +Model string + +Skills []string + +Tools []Tool + +AllowedSubagents []string + } + + class AgentConfig { + +ID string + +Default bool + +Name string + +Role string + +SystemPrompt string + +Workspace string + +Model *AgentModelConfig + +Skills []string + +Subagents *SubagentsConfig + } + + class RouteResolver { + -bindings []AgentBinding + +ResolveAgent(channel, chatID, peerKind, peerID) string + } + + class FallbackChain { + -primary ModelRef + -fallbacks []ModelRef + -cooldown *CooldownTracker + +Chat(ctx, messages, tools, model, opts) *LLMResponse + } + + AgentRegistry "1" --> "*" AgentInstance : manages + AgentConfig ..> AgentInstance : creates + AgentInstance --> FallbackChain : uses for LLM calls + RouteResolver --> AgentRegistry : resolves agent from +``` + +## Provider Protocol Architecture (PR #213 + #283) + +```mermaid +graph TB + subgraph "Config Layer" + CFG[config.json] + ML[model_list - future] + end + + subgraph "Factory (factory.go)" + RS[resolveProviderSelection] + CP[CreateProvider] + end + + subgraph "Protocol Families" + subgraph "OpenAI-Compatible" + OC[openai_compat/provider.go] + HTTP[HTTPProvider - thin delegate] + end + subgraph "Anthropic" + ANT[anthropic/provider.go] + CP2[ClaudeProvider] + end + subgraph "CLI-Based" + CC[ClaudeCliProvider] + CX[CodexCliProvider] + end + subgraph "OAuth/Token" + CA[CodexProvider - OAuth] + CL[ClaudeProvider - OAuth] + end + subgraph "Resilience" + FB[FallbackChain] + CD[CooldownTracker] + EC[ErrorClassifier] + end + end + + subgraph "External LLMs" + OPENAI[OpenAI] + GROQ[Groq] + DEEP[DeepSeek] + OR[OpenRouter] + ANTH[Anthropic] + GEM[Gemini] + OLL[Ollama] + CLICLI[claude CLI] + CODCLI[codex CLI] + end + + CFG --> RS + ML -.-> RS + RS --> CP + + CP --> HTTP + CP --> ANT + CP --> CC + CP --> CX + CP --> CA + CP --> CL + + HTTP --> OC + OC --> OPENAI + OC --> GROQ + OC --> DEEP + OC --> OR + OC --> OLL + ANT --> ANTH + CP2 --> ANTH + OC --> GEM + CC --> CLICLI + CX --> CODCLI + CA --> OPENAI + + FB --> CD + FB --> EC + FB -.-> HTTP + FB -.-> ANT +``` diff --git a/docs/architecture/c4-code-detail.md b/docs/architecture/c4-code-detail.md new file mode 100644 index 000000000..89aee8d6f --- /dev/null +++ b/docs/architecture/c4-code-detail.md @@ -0,0 +1,148 @@ +# C4 - Code Detail + +Key interfaces, structs, and data flows at the code level. + +## Core Interfaces + +```mermaid +classDiagram + class LLMProvider { + <> + +Chat(ctx, messages, tools, model, opts) *LLMResponse + +GetDefaultModel() string + } + + class Tool { + <> + +Name() string + +Description() string + +Parameters() map[string]any + +Execute(args map[string]any) (string, error) + } + + class ContextualTool { + <> + +SetContext(ctx ToolContext) + } + + class AsyncTool { + <> + +ExecuteAsync(ctx, args) (string, error) + } + + class AgentResolver { + <> + +GetAgentInfo(agentID string) *AgentInfo + +ListAgents() []AgentInfo + } + + Tool <|-- ContextualTool + Tool <|-- AsyncTool + LLMProvider <|.. HTTPProvider + LLMProvider <|.. ClaudeCliProvider + LLMProvider <|.. CodexCliProvider + LLMProvider <|.. CodexProvider + LLMProvider <|.. ClaudeProvider + LLMProvider <|.. FallbackChain + Tool <|.. BlackboardTool + Tool <|.. HandoffTool + Tool <|.. ListAgentsTool + ContextualTool <|.. HandoffTool + AgentResolver <|.. registryResolver +``` + +## Tool Loop Execution + +```mermaid +flowchart TD + MSG[Incoming Message] --> RR[RouteResolver.ResolveAgent] + RR --> AI[AgentInstance selected] + AI --> SL[Session.Load history] + SL --> SP[Build system prompt] + SP --> BS{Multi-agent?} + + BS -->|Yes| INJ[Inject Blackboard snapshot into system prompt] + BS -->|No| LLM + + INJ --> LLM[provider.Chat - send to LLM] + LLM --> RESP{Response type?} + + RESP -->|Text only| OUT[Return text to channel] + RESP -->|Tool calls| TC[Execute tool calls] + + TC --> WHICH{Which tool?} + + WHICH -->|blackboard| BB[BlackboardTool.Execute] + BB --> WL[Read/Write/List/Delete shared context] + WL --> LLM + + WHICH -->|handoff| HO[HandoffTool.Execute] + HO --> EH[ExecuteHandoff] + EH --> TA[Resolve target agent] + TA --> WC[Write context to blackboard] + WC --> RTL[RunToolLoop for target agent] + RTL --> HR[HandoffResult] + HR --> LLM + + WHICH -->|list_agents| LA[ListAgentsTool.Execute] + LA --> LLM + + WHICH -->|shell, file, web...| OT[Other tools execute] + OT --> LLM + + RESP -->|stop| SS[Session.Save] + SS --> OUT +``` + +## Blackboard Lifecycle + +```mermaid +stateDiagram-v2 + [*] --> Empty: getOrCreateBlackboard(sessionKey) + + Empty --> HasEntries: Agent writes via BlackboardTool + HasEntries --> HasEntries: Agent reads/writes/deletes + HasEntries --> Snapshot: System prompt build + Snapshot --> HasEntries: Snapshot injected, loop continues + + HasEntries --> HandoffContext: Handoff writes "handoff_context_*" + HandoffContext --> TargetReads: Target agent reads context + TargetReads --> HasEntries: Target completes, result written + + HasEntries --> Empty: All entries deleted + Empty --> [*]: Session ends + + note right of Snapshot + Snapshot format: + ## Shared Context + - key1: value1 (by agent-a) + - key2: value2 (by agent-b) + end note +``` + +## Fallback Chain Decision Tree + +```mermaid +flowchart TD + REQ[Chat Request] --> P[Try Primary Model] + P --> PS{Success?} + PS -->|Yes| RST[Reset cooldown] --> RET[Return response] + PS -->|No| CLS[ClassifyError] + + CLS --> RT{Retriable?} + RT -->|No: auth, format| FAIL[Return error immediately] + RT -->|Yes| NXT[Next candidate] + + NXT --> CD{In cooldown?} + CD -->|Yes| SKIP[Skip, try next] + CD -->|No| TRY[Try candidate] + + SKIP --> MORE{More candidates?} + TRY --> TS{Success?} + TS -->|Yes| RST2[Reset cooldown] --> RET + TS -->|No| REC[Record failure, update cooldown] + REC --> MORE + + MORE -->|Yes| NXT + MORE -->|No| EXHAUST[FallbackExhaustedError] +``` diff --git a/docs/architecture/roadmap.md b/docs/architecture/roadmap.md new file mode 100644 index 000000000..3beb74685 --- /dev/null +++ b/docs/architecture/roadmap.md @@ -0,0 +1,104 @@ +# Multi-Agent Feature Roadmap + +Phased implementation plan based on issues #294, #283, and discussion #122. + +## Phase Overview + +```mermaid +gantt + title PicoClaw Multi-Agent Roadmap + dateFormat YYYY-MM-DD + axisFormat %b %d + + section Provider Refactor (#283) + Phase 1: Protocol packages (PR #213) :done, p1, 2026-02-01, 2026-02-18 + Phase 2: model_list + explicit api_type :active, p2, 2026-02-19, 2026-03-05 + Phase 3: Independent Gemini protocol :p3, after p2, 7d + Phase 4: Local LLM + cleanup :p4, after p3, 5d + + section Multi-Agent (#294) + Fallback chain + routing (PR #131) :done, ma1, 2026-02-01, 2026-02-18 + Blackboard + Handoff + Discovery (PR #423) :active, ma2, 2026-02-18, 2026-03-01 + Swarm mode (async agent negotiation) :ma3, after ma2, 14d + Visual AIEOS dashboard :ma4, after ma3, 14d + + section Integration + model_list + multi-agent config merge :int1, after p2, 7d + Community agent marketplace :int2, after ma3, 21d +``` + +## Detailed Status + +### Done + +| Phase | PR | Description | +|-------|----|-------------| +| Provider Protocol Refactor | [#213](https://github.com/sipeed/picoclaw/pull/213) | `protocoltypes/`, `openai_compat/`, `anthropic/` packages, thin delegates, factory refactor | +| Fallback Chain + Routing | [#131](https://github.com/sipeed/picoclaw/pull/131) | `FallbackChain`, `CooldownTracker`, `ErrorClassifier`, `AgentRegistry`, `RouteResolver`, `AgentInstance`, channel peer metadata | + +### In Progress (PR #423) + +| Component | Package | Files | Status | +|-----------|---------|-------|--------| +| Agent Config extensions | `pkg/config` | `config.go` | `Role`, `SystemPrompt` fields added | +| Blackboard shared context | `pkg/multiagent` | `blackboard.go`, `blackboard_tool.go` | Complete, 18 tests | +| Agent Handoff | `pkg/multiagent` | `handoff.go`, `handoff_tool.go` | Complete, 10 tests | +| Agent Discovery | `pkg/multiagent` | `list_agents_tool.go` | Complete | +| AgentLoop integration | `pkg/agent` | `loop.go` | Snapshot injection, tool registration, per-session blackboards | +| AgentResolver interface | `pkg/multiagent` | `handoff.go` | Decouples multiagent from agent pkg | + +### Next: Provider Phase 2 - `model_list` (#283) + +Based on @yinwm's design in [issue #283](https://github.com/sipeed/picoclaw/issues/283#issuecomment-3915867555): + +``` +model_list config -> model-centric resolution -> protocol prefix routing +``` + +| Task | Description | Impact | +|------|-------------|--------| +| `ModelConfig` struct | `model_name`, `model` (protocol/id), `api_base`, `api_key` | Eliminates per-vendor code changes | +| Protocol prefix routing | `openai/`, `anthropic/`, `antigravity/` prefixes | Clean protocol selection | +| Backward compatibility | Support both `providers` (deprecated) and `model_list` | Smooth migration | +| Agent model reference | Agents reference `model_name` instead of `provider` + `model` | Simplifies multi-agent config | + +### Future: Out of Scope for #294 + +| Feature | Issue | Depends on | +|---------|-------|------------| +| Intelligent Model Routing (small/large model token saving) | TBD | model_list + multi-agent | +| Swarm Mode (autonomous agent-to-agent negotiation) | TBD | Handoff foundation | +| Visual AIEOS Dashboard | TBD | All above | +| Community Agent Marketplace | TBD | Stable agent interface | + +## Dependency Graph + +```mermaid +graph TD + PR213[PR #213: Protocol Refactor]:::done --> PR131[PR #131: Fallback + Routing]:::done + PR131 --> PR423[PR #423: Blackboard + Handoff]:::active + PR213 --> P2[Phase 2: model_list]:::planned + P2 --> P3[Phase 3: Gemini Protocol]:::planned + P3 --> P4[Phase 4: Local LLM]:::planned + PR423 --> SWARM[Swarm Mode]:::future + PR423 --> P2 + P2 --> MCONFIG[model_list + multi-agent config merge]:::planned + SWARM --> DASH[AIEOS Dashboard]:::future + MCONFIG --> MARKET[Agent Marketplace]:::future + + classDef done fill:#22c55e,color:#fff + classDef active fill:#eab308,color:#000 + classDef planned fill:#3b82f6,color:#fff + classDef future fill:#6b7280,color:#fff +``` + +## Key Decisions + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| Shared context pattern | Blackboard (key-value) | Simple, auditable, no coupling between agents | +| Handoff mechanism | Synchronous via RunToolLoop | Predictable, debuggable; async deferred to Swarm Mode | +| Circular import avoidance | `AgentResolver` interface in pkg/multiagent | Clean dependency direction: agent -> multiagent, not reverse | +| Multi-agent tool activation | Conditional on `len(agents) > 1` | Zero overhead for single-agent setups | +| Provider abstraction | Protocol-first (openai_compat, anthropic) | Adding new OpenAI-compat providers = config only | +| Config evolution | model_list (LiteLLM-inspired) | Model-centric aligns with multi-agent where agents pick models | diff --git a/docs/architecture/sequences.md b/docs/architecture/sequences.md new file mode 100644 index 000000000..f182fd2d1 --- /dev/null +++ b/docs/architecture/sequences.md @@ -0,0 +1,204 @@ +# Sequence Diagrams + +Runtime interaction flows for multi-agent collaboration. + +## 1. Agent Handoff Flow + +A main agent delegates a sub-task to a specialized agent. + +```mermaid +sequenceDiagram + participant U as User (Discord/Telegram) + participant CH as Channel Manager + participant AL as AgentLoop + participant RR as RouteResolver + participant MA as Main Agent + participant LLM as LLM Provider + participant HT as HandoffTool + participant BB as Blackboard + participant AR as AgentResolver + participant SA as Specialized Agent + + U->>CH: "Translate this code to Python" + CH->>AL: Message{channel, chat_id, content} + AL->>RR: ResolveAgent(channel, chat_id) + RR-->>AL: "main" + AL->>MA: Load AgentInstance + session + AL->>BB: getOrCreateBlackboard(sessionKey) + AL->>AL: Inject BB snapshot into system prompt + AL->>LLM: Chat(messages + tools) + LLM-->>AL: ToolCall{name: "handoff", args: {target: "coder", task: "translate to python"}} + + AL->>HT: Execute(args) + HT->>AR: GetAgentInfo("coder") + AR-->>HT: AgentInfo{ID: "coder", Role: "Code Expert"} + HT->>BB: Set("handoff_context_coder", task + context) + HT->>AL: RunToolLoop(coderAgent, task) + + AL->>SA: Load AgentInstance("coder") + AL->>LLM: Chat(coder_system_prompt + task) + LLM-->>AL: "Here's the Python translation..." + AL-->>HT: HandoffResult{Response: "...", Success: true} + + HT-->>AL: Tool result string + AL->>LLM: Chat(messages + tool_result) + LLM-->>AL: "The coder agent translated your code: ..." + AL->>CH: Send response + CH->>U: "The coder agent translated your code: ..." +``` + +## 2. Blackboard Shared Context Flow + +Multiple agents share data through the blackboard within a session. + +```mermaid +sequenceDiagram + participant A1 as Agent: Researcher + participant BB as Blackboard + participant AL as AgentLoop + participant A2 as Agent: Writer + + Note over A1, A2: Same session, shared blackboard + + A1->>BB: BlackboardTool.write("findings", "3 key points...") + BB-->>A1: OK + + A1->>BB: BlackboardTool.write("sources", "arxiv:2024...") + BB-->>A1: OK + + Note over AL: Handoff from Researcher to Writer + + AL->>BB: Snapshot() + BB-->>AL: "findings: 3 key points... (by researcher)\nsources: arxiv:2024... (by researcher)" + + AL->>A2: System prompt + snapshot + task + + A2->>BB: BlackboardTool.read("findings") + BB-->>A2: "3 key points..." + + A2->>BB: BlackboardTool.read("sources") + BB-->>A2: "arxiv:2024..." + + A2->>BB: BlackboardTool.write("draft", "Article based on findings...") + BB-->>A2: OK + + Note over BB: Blackboard state:
findings (by researcher)
sources (by researcher)
draft (by writer) +``` + +## 3. Model Fallback Chain Flow + +Provider resilience with automatic failover. + +```mermaid +sequenceDiagram + participant AL as AgentLoop + participant FB as FallbackChain + participant CD as CooldownTracker + participant EC as ErrorClassifier + participant P1 as Primary: GPT-4o + participant P2 as Fallback: Claude-3.5 + participant P3 as Fallback: DeepSeek + + AL->>FB: Chat(messages) + + FB->>CD: IsAvailable("gpt-4o")? + CD-->>FB: Yes + + FB->>P1: Chat(messages) + P1-->>FB: Error 429 (rate limit) + + FB->>EC: ClassifyError(err) + EC-->>FB: FailoverReason: RATE_LIMITED (retriable) + + FB->>CD: RecordFailure("gpt-4o", RATE_LIMITED) + Note over CD: gpt-4o cooldown: 30s + + FB->>CD: IsAvailable("claude-3.5")? + CD-->>FB: Yes + + FB->>P2: Chat(messages) + P2-->>FB: Error 503 (overloaded) + + FB->>EC: ClassifyError(err) + EC-->>FB: FailoverReason: OVERLOADED (retriable) + + FB->>CD: RecordFailure("claude-3.5", OVERLOADED) + + FB->>CD: IsAvailable("deepseek")? + CD-->>FB: Yes + + FB->>P3: Chat(messages) + P3-->>FB: LLMResponse{Content: "..."} + + FB->>CD: RecordSuccess("deepseek") + FB-->>AL: LLMResponse +``` + +## 4. Route Resolution Flow + +How incoming messages are routed to the correct agent. + +```mermaid +sequenceDiagram + participant MSG as Incoming Message + participant RR as RouteResolver + participant REG as AgentRegistry + participant SK as SessionKeyBuilder + + MSG->>RR: ResolveAgent(channel:"discord", chat:"123", peer_kind:"guild", peer_id:"456") + + RR->>RR: Check bindings + Note over RR: Binding: {channel: "discord", chat: "123"} -> "support-agent" + + alt Match found + RR-->>MSG: "support-agent" + else No match + RR->>REG: GetDefault() + REG-->>RR: "main" + RR-->>MSG: "main" + end + + MSG->>SK: BuildKey(channel, chat, agentID) + SK-->>MSG: "discord:123:support-agent" + Note over MSG: Session key used for:
- Session history
- Blackboard lookup
- State persistence +``` + +## 5. Multi-Agent Configuration Lifecycle + +From config.json to running agents. + +```mermaid +sequenceDiagram + participant CFG as config.json + participant REG as AgentRegistry + participant INST as AgentInstance + participant LOOP as AgentLoop + participant TOOLS as Tool Registry + + CFG->>REG: Parse agents.list[] + + loop For each AgentConfig + REG->>INST: NewAgentInstance(agentCfg, cfg) + INST->>INST: Set ID, Name, Role, SystemPrompt + INST->>INST: Create per-agent tools (shell, file, exec) + INST-->>REG: Register(instance) + end + + alt No agents.list configured + REG->>INST: Create implicit "main" agent + INST-->>REG: Register as default + end + + REG-->>LOOP: Registry ready + + LOOP->>LOOP: Check registry.ListAgentIDs() + + alt len(agents) > 1 + LOOP->>TOOLS: Register BlackboardTool + LOOP->>TOOLS: Register HandoffTool + LOOP->>TOOLS: Register ListAgentsTool + Note over TOOLS: Multi-agent tools active + else Single agent + Note over TOOLS: No multi-agent tools (zero overhead) + end +```