[Meetily后端框架] AI摘要结构化 | `SummaryResponse`模型 | Pydantic库 | vs marshmallow库
第3章:摘要数据结构(Pydantic库)
欢迎回来!
在之前的第2章:API文档中,我们知道API网关提供了端点
而API文档准确告诉我们如何与这些端点通信,包括需要发送的数据格式
和期望接收的数据格式。
我们期望接收的最重要数据之一是会议摘要。
但在计算机程序中,会议摘要究竟应该是什么样子的?
它不仅仅是一个大段的文本。我们希望它是有结构的,包含明确的行动项、决策等部分,以便应用程序可以轻松地美观显示或提取特定条目。
这就是摘要数据结构的意义所在,在我们的项目中,我们使用**Pydantic模型**来定义和强制实施这种结构。
定义数据结构解决了什么问题?
假设我们需要给朋友写信。可以自由发挥——从天气开始,聊聊日常生活,最后再补充一些内容。这非常灵活!
现在想象我们正在填写工作申请表。这是不灵活的。它要求我们在不同的方框中填写姓名、地址、工作经历等具体字段。
这种明确的格式要求是因为公司需要高效处理这些信息。
在编程中,特别是当应用程序的不同部分(如后端和前端)甚至不同程序(如我们的后端与AI模型交互)需要交换复杂信息时,这些信息需要像表单一样结构化,而不是自由格式的书信。
如果后端像这样发送摘要数据:
{"MeetingTitle": "Project Alpha Kickoff","ActionItemsList": ["Send intro email to team by Friday","Schedule follow-up sync next week"],"DecisionsMade": [{"decision": "Use Tool X", "who": "Team Lead"},{"decision": "Timeline set to 6 weeks", "who": "Project Manager"}]// ... 其他字段 ...
}
而后端开发者后来不小心将"ActionItemsList"改为"ActionsToDo",或者更改"DecisionsMade"的结构,期望旧格式的前端开发者会遇到应用程序崩溃的问题!
我们需要一种方法来明确定义数据的预期格式,并确保数据始终遵循这种格式。
引入Pydantic模型:数据的蓝图
本项目使用名为Pydantic的Python库
来解决这个问题。
Pydantic允许我们使用标准Python类和类型提示来定义数据的*结构*和*类型*
。这些类定义就成为我们的Pydantic模型。
将Pydantic模型视为数据的蓝图或模板。它规定了:
- 数据应包含哪些"字段"或信息片段(如
MeetingTitle
、ActionItemsList
) - 每个字段的数据类型(如
string
、list
、number
) - 如果字段包含更复杂的数据,该数据的蓝图是什么样
当基于Pydantic模型创建对象
时,Pydantic会自动**验证**提供的数据。
如果数据不符合蓝图约定(例如在需要字符串的位置使用数字,或缺少必填字段),Pydantic将抛出错误
。这有助于尽早发现错误!
我们的摘要蓝图:SummaryResponse
模型
在meeting-minutes
项目中,AI生成的最终结构化摘要的主要蓝图由名为SummaryResponse
的Pydantic模型定义。
该模型设计用于容纳摘要中所需的所有不同部分,如整体摘要、行动项、决策等。
-
我们可以在API文档中查看
SummaryResponse
的预期JSON结构,特别是在/get-summary/{process_id}
端点和Data Models
部分。 -
它定义了
MeetingName
、SectionSummary
、CriticalDeadlines
、KeyItemsDecisions
等字段,并规定了每个字段的内容。
让我们看看后端代码(backend/app/transcript_processor.py
)中如何使用Pydantic模型逐步构建这个结构。
最小构件:Block
模型
摘要是由多个部分组成的,每个部分包含单独的"块"内容——如单个行动项、单个决策点或摘要文本段落。
以下是Block
的Pydantic模型:
# backend/app/transcript_processor.py(简化片段)
from pydantic import BaseModel # 需要导入BaseModel来创建Pydantic模型class Block(BaseModel):"""表示摘要部分中的内容块"""id: str # 该块的唯一标识符(字符串)type: str # 块的类型(如"text"、"action"、"decision")(字符串)content: str # 块的实际文本内容(字符串)color: str # 颜色提示,可能用于UI显示(字符串)# 符合此蓝图的数据示例:
# {"id": "123", "type": "action", "content": "Email report to John", "color": "yellow"}
说明:
class Block(BaseModel):
这行告诉Pydantic我们正在定义一个名为Block
的模型,它继承自BaseModel
,从而获得Pydantic的所有功能id: str
:定义名为id
的字段,指定其必须是str
(字符串)type: str
、content: str
、color: str
:类似地定义其他必填字符串字段- 文档字符串
"""表示摘要部分中的内容块..."""
帮助解释模型的用途
如果尝试创建像Block(id=123, type="text", content="...", color="...")
这样的Block对象,Pydantic会报错,因为id
是数字(123
),而蓝图规定必须是字符串(str
)。
组合块:Section
模型
多个Block
对象的集合构成摘要中的一个Section
(如"Immediate Action Items"部分或"Key Items Decisions"部分)。
以下是Section
的Pydantic模型:
# backend/app/transcript_processor.py(简化片段)
from pydantic import BaseModel
from typing import List # 需要用来指定列表类型# (Block模型定义在此处)
# class Block(BaseModel): ...class Section(BaseModel):"""表示会议摘要中的一个部分"""title: str # 部分的标题(如"Action Items")(字符串)blocks: List[Block] # Block对象列表(Block列表)# 符合此蓝图的数据示例:
# {
# "title": "Immediate Action Items",
# "blocks": [
# {"id": "1", "type": "action", "content": "Email report", "color": "yellow"},
# {"id": "2", "type": "action", "content": "Schedule meeting", "color": "yellow"}
# ]
# }
说明:
class Section(BaseModel):
定义Section
模型title: str
:部分标题的必填字符串字段blocks: List[Block]
:这里使用Block
模型。该字段是一个List
(标准Python类型提示),列表中的项必须是Block
对象(符合Block
蓝图的数据)
因此,Section
必须有一个字符串标题和一个列表,列表中的每个项都必须符合Block
蓝图。
完整摘要蓝图:SummaryResponse
模型
最后,完整的摘要输出由SummaryResponse
模型表示。该模型组合了多个Section
模型,每个模型表示从转录文本中提取的不同类别信息。
以下是backend/app/transcript_processor.py
中SummaryResponse
的(稍作简化的)Pydantic模型:
# backend/app/transcript_processor.py(简化片段)
from pydantic import BaseModel
from typing import List # 仍然需要用于部分内部的列表# (Block和Section模型定义在此处)
# class Block(BaseModel): ...
# class Section(BaseModel): ...class SummaryResponse(BaseModel):"""表示基于转录文本部分的会议摘要响应"""MeetingName : strSectionSummary : Section # 整体摘要部分CriticalDeadlines: Section # 截止日期部分KeyItemsDecisions: Section # 决策部分ImmediateActionItems: Section # 行动项部分NextSteps: Section # 后续步骤部分OtherImportantPoints: Section # 其他要点部分ClosingRemarks: Section # 结束语部分# 符合此蓝图的数据示例:
# {
# "MeetingName": "Q3 Planning",
# "SectionSummary": { ... 符合Section模型的数据 ... },
# "CriticalDeadlines": { ... 符合Section模型的数据 ... },
# "KeyItemsDecisions": { ... 符合Section模型的数据 ... },
# "ImmediateActionItems": { ... 符合Section模型的数据 ... },
# "NextSteps": { ... 符合Section模型的数据 ... },
# "OtherImportantPoints": { ... 符合Section模型的数据 ... },
# "ClosingRemarks": { ... 符合Section模型的数据 ... }
# }
说明:
class SummaryResponse(BaseModel):
定义主摘要模型MeetingName: str
:会议名称的简单字符串字段SectionSummary: Section
、CriticalDeadlines: Section
等:这些字段指定每个键必须包含完全匹配我们之前定义的Section
蓝图的数据
因此,SummaryResponse
蓝图要求一个会议名称
(字符串)和七个特定部分
每个部分都必须有标题(字符串)和块列表,每个块必须有id、类型、内容和颜色(都是字符串)。这为摘要数据创建了严格的多层结构。
🎢后端如何使用Pydantic模型
现在我们有了这些蓝图,后端如何应用它们?
- 定义API预期: 如第1章:后端API网关所示,像
TranscriptRequest
这样的Pydantic模型用于定义API端点接收数据的格式。FastAPI自动读取这些模型,将其用于请求验证和生成API文档 - 指导AI输出(使用Pydantic-AI): 我们后端的关键部分是
TranscriptProcessor
(详见第4章:转录处理逻辑和第5章:AI模型交互(Pydantic-AI代理))。该处理器使用pydantic-ai
库中的Agent
,专门配置为生成符合SummaryResponse
Pydantic模型的数据pydantic-ai
库帮助弥合AI模型的文本输出与我们期望的结构化数据之间的差距。它告诉AI:“生成类似JSON的文本,并确保该JSON符合特定的SummaryResponse
结构。”如果AI的初始输出不完全匹配,pydantic-ai
代理通常会尝试修复或重试,直到获得有效数据
- 验证: 当
TranscriptProcessor
获取AI的输出时,会立即将其解析为SummaryResponse
Pydantic对象。如果AI生成的数据无效(不符合SummaryResponse
蓝图),Pydantic会立即抛出验证错误。这防止了错误数据进入系统或保存到数据库管理 - 序列化: 后端将摘要数据作为
SummaryResponse
Pydantic对象后,可以轻松转换为JSON格式通过API网关发送回前端。Pydantic有内置方法(model_dump_json()
)实现此功能。FastAPI在从端点函数返回Pydantic模型对象时也会自动处理
下面是展示AI处理和Pydantic作用的简化流程:
该流程图展示了AI的原始文本输出如何
通过
Pydantic-AI代理
适应SummaryResponse
蓝图创建结构化Pydantic对象
FastAPI随后可以轻松将其转换为JSON并发送至前端。
使用Pydantic模型的优势
使用Pydantic模型定义数据结构具有显著优势:
- 定义清晰: 模型代码明确定义了数据的预期形态和类型
- 自动验证: Pydantic根据蓝图检查出入数据,尽早捕获错误
- 提升代码可读性: 使用明确定义的Pydantic对象的代码比传递不确定内容的字典更易理解
- 轻松序列化/反序列化: Python对象与JSON之间的转换(API和数据库常用)由Pydantic和FastAPI自动处理
- 自文档化: Pydantic模型直接贡献于FastAPI在
/docs
生成的交互式API文档,使文档更准确且易于维护
通过定义
SummaryResponse
及其嵌套的Section
和Block
模型,我们为AI生成的摘要创建了健壮且可预测的格式
使后端工作更轻松,并确保前端始终接收可处理的数据。
结论
在本章中,我们探讨了meeting-minutes
项目如何使用Pydantic库定义AI生成会议摘要的结构。
我们了解了像Block
、Section
和顶层SummaryResponse
这样的模型如何作为蓝图来规定必填字段和数据类型。 (即底层调用Pydantic库
)
-
使用Pydantic可以确保摘要数据始终遵循可预测的格式,这对验证、后端代码的易用性以及通过后端API网关与前端可靠通信至关重要(如第1章所述)。
-
这也直接关联到第2章描述的API文档中的数据格式规范。
现在我们已经理解了摘要的预期
输出
结构,接下来的问题是:如何实际处理原始会议转录文本来输入
生成符合SummaryResponse
蓝图的数据?
这涉及转录分析的核心逻辑。
在下一章中,我们将深入探讨转录处理逻辑,它负责获取原始文本输入并生成由Pydantic模型定义的结构化数据。
第4章:转录处理逻辑
补充
Pydantic库的底层调用关系
在智能创作助手的模型架构中,Block
、Section
和SummaryResponse
等模型通常基于Pydantic库实现数据验证和序列化。Pydantic提供了类型注解、数据校验和自动生成文档的功能,适合构建结构化数据模型。
模型与Pydantic的关系
Block
、Section
等组件通常定义为Pydantic的BaseModel
子类,利用其类型系统确保输入输出的数据结构符合预期。例如:
from pydantic import BaseModelclass Block(BaseModel):content: strtype: str
SummaryResponse
作为顶层响应模型,可能嵌套其他Pydantic模型,形成层次化结构。Pydantic的嵌套模型能力支持复杂数据关系的描述和验证。
Pydantic的核心优势
- 数据验证:运行时自动检查字段类型和约束条件,如字符串长度、数值范围等。
- 序列化:提供
model_dump()
和model_dump_json()
等方法,方便与JSON等格式互转。 - 文档生成:结合类型注解自动生成OpenAPI/Swagger文档,适合API开发场景。
其他可能的底层依赖
虽然Pydantic是常见选择,但具体实现可能因框架而异。FastAPI等框架默认集成Pydantic
,而其他系统可能采用自定义验证逻辑或替代库(如marshmallow
)。
需结合具体代码库或文档确认实际调用关系。
marshmallow库
marshmallow
是一个Python库,用于将复杂的数据类型(如对象、字典)与简单数据类型(如JSON)相互转换,常用于API开发中的数据验证和序列化。
通俗讲:它像数据的“翻译官”,确保输入输出格式
正确且干净。
marshmallow库 vs Pydantic库
功能定位
-
marshmallow:
专注于
数据序列化/反序列化,适合API请求/响应数据的格式转换。 -
Pydantic:
强调数据验证
和类型提示,适合构建数据模型并自动处理类型转换。
典型场景
-
marshmallow:常见于
Flask
等框架的JSON数据处理
。更轻量 -
Pydantic:多用于
FastAPI的请求参数
验证和配置管理。更多功能
开发体验
-
marshmallow:
需显式定义序列化规则
。 -
Pydantic:利用Python类型注解自动生成验证逻辑。
一般方案
优先使用 Pydantic 的完整功能链(如 BaseModel + validator + json()
),减少依赖混合库带来的维护成本
。
example:
若需 Marshmallow 的特定功能(如自定义验证逻辑),可单独在部分逻辑中使用。
Pydantic 的 validator 装饰器通常能满足类似需求。