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. Vi skulle uppskatta det om du kunde Om du tycker att denna tutorial är till hjälp. ⭐ star CocoIndex på GitHub CocoIndex på GitHub Använd fall Akademisk sökning och sökning samt forskningsbaserade AI-agenter Papper rekommendationssystem Forskning kunskap grafer Semantisk analys av vetenskaplig litteratur Vad vi kommer att uppnå Låt oss ta en titt på detta Som ett exempel. pdf är Här är vad vi vill uppnå: 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. Detta möjliggör bättre metadata-drivna semantiska sökresultat. Till exempel kan du matcha textfrågor mot titlar och abstrakter. 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 Här hittar du hela koden . här Om den här artikeln är användbar för dig, ge oss en stjärna ⭐ på För att hjälpa oss att växa. GitHub Kärnkomponenter 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? Förutsättningar . Install PostgreSQL CocoIndex uses PostgreSQL internally for incremental processing. . Configure your OpenAI API key Alternativt har vi inhemskt stöd för Gemini, Ollama, LiteLLM, checkout the . Guiden Du kan välja din favorit LLM-leverantör och kan arbeta helt på plats. Definiera indexering flöde Detta projekt visar ett något mer omfattande exempel på metadata förståelse närmare verkliga användningsfall. Du kommer att se hur lätt det är att uppnå denna design av CocoIndex inom 100 rader av indexering logik - . Koden är För att bättre hjälpa dig att navigera vad vi ska gå igenom, här är ett flödesdiagram. Importera en lista med papper i 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. Låt oss zooma in i stegen. Importera papper @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), ) kommer att skapa en tabell med underfält ( och och , flow_builder.add_source filename content Vi kan hänvisa till den För mer detaljer. Dokumentation Extrahera och samla in metadata Extrahera första sidan för grundläggande information Definiera en anpassad funktion för att extrahera den första sidan och antalet sidor i 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()) Sätt in detta i ditt flöde. Vi extraherar metadata från den första sidan för att minimera bearbetningskostnaderna, eftersom hela PDF-filen kan vara mycket stor. with data_scope["documents"].row() as doc: doc["basic_info"] = doc["content"].transform(extract_basic_info) Efter detta steg bör du ha grundläggande information om varje papper. Parse grundläggande info Vi kommer att konvertera den första sidan till Markdown med hjälp av Marker. Alternativt kan du enkelt ansluta din favorit PDF-analysator, till exempel Docling. Definiera en markörkonverteringsfunktion och cache den, eftersom dess initialisering är resursintensiv. Detta säkerställer att samma konverteringsinstans återanvänds för olika inmatningsfiler. @cache def get_marker_converter() -> PdfConverter: config_parser = ConfigParser({}) return PdfConverter( create_model_dict(), config=config_parser.generate_config_dict() ) Anslut den till en custom-funktion. @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 Skicka det till din transformation with data_scope["documents"].row() as doc: doc["first_page_md"] = doc["basic_info"]["first_page"].transform( pdf_to_markdown ) Efter detta steg bör du ha den första sidan av varje papper i Markdown-format. Extrahera grundläggande information med LLM Definiera ett system för LLM-utvinning. CocoIndex stöder LLM-strukturerad utvinning med komplexa och förankrade system. Om du är intresserad av att lära dig mer om förankrade system, hänvisa till . this article @dataclasses.dataclass class PaperMetadata: """ Metadata for a paper. """ title: str authors: list[Author] abstract: str Plugga in den i Med en dataklass definierad kommer CocoIndex automatiskt att analysera LLM-svaret i dataklass. 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.", ) ) Efter detta steg bör du ha metadata för varje papper. Samla pappersmetadata 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"], ) Hämta allt du behöver :) samla Två informationen author filenamn Författare filenamn Här vill vi samla Author → Papers i en separat tabell för att bygga en sökfunktionalitet. Samlas helt enkelt av författaren. 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"], ) Beräkna och samla in inbäddningar Titeln doc["title_embedding"] = doc["metadata"]["title"].transform( cocoindex.functions.SentenceTransformerEmbed( model="sentence-transformers/all-MiniLM-L6-v2" ) ) Abstrakt Dela abstrakt i bitar, bädda in varje bit och samla in deras inbäddningar. Ibland kan abstrakten vara väldigt lång. 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, ) Efter detta steg bör du ha de abstrakta bitarna av varje papper. Sätt in varje bit och samla in sina inbäddningar. with doc["abstract_chunks"].row() as chunk: chunk["embedding"] = chunk["text"].transform( cocoindex.functions.SentenceTransformerEmbed( model="sentence-transformers/all-MiniLM-L6-v2" ) ) Efter detta steg bör du ha inbäddningarna av de abstrakta bitarna av varje papper. Samla in 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"], ) Exporten Slutligen exporterar vi data till 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, ) ], ) I det här exemplet använder vi PGVector som inbäddade butiker/ Med CocoIndex kan du göra en linjeskift på andra stödda Vector-databaser som Qdrant, se detta for more details. Guiden Vi strävar efter att standardisera gränssnitt och göra det som att bygga LEGO. Visa i CocoInsight steg för steg Du kan gå igenom projektet steg för steg i att se CocoInsight exakt hur varje fält är konstruerat och vad som händer bakom kulisserna. Sök efter index Du kan hänvisa till detta avsnitt av Om Om Inbäddad text Hur man bygger en fråga mot inbäddningar. För tillfället CocoIndex inte tillhandahålla ytterligare fråge gränssnitt. vi kan skriva SQL eller förlita sig på fråge motor av målet lagring. Många databaser har redan optimerat frågeimplementeringar med sina egna bästa metoder Frågeutrymmet har utmärkta lösningar för frågor, omrankning och andra sökrelaterade funktioner. Om du behöver hjälp med att skriva frågan, var vänlig känna dig fri att kontakta oss på . Diskord Stöd oss Vi förbättrar ständigt, och fler funktioner och exempel kommer snart. Om den här artikeln är användbar för dig, ge oss en stjärna ⭐ på För att hjälpa oss att växa. GitHub Tack för läsningen!