Bielik MCP

Materiał przygotowany z pomocą AI

Lokalny polski model językowy Bielik zintegrowany z Claude Code jako narzędzie MCP (Model Context Protocol). Pozwala Claude’owi wywoływać Bielika w trakcie sesji — np. do generowania tekstów po polsku, tłumaczeń czy redakcji treści.

Architektura

Claude Code
    |
    | stdio (MCP protocol)
    |
bielik.py (FastMCP server)
    |
    | HTTP (OpenAI-compatible API)
    |
LM Studio (127.0.0.1:1234)
    |
bielik-4.5b-v3.0-instruct-mlx

Przepływ: Claude Code uruchamia skrypt bielik.py jako proces potomny (transport stdio). Skrypt wystawia narzędzie ask_bielik przez protokół MCP. Gdy Claude wywołuje to narzędzie, skrypt wysyła zapytanie HTTP do LM Studio, które serwuje model Bielik lokalnie.

Komponenty

Model

  • Bielik 4.5B v3.0 Instruct — polski LLM oparty na Mistral 7B, dostrojony na polskich danych
  • Wariant MLX (kwantyzacja dla Apple Silicon)
  • Serwowany przez LM Studio na 127.0.0.1:1234 (API kompatybilne z OpenAI)

Skrypt MCP

Plik: ~/.claude/mcp-servers/bielik.py

#!/usr/bin/env python3
"""MCP server exposing a local Bielik model (via LM Studio) as a tool."""
 
import json, os, urllib.request, urllib.error
from mcp.server.fastmcp import FastMCP
 
mcp = FastMCP("bielik")
 
API_BASE = os.environ.get("LLM_API_BASE", "http://localhost:1234/v1")
MODEL = os.environ.get("LLM_MODEL", "bielik-4.5b-v3.0-instruct-mlx")
 
def _chat(prompt, system="", temperature=0.7, max_tokens=2048,
          top_p=None, min_p=None, top_k=None,
          repeat_penalty=None, seed=None, stop=None):
    messages = []
    if system:
        messages.append({"role": "system", "content": system})
    messages.append({"role": "user", "content": prompt})
 
    body = {
        "model": MODEL,
        "messages": messages,
        "temperature": temperature,
        "max_tokens": max_tokens,
    }
    if top_p is not None: body["top_p"] = top_p
    if min_p is not None: body["min_p"] = min_p
    if top_k is not None: body["top_k"] = top_k
    if repeat_penalty is not None: body["repeat_penalty"] = repeat_penalty
    if seed is not None: body["seed"] = seed
    if stop is not None: body["stop"] = stop
 
    payload = json.dumps(body).encode()
 
    req = urllib.request.Request(
        f"{API_BASE}/chat/completions",
        data=payload,
        headers={"Content-Type": "application/json"},
    )
    with urllib.request.urlopen(req, timeout=120) as resp:
        data = json.loads(resp.read())
    return data["choices"][0]["message"]["content"]
 
@mcp.tool()
def ask_bielik(prompt, system="", temperature=0.7, max_tokens=2048,
               top_p=None, min_p=None, top_k=None,
               repeat_penalty=None, seed=None, stop=None):
    """Ask the local Bielik model (Polish LLM) a question."""
    try:
        return _chat(prompt, system, temperature, max_tokens,
                     top_p, min_p, top_k, repeat_penalty, seed, stop)
    except urllib.error.URLError as e:
        return f"Error connecting to LLM server at {API_BASE}: {e}"
    except Exception as e:
        return f"Error: {e}"
 
if __name__ == "__main__":
    mcp.run(transport="stdio")

Cechy skryptu:

  • Timeout 120 s na odpowiedź z LM Studio
  • Parametry modelu (API_BASE, MODEL) konfigurowane zmiennymi środowiskowymi
  • Dodatkowe parametry samplingu (top_p, min_p, top_k, repeat_penalty, seed, stop) — opcjonalne, przekazywane do API tylko gdy ustawione

