Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds
Powrót do kolekcji
Przewodnik11 min czytania

Embedchain, projekt zamknięty i co dalej

Rozwój Embedchaina stanął w 2025 roku, a projekt przeszedł w Mem0. Co robił, dlaczego się skończył, jak wygląda migracja i czym go zastąpić.

Embedchain, czyli czego uczy zamknięty projekt

Zacznijmy od rzeczy najważniejszej, bo oszczędzi Ci ona wieczoru pracy. Rozwój Embedchaina się zakończył: ostatnie wydanie, 0.1.128, pochodzi z marca 2025 roku, a kod zniknął z repozytorium, które nosi dziś nazwę Mem0 i rozwiązuje inny problem. Stary adres repozytorium przekierowuje właśnie tam.

Jeśli szukasz biblioteki do nowego projektu, to nie jest właściwy wybór i możesz przejść od razu do sekcji o alternatywach. Jeśli masz działający kod na Embedchainie, poniżej znajdziesz opis tego, co się właściwie stało i jak wygląda przejście dalej.

Co ten projekt robił

Embedchain powstał w momencie, gdy budowa wyszukiwania po własnych dokumentach wymagała złożenia kilku warstw ręcznie: wczytania plików, podziału na fragmenty, wyliczenia wektorów, zapisania ich w bazie i sklejenia zapytania z modelem językowym.

Propozycja była prosta i skuteczna. Cały ten łańcuch mieścił się w kilku linijkach, a wybory domyślne były rozsądne, więc pierwszy działający prototyp powstawał w kwadrans zamiast w dwa dni.

Code
Python
from embedchain import App

aplikacja = App()
aplikacja.add("https://example.com/dokumentacja")
aplikacja.add("./umowa.pdf")

print(aplikacja.query("Jaki jest okres wypowiedzenia?"))

Wartość polegała na tym, czego tu nie widać. Rozpoznanie typu źródła, wczytanie treści, podział na fragmenty, wybór modelu osadzeń, konfiguracja bazy wektorowej i złożenie promptu działy się bez jednej linii konfiguracji. Dla kogoś, kto chciał sprawdzić pomysł, a nie budować infrastrukturę, była to duża oszczędność.

Biblioteka obsługiwała kilkanaście typów źródeł: strony internetowe, pliki PDF, dokumenty tekstowe, kanały wideo, repozytoria kodu i bazy danych. Wymienne były też modele językowe oraz magazyny wektorów, w tym rozwiązania opisane w tekstach o Chromie i Qdrancie.

Dlaczego projekt się skończył

Powód nie leży w błędach ani w braku zainteresowania, tylko w zmianie problemu, który warto rozwiązywać.

Wyszukiwanie po dokumentach stało się funkcją, a nie produktem. Modele językowe dostały okna kontekstu liczone w setkach tysięcy tokenów, więc dla wielu zastosowań wrzucenie dokumentu wprost do promptu okazało się prostsze niż utrzymywanie osobnej bazy wektorowej. Jednocześnie duże biblioteki, w tym LangChain, objęły ten sam zakres i dołożyły resztę potrzebną w produkcji.

Warstwa upraszczająca wpadła w klasyczną pułapkę. Jest wygodna dopóki robisz dokładnie to, co przewidział autor, a gdy potrzebujesz zmienić sposób cięcia albo dołożyć własne filtrowanie po metadanych, abstrakcja zaczyna przeszkadzać. Projekty tej klasy albo rosną w stronę pełnego frameworka, albo tracą rację bytu.

Autorzy wybrali trzecią drogę i przeszli do innego problemu. Zamiast pytać, jak odnaleźć fragment dokumentu, zapytali, jak aplikacja ma pamiętać rozmowę z użytkownikiem między sesjami. Tak powstał Mem0, i to jest zmiana kierunku, a nie kolejna wersja tego samego.

Na czym polega różnica wobec następcy

To rozróżnienie warto zrozumieć, bo z zewnątrz oba narzędzia wyglądają podobnie: jedno i drugie zapisuje tekst, liczy wektory i coś potem znajduje.

Wyszukiwanie po dokumentach operuje na zbiorze, który jest z góry dany i zmienia się rzadko. Dokumenty wczytujesz raz, a zapytanie odnajduje w nich pasujące fragmenty. Wiedza jest zewnętrzna wobec rozmowy.

