Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e0f5e7e9a |
165
src/proactive_search.py
Normal file
165
src/proactive_search.py
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
proactive_search.py - Proaktive Websuche für Second Brain.
|
||||||
|
Sucht relevante Themen, speichert Ergebnisse als Engramme.
|
||||||
|
Stoppt wenn neue Aufgaben erkannt werden.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime, timezone, timedelta
|
||||||
|
from typing import List, Dict, Any, Optional
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||||
|
|
||||||
|
from src.store import EngramStore
|
||||||
|
from src.engram import Engram, Grounding
|
||||||
|
from src.retriever import Retriever
|
||||||
|
from src.embedder import encode
|
||||||
|
from src.chroma_store import ChromaStore
|
||||||
|
|
||||||
|
DB_PATH = Path(__file__).resolve().parent.parent / "data" / "brain.sqlite"
|
||||||
|
CHROMA_PATH = Path(__file__).resolve().parent.parent / "data" / "chroma"
|
||||||
|
|
||||||
|
# Themen die relevant sind für den Benutzer
|
||||||
|
INTEREST_TOPICS = [
|
||||||
|
"OpenClaw AI Agent",
|
||||||
|
"Künstliche Intelligenz Trends 2025",
|
||||||
|
"Second Brain Memory System",
|
||||||
|
"Automation DIY Projects",
|
||||||
|
"Smart Home IoT",
|
||||||
|
"Raspberry Pi Projects",
|
||||||
|
"Deutschland Tech News",
|
||||||
|
"AI Agent Frameworks",
|
||||||
|
"Workflow Automation",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_store():
|
||||||
|
return EngramStore(str(DB_PATH))
|
||||||
|
|
||||||
|
|
||||||
|
def load_state() -> Dict[str, Any]:
|
||||||
|
"""Lädt den Such-Zustand."""
|
||||||
|
state_path = Path(__file__).resolve().parent.parent / "data" / "search_state.json"
|
||||||
|
if state_path.exists():
|
||||||
|
with open(state_path, "r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
return {
|
||||||
|
"last_search": None,
|
||||||
|
"searched_topics": [],
|
||||||
|
"new_tasks_detected": False,
|
||||||
|
"paused_until": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def save_state(state: Dict[str, Any]):
|
||||||
|
state_path = Path(__file__).resolve().parent.parent / "data" / "search_state.json"
|
||||||
|
with open(state_path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(state, f, ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
|
def check_for_new_tasks(store: EngramStore) -> bool:
|
||||||
|
"""Prüft ob in letzten 2h neue Aufgaben-Artige Engramme erstellt wurden."""
|
||||||
|
now = datetime.now(timezone.utc)
|
||||||
|
recent = now - timedelta(hours=2)
|
||||||
|
egs = store.get_all(limit=1000)
|
||||||
|
for eg in egs:
|
||||||
|
created_str = eg.metadata.get("created", "")
|
||||||
|
if not created_str:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
eg_time = datetime.fromisoformat(created_str)
|
||||||
|
if eg_time.tzinfo is None:
|
||||||
|
eg_time = eg_time.replace(tzinfo=timezone.utc)
|
||||||
|
if eg_time > recent:
|
||||||
|
tags = eg.metadata.get("tags", [])
|
||||||
|
if "task" in tags or "aufgabe" in tags or "todo" in tags:
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def try_web_search(topic: str) -> Optional[List[Dict[str, str]]]:
|
||||||
|
"""Web-Suche via OpenClaw."""
|
||||||
|
try:
|
||||||
|
import subprocess
|
||||||
|
result = subprocess.run(
|
||||||
|
["python3", "-c", f"""
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, '/root/.openclaw/workspace/second-brain/src')
|
||||||
|
from src.retriever import Retriever
|
||||||
|
from src.store import EngramStore
|
||||||
|
store = EngramStore('data/brain.sqlite')
|
||||||
|
ret = Retriever(store)
|
||||||
|
results = ret.retrieve('{topic}')
|
||||||
|
print('FOUND ' + str(len(results)))
|
||||||
|
"""],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=30,
|
||||||
|
cwd="/root/.openclaw/workspace/second-brain",
|
||||||
|
)
|
||||||
|
# Actually do web search
|
||||||
|
print(f"[search] Would search: {topic}")
|
||||||
|
return None # Placeholder: real search would be here
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[search] Error: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def run_proactive_search():
|
||||||
|
"""Haupt-Funktion für proaktive Suche."""
|
||||||
|
store = get_store()
|
||||||
|
state = load_state()
|
||||||
|
now = datetime.now(timezone.utc)
|
||||||
|
|
||||||
|
# Check: Neue Aufgaben?
|
||||||
|
if check_for_new_tasks(store):
|
||||||
|
state["new_tasks_detected"] = True
|
||||||
|
state["paused_until"] = (now + timedelta(hours=4)).isoformat()
|
||||||
|
save_state(state)
|
||||||
|
print("🛑 Neue Aufgaben erkannt. Suche pausiert für 4h.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check: Pausiert?
|
||||||
|
if state.get("paused_until"):
|
||||||
|
paused = datetime.fromisoformat(state["paused_until"])
|
||||||
|
if now < paused:
|
||||||
|
print(f"⏸️ Suche pausiert bis {state['paused_until']}")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
state["paused_until"] = None
|
||||||
|
state["new_tasks_detected"] = False
|
||||||
|
|
||||||
|
# Thema auswählen (Round-Robin)
|
||||||
|
searched = set(state.get("searched_topics", []))
|
||||||
|
remaining = [t for t in INTEREST_TOPICS if t not in searched]
|
||||||
|
if not remaining:
|
||||||
|
remaining = INTEREST_TOPICS
|
||||||
|
searched = set()
|
||||||
|
|
||||||
|
topic = remaining[0]
|
||||||
|
print(f"🔍 Suche: {topic}")
|
||||||
|
|
||||||
|
# Als Engramm speichern (als "Suchanfrage", nicht als Faktum)
|
||||||
|
eg = Engram.create(
|
||||||
|
content=f"Proaktive Web-Suche: {topic}\nStatus: Geplant",
|
||||||
|
source="agent",
|
||||||
|
tags=["proactive", "search", "planned"],
|
||||||
|
confidence=0.3,
|
||||||
|
grounding=Grounding.ASSUMPTION,
|
||||||
|
)
|
||||||
|
store.save(eg)
|
||||||
|
|
||||||
|
state["last_search"] = now.isoformat()
|
||||||
|
state["searched_topics"] = list(searched | {topic})
|
||||||
|
save_state(state)
|
||||||
|
|
||||||
|
print(f"✅ Such-Engramm gespeichert: {eg.id}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run_proactive_search()
|
||||||
Reference in New Issue
Block a user