跳转到内容

使用表格行提取技术提取重复实体

此示例演示了如何使用 PER_TABLE_ROW 提取目标,从包含重复实体(如表格、列表或目录)的文档中提取结构化数据。

PER_DOC(请参阅下表快速了解不同的提取目标)是LlamaExtract中的默认提取目标,它在执行提取时会考虑整个文档的上下文。当提取实体列表时,基于LLM的提取存在一个关键故障模式——它经常仅从长列表中提取前几十个条目。这是因为LLM对重复数据的注意力有限。文档级提取不能保证完全覆盖,长列表会导致提取不完整。

解决方案PER_TABLE_ROW通过单独或分批处理每个实体来解决这个问题,确保无论列表长度如何都能彻底提取所有条目。

当使用 extraction_target=ExtractTarget.PER_TABLE_ROW 时,您需要为单个实体(例如一家医院、一个产品、一个发票行项目)定义模式,而不是整个文档。LlamaExtract 会自动:

  • 检测用于区分各个实体(表格行、列表项、章节标题等)的格式化模式
  • 将您的模式应用于每个已识别的实体
  • 返回一个 list[YourSchema],每个实体对应一个对象

当每个实体本地包含您的模式所需的全部信息时,这种方法非常理想。

提取目标最适合返回值
PER_DOC单实体文档、摘要或简短列表整个文档对应一个JSON对象
PER_PAGE多页文档,其中每页内容相互独立每页一个JSON对象
PER_TABLE_ROW包含重复实体的长列表、表格、目录JSON对象列表(每个实体对应一个)

📖 更多详细信息,请参阅提取目标文档

from dotenv import load_dotenv
from llama_cloud_services import LlamaExtract
# Load environment variables (put LLAMA_CLOUD_API_KEY in your .env file)
load_dotenv(override=True)
# Optionally, add your project id/organization id
llama_extract = LlamaExtract()

我们有一份PDF文档,其中按县列出了医院列表以及加州蓝盾提供的不同保险计划。

你可以在此处获取 BSC-Hospital-List-by-County.pdf

First few entries from the PDF

我们希望从此表中提取每家医院以及适用的保险计划列表。

这是 PER_TABLE_ROW 提取的理想用例:

  • 清晰的结构: 文档采用明确的表格格式,包含行和列
  • 重复实体: 每行代表一个具有一致属性的医院
  • 本地信息:每家医院的所有数据(县区、名称、计划)都包含在其行内

请注意,我们的 Hospital 模式描述的是单个医院,而非完整文档。LlamaExtract 将返回一个 list[Hospital],其中每个表格行对应一个条目。

