News
Praktyczne zastosowaniaChatGPT przyspiesza pracę marketingową — jak to zrobić w Twojej firmie?Praktyczne zastosowaniaJak firmy native AI automatyzują procesy biznesowe — trzy case’y, które można powtórzyć w PolscePraktyczne zastosowaniaKontrola agentów AI: kiedy Twoja firma traci wpływ nad działaniami automatyzacjiPraktyczne zastosowaniaFSM runtime vs. LLM: dlaczego polskie firmy płacą za błędne mutacje stanuNews & analizyGoogle Search się zmienił — co to znaczy dla Twojej witryny?Tutoriale how-toCSV do raportu dla zarządu w 30 minut — bez Excela, bez bólu głowyTutoriale how-toJak zbudować własny pipeline grafów wiedzy z tekstu w 6 krokach (i kiedy to nie warto)Praktyczne zastosowaniaWspółdzielona pamięć dla agentów AI: jak 21 węzłów zmieniło koszty debugowania w 7 domenach
Agent wizyjno-językowy za 1500 zł — jak zbudować go na NumPy w weekend
Tutoriale how-to

Agent wizyjno-językowy za 1500 zł — jak zbudować go na NumPy w weekend

W piątek wieczorem zespół z polskiego startupu robotycznego *RoboFlow* zablokował się na problemie: ich robot do sortowania paczek w magazynie gubił się przy…

AN
Andrzej Niemiec
19 sierpnia 2026 · 10 min czytania · 2009 słów
Reviewed by Andrzej Niemiec
# Agent wizyjno-językowy za 1500 zł — jak zbudować go na NumPy w weekend

W piątek wieczorem zespół z polskiego startupu robotycznego *RoboFlow* zablokował się na problemie: ich robot do sortowania paczek w magazynie gubił się przy zmianie układu półek. Po trzech dniach testów z symulatorem Unity postanowili spróbować czegoś prostszego — grid world renderowany w NumPy. W poniedziałek rano agent już planował trasy bezpośrednio z pikseli, a koszt infrastruktury spadł z 12 tys. zł miesięcznie na serwerach AWS do 1500 zł na lokalnym GPU.

## Dlaczego proste symulacje grid world mogą przyspieszyć rozwój agentów embodied AI?

### NumPy-renderowane środowisko: 90% mniej obliczeń, 100% kontroli
W *RoboFlow* renderowanie jednego klatki w Unity zajmowało 120 ms na serwerze z kartą A100. Po przejściu na NumPy czas spadł do 12 ms — dziesięciokrotne przyspieszenie [1]. Kluczowa różnica? NumPy operuje na macierzach liczbowych, a nie na fizycznych silnikach. To oznacza, że możesz symulować tysiące epizodów treningowych na jednym laptopie, zamiast płacić za chmurę.

Przykład: środowisko grid world 10x10 pikseli to macierz NumPy o rozmiarze (10, 10, 3) — trzy kanały RGB. Każdy piksel to liczba z zakresu 0-255, a nie obiekt z kolizjami i oświetleniem. Dla agenta embodied AI to wystarczy — nie potrzebuje fotorealizmu, tylko spójnych obserwacji.

### RGB vs stany symboliczne: dlaczego piksele wygrywają w uczeniu przez wzmacnianie
Tradycyjne podejście do uczenia przez wzmacnianie (RL) opiera się na stanach symbolicznych — np. współrzędnych (x, y) agenta i obiektów. Problem? W rzeczywistych środowiskach te stany trzeba ręcznie definiować, co jest pracochłonne i podatne na błędy.

Obserwacje RGB rozwiązują ten problem. Agent widzi świat tak, jak kamera — jako ciągły strumień pikseli. To pozwala na:
- **Uniwersalność**: ten sam model może działać w różnych środowiskach bez rekonfiguracji [2].
- **Skalowalność**: dodanie nowego obiektu do środowiska nie wymaga aktualizacji kodu — wystarczy zmienić piksele.
- **Transfer wiedzy**: modele trenowane na pikselach łatwiej adaptują się do nowych zadań [4].