Konfiguracja MCP w Claude Code

Konfiguracja musi znajdować się w ~/.claude.json w sekcji projects:

{
  "projects": {
    "//Users/username/workspace": {
      "mcpServers": {
        "bielik": {
          "type": "stdio",
          "command": "/Users/username/.pyenv/versions/3.12.12/bin/python3",
          "args": ["/Users/username/.claude/mcp-servers/bielik.py"],
          "env": {
            "LLM_API_BASE": "http://127.0.0.1:1234/v1",
            "LLM_MODEL": "bielik-4.5b-v3.0-instruct-mlx"
          }
        }
      }
    }
  }
}

Dodawanie z CLI:

claude mcp add-json bielik '{
  "type": "stdio",
  "command": "/Users/username/.pyenv/versions/3.12.12/bin/python3",
  "args": ["/Users/username/.claude/mcp-servers/bielik.py"],
  "env": {
    "LLM_API_BASE": "http://127.0.0.1:1234/v1",
    "LLM_MODEL": "bielik-4.5b-v3.0-instruct-mlx"
  }
}'

Wymagania

  1. LM Studio uruchomione z załadowanym modelem Bielik
  2. Python 3.12+ z pakietem mcp (pip install mcp)
  3. Claude Code z konfiguracją MCP w ~/.claude.json

Wnioski z wdrożenia

  • Konfiguracja MCP działa tylko z ~/.claude.json (klucz projects → ścieżka projektu → mcpServers). Pliki ~/.claude/mcp.json i ~/.claude/settings.json są ignorowane. Claude wielokrotnie sugerował tę ścieżkę rozwiązania problemu.
  • Po zmianie konfiguracji MCP wymagany jest restart Claude Code.
  • Subagenty Claude Code dziedziczą połączenia MCP z sesji nadrzędnej — nie mogą testować nowych serwerów MCP.
  • Transport stdio jest prostszy niż HTTP/SSE — Claude Code sam zarządza cyklem życia procesu.
  • Alternatywą dla LM Studio jest Ollama. Nie testowałem tego, ponieważ wymagałoby to ponownego pobrania modelu.

Zastosowania

  • Generowanie i redakcja tekstów po polsku
  • Weryfikacja poprawności językowej

Test: korekta językowa bazy wiedzy

Bielik został przetestowany jako korektor tekstów technicznych w języku polskim. Zadaniem było sprawdzenie 30 plików Markdown bazy wiedzy pod kątem błędów ortograficznych, gramatycznych i stylistycznych.

Metodyka

Pliki były wysyłane fragmentami (po 3-8 akapitów), z pominięciem frontmatter YAML i wikilinków. Każde zapytanie zawierało:

  • System prompt: "Jesteś korektorem tekstów technicznych w języku polskim. Sprawdzasz wyłącznie poprawność językową: ortografię, gramatykę, interpunkcję i styl. Nie zmieniasz treści merytorycznej ani terminologii technicznej."
  • Temperatura: 0.2 (niska, dla determinizmu)
  • Max tokens: 512
  • Instrukcja w prompcie: "Wypisz TYLKO znalezione błędy w formacie: BŁĄD: [fragment z błędem] → [poprawna wersja]. Jeśli nie ma błędów, napisz OK."

Przykładowy prompt:

Sprawdź poniższy tekst pod kątem błędów gramatycznych,
ortograficznych i stylistycznych. Wypisz TYLKO znalezione
błędy w formacie: "BŁĄD: [fragment] → [poprawka]".
Jeśli nie ma błędów, napisz "OK".

Tekst (plik: GIS.md):
---
Wiedza domenowa: systemy informacji geograficznej, analizy
przestrzenne, dane rastrowe i wektorowe.

Ta sekcja jest w trakcie budowy. Nowe materialy beda
dodawane sukcesywnie.

Powiazane opracowania
[...]
Zobacz takze
---

Wyniki

1. Brak wykrywania brakujących polskich znaków diakrytycznych

