W tym blogu przejdziemy przez kompleksowy przykład indeksowania artykułów badawczych z wyodrębnieniem różnych metadanych - poza pełnym rozdrabnianiem tekstu i osadzeniem - i zbudujemy semantyczne osadzenia do indeksowania i kwerendowania. Bardzo bym to docenił, gdybyś mógł Jeśli uważasz, że ten tutorial jest pomocny. ⭐ star CocoIndex na GitHub CocoIndex na GitHub Używaj przypadków Wyszukiwanie akademickie i odzyskiwanie, a także agenci AI oparte na badaniach Papierowe systemy rekomendacji Grafiki wiedzy badawczej Analiza semantyczna literatury naukowej Co osiągniemy Przyjrzyjmy się temu Jako przykład . PDF Oto co chcemy osiągnąć: Extract the paper metadata, including file name, title, author information, abstract, and number of pages. Build vector embeddings for the metadata, such as the title and abstract, for semantic search. Umożliwia to lepsze wyniki wyszukiwania semantycznego oparte na metadanych. Na przykład można dopasować zapytania tekstowe do tytułów i abstraktów. Build an index of authors and all the file names associated with each author to answer questions like "Give me all the papers by Jeff Dean." If you want to perform full PDF embedding for the paper, you can also refer to . this article Znajdziesz pełny kod . tutaj Jeśli ten artykuł jest dla Ciebie przydatny, daj nam gwiazdkę Aby pomóc nam się rozwijać. GitHub Podstawowe komponenty PDF Preprocessing Reads PDFs using and extracts: pypdf Total number of pages First page content (used as a proxy for metadata-rich information) Markdown Conversion Converts the first page to Markdown using . Marker LLM-Powered Metadata Extraction Sends the first-page Markdown to GPT-4o using CocoIndex's function. ExtractByLlm Extracted metadata includes and more. (string) title (with name, email, and affiliation) authors (string) abstract Semantic Embedding The title is embedded directly using the model by the SentenceTransformer. all-MiniLM-L6-v2 Abstracts are chunked based on semantic punctuation and token count, then each chunk is embedded individually. Relational Data Collection Authors are unrolled and collected into an relation, enabling queries like: author_papers Show all papers by X Which co-authors worked with Y? Wymagania . Install PostgreSQL CocoIndex uses PostgreSQL internally for incremental processing. . Configure your OpenAI API key Alternatywnie mamy natywne wsparcie dla Gemini, Ollama, LiteLLM, . Przewodnik Możesz wybrać swojego ulubionego dostawcę LLM i możesz pracować całkowicie na miejscu. Indeksowanie przepływu Ten projekt demonstruje nieco bardziej kompleksowy przykład zrozumienia metadanych bliżej rzeczywistych przypadków użytkowania. Zobaczysz, jak łatwo jest osiągnąć ten projekt przez CocoIndex w ciągu 100 linii logiki indeksowania - . Kodem Aby lepiej pomóc Ci poruszać się tym, przez co przechodzimy, oto diagram przepływu. Importowanie listy dokumentów w formacie PDF. For each file: Extract the first page of the paper. Convert the first page to Markdown. Extract metadata (title, authors, abstract) from the first page. Split the abstract into chunks, and compute embeddings for each chunk. Export to the following tables in Postgres with PGVector: Metadata (title, authors, abstract) for each paper. Author-to-paper mapping, for author-based query. Embeddings for titles and abstract chunks, for semantic search. Pozwólmy sobie na zoom w krokach. Importowanie dokumentów @cocoindex.flow_def(name="PaperMetadata") def paper_metadata_flow( flow_builder: cocoindex.FlowBuilder, data_scope: cocoindex.DataScope ) -> None: data_scope["documents"] = flow_builder.add_source( cocoindex.sources.LocalFile(path="papers", binary=True), refresh_interval=datetime.timedelta(seconds=10), ) zostanie utworzona tabela z podpolami ( , • , flow_builder.add_source filename content Możemy odnieść się do Na więcej szczegółów. dokumentacji Wyodrębnianie i zbieranie metadanych Wyciąg z pierwszej strony dla podstawowych informacji Definiuj funkcję niestandardową, aby wyodrębnić pierwszą stronę i liczbę stron PDF. @dataclasses.dataclass class PaperBasicInfo: num_pages: int first_page: bytes @cocoindex.op.function() def extract_basic_info(content: bytes) -> PaperBasicInfo: """Extract the first pages of a PDF.""" reader = PdfReader(io.BytesIO(content)) output = io.BytesIO() writer = PdfWriter() writer.add_page(reader.pages[0]) writer.write(output) return PaperBasicInfo(num_pages=len(reader.pages), first_page=output.getvalue()) Teraz włóż to do swojego przepływu. Wyodrębniamy metadane z pierwszej strony, aby zminimalizować koszty przetwarzania, ponieważ cały PDF może być bardzo duży. with data_scope["documents"].row() as doc: doc["basic_info"] = doc["content"].transform(extract_basic_info) Po tym kroku powinieneś mieć podstawowe informacje o każdym papierze. Przeczytaj podstawowe informacje Konwertujemy pierwszą stronę na Markdown za pomocą markera. Alternatywnie możesz łatwo podłączyć swój ulubiony analizator PDF, taki jak Docling. Definiuj funkcję konwertera markerów i przechowuj ją w pamięci podręcznej, ponieważ jej inicjalizacja jest wymagająca zasobów. Zapewnia to, że ta sama instancja konwertera jest ponownie wykorzystywana dla różnych plików wejściowych. @cache def get_marker_converter() -> PdfConverter: config_parser = ConfigParser({}) return PdfConverter( create_model_dict(), config=config_parser.generate_config_dict() ) Włącz go do funkcji custom. @cocoindex.op.function(gpu=True, cache=True, behavior_version=1) def pdf_to_markdown(content: bytes) -> str: """Convert to Markdown.""" with tempfile.NamedTemporaryFile(delete=True, suffix=".pdf") as temp_file: temp_file.write(content) temp_file.flush() text, _, _ = text_from_rendered(get_marker_converter()(temp_file.name)) return text Prześlij je do swojej transformacji with data_scope["documents"].row() as doc: doc["first_page_md"] = doc["basic_info"]["first_page"].transform( pdf_to_markdown ) Po tym kroku powinieneś mieć pierwszą stronę każdego papieru w formacie Markdown. Wyodrębnij podstawowe informacje z LLM CocoIndex natywnie obsługuje ekstrakcję strukturalną LLM z złożonymi i niestandardowymi schematami. If you are interested in learning more about nested schemas, refer to . Ten artykuł @dataclasses.dataclass class PaperMetadata: """ Metadata for a paper. """ title: str authors: list[Author] abstract: str Włóż go do Z definicją klasy danych, CocoIndex automatycznie analizuje odpowiedź LLM do klasy danych. ExtractByLlm doc["metadata"] = doc["first_page_md"].transform( cocoindex.functions.ExtractByLlm( llm_spec=cocoindex.LlmSpec( api_type=cocoindex.LlmApiType.OPENAI, model="gpt-4o" ), output_type=PaperMetadata, instruction="Please extract the metadata from the first page of the paper.", ) ) Po tym kroku powinieneś mieć metadane każdego papieru. Zbieranie metadanych papierowych paper_metadata = data_scope.add_collector() with data_scope["documents"].row() as doc: # ... process # Collect metadata paper_metadata.collect( filename=doc["filename"], title=doc["metadata"]["title"], authors=doc["metadata"]["authors"], abstract=doc["metadata"]["abstract"], num_pages=doc["basic_info"]["num_pages"], ) Just collect anything you need :) Zbiórka Dwóch Informacje Autorka filename Autorka filename Tutaj chcemy zebrać Autor → Papiery w oddzielnej tabeli, aby zbudować funkcję wyszukiwania. Po prostu zebrane przez autora. author_papers = data_scope.add_collector() with data_scope["documents"].row() as doc: with doc["metadata"]["authors"].row() as author: author_papers.collect( author_name=author["name"], filename=doc["filename"], ) Obliczanie i zbieranie wbudowanych Tytuł doc["title_embedding"] = doc["metadata"]["title"].transform( cocoindex.functions.SentenceTransformerEmbed( model="sentence-transformers/all-MiniLM-L6-v2" ) ) abstrakcyjna Podziel abstrakt na kawałki, osadź każdy kawałek i zbierz ich osadzenia. Czasami abstrakcja może być bardzo długa. doc["abstract_chunks"] = doc["metadata"]["abstract"].transform( cocoindex.functions.SplitRecursively( custom_languages=[ cocoindex.functions.CustomLanguageSpec( language_name="abstract", separators_regex=[r"[.?!]+\s+", r"[:;]\s+", r",\s+", r"\s+"], ) ] ), language="abstract", chunk_size=500, min_chunk_size=200, chunk_overlap=150, ) Po tym kroku powinieneś mieć abstrakcyjne kawałki każdego papieru. Wkładaj każdy kawałek i zbieraj ich wkładki. with doc["abstract_chunks"].row() as chunk: chunk["embedding"] = chunk["text"].transform( cocoindex.functions.SentenceTransformerEmbed( model="sentence-transformers/all-MiniLM-L6-v2" ) ) Po tym kroku powinieneś mieć osadzenia abstrakcyjnych kawałków każdego papieru. Zbiórka Embeddings metadata_embeddings = data_scope.add_collector() with data_scope["documents"].row() as doc: # ... process # collect title embedding metadata_embeddings.collect( id=cocoindex.GeneratedField.UUID, filename=doc["filename"], location="title", text=doc["metadata"]["title"], embedding=doc["title_embedding"], ) with doc["abstract_chunks"].row() as chunk: # ... process # collect abstract chunks embeddings metadata_embeddings.collect( id=cocoindex.GeneratedField.UUID, filename=doc["filename"], location="abstract", text=chunk["text"], embedding=chunk["embedding"], ) eksportu Wreszcie, eksportujemy dane do Postgres. paper_metadata.export( "paper_metadata", cocoindex.targets.Postgres(), primary_key_fields=["filename"], ) author_papers.export( "author_papers", cocoindex.targets.Postgres(), primary_key_fields=["author_name", "filename"], ) metadata_embeddings.export( "metadata_embeddings", cocoindex.targets.Postgres(), primary_key_fields=["id"], vector_indexes=[ cocoindex.VectorIndexDef( field_name="embedding", metric=cocoindex.VectorSimilarityMetric.COSINE_SIMILARITY, ) ], ) In this example we use PGVector as embedding stores/ Dzięki CocoIndex możesz przełączyć jedną linię w innych obsługiwanych bazach danych Vector, takich jak Qdrant, zobacz to Na więcej szczegółów. Przewodnik Naszym celem jest standaryzacja interfejsów i uczynienie ich budowaniem LEGO. View in CocoInsight step by step Możesz przejść przez projekt krok po kroku Do zobaczenia CocoInsight Dokładnie jak każde pole jest zbudowane i co dzieje się za kulisami. Zapytaj o indeks Możesz odwołać się do tej części o Wbudowane teksty Jak zbudować zapytanie przeciwko wbudowaniom. Na razie CocoIndex nie zapewnia dodatkowego interfejsu zapytania. Możemy pisać SQL lub polegać na silniku zapytań przez pamięć docelową. Wiele baz danych już zoptymalizowało wdrożenia zapytań za pomocą własnych najlepszych praktyk Przestrzeń kwerendy ma doskonałe rozwiązania do kwerendowania, ponownego rankingowania i innych funkcji związanych z wyszukiwaniem. Jeśli potrzebujesz pomocy w pisaniu zapytania, proszę poczuć się swobodnie, aby dotrzeć do nas . Dyskryminacja Wspieraj nas Nieustannie się doskonalimy, a wkrótce pojawią się kolejne funkcje i przykłady. Jeśli ten artykuł jest dla Ciebie przydatny, daj nam gwiazdkę Aby pomóc nam się rozwijać. GitHub Dzięki za czytanie!