Warstwa pamięci operuje na tym, co powiedział użytkownik, i zmienia się przy każdej wypowiedzi. Nie zapisujesz całej rozmowy, tylko wnioski z niej: preferencje, fakty o osobie, ustalenia. Model językowy uczestniczy w zapisie, bo to on decyduje, co z wypowiedzi jest warte zapamiętania i co unieważnia poprzedni zapis.

Trzy praktyczne konsekwencje. Pierwsza: pamięć wymaga aktualizacji, a nie tylko dopisywania, bo nowa informacja bywa sprzeczna ze starą. Druga: zapis kosztuje wywołanie modelu, więc jest droższy niż zwykłe wyliczenie wektora. Trzecia: dane są przypisane do osoby, co pociąga wymagania dotyczące usuwania i prywatności, których wyszukiwanie po dokumentacji firmowej nie ma.

Jak wygląda migracja

Nie ma tu automatu przenoszącego kod, bo interfejsy różnią się na poziomie pojęć, a nie nazw funkcji. Skala pracy zależy od tego, do czego używałeś biblioteki.

Jeśli budowałeś wyszukiwanie po stałym zbiorze dokumentów, następca nie jest właściwym celem. Sensowniejsze jest przejście na jedną z dojrzałych bibliotek albo napisanie tej warstwy samodzielnie, bo to dziś kilkadziesiąt linii kodu.

Jeśli budowałeś asystenta, który miał pamiętać użytkownika między sesjami, przejście ma sens i przypomina raczej napisanie nowej warstwy niż tłumaczenie starej.

Code
Python
from mem0 import Memory

pamiec = Memory()
pamiec.add("Pracuje w strefie czasowej Europa/Warszawa", user_id="anna")

kontekst = pamiec.search("strefa czasowa", filters={"user_id": "anna"})

Największa różnica przy przepisywaniu dotyczy tego, kiedy coś zapisujesz. W starym układzie ładowałeś dokumenty na starcie i potem tylko pytałeś. W nowym zapis dzieje się w trakcie rozmowy, po każdej turze, a wyszukiwanie idzie przed wywołaniem modelu, żeby dołożyć kontekst do promptu.

Trzecia droga, najczęściej pomijana, to rezygnacja z biblioteki. Jeśli używałeś tylko podstawowego przepływu, samodzielna implementacja na bazie wektorowej i kliencie modelu daje mniej kodu do utrzymania niż zależność, której cykl życia właśnie się skończył.

Czym zastąpić go w nowym projekcie

RozwiązanieMocna stronaSłabośćKiedy wybrać
Mem0Pamięć rozmowy, aktualizacja zapisówInny problem niż wyszukiwanie dokumentówAsystent pamiętający użytkownika
LangChainNajwiększy zakres, wiele integracjiCiężka abstrakcja, sporo pojęćZłożony przepływ z wieloma krokami
LlamaIndexNastawiony wprost na dokumentyMniejszy zasięg poza wyszukiwaniemWyszukiwanie po własnym zbiorze
Własna implementacjaZero zależności, pełna kontrolaWszystko piszesz samProsty przepływ, jeden typ źródła

Ostatni wiersz zasługuje na więcej uwagi, niż zwykle dostaje. Podstawowe wyszukiwanie po dokumentach to dziś podział tekstu, wywołanie modelu osadzeń, zapis do bazy i zapytanie z dołączonym kontekstem. To mieści się w stu linijkach, które rozumiesz w całości i które nie przestaną działać, gdy ktoś zarchiwizuje repozytorium.

Wybór między drugim a trzecim wierszem zależy od zakresu. Jeśli poza wyszukiwaniem potrzebujesz narzędzi, agentów i wielu kroków, wygrywa szersza biblioteka. Jeśli chodzi wyłącznie o dokumenty, węższe narzędzie daje mniej pojęć do nauczenia się.

Pierwszy wiersz rozwiązuje inny problem i lepiej rozstrzygnąć to przed migracją, bo pamięć rozmowy nie zastąpi wyszukiwania po dokumentach. Jeśli jednak to pamięć była faktycznym celem, dalej w tę stronę idzie Letta, gdzie agent sam edytuje swoje bloki pamięci, wywołując narzędzia. Ma przy tym własną odmianę tej samej historii: pythonowy serwer autorzy w lipcu 2026 roku opisali jako przestarzały i utrzymywany awaryjnie, a rozwój przenieśli do pakietu instalowanego z npm.

Własna warstwa w praktyce

Skoro najczęstszą odpowiedzią na zamknięcie takiej biblioteki jest napisanie tego samemu, warto pokazać, ile pracy to naprawdę oznacza.

Code
Python
import chromadb
from openai import OpenAI

