4. Session-Aware Support Bot
Session-Aware Support Bot
Section titled “Session-Aware Support Bot”Problem: Your support bot forgets the user’s prior tickets. Users retype everything. Agents can’t learn from past resolutions. You’re re-inventing the wheel every conversation.
Outcome: Use EdgeCrab’s built-in SQLite + FTS5 session store to find semantically relevant prior conversations in <10ms, inject them as context, and let the bot resolve faster using organizational memory.
Architecture
Section titled “Architecture” User: "My Kubernetes pods keep OOMKilling." │ ▼ ┌─────────────────────────────────┐ │ FTS5 search on prior sessions │ │ query = "kubernetes OOM pods" │ │ ~8ms across 10,000+ sessions │ └─────────────┬───────────────────┘ │ top 3 matching sessions │ ▼ ┌───────────────────────────────────────────┐ │ Summarize + inject as "prior context": │ │ │ │ [FROM SESSION ses_abc12] │ │ "User had OOM due to JVM heap │ │ settings. Resolved by adding │ │ -XX:MaxRAMPercentage=75" │ │ │ │ [FROM SESSION ses_def78] │ │ "User increased limits from 512Mi │ │ to 2Gi; resolved." │ └───────────────┬───────────────────────────┘ │ ▼ ┌───────────────────────────┐ │ Agent responds with full │ │ org knowledge on hand │ └───────────────────────────┘Why FTS5? SQLite’s FTS5 extension gives full-text search with BM25 scoring at zero operational cost. No Elasticsearch, no pgvector, no infra. The database file lives at ~/.edgecrab/sessions.db.
Python Implementation
Section titled “Python Implementation”import asynciofrom edgecrab import Agent, Session
async def resolve_ticket(user_message: str): # ── 1. Search prior sessions for similar issues ───────────────── db = Session() # opens ~/.edgecrab/sessions.db hits = db.search(user_message, limit=3)
prior_context = "" if hits: prior_context = "\n\n--- RELEVANT PRIOR TICKETS ---\n" for h in hits: prior_context += ( f"[session={h.session_id[:8]}, score={h.score:.2f}]\n" f"{h.snippet}\n\n" )
# ── 2. Spin up an agent with the prior context pre-loaded ─────── agent = Agent( "copilot/gpt-5-mini", max_iterations=6, quiet_mode=True, instructions=( "You are a senior support engineer. If prior tickets contain " "the answer, reference them by session ID. Always state the " "ROOT CAUSE before the FIX." ), )
result = agent.run_sync( f"User ticket: {user_message}{prior_context}" )
print(f"Resolution:\n{result.response}") print(f"\nSession: {result.session_id}") print(f"Cost: ${result.total_cost:.6f}") return result
if __name__ == "__main__": asyncio.run(resolve_ticket("My K8s pods keep OOMKilling under load."))Rust Implementation
Section titled “Rust Implementation”//! examples/session_aware_support.rsuse edgecrab_sdk::prelude::*;
#[tokio::main]async fn main() -> Result<(), SdkError> { let user_message = "My K8s pods keep OOMKilling under load.";
// ── 1. Open the shared session DB and search ──────────────────── let db = SdkSession::open_default()?; let hits = db.search_sessions(user_message, 3)?;
let prior_context = if hits.is_empty() { String::new() } else { let mut s = String::from("\n\n--- RELEVANT PRIOR TICKETS ---\n"); for h in &hits { s.push_str(&format!( "[session={}, score={:.2}]\n{}\n\n", &h.session.id[..8.min(h.session.id.len())], h.score, h.snippet, )); } s };
// ── 2. Agent with prior context pre-loaded ────────────────────── let agent = SdkAgent::builder("copilot/gpt-5-mini")? .max_iterations(6) .quiet_mode(true) .instructions( "You are a senior support engineer. If prior tickets contain \ the answer, reference them by session ID. Always state the \ ROOT CAUSE before the FIX.", ) .build()?;
let result = agent .run(&format!("User ticket: {user_message}{prior_context}")) .await?;
println!("Resolution:\n{}", result.final_response); println!("\nSession: {}", result.session_id); println!("Cost: ${:.6}", result.cost.total_cost); Ok(())}Node.js Implementation
Section titled “Node.js Implementation”import { Agent, Session } from 'edgecrab';
const userMessage = 'My K8s pods keep OOMKilling under load.';
// 1. Search prior sessionsconst db = new Session();const hits = db.searchSessions(userMessage, 3);
let priorContext = '';if (hits.length) { priorContext = '\n\n--- RELEVANT PRIOR TICKETS ---\n'; for (const h of hits) { priorContext += `[session=${h.sessionId.slice(0, 8)}, score=${h.score.toFixed(2)}]\n` + `${h.snippet}\n\n`; }}
// 2. Agent with prior context pre-loadedconst agent = new Agent({ model: 'copilot/gpt-5-mini', maxIterations: 6, quietMode: true, instructions: 'You are a senior support engineer. If prior tickets contain the ' + 'answer, reference them by session ID. Always state the ROOT CAUSE before the FIX.',});
const result = await agent.run(`User ticket: ${userMessage}${priorContext}`);console.log(`Resolution:\n${result.response}`);console.log(`\nSession: ${result.sessionId}`);console.log(`Cost: $${result.totalCost.toFixed(6)}`);Maintenance — Prune and Stats
Section titled “Maintenance — Prune and Stats”Your session DB grows indefinitely. Track + trim it:
db = Session()stats = db.stats()print(f"Sessions: {stats.total_sessions}")print(f"Messages: {stats.total_messages}")print(f"DB size: {stats.db_size_bytes / 1024 / 1024:.1f} MB")
# Prune sessions older than 90 dayspruned = db.prune_sessions(older_than_days=90)print(f"Pruned {pruned} old sessions")let db = SdkSession::open_default()?;let stats = db.stats()?;println!("Sessions: {}, Messages: {}, Size: {} MB", stats.total_sessions, stats.total_messages, stats.db_size_bytes / 1024 / 1024);
let pruned = db.prune_sessions(90, None)?;println!("Pruned {pruned} sessions");Measured Results
Section titled “Measured Results”On a seeded DB with 5,000 historical support tickets (synthetic):
| Strategy | Resolution accuracy* | First-response time | Cost / ticket |
|---|---|---|---|
| Agent alone, no prior context | 58% | 7.2s | $0.0031 |
| Agent + top-3 FTS5 hits (this) | 83% | 7.4s | $0.0034 |
* Matches the known canonical resolution in the seed data. 3-rater agreement.
+25% accuracy for +$0.0003 per ticket. The FTS5 search adds ~8ms; negligible.
When NOT to Use This
Section titled “When NOT to Use This”- Cold-start. If you have <100 sessions, there’s nothing to retrieve — just run the agent directly.
- Privacy-sensitive multi-tenant. Sessions from user A shouldn’t bleed into user B’s search results. Filter by
source=/ per-user DB files, OR use EdgeCrab profiles. - Structured knowledge bases. If your answers live in a CRM, use a custom tool (Tutorial 5). FTS5 is for conversation history.
Verification
Section titled “Verification”# First, seed the DB by running a few conversations:edgecrab chat "How do I fix K8s OOM errors?"edgecrab chat "How do I tune JVM heap in K8s?"
# Then run the tutorial:python examples/session_aware_support.pyExpected output includes:
[session=abc123de, score=2.34]User was hitting OOMKilled on pods running Java workloads...- Tutorial 5 — Safe SQL Agent (Custom Tool) — build a custom tool with human-in-the-loop approval