В този блог ще преминем през цялостен пример за индексиране на изследователски статии с извличане на различни метаданни - отвъд пълното изрязване и вграждане на текст - и ще изградим семантични вграждания за индексиране и заявки. Ще ви бъдем благодарни, ако можете Ако смятате, че това урок е полезно. ⭐ star CocoIndex на GitHub CocoIndex на GitHub Използвайте случаи Академично търсене и извличане, както и агенти на ИИ, базирани на изследвания Системи за препоръки на хартия График на научните изследвания Семантичен анализ на научната литература Какво ще постигнем Нека да разгледаме това Като пример . 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. Нека да направим zoom в стъпките. Импортиране на документи @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 Определете схема за извличане на 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_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 можете да превключвате една линия в други поддържани векторни бази данни като Qdrant, вижте това За повече подробности. Ръководство Целта ни е да стандартизираме интерфейсите и да го направим като изграждане на LEGO. Преглед в CocoInsight стъпка по стъпка Можете да преминете през проекта стъпка по стъпка в Виж още Кокосовото Точно как е построено всяко поле и какво се случва зад сцените. Искам индекс Можете да разгледате този раздел на за Текстови вмъквания Как да изградите заявка срещу вграждания. Засега CocoIndex не предоставя допълнителен интерфейс за заявки.Можем да напишем SQL или да разчитаме на двигателя за заявки от целевото хранилище. Много бази данни вече са оптимизирали имплементациите на заявки със собствените си най-добри практики Пространството за заявки има отлични решения за заявки, прекласиране и други функционалности, свързани с търсенето. Ако имате нужда от помощ при писането на заявката, моля, не се колебайте да се свържете с нас на . Несъгласие Подкрепете ни Ние непрекъснато се подобряваме, а още функции и примери скоро ще дойдат. Ако тази статия е полезна за вас, моля, дайте ни звезда За да ни помогне да израснем. GitHub Благодаря за четенето!