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. Lo apprezzeremmo molto se poteste Se trovi utile questo tutorial. ⭐ star CocoIndex su GitHub CocoIndex su GitHub Usare i casi Ricerca e recupero accademici, nonché agenti AI basati sulla ricerca Sistemi di raccomandazione cartacea Grafici della conoscenza di ricerca Analisi semantica della letteratura scientifica Cosa raggiungeremo Diamo un'occhiata a questo Come un esempio. Il PDF Ecco cosa vogliamo raggiungere: 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. Questo consente risultati di ricerca semantici migliori basati su metadati. ad esempio, è possibile corrispondere le query di testo a titoli e astratti. 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 Puoi trovare il codice completo . qui Se questo articolo vi è stato utile, vi preghiamo di darci una stella per aiutarci a crescere. di Github Componenti principali 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? Prerequisiti . Install PostgreSQL CocoIndex uses PostgreSQL internally for incremental processing. . Configure your OpenAI API key In alternativa, abbiamo il supporto nativo per Gemini, Ollama, LiteLLM, il check-out . Guida Puoi scegliere il tuo fornitore LLM preferito e puoi lavorare completamente in loco. Definire il flusso di indexazione Questo progetto dimostra un esempio leggermente più completo di comprensione dei metadati più vicino ai casi di utilizzo del mondo reale. Vedrai quanto sia facile raggiungere questo design da CocoIndex entro 100 righe di logica di indicizzazione - . codice Per aiutarti a navigare meglio attraverso ciò che passeremo, ecco un diagramma di flusso. Importare un elenco di documenti 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. Lo zoom nei passi. Importare i documenti @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), ) creerà una tabella con sottocampi ( di , il flow_builder.add_source filename content Possiamo fare riferimento alla for more details. Documentazione Estrazione e raccolta dei metadati Estratto prima pagina per informazioni di base Definisci una funzione personalizzata per estrarre la prima pagina e il numero di pagine del 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()) Aggiungi questo al tuo flusso. Estrattiamo i metadati dalla prima pagina per ridurre al minimo i costi di elaborazione, poiché l'intero PDF può essere molto grande. with data_scope["documents"].row() as doc: doc["basic_info"] = doc["content"].transform(extract_basic_info) Dopo questo passaggio, dovresti avere le informazioni di base di ciascuna carta. Scopri le informazioni di base Convertiamo la prima pagina in Markdown utilizzando Marker. In alternativa, puoi facilmente collegare il tuo analizzatore PDF preferito, come Docling. Definisci una funzione di convertitore di marcatore e la cache, poiché la sua inizializzazione è risorsa-intensiva. Questo assicura che la stessa istanza del convertitore sia riutilizzata per diversi file di input. @cache def get_marker_converter() -> PdfConverter: config_parser = ConfigParser({}) return PdfConverter( create_model_dict(), config=config_parser.generate_config_dict() ) Inserisci una funzione 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 Trasmetterlo alla tua trasformazione with data_scope["documents"].row() as doc: doc["first_page_md"] = doc["basic_info"]["first_page"].transform( pdf_to_markdown ) Dopo questo passaggio, dovresti avere la prima pagina di ogni foglio in formato Markdown. Estratto informazioni di base con LLM Definisci uno schema per l'estrazione LLM. CocoIndex supporta in modo nativo l'estrazione strutturata LLM con schemi complessi e nested. Se siete interessati a saperne di più sugli schemi a nido, rivolgersi a . Questo articolo @dataclasses.dataclass class PaperMetadata: """ Metadata for a paper. """ title: str authors: list[Author] abstract: str Inserisci questo nel Con una data class definita, CocoIndex analizzerà automaticamente la risposta LLM nella data class. 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.", ) ) Dopo questo passaggio, dovresti avere i metadati di ciascuna carta. Raccogliere metadati cartacei 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"], ) Raccogli tutto ciò di cui hai bisogno :) Collezione due Informazioni Autore filename Autore filename Qui vogliamo raccogliere Autore → Documenti in una tabella separata per costruire una funzionalità di ricerca. raccolta da parte dell’autore. 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"], ) Calcolare e raccogliere embeddings Titolo doc["title_embedding"] = doc["metadata"]["title"].transform( cocoindex.functions.SentenceTransformerEmbed( model="sentence-transformers/all-MiniLM-L6-v2" ) ) astratto Dividere gli astratti in pezzi, incollare ogni pezzo e raccogliere i loro incollamenti. A volte l’abstract può essere molto lungo. 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, ) Dopo questo passaggio, dovresti avere i pezzi astratti di ciascuna carta. Inserisci ogni pezzo e raccogli i loro inserti. with doc["abstract_chunks"].row() as chunk: chunk["embedding"] = chunk["text"].transform( cocoindex.functions.SentenceTransformerEmbed( model="sentence-transformers/all-MiniLM-L6-v2" ) ) Dopo questo passaggio, dovresti avere le incollature dei pezzi astratti di ciascuna carta. Raccolta di 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"], ) esportazione Infine, esportiamo i dati a 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 questo esempio utilizziamo PGVector come negozi di incorporazione/ Con CocoIndex, è possibile effettuare uno switch di linea su altri database Vector supportati come Qdrant, vedere questo Per maggiori dettagli. Guida Il nostro obiettivo è standardizzare le interfacce e renderle come costruire LEGO. Visualizzazione in CocoInsight passo dopo passo Puoi percorrere il progetto passo dopo passo in Vediamo CocoInsight Esattamente come ogni campo è costruito e cosa succede dietro le quinte. Scopri l'indice Puoi consultare questa sezione di circa Inserimento di testo Come creare una query contro gli embeddings. Per ora CocoIndex non fornisce interfaccia di query aggiuntiva. Possiamo scrivere SQL o fare affidamento sul motore di query da parte dello storage di destinazione. Molte banche dati hanno già ottimizzato le implementazioni di query con le proprie migliori pratiche Lo spazio di query ha ottime soluzioni per la querying, il re-ranking e altre funzionalità relative alla ricerca. Se avete bisogno di assistenza con la scrittura della query, siate liberi di contattarci a . Discordia Sostienici Stiamo migliorando costantemente, e presto arriveranno altre funzionalità e esempi. Se questo articolo vi è stato utile, vi preghiamo di darci una stella per aiutarci a crescere. di Github Grazie per la lettura!