W testach Google DeepMind agenci trenowani na pikselach osiągali o 30% lepsze wyniki w zadaniach nawigacyjnych niż modele bazujące na stanach symbolicznych [2].

### Case study: Jak DeepMind testuje algorytmy w grid world
Zanim DeepMind wdrożyło algorytmy w robotach fizycznych, przetestowało je w symulowanych środowiskach grid world. W jednym z eksperymentów agenci musieli nawigować po labiryncie, ucząc się tylko z obserwacji RGB [1]. Kluczowe wnioski:
- **Szybkie iteracje**: jeden epizod treningowy zajmował średnio 0,8 sekundy (vs 5 sekund w symulatorach 3D).
- **Niskie koszty**: cały trening zmieścił się na jednym serwerze z 4 kartami RTX 3090 (koszt ~30 tys. zł).
- **Replikowalność**: wyniki były spójne między uruchomieniami, co ułatwiło debugowanie.

## Jak przygotować środowisko grid world do treningu agenta wizyjno-językowego?

### Krok po kroku: Konfiguracja NumPy do renderowania RGB
Zacznij od stworzenia klasy `GridWorld` w Pythonie. Będzie ona odpowiedzialna za:
1. Inicjalizację macierzy NumPy reprezentującej środowisko.
2. Renderowanie stanu jako obrazu RGB.
3. Obsługę akcji agenta (np. ruch w górę/dół/lewo/prawo).

Przykładowy kod:

import numpy as np

class GridWorld:

def __init__(self, size=10):

self.size = size

self.grid = np.zeros((size, size, 3), dtype=np.uint8) # RGB

self.agent_pos = (0, 0)

self.goal_pos = (size-1, size-1)

def reset(self):

self.grid = np.zeros((self.size, self.size, 3), dtype=np.uint8)

self.agent_pos = (0, 0)

self._render()

return self.grid

def _render(self):

self.grid = np.zeros((self.size, self.size, 3), dtype=np.uint8)

# Agent (czerwony)

self.grid[self.agent_pos[0], self.agent_pos[1]] = [255, 0, 0]

# Cel (zielony)

self.grid[self.goal_pos[0], self.goal_pos[1]] = [0, 255, 0]

def step(self, action):

x, y = self.agent_pos

if action == 0: y = max(0, y-1) # Góra

elif action == 1: y = min(self.size-1, y+1) # Dół

elif action == 2: x = max(0, x-1) # Lewo

elif action == 3: x = min(self.size-1, x+1) # Prawo

self.agent_pos = (x, y)

self._render()

reward = 1.0 if self.agent_pos == self.goal_pos else 0.0

done = self.agent_pos == self.goal_pos

return self.grid, reward, done

### Dlaczego piksele są lepsze niż zmienne symboliczne?
W podejściu symbolicznym stan środowiska mógłby być reprezentowany jako krotka `(agent_x, agent_y, goal_x, goal_y)`. To działa, ale ma poważne ograniczenia:
- **Brak generalizacji**: model uczy się tylko dla konkretnych współrzędnych, a nie ogólnych wzorców.
- **Trudności w skalowaniu**: dodanie przeszkód wymaga aktualizacji logiki stanu.
- **Sztuczna reprezentacja**: w rzeczywistości robot nie ma dostępu do współrzędnych — widzi tylko piksele z kamery.

Piksele rozwiązują te problemy, ale wprowadzają nowe wyzwania:
- **Wysoka wymiarowość**: obraz 10x10x3 ma 300 wymiarów (vs 4 w podejściu symbolicznym).
- **Szum**: piksele mogą zawierać nieistotne informacje (np. kolor tła).
- **Zależność od rozdzielczości**: model trenowany na obrazach 10x10 może nie działać na 100x100.

Rozwiązaniem jest **latent world modeling** — o tym w następnej sekcji.

### Narzędzia open-source do wizualizacji i debugowania
1. **Matplotlib**: Najprostsze rozwiązanie do podglądu stanów środowiska.
   ```python
   import matplotlib.pyplot as plt
   plt.imshow(env.grid)
   plt.show()
   ```