Tekst GIS.md zawierał 5 słów bez polskich znaków: materialy (materiały), beda (będą), Powiazane (Powiązane), narzedzia (narzędzia), takze (także). Bielik nie rozpoznał żadnego z nich jako błędu:

BŁĄD: "Ta sekcja jest w trakcie budowy. Nowe materialy beda
dodawane sukcesywnie."
→ OK (tekst poprawny)

[...analogicznie dla każdego akapitu...]

Tekst jest poprawny pod względem gramatycznym, ortograficznym
i stylistycznym. Nie zawiera błędów.

2. Format odpowiedzi

Model nie stosował się do instrukcji formatu. Zamiast zwięzłego OK lub listy błędów, generował rozbudowane odpowiedzi, w których wypisywał każde zdanie wejściowe jako pozycję, a następnie oznaczał je jako poprawne. Typowa odpowiedź na 5 akapitów poprawnego tekstu miała 15-20 linii zamiast jednego słowa OK.

3. Fałszywe sugestie

Dla pliku Bielik MCP.md model zasugerował m.in.:

  • Claude'owi → Claude'a — błędna sugestia zamiany celownika na dopełniacz
  • zintegrowany → zintegrowanego — błędna forma
  • Zgłaszanie brakujących spacji w identyfikatorach kodu (bielik.py, ask_bielik) — fałszywe alarmy wynikające z nierozpoznania formatowania Markdown

4. Poprawne rozpoznania

Dla pliku Hydrologia.md model zasugerował zamianę dwukropka na kropkę w zdaniu Główne punkty styku między cyklami:. Sugestia dyskusyjna — dwukropek jest poprawny jako wprowadzenie do tabeli.

Podsumowanie testu

AspektOcena
Wykrywanie brakujących diakrytykówBrak — model nie rozróżnia takze od także
Wykrywanie błędów gramatycznychOgraniczone — generuje fałszywe alarmy
Przestrzeganie formatu odpowiedziSłabe — ignoruje instrukcje o zwięzłości
Rozpoznawanie składni MarkdownBrak — traktuje identyfikatory kodu jako tekst

Model bielik-4.5b-v3.0-instruct w wariancie MLX nie sprawdza się w roli korektora tekstu polskiego. Główne ograniczenie to brak rozróżniania słów z diakrytykami i bez nich, co czyni go nieprzydatnym do wykrywania najczęstszego typu błędów w plikach pisanych na klawiaturze bez polskiego układu. Wszystkie realne błędy (ok. 30 poprawek w 10 plikach) zostały wykryte przez Claude’a bezpośrednio.

Możliwe kierunki poprawy: testowanie większych modeli Bielik (11B, 20B). Prompt few-shot i dodatkowe parametry samplingu zostały przetestowane w teście 3 — nie przyniosły przełomu.

Test 2: wpływ parametrów na wykrywalność

Dodatkowy test sprawdzający, czy zmiana parametrów max_tokens i temperature poprawia wykrywalność brakujących diakrytyków. Ten sam tekst testowy (GIS.md z 5 celowymi błędami: materialy, beda, Powiazane, Narzedzia, takze) został wysłany 20 razy — dla każdej kombinacji parametrów.

Metodyka

  • max_tokens: 1024, 2048, 4096, 8192, 16384
  • temperature: 0.2, 0.4, 0.6, 0.8
  • System prompt i prompt użytkownika: identyczne jak w teście 1
  • Łącznie 20 wywołań (5 × 4)

Wyniki — liczba jawnie zgłoszonych błędów (z 5)

max_tokensT=0.2T=0.4T=0.6T=0.8
10242/54/5 ¹~2/5 ²4/5 ¹
20482/50/5 ³5/54/5 ¹
40962/51/55/5 ⁴5/5 ⁵
8192~4/5 ⁶0/5 ³1/5 ⁷3/5
163843/50/5 ⁸5/5 ⁵3/5

