Track
Duże modele językowe stają się bardziej użyteczne, gdy nadamy im wyspecjalizowane role, narzędzia i dostęp do danych na żywo. Zamiast polegać na jednym modelu, możemy zbudować mały zespół badawczy, w którym każdy agent skupia się na innej części analizy.
Dokładnie to zrobimy w tym samouczku: zbudujemy multi-agentowego analityka akcji, używając GLM-5.3-Flash i CrewAI. Trzech agentów analizuje fundamenty, wskaźniki techniczne i najnowsze wiadomości, a zarządzający portfelem łączy ich wnioski w końcowy sygnał badawczy: KUP, TRZYMAJ lub SPRZEDAJ.
Najpierw przetestujemy przepływ w Jupyter Notebooku, a potem zamienimy go w prosty interfejs terminalowy, który potrafi analizować akcje i odpowiadać na pytania uzupełniające.
W projekcie używamy:
- GLM-5.3-Flash do wnioskowania
- CrewAI do koordynacji agentów
- Finnhub do danych rynkowych i fundamentalnych
- Yahoo Finance do historycznych cen
- Tavily do najnowszych wiadomości
- Pandas i NumPy do wskaźników technicznych
Nowy w tym frameworku? Nasz kurs Building AI Agents with CrewAI omawia agentów, zespoły i przepływy od podstaw.
Dlaczego warto użyć GLM-5.3-Flash w aplikacji multiagentowej
GLM-5.3-Flash oferuje imponujący balans wydajności, efektywności i ceny. Pełny przegląd znajdziesz w naszym przewodniku po GLM-5.3-Flash oraz w porównaniu z Qwen3.8-Flash-Next.
Konkurencyjna wydajność i efektywność
W Artificial Analysis Intelligence Index uzyskuje wynik 57, plasując go w czołówce modeli i czyniąc konkurencyjnym wobec wielu znacznie droższych modeli granicznych. Na przykład:
-
GPT-5.6 Sol również ma 57 (przy
highwysiłku wnioskowania), ale kosztuje ponad 25× więcej za tokeny wejściowe i 40× więcej za wyjściowe. -
Claude Sonnet 5 wypada nawet nieco słabiej, z wynikiem 55 przy
maxwysiłku wnioskowania. Jest 20×/30× droższy za tokeny wejścia/wyjścia niż GLM-5.3-Flash.
Ma 320B łącznych parametrów, ale tylko ok. 18B parametrów jest aktywnych na token, co pozwala osiągać wysoką wydajność przy względnie niskich wymaganiach obliczeniowych.

Źródło: AI Model & API Providers Analysis | Artificial Analysis
Atrakcyjne ceny
Jak już wspomniałem, ta wydajność idzie w parze ze stosunkowo niską ceną API. W momencie pisania OpenRouter oferuje GLM-5.3-Flash z 50% rabatem: $0,075 za milion tokenów wejściowych i $0,25 za milion tokenów wyjściowych.
Nawet w regularnej cenie $0,15/$0,50 pozostaje tańszy niż modele takie jak GPT-5.6 Luna od OpenAI, który kosztuje $0,20/$1,20 (52 w AA Intelligence Index przy max wnioskowaniu). To atrakcyjna opcja nie tylko do eksperymentów, ale i większych, produkcyjnych aplikacji agentowych.

Źródło: GLM 5.3 Flash - API Pricing & Benchmarks | OpenRouter
To ma jeszcze większe znaczenie przy budowie systemu multiagentowego, bo jedno żądanie użytkownika może uruchomić wiele wywołań LLM w tle. Pojedynczy agent LLM może kilka razy wywołać model do wnioskowania, wyboru narzędzi, przetworzenia wyników i wygenerowania odpowiedzi. Jeśli jeden agent wykona około siedmiu wywołań LLM, a twój przepływ używa pięciu agentów, pojedyncze żądanie może łatwo dać 35 i więcej wywołań.
Dokładna liczba zależy od projektu systemu, ale sedno jest proste: agentowe przepływy szybko stają się kosztowne. Użycie sprawnego modelu o niskiej cenie tokenów, jak GLM-5.3-Flash, robi dużą różnicę przy przejściu z małego dema do realnej aplikacji produkcyjnej.
1. Skonfiguruj środowisko dla GLM-5.3-Flash
Zanim zbudujemy przepływ multiagentowy, utwórz klucze API do Z.ai, Finnhub i Tavily.
Utwórz klucze API
W przypadku Z.ai upewnij się, że używasz zwykłego API, a nie GLM Coding Plan. Coding Plan jest przeznaczony dla wspieranych narzędzi do kodowania AI i nie powinien być używany jako uniwersalne API dla własnej aplikacji multiagentowej.
Wygeneruj zwykły klucz API Z.ai i doładuj niewielką kwotą swoje konto. Ponieważ GLM-5.3-Flash jest niedrogi, na start wystarczy małe saldo.