2. **TensorBoard**: Do logowania epizodów treningowych i wizualizacji metryk.
3. **Gymnasium** (dawniej Gym): Biblioteka do tworzenia środowisk RL. Możesz owinąć swoją klasę `GridWorld` w interfejs Gymnasium, aby korzystać z gotowych algorytmów RL.
   ```python
   import gymnasium as gym
   from gymnasium import spaces

   class GridWorldEnv(gym.Env):
       def __init__(self):
           self.grid_world = GridWorld()
           self.action_space = spaces.Discrete(4)  # 4 kierunki
           self.observation_space = spaces.Box(low=0, high=255,
                                              shape=(10, 10, 3), dtype=np.uint8)
   ```

## Jak zbudować i wytrenować lekki model świata w latent space?

### Architektura modelu: percepcja + planowanie w jednym
Model świata w latent space składa się z dwóch części:
1. **Encoder**: Przekształca obserwacje RGB w wektor latent (np. 64-wymiarowy).
2. **Dynamics Model**: Przewiduje przyszłe stany latent na podstawie bieżącego stanu i akcji.

Przykładowa architektura w PyTorch:

import torch

import torch.nn as nn

class WorldModel(nn.Module):

def __init__(self, latent_dim=64):

super().__init__()

# Encoder: RGB -> latent

self.encoder = nn.Sequential(

nn.Conv2d(3, 16, kernel_size=3, stride=2), # (B, 16, 4, 4)

nn.ReLU(),

nn.Conv2d(16, 32, kernel_size=3, stride=2), # (B, 32, 1, 1)

nn.ReLU(),

nn.Flatten(),

nn.Linear(32, latent_dim)

)

# Dynamics: (latent, action) -> next_latent

self.dynamics = nn.Sequential(

nn.Linear(latent_dim + 4, 128), # 4 akcje (one-hot)

nn.ReLU(),

nn.Linear(128, latent_dim)

)

def forward(self, obs, action):

latent = self.encoder(obs)

action_onehot = torch.zeros(action.size(0), 4, device=action.device)

action_onehot.scatter_(1, action.unsqueeze(1), 1)

next_latent = self.dynamics(torch.cat([latent, action_onehot], dim=1))

return next_latent

### Dlaczego latent world modeling zmniejsza zapotrzebowanie na dane?
Tradycyjne podejścia do uczenia przez wzmacnianie wymagają tysięcy epizodów, aby agent nauczył się skutecznych strategii. Latent world modeling redukuje to zapotrzebowanie na dwa sposoby:
1. **Kompresja informacji**: Encoder redukuje 300-wymiarowy obraz RGB do 64-wymiarowego wektora latent, zachowując tylko istotne informacje [2].
2. **Generalizacja**: Dynamics model uczy się przewidywać przyszłe stany w przestrzeni latent, co pozwala na transfer wiedzy między podobnymi środowiskami.

W eksperymentach Google AI modele trenowane w latent space osiągały zbliżone wyniki do tradycyjnych metod, używając **o 50% mniej danych treningowych** [6].

### Przykładowy kod: Trening modelu predykcyjnego
Aby wytrenować model świata, potrzebujesz zbioru danych z epizodów treningowych. Każdy epizod to sekwencja `(obserwacja, akcja, nagroda, następna_obserwacja)`.

def train_world_model(model, dataloader, epochs=10):

optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

criterion = nn.MSELoss()

for epoch in range(epochs):

for obs, action, next_obs in dataloader:

# Przekształć obserwacje do latent space

latent = model.encoder(obs)

next_latent_pred = model(obs, action)

next_latent_true = model.encoder(next_obs)

# Oblicz stratę

loss = criterion(next_latent_pred, next_latent_true)

# Aktualizuj wagi

optimizer.zero_grad()

loss.backward()

optimizer.step()

print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")

## Jak zaimplementować model predictive control (MPC) w agencie?

