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. Будемо дуже вдячні, якщо ви зможете Якщо вам допоможе цей туториал. ⭐ star CocoIndex on GitHub CocoIndex на GitHub Використовуйте випадки Академічний пошук та пошук, а також агенти AI на основі досліджень Системи паперових рекомендацій Графік досліджень знань Семантичний аналіз наукової літератури Що ми досягнемо Давайте подивимося на це Як приклад PDF Ось що ми хочемо досягти: 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. Це дозволяє отримати кращі результати семантичного пошуку, орієнтованих на метадані.Наприклад, ви можете порівнювати текстові запити з заголовками та абстрактами. 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 Ви можете знайти повний код . Тут Якщо ця стаття буде корисною для вас, будь ласка, дайте нам зірку Щоб допомогти нам рости. GitHub Основні компоненти 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? передумови . Install PostgreSQL CocoIndex uses PostgreSQL internally for incremental processing. . Configure your OpenAI API key Альтернативно, ми маємо рідну підтримку для Gemini, Ollama, LiteLLM, . Керівництво Ви можете вибрати свого улюбленого постачальника LLM і можете працювати повністю на місці. Визначення індексування потоку Цей проект демонструє дещо більш всеосяжний приклад розуміння метаданих ближче до реальних випадків використання. Ви побачите, наскільки легко досягти цього дизайну CocoIndex в межах 100 рядків логіки індексування - . Код Щоб краще допомогти вам пересуватися через те, що ми будемо проходити, ось діаграма потоку. Імпортуйте список документів у форматі 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. Давайте зуміємо на кроках. Ввезення документів @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), ) буде створено таблицю з підполями ( , , що flow_builder.add_source filename content Ми можемо посилатися на Для більше деталей. Документація Використання та збір метаданих Витяг першої сторінки для базової інформації Визначте функцію налаштування для вилучення першої сторінки та кількості сторінок 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()) Тепер включіть це в свій потік. Ми витягуємо метадані з першої сторінки, щоб мінімізувати витрати на обробку, оскільки весь PDF може бути дуже великим. with data_scope["documents"].row() as doc: doc["basic_info"] = doc["content"].transform(extract_basic_info) Після цього кроку ви повинні мати основну інформацію про кожну статтю. Завантажити основну інформацію Ми перетворимо першу сторінку в Markdown за допомогою маркера. Альтернативно, ви можете легко підключити свій улюблений PDF-парасер, наприклад Docling. Визначте функцію конвертера маркера і запам'ятайте її, оскільки її ініціалізація є ресурсною. Це гарантує, що одна і та ж інстанція конвертера повторно використовується для різних вхідних файлів. @cache def get_marker_converter() -> PdfConverter: config_parser = ConfigParser({}) return PdfConverter( create_model_dict(), config=config_parser.generate_config_dict() ) Використовуйте його в звичній функції. @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 Передайте його вашій трансформації with data_scope["documents"].row() as doc: doc["first_page_md"] = doc["basic_info"]["first_page"].transform( pdf_to_markdown ) Після цього кроку, ви повинні мати першу сторінку кожного паперу в форматі Markdown. Витягти основну інформацію з LLM CocoIndex природним чином підтримує структуровану екстракцію LLM з складними і втіленими схемами. Якщо ви зацікавлені в тому, щоб дізнатися більше про гніздних схемах, зверніться до . Ця стаття @dataclasses.dataclass class PaperMetadata: """ Metadata for a paper. """ title: str authors: list[Author] abstract: str Підключіть його до При визначенні класу даних, CocoIndex автоматично аналізує відповідь LLM до класу даних. 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.", ) ) Після цього кроку, ви повинні мати метадані кожного паперу. Збір паперових метаданих 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"], ) Знайдіть все, що вам потрібно :) Колекція два Інформація author Фільми Автор Фільми Тут ми хочемо зібрати Author → Papers в окремій таблиці, щоб побудувати функціональність пошуку. Збирається лише автором. 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"], ) Розраховувати та збирати вбудовані Титул doc["title_embedding"] = doc["metadata"]["title"].transform( cocoindex.functions.SentenceTransformerEmbed( model="sentence-transformers/all-MiniLM-L6-v2" ) ) абстрактні Розділити абстракт на шматочки, вставити кожну шматочку і зібрати їх вставлення. Іноді абстракт може бути дуже довгим. 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, ) Після цього кроку ви повинні мати абстрактні шматочки кожного паперу. Покладіть кожен шматочок і зберіть їх вбудовані. with doc["abstract_chunks"].row() as chunk: chunk["embedding"] = chunk["text"].transform( cocoindex.functions.SentenceTransformerEmbed( model="sentence-transformers/all-MiniLM-L6-v2" ) ) Після цього кроку ви повинні мати вбудовані абстрактні шматочки кожного паперу. Збирати вбудовані 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"], ) експорт Нарешті, ми експортуємо дані в 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, ) ], ) У цьому прикладі ми використовуємо PGVector як вбудовані магазини/ За допомогою CocoIndex ви можете перемикати одну лінію на інших підтримуваних базах даних Vector, таких як Qdrant. Для більше деталей. Керівництво Ми прагнемо стандартизувати інтерфейси і зробити це схожим на будівництво LEGO. Перегляд в CocoInsight крок за кроком Ви можете пройти через проект крок за кроком в Побачити Кокосові Точно так, як побудовано кожне поле і що відбувається за сценами. Знайдіть індекс Ви можете ознайомитися з цим розділом Про Вбудовані тексти Як створити запит проти вбудованих. На даний момент CocoIndex не надає додаткового інтерфейсу запитів. Ми можемо писати SQL або покладатися на двигун запитів цільовим сховищем. Багато баз даних вже оптимізували впровадження запитів своїми власними найкращими практиками. Простір запитів має відмінні рішення для запитів, перекладинки та інших функцій, пов'язаних з пошуком. Якщо вам потрібна допомога з написанням запиту, будь ласка, будь ласка, будь ласка, зв'яжіться з нами на . Розбіжності Підтримай нас Ми постійно вдосконалюємося, і незабаром з'являться більше функцій та прикладів. Якщо ця стаття буде корисною для вас, будь ласка, дайте нам зірку Щоб допомогти нам рости. GitHub Дякую за читання!