# AI Agent Person Processor ⚡ Zaawansowana aplikacja w Go oparta o Clean Architecture, która wykorzystuje **ZOPTYMALIZOWANY** proces z LLM Function Calling + równoległymi obliczeniami do inteligentnego przetwarzania danych osób i generowania raportów lokalizacyjnych. ## 🚀 Kluczowa optymalizacja: - **Tylko 2 wywołania LLM na osobę** (poprzednio: 30-50) - **Równoległe obliczenia odległości** (wszystkie elektrownie jednocześnie) - **20x szybsze** przetwarzanie - **Wielowątkowe goroutines** w Go ## Funkcjonalność ### Zoptymalizowany proces przetwarzania: **Dla każdej osoby sekwencyjnie - TYLKO 2 WYWOŁANIA LLM:** 1. **[LLM Call 1/2] Pobierz lokalizacje osoby** (function calling: `get_location`) - Zwraca **WSZYSTKIE** możliwe lokalizacje osoby (np. 7, 12, 17 lokalizacji) 2. **[Lokalne obliczenia RÓWNOLEGŁE] Znajdź globalnie najbliższą elektrownię** - **Dla KAŻDEJ lokalizacji osoby** (pętla): - Dla KAŻDEJ elektrowni uruchamia oddzielną goroutine - Każda goroutine pobiera współrzędne z geocoding cache - Równolegle oblicza odległość Haversine (bez LLM!) - Znajduje najbliższą elektrownię dla tej konkretnej lokalizacji - **Wybiera MINIMUM ze wszystkich lokalizacji×elektrowni** - **Wielowątkowe przetwarzanie** - wszystkie elektrownie jednocześnie 3. **[LLM Call 2/2] Pobierz poziom dostępu** (function calling: `get_access_level`) - Używa tylko roku urodzenia (integer) 4. **Generuje raport dla osoby** - Zapisuje w `output/person_reports/{Name}_{Surname}.json` - Zawiera: nazwę elektrowni, kod, odległość, poziom dostępu, współrzędne **Po przetworzeniu wszystkich osób:** - Znajduje osobę z **najmniejszą odległością** do jej najbliższej elektrowni - Generuje `output/final_answer.json` do weryfikacji ## Architektura ``` . ├── cmd/app/ # Punkt wejścia ├── internal/ │ ├── config/ # Konfiguracja │ ├── domain/ # Modele i interfejsy │ │ ├── person.go # Model osoby │ │ ├── location.go # Haversine i obliczenia odległości │ │ ├── report.go # Modele raportów │ │ ├── llm.go # LLM z function calling │ │ └── tools.go # Definicje narzędzi dla agenta │ ├── infrastructure/ │ │ ├── api/ # Klient API hub.ag3nts.org │ │ ├── json/ # Repozytoria JSON │ │ └── llm/ # Providery LLM │ └── usecase/ │ └── person_agent_processor.go # Logika przetwarzania osób przez agenta ├── output/ │ ├── locations/ # Dane lokalizacji osób (z API) │ ├── accesslevel/ # Dane poziomów dostępu (z API) │ ├── person_reports/ # Raport dla każdej osoby │ ├── findhim_locations.json # Lista elektrowni z kodami │ └── final_answer.json # Końcowa odpowiedź do weryfikacji └── lista.json # Dane wejściowe ``` ## Przykładowy raport osoby Raport dla osoby (`output/person_reports/Oskar_Sieradzki.json`): ```json { "name": "Oskar", "surname": "Sieradzki", "nearest_plant": "Grudziądz", "plant_code": "PWR7264PL", "distance_km": 83.38, "access_level": 7, "primary_latitude": 53.483, "primary_longitude": 18.754 } ``` Każdy raport zawiera najbliższą elektrownię dla danej osoby wraz z odległością i kodem elektrowni. ## Optymalizacje ### ⚡ EKSTREMALNA OPTYMALIZACJA - 2 WYWOŁANIA LLM NA OSOBĘ: **Poprzednia wersja:** 30-50 iteracji LLM na osobę **Nowa wersja:** **TYLKO 2 wywołania LLM na osobę** ✨ ### Jak to działa: **FAZA INICJALIZACJI (jednokrotnie przy starcie):** - Pobiera listę elektrowni z API - **Geocoding API (OpenStreetMap Nominatim):** dla każdej elektrowni pobiera dokładne współrzędne - Zapisuje współrzędne w pamięci cache - Fallback do `CityCoordinates` jeśli API nie odpowiada **FAZA PRZETWARZANIA (dla każdej osoby):** 1. **LLM Call 1:** `get_location` - pobiera **WSZYSTKIE** lokalizacje osoby (function calling) 2. **Lokalne obliczenia (RÓWNOLEGŁE dla WSZYSTKICH lokalizacji):** - **Dla KAŻDEJ lokalizacji osoby:** - Dla każdej elektrowni: osobna goroutine - Pobiera współrzędne z cache (dokładne z geocoding API!) - Oblicza odległość Haversine - Znajduje najbliższą elektrownię dla tej lokalizacji - **Wybiera GLOBALNIE najmniejszą odległość** ze wszystkich kombinacji - Wszystko dzieje się **jednocześnie** (wielowątkowo) - **BEZ użycia LLM!** 3. **LLM Call 2:** `get_access_level` - pobiera poziom dostępu (function calling) ### Kluczowe ulepszenia: - **Redukcja wywołań LLM z ~40 do 2** (20x szybciej! 💨) - **Równoległe przetwarzanie** - wszystkie elektrownie jednocześnie - **Dokładne współrzędne** - OpenStreetMap Nominatim API (geocoding) - **Geocoding przy starcie** - jednokrotne pobranie współrzędnych wszystkich elektrowni - **Haversine bez LLM** - lokalne obliczenia w Go - **Fallback coordinates** - jeśli geocoding API zawiedzie, używamy CityCoordinates - Temperature = 0.0 dla deterministycznych wyników ### Zalety tego podejścia: - ⚡ **Dramatycznie szybsze** - 20x mniej wywołań LLM - 💰 **Tańsze** - minimalna konsumpcja API - 🚀 **Równoległe obliczenia** - wykorzystanie wszystkich rdzeni CPU - 🎯 **Deterministyczne** - te same wyniki za każdym razem - 📊 **Szczegółowe logi** - każdy krok widoczny - 🔧 **Łatwe debugowanie** - jasna struktura 2-fazowa ### System logowania: Aplikacja generuje bardzo szczegółowe logi pokazujące: - **Fazę ładowania danych** (osoby, elektrownie) - **Dla każdej osoby:** - `[LLM Call 1/2]` - wywołanie get_location - `[Local Processing]` - równoległe goroutines dla każdej elektrowni - Wyniki z każdej goroutine: `Goroutine [Zabrze]: 123.45 km` - `[LLM Call 2/2]` - wywołanie get_access_level - Finalne wyniki z podsumowaniem `Total LLM calls: 2` - **Fazę znajdowania minimalnej odległości** - **Ostateczny wynik** z instrukcją weryfikacji ## Konfiguracja ```json { "api_key": "your-api-key", "input_file": "lista.json", "output_dir": "output", "locations_api": "https://hub.ag3nts.org/api/location", "access_level_api": "https://hub.ag3nts.org/api/accesslevel", "locations_url": "https://hub.ag3nts.org", "llm": { "provider": "lmstudio", "model": "bielik-11b-v3.0-instruct-gptq-marlin@q8_0", "base_url": "http://localhost:1234" } } ``` ### Providery LLM #### LM Studio (lokalny) ```json "llm": { "provider": "lmstudio", "model": "bielik-11b-v3.0-instruct-gptq-marlin@q8_0", "base_url": "http://localhost:1234" } ``` #### OpenRouter (API) ```json "llm": { "provider": "openrouter", "model": "anthropic/claude-3.5-sonnet", "api_key": "your-openrouter-api-key" } ``` **Uwaga**: Model Bielik jest zalecany dla function calling - działa szybciej i stabilniej niż DeepSeek R1. ## Budowanie i uruchomienie ```bash # Budowanie go build -o person-processor ./cmd/app # Uruchomienie ./person-processor -config config.json ``` ## Weryfikacja odpowiedzi Po zakończeniu przetwarzania, aplikacja zapisuje końcową odpowiedź w pliku `output/final_answer.json`. Aby wysłać odpowiedź do weryfikacji: ```bash curl -X POST https://hub.ag3nts.org/verify \ -H "Content-Type: application/json" \ -d @output/final_answer.json ``` Lub z formatowaniem odpowiedzi: ```bash curl -X POST https://hub.ag3nts.org/verify \ -H "Content-Type: application/json" \ -d @output/final_answer.json | jq . ``` Format odpowiedzi (`output/final_answer.json`): ```json { "apikey": "your-api-key", "task": "findhim", "answer": { "name": "Imię", "surname": "Nazwisko", "accessLevel": 7, "powerPlant": "PWR1234PL" } } ``` ## Wzór Haversine Aplikacja używa wzoru Haversine do obliczania odległości między dwoma punktami na kuli ziemskiej: ``` a = sin²(Δlat/2) + cos(lat1) × cos(lat2) × sin²(Δlon/2) c = 2 × atan2(√a, √(1−a)) distance = R × c ``` gdzie R = 6371 km (promień Ziemi) ## Narzędzia agenta LLM i lokalne funkcje ### 🤖 LLM Function Calling (2 wywołania na osobę): #### get_location Pobiera wszystkie możliwe lokalizacje osoby z API. **Parametry:** - `name` (string) - imię osoby - `surname` (string) - nazwisko osoby **Zwraca:** tablicę obiektów z `latitude` i `longitude` **Logi:** `→ API call: get_location(Imię, Nazwisko)` **Użycie:** LLM Call 1/2 #### get_access_level Pobiera poziom dostępu osoby z API. **Parametry:** - `name` (string) - imię osoby - `surname` (string) - nazwisko osoby - `birth_year` (integer) - **tylko rok** urodzenia (np. 1987, NIE pełna data) **Zwraca:** obiekt z `accessLevel` (integer) **Logi:** `→ API call: get_access_level(Imię, Nazwisko, rok)` **Użycie:** LLM Call 2/2 #### find_nearest_point Znajduje najbliższy punkt z listy do punktu referencyjnego (np. elektrownia). **Parametry:** - `reference_lat` (float) - szerokość geograficzna punktu referencyjnego - `reference_lon` (float) - długość geograficzna punktu referencyjnego - `points` (array) - tablica punktów do sprawdzenia, każdy punkt: `[latitude, longitude]` **Zwraca:** obiekt z `latitude`, `longitude`, `distance_km`, `index` (indeks najbliższego punktu) **Przykład wywołania:** ```json { "reference_lat": 50.3086, "reference_lon": 18.7864, "points": [ [52.2297, 21.0122], [51.1079, 17.0385], [50.0647, 19.9450] ] } ``` **Zwraca:** ```json { "latitude": 50.0647, "longitude": 19.9450, "distance_km": 45.23, "index": 2 } ``` **Logi:** `→ Tool call: find_nearest_point(ref: 50.3086,18.7864, 3 points)` **Użycie:** Opcjonalna - dostępna dla LLM jeśli zdecyduje się jej użyć --- ### ⚡ Lokalne funkcje (bez LLM - wielowątkowe): #### GetPlantGeolocation (Geocoding API) Pobiera dokładne współrzędne geograficzne elektrowni z OpenStreetMap Nominatim API. **API:** `https://nominatim.openstreetmap.org/search` **Parametry:** - `city` - nazwa miasta - `country` - Poland - `format` - json - `limit` - 1 **Zwraca:** Dokładne współrzędne `{lat, lon}` (6 miejsc po przecinku!) **Użycie:** Jednokrotnie przy starcie aplikacji (cache w pamięci) **Logi:** ``` → Fetching accurate geolocations from geocoding API... • Geocoding: Zabrze... ✓ Fetched: 50.308615°N, 18.786375°E • Geocoding: Grudziądz... ✓ Fetched: 53.483624°N, 18.753536°E ``` #### CityCoordinates (baza fallback) Hardcoded współrzędne wszystkich elektrowni w Polsce (fallback gdy API nie odpowiada). **Zawartość:** - Zabrze, Piotrków Trybunalski, Grudziądz, Tczew, Radom, Chełmno, Żarnowiec - Każde miasto: `{Lat: float64, Lon: float64}` **Użycie:** Fallback gdy geocoding API zawiedzie #### Haversine (wzór odległości) Oblicza odległość między dwoma punktami na kuli ziemskiej. **Parametry:** - `lat1, lon1` - współrzędne punktu 1 - `lat2, lon2` - współrzędne punktu 2 **Zwraca:** odległość w kilometrach (float64) **Użycie:** Równoległe goroutines dla każdej elektrowni (bez LLM!) **Logi:** `• Goroutine [Nazwa]: 123.45 km` ## Wymagania - Go 1.21+ - LM Studio lub klucz OpenRouter API - Model LLM wspierający function calling (zalecany: Bielik 11B)