函数调用快速提取结构化数据使用技巧

函数调用快速提取结构化数据使用技巧

一、这是什么?(概念解释)

结构化输出(Structured Output) 是让大语言模型返回固定格式数据的能力,而不是普通的文本。

在 LangChain 中,通过 with_structured_output() 方法,可以指定一个 Pydantic 模型,让 LLM 直接返回符合该模型的数据结构。

核心优势:

  • 类型安全:直接得到 Python 对象,无需手动解析
  • 自动验证:Pydantic 自动验证返回的数据格式
  • 两种模式
    • function_calling:使用函数调用方式(推荐)
    • json_mode:使用 JSON 模式(兼容性强)

二、有什么用?(应用场景)

场景 说明
信息提取 从文本中提取姓名、日期、地址等结构化信息
数据清洗 将非结构化数据转换为结构化格式
表单填充 从对话中自动提取表单字段
QA 生成 从文档中提取问题和答案对
实体识别 识别人名、地名、机构名等命名实体
分类标注 对文本进行分类并返回分类标签
数据转换 将一种格式的数据转换为另一种格式

三、完整示例代码

python 复制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import dotenv
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI

dotenv.load_dotenv()

# ============ 第一步:定义数据结构(Pydantic 模型)============

class QAExtra(BaseModel):
    """一个问答键值对工具,传递对应的假设性问题+答案"""
    question: str = Field(description="假设性问题")
    answer: str = Field(description="假设性问题对应的答案")

# ============ 第二步:创建结构化输出模型 ============

llm = ChatOpenAI(model="moonshot-v1-8k")

# 方式一:使用 JSON 模式(兼容性强)
structured_llm = llm.with_structured_output(QAExtra, method="json_mode")

# 方式二:使用函数调用(推荐,如果模型支持)
# structured_llm = llm.with_structured_output(QAExtra, method="function_calling")

# ============ 第三步:创建链 ============

prompt = ChatPromptTemplate.from_messages([
    ("system", "请从用户传递的query中提取出假设性的问题+答案。响应格式为JSON,并携带`question`和`answer`两个字段。"),
    ("human", "{query}")
])

chain = {"query": RunnablePassthrough()} | prompt | structured_llm

# ============ 第四步:调用 ============

result = chain.invoke("我叫慕小课,我喜欢打篮球,游泳。")
print(result)
# 输出:QAExtra(question="慕小课喜欢什么运动?", answer="打篮球和游泳")

# 可以直接访问字段
print(f"问题:{result.question}")
print(f"答案:{result.answer}")

四、高级示例:提取多个字段

python 复制代码
from typing import List, Optional
from pydantic import BaseModel, Field

class PersonInfo(BaseModel):
    """从文本中提取人物信息"""
    name: str = Field(description="人物姓名")
    age: Optional[int] = Field(description="年龄,如果没有则为None", default=None)
    hobbies: List[str] = Field(description="爱好列表")
    city: Optional[str] = Field(description="所在城市", default=None)

class PersonList(BaseModel):
    """人物列表"""
    people: List[PersonInfo] = Field(description="提取到的人物列表")

# 创建结构化输出
structured_llm = llm.with_structured_output(PersonList, method="json_mode")

prompt = ChatPromptTemplate.from_messages([
    ("system", "请从文本中提取所有人物的信息,包括姓名、年龄、爱好和城市。"),
    ("human", "{text}")
])

chain = prompt | structured_llm

text = "我叫张三,今年25岁,喜欢编程和阅读。我的朋友李四30岁,他喜欢打篮球,住在北京。"
result = chain.invoke(text)

for person in result.people:
    print(f"{person.name}, {person.age}岁, 爱好: {person.hobbies}, 城市: {person.city}")

五、流程对比图

javascript 复制代码
┌─────────────────────────────────────────────────────────────────────────┐
│              普通输出 VS 结构化输出对比                                    │
└─────────────────────────────────────────────────────────────────────────┘


  ================== 普通文本输出 ==================

  用户输入                    LLM                     应用层
     │                        │                         │
     ▼                        ▼                         │
  "我叫慕小课..." ────────────▶│ 分析文本                 │
     │                        │                         │
     │                        │ 生成文本回答             │
     │◀───────────────────────│                         │
     │                        │                         │
  "根据文本,慕小课           │                         │
   喜欢打篮球和游泳"          │                         │
     │                        │                         │
     ▼                        ▼                         ▼
  需要手动解析字符串                                  ❌ 不方便


  ================== 结构化输出 ==================

  用户输入                    LLM                     应用层
     │                        │                         │
     ▼                        ▼                         │
  "我叫慕小课..." ────────────▶│ 分析文本                 │
     │                        │                         │
     │                        │ 返回 JSON 对象           │
     │◀───────────────────────│ {question: "...",       │
     │                        │  answer: "..."}         │
     │                        │                         │
     ▼                        ▼                         ▼
  直接得到 Python 对象                              ✓ 类型安全
  QAExtra(                                          ✓ 自动验证
    question="慕小课喜欢...",                         ✓ 无需解析
    answer="打篮球和游泳"
  )


  ┌─────────────────────────────────────────────────────────────────────────┐
  │                         核心差异                                         │
  └─────────────────────────────────────────────────────────────────────────┘

  普通输出:                     结构化输出:
  - 返回字符串                  - 返回 Pydantic 对象
  - 需要手动解析                - 自动验证类型
  - 容易出错                    - 类型安全
  - 不方便集成                  - 直接使用字段
相关推荐
yu俞娥宝1 分钟前
Codex官网前端可抄吗?技术拆解、风险边界与合规借鉴方案
前端
宫水三叶的刷题日记9 分钟前
馋猫外卖:AI 让字节式试错没有天花板
后端
捧 花26 分钟前
FastAPI 基础语法:从一个完整接口理解 Web API 的设计
前端·python·fastapi·middleware
Hilaku33 分钟前
一行 CSS 新特性干掉 20 行 JavaScript ?
前端·javascript·程序员
JarvanMo1 小时前
AI 写代码暴增 161 倍,移动开发有没有变得更差?
前端
SomeB1oody1 小时前
【RustyML入门】7.4. 按需裁剪与模块化集成
开发语言·后端·机器学习·rust·教程
罗超驿1 小时前
SpringBoot 快速入门
java·spring boot·后端
梦曦i1 小时前
RouterLink v2.5.0:H5端原生能力全面回归
前端·uni-app
小林ixn1 小时前
React + Zustand + JWT:从零实现登录鉴权与请求拦截
前端·react.js·前端框架
葡萄城技术团队1 小时前
下拉多选类型单元格:在 SpreadJS 里接入 xm\-select
前端