Źródło: Z.ai API Platform — Zacznij budować z GLM-5.3
Możesz też założyć darmowe konta w Finnhub i Tavily i wygenerować klucze API. Ich darmowe plany wystarczą do testów tego projektu.
Skonfiguruj pliki projektu
Następnie utwórz plik .env w folderze projektu i dodaj klucze API:
ZAI_API_KEY="your_zai_api_key"
FINNHUB_API_KEY="your_finnhub_api_key"
TAVILY_API_KEY="your_tavily_api_key"
Teraz utwórz nowy Jupyter Notebook. Najpierw zbudujemy i przetestujemy tu kompletny przepływ multiagentowy, przejrzymy wyniki każdego agenta i naprawimy problemy, zanim zamienimy to w pełnoprawną aplikację.
Zainstaluj potrzebne biblioteki
Użyjemy CrewAI do tworzenia i koordynacji agentów, Finnhub do danych rynkowych i Tavily do wyszukiwania w sieci w czasie rzeczywistym. Zainstaluj wymagane pakiety Pythona:
!pip install -q crewai crewai-tools finnhub-python tavily-python pandas numpy requests python-dotenv openai
Importuj biblioteki i klucze API
Następnie zaimportuj wymagane biblioteki i wczytaj klucze API:
import os
import re
import time
import finnhub
import numpy as np
import pandas as pd
import requests
from dotenv import load_dotenv
from openai import OpenAI
from tavily import TavilyClient
from crewai import Agent, Crew, LLM, Process, Task
from crewai.tools import tool
from crewai_tools import TavilySearchTool
# Load environment variables
load_dotenv()
# Disable CrewAI tracing
os.environ.setdefault("CREWAI_TRACING_ENABLED", "false")
os.environ.setdefault("OTEL_SDK_DISABLED", "true")
Na koniec wybierz spółkę, którą agenci mają analizować:
TICKER = "NVDA"
Ja w całym notatniku użyję NVIDIA (NVDA), ale śmiało podmień na inny interesujący cię ticker, np. AAPL, MSFT albo TSLA.
2. Skonfiguruj GLM-5.3-Flash i API danych
Gdy klucze API są gotowe, możemy podłączyć usługi, z których nasi agenci będą korzystać w projekcie.
Zacznij od inicjalizacji Finnhub do danych rynkowych i Tavily do wyszukiwania w sieci:
finnhub_client = finnhub.Client(
api_key=os.environ["FINNHUB_API_KEY"]
)
tavily_client = TavilyClient(
api_key=os.environ["TAVILY_API_KEY"]
)
Jeśli chcesz budować agentów finansowych i nie wiesz, które źródło danych wybrać, polecam nasz przewodnik po wyborze właściwego API danych giełdowych.
Następnie skonfiguruj GLM-5.3-Flash jako główny LLM dla naszych agentów CrewAI. Z.ai udostępnia API zgodne z OpenAI, więc możemy podłączyć je bezpośrednio przez klasę LLM CrewAI:
llm = LLM(
model="openai/glm-5.3-flash",
api_key=os.environ["ZAI_API_KEY"],
base_url="https://api.z.ai/api/paas/v4/",
temperature=0.1,
)
Utrzymujemy niską temperaturę, bo aplikacja skupia się na badaniach i analizie finansowej, gdzie zależy nam na spójnych i skupionych odpowiedziach agentów.
Mając podłączony model, dane rynkowe i wyszukiwanie w sieci, możemy zacząć budować narzędzia, z których skorzystają agenci.
3. Zbierz historyczne dane rynkowe
Potrzebujemy historycznych danych cenowych, by agenci rozumieli, jak spółka zachowywała się w czasie.
Do tego użyjemy publicznego endpointu wykresów Yahoo Finance, aby zebrać dzienne dane: otwarcie, maksimum, minimum, zamknięcie i wolumen.
Pobierz historię cen
HTTP = requests.Session()
HTTP.headers.update({"User-Agent": "glm-stock-swarm/1.0"})
def _yahoo_history(ticker: str, days: int) -> pd.DataFrame:
end = int(time.time())
start = end - days * 24 * 60 * 60
response = HTTP.get(
f"https://query1.finance.yahoo.com/v8/finance/chart/{ticker}",
params={
"period1": start,
"period2": end,
"interval": "1d",
"events": "history",
},
timeout=30,
)
response.raise_for_status()
result = response.json()["chart"]["result"][0]
values = result["indicators"]["quote"][0]
frame = pd.DataFrame({
"date": pd.to_datetime(
result["timestamp"], unit="s", utc=True
).tz_localize(None),
"open": values["open"],
"high": values["high"],
"low": values["low"],
"close": values["close"],
"volume": values["volume"],
})
frame = (
frame.dropna(subset=["close"])
.sort_values("date")
.reset_index(drop=True)
)
frame.attrs["source"] = "Yahoo public chart"
return frame
def get_price_history(ticker: str, days: int = 450) -> pd.DataFrame:
return _yahoo_history(ticker.strip().upper(), days)
Zbierzemy ok. 450 dni historii, co daje dość danych do wyliczenia dłuższych wskaźników, np. 200-dniowej średniej kroczącej.
Oblicz wskaźniki techniczne
Policzmy kilka powszechnie używanych wskaźników:
- Średnie kroczące: średnia z ostatnich 20, 50 i 200 dni
- Relative Strength Index (RSI): skala 0–100 wskazująca wykupienie (>70) lub wyprzedanie (<30)
- MACD (Moving Average Convergence/Divergence): różnica długoterminowej i krótkoterminowej wykładniczej średniej kroczącej (EMA)
def add_indicators(frame: pd.DataFrame) -> pd.DataFrame:
if frame.empty:
return frame.copy()
result = frame.copy()
result.attrs.update(frame.attrs)
# Moving averages
result["SMA20"] = result["close"].rolling(20).mean()
result["SMA50"] = result["close"].rolling(50).mean()
result["SMA200"] = result["close"].rolling(200).mean()
# RSI
delta = result["close"].diff()
gain = delta.clip(lower=0)
loss = -delta.clip(upper=0)
avg_gain = gain.ewm(
alpha=1 / 14, min_periods=14, adjust=False
).mean()
avg_loss = loss.ewm(
alpha=1 / 14, min_periods=14, adjust=False
).mean()
result["RSI14"] = 100 - (
100 / (1 + avg_gain / avg_loss.replace(0, np.nan))
)
# MACD
result["EMA12"] = result["close"].ewm(
span=12, adjust=False
).mean()
result["EMA26"] = result["close"].ewm(
span=26, adjust=False
).mean()
result["MACD"] = result["EMA12"] - result["EMA26"]
return result
Te wskaźniki będą przydatne, bo dają agentowi analizy technicznej szybki podgląd trendów i momentum bez konieczności liczenia wszystkiego przez LLM.
Sprawdź ostatnie wartości
Zbierz dane, policz wskaźniki i wyświetl ostatnie wiersze:
history = add_indicators(
get_price_history(TICKER)
)
if len(history) < 200:
raise RuntimeError(
f"Need at least 200 daily observations; received {len(history)}."
)
print(
f"Loaded {len(history)} rows "
f"from {history.attrs['source']}."
)
history.tail(3)[
[
"date",
"close",
"SMA20",
"SMA50",
"SMA200",
"RSI14",
"MACD",
]
]

