Files

926 lines
25 KiB
Go

// Agentic Consensus over ZAP Protocol
//
// Demonstrates high-performance AI agent consensus using the ZAP protocol,
// featuring dynamic capability discovery, tool registration, and MCP bridging.
//
// Architecture:
//
// ┌────────────────────────────────────────────────────────────────┐
// │ AGENTIC CONSENSUS SWARM │
// ├────────────────────────────────────────────────────────────────┤
// │ │
// │ ┌─────────┐ ZAP ┌─────────┐ ZAP ┌─────────┐ │
// │ │ Claude │◄──────────►│ GPT │◄──────────►│ Qwen │ │
// │ │ Agent │ │ Agent │ │ Agent │ │
// │ └────┬────┘ └────┬────┘ └────┬────┘ │
// │ │ │ │ │
// │ │ ZAP │ ZAP │ │
// │ └──────────┬───────────┴──────────┬───────────┘ │
// │ │ │ │
// │ ┌─────┴────┐ ┌──────┴─────┐ │
// │ │ Copilot │◄────────►│ Gemini │ │
// │ │ Agent │ ZAP │ (Arbiter) │ │
// │ └──────────┘ └────────────┘ │
// │ │
// └──────────────────────────────────────────────────────────────┘
//
// Features:
// - Dynamic agent discovery via mDNS + ZAP handshake
// - Capability registration and tool exposure
// - Consensus on multi-agent task execution
// - MCP<>ZAP protocol bridging
// - Zero-copy message passing for high performance
//
// Usage:
//
// go run main.go [query]
package main
import (
"context"
"encoding/binary"
"fmt"
"io"
"log/slog"
"net"
"os"
"strings"
"sync"
"sync/atomic"
"time"
)
// ZAP Wire Format (compatible with github.com/luxfi/zap)
const (
ZAPMagic = 0x5041_5A00 // "ZAP\0" little-endian
ZAPVersion = 1
)
// Message types for agentic consensus
const (
MsgTypeHandshake uint16 = 0 // Initial connection
MsgTypeCapabilities uint16 = 1 // Capability announcement
MsgTypeQuery uint16 = 10 // Task query
MsgTypeResponse uint16 = 11 // Agent response
MsgTypeVote uint16 = 12 // Vote for response
MsgTypeSynthesis uint16 = 13 // Final synthesis
MsgTypeToolCall uint16 = 20 // Tool invocation
MsgTypeToolResult uint16 = 21 // Tool result
)
// Capability represents an agent's exposed tool/capability
type Capability struct {
Name string
Description string
Parameters []string
}
// AgentConfig defines agent properties
type AgentConfig struct {
ID int
Name string
Model string
Capabilities []Capability
IsArbiter bool // Gemini is the arbiter
}
// Default agent configurations
var DefaultAgents = []AgentConfig{
{0, "Claude", "claude-sonnet-4-20250514", []Capability{
{"analyze", "Deep code analysis and reasoning", []string{"code", "context"}},
{"review", "Security and quality review", []string{"code", "rules"}},
}, false},
{1, "GPT", "gpt-4o", []Capability{
{"generate", "Code generation and completion", []string{"prompt", "language"}},
{"explain", "Technical explanations", []string{"topic", "level"}},
}, false},
{2, "Copilot", "gpt-4o", []Capability{
{"complete", "Inline code completion", []string{"prefix", "suffix"}},
{"suggest", "Improvement suggestions", []string{"code", "goal"}},
}, false},
{3, "Qwen", "qwen-max", []Capability{
{"translate", "Multi-language translation", []string{"text", "target"}},
{"reason", "Chain-of-thought reasoning", []string{"problem"}},
}, false},
{4, "Gemini", "gemini-1.5-pro", []Capability{
{"synthesize", "Multi-source synthesis", []string{"inputs"}},
{"arbitrate", "Consensus arbitration", []string{"votes", "responses"}},
}, true}, // Arbiter
}
// Agent represents a ZAP-connected AI agent
type Agent struct {
config AgentConfig
nodeID string
port int
// Networking
listener net.Listener
conns map[string]*AgentConn
connsMu sync.RWMutex
// Capability registry
peerCaps map[string][]Capability
capsMu sync.RWMutex
// Consensus state
responses map[uint64]map[int]string
votes map[uint64]map[int][]int
synthesis map[uint64]string
stateMu sync.Mutex
// Stats
queryCount atomic.Int64
responseCount atomic.Int64
voteCount atomic.Int64
toolCallCount atomic.Int64
ctx context.Context
cancel context.CancelFunc
wg sync.WaitGroup
logger *slog.Logger
}
type AgentConn struct {
nodeID string
conn net.Conn
caps []Capability
mu sync.Mutex
}
// NewAgent creates a new ZAP-enabled agent
func NewAgent(config AgentConfig, port int, logger *slog.Logger) *Agent {
ctx, cancel := context.WithCancel(context.Background())
return &Agent{
config: config,
nodeID: fmt.Sprintf("agent-%s-%d", strings.ToLower(config.Name), config.ID),
port: port,
conns: make(map[string]*AgentConn),
peerCaps: make(map[string][]Capability),
responses: make(map[uint64]map[int]string),
votes: make(map[uint64]map[int][]int),
synthesis: make(map[uint64]string),
ctx: ctx,
cancel: cancel,
logger: logger,
}
}
// Start begins listening for ZAP connections
func (a *Agent) Start() error {
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", a.port))
if err != nil {
return fmt.Errorf("listen failed: %w", err)
}
a.listener = listener
a.wg.Add(1)
go a.acceptLoop()
a.logger.Info("Agent started",
"name", a.config.Name,
"model", a.config.Model,
"port", a.port,
"capabilities", len(a.config.Capabilities),
"arbiter", a.config.IsArbiter,
)
return nil
}
// Stop shuts down the agent
func (a *Agent) Stop() {
a.cancel()
if a.listener != nil {
a.listener.Close()
}
a.connsMu.Lock()
for _, c := range a.conns {
c.conn.Close()
}
a.connsMu.Unlock()
a.wg.Wait()
}
func (a *Agent) acceptLoop() {
defer a.wg.Done()
for {
conn, err := a.listener.Accept()
if err != nil {
select {
case <-a.ctx.Done():
return
default:
continue
}
}
a.wg.Add(1)
go a.handleConn(conn, false)
}
}
func (a *Agent) handleConn(netConn net.Conn, initiator bool) {
defer a.wg.Done()
defer netConn.Close()
// Handshake
peerID, err := a.doHandshake(netConn, initiator)
if err != nil {
a.logger.Debug("Handshake failed", "error", err)
return
}
ac := &AgentConn{nodeID: peerID, conn: netConn}
a.connsMu.Lock()
a.conns[peerID] = ac
a.connsMu.Unlock()
// Exchange capabilities
a.sendCapabilities(ac)
defer func() {
a.connsMu.Lock()
delete(a.conns, peerID)
a.connsMu.Unlock()
a.capsMu.Lock()
delete(a.peerCaps, peerID)
a.capsMu.Unlock()
a.logger.Info("Peer disconnected", "peer", peerID)
}()
a.logger.Info("Peer connected", "peer", peerID)
// Message loop
for {
select {
case <-a.ctx.Done():
return
default:
}
msg, err := a.readMessage(netConn)
if err != nil {
return
}
a.handleMessage(peerID, msg)
}
}
// ConnectTo establishes connection to another agent
func (a *Agent) ConnectTo(addr string) error {
netConn, err := net.DialTimeout("tcp", addr, 5*time.Second)
if err != nil {
return err
}
a.wg.Add(1)
go a.handleConn(netConn, true)
return nil
}
func (a *Agent) doHandshake(conn net.Conn, initiator bool) (string, error) {
if initiator {
if err := a.sendHandshake(conn); err != nil {
return "", err
}
return a.recvHandshake(conn)
}
peerID, err := a.recvHandshake(conn)
if err != nil {
return "", err
}
return peerID, a.sendHandshake(conn)
}
func (a *Agent) sendHandshake(conn net.Conn) error {
// ZAP header + node ID
buf := make([]byte, 128)
// Magic
binary.LittleEndian.PutUint32(buf[0:4], ZAPMagic)
// Version
buf[4] = ZAPVersion
// Flags (MsgTypeHandshake << 8)
binary.LittleEndian.PutUint16(buf[6:8], MsgTypeHandshake<<8)
// Node ID as payload
idBytes := []byte(a.nodeID)
copy(buf[16:], idBytes)
buf[16+60] = byte(len(idBytes))
return a.writeFrame(conn, buf[:80])
}
func (a *Agent) recvHandshake(conn net.Conn) (string, error) {
buf, err := a.readFrame(conn)
if err != nil {
return "", err
}
// Verify magic
magic := binary.LittleEndian.Uint32(buf[0:4])
if magic != ZAPMagic {
return "", fmt.Errorf("invalid ZAP magic: %x", magic)
}
// Extract node ID
idLen := int(buf[16+60])
if idLen > 60 || idLen == 0 {
return "", fmt.Errorf("invalid node ID length: %d", idLen)
}
return string(buf[16 : 16+idLen]), nil
}
func (a *Agent) sendCapabilities(ac *AgentConn) {
// Build capabilities message
buf := make([]byte, 1024)
// Header
binary.LittleEndian.PutUint32(buf[0:4], ZAPMagic)
buf[4] = ZAPVersion
binary.LittleEndian.PutUint16(buf[6:8], MsgTypeCapabilities<<8)
// Encode capabilities as simple format
offset := 16
buf[offset] = byte(len(a.config.Capabilities))
offset++
for _, cap := range a.config.Capabilities {
nameBytes := []byte(cap.Name)
buf[offset] = byte(len(nameBytes))
offset++
copy(buf[offset:], nameBytes)
offset += len(nameBytes)
descBytes := []byte(cap.Description)
buf[offset] = byte(len(descBytes))
offset++
copy(buf[offset:], descBytes)
offset += len(descBytes)
}
ac.mu.Lock()
a.writeFrame(ac.conn, buf[:offset])
ac.mu.Unlock()
}
func (a *Agent) handleMessage(from string, msg []byte) {
if len(msg) < 8 {
return
}
msgType := binary.LittleEndian.Uint16(msg[6:8]) >> 8
switch msgType {
case MsgTypeCapabilities:
a.handleCapabilities(from, msg)
case MsgTypeQuery:
a.handleQuery(from, msg)
case MsgTypeResponse:
a.handleResponse(from, msg)
case MsgTypeVote:
a.handleVote(from, msg)
case MsgTypeSynthesis:
a.handleSynthesis(from, msg)
case MsgTypeToolCall:
a.handleToolCall(from, msg)
}
}
func (a *Agent) handleCapabilities(from string, msg []byte) {
if len(msg) < 17 {
return
}
offset := 16
capCount := int(msg[offset])
offset++
caps := make([]Capability, 0, capCount)
for i := 0; i < capCount && offset < len(msg); i++ {
nameLen := int(msg[offset])
offset++
if offset+nameLen > len(msg) {
break
}
name := string(msg[offset : offset+nameLen])
offset += nameLen
descLen := int(msg[offset])
offset++
if offset+descLen > len(msg) {
break
}
desc := string(msg[offset : offset+descLen])
offset += descLen
caps = append(caps, Capability{Name: name, Description: desc})
}
a.capsMu.Lock()
a.peerCaps[from] = caps
a.capsMu.Unlock()
a.logger.Info("Received capabilities", "from", from, "caps", len(caps))
}
func (a *Agent) handleQuery(from string, msg []byte) {
a.queryCount.Add(1)
if len(msg) < 24 {
return
}
queryID := binary.LittleEndian.Uint64(msg[16:24])
queryLen := int(binary.LittleEndian.Uint32(msg[24:28]))
if 28+queryLen > len(msg) {
queryLen = len(msg) - 28
}
query := string(msg[28 : 28+queryLen])
a.logger.Info("Received query", "from", from, "queryID", queryID, "query", truncate(query, 50))
// Generate response
go func() {
response := a.generateResponse(query)
a.broadcastResponse(queryID, response)
}()
}
func (a *Agent) handleResponse(from string, msg []byte) {
a.responseCount.Add(1)
if len(msg) < 28 {
return
}
queryID := binary.LittleEndian.Uint64(msg[16:24])
agentID := int(binary.LittleEndian.Uint32(msg[24:28]))
respLen := int(binary.LittleEndian.Uint32(msg[28:32]))
if 32+respLen > len(msg) {
respLen = len(msg) - 32
}
response := string(msg[32 : 32+respLen])
a.stateMu.Lock()
if a.responses[queryID] == nil {
a.responses[queryID] = make(map[int]string)
}
a.responses[queryID][agentID] = response
count := len(a.responses[queryID])
a.stateMu.Unlock()
a.logger.Info("Received response", "from", from, "agentID", agentID, "responses", count)
// Vote when we have enough responses
if count >= 4 {
go a.castVote(queryID)
}
}
func (a *Agent) handleVote(from string, msg []byte) {
a.voteCount.Add(1)
if len(msg) < 32 {
return
}
queryID := binary.LittleEndian.Uint64(msg[16:24])
voterID := int(binary.LittleEndian.Uint32(msg[24:28]))
voteFor := int(binary.LittleEndian.Uint32(msg[28:32]))
a.stateMu.Lock()
if a.votes[queryID] == nil {
a.votes[queryID] = make(map[int][]int)
}
a.votes[queryID][voteFor] = append(a.votes[queryID][voteFor], voterID)
totalVotes := 0
for _, v := range a.votes[queryID] {
totalVotes += len(v)
}
a.stateMu.Unlock()
a.logger.Info("Received vote", "from", from, "voter", voterID, "for", voteFor, "total", totalVotes)
// Arbiter synthesizes when votes are in
if a.config.IsArbiter && totalVotes >= 4 {
go a.synthesizeConsensus(queryID)
}
}
func (a *Agent) handleSynthesis(from string, msg []byte) {
if len(msg) < 28 {
return
}
queryID := binary.LittleEndian.Uint64(msg[16:24])
synthLen := int(binary.LittleEndian.Uint32(msg[24:28]))
if 28+synthLen > len(msg) {
synthLen = len(msg) - 28
}
synthesis := string(msg[28 : 28+synthLen])
a.stateMu.Lock()
a.synthesis[queryID] = synthesis
a.stateMu.Unlock()
fmt.Printf("\n%s\n", synthesis)
}
func (a *Agent) handleToolCall(from string, msg []byte) {
a.toolCallCount.Add(1)
// Tool execution would go here
a.logger.Info("Tool call received", "from", from)
}
func (a *Agent) generateResponse(query string) string {
// Simulate different agent personalities
personalities := map[string]string{
"Claude": "Analyzing with careful reasoning: ",
"GPT": "Based on comprehensive analysis: ",
"Copilot": "From a practical coding perspective: ",
"Qwen": "Through methodical examination: ",
"Gemini": "Synthesizing multiple viewpoints: ",
}
time.Sleep(time.Duration(50+a.config.ID*30) * time.Millisecond)
prefix := personalities[a.config.Name]
caps := make([]string, 0, len(a.config.Capabilities))
for _, c := range a.config.Capabilities {
caps = append(caps, c.Name)
}
return fmt.Sprintf("%sFor '%s', I would approach this using my capabilities (%s). "+
"Key considerations include accuracy, performance, and maintainability. "+
"Agent %s recommends a balanced approach considering all factors.",
prefix, truncate(query, 30), strings.Join(caps, ", "), a.config.Name)
}
func (a *Agent) broadcastResponse(queryID uint64, response string) {
buf := make([]byte, 4096)
binary.LittleEndian.PutUint32(buf[0:4], ZAPMagic)
buf[4] = ZAPVersion
binary.LittleEndian.PutUint16(buf[6:8], MsgTypeResponse<<8)
binary.LittleEndian.PutUint64(buf[16:24], queryID)
binary.LittleEndian.PutUint32(buf[24:28], uint32(a.config.ID))
respBytes := []byte(response)
binary.LittleEndian.PutUint32(buf[28:32], uint32(len(respBytes)))
copy(buf[32:], respBytes)
a.broadcast(buf[:32+len(respBytes)])
// Store own response
a.stateMu.Lock()
if a.responses[queryID] == nil {
a.responses[queryID] = make(map[int]string)
}
a.responses[queryID][a.config.ID] = response
a.stateMu.Unlock()
}
func (a *Agent) castVote(queryID uint64) {
a.stateMu.Lock()
responses := make(map[int]string)
for k, v := range a.responses[queryID] {
responses[k] = v
}
a.stateMu.Unlock()
// Vote for longest response (heuristic)
bestAgent := -1
bestLen := 0
for agentID, resp := range responses {
if len(resp) > bestLen {
bestLen = len(resp)
bestAgent = agentID
}
}
if bestAgent == -1 {
return
}
buf := make([]byte, 64)
binary.LittleEndian.PutUint32(buf[0:4], ZAPMagic)
buf[4] = ZAPVersion
binary.LittleEndian.PutUint16(buf[6:8], MsgTypeVote<<8)
binary.LittleEndian.PutUint64(buf[16:24], queryID)
binary.LittleEndian.PutUint32(buf[24:28], uint32(a.config.ID))
binary.LittleEndian.PutUint32(buf[28:32], uint32(bestAgent))
a.broadcast(buf[:32])
// Record own vote
a.stateMu.Lock()
if a.votes[queryID] == nil {
a.votes[queryID] = make(map[int][]int)
}
a.votes[queryID][bestAgent] = append(a.votes[queryID][bestAgent], a.config.ID)
a.stateMu.Unlock()
}
func (a *Agent) synthesizeConsensus(queryID uint64) {
a.stateMu.Lock()
if _, exists := a.synthesis[queryID]; exists {
a.stateMu.Unlock()
return
}
a.synthesis[queryID] = "pending"
responses := make(map[int]string)
for k, v := range a.responses[queryID] {
responses[k] = v
}
votes := make(map[int][]int)
for k, v := range a.votes[queryID] {
votes[k] = v
}
a.stateMu.Unlock()
// Find winner
winner := -1
maxVotes := 0
for agentID, voters := range votes {
if len(voters) > maxVotes {
maxVotes = len(voters)
winner = agentID
}
}
// Build synthesis
var sb strings.Builder
sb.WriteString("═══════════════════════════════════════════════════════════════\n")
sb.WriteString(" AGENTIC CONSENSUS REACHED \n")
sb.WriteString("═══════════════════════════════════════════════════════════════\n\n")
sb.WriteString(fmt.Sprintf("Winner: %s (Agent %d) with %d votes\n\n",
DefaultAgents[winner].Name, winner, maxVotes))
sb.WriteString("Vote Distribution:\n")
for agentID, voters := range votes {
voterNames := make([]string, len(voters))
for i, v := range voters {
voterNames[i] = DefaultAgents[v].Name
}
sb.WriteString(fmt.Sprintf(" • %s: %d votes (%s)\n",
DefaultAgents[agentID].Name, len(voters), strings.Join(voterNames, ", ")))
}
sb.WriteString(fmt.Sprintf("\nWinning Response:\n%s\n", responses[winner]))
sb.WriteString("\n───────────────────────────────────────────────────────────────\n")
sb.WriteString("Synthesized by Gemini (Arbiter) via ZAP Protocol\n")
synthesis := sb.String()
a.stateMu.Lock()
a.synthesis[queryID] = synthesis
a.stateMu.Unlock()
// Broadcast synthesis
buf := make([]byte, 8192)
binary.LittleEndian.PutUint32(buf[0:4], ZAPMagic)
buf[4] = ZAPVersion
binary.LittleEndian.PutUint16(buf[6:8], MsgTypeSynthesis<<8)
binary.LittleEndian.PutUint64(buf[16:24], queryID)
synthBytes := []byte(synthesis)
binary.LittleEndian.PutUint32(buf[24:28], uint32(len(synthBytes)))
copy(buf[28:], synthBytes)
a.broadcast(buf[:28+len(synthBytes)])
}
// BroadcastQuery sends a query to all connected agents
func (a *Agent) BroadcastQuery(queryID uint64, query string) {
buf := make([]byte, 2048)
binary.LittleEndian.PutUint32(buf[0:4], ZAPMagic)
buf[4] = ZAPVersion
binary.LittleEndian.PutUint16(buf[6:8], MsgTypeQuery<<8)
binary.LittleEndian.PutUint64(buf[16:24], queryID)
queryBytes := []byte(query)
binary.LittleEndian.PutUint32(buf[24:28], uint32(len(queryBytes)))
copy(buf[28:], queryBytes)
a.broadcast(buf[:28+len(queryBytes)])
}
func (a *Agent) broadcast(data []byte) {
a.connsMu.RLock()
conns := make([]*AgentConn, 0, len(a.conns))
for _, c := range a.conns {
conns = append(conns, c)
}
a.connsMu.RUnlock()
for _, c := range conns {
c.mu.Lock()
a.writeFrame(c.conn, data)
c.mu.Unlock()
}
}
// GetPeerCapabilities returns discovered capabilities from all peers
func (a *Agent) GetPeerCapabilities() map[string][]Capability {
a.capsMu.RLock()
defer a.capsMu.RUnlock()
result := make(map[string][]Capability)
for k, v := range a.peerCaps {
result[k] = v
}
return result
}
// GetConnectedPeers returns connected peer node IDs
func (a *Agent) GetConnectedPeers() []string {
a.connsMu.RLock()
defer a.connsMu.RUnlock()
peers := make([]string, 0, len(a.conns))
for id := range a.conns {
peers = append(peers, id)
}
return peers
}
// GetSynthesis retrieves synthesis for a query
func (a *Agent) GetSynthesis(queryID uint64) (string, bool) {
a.stateMu.Lock()
defer a.stateMu.Unlock()
s, ok := a.synthesis[queryID]
return s, ok && s != "pending"
}
// Wire format helpers
func (a *Agent) writeFrame(w io.Writer, data []byte) error {
lenBuf := make([]byte, 4)
binary.LittleEndian.PutUint32(lenBuf, uint32(len(data)))
if _, err := w.Write(lenBuf); err != nil {
return err
}
_, err := w.Write(data)
return err
}
func (a *Agent) readFrame(r io.Reader) ([]byte, error) {
lenBuf := make([]byte, 4)
if _, err := io.ReadFull(r, lenBuf); err != nil {
return nil, err
}
length := binary.LittleEndian.Uint32(lenBuf)
if length > 1024*1024 {
return nil, fmt.Errorf("frame too large: %d", length)
}
data := make([]byte, length)
if _, err := io.ReadFull(r, data); err != nil {
return nil, err
}
return data, nil
}
func (a *Agent) readMessage(r io.Reader) ([]byte, error) {
return a.readFrame(r)
}
func truncate(s string, n int) string {
if len(s) <= n {
return s
}
return s[:n] + "..."
}
func main() {
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
fmt.Println("╔═══════════════════════════════════════════════════════════════╗")
fmt.Println("║ AGENTIC CONSENSUS OVER ZAP PROTOCOL ║")
fmt.Println("║ High-Performance Multi-Agent AI Consensus ║")
fmt.Println("╚═══════════════════════════════════════════════════════════════╝")
fmt.Println()
// Display agent info
fmt.Println("Initializing Agent Swarm:")
for _, cfg := range DefaultAgents {
caps := make([]string, len(cfg.Capabilities))
for i, c := range cfg.Capabilities {
caps[i] = c.Name
}
role := ""
if cfg.IsArbiter {
role = " [ARBITER]"
}
fmt.Printf(" [%d] %s (%s)%s\n Capabilities: %s\n",
cfg.ID, cfg.Name, cfg.Model, role, strings.Join(caps, ", "))
}
fmt.Println()
// Create agents
agents := make([]*Agent, len(DefaultAgents))
basePort := 21000
for i, cfg := range DefaultAgents {
agents[i] = NewAgent(cfg, basePort+i, logger)
}
// Start all agents
for i, agent := range agents {
if err := agent.Start(); err != nil {
fmt.Printf("Failed to start agent %d: %v\n", i, err)
return
}
defer agent.Stop()
}
time.Sleep(100 * time.Millisecond)
// Connect agents in full mesh
fmt.Println("Establishing ZAP mesh network...")
for i := 0; i < len(agents); i++ {
for j := i + 1; j < len(agents); j++ {
addr := fmt.Sprintf("127.0.0.1:%d", basePort+j)
if err := agents[i].ConnectTo(addr); err != nil {
fmt.Printf("Warning: %s failed to connect to %s: %v\n",
agents[i].config.Name, agents[j].config.Name, err)
}
}
}
time.Sleep(300 * time.Millisecond)
// Show network topology
fmt.Println("\nNetwork Topology:")
for _, agent := range agents {
peers := agent.GetConnectedPeers()
caps := agent.GetPeerCapabilities()
totalCaps := 0
for _, c := range caps {
totalCaps += len(c)
}
fmt.Printf(" %s: %d peers, %d discovered capabilities\n",
agent.config.Name, len(peers), totalCaps)
}
fmt.Println()
// Get query from args or use default
query := "What is the most important principle in software engineering?"
if len(os.Args) > 1 {
query = strings.Join(os.Args[1:], " ")
}
fmt.Printf("Query: %s\n", query)
fmt.Println("\nBroadcasting to swarm via ZAP protocol...")
fmt.Println()
// Claude initiates the query
start := time.Now()
agents[0].BroadcastQuery(1, query)
// Also process locally
go func() {
response := agents[0].generateResponse(query)
agents[0].broadcastResponse(1, response)
}()
// Wait for consensus
timeout := time.After(15 * time.Second)
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-timeout:
fmt.Println("\nTimeout waiting for consensus")
goto done
case <-ticker.C:
if synthesis, ok := agents[4].GetSynthesis(1); ok {
elapsed := time.Since(start)
fmt.Printf("Consensus achieved in %v\n", elapsed)
_ = synthesis // Already printed by handleSynthesis
goto done
}
}
}
done:
// Print stats
fmt.Println("\n=== Agent Statistics ===")
for _, agent := range agents {
fmt.Printf(" %s: queries=%d responses=%d votes=%d tools=%d\n",
agent.config.Name,
agent.queryCount.Load(),
agent.responseCount.Load(),
agent.voteCount.Load(),
agent.toolCallCount.Load(),
)
}
}