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. Agradeceríamos mucho si pudieras Si te resulta útil este tutorial. ⭐ star CocoIndex en GitHub CocoIndex en GitHub Uso de casos Busca y recuperación académica, así como agentes de IA basados en la investigación Sistemas de recomendación de papel Gráficos de conocimiento de investigación Análisis semántico de la literatura científica Lo que lograremos Vamos a echar un vistazo a esto como un ejemplo. El PDF Aquí está lo que queremos lograr: 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. Esto permite mejores resultados de búsqueda semántica basados en metadatos. Por ejemplo, puede combinar consultas de texto con títulos y abstracciones. 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 Puedes encontrar el código completo . Aquí Si este artículo es útil para usted, por favor déjenos una estrella ⭐ en Para ayudarnos a crecer. GitHub Componentes básicos 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? Precondiciones . Install PostgreSQL CocoIndex uses PostgreSQL internally for incremental processing. . Configure your OpenAI API key Alternativamente, tenemos soporte nativo para Gemini, Ollama, LiteLLM, checkout el . Guía Usted podría elegir su proveedor de LLM favorito y puede trabajar completamente en el lugar. Definición del flujo de indexación Este proyecto demuestra un ejemplo ligeramente más completo de comprensión de metadatos más cerca de los casos de uso del mundo real. Verá lo fácil que es lograr este diseño de CocoIndex dentro de 100 líneas de lógica de indexación. . Código Para ayudarle a navegar mejor por lo que vamos a caminar, aquí está un diagrama de flujo. Importar una lista de documentos en 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. Vamos a zoom en los pasos. Importar los documentos @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), ) se creará una tabla con subcampos ( , de , el flow_builder.add_source filename content Podemos referirnos a la Para más detalles. Documentación Extraer y recoger metadatos Extraer la primera página para información básica Define una función personalizada para extraer la primera página y el número de páginas 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()) Introduce esto en tu flujo. Extraemos metadatos de la primera página para minimizar el coste de procesamiento, ya que todo el PDF puede ser muy grande. with data_scope["documents"].row() as doc: doc["basic_info"] = doc["content"].transform(extract_basic_info) Después de este paso, usted debe tener la información básica de cada papel. Página de información básica Convertiremos la primera página a Markdown usando Marker. Alternativamente, puede conectar fácilmente su analizador de PDF favorito, como Docling. Define una función de conversión de marcador y la cache, ya que su inicialización es intensiva en recursos. Esto asegura que la misma instancia de conversor sea reutilizada para diferentes archivos de entrada. @cache def get_marker_converter() -> PdfConverter: config_parser = ConfigParser({}) return PdfConverter( create_model_dict(), config=config_parser.generate_config_dict() ) Introducirla en una función 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 Envíalo a tu transformación with data_scope["documents"].row() as doc: doc["first_page_md"] = doc["basic_info"]["first_page"].transform( pdf_to_markdown ) Después de este paso, debe tener la primera página de cada papel en formato Markdown. Extraer información básica con LLM Define un esquema para la extracción de LLM. CocoIndex apoya nativamente la extracción estructurada de LLM con esquemas complejos y anclados. Si usted está interesado en aprender más sobre los esquemas de nicho, consulte . Este artículo @dataclasses.dataclass class PaperMetadata: """ Metadata for a paper. """ title: str authors: list[Author] abstract: str Póngalo en la Con una clase de datos definida, CocoIndex analizará automáticamente la respuesta LLM en la clase de datos. 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.", ) ) Después de este paso, debe tener los metadatos de cada papel. Recopilación de metadatos en papel 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"], ) Recoge todo lo que necesites :) Colección Dos Información autoría filename autoría filename Aquí queremos recopilar Autor → Papeles en una tabla separada para construir una funcionalidad de búsqueda. Simplemente recogido por el autor. 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"], ) Calcular y recoger embeddings Título doc["title_embedding"] = doc["metadata"]["title"].transform( cocoindex.functions.SentenceTransformerEmbed( model="sentence-transformers/all-MiniLM-L6-v2" ) ) Abstracción Divide el abstracto en pedazos, incrusta cada pedazo y recoge sus incrustaciones. A veces el abstracto puede ser muy largo. 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, ) Después de este paso, debe tener los pedazos abstractos de cada papel. Incorporar cada pieza y recoger sus embotellamientos. with doc["abstract_chunks"].row() as chunk: chunk["embedding"] = chunk["text"].transform( cocoindex.functions.SentenceTransformerEmbed( model="sentence-transformers/all-MiniLM-L6-v2" ) ) Después de este paso, debe tener las incorporaciones de los pedazos abstractos de cada papel. Colección 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"], ) Exportaciones Por último, exportamos los datos 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, ) ], ) En este ejemplo, utilizamos PGVector como embedding stores/ Con CocoIndex, puede hacer un interruptor de línea en otras bases de datos Vector compatibles como Qdrant, véase esto Para más detalles. Guía Nuestro objetivo es estandarizar las interfaces y hacer que sea como construir LEGO. Ver en CocoInsight paso a paso Usted puede caminar por el proyecto paso a paso en para ver CocoInsight exactamente cómo se construye cada campo y lo que sucede detrás de las escenas. Queremos el índice Puedes consultar esta sección de Sobre Enlaces de texto Cómo crear una consulta contra embeddings. Por el momento CocoIndex no proporciona interfaz de consulta adicional.Podemos escribir SQL o confiar en el motor de consulta por el almacenamiento objetivo. Muchas bases de datos ya han optimizado las implementaciones de consultas con sus propias mejores prácticas El espacio de consulta tiene excelentes soluciones para la consulta, el reencuentro y otras funcionalidades relacionadas con la búsqueda. Si necesita ayuda con la redacción de la consulta, siéntese libre de contactar con nosotros en . Discordia Apoyanos Estamos constantemente mejorando, y más características y ejemplos vienen pronto. Si este artículo es útil para usted, por favor déjenos una estrella ⭐ en Para ayudarnos a crecer. GitHub ¡Gracias por leer!