Powinieneś zobaczyć najnowszą cenę akcji wraz z jej średnimi kroczącymi, RSI i MACD, podobnie jak powyżej.
Później przekażemy te dane agentowi analizy technicznej, by mógł zidentyfikować trendy cenowe, momentum i bycze lub niedźwiedzie sygnały.
4. Zamień źródła danych w narzędzia CrewAI
Skoro źródła danych działają, musimy zamienić je w narzędzia CrewAI. Dzięki temu agenci mogą wywoływać je bezpośrednio, gdy potrzebują informacji fundamentalnych, technicznych lub newsów.
Utwórz narzędzia danych o akcjach
Najpierw stwórz małą funkcję pomocniczą, aby trzymać liczby w czystym i spójnym formacie:
def _number(value, decimals: int = 2) -> str:
if value is None or pd.isna(value):
return "N/A"
return f"{float(value):,.{decimals}f}"
Następnie utwórz narzędzie do fundamentów. Korzysta z Finnhub, by pobrać najnowszą cenę, wyceny, wzrost, rentowność i inne fundamenty spółki.
@tool("Get Stock Fundamentals")
def get_fundamentals(ticker: str) -> str:
"""Get current price and company fundamentals from Finnhub for one ticker."""
ticker = ticker.strip().upper()
quote = finnhub_client.quote(ticker)
metrics = finnhub_client.company_basic_financials(ticker, "all")
m = metrics.get("metric", {})
return f"""Ticker: {ticker}
Source: Finnhub
Current price: {_number(quote.get('c'))}
Previous close: {_number(quote.get('pc'))}
Daily change %: {_number(quote.get('dp'))}
Day high / low: {_number(quote.get('h'))} / {_number(quote.get('l'))}
Market cap (USD millions): {_number(m.get('marketCapitalization'))}
52-week high / low: {_number(m.get('52WeekHigh'))} / {_number(m.get('52WeekLow'))}
Normalized annual P/E: {_number(m.get('peNormalizedAnnual'))}
Annual P/B: {_number(m.get('pbAnnual'))}
Annual P/S: {_number(m.get('psAnnual'))}
ROE TTM: {_number(m.get('roeTTM'))}
Net margin TTM: {_number(m.get('netProfitMarginTTM'))}
Revenue growth TTM YoY: {_number(m.get('revenueGrowthTTMYoy'))}
EPS growth TTM YoY: {_number(m.get('epsGrowthTTMYoy'))}
Annual debt/equity: {_number(m.get('totalDebt/totalEquityAnnual'))}"""
Utworzymy też narzędzie analizy technicznej, korzystające z danych historycznych i wskaźników z poprzedniej sekcji.
@tool("Analyze Stock Technicals")
def get_technicals(ticker: str) -> str:
"""Calculate price trends, returns, moving averages, RSI, and MACD."""
ticker = ticker.strip().upper()
frame = add_indicators(get_price_history(ticker))
if len(frame) < 200:
return (
f"Technical data unavailable for {ticker}: "
"fewer than 200 observations."
)
latest = frame.iloc[-1]
return f"""Ticker: {ticker}
Source: {frame.attrs.get('source', 'unknown')}
Last market date: {latest['date'].date()}
Close: {_number(latest['close'])}
SMA20 / SMA50 / SMA200: {_number(latest['SMA20'])} / {_number(latest['SMA50'])} / {_number(latest['SMA200'])}
RSI14: {_number(latest['RSI14'])}
MACD: {_number(latest['MACD'])}
20-day return: {_number((latest['close'] / frame['close'].iloc[-21] - 1) * 100)}%
50-day return: {_number((latest['close'] / frame['close'].iloc[-51] - 1) * 100)}%
Price vs SMA20: {_number((latest['close'] / latest['SMA20'] - 1) * 100)}%
Price vs SMA50: {_number((latest['close'] / latest['SMA50'] - 1) * 100)}%
Price vs SMA200: {_number((latest['close'] / latest['SMA200'] - 1) * 100)}%"""
Kluczowa jest tu dekoracja @tool. Zamienia zwykłą funkcję Pythona w narzędzie, które agent CrewAI może zdecydować się wywołać w trakcie zadania.
Dodaj narzędzie wyszukiwania w sieci
Do wiadomości o spółce i ostatnich wydarzeń użyjemy narzędzia wyszukiwania Tavily w CrewAI:
web_search = TavilySearchTool(
api_key=os.environ["TAVILY_API_KEY"],
topic="news",
search_depth="advanced",
days=30,
max_results=5,
)
Ograniczamy wyszukiwanie do ostatnich 30 dni i zwracamy maksymalnie pięć wyników, co powinno dostarczyć agentom wystarczająco dużo świeżych informacji bez przeładowania treściami.
Przetestuj narzędzia giełdowe
Zanim utworzymy agentów, uruchom bezpośrednio dwa własne narzędzia:
print(get_fundamentals.run(ticker=TICKER))
print()
print(get_technicals.run(ticker=TICKER))

