AjinkyaPagare's picture
fix: python3.10, disable flash-attn, reduce memory for HF Spaces CPU
043a495
"""
Knowledge Web — persistent graph-based knowledge store.
Ultra-lightweight knowledge graph that stores agent solutions,
allowing agents to learn from past executions and share knowledge
across sessions. Uses a simple JSON-backed graph structure.
"""
import os
import json
import time
import hashlib
from typing import Optional
_STORE_PATH = os.getenv("ADAM_KNOWLEDGE_PATH", "/tmp/adam_knowledge.json")
_MAX_NODES = int(os.getenv("ADAM_KNOWLEDGE_MAX", "500"))
class KnowledgeWeb:
"""
Persistent knowledge graph for agent learning.
Stores:
- Solutions: goal → result mappings with confidence scores
- Concepts: extracted named entities and relationships
- Patterns: reusable execution patterns
Queries:
- Similarity search over past solutions
- Concept relationship traversal
- Pattern matching for known goal types
"""
def __init__(self):
self._solutions: dict[str, dict] = {}
self._concepts: dict[str, set] = {} # concept → related goal hashes
self._load()
def _load(self):
"""Load knowledge from disk."""
try:
if os.path.exists(_STORE_PATH):
with open(_STORE_PATH, "r") as f:
data = json.load(f)
self._solutions = data.get("solutions", {})
self._concepts = {k: set(v) for k, v in data.get("concepts", {}).items()}
except Exception:
pass
def _save(self):
"""Persist knowledge to disk."""
try:
os.makedirs(os.path.dirname(_STORE_PATH) or ".", exist_ok=True)
# Limit size
solutions = dict(sorted(
self._solutions.items(),
key=lambda x: x[1].get("ts", 0),
reverse=True
)[:_MAX_NODES])
with open(_STORE_PATH, "w") as f:
json.dump({
"solutions": solutions,
"concepts": {k: list(v) for k, v in self._concepts.items()},
}, f)
except Exception:
pass
async def store_solution(self, goal: str, action: str, result: str,
confidence: float = 0.8):
"""Store a successful solution for future reuse."""
goal_hash = hashlib.md5(goal.lower().encode()).hexdigest()[:16]
self._solutions[goal_hash] = {
"goal": goal[:200],
"action": action[:100],
"result": result[:500],
"confidence": min(1.0, max(0.0, confidence)),
"ts": time.time(),
"use_count": self._solutions.get(goal_hash, {}).get("use_count", 0) + 1,
}
# Extract and index concepts
concepts = self._extract_concepts(goal)
for concept in concepts:
if concept not in self._concepts:
self._concepts[concept] = set()
self._concepts[concept].add(goal_hash)
self._save()
async def query_solution(self, goal: str, context: str = "",
min_confidence: float = 0.0) -> Optional[dict]:
"""
Query for a past solution matching the goal.
Returns the best match if confidence threshold is met.
"""
if not self._solutions:
return None
goal_lower = goal.lower()
goal_hash = hashlib.md5(goal_lower.encode()).hexdigest()[:16]
# Exact match
if goal_hash in self._solutions:
entry = self._solutions[goal_hash]
entry["confidence"] = min(1.0, entry.get("confidence", 1.0) * 0.95 + 0.05)
return {
"summary": f"Found past solution (used {entry.get('use_count', 0)} times)",
"result": entry["result"],
"confidence": entry["confidence"],
"action": entry["action"],
}
# Concept-based match
goal_concepts = self._extract_concepts(goal)
best_match = None
best_score = 0.0
for concept in goal_concepts:
if concept in self._concepts:
for related_hash in self._concepts[concept]:
if related_hash in self._solutions:
entry = self._solutions[related_hash]
score = entry.get("confidence", 0.5) * 0.8 + 0.2
if score > best_score:
best_score = score
best_match = entry
if best_match and best_score >= min_confidence:
return {
"summary": f"Found related solution (confidence: {best_score:.2f})",
"result": best_match["result"],
"confidence": best_score,
"action": best_match["action"],
}
return None
def _extract_concepts(self, text: str) -> list[str]:
"""Extract key concepts from text for indexing."""
text_lower = text.lower()
concepts = set()
# Common concept patterns
patterns = [
"code", "python", "javascript", "function", "script",
"search", "find", "research", "lookup",
"file", "data", "database", "api",
"write", "create", "generate", "build",
"analyze", "analyze", "compare", "evaluate",
"debug", "fix", "repair", "error",
"install", "setup", "configure", "deploy",
"learn", "explain", "understand", "tutorial",
]
for pattern in patterns:
if pattern in text_lower:
concepts.add(pattern)
# Add significant words (3+ chars)
words = set(w for w in text_lower.split() if len(w) >= 4 and w.isalpha())
concepts.update(list(words)[:5])
return list(concepts)
async def size(self) -> int:
"""Get the number of stored solutions."""
return len(self._solutions)
async def clear(self):
"""Clear all knowledge."""
self._solutions.clear()
self._concepts.clear()
self._save()
def get_stats(self) -> dict:
"""Get knowledge web statistics."""
return {
"solutions": len(self._solutions),
"concepts": len(self._concepts),
"top_concepts": sorted(
[(k, len(v)) for k, v in self._concepts.items()],
key=lambda x: x[1], reverse=True
)[:5],
}