klient = OpenAI()
baza = chromadb.PersistentClient(path="./dane")
kolekcja = baza.get_or_create_collection("dokumenty")

def wektor(tekst: str) -> list[float]:
    odpowiedz = klient.embeddings.create(
        model="text-embedding-3-small",
        input=tekst,
    )
    return odpowiedz.data[0].embedding

def dodaj(fragmenty: list[str], zrodlo: str) -> None:
    kolekcja.add(
        ids=[f"{zrodlo}-{i}" for i in range(len(fragmenty))],
        embeddings=[wektor(f) for f in fragmenty],
        documents=fragmenty,
        metadatas=[{"zrodlo": zrodlo} for _ in fragmenty],
    )

def zapytaj(pytanie: str) -> str:
    trafienia = kolekcja.query(query_embeddings=[wektor(pytanie)], n_results=5)
    kontekst = "\n\n".join(trafienia["documents"][0])
    odpowiedz = klient.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "Odpowiadaj wylacznie na podstawie kontekstu."},
            {"role": "user", "content": f"Kontekst:\n{kontekst}\n\nPytanie: {pytanie}"},
        ],
    )
    return odpowiedz.choices[0].message.content

To jest w zasadzie całość podstawowego przepływu, pominąwszy podział tekstu na fragmenty, który dokłada kilkanaście linii albo jedno wywołanie gotowej biblioteki. Trzydzieści kilka linii kodu zamiast zależności o niepewnej przyszłości.

Zysk nie polega jednak na liczbie linii, tylko na tym, że każda decyzja jest widoczna. Wiesz, jaki model osadzeń liczy wektory, ile fragmentów trafia do promptu i jak dokładnie brzmi instrukcja systemowa. Przy bibliotece pośredniczącej te trzy rzeczy są ukryte, a to właśnie one decydują o jakości odpowiedzi i o rachunku.

Widać tu też, gdzie leżą prawdziwe punkty do strojenia. Liczba zwracanych fragmentów wpływa na koszt i na trafność w przeciwnych kierunkach. Metadane pozwalają zawęzić wyszukiwanie do jednego źródła. Instrukcja systemowa decyduje, czy model przyzna się do niewiedzy, czy dopowie brakującą część. Żadnej z tych rzeczy nie da się dobrać z góry, bo zależą od Twoich dokumentów.

Miejsca, w których warto sięgnąć po gotowe rozwiązanie, są dwa. Pierwsze to wczytywanie egzotycznych formatów, bo tam pisanie własnego kodu się nie opłaca. Drugie to przepływy z wieloma krokami i narzędziami, gdzie ręczne sklejanie zaczyna przypominać budowę frameworka.

Jedna rzecz w tym kodzie zasługuje na uwagę od początku, bo później poprawia się ją trudno. Wyliczanie wektorów pojedynczo, fragment po fragmencie, działa przy dziesięciu dokumentach i staje się nie do przyjęcia przy tysiącu. Modele osadzeń przyjmują wiele tekstów w jednym wywołaniu, więc przetwarzanie partiami skraca czas indeksowania kilkudziesięciokrotnie i obniża liczbę zapytań do usługi. To najprostsza optymalizacja w całym tym przepływie i zarazem najczęściej pomijana, bo przy pierwszym prototypie różnicy nie widać.

Czego uczy ta historia

Zamknięcie projektu jest w tym ekosystemie zdarzeniem zwyczajnym, a nie wyjątkiem, więc warto wyciągnąć z tego wnioski na przyszłość.

Pierwszy dotyczy warstw upraszczających. Biblioteka, której cała wartość polega na skróceniu piętnastu linii do trzech, jest krucha z założenia. Kiedy podstawowa czynność stanie się prostsza albo wchłonie ją większy projekt, warstwa traci powód istnienia. To nie jest zarzut wobec autorów, tylko przewidywalna właściwość takich narzędzi.

Drugi dotyczy uzależnienia od abstrakcji. Kod wywołujący bazę wektorową i model językowy wprost przetrwa zamknięcie dowolnego projektu pośredniczącego. Kod zbudowany wokół cudzego obiektu głównego wymaga przepisania.

Trzeci dotyczy oceny zależności przed jej dodaniem. Warto sprawdzić datę ostatniej zmiany, liczbę osób z prawem zapisu i to, czy projekt ma za sobą firmę, a jeśli tak, czy on sam jest tą firmą, czy tylko jej pobocznym wydaniem. Odpowiedzi na te pytania mówią więcej niż liczba gwiazdek.