Powinieneś teraz zobaczyć najnowsze metryki fundamentalne, a następnie wskaźniki techniczne dla wybranej spółki.
5. Zbuduj multiagentowy zespół badawczy
Skoro narzędzia są gotowe, możemy stworzyć nasz multiagentowy zespół badawczy.
Użyjemy trzech specjalistów: po jednym do analizy fundamentalnej, technicznej i newsów, plus zarządzający portfelem, który połączy ich wnioski w końcową ocenę.
Utwórz agentów-specjalistów
Najpierw utwórz analityka fundamentalnego:
fundamental_agent = Agent(
role="Fundamental Analyst",
goal=(
"Evaluate financial health, growth, profitability, "
"valuation, and balance-sheet risk."
),
backstory=(
"You are a careful long-term equity analyst. "
"Use the fundamentals tool and never invent a figure. "
"Treat N/A as missing, not as zero."
),
tools=[get_fundamentals],
llm=llm,
allow_delegation=False,
max_iter=4,
verbose=False,
)
Następnie utwórz analityka technicznego:
technical_agent = Agent(
role="Technical Analyst",
goal=(
"Determine whether the current price setup is "
"bullish, bearish, or neutral."
),
backstory=(
"You interpret indicators calculated by Python. "
"Never guess market prices or indicator values."
),
tools=[get_technicals],
llm=llm,
allow_delegation=False,
max_iter=4,
verbose=False,
)
Potem utwórz analityka newsów finansowych:
news_agent = Agent(
role="Financial News Analyst",
goal=(
"Find material recent developments and distinguish "
"confirmed reporting from speculation."
),
backstory=(
"You are a skeptical financial-news researcher. "
"Prioritize primary and reputable sources, dates, and links."
),
tools=[web_search],
llm=llm,
allow_delegation=False,
max_iter=5,
verbose=False,
)
Każdy specjalista dostaje tylko to narzędzie, którego faktycznie potrzebuje. Dzięki temu przepływ jest skupiony i zapobiega wywoływaniu niepowiązanych narzędzi.
Utwórz zarządzającego portfelem
Na koniec utwórz agenta-menadżera, który przejrzy trzy raporty specjalistów:
manager_agent = Agent(
role="Portfolio Manager",
goal=(
"Combine the specialist reports into a balanced, "
"evidence-based research signal."
),
backstory=(
"You lead an equity research team. "
"Weigh bullish and bearish evidence, use HOLD when evidence "
"is mixed, and never add unsupported facts."
),
llm=llm,
allow_delegation=False,
max_iter=4,
verbose=False,
)
Zarządzający portfelem nie potrzebuje bezpośredniego dostępu do naszych narzędzi danych. Pracuje na raportach przygotowanych przez trzech specjalistów.
Ustawienie allow_delegation=False utrzymuje przepływ przewidywalnym, a max_iter ogranicza liczbę cykli wnioskowania i użycia narzędzi przez każdego agenta.
6. Przydziel wyspecjalizowane zadania badawcze
Tworząc agentów, definiujemy kim są. Teraz musimy każdemu dokładnie powiedzieć, co ma zrobić i co ma zawierać jego wynik końcowy.
Zdefiniuj zadania badawcze
Zacznij od zadania analizy fundamentalnej:
fundamental_task = Task(
description=(
f"Analyze {TICKER}'s growth, profitability, valuation, "
"balance sheet, and business quality. "
"Use Get Stock Fundamentals. "
"Return a Fundamental Score from 0-100, strongest positive, "
"biggest risk, and note all material missing data."
),
expected_output=(
"A concise fundamental assessment with a 0-100 score, "
"evidence, strongest positive, biggest risk, "
"and missing-data note."
),
agent=fundamental_agent,
)
Teraz zdefiniuj zadanie analizy technicznej:
technical_task = Task(
description=(
f"Analyze {TICKER}'s technical setup. "
"Use Analyze Stock Technicals. "
"Consider SMA20/50/200, RSI14, MACD, recent returns, and trend. "
"Return Technical Score 0-100, Bullish/Neutral/Bearish signal, "
"trend, momentum, and main technical risk."
),
expected_output=(
"A concise technical assessment with score, signal, trend, "
"momentum, data source, and main risk."
),
agent=technical_agent,
)
Dla ostatnich wydarzeń utwórz zadanie researchu newsów:
news_task = Task(
description=(
f"Research the most important recent news for {TICKER}, "
"focusing on the last 30 days: earnings, guidance, analyst "
"revisions, products, partnerships, M&A, regulation, lawsuits, "
"management, and industry developments. "
"Use Tavily Search. "
"Return News Score 0-100, sentiment, positive and negative "
"catalysts, publication dates, and source URLs. "
"Ignore low-quality speculation."
),
expected_output=(
"A sourced recent-news assessment with score, sentiment, "
"catalysts, dates, and clickable URLs."
),
agent=news_agent,
)
Jasne zadanie i format wyniku dla każdego agenta jest ważny. Ułatwia to zarządzającemu portfelem porównanie raportów zamiast otrzymywania zupełnie różnych odpowiedzi.
Utwórz zadanie decyzji końcowej
Ostatnie zadanie zbiera trzy raporty w całość:
decision_task = Task(
description=(
f"Review all three specialist reports and make the final "
f"educational research assessment for {TICKER}. "
"Return exactly one signal: BUY, HOLD, or SELL. "
"Use HOLD when evidence is mixed or confidence is insufficient. "
"Base every claim only on the supplied reports."
),
expected_output=(
"Ticker; Signal; Overall Score /100; Confidence %; "
"Fundamental, Technical, and News scores; Bull Case; "
"Bear Case; Main Catalyst; Main Risk; Final Explanation; "
"Sources; and an educational-not-financial-advice disclaimer."
),
agent=manager_agent,
context=[
fundamental_task,
technical_task,
news_task,
],
)
Kluczowa jest właściwość context. Daje ona zarządzającemu portfelem dostęp do wyników wszystkich trzech zadań specjalistycznych, co pozwala porównać sygnały fundamentalne, techniczne i newsowe przed wydaniem końcowej oceny KUP, TRZYMAJ lub SPRZEDAJ.
Na tym etapie agenci, narzędzia i zadania są gotowe. Teraz możemy spiąć wszystko w przepływ CrewAI i uruchomić kompletny zespół badawczy.
7. Uruchom multiagentowy zespół badawczy
Teraz możemy połączyć wszystko i uruchomić pełny przepływ badania akcji.
Utwórz zespół (crew)
Utwórz zespół CrewAI zawierający trzech specjalistów i zarządzającego portfelem:
crew = Crew(
agents=[
fundamental_agent,
technical_agent,
news_agent,
manager_agent,
],
tasks=[
fundamental_task,
technical_task,
news_task,
decision_task,
],
process=Process.sequential,
verbose=False,
tracing=False,
)
Używamy Process.sequential, więc zadania badawcze wykonują się po kolei. Najpierw analityk fundamentalny, potem techniczny i newsowy, a na końcu zarządzający portfelem łączy raporty w końcową ocenę.
Uruchom zespół badawczy
Uruchom przepływ asynchronicznie:
result = await crew.kickoff_async()
Ten krok może chwilę potrwać, bo agenci wykonują wiele wywołań LLM i narzędzi. U mnie kompletny przepływ zajął około 4–5 minut.
Po zakończeniu wyświetl końcowy raport zarządzającego portfelem:
print(result.raw)

