news 2026/9/15 18:54:14

使用 Instructor 将 Markdown 表格直接提取为 Pandas DataFrame

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 Instructor 将 Markdown 表格直接提取为 Pandas DataFrame

使用 Instructor 将 Markdown 表格直接提取为 Pandas DataFrame

【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor

本文讲解如何在 instructor 项目中,利用 Pydantic 的类型注解体系定义一个名为MarkdownDataFrame的自定义类型,让 LLM 输出的 Markdown 表格在返回时被自动解析成pandas.DataFrame,从而直接在数据分析、报表生成等场景中无缝衔接。读完本文,你将掌握BeforeValidatorPlainSerializerWithJsonSchema的组合用法,能实现"单表提取""带标题的复合结构提取"以及"一次响应提取多张表"三类实战方案。

问题的切入点:LLM 输出的是 Markdown,你要的是 DataFrame

LLM 天然擅长以 Markdown 表格的文本形式组织数据,但数据分析链路里我们真正需要的是结构化的pandas.DataFrame。手工写解析逻辑既繁琐又脆弱。instructor 的做法是:把"字符串 → DataFrame"的转换逻辑封装进类型本身,让response_model一旦声明为 DataFrame,模型返回的 Markdown 文本就会在验证阶段被自动转换成 DataFrame 对象。

核心武器:用Annotated编排一个"自带转换逻辑"的类型

MarkdownDataFrame本质上是 Pydantic 的Annotated元数据组合体,它把四种元数据叠加在InstanceOf[pd.DataFrame]上,声明如下(完整代码见 docs/examples/pandas_df.md):

from io import StringIO from typing import Annotated, Any from pydantic import ( BaseModel, BeforeValidator, PlainSerializer, InstanceOf, WithJsonSchema, ) import pandas as pd import instructor def md_to_df(data: Any) -> Any: # Convert markdown to DataFrame if isinstance(data, str): return ( pd.read_csv( StringIO(data), # Process data sep="|", index_col=1, ) .dropna(axis=1, how="all") .iloc[1:] .applymap(lambda x: x.strip()) ) return data MarkdownDataFrame = Annotated[ # Validates final type InstanceOf[pd.DataFrame], # Converts markdown to DataFrame BeforeValidator(md_to_df), # Converts DataFrame to markdown on model_dump_json PlainSerializer(lambda df: df.to_markdown()), # Adds a description to the type WithJsonSchema( { "type": "string", "description": """ The markdown representation of the table, each one should be tidy, do not try to join tables that should be separate""", } ), ]

各组成部分的职责分工如下:

组件作用
InstanceOf[pd.DataFrame]声明最终通过验证后的对象类型必须是pd.DataFrame实例,作为类型约束的锚点
BeforeValidator(md_to_df)在标准验证之前运行转换函数,把模型返回的 Markdown 字符串解析成 DataFrame
PlainSerializer(lambda df: df.to_markdown())反向序列化:当调用model_dump_json()或把模型写入 JSON 时,把 DataFrame 还原成 Markdown 字符串,保证可序列化性
WithJsonSchema({...})覆盖该字段在发给 LLM 的 JSON Schema 中的表示:告诉模型这里要输出一段字符串(Markdown 表格),并给出语义提示

md_to_df逐行拆解

  • pd.read_csv(StringIO(data), sep="|", index_col=1):以|为分隔符解析 Markdown 表格文本,并把第 1 列(索引列,通常为表头前的空列或首列)作为行索引;
  • .dropna(axis=1, how="all"):丢弃全空的列(Markdown 表格边缘的|会产生空列);
  • .iloc[1:]:跳过分隔行(---|---那一行);
  • .applymap(lambda x: x.strip()):去除每个单元格两端的空白字符。

这套解析逻辑在同一仓库的 examples/extract-table/run_vision.py 中也有几乎完全一致的实现(仅将applymap换成了等价写法.map),可以对照查看。

完整实战一:直接提取原始 DataFrame

有了MarkdownDataFrame,客户端初始化与提取函数非常简洁。注意当前仓库推荐使用instructor.from_provider("provider/model-name")这种统一入口创建客户端:

client = instructor.from_provider("openai/gpt-5-nano") def extract_df(data: str) -> pd.DataFrame: return client.create( model="gpt-5.4-mini", response_model=MarkdownDataFrame, messages=[ { "role": "system", "content": "You are a data extraction system, table of writing perfectly formatted markdown tables.", }, { "role": "user", "content": f"Extract the data into a table: {data}", }, ], ) if __name__ == "__main__": df = extract_df( """Create a table of the last 5 presidents of the United States, including their party and the years they served.""" ) assert isinstance(df, pd.DataFrame) print(df) """ Party Years Served President Joe Biden Democrat 2021 - Present Donald Trump Republican 2017 - 2021 Barack Obama Democrat 2009 - 2017 George W. Bush Republican 2001 - 2009 Bill Clinton Democrat 1993 - 2001 """

assert isinstance(df, pd.DataFrame)保证了解析结果一定是 DataFrame,随后的print(df)即可直接输出格式化表格。

完整实战二:提取"标题 + DataFrame"复合结构

很多时候我们不仅需要表格数据,还需要标题等上下文信息。此时只需把MarkdownDataFrame作为 Pydantic 模型的一个字段:

class Table(BaseModel): title: str data: MarkdownDataFrame def extract_table(data: str) -> Table: return client.create( model="gpt-5.4-mini", response_model=Table, messages=[ { "role": "system", "content": "You are a data extraction system, table of writing perfectly formatted markdown tables.", }, { "role": "user", "content": f"Extract the data into a table: {data}", }, ], )

运行效果:

