# ADK-Go + Episteme Integration Guide > **Purpose:** Technical reference for integrating AI agents with Episteme using Google's Agent Development Kit for Go (ADK-Go). > **For use case context:** See [use-cases/agile-agent-team.md](../../../use-cases/agile-agent-team.md) ## Overview This guide shows how AI agents integrate with Episteme for: - **Query** - Retrieve knowledge with lens-based resolution - **Assert** - Store knowledge with lifecycle and confidence - **Constraint Check** - Pre-flight validation before actions - **Trace** - Audit trail for incident investigation - **Supersede** - Correct errors with cascade tracking Each agent type has specific patterns for tool usage and callback integration. --- ## Tool Definitions Every Episteme operation is exposed as an ADK-Go tool: ```go package episteme import ( "google.golang.org/adk/tool" "google.golang.org/adk/tool/functiontool" ) // === QUERY TOOL === // Used by: All agents type QueryInput struct { Subject string `json:"subject" jsonschema:"Entity to query (e.g., auth/jwt)"` Predicate string `json:"predicate" jsonschema:"Relation to query (e.g., signing_algorithm)"` Lens string `json:"lens,omitempty" jsonschema:"Resolution: consensus, authority, recency, constraints"` Lifecycle string `json:"lifecycle,omitempty" jsonschema:"Filter: proposed, approved, deprecated"` MinConfidence float32 `json:"min_confidence,omitempty" jsonschema:"Minimum confidence threshold (0.0-1.0)"` AsOf string `json:"as_of,omitempty" jsonschema:"Time-travel: ISO8601 timestamp"` } type QueryOutput struct { Value interface{} `json:"value"` Confidence float32 `json:"confidence"` Lifecycle string `json:"lifecycle"` Sources []Source `json:"sources"` QueryID string `json:"query_id"` // CRITICAL: for audit trail } type Source struct { Hash string `json:"hash"` SourceHash string `json:"source_hash"` Weight float32 `json:"weight"` } func queryEpisteme(ctx tool.Context, input QueryInput) QueryOutput { // Call Episteme API result, err := epistemeClient.Query(ctx, input) if err != nil { return QueryOutput{Error: err.Error()} } return result } // === ASSERT TOOL === // Used by: Research Agent, Human Supervisor type AssertInput struct { Subject string `json:"subject" jsonschema:"Entity being described"` Predicate string `json:"predicate" jsonschema:"Relation being asserted"` Object interface{} `json:"object" jsonschema:"Value being claimed"` SourceHash string `json:"source_hash" jsonschema:"BLAKE3 hash of evidence"` Confidence float32 `json:"confidence" jsonschema:"Certainty level (0.0-1.0)"` Lifecycle string `json:"lifecycle,omitempty" jsonschema:"proposed, under_review, approved"` ParentHash string `json:"parent_hash,omitempty" jsonschema:"Hash of assertion being updated"` Meta *AssertMeta `json:"meta,omitempty" jsonschema:"Negative constraints and metadata"` } type AssertMeta struct { ForbiddenAlternative string `json:"forbidden_alternative,omitempty"` Reason string `json:"reason,omitempty"` } type AssertOutput struct { Hash string `json:"hash"` Success bool `json:"success"` Error string `json:"error,omitempty"` } // === CONSTRAINT CHECK TOOL === // Used by: Implementation Agent (pre-flight) type ConstraintCheckInput struct { Context string `json:"context" jsonschema:"Domain context (e.g., python_http, auth_jwt)"` } type ConstraintCheckOutput struct { Constraints []Constraint `json:"constraints"` } type Constraint struct { Subject string `json:"subject"` MustUse string `json:"must_use,omitempty"` Forbidden string `json:"forbidden,omitempty"` Reason string `json:"reason"` } // === TRACE TOOL === // Used by: On-Call SRE, Human Supervisor type TraceInput struct { AgentID string `json:"agent_id" jsonschema:"Agent to trace"` From string `json:"from" jsonschema:"Start time (ISO8601 or relative like -6h)"` To string `json:"to,omitempty" jsonschema:"End time (default: now)"` Subject string `json:"subject,omitempty" jsonschema:"Filter by subject pattern (e.g., auth/*)"` } type TraceOutput struct { Queries []QueryTrace `json:"queries"` } type QueryTrace struct { QueryID string `json:"query_id"` Timestamp string `json:"timestamp"` Subject string `json:"subject"` Predicate string `json:"predicate"` Lens string `json:"lens"` Result string `json:"result"` Confidence float32 `json:"confidence"` Contributing []string `json:"contributing_assertions"` } // === SUPERSEDE TOOL === // Used by: Human Supervisor type SupersedeInput struct { Hash string `json:"hash" jsonschema:"Assertion hash to supersede"` Reason string `json:"reason" jsonschema:"Why this is being superseded"` Type string `json:"type" jsonschema:"Invalidate, Temporal, RequiresReview, Additive"` } type SupersedeOutput struct { NewHash string `json:"new_hash"` AffectedAssertions []string `json:"affected_assertions"` } ``` --- ## Callback Integration Callbacks are critical for enforcing constraints and maintaining audit trails: ```go import ( "google.golang.org/adk/agent" "google.golang.org/adk/agent/llmagent" "google.golang.org/adk/model" "google.golang.org/adk/tool" ) // Implementation Agent with BeforeToolCallback for constraint checking implementationAgent, err := llmagent.New(llmagent.Config{ Name: "implementation_agent", Model: model, Description: "Writes code against current approved patterns", Instruction: "You write code. Always use approved patterns only.", Tools: []tool.Tool{queryTool, constraintTool}, // CRITICAL: Check constraints BEFORE any tool that generates code BeforeToolCallback: func(ctx agent.CallbackContext, call *tool.Call) (*tool.Call, error) { // If agent is about to write code, check constraints first if needsConstraintCheck(call) { context := extractDomainContext(call) // e.g., "python_http" constraints, err := checkConstraints(ctx, context) if err != nil { return nil, fmt.Errorf("constraint check failed: %w", err) } for _, c := range constraints { if violatesConstraint(call, c) { return nil, fmt.Errorf("blocked: %s is forbidden - %s", c.Forbidden, c.Reason) } } } return call, nil }, // Log decisions for audit trail AfterModelCallback: func(ctx agent.CallbackContext, resp *model.LLMResponse) (*model.LLMResponse, error) { decision := extractDecision(resp) logToEpisteme(ctx, AuditEntry{ AgentID: ctx.Agent().Name(), Timestamp: time.Now(), Decision: decision, SessionID: ctx.Session().ID(), }) return resp, nil }, }) // Lead Orchestrator with confidence threshold escalation leadOrchestrator, err := llmagent.New(llmagent.Config{ Name: "lead_orchestrator", Model: model, Description: "Coordinates agent team, routes work based on knowledge", Instruction: `You coordinate the agent team. Query Episteme for current state. If confidence < 0.8, escalate to human supervisor.`, Tools: []tool.Tool{queryTool, delegateTool}, OutputKey: "orchestrator_decision", // Pass to downstream agents // Check confidence scores and escalate if too low AfterToolCallback: func(ctx agent.CallbackContext, call *tool.Call, result *tool.Result) (*tool.Result, error) { if call.Name == "episteme_query" { var queryResult QueryOutput json.Unmarshal(result.Output, &queryResult) if queryResult.Confidence < 0.8 { // Mark for human review ctx.Session().State().Set("needs_human_review", true) ctx.Session().State().Set("low_confidence_query", queryResult.QueryID) } } return result, nil }, }) ``` --- ## Agent-Specific Patterns ### Lead Orchestrator Fast queries with confidence thresholds for routing decisions: ```go // Query current auth config with confidence threshold result := queryEpisteme(ctx, QueryInput{ Subject: "auth/jwt", Predicate: "signing_algorithm", Lens: "authority", MinConfidence: 0.8, }) if result.Confidence < 0.8 { // Escalate to human - can't route confidently ctx.Session().State().Set("escalation_reason", fmt.Sprintf("Confidence %.2f below threshold for %s/%s", result.Confidence, result.Subject, result.Predicate)) return escalateToHuman(ctx) } // Route to implementation agent with high confidence ctx.Session().State().Set("auth_config", result.Value) return delegateTo(ctx, "implementation_agent") ``` ### Implementation Agent Queries approved patterns only, with pre-flight constraint checks: ```go // CRITICAL: Filter to approved lifecycle ONLY result := queryEpisteme(ctx, QueryInput{ Subject: "auth/jwt", Predicate: "signing_algorithm", Lens: "authority", Lifecycle: "approved", // Never use proposed patterns! }) if result.Lifecycle != "approved" { return fmt.Errorf("no approved pattern found for %s/%s", input.Subject, input.Predicate) } // Pre-flight: check for forbidden alternatives constraints := checkConstraints(ctx, "auth_jwt") for _, c := range constraints.Constraints { // Agent now knows: "use X, don't use Y, because Z" // This enables contrastive learning } ``` ### Research Agent Stores conflicting information with uncertainty: ```go // Store first source assertKnowledge(ctx, AssertInput{ Subject: "jwt_rotation", Predicate: "best_practice", Object: "rotate_daily", SourceHash: hashURL("https://security.io/jwt-best-practices"), Confidence: 0.7, // Express uncertainty Lifecycle: "proposed", // Not approved yet }) // Store contradicting source - don't flatten! assertKnowledge(ctx, AssertInput{ Subject: "jwt_rotation", Predicate: "best_practice", Object: "rotate_hourly", SourceHash: hashURL("https://owasp.org/jwt-rotation"), Confidence: 0.8, Lifecycle: "proposed", }) // Let Lead Orchestrator resolve via Lens::Consensus or Lens::Authority ``` ### Human Supervisor Time-travel queries and corrections with impact analysis: ```go // What was believed during the incident? result := queryEpisteme(ctx, QueryInput{ Subject: "auth/jwt", Predicate: "signing_algorithm", AsOf: "2024-01-15T21:00:00Z", // Time of incident }) // Trace agent queries during the incident window traces := traceQueries(ctx, TraceInput{ AgentID: "deployment-agent", From: "2024-01-15T20:00:00Z", To: "2024-01-15T22:00:00Z", Subject: "auth/*", }) // Found the bug: agent queried without lifecycle filter // Correct the record impact := supersede(ctx, SupersedeInput{ Hash: badAssertionHash, Reason: "Proposal treated as approved - agent didn't filter by lifecycle", Type: "Invalidate", }) // impact.AffectedAssertions shows downstream effects fmt.Printf("Corrected. %d downstream assertions affected.\n", len(impact.AffectedAssertions)) ``` ### On-Call SRE Sub-second trace commands for incident investigation: ```go // It's 3am. Auth is broken. What happened? // Step 1: What did deployment agent query? (<500ms required) traces := traceQueries(ctx, TraceInput{ AgentID: "deployment-agent", From: "-6h", Subject: "auth/*", }) for _, t := range traces.Queries { fmt.Printf("[%s] %s/%s via %s -> %s (conf: %.2f)\n", t.Timestamp, t.Subject, t.Predicate, t.Lens, t.Result, t.Confidence) if len(t.Contributing) > 0 { fmt.Printf(" Contributing: %v\n", t.Contributing) } } // Step 2: What changed in the last 24 hours? diff := queryDiff(ctx, DiffInput{ Subject: "auth/jwt", From: "-24h", }) // Step 3: Mark bad assertion (found via trace) supersede(ctx, SupersedeInput{ Hash: badAssertionHash, Reason: "RFC proposal incorrectly treated as approved config", Type: "Invalidate", }) // Step 4: Verify fix result := queryEpisteme(ctx, QueryInput{ Subject: "auth/jwt", Predicate: "signing_algorithm", Lifecycle: "approved", }) fmt.Printf("Current approved value: %s\n", result.Value) ``` --- ## Multi-Agent Pipeline Example Complete example showing agents coordinating through Episteme: ```go package main import ( "context" "google.golang.org/adk/agent" "google.golang.org/adk/agent/llmagent" "google.golang.org/adk/agent/sequentialagent" "google.golang.org/adk/model/gemini" ) func main() { ctx := context.Background() model, _ := gemini.NewModel(ctx, "gemini-3-flash-preview", nil) // Research Agent: Discovers and stores knowledge researchAgent, _ := llmagent.New(llmagent.Config{ Name: "research_agent", Model: model, Description: "Researches and stores knowledge with source attribution", Instruction: `Research the topic. Store findings with confidence scores. Mark conflicting sources - don't resolve them.`, Tools: []tool.Tool{assertTool, searchTool}, OutputKey: "research_findings", }) // Lead Orchestrator: Queries and routes leadAgent, _ := llmagent.New(llmagent.Config{ Name: "lead_orchestrator", Model: model, Description: "Coordinates team based on current knowledge state", Instruction: `Query Episteme for current approved patterns. Research findings available in {research_findings}. If confidence >= 0.8, route to implementation. If confidence < 0.8, flag for human review.`, Tools: []tool.Tool{queryTool}, OutputKey: "routing_decision", AfterToolCallback: confidenceEscalationCallback, }) // Implementation Agent: Writes code implAgent, _ := llmagent.New(llmagent.Config{ Name: "implementation_agent", Model: model, Description: "Writes code against approved patterns only", Instruction: `Write code for the task. Routing decision in {routing_decision}. ONLY use approved patterns. Check constraints first.`, Tools: []tool.Tool{queryTool, constraintTool, writeCodeTool}, OutputKey: "implementation", BeforeToolCallback: constraintEnforcementCallback, }) // Sequential pipeline pipeline, _ := sequentialagent.New(sequentialagent.Config{ AgentConfig: agent.Config{ Name: "AgileDevPipeline", Description: "Research -> Route -> Implement with Episteme coordination", SubAgents: []agent.Agent{researchAgent, leadAgent, implAgent}, }, }) // Run the pipeline runner := agent.NewRunner(pipeline, sessionService) for event, err := range runner.Run(ctx, userID, sessionID, userMessage, nil) { if err != nil { log.Printf("Pipeline error: %v", err) } handleEvent(event) } } ``` --- ## Session State vs. Episteme | Use Case | Mechanism | Why | |----------|-----------|-----| | Agent-to-agent handoff in pipeline | Session State + `OutputKey` | Ephemeral, same conversation | | Permanent organizational knowledge | Episteme `Assert` | Persists across sessions/agents | | "What was decided in this conversation" | Session State | Temporary, conversation-scoped | | "What should all agents know forever" | Episteme `Assert` + `lifecycle: approved` | Permanent, query-audited | | Temporary working data | `temp:` prefixed state keys | Auto-cleared after session | | Agent decision audit trail | Episteme `QueryAudit` | Immutable, time-travel enabled | --- ## CLI Commands for Debugging ```bash # Trace what an agent queried episteme trace --agent deployment-agent --time "6 hours ago" --subject "auth/*" # Show assertion provenance episteme show --hash $ASSERTION_HASH --provenance # Supersede bad assertion episteme supersede --hash $BAD_HASH --reason "Proposal treated as approved" \ --type "Invalidate" # See cascade impact episteme cascade --root $BAD_HASH # Verify correction episteme query --subject auth/jwt --predicate signing_algorithm --lifecycle approved ``` **SRE's 3am workflow:** Query -> Trace -> Identify -> Correct -> Verify. Under 10 minutes.