Skip to content
LlamaParse 文档版本 跟随上游 源快照 3c3a0520 更新于 2026-10-16

已弃用

本页记录的是已弃用的 Extract v1 API。新的接入请使用 Extract v2;改造已有的 v1 代码请见v1 到 v2 迁移指南。

这个示例演示如何用 PER_TABLE_ROW 抽取目标,从含重复实体(表格、列表或目录)的文档里抽取结构化数据。

为什么要用表格型抽取目标?

PER_DOC(各抽取目标的速览见下表)是 LlamaExtract 的默认抽取目标,它会在抽取时通观整份文档的上下文。而抽取实体列表时,基于 LLM 的抽取有一个致命的失败模式 —— 往往只抽出长列表最前面几十条。这是因为 LLM 对重复性数据的注意力有限。文档级抽取并不保证覆盖穷尽,长列表必然导致抽取不完整。

解法:PER_TABLE_ROW 逐个、或以较小批次处理每个实体,从而无论列表多长都能穷尽抽取全部条目。

实体级抽取

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

  • 找出区分各个实体的排版模式(表格行、列表项、章节标题等)
  • 把你的 schema 应用到每一个被识别出的实体
  • 返回一个 list[YourSchema],每个实体一条

当每个实体在本地就含齐了你 schema 所需的信息时,这种做法最为理想。

选择合适的抽取目标

抽取目标最适合返回什么
PER_DOC单实体文档、摘要或短列表整份文档对应一个 JSON 对象
PER_PAGE每页各自独立的长文档每页一个 JSON 对象
PER_TABLE_ROW长列表、表格、含重复实体的目录一个 JSON 对象列表(每实体一条)

📖 更多细节见抽取目标文档。

  • Python
  • Typescript
  • Python(legacy)

终端窗口

pip install llama_cloud<2.0
from llama_cloud import LlamaCloud, AsyncLlamaCloud

client = LlamaCloud(api_key="your_api_key")  # Or reads from LLAMA_CLOUD_API_KEY env var

终端窗口

npm install @llamaindex/llama-cloud zod
import LlamaCloud from "@llamaindex/llama-cloud";

const client = new LlamaCloud({
  apiKey: "your_api_key",  // Or reads from LLAMA_CLOUD_API_KEY env var
});

终端窗口

pip install llama_cloud_services python-dotenv
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,里面按县列出医院,以及 Blue Shield of California 提供的各种保险计划。

你可以在这里取得 BSC-Hospital-List-by-County.pdf

PDF 的前几条记录

我们想把每家医院连同其适用的保险计划列表一起抽出来。

示例 1:结构化表格

这正是 PER_TABLE_ROW 抽取的理想场景:

  • 结构清晰:文档有明确的行列表格格式
  • 实体重复:每一行是一家医院,属性一致
  • 信息局部:每家医院的全部数据(县、名称、计划)都在它自己那一行里

注意我们的 Hospital schema 描述的是一家医院,而不是整份文档。LlamaExtract 会返回一个 list[Hospital],每个表格行一条。

  • Python
  • Typescript
  • Python(legacy)
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"
    )
file_obj = await client.files.create(file="BSC-Hospital-List-by-County.pdf", purpose="extract")
file_id = file_obj.id

# Stateless one-shot extraction
result = client.extraction.extract(
    file_id=file_id,
    config={
        "extraction_mode": "PREMIUM",
        "extraction_target": "PER_TABLE_ROW",
        "parse_model": "anthropic-sonnet-4.5",
    },
    data_schema=Hospital.model_json_schema(),
)
import { z } from "zod";

const HospitalSchema = z.object({
  county: z.string().describe("County name"),
  hospital_name: z.string().describe("Name of the hospital"),
  plan_names: z
    .array(
      z.enum([
        "Trio HMO",
        "SaveNet",
        "Access+ HMO",
        "BlueHPN PPO",
        "Tandem PPO",
        "PPO",
      ])
    )
    .describe(
      "List of plans available at the hospital. One of: Trio HMO, SaveNet, Access+ HMO, BlueHPN PPO, Tandem PPO, PPO"
    ),
});
import fs from "fs";