Powinieneś otrzymać szczegółowy raport podobny do powyższego, zawierający m.in.:
- Sygnał KUP, TRZYMAJ lub SPRZEDAJ
- Wynik ogólny
- Poziom pewności
- Wyniki specjalistów
- Tezę byczą
- Tezę niedźwiedzią
- Katalizatory
- Ryzyka
- Źródła
W tym przykładzie agenci niezależnie przeanalizowali NVIDIĘ, a następnie połączyli swoje wnioski w jeden, oparty na dowodach raport. Tu właśnie widać korzyść podejścia multiagentowego: zamiast prosić jeden model o zrobienie wszystkiego naraz, każdy agent skupia się na swojej domenie, a zarządzający portfelem scala całość.
Jeśli masz problemy z uruchomieniem powyższego kodu, dołączyłem kompletny Jupyter Notebook z całym kodem i wynikami do wglądu lub samodzielnego uruchomienia: glm-stock-swarm/stock_analyst_crew.ipynb
8. Zbuduj i uruchom interfejs terminalowy
Po przetestowaniu wszystkiego w Jupyter Notebooku, przerobiłem kompletny przepływ na interfejs użytkownika w terminalu (TUI). Zamiast ręcznie uruchamiać każdą komórkę, możesz teraz odpalić cały multiagentowy analityk akcji jednym poleceniem.
Kompletny projekt, wraz z kodem i instrukcjami konfiguracji, jest dostępny na GitHubie: repozytorium glm-stock-swarm.
Zainstaluj i uruchom aplikację
Zacznij od sklonowania repozytorium:
git clone https://github.com/kingabzpro/glm-stock-swarm.git
cd glm-stock-swarm
Dodaj swoje klucze API Z.ai, Finnhub i Tavily do pliku .env, a następnie zainstaluj zależności przy pomocy uv:
uv sync
Na koniec uruchom TUI:
uv run glm-stock-swarm
Przeanalizuj spółkę
Interfejs udostępnia proste pole wejściowe, w którym możesz wpisać dowolny obsługiwany ticker, aby rozpocząć analizę.

