LlamaIndex 中结构化数据提取的示例
如果您尚未阅读我们的结构化数据提取教程,我们建议从那里开始。本笔记本演示了教程中介绍的一些技术。
我们从LLM的基本语法开始,然后继续学习如何将其与更高级的模块(如查询引擎和智能体)结合使用。
许多关于结构化输出的底层行为是由我们的Pydantic程序模块驱动的。查看我们的深度结构化输出指南获取更多详细信息。
import nest_asyncio
nest_asyncio.apply()from llama_index.llms.openai import OpenAIfrom llama_index.embeddings.openai import OpenAIEmbeddingfrom llama_index.core import Settings
llm = OpenAI(model="gpt-4o")embed_model = OpenAIEmbedding(model="text-embedding-3-small")Settings.llm = llmSettings.embed_model = embed_model您可以通过as_structured_llm将任何LLM转换为“结构化LLM”。
这里我们传入一个简单的 Album 类,它包含一个歌曲列表。然后我们可以使用普通的LLM端点,如聊天/完成。
注意: 支持异步操作,但流式传输功能即将推出。
from typing import Listfrom pydantic import BaseModel, Field
class Song(BaseModel): """Data model for a song."""
title: str length_seconds: int
class Album(BaseModel): """Data model for an album."""
name: str artist: str songs: List[Song]from llama_index.core.llms import ChatMessage
sllm = llm.as_structured_llm(output_cls=Album)input_msg = ChatMessage.from_str("Generate an example album from The Shining")output = sllm.chat([input_msg])# get actual objectoutput_obj = output.rawprint(str(output))print(output_obj)assistant: {"name": "The Shining: Original Soundtrack", "artist": "Various Artists", "songs": [{"title": "Main Title", "length_seconds": 180}, {"title": "Rocky Mountains", "length_seconds": 210}, {"title": "Lontano", "length_seconds": 720}, {"title": "Music for Strings, Percussion and Celesta", "length_seconds": 540}, {"title": "Utrenja (Excerpt)", "length_seconds": 300}, {"title": "The Awakening of Jacob", "length_seconds": 480}, {"title": "De Natura Sonoris No. 2", "length_seconds": 540}, {"title": "Home", "length_seconds": 180}, {"title": "Midnight, the Stars and You", "length_seconds": 180}, {"title": "It's All Forgotten Now", "length_seconds": 150}, {"title": "Masquerade", "length_seconds": 180}]}name='The Shining: Original Soundtrack' artist='Various Artists' songs=[Song(title='Main Title', length_seconds=180), Song(title='Rocky Mountains', length_seconds=210), Song(title='Lontano', length_seconds=720), Song(title='Music for Strings, Percussion and Celesta', length_seconds=540), Song(title='Utrenja (Excerpt)', length_seconds=300), Song(title='The Awakening of Jacob', length_seconds=480), Song(title='De Natura Sonoris No. 2', length_seconds=540), Song(title='Home', length_seconds=180), Song(title='Midnight, the Stars and You', length_seconds=180), Song(title="It's All Forgotten Now", length_seconds=150), Song(title='Masquerade', length_seconds=180)]output = await sllm.achat([input_msg])# get actual objectoutput_obj = output.rawprint(str(output))assistant: {"name": "The Shining: Original Soundtrack", "artist": "Various Artists", "songs": [{"title": "Main Title (The Shining)", "length_seconds": 180}, {"title": "Rocky Mountains", "length_seconds": 210}, {"title": "Lontano", "length_seconds": 240}, {"title": "Music for Strings, Percussion and Celesta", "length_seconds": 300}, {"title": "Utrenja (Excerpt)", "length_seconds": 180}, {"title": "The Awakening of Jacob", "length_seconds": 150}, {"title": "De Natura Sonoris No. 2", "length_seconds": 270}, {"title": "Home", "length_seconds": 200}, {"title": "Heartbeats and Worry", "length_seconds": 160}, {"title": "The Overlook", "length_seconds": 220}]}from IPython.display import clear_outputfrom pprint import pprint
stream_output = sllm.stream_chat([input_msg])for partial_output in stream_output: clear_output(wait=True) pprint(partial_output.raw.dict())
output_obj = partial_output.rawprint(str(output)){'artist': 'Various Artists', 'name': 'The Shining: Original Soundtrack', 'songs': [{'length_seconds': 180, 'title': 'Main Title'}, {'length_seconds': 210, 'title': 'Rocky Mountains'}, {'length_seconds': 240, 'title': 'Lontano'}, {'length_seconds': 540, 'title': 'Music for Strings, Percussion and Celesta'}, {'length_seconds': 300, 'title': 'Utrenja (Excerpt)'}, {'length_seconds': 360, 'title': 'The Awakening of Jacob'}, {'length_seconds': 420, 'title': 'De Natura Sonoris No. 2'}, {'length_seconds': 180, 'title': 'Home'}, {'length_seconds': 180, 'title': 'Midnight, the Stars and You'}, {'length_seconds': 150, 'title': "It's All Forgotten Now"}, {'length_seconds': 120, 'title': 'Masquerade'}]}assistant: {"name": "The Shining: Original Soundtrack", "artist": "Various Artists", "songs": [{"title": "Main Title (The Shining)", "length_seconds": 180}, {"title": "Rocky Mountains", "length_seconds": 210}, {"title": "Lontano", "length_seconds": 240}, {"title": "Music for Strings, Percussion and Celesta", "length_seconds": 300}, {"title": "Utrenja (Excerpt)", "length_seconds": 180}, {"title": "The Awakening of Jacob", "length_seconds": 150}, {"title": "De Natura Sonoris No. 2", "length_seconds": 270}, {"title": "Home", "length_seconds": 200}, {"title": "Heartbeats and Worry", "length_seconds": 160}, {"title": "The Overlook", "length_seconds": 220}]}from IPython.display import clear_outputfrom pprint import pprint
stream_output = await sllm.astream_chat([input_msg])async for partial_output in stream_output: clear_output(wait=True) pprint(partial_output.raw.dict()){'artist': 'Various Artists', 'name': 'The Shining: Original Soundtrack', 'songs': [{'length_seconds': 180, 'title': 'Main Title'}, {'length_seconds': 210, 'title': 'Rocky Mountains'}, {'length_seconds': 720, 'title': 'Lontano'}, {'length_seconds': 540, 'title': 'Music for Strings, Percussion and Celesta'}, {'length_seconds': 300, 'title': 'Utrenja (Excerpt)'}, {'length_seconds': 480, 'title': 'The Awakening of Jacob'}, {'length_seconds': 540, 'title': 'De Natura Sonoris No. 2'}, {'length_seconds': 180, 'title': 'Home'}, {'length_seconds': 180, 'title': 'Midnight, the Stars and You'}, {'length_seconds': 180, 'title': "It's All Forgotten Now"}, {'length_seconds': 180, 'title': 'Masquerade'}]}1.b 使用 structured_predict 函数
Section titled “1.b Use the structured_predict Function”无需显式执行 llm.as_structured_llm(...),每个LLM类都包含一个 structured_predict 函数,让您能够更便捷地通过提示模板+模板变量调用LLM,只需一行代码即可返回结构化输出。
# use query pipelinesfrom llama_index.core.prompts import ChatPromptTemplatefrom llama_index.core.llms import ChatMessagefrom llama_index.llms.openai import OpenAI
chat_prompt_tmpl = ChatPromptTemplate( message_templates=[ ChatMessage.from_str( "Generate an example album from {movie_name}", role="user" ) ])
llm = OpenAI(model="gpt-4o")album = llm.structured_predict( Album, chat_prompt_tmpl, movie_name="Lord of the Rings")albumAlbum(name='Songs of Middle-earth', artist='Various Artists', songs=[Song(title='The Shire', length_seconds=180), Song(title='The Fellowship', length_seconds=240), Song(title="Gollum's Theme", length_seconds=200), Song(title="Rohan's Call", length_seconds=220), Song(title="The Battle of Helm's Deep", length_seconds=300), Song(title='Lothlórien', length_seconds=210), Song(title='The Return of the King', length_seconds=250), Song(title='Into the West', length_seconds=260)])你也可以将其集成到RAG流程中。下面我们展示了从苹果公司10-K报告中提取结构化数据的示例。
!mkdir data!wget "https://s2.q4cdn.com/470004039/files/doc_financials/2021/q4/_10-K-2021-(As-Filed).pdf" -O data/apple_2021_10k.pdf选项1:使用LlamaParse
Section titled “Option 1: Use LlamaParse”您需要一个 https://cloud.llamaindex.ai/ 账户和一个API密钥才能使用LlamaParse,这是我们用于10K文件处理的文档解析器。
from llama_parse import LlamaParse
# os.environ["LLAMA_CLOUD_API_KEY"] = "llx-..."orig_docs = LlamaParse(result_type="text").load_data( "./data/apple_2021_10k.pdf")Started parsing the file under job_id cac11eca-7e00-452f-93f6-19c861b4c130from copy import deepcopyfrom llama_index.core.schema import TextNode
def get_page_nodes(docs, separator="\n---\n"): """Split each document into page node, by separator.""" nodes = [] for doc in docs: doc_chunks = doc.text.split(separator) for doc_chunk in doc_chunks: node = TextNode( text=doc_chunk, metadata=deepcopy(doc.metadata), ) nodes.append(node)
return nodes
docs = get_page_nodes(orig_docs)print(docs[0].get_content()) UNITED STATES SECURITIES AND EXCHANGE COMMISSION Washington, D.C. 20549
FORM 10-K(Mark One) ☒ ANNUAL REPORT PURSUANT TO SECTION 13 OR 15(d) OF THE SECURITIES EXCHANGE ACT OF 1934 For the fiscal year ended September 25, 2021 or ☐ TRANSITION REPORT PURSUANT TO SECTION 13 OR 15(d) OF THE SECURITIES EXCHANGE ACT OF 1934 For the transition period from to . Commission File Number: 001-36743
Apple Inc. (Exact name of Registrant as specified in its charter)
California 94-2404110 (State or other jurisdiction (I.R.S. Employer Identification No.) of incorporation or organization)
One Apple Park Way Cupertino, California 95014 (Address of principal executive offices) (Zip Code) (408) 996-1010 (Registrant’s telephone number, including area code)
Securities registered pursuant to Section 12(b) of the Act:
Trading Title of each class symbol(s) Name of each exchange on which registered Common Stock, $0.00001 par value per share AAPL The Nasdaq Stock Market LLC 1.000% Notes due 2022 — The Nasdaq Stock Market LLC 1.375% Notes due 2024 — The Nasdaq Stock Market LLC 0.000% Notes due 2025 — The Nasdaq Stock Market LLC 0.875% Notes due 2025 — The Nasdaq Stock Market LLC 1.625% Notes due 2026 — The Nasdaq Stock Market LLC 2.000% Notes due 2027 — The Nasdaq Stock Market LLC 1.375% Notes due 2029 — The Nasdaq Stock Market LLC 3.050% Notes due 2029 — The Nasdaq Stock Market LLC 0.500% Notes due 2031 — The Nasdaq Stock Market LLC 3.600% Notes due 2042 — The Nasdaq Stock Market LLC
Securities registered pursuant to Section 12(g) of the Act: None
Indicate by check mark if the Registrant is a well-known seasoned issuer, as defined in Rule 405 of the Securities Act. Yes ☒ No ☐Indicate by check mark if the Registrant is not required to file reports pursuant to Section 13 or Section 15(d) of the Act. Yes ☐ No ☒选项2:使用SimpleDirectoryReader
Section titled “Option 2: Use SimpleDirectoryReader”您也可以选择使用我们 SimpleDirectoryReader 中捆绑的免费PDF解析器。
# # OPTION 2: Use SimpleDirectoryReader# from llama_index.core import SimpleDirectoryReader
# reader = SimpleDirectoryReader(input_files=["apple_2021_10k.pdf"])# docs = reader.load_data()构建RAG管道,定义结构化输出模式
Section titled “Build RAG Pipeline, Define Structured Output Schema”我们使用可靠的VectorStoreIndex和重排序模块构建了一个RAG流水线。然后我们将输出定义为Pydantic模型。这使我们能够创建一个带有附加输出类的结构化LLM。
from llama_index.core import VectorStoreIndex
# skip chunking since we're doing page-level chunkingindex = VectorStoreIndex(docs)from llama_index.postprocessor.flag_embedding_reranker import ( FlagEmbeddingReranker,)
reranker = FlagEmbeddingReranker( top_n=5, model="BAAI/bge-reranker-large",)from pydantic import BaseModel, Fieldfrom typing import List
class Output(BaseModel): """Output containing the response, page numbers, and confidence."""
response: str = Field(..., description="The answer to the question.") page_numbers: List[int] = Field( ..., description="The page numbers of the sources used to answer this question. Do not include a page number if the context is irrelevant.", ) confidence: float = Field( ..., description="Confidence value between 0-1 of the correctness of the result.", ) confidence_explanation: str = Field( ..., description="Explanation for the confidence score" )
sllm = llm.as_structured_llm(output_cls=Output)query_engine = index.as_query_engine( similarity_top_k=5, node_postprocessors=[reranker], llm=sllm, response_mode="tree_summarize", # you can also select other modes like `compact`, `refine`)response = query_engine.query("Net sales for each product category in 2021")print(str(response)){"response": "In 2021, the net sales for each product category were as follows: iPhone: $191,973 million, Mac: $35,190 million, iPad: $31,862 million, Wearables, Home and Accessories: $38,367 million, and Services: $68,425 million.", "page_numbers": [21], "confidence": 1.0, "confidence_explanation": "The figures are directly taken from the provided data, ensuring high accuracy."}response.response.dict(){'response': 'In 2021, the net sales for each product category were as follows: iPhone: $191,973 million, Mac: $35,190 million, iPad: $31,862 million, Wearables, Home and Accessories: $38,367 million, and Services: $68,425 million.', 'page_numbers': [21], 'confidence': 1.0, 'confidence_explanation': 'The figures are directly taken from the provided data, ensuring high accuracy.'}