This commit is contained in:
2026-03-12 02:10:57 +01:00
parent a8e769c73d
commit 3c6dd52d8f
29 changed files with 3323 additions and 0 deletions

365
README.md Normal file
View File

@@ -0,0 +1,365 @@
# 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, √(1a))
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)