Po wysłaniu tickera możesz obserwować pracę agentów po kolei. Najpierw zaczyna analityk fundamentalny, po nim techniczny, potem newsowy, a na końcu zarządzający portfelem, który łączy wszystko w końcową decyzję.

Na końcu TUI generuje kompletny raport bezpośrednio w terminalu, zawierający wynik fundamentalny, techniczny, newsowy, wynik ogólny, pewność, tezę byczą, niedźwiedzią, ryzyka, katalizatory i końcowy sygnał KUP, TRZYMAJ lub SPRZEDAJ.
Zadaj pytania uzupełniające
Dodałem też czat follow-up, żeby nie trzeba było uruchamiać całego pipeline’u multiagentowego za każdym razem, gdy masz kolejne pytanie.

Na przykład po otrzymaniu rekomendacji SPRZEDAJ zapytałem, czy powinienem sprzedać całą pozycję, czy tylko część.

Zamiast ponownie uruchamiać wszystkich czterech agentów, aplikacja wykorzystuje kontekst z wygenerowanego raportu badawczego i wysyła znacznie szybszą odpowiedź uzupełniającą.
To czyni aplikację bardziej praktyczną, bo kosztowny research multiagentowy uruchamia się raz, a pytania dodatkowe mogą korzystać z istniejącej analizy.
Końcowe uwagi
Warto odnotować niezawodność wywoływania narzędzi w moich testach. Wszyscy trzej specjaliści wybierali właściwe narzędzie, respektowali konwencję N/A zamiast traktować brak danych jak zero i zwracali format „wynik + dowody”, którego potrzebował zarządzający portfelem. To zwykle miejsce, gdzie tańsze modele zawodzą.
Cena to szybkość. Artificial Analysis mierzy GLM-5.3-Flash na ok. 45 tokenów wyjściowych na sekundę na API Z.ai, co jest dolnym zakresem dla modeli open-weight tej wielkości — i czuć to w 4–5 minutach pełnego przebiegu zespołu. Na ich indeksie inteligencji ma 57 wobec 60 dla GLM-5.3, więc oddajesz trochę jakości wnioskowania, ale to dobra wymiana, biorąc pod uwagę, że płacisz ok. jedną dziesiątą ceny.
Nic cię tu nie blokuje. Przesiadka na OpenRouter, Hugging Face Inference czy model lokalny przez Ollama zwykle oznacza zmianę trzech rzeczy: klucza API, nazwy modelu i base URL. Podobnie z narzędziami: yfinance zrobi to, co tu dzielą Finnhub i Yahoo.
Następny krok, jaki bym zrobił, to podpięcie do API paper-trading i zaplanowanie porannego przebiegu. Trzymaj to jednak na koncie demo. Ten projekt powstał, by uczyć projektowania agentów, a nie do handlu.
Jeśli chcesz nauczyć się wszystkiego o agentowym AI od podstaw, polecam nasz ścieżkę umiejętności AI Agent Fundamentals jako świetny punkt startowy.