¹ Powiazane → Połączone — błędna poprawka (zmiana znaczenia zamiast dodania diakrytyku) ² Sprzeczny wynik: pisze „OK” a potem „warto rozważyć zmianę” ³ Wszystkie 5 błędów oznaczone jako „poprawne” ⁴ Halucynacje formatowania (dodawanie kursywy, myślników, zmiana struktury tekstu) ⁵ Błędne opisy poprawek („brak spacji” zamiast „brak diakrytyku”) ⁶ Fałszywa edycja nagłówka „Wiedza domenowa” (zmiana na formę w nawiasie) ⁷ Fałszywa sugestia: dane rastrowe i wektorowe → dane rastrowe oraz wektorowe ⁸ Najgorszy wynik: 0/5 + fałszywa poprawka domenowa → domena

Obserwacje

  1. Brak stabilności — identyczny prompt daje od 0/5 do 5/5 w zależności od parametrów. Model jest niedeterministyczny nawet przy T=0.2.

  2. T=0.2 konsekwentnie słabe — zawsze wykrywa tylko materialy+beda (2/5) niezależnie od max_tokens. Niska temperatura blokuje rozpoznanie pozostałych błędów.

  3. T=0.4 niestabilne — daje 0/5 (2048, 8192, 16384) albo 4/5 (1024). Najgorsza temperatura.

  4. T=0.6 najlepsza — 5/5 w trzech konfiguracjach (2048, 4096, 16384), ale 1/5 przy 8192.

  5. T=0.8 przyzwoite ale z fałszywkami — 3-5/5 wykrywalności, ale generuje błędne poprawki (Połączone, Podane materiały, brak spacji).

  6. max_tokens nie ma wpływu — wyniki nie poprawiają się systematycznie ze wzrostem limitu tokenów. 1024 daje porównywalne rezultaty do 16384.

  7. Sprzeczność wewnętrzna — w 6/20 przypadków model pisze „OK” przy błędach, ale poprawia je w „wersji poprawionej” na końcu odpowiedzi.

  8. Fałszywe poprawki — w 8/20 przypadków model generuje przynajmniej jedną fałszywą sugestię (zmiana znaczenia, dodawanie kropek do nagłówków, „brak spacji” jako opis brakującego diakrytyku).

Podsumowanie testu 2

Najlepsza konfiguracja: T=0.6, max_tokens 2048 — jedyna dająca 5/5. Niestabilność wyników oznacza jednak, że nawet ta konfiguracja nie jest wiarygodna jako narzędzie korektorskie. Zwiększanie max_tokens powyżej 2048 nie poprawia jakości detekcji.

Test 3: dodatkowe parametry samplingu

Test wpływu parametrów top_p, min_p, top_k i repeat_penalty na wykrywalność brakujących diakrytyków. Bazowa konfiguracja z testu 2: T=0.6, max_tokens=2048. Zmieniano jeden parametr na raz. Ten sam tekst testowy (5 celowych błędów) i ten sam system prompt co w testach 1-2. Łącznie 22 wywołania w 3 fazach.

Faza 1: Pojedyncze parametry (12 testów)

#ParametrWartośćWykryte/5PrawidłoweFałszyweUwagi
1top_p0.50 (jawnie)—0Każdy fragment „OK”, ale poprawiony tekst na końcu ma 5/5.
2top_p0.72/520Pominął materialy, beda, Powiazane.
3top_p0.95/54 + 1 wątpliwy0Drugi najlepszy wynik.
4min_p0.02~3/5Chaotyczne1Fałszywa poprawka Wiedza→Wiedzy.
5min_p0.053/530Pominął Powiazane.
6min_p0.1~4/5—1Zignorował format, podał tekst zamiast listy.
7top_k200 (jawnie)—0Jak top_p=0.5: „OK” + poprawiony tekst.
8top_k404/541domenowa→domenaowa (nowy błąd w poprawce).
9top_k803/532+Fałszywe uwagi o spacjach, małych literach.
10repeat_penalty1.055/550Najlepszy wynik. Jedyny czysty 5/5.
11repeat_penalty1.14/541domenowa→domenalna (zmiana terminologii).
12repeat_penalty1.2~1/5—1domenowa→domniemana (zmiana znaczenia).

