stemedb/ai-lookup/patterns/adk-integration.md
jordan 3cfaa1e1d3 feat: Complete Phase 1 (The Spine) - storage foundation
Phase 1 delivers the complete durability and storage layer:

- WAL with crash recovery: Append-only journal with BLAKE3 checksums,
  fsync guarantees, and proper seek-to-EOF on reopen
- Storage engine: sled-backed KVStore with scan_prefix for range queries
- Content-addressed storage: H:{hash}, V:{hash}, E:{hash} key patterns
- Ingestor: Background worker tailing WAL, writing to KV with 8-byte
  aligned record headers for rkyv zero-copy deserialization
- Comprehensive tests: 31 tests covering crash recovery, round-trips,
  and multi-cycle durability

New crates: stemedb-wal, stemedb-storage, stemedb-ingest

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 14:15:34 -07:00

9.6 KiB

ADK-Go Integration Pattern

Summary

Tool definitions and callback patterns for Google ADK-Go agents consuming Episteme. Covers query, assert, constraint check, and trace operations with proper struct tags and callback signatures.

Core Mechanism

ADK-Go agents interact with Episteme through:

  1. Tools - Structured operations (query, assert, trace) defined as Go structs
  2. Callbacks - Hooks for pre-flight checks and audit logging
  3. State - Session state for agent-to-agent communication

Tool Definition Pattern

All Episteme tools follow this structure:

import (
    "google.golang.org/adk/tool"
    "google.golang.org/adk/tool/functiontool"
)

type [Operation]Input struct {
    // Required fields (no omitempty)
    Subject   string `json:"subject" jsonschema:"Entity to query (e.g., auth/jwt)"`
    Predicate string `json:"predicate" jsonschema:"Relation to query (e.g., signing_algorithm)"`

    // Optional fields (with omitempty)
    Lens      string `json:"lens,omitempty" jsonschema:"Resolution: consensus, authority, recency, constraints"`
}

type [Operation]Output struct {
    Value      interface{} `json:"value"`
    Confidence float32     `json:"confidence"`
    QueryID    string      `json:"query_id"`  // CRITICAL: for audit trail
    Error      string      `json:"error,omitempty"`
}

func [operation](ctx tool.Context, input [Operation]Input) [Operation]Output {
    // 1. Call Episteme API
    // 2. Return structured result with QueryID
}

// Register with functiontool.New()
tool, err := functiontool.New(
    functiontool.Config{
        Name:        "episteme_query",
        Description: "Query Episteme knowledge graph",
    },
    queryEpisteme,
)

Standard Tools

Query Tool

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"`
}

Assert Tool

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:"Additional 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

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 (SRE)

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"`
}

Callback Integration

BeforeToolCallback - Constraint Pre-flight

Critical for preventing repeat mistakes:

agent, err := llmagent.New(llmagent.Config{
    Name:  "implementation_agent",
    Model: model,
    Tools: []tool.Tool{queryTool, assertTool},

    BeforeToolCallback: func(ctx agent.CallbackContext, call *tool.Call) (*tool.Call, error) {
        // Check constraints before any tool that could use wrong patterns
        if needsConstraintCheck(call) {
            constraints := checkConstraints(ctx, extractContext(call))
            for _, c := range constraints {
                if violates(call, c) {
                    return nil, fmt.Errorf("blocked: %s is forbidden - %s",
                        c.Forbidden, c.Reason)
                }
            }
        }
        return call, nil
    },
})

AfterModelCallback - Audit Trail

Log agent decisions for debugging:

AfterModelCallback: func(ctx agent.CallbackContext, resp *model.LLMResponse) (*model.LLMResponse, error) {
    // Log what the agent decided for future tracing
    decision := extractDecision(resp)
    logToEpisteme(ctx, AuditEntry{
        AgentID:   ctx.Agent().Name(),
        Timestamp: time.Now(),
        Decision:  decision,
        Context:   ctx.Session().State(),
    })
    return resp, nil
},

AfterToolCallback - Error Learning

Trigger Gardener when tools fail:

AfterToolCallback: func(ctx agent.CallbackContext, call *tool.Call, result *tool.Result) (*tool.Result, error) {
    if result.Error != nil {
        // Notify Gardener of the failure for TrustRank back-propagation
        notifyGardener(ctx, GardenerEvent{
            AgentID:     ctx.Agent().Name(),
            ToolCall:    call,
            Error:       result.Error,
            QueryID:     extractQueryID(call),
        })
    }
    return result, nil
},

Agent-Specific Patterns

Lead Orchestrator

Fast queries with confidence thresholds:

result := queryEpisteme(ctx, QueryInput{
    Subject:       "auth/jwt",
    Predicate:     "signing_algorithm",
    Lens:          "authority",
    MinConfidence: 0.8,
})
if result.Confidence < 0.8 {
    // Escalate to human or research agent
    return escalate(ctx, result)
}

Implementation Agent

Approved patterns only:

result := queryEpisteme(ctx, QueryInput{
    Subject:   "auth/jwt",
    Predicate: "signing_algorithm",
    Lens:      "authority",
    Lifecycle: "approved",  // CRITICAL: never use proposed patterns
})

Research Agent

Store with uncertainty and contradictions:

assertKnowledge(ctx, AssertInput{
    Subject:    "jwt_rotation",
    Predicate:  "best_practice",
    Object:     "rotate_daily",
    SourceHash: hashURL(sourceURL),
    Confidence: 0.7,  // Express uncertainty
    Lifecycle:  "proposed",
})

// Store contradicting information - don't flatten
assertKnowledge(ctx, AssertInput{
    Subject:    "jwt_rotation",
    Predicate:  "best_practice",
    Object:     "rotate_weekly",
    SourceHash: hashURL(differentSourceURL),
    Confidence: 0.6,
    Lifecycle:  "proposed",
})

Human Supervisor

Time-travel and corrections:

// What was believed during the incident?
result := queryEpisteme(ctx, QueryInput{
    Subject:   "auth/jwt",
    Predicate: "signing_algorithm",
    AsOf:      "2024-01-15T21:00:00Z",
})

// Correct the record
supersede(ctx, SupersedeInput{
    Hash:   badAssertionHash,
    Reason: "Proposal treated as approved - was never reviewed",
    Type:   "Invalidate",
})

On-Call SRE

Trace agent decisions:

traces := traceAgentQueries(ctx, TraceInput{
    AgentID: "deployment-agent",
    From:    "-6h",
    Subject: "auth/*",
})

for _, t := range traces.Queries {
    fmt.Printf("[%s] %s/%s via %s -> %s (%.2f)\n",
        t.Timestamp, t.Subject, t.Predicate,
        t.Lens, t.Result, t.Confidence)
    fmt.Printf("  Contributing: %v\n", t.Contributing)
}

State vs Episteme

Use Mechanism
Agent-to-agent handoff (same session) Session State + OutputKey
Persistent organizational knowledge Episteme Assert
What was decided in this conversation Session State
What should all agents know forever Episteme Assert
Temporary working data temp: prefixed state keys
Audit trail Episteme QueryAudit