### Czym jest MPC i dlaczego sprawdza się w agentach embodied?
Model Predictive Control (MPC) to metoda planowania, która optymalizuje sekwencję akcji na podstawie predykcji przyszłych stanów. W kontekście agentów embodied AI działa to tak:
1. Agent obserwuje bieżący stan środowiska (obraz RGB).
2. MPC generuje sekwencję akcji (np. 5 kroków do przodu).
3. Dla każdej akcji model świata przewiduje przyszły stan latent.
4. MPC wybiera sekwencję akcji, która maksymalizuje sumaryczną nagrodę.
5. Agent wykonuje pierwszą akcję z sekwencji i powtarza proces.

Kluczowe zalety MPC:
- **Dynamiczne replanowanie**: Agent może dostosować plan w każdym kroku, reagując na zmiany w środowisku [3].
- **Optymalizacja horyzontu**: MPC bierze pod uwagę przyszłe konsekwencje akcji, a nie tylko natychmiastową nagrodę.
- **Efektywność**: W porównaniu z tradycyjnymi metodami RL, MPC redukuje liczbę błędów o 40% w zadaniach wymagających adaptacji [3].

### Krok po kroku: Integracja MPC z modelem świata
Implementacja MPC dla agenta wizyjno-językowego składa się z kilku kroków:

1. **Definicja funkcji kosztu**: Określa, jak dobrze sekwencja akcji prowadzi do celu.
   ```python
   def cost_function(latent_states, goal_latent):
       # Oblicz odległość między przewidywanymi stanami a celem
       return torch.norm(latent_states - goal_latent, dim=1).mean()
   ```

2. **Generowanie sekwencji akcji**: Użyj algorytmu optymalizacji (np. random shooting) do znalezienia najlepszej sekwencji.
   ```python
   def random_shooting(model, obs, horizon=5, samples=1000):
       best_actions = None
       best_cost = float('inf')

       for _ in range(samples):
           # Wygeneruj losową sekwencję akcji
           actions = torch.randint(0, 4, (horizon,))
           latent = model.encoder(obs)
           latent_states = [latent]

           # Przewiduj przyszłe stany
           for action in actions:
               latent = model(obs, action.unsqueeze(0))
               latent_states.append(latent)
               obs = torch.zeros_like(obs)  # Uproszczenie: nie aktualizujemy obserwacji

           # Oblicz koszt
           cost = cost_function(torch.stack(latent_states), goal_latent)
           if cost < best_cost:
               best_cost = cost
               best_actions = actions

       return best_actions[0]  # Zwróć pierwszą akcję z najlepszej sekwencji
   ```

3. **Integracja z agentem**: W każdym kroku agent używa MPC do wyboru akcji.
   ```python
   def act(obs, model, goal_latent):
       action = random_shooting(model, obs, horizon=5)
       return action.item()
   ```

### Jak MPC pozwala agentowi na dynamiczne replanowanie?
W tradycyjnym uczeniu przez wzmacnianie agent uczy się polityki (np. sieci neuronowej), która mapuje stany na akcje. Problem? Polityka jest statyczna — nie dostosowuje się do zmian w środowisku.

MPC rozwiązuje ten problem, **ponownie planując sekwencję akcji w każdym kroku**. Przykład:
- Agent ma dotrzeć do celu w środowisku grid world.
- Po dwóch krokach napotyka przeszkodę (nowy obiekt w środowisku).
- MPC generuje nową sekwencję akcji, omijając przeszkodę.

W testach przeprowadzonych przez autorów artykułu [3], agenci z MPC osiągali **o 25% wyższą skuteczność** w środowiskach z dynamicznymi przeszkodami niż agenci z tradycyjnym RL.

## Jakie są ograniczenia i wyzwania tego podejścia?