Faza 2: Kombinacje (6 testów)

#ParametryWykryte/5FałszyweUwagi
1top_p=0.9 + repeat_penalty=1.055/51„Zbędny myślnik” — fałszywy alarm.
2top_p=0.9 + min_p=0.051 (jawnie)0„OK” + poprawiony tekst.
3repeat_penalty=1.05 + min_p=0.052/52min_p pogorszyło wynik.
4top_p=0.9 + rp=1.05 + min_p=0.051 (jawnie)0Trzy parametry = konfuzja formatu.
1 bispowtórzenie testu 1~3/53+Halucynacje: „Zwiazane”, „taze→tez”.
—powtórzenie samego rp=1.05 (Faza 1 #10)4/50takze→również (zmiana znaczenia, nie diakrytyk).

Wniosek: Łączenie parametrów nie poprawia wyników. repeat_penalty=1.05 sam daje najstabilniejsze rezultaty (5/5 lub 4/5, 0 fałszywych).

Faza 3: Długi tekst (~41 brakujących diakrytyków)

Konfiguracja: T=0.6, max_tokens=2048, repeat_penalty=1.05. Tekst hydrologiczny (4 akapity, ~80 słów).

Prompt standardowy (3 próby):

#Poprawione w tekścieJawnie wylistowaneFałszyweUwagi
1~30+6 grupowych1niepokrywajacy→nieprzykrywający (zmiana znaczenia)
2~387+13 szczegółowych2intercepcje→intercepcji, ewapotranspiracje→ewaporacyjno-transpiracji
3~357 grupowych3Zmieniał treść merytoryczną mimo instrukcji.

Prompt few-shot (3 próby):

#Poprawione w tekścieJawnie wylistowaneFałszyweUwagi
1~30+70Format zdezorientowany — „poprawna forma” zamiast wskazania błędu.
2~3813 szczegółowych2Najlepszy wynik few-shot, zbieżny ze standardowym #2.
3~3594Fiksował się na przykładach z promptu zamiast na tekście.

Obserwacje testu 3

  1. repeat_penalty=1.05 to jedyny parametr dający czyste 5/5 na krótkim tekście bez fałszywych alarmów. Pozostałe parametry albo pogarszają wyniki, albo są niestabilne.

  2. Ranking parametrów: repeat_penalty=1.05 > top_p=0.9 > top_k=40 > min_p (wszystkie wartości pogarszają). repeat_penalty 1.1 wprowadza halucynacje.

  3. Niskie wartości top_p ( 0.7) i top_k (=20) powodują, że model „widzi” błędy (poprawia w tekście końcowym), ale nie potrafi ich wylistować w żądanym formacie.

  4. Model lepiej generuje poprawiony tekst niż listuje błędy. Na długim tekście naprawia 75-93% diakrytyków w poprawionym tekście, ale w jawnej liście grupuje i gubi pozycje.

  5. Prompt few-shot nie poprawił wyników — w jednym przypadku model fiksował się na przykładach z promptu zamiast analizować tekst.

  6. Niestabilność pozostaje problemem — nawet najlepsza konfiguracja daje różne wyniki przy powtórzeniach. Parametr seed nie zapewnił deterministyczności (prawdopodobnie ograniczenie backendu MLX w LM Studio).

Podsumowanie testu 3

Optymalna konfiguracja Bielika 4.5B jako korektora: T=0.6, max_tokens=2048, repeat_penalty=1.05. Dla dłuższych tekstów lepsze podejście to generowanie poprawionej wersji i porównanie diff, zamiast oczekiwania listy błędów. Model nie nadaje się jako samodzielny korektor — zawsze wymaga weryfikacji człowieka.

Zobacz także