from pydantic import BaseModel, Field
class Hospital(BaseModel):
"""List of hospitals by county available for different BSC plans"""
county: str = Field(description="County name")
hospital_name: str = Field(description="Name of the hospital")
plan_names: list[str] = Field(
description="List of plans available at the hospital. One of: Trio HMO, SaveNet, Access+ HMO, BlueHPN PPO, Tandem PPO, PPO"
)
from llama_cloud_services.extract import ExtractConfig, ExtractMode, ExtractTarget
result = await llama_extract.aextract(
data_schema=Hospital,
files="BSC-Hospital-List-by-County.pdf",
config=ExtractConfig(
extraction_mode=ExtractMode.PREMIUM,
extraction_target=ExtractTarget.PER_TABLE_ROW,
parse_model="anthropic-sonnet-4.5",
),
)
len(result.data)
380
result.data[:10]
[{'county': 'Alameda',
'hospital_name': 'Alameda Hospital',
'plan_names': ['Trio HMO',
'SaveNet',
'Access+ HMO',
'BlueHPN PPO',
'Tandem PPO',
'PPO']},
{'county': 'Alameda',
'hospital_name': 'Alta Bates Med Ctr Herrick Campus',
'plan_names': ['Trio HMO',
'Access+ HMO',
'BlueHPN PPO',
'Tandem PPO',
'PPO']},
{'county': 'Alameda',
'hospital_name': 'Alta Bates Summit Med Ctr Alta Bates Campus',
'plan_names': ['Trio HMO',
'Access+ HMO',
'BlueHPN PPO',
'Tandem PPO',
'PPO']},
{'county': 'Alameda',
'hospital_name': 'Alta Bates Summit Med Ctr Summit Campus',
'plan_names': ['Trio HMO',
'Access+ HMO',
'BlueHPN PPO',
'Tandem PPO',
'PPO']},
{'county': 'Alameda',
'hospital_name': 'Alta Bates Summit Medical Center',
'plan_names': ['Trio HMO',
'Access+ HMO',
'BlueHPN PPO',
'Tandem PPO',
'PPO']},
{'county': 'Alameda',
'hospital_name': 'BHC Fremont Hospital',
'plan_names': ['Trio HMO',
'SaveNet',
'Access+ HMO',
'BlueHPN PPO',
'Tandem PPO',
'PPO']},
{'county': 'Alameda',
'hospital_name': 'Centre For Neuro Skills San Francisco',
'plan_names': ['Trio HMO',
'SaveNet',
'Access+ HMO',
'BlueHPN PPO',
'Tandem PPO',
'PPO']},
{'county': 'Alameda',
'hospital_name': 'Eden Medical Center',
'plan_names': ['Trio HMO', 'Access+ HMO', 'PPO']},
{'county': 'Alameda',
'hospital_name': 'Fairmont Hospital',
'plan_names': ['Trio HMO',
'SaveNet',
'Access+ HMO',
'BlueHPN PPO',
'Tandem PPO',
'PPO']},
{'county': 'Alameda',
'hospital_name': 'Highland Hospital',
'plan_names': ['Trio HMO',
'SaveNet',
'Access+ HMO',
'BlueHPN PPO',
'Tandem PPO',
'PPO']}]

成功! 我们从多页PDF中提取了全部380家医院。每个实体都正确解析了其所属县区、医院名称和适用的保险计划。若使用PER_DOC,我们可能只能获取前20-30条条目。

PER_TABLE_ROW 提取目标同样适用于非显式表格但具有相似属性的文档:

  • 有序列表: 玩具按顺序排列,并带有视觉分隔(章节标题、间距)
  • 重复模式: 每个玩具条目都具有一致的结构(代码、名称、规格、描述)
  • 本地信息:每个玩具的所有属性在其条目中被分组在一起

尽管这不是传统的表格格式,但每个玩具实体在本地都包含了我们模式所需的全部信息。LlamaExtract 检测区分每个玩具的格式模式,并将它们提取为独立的实体。

from pydantic import BaseModel, Field
class ToyCatalog(BaseModel):
"""Product information from a toy catalog."""
section_name: str = Field(
description="The name of the toy section (e.g. Table Toys, Active Toys)."
)
product_code: str = Field(
description="The unique product code for the toy (e.g., GA457)."
)
toy_name: str = Field(description="The name of the toy.")
age_range: str = Field(
description="The recommended age range for the toy (e.g., 6 +, 4 +).",
)
player_range: str = Field(
description="The number of players the toy is designed for (e.g., 2, 2-4, 1-6).",
)
material: str = Field(
description="The primary material(s) the toy is made of (e.g., wood, cardboard).",
)
description: str = Field(
description="A brief description of the toy and its components and dimensions.",
)

再次说明,我们的模式代表的是单个玩具产品,而非整个目录。系统将返回一个list[ToyCatalog],每个玩具对应一个条目。

你可以在此处获取 Click-BS-Toys-Catalogue-2024.pdf