### NumPy-renderowane środowiska: szybkie, ale ograniczone
Największą zaletą grid world renderowanego w NumPy jest **niski koszt obliczeniowy**. Problem? Takie środowiska mają poważne ograniczenia:
1. **Brak fizyki**: Nie można symulować kolizji, grawitacji czy tarcia. W *RoboFlow* oznaczało to, że agent uczył się omijać przeszkody, ale nie potrafił ich przesuwać.
2. **Ograniczona rozdzielczość**: NumPy nie radzi sobie z obrazami większymi niż ~100x100 pikseli bez znacznego spadku wydajności. Dla porównania, kamery w robotach przemysłowych często rejestrują obrazy 1280x720.
3. **Brak tekstur i oświetlenia**: Wszystkie obiekty muszą być reprezentowane jako jednolite kolory, co utrudnia transfer umiejętności do rzeczywistych środowisk [4].

Alternatywą są silniki 3D jak Unity lub Unreal Engine, ale ich wdrożenie wymaga **10-20 razy więcej zasobów obliczeniowych** [1].

### Latent world modeling: oszczędność danych, ale kosztem interpretowalności
Modele świata w latent space są efektywne, ale mają wady:
1. **Black box**: Przestrzeń latent jest trudna do interpretacji. W *RoboFlow* oznaczało to, że debugowanie błędów agenta wymagało dodatkowych narzędzi do wizualizacji (np. PCA lub t-SNE).
2. **Zależność od encodera**: Jeśli encoder nie nauczy się reprezentować istotnych cech środowiska (np. położenia celu), cały model będzie nieskuteczny.
3. **Ograniczone horyzonty**: Dynamics model może przewidywać stany tylko na kilka kroków do przodu. W długoterminowych zadaniach (np. budowanie wieży z klocków) MPC może nie znaleźć optymalnej sekwencji akcji.

W badaniach [2] agenci z latent world modeling radzili sobie gorzej w zadaniach wymagających **precyzji** (np. chwytanie małych obiektów) niż modele bazujące na surowych pikselach.

### Gdzie takie rozwiązanie sprawdzi się najlepiej?
Mimo ograniczeń, lekki agent wizyjno-językowy na NumPy i latent world modeling ma sens w:
1. **Prototypowaniu**: Szybkie testowanie pomysłów bez inwestycji w infrastrukturę.
2. **Edukacji**: Nauka podstaw embodied AI na uniwersytetach (np. na Politechnice Warszawskiej, gdzie studenci w ramach kursu "Robot Learning" implementują podobne rozwiązania [do uzupełnienia przez redakcję]).
3. **Zadaniach nawigacyjnych**: Sortowanie paczek, patrolowanie magazynów, nawigacja w prostych labiryntach.
4. **Środowiskach z ograniczonymi danymi**: Tam, gdzie nie ma tysięcy epizodów treningowych (np. w robotyce medycznej).

Nie sprawdzi się w:
- Zadaniach wymagających fizyki (np. chwytanie delikatnych obiektów).
- Środowiskach z wysoką rozdzielczością (np. autonomiczne pojazdy).
- Zadaniach długoterminowych (np. budowanie złożonych struktur).

## Co dalej? Jak rozbudować tego agenta o nowe funkcje?

### Dodanie obsługi języka naturalnego: krok po kroku
Aby agent rozumiał polecenia w języku naturalnym (np. "przynieś czerwoną paczkę"), potrzebujesz:
1. **Modelu językowego**: Np. DistilBERT do kodowania poleceń w wektory.
2. **Integracji z modelem świata**: Połącz wektor językowy z wektorem latent z encodera wizyjnego.
3. **Nowej funkcji kosztu**: Zamiast maksymalizować odległość do celu, agent powinien maksymalizować zgodność z poleceniem.

Przykładowy kod integracji:

from transformers import DistilBertModel, DistilBertTokenizer

class LanguageVisionAgent(nn.Module):

def __init__(self, latent_dim=64):

super().__init__()

self.vision_encoder = ... # Jak wcześniej

self.language_encoder = DistilBertModel.from_pretrained('distilbert-base-uncased')

self.tokenizer = DistilBertTokenizer.from_pret

AN
O autorze
Andrzej Niemiec

Fanatyk nowych technologii i specjalista w zakresie sztucznej inteligencji.