{ "cells": [ { "cell_type": "markdown", "id": "41aecfc5", "metadata": {}, "source": [ "\"Open" ] }, { "cell_type": "markdown", "id": "b07531d9-7473-480d-bee6-c1ee4cbc207c", "metadata": {}, "source": [ "# Automated Metadata Extraction for Better Retrieval + Synthesis\n", "\n", "In this tutorial, we show you how to perform automated metadata extraction for better retrieval results.\n", "We use two extractors: a QuestionAnsweredExtractor which generates question/answer pairs from a piece of text, and also a SummaryExtractor which extracts summaries, not only within the current text, but also within adjacent texts.\n", "\n", "We show that this allows for \"chunk dreaming\" - each individual chunk can have more \"holistic\" details, leading to higher answer quality given retrieved results.\n", "\n", "Our data source is taken from Eugene Yan's popular article on LLM Patterns: https://eugeneyan.com/writing/llm-patterns/" ] }, { "cell_type": "markdown", "id": "9a4873de-eaa9-4854-8aeb-050704bd894f", "metadata": {}, "source": [ "## Setup" ] }, { "cell_type": "markdown", "id": "8068876f", "metadata": {}, "source": [ "If you're opening this Notebook on colab, you will probably need to install LlamaIndex 🦙." ] }, { "cell_type": "code", "execution_count": null, "id": "bf94b39c", "metadata": {}, "outputs": [], "source": [ "%pip install llama-index-llms-openai\n", "%pip install llama-index-readers-web" ] }, { "cell_type": "code", "execution_count": null, "id": "42c5e11d", "metadata": {}, "outputs": [], "source": [ "!pip install llama-index" ] }, { "cell_type": "code", "execution_count": null, "id": "40d399c4-c93c-41bf-9a47-48aefabb75e3", "metadata": {}, "outputs": [], "source": [ "import nest_asyncio\n", "\n", "nest_asyncio.apply()\n", "\n", "import os\n", "import openai" ] }, { "cell_type": "code", "execution_count": null, "id": "4c80a958-e94f-4260-81fc-8791019dae87", "metadata": {}, "outputs": [], "source": [ "# OPTIONAL: setup W&B callback handling for tracing\n", "from llama_index.core import set_global_handler\n", "\n", "set_global_handler(\"wandb\", run_args={\"project\": \"llamaindex\"})" ] }, { "cell_type": "code", "execution_count": null, "id": "89cb51f7-1764-40d1-bd25-d7551aa57207", "metadata": {}, "outputs": [], "source": [ "os.environ[\"OPENAI_API_KEY\"] = \"sk-...\"\n", "openai.api_key = os.environ[\"OPENAI_API_KEY\"]" ] }, { "cell_type": "markdown", "id": "7a6ef3fc-0d04-43a2-b0d6-4d8f3c90ef3d", "metadata": {}, "source": [ "## Define Metadata Extractors\n", "\n", "Here we define metadata extractors. We define two variants:\n", "- metadata_extractor_1 only contains the QuestionsAnsweredExtractor\n", "- metadata_extractor_2 contains both the QuestionsAnsweredExtractor as well as the SummaryExtractor" ] }, { "cell_type": "code", "execution_count": null, "id": "0adb8e4a-6728-4073-8256-8b3be4ab1e64", "metadata": {}, "outputs": [], "source": [ "from llama_index.llms.openai import OpenAI\n", "from llama_index.core.schema import MetadataMode" ] }, { "cell_type": "code", "execution_count": null, "id": "a0231dff-7443-46bf-9b9d-759198d3408e", "metadata": {}, "outputs": [], "source": [ "llm = OpenAI(temperature=0.1, model=\"gpt-3.5-turbo\", max_tokens=512)" ] }, { "cell_type": "markdown", "id": "2db2cf90-f295-4a3d-a47c-4b2b1dd2d7c5", "metadata": {}, "source": [ "We also show how to instantiate the `SummaryExtractor` and `QuestionsAnsweredExtractor`." ] }, { "cell_type": "code", "execution_count": null, "id": "3bda151d-6fb8-427e-82fc-0f3bb469d705", "metadata": {}, "outputs": [], "source": [ "from llama_index.core.node_parser import TokenTextSplitter\n", "from llama_index.core.extractors import (\n", " SummaryExtractor,\n", " QuestionsAnsweredExtractor,\n", ")\n", "\n", "node_parser = TokenTextSplitter(\n", " separator=\" \", chunk_size=256, chunk_overlap=128\n", ")\n", "\n", "\n", "extractors_1 = [\n", " QuestionsAnsweredExtractor(\n", " questions=3, llm=llm, metadata_mode=MetadataMode.EMBED\n", " ),\n", "]\n", "\n", "extractors_2 = [\n", " SummaryExtractor(summaries=[\"prev\", \"self\", \"next\"], llm=llm),\n", " QuestionsAnsweredExtractor(\n", " questions=3, llm=llm, metadata_mode=MetadataMode.EMBED\n", " ),\n", "]" ] }, { "cell_type": "markdown", "id": "e4e54937-e9e7-48ed-8600-72cd2f3c529b", "metadata": {}, "source": [ "## Load in Data, Run Extractors\n", "\n", "We load in Eugene's essay (https://eugeneyan.com/writing/llm-patterns/) using our LlamaHub SimpleWebPageReader.\n", "\n", "We then run our extractors." ] }, { "cell_type": "code", "execution_count": null, "id": "c72c45a9-dcad-4925-b2f7-d25fe5d80c2d", "metadata": {}, "outputs": [], "source": [ "from llama_index.core import SimpleDirectoryReader" ] }, { "cell_type": "code", "execution_count": null, "id": "4044f6bd-be50-4958-8d25-4edd6552f103", "metadata": {}, "outputs": [], "source": [ "# load in blog\n", "\n", "from llama_index.readers.web import SimpleWebPageReader\n", "\n", "reader = SimpleWebPageReader(html_to_text=True)\n", "docs = reader.load_data(urls=[\"https://eugeneyan.com/writing/llm-patterns/\"])" ] }, { "cell_type": "code", "execution_count": null, "id": "f2701c7e-b67e-4c24-98df-73f96e3756a2", "metadata": {}, "outputs": [], "source": [ "print(docs[0].get_content())" ] }, { "cell_type": "code", "execution_count": null, "id": "269f8ecc-489d-435f-9d81-a9c64fd4d400", "metadata": {}, "outputs": [], "source": [ "orig_nodes = node_parser.get_nodes_from_documents(docs)" ] }, { "cell_type": "code", "execution_count": null, "id": "d63b7df2-0e5a-4e98-85ea-88ddcf37c99e", "metadata": {}, "outputs": [], "source": [ "# take just the first 8 nodes for testing\n", "nodes = orig_nodes[20:28]" ] }, { "cell_type": "code", "execution_count": null, "id": "eb2a96d9-03fe-4d60-9829-4fa80f7ff571", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "is to measure the distance that words would\n", "have to move to convert one sequence to another.\n", "\n", "However, there are several pitfalls to using these conventional benchmarks and\n", "metrics.\n", "\n", "First, there’s **poor correlation between these metrics and human judgments.**\n", "BLEU, ROUGE, and others have had [negative correlation with how humans\n", "evaluate fluency](https://arxiv.org/abs/2008.12009). They also showed moderate\n", "to less correlation with human adequacy scores. In particular, BLEU and ROUGE\n", "have [low correlation with tasks that require creativity and\n", "diversity](https://arxiv.org/abs/2303.16634).\n", "\n", "Second, these metrics often have **poor adaptability to a wider variety of\n", "tasks**. Adopting a metric proposed for one task to another is not always\n", "prudent. For example, exact match metrics such as BLEU and ROUGE are a poor\n", "fit for tasks like abstractive summarization or dialogue. Since they’re based\n", "on n-gram overlap between output and reference, they don’t make sense for a\n", "dialogue task where a wide variety\n" ] } ], "source": [ "print(nodes[3].get_content(metadata_mode=\"all\"))" ] }, { "cell_type": "markdown", "id": "970f4dd0-d5e2-4bef-abc1-494020a9a2b5", "metadata": {}, "source": [ "### Run metadata extractors" ] }, { "cell_type": "code", "execution_count": null, "id": "17fd3537-c8e0-4160-b39d-815dbb6504c9", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "e828522a65bb4304bdaeae041a2eee31", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Parsing documents into nodes: 0%| | 0/8 [00:00