result = await llama_extract.aextract(
data_schema=ToyCatalog,
files="Click-BS-Toys-Catalogue-2024.pdf",
config=ExtractConfig(
extraction_mode=ExtractMode.PREMIUM,
extraction_target=ExtractTarget.PER_TABLE_ROW,
parse_model="anthropic-sonnet-4.5",
),
)
len(result.data)
153
result.data[:10]
[{'section_name': 'Table Toys',
'product_code': 'GA457',
'toy_name': 'Dots and Boxes',
'age_range': '6+',
'player_range': '2',
'material': 'wood',
'description': 'base 17x17 cm\n50 border pieces 4x1,2x0,3 cm\n34 trees 2,6x1,4 cm'},
{'section_name': 'Table Toys',
'product_code': 'GA456',
'toy_name': '3 In a Row',
'age_range': '8+',
'player_range': '2',
'material': 'wood, pine, cardboard',
'description': 'base 24x22,5x2,5 cm\n30 cards 5,5x5 cm\n6 chips'},
{'section_name': 'Table Toys',
'product_code': 'GA467',
'toy_name': 'Which Cow am i?',
'age_range': '6+',
'player_range': '2',
'material': 'wood, beech',
'description': '2 cow bases 56x4x4,5 cm\n16 cards 4x5 cm'},
{'section_name': 'Table Toys',
'product_code': 'GA460',
'toy_name': 'Balance Bunnies',
'age_range': '4+',
'player_range': '2',
'material': 'wood',
'description': '1 base 35x12x25 cm\n7 bunnies 7 foxes\n1 dice 3 cm'},
{'section_name': 'Table Toys',
'product_code': 'GA462',
'toy_name': 'Color Combination Race',
'age_range': '4+',
'player_range': '2-4',
'material': 'wood, cardboard',
'description': 'base 6,5x6,5x15 cm, rings 5,5x5,5x0,5 mm\ncardholder 6x6x2 cm, cards 5,5x5,5 cm\ncolor cards Ø 15,5 cm - Ø 7 cm'},
{'section_name': 'Table Toys',
'product_code': 'GA465',
'toy_name': 'Plop It',
'age_range': '6+',
'player_range': '2-4',
'material': 'wood, elastic, cardboard',
'description': 'Catch the right balls and plop them in the net!\n* 2 ploppers 8x5 cm\n* 2 net holders Ø 5cm, length 55 cm\n* 6 cards 1,5x2,5 cm, 30 balls Ø 2,5 cm\n* 1 rope 120 cm'},
{'section_name': 'Table Toys',
'product_code': 'GA466',
'toy_name': 'Whack a Shape',
'age_range': '4+',
'player_range': '2-4',
'material': 'wood',
'description': '* base 38,5x15,5 cm\n* 2 stands 36 half balls, 4 hammers\n* 1 dice 2,5 cm\n* 4 cards'},
{'section_name': 'Table Toys',
'product_code': 'GA458',
'toy_name': 'Sling Puck | Table Hockey',
'age_range': '6+',
'player_range': '2',
'material': 'wood',
'description': '* double sides base 39x21x3 cm\n* 10 chips Ø 2,5 cm\n* 2 pushers 4x4x3 cm'},
{'section_name': 'Table Toys',
'product_code': 'GA039',
'toy_name': 'DIY Birdhouse',
'age_range': '3+',
'player_range': '1',
'material': 'wood',
'description': '* house 9x9x13 cm'},
{'section_name': 'Table Toys',
'product_code': 'GA319',
'toy_name': 'Triangle Domino',
'age_range': '6+',
'player_range': '2-4',
'material': 'wood',
'description': '* 35 triangles 10x10 x10 cm'}]

成功! 尽管采用半结构化格式,我们还是从目录中提取了所有152件玩具产品(附录部分有一个额外重复提取的玩具)。LlamaExtract自动检测到分隔每个玩具条目的视觉模式,并将我们的模式应用于每一个条目。

PER_TABLE_ROW 提取目标在从文档中提取重复的结构化实体方面非常强大。关键要点:

  1. 模式设计:为单个实体定义模式,而非整个文档。系统返回 list[YourSchema]

  2. 支持多种格式:不仅限于传统表格——任何包含可区分重复实体(项目符号、编号、标题、视觉分隔等)的文档均可。通用要求是每个实体应在其本地上下文中包含模式所需的全部数据。

  3. 自动模式识别: LlamaExtract 识别区分实体的格式化模式,并将您的模式应用于每一个实体。