table = extract_table( """Create a table of the last 5 presidents of the United States, including their party and the years they served.""" ) assert isinstance(table, Table) assert isinstance(table.data, pd.DataFrame) print(table.title) #> Last 5 Presidents of the United States print(table.data) """ Party Years Served President Joe Biden Democratic 2021-2025 Donald Trump Republican 2017-2021 Barack Obama Democratic 2009-2017 George W. Bush Republican 2001-2009 Bill Clinton Democratic 1993-2001 """

可以看到,字段级titledata协同工作:标题走普通字符串验证,data字段则走md_to_df转换路径,互不干扰。更复杂的MultipleTablestables: list[Table])组合方式同样出现在 examples/extract-table/run_vision.py 中,可作为参考。

进阶:一次响应提取多张表Iterable[Table]

原文档明确指出:还可以请求Iterable[Table]一次获得多张表格。这依赖 instructor 的 Iterable DSL。其底层实现位于 instructor/v2/dsl/iterable.py,核心机制是:

  • 通过IterableModel(subtask_class)动态生成一个包装类(内部包含tasks: list[Table]字段,并自动命名为Iterable{Table});
  • tasks_from_chunks/extract_cls_task_type负责从模型返回的 JSON 流中逐段切分出每个Table对象,逐条yield
  • 支持普通(create)与流式(from_streaming_response)两种消费方式。

典型用法示意:

from typing import Iterable def extract_tables(data: str) -> Iterable[Table]: return client.create( model="gpt-5.4-mini", response_model=Iterable[Table], messages=[{"role": "user", "content": f"Extract all tables: {data}"}], ) for table in extract_tables("..."): print(table.title, table.data)

注意:Iterable[Table]要求Table是 PydanticBaseModel子类,MarkdownDataFrame作为其字段会自然继承转换逻辑,因此每个table.data仍然是解析好的 DataFrame。

底层原理:instructor 如何驱动这套转换

整个流程可以概括为三步(对应 instructor/v2/core/client.py 中client.create的处理链路):

  1. Schema 生成:instructor 依据response_model生成 JSON Schema 并注入 prompt。WithJsonSchema覆盖后,LLM 看到的MarkdownDataFrame就是一个type: "string"且带"markdown representation of the table"描述的字段,从而稳定输出 Markdown 表格文本;
  2. 响应验证:拿到模型输出后,Pydantic 按字段执行验证,BeforeValidator(md_to_df)在类型检查前把字符串解析为 DataFrame,随后InstanceOf[pd.DataFrame]做最终类型断言;
  3. 序列化还原:当结果需要model_dump_json()(例如返回给 FastAPI 或写入日志)时,PlainSerializer(lambda df: df.to_markdown())又把 DataFrame 序列化为 Markdown 字符串,保证 JSON 可持久化。

from_provider入口的模型字符串要求"provider/model-name"格式(例如"openai/gpt-5-nano"),provider 不支持时会抛出ConfigurationError;OpenAI 系默认采用Mode.TOOLS,相关逻辑见 instructor/v2/auto_client.py。对于不支持 function calling 的视觉模型,可显式指定mode=instructor.Mode.MD_JSON,具体参考 docs/examples/extracting_tables.md。

环境依赖与使用前提

  • 需要安装pandastabulatedf.to_markdown()依赖 tabulate 提供 Markdown 渲染)。仓库 pyproject.toml 中声明了tabulate<1.0.0,>=0.9.0的依赖范围,文档 docs/examples/tables_from_vision.md 也注明pip install pandas tabulate
  • 本模式对纯文本 LLM 与视觉模型(配合图片输入)均适用;当数据中夹杂多张独立表格时,务必在 schema 描述中要求"每张表保持整洁、不要强行合并",正如WithJsonSchemado not try to join tables that should be separate的提示所强调的。

小结

通过MarkdownDataFrame这一个类型定义,instructor 把"Markdown → DataFrame"的转换下沉到了 Pydantic 验证管线中,使得下游代码拿到的不再是待解析的字符串,而是立即可用的pandas.DataFrame。你可以在此基础上自由组合出Table复合模型、Iterable[Table]批量提取乃至视觉表格提取(见 docs/examples/extracting_tables.md 与 docs/examples/tables_from_vision.md)等方案,让 LLM 的结构化输出与数据科学工作流无缝衔接。关于自定义类型的更多玩法,可继续阅读 docs/concepts/types.md。

【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 18:52:07

MobaXterm连不上VMware中CentOS 7?从网络到sshd的完整排查指南

MobaXterm连不上VMware里的CentOS 7&#xff0c;这事我太熟了。前前后后帮同事排查过不下二十次&#xff0c;自己也踩过几回坑&#xff0c;绝大多数情况下问题都出在虚拟机网络配置和sshd服务这几块&#xff0c;真正是VMware或者MobaXterm本身故障的反倒很少。这篇文章我就按实…

作者头像 李华
网站建设 2026/9/15 18:48:12

H5微场景源码包拆解:从解压到上线的完整工程实践

简介&#xff1a;这是一份面向Web前端学习者与开发者的H5微场景源码合集&#xff0c;13套项目涉及产品发布、品牌宣传、婚礼邀请、节日贺卡、教育培训等常见类型。初学者可通过完整工程理解从零搭建交互页面的流程&#xff0c;有经验的开发者则能直接从中提取动画交互方案或改造…

作者头像 李华
网站建设 2026/9/15 18:47:46

gs-quant Portfolio 指南:用 Python 构建、定价与风险管理投资组合

gs-quant Portfolio 指南&#xff1a;用 Python 构建、定价与风险管理投资组合 【免费下载链接】gs-quant Python toolkit for quantitative finance 项目地址: https://gitcode.com/GitHub_Trending/gs/gs-quant gs-quant 的 Portfolio 类是量化交易中处理一篮子工具的…

作者头像 李华