Czwarty jest optymistyczny. Zarchiwizowany kod nie znika, więc działający projekt na przypiętej wersji będzie działał dalej. Ryzyko dotyczy poprawek bezpieczeństwa i zgodności z nowymi wersjami zależności, a nie nagłego zniknięcia biblioteki.

Jeśli masz takie wdrożenie na produkcji, trzy kroki wystarczą, żeby przestać się nim martwić. Przypnij dokładne wersje wszystkich zależności, łącznie z pośrednimi, bo to one najczęściej psują projekt bez opieki. Zapisz w repozytorium notatkę mówiącą wprost, że biblioteka nie jest już rozwijana i dlaczego nadal tam jest, bo za rok nikt tego nie będzie pamiętał. Wydziel wywołania biblioteki za własnym interfejsem obejmującym dwie operacje: dodanie dokumentu i zapytanie. Ten trzeci krok zajmuje popołudnie i zamienia przyszłą migrację z przepisywania aplikacji w podmianę jednego pliku.

Warto też ustawić przypomnienie o sprawdzeniu zgłoszeń bezpieczeństwa dla tej zależności co kilka miesięcy. Zarchiwizowany projekt nie dostanie poprawki, ale wiedza o problemie pozwala zareagować wcześniej niż w dniu, w którym ktoś go wykorzysta.

Typowe błędy

Pierwszy to rozpoczynanie nowego projektu na porzuconej bibliotece dlatego, że wyszła wysoko w wyszukiwarce. Data ostatniego wydania mówi więcej niż pozycja w wynikach.

Drugi to traktowanie następcy jako wersji zgodnej wstecz. To narzędzie do innego problemu, więc migracja jest przepisaniem warstwy, a nie podmianą importów.

Trzeci to przenoszenie wyszukiwania po dokumentach do warstwy pamięci. Zapisywanie stałej dokumentacji przez mechanizm zaprojektowany do zmiennych faktów o użytkowniku kosztuje wywołania modelu i nie daje nic w zamian.

Czwarty to brak przypiętych wersji zależności. Przy projekcie bez opieki każda aktualizacja biblioteki obok potrafi go zepsuć, a nikt tego nie naprawi.

Piąty to migracja w całości naraz. Sensowniej jest wydzielić warstwę dostępu do wyszukiwania za własnym interfejsem, a potem podmienić implementację pod spodem.

Szósty to pominięcie pytania, czy biblioteka jest w ogóle potrzebna. Przy jednym typie źródła i prostym przepływie własny kod bywa krótszy niż konfiguracja cudzego.

FAQ

Czy Embedchain jeszcze działa?

Tak, pakiet nadal leży w rejestrze PyPI, instaluje się i uruchamia, a istniejące projekty na przypiętych wersjach działają dalej. Rozwój jednak stanął na wydaniu 0.1.128 z marca 2025 roku, a kod zniknął z repozytorium, więc nie powstają poprawki ani wsparcie dla nowych wersji zależności.

Czy Mem0 to nowa wersja Embedchaina?

Nie w sensie zgodności. Mem0 wyrósł z tego samego projektu i tej samej organizacji, natomiast rozwiązuje inny problem: pamięta rozmowę z użytkownikiem zamiast przeszukiwać stały zbiór dokumentów. Interfejsy się różnią, więc przejście wymaga napisania nowej warstwy.

Czym zastąpić go przy wyszukiwaniu po dokumentach?

Zależy od zakresu. Przy prostym przepływie własna implementacja na bazie wektorowej to około stu linii kodu bez żadnej zależności. Przy złożonym przepływie sensowniejsze są dojrzałe biblioteki takie jak LangChain albo LlamaIndex.

Czy warto migrować, jeśli wszystko działa?

Nie natychmiast. Działający system na przypiętych wersjach można zostawić i zaplanować przejście spokojnie. Warto natomiast wydzielić dostęp do wyszukiwania za własnym interfejsem, bo to zamienia przyszłą migrację z przepisywania aplikacji w podmianę jednego modułu.

Jak sprawdzić, czy biblioteka ma przyszłość?

Spójrz na datę ostatniego wydania, liczbę osób z prawem zapisu i tempo zamykania zgłoszeń. Sprawdź też, czy projekt jest głównym produktem firmy, czy jej wydaniem pobocznym, bo to drugie znika częściej. Liczba gwiazdek nie mówi nic o utrzymaniu.

Historię projektu opisuje repozytorium Mem0, a dokumentacja następcy stoi na stronie projektu.