#!/usr/bin/env python3 """ Landvex Agent Orkestrering Koordinerar 50GB agentkraft för skalad datainsamling. """ import asyncio import json import subprocess from datetime import datetime from pathlib import Path from typing import List, Dict, Optional from dataclasses import dataclass, asdict from enum import Enum class AgentStatus(Enum): LEDIG = "ledig" KOR = "kor" KLAR = "klar" FEL = "fel" @dataclass class AgentUppgift: uppgift_id: str typ: str # "skorda", "extrahera", "upplos", "trana" doman: str # TRP, ELN, VAT, etc. tier: int # 1, 2, 3 prioritet: int # 1-10 parametrar: Dict status: str = "pending" resultat: Optional[Dict] = None startad: Optional[str] = None avslutad: Optional[str] = None class LandvexAgentOrkestrerare: """Orkestrerar agenter för parallell datainsamling.""" def __init__( self, max_parallella: int = 20, ko_dir: Path = Path("/data/koer"), output_dir: Path = Path("/data/output"), ): self.max_parallella = max_parallella self.ko_dir = ko_dir self.output_dir = output_dir self.agenter: Dict[str, Dict] = {} self.ko: List[AgentUppgift] = [] self.statistik = { "skordade": 0, "extraherade": 0, "upplosta": 0, "tränade": 0, "fel": 0, } def skapa_uppgifter_for_tier(self, tier: int, domäner: List[str]) -> List[AgentUppgift]: """Skapa uppgifter för en specifik tier.""" uppgifter = [] for doman in domäner: # Skördningsuppgifter uppgifter.append(AgentUppgift( uppgift_id=f"skorda-{doman}-t{tier}-{datetime.utcnow().strftime('%Y%m%d%H%M%S')}", typ="skorda", doman=doman, tier=tier, prioritet=10 - tier, # Tier 1 = högst prioritet parametrar={"kallor": "alla", "max_dokument": 1000}, )) # Extraktionsuppgifter uppgifter.append(AgentUppgift( uppgift_id=f"extrahera-{doman}-t{tier}-{datetime.utcnow().strftime('%Y%m%d%H%M%S')}", typ="extrahera", doman=doman, tier=tier, prioritet=9 - tier, parametrar={"batch_storlek": 100}, )) # Entitetsupplösning uppgifter.append(AgentUppgift( uppgift_id=f"upplos-{doman}-t{tier}-{datetime.utcnow().strftime('%Y%m%d%H%M%S')}", typ="upplos", doman=doman, tier=tier, prioritet=8 - tier, parametrar={"tröskel": 0.85}, )) return uppgifter async def kör_agent(self, uppgift: AgentUppgift) -> Dict: """Kör en agent för en specifik uppgift.""" uppgift.startad = datetime.utcnow().isoformat() uppgift.status = "kor" try: if uppgift.typ == "skorda": resultat = await self._kör_skordare(uppgift) elif uppgift.typ == "extrahera": resultat = await self._kör_extraktor(uppgift) elif uppgift.typ == "upplos": resultat = await self._kör_upplosare(uppgift) elif uppgift.typ == "trana": resultat = await self._kör_tranare(uppgift) else: raise ValueError(f"Okänd uppgiftstyp: {uppgift.typ}") uppgift.status = "klar" uppgift.resultat = resultat self.statistik[f"{uppgift.typ}ade"] += 1 except Exception as e: uppgift.status = "fel" uppgift.resultat = {"fel": str(e)} self.statistik["fel"] += 1 uppgift.avslutad = datetime.utcnow().isoformat() return uppgift.resultat async def _kör_skordare(self, uppgift: AgentUppgift) -> Dict: """Kör skördare för en domän.""" # Anropa crawler.py process = await asyncio.create_subprocess_exec( "python3", "skordare/crawler.py", "--doman", uppgift.doman, "--output", str(self.output_dir / "skordat"), stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) stdout, stderr = await process.communicate() return { "returncode": process.returncode, "stdout": stdout.decode()[:1000], "stderr": stderr.decode()[:1000], } async def _kör_extraktor(self, uppgift: AgentUppgift) -> Dict: """Kör extraktor för skördade dokument.""" process = await asyncio.create_subprocess_exec( "python3", "extraktor/extraktor.py", "--input", str(self.output_dir / "skordat"), "--output", str(self.output_dir / "extraherat"), stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) stdout, stderr = await process.communicate() return { "returncode": process.returncode, "stdout": stdout.decode()[:1000], "stderr": stderr.decode()[:1000], } async def _kör_upplosare(self, uppgift: AgentUppgift) -> Dict: """Kör entitetsupplösare.""" process = await asyncio.create_subprocess_exec( "python3", "upplosare/entitetsupplosare.py", "--input", str(self.output_dir / "extraherat"), "--output", str(self.output_dir / "upplost"), stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) stdout, stderr = await process.communicate() return { "returncode": process.returncode, "stdout": stdout.decode()[:1000], "stderr": stderr.decode()[:1000], } async def _kör_tranare(self, uppgift: AgentUppgift) -> Dict: """Kör vision-träning.""" process = await asyncio.create_subprocess_exec( "python3", "../vision/traening/trainer.py", "--dataset", str(self.output_dir / "dataset"), "--output", str(self.output_dir / "modeller"), stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) stdout, stderr = await process.communicate() return { "returncode": process.returncode, "stdout": stdout.decode()[:1000], "stderr": stderr.decode()[:1000], } async def kör_alla(self, uppgifter: List[AgentUppgift]): """Kör alla uppgifter med begränsad parallellism.""" self.ko = sorted(uppgifter, key=lambda x: x.prioritet, reverse=True) print(f"🚀 Startar {len(self.ko)} uppgifter (max {self.max_parallella} parallella)") # Kör i batcher while self.ko: batch = self.ko[:self.max_parallella] self.ko = self.ko[self.max_parallella:] print(f"\n📦 Kör batch: {len(batch)} uppgifter") tasks = [self.kör_agent(u) for u in batch] resultat = await asyncio.gather(*tasks, return_exceptions=True) for uppgift, res in zip(batch, resultat): status = "✅" if uppgift.status == "klar" else "❌" print(f" {status} {uppgift.typ} {uppgift.doman} T{uppgift.tier}: {uppgift.status}") print(f"\n📊 Statistik: {self.statistik}") def spara_rapport(self): """Spara körningsrapport.""" self.output_dir.mkdir(parents=True, exist_ok=True) rapport = { "tidstampel": datetime.utcnow().isoformat(), "statistik": self.statistik, "uppgifter": [asdict(u) for u in self.ko], } fil_path = self.output_dir / f"rapport_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}.json" with open(fil_path, 'w') as f: json.dump(rapport, f, indent=2) print(f"\n📝 Rapport sparad: {fil_path}") async def main(): """Demo: orkestrera agenter för Tier 1.""" orkestrerare = LandvexAgentOrkestrerare(max_parallella=5) # Skapa uppgifter för Tier 1 (alla 9 domäner) domäner = ["TRP", "ELN", "VAT", "TEL", "BYG", "PRK", "JVG", "HMN", "FLG"] uppgifter = orkestrerare.skapa_uppgifter_for_tier(tier=1, domäner=domäner) print(f"📋 Skapade {len(uppgifter)} uppgifter för Tier 1") # Kör await orkestrerare.kör_alla(uppgifter) orkestrerare.spara_rapport() if __name__ == "__main__": asyncio.run(main())