In this blog we will walk through a comprehensive example of indexing research papers with extracting different metadata — beyond full text chunking and embedding — and build semantic embeddings for indexing and querying. We would greatly appreciate it if you could Wenn Sie dieses Tutorial hilfreich finden. ⭐ star CocoIndex auf GitHub CocoIndex auf GitHub Verwenden von Fällen Akademische Suche und Abruf sowie forschungsbasierte KI-Agenten Papierempfehlungssysteme Forschung Wissensgrafiken Semantische Analyse der wissenschaftlichen Literatur Was wir erreichen werden Lassen Sie uns einen Blick auf diese Als ein Beispiel. PDF Hier ist, was wir erreichen wollen: 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. Dies ermöglicht bessere metadatenbasierte semantische Suchergebnisse. Zum Beispiel können Sie Textabfragen mit Titeln und Abstrakten übereinstimmen. 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 Hier finden Sie den vollständigen Code . hier Wenn Ihnen dieser Artikel hilfreich ist, geben Sie uns bitte einen Stern um uns zu helfen zu wachsen. Github Kernkomponenten 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? Voraussetzungen . Install PostgreSQL CocoIndex uses PostgreSQL internally for incremental processing. . Configure your OpenAI API key Alternativ haben wir native Unterstützung für Gemini, Ollama, LiteLLM, Checkout . Führer Sie können Ihren bevorzugten LLM-Anbieter auswählen und vollständig vor Ort arbeiten. Definition von Indexing Flow Dieses Projekt demonstriert ein etwas umfassenderes Beispiel für das Verständnis von Metadaten näher an reale Anwendungsfälle. Sie werden sehen, wie einfach es ist, dieses Design von CocoIndex innerhalb von 100 Zeilen der Indexierungslogik zu erreichen - . Der Code Um Ihnen besser zu helfen zu navigieren, was wir durchlaufen werden, hier ist ein Flussdiagramm. Importieren einer Liste von Dokumenten in 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. Wir zoomen in den Schritten. Importieren der Papiere @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), ) wird eine Tabelle mit Unterfeldern erstellen ( , der , die flow_builder.add_source filename content Wir können uns auf die Für mehr Details. Dokumentation Extrahieren und Sammeln von Metadaten Erste Seite extrahieren für Grundinformationen Definieren Sie eine benutzerdefinierte Funktion, um die erste Seite und die Anzahl der Seiten des PDF zu extrahieren. @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()) Schließen Sie das jetzt in Ihren Fluss ein. Wir extrahieren Metadaten von der ersten Seite, um die Verarbeitungskosten zu minimieren, da die gesamte PDF sehr groß sein kann. with data_scope["documents"].row() as doc: doc["basic_info"] = doc["content"].transform(extract_basic_info) Nach diesem Schritt sollten Sie die grundlegenden Informationen zu jedem Papier haben. Parse Grundinformationen Wir konvertieren die erste Seite in Markdown mithilfe von Marker. Alternativ können Sie Ihren Lieblings-PDF-Parser wie Docling problemlos anschließen. Definieren Sie eine Marker-Konverter-Funktion und cache es, da seine Initialisierung ressourcenintensiv ist. Dadurch wird sichergestellt, dass die gleiche Konverterinstanz für verschiedene Eingabedateien wiederverwendet wird. @cache def get_marker_converter() -> PdfConverter: config_parser = ConfigParser({}) return PdfConverter( create_model_dict(), config=config_parser.generate_config_dict() ) Schalten Sie es in eine custom-Funktion ein. @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 Geben Sie es zu Ihrer Transformation with data_scope["documents"].row() as doc: doc["first_page_md"] = doc["basic_info"]["first_page"].transform( pdf_to_markdown ) Nach diesem Schritt sollten Sie die erste Seite jedes Papiers im Markdown-Format haben. Extrahieren Sie grundlegende Informationen mit LLM CocoIndex unterstützt nativ die LLM-strukturierte Extraktion mit komplexen und eingebetteten Schemata. Wenn Sie daran interessiert sind, mehr über Nested Schemes zu erfahren, wenden Sie sich an . Dieser Artikel @dataclasses.dataclass class PaperMetadata: """ Metadata for a paper. """ title: str authors: list[Author] abstract: str Plug it in die Mit einer definierten Dataklasse analysiert CocoIndex automatisch die LLM-Antwort in die Dataklasse. 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.", ) ) Nach diesem Schritt sollten Sie die Metadaten jedes Papiers haben. Papiermetadaten sammeln 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"], ) Sammle alles, was du brauchst :) Sammeln Zwei Informationen Autorin Filename Autorin Filename Hier wollen wir Autor → Papers in einer separaten Tabelle sammeln, um eine Suchfunktionalität aufzubauen. Nur von der Autorin gesammelt. 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"], ) Berechnen und sammeln von Embeddings Titel doc["title_embedding"] = doc["metadata"]["title"].transform( cocoindex.functions.SentenceTransformerEmbed( model="sentence-transformers/all-MiniLM-L6-v2" ) ) Abstrakte Teilen Sie die Abstrakte in Stücke auf, einbinden Sie jeden Stück und sammeln Sie ihre Einbettungen. Manchmal kann die Abstraktion sehr lang sein. 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, ) Nach diesem Schritt sollten Sie die abstrakten Stücke jedes Papiers haben. Fügen Sie jedes Stück ein und sammeln Sie ihre Einbettungen. with doc["abstract_chunks"].row() as chunk: chunk["embedding"] = chunk["text"].transform( cocoindex.functions.SentenceTransformerEmbed( model="sentence-transformers/all-MiniLM-L6-v2" ) ) Nach diesem Schritt sollten Sie die Einbettungen der abstrakten Stücke jedes Papiers haben. Sammeln von 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"], ) der Export Schließlich exportieren wir die Daten nach 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 diesem Beispiel verwenden wir PGVector als Embedding Stores/ Mit CocoIndex können Sie einen Linienwechsel auf anderen unterstützten Vektor-Datenbanken wie Qdrant durchführen. Für mehr Details. Führer Unser Ziel ist es, Schnittstellen zu standardisieren und es wie das Bauen von LEGO zu machen. CocoInsight Schritt für Schritt ansehen Sie können das Projekt Schritt für Schritt durchlaufen zu sehen CocoInsight Wie jedes Feld konstruiert ist und was hinter den Kulissen passiert. Wünsche zum Index Sie können sich auf diesen Abschnitt der über Text Embeddings Wie man query gegen embeddings aufbaut. Im Moment bietet CocoIndex keine zusätzliche Abfrage-Schnittstelle.Wir können SQL schreiben oder auf die Abfrage-Engine durch den Ziel-Speicher verlassen. Viele Datenbanken haben bereits query-Implementierungen mit ihren eigenen Best Practices optimiert Der Abfrage-Raum verfügt über hervorragende Lösungen für Abfragen, Reranking und andere Suchfunktionen. Wenn Sie Hilfe bei der Erstellung der Abfrage benötigen, fühlen Sie sich bitte frei, uns zu kontaktieren . Diskord Unterstützt uns Wir verbessern uns ständig und bald kommen weitere Features und Beispiele. Wenn Ihnen dieser Artikel hilfreich ist, geben Sie uns bitte einen Stern um uns zu helfen zu wachsen. Github Danke fürs Lesen!