const fileObj = await client.files.create({
  file: fs.createReadStream("BSC-Hospital-List-by-County.pdf"),
  purpose: "extract",
});
const fileId = fileObj.id;

// Stateless one-shot extraction
const result = await client.extraction.extract({
  file_id: fileId,
  config: {
    extraction_mode: "PREMIUM",
    extraction_target: "PER_TABLE_ROW",
    parse_model: "anthropic-sonnet-4.5",
  },
  data_schema: z.toJSONSchema(HospitalSchema),
});
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",
    ),
)

结果

数一下抽出的医院条数,会发现 380 条全部拿到了!

打印前几条看看:

    [{'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 条。

从玩具目录抽取

示例 2:半结构化列表

PER_TABLE_ROW 抽取目标同样适用于并非显式表格、但具备相似性质的文档:

  • 有序罗列:玩具按顺序排列,彼此有视觉分隔(章节标题、留白)
  • 模式重复:每个玩具条目的结构一致(编号、名称、规格、描述)
  • 信息局部:每个玩具的全部属性都聚在它自己的条目里

尽管这不是传统的表格格式,每个玩具实体在本地仍含齐了 schema 所需的信息。LlamaExtract 会找出区分各个玩具的排版模式,并把它们作为独立实体抽取出来。

  • Python
  • Typescript
  • Python(legacy)
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.",
    )
file_obj = await client.files.create(file="Click-BS-Toys-Catalogue-2024.pdf", purpose="extract")
file_id = file_obj.id

# Stateless one-shot extraction
result = client.extraction.extract(
    file_id=file_id,
    config={
        "extraction_mode": "PREMIUM",
        "extraction_target": "PER_TABLE_ROW",
        "parse_model": "anthropic-sonnet-4.5",
    },
    data_schema=ToyCatalog.model_json_schema(),
)
import { z } from "zod";

const ToyCatalogSchema = z.object({
  section_name: z
    .string()
    .describe("The name of the toy section (e.g. Table Toys, Active Toys)."),
  product_code: z
    .string()
    .describe("The unique product code for the toy (e.g., GA457)."),
  toy_name: z.string().describe("The name of the toy."),
  age_range: z
    .string()
    .describe("The recommended age range for the toy (e.g., 6 +, 4 +)."),
  player_range: z
    .string()
    .describe(
      "The number of players the toy is designed for (e.g., 2, 2-4, 1-6)."
    ),
  material: z
    .string()
    .describe(
      "The primary material(s) the toy is made of (e.g., wood, cardboard)."
    ),
  description: z
    .string()
    .describe(
      "A brief description of the toy and its components and dimensions."
    ),
});
import fs from "fs";

const fileObj = await client.files.create({
  file: fs.createReadStream("Click-BS-Toys-Catalogue-2024.pdf"),
  purpose: "extract",
});
const fileId = fileObj.id;

// Stateless one-shot extraction
const result = await client.extraction.extract({
  file_id: fileId,
  config: {
    extraction_mode: "PREMIUM",
    extraction_target: "PER_TABLE_ROW",
    parse_model: "anthropic-sonnet-4.5",
  },
  data_schema: z.toJSONSchema(ToyCatalogSchema),
});
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.",
    )
from llama_cloud_services.extract import ExtractConfig, ExtractMode, ExtractTarget

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",
    ),
)

结果

同样地,我们的 schema 表示的是单个玩具产品,不是整本目录。系统会返回一个 list[ToyCatalog],每个玩具一条。

你可以在这里取得 Click-BS-Toys-Catalogue-2024.pdf

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 自动识别出分隔各个玩具的视觉模式,并把我们的 schema 逐个应用上去。

总结

PER_TABLE_ROW 抽取目标在抽取文档里的重复结构化实体时非常有力。要点回顾:

  1. schema 设计:为你的 schema 定义单个实体,而不是整份文档。系统返回 list[YourSchema]。

  2. 适配多种格式:不限于传统表格 —— 任何有可区分重复实体的文档都行(项目符号、编号、标题、视觉分隔等)。共同的要求是每个实体在本地上下文里就含齐你 schema 需要的数据。

  3. 自动的模式识别:LlamaExtract 会找出区分实体的排版模式,并把你的 schema 应用到每一个上。

BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。