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>
9.6 KiB
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:
- Tools - Structured operations (query, assert, trace) defined as Go structs
- Callbacks - Hooks for pre-flight checks and audit logging
- 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 |
Related
- services/assertion.md - Core assertion structure
- services/lens.md - Lens resolution strategies
- services/lifecycle.md - Lifecycle stage filtering
- features/gardener.md - Error back-propagation
- features/query-audit.md - Trace and debug