☰
BentoML 输入输出类型(IO Types)完全指南:定义 Service API 数据契约
2026/9/25 2:20:09 网站建设 项目流程
  • 模型推理服务
  • 人工智能
  • 后端
  • 大模型
  • MLOps
  • LLMOps

【免费下载链接】BentoML

The easiest way to serve AI apps and models - Build Model Inference APIs, Job queues, LLM apps, Multi-model pipelines, and more!

项目地址:https://gitcode.com/gh_mirrors/be/BentoML
点击查看免费下载

本文围绕 BentoML 官方文档 iotypes.rst 展开,系统讲解如何通过 Python 类型注解为 BentoML Service 的 API 定义输入输出(IO)类型。你将掌握标准 Python 类型、Pydantic 模型、张量(numpy/torch/tensorflow)、Pandas DataFrame、PIL 图像与pathlib.Path文件类型、根输入(Root input)以及复合类型的完整用法,并了解 BentoML 内置的bentoml.validators校验器与底层实现原理,从而直接写出可被 BentoML 客户端与 UI 正确消费的 API 数据契约。

概述:为什么 IO 类型如此重要

在 BentoML 中创建 Service 时,必须为 Service 的每个 API 明确指定输入和输出(IO)类型。这些类型决定了 Service API 的逻辑形态,引导数据在 Service 内部和外部的流动方式。BentoML 支持 Python 常见的数据类型、Pydantic 类型以及机器学习(ML)工作流专属的类型,这使得 BentoML Service 能够无缝对接不同的数据源和 ML 框架。

支持的类型总体分为四类:

  • 标准 Python 类型:str、int、float、boolean、list、dict等基础类型;
  • Pydantic 字段类型:借助 Pydantic 提供结构化、可校验的复杂数据模式;
  • ML 专属类型:numpy.ndarray、torch.Tensor、tensorflow.Tensor(张量数据)、pandas.DataFrame(表格数据)、PIL.Image.Image(图像数据)、pathlib.Path(文件路径);
  • 根输入(Root input):允许 API 只接收一个仅按位置传递(positional-only)的参数,请求体中无需任何 key。

你通过 Python 类型注解来声明每个 API 端点的预期输入输出类型。这不仅能按声明的 schema 校验数据,还能增强代码的可读性。类型注解在生成 API、BentoML 客户端和 UI 组件时扮演关键角色,确保与 Service 的交互一致且可预期。此外,还可以用pydantic.Field为参数补充默认值与描述等附加信息,提升 API 的可用性并提供基础文档。

从实现层面看,@bentoml.api装饰器会为每个 API 方法构造APIMethod对象,其input_spec与output_spec均由 decorators.py 中的api()包装,并通过IODescriptor.from_input/from_output从函数签名中动态推断出来(见 io_models.py)。也就是说,你在方法签名里写下的每一个注解,都会被反射式地转换成请求解析与响应序列化的真实 schema。

定义 API Schema:各类类型实战

标准 Python 类型

字符串、整数、浮点数、布尔值、列表和字典等标准类型最常用于简单数据结构,可以轻松集成到 Service 中。以下示例展示了带默认值与描述的标准类型参数:

from pydantic import Field import bentoml @bentoml.service class LanguageModel: @bentoml.api def generate( self, prompt: str = Field(description="The prompt text"), temperature: float = Field(default=0.0, description="A sampling temperature between 0 and 2"), max_tokens: int = Field(default=1000, description="max tokens to use"), ) -> Generator[str, None, None]: # Implementation of the language generation model ...

示例要点:

  • 使用str、float、int等标准类型注解声明每个参数的期望类型;
  • 返回类型是一个Generator,表示响应可以流式输出。BentoML 检测到生成器函数后会将output_spec的media_type自动设置为text/event-stream(见 method.py),并在IO层通过StreamingResponse逐块发送数据(见 io_models.py);
  • pydantic.Field用于设置默认值并为参数提供描述,这些描述最终会进入 OpenAPI schema,成为 API 文档的一部分。

示例值与可空输入

可以为接受示例值或可空字段的 API 定义输入。设置示例值使用pydantic.Field的examples参数:

from pydantic import Field import bentoml @bentoml.service class IrisClassifier: @bentoml.api def classify(self, input: np.ndarray = Field(examples=[[0.1, 0.4, 0.2, 1.0]]) -> np.ndarray: ...

处理可空输入则使用Optional:

from pydantic import Field from typing import Optional import bentoml @bentoml.service class LanguageModel: @bentoml.api def generate( self, prompt: int = Field(description="The prompt text"), temperature: Optional[float] = Field(default=None, description="A sampling temperature between 0 and 2"), max_tokens: Optional[float] = Field(default=None, description="max tokens to use"), ) -> Generator[str, None, None]: ...

在LanguageModel中,temperature和max_tokens被标记为Optional,意味着它们可以是None。使用Optional类型时必须提供默认值(此处为default=None)。通用的 Union 类型目前不受支持——这一点在源码中有明确印证:IOMixin.__pydantic_init_subclass__只放行X | None这种恰好两个成员且其一为None的联合类型,其他 Union 会直接抛出TypeError(见 io_models.py)。

Pydantic 模型

Pydantic 模型支持更结构化、带校验的数据,特别适合需要严格校验复杂数据结构的场景。以下示例定义了一个广告文案生成服务的结构化输入:

from pydantic import BaseModel, Field import bentoml # Define a Pydantic model for structured data input class AdsGenerationParams(BaseModel): prompt: str = Field(description="The prompt text") industry: str = Field(description="The industry the company belongs to") target_audience: str = Field(description="Target audience for the advertisement") temperature: float = Field(default=0.0, description="A sampling temperature between 0 and 2") @bentoml.service class AdsWriter: @bentoml.api def generate(self, params: AdsGenerationParams) -> str: # Implementation logic ...

AdsGenerationParams定义了输入数据的结构与校验规则:每个字段都标注了类型,可以包含默认值和描述。Pydantic 会自动按该 schema 校验传入数据,若数据不符合 schema,会在方法执行前抛出错误。

你还可以把 Pydantic 模型直接作为 Service API 的顶层输入(top level),无需将 payload 包装在某个 key 之下:

from pydantic import BaseModel, Field import typing as t import bentoml class AdsGenerationParams(BaseModel): prompt: str = Field(description="The prompt text") industry: str = Field(description="The industry the company belongs to") target_audience: str = Field(description="Target audience for the advertisement") temperature: float = Field(default=0.0, description="A sampling temperature between 0 and 2") @bentoml.service class AdsWriter: @bentoml.api(input_spec=AdsGenerationParams) def generate(self, **params: t.Any) -> str: # Access parameters from the request prompt = params['prompt'] industry = params['industry'] target_audience = params['target_audience'] temperature = params['temperature'] # Use the parameters in your Service logic ...

此时,请求中经过校验与解析的所有字段会以关键字参数的形式进入params字典,可以直接按AdsGenerationParams中定义的字段名作为 key 访问。从源码看,**params会被IODescriptor.from_input识别为VAR_KEYWORD参数并映射为内部kwargs字段(见 io_models.py);而显式传入input_spec时,method.py 中的_io_descriptor_converter会直接复用该模型作为输入描述符。

注意:Pydantic 的BaseModel只支持 Python 内置类型作为字段类型。需要支持numpy.ndarray、pandas.DataFrame、torch.Tensor等类型时,应改用bentoml.IODescriptor:

import bentoml class MyInputParams(bentoml.IODescriptor): data: np.ndarray[tuple[int], np.dtype[np.float16]]

bentoml.IODescriptor本质上是IOMixin + BaseModel的组合(见 io_models.py),它通过__get_pydantic_core_schema__在 Pydantic 校验链路中注入 BentoML 对张量、DataFrame、图像、文件等类型的自定义编解码逻辑。

文件(Files)

使用pathlib.Path处理文件输入和输出,适用于处理音频、图像、文档等文件型数据的 Service。下面是一个接受Path对象作为输入(指向一个音频文件)的简单示例:

from pathlib import Path import bentoml @bentoml.service class WhisperX: @bentoml.api def to_text(self, audio: Path) -> str: # Implementation for converting audio files to text ...

要限制文件类型(例如只接受音频),可以使用ContentType校验器配合Annotated类型。例如让 API 方法只接受 MP3 音频文件:

from pathlib import Path from bentoml.validators import ContentType from typing import Annotated # Python 3.9 or above from typing_extensions import Annotated # Older than 3.9 import bentoml @bentoml.service class WhisperX: @bentoml.api def to_text(self, audio: Annotated[Path, ContentType("audio/mp3")]) -> str: ...

若要以路径形式输出文件,可以使用context.temp_dir为每个请求提供独立临时目录并存放输出文件。bentoml.Context在内部通过request_temp_dir()为每个请求从TempfilePool获取唯一的临时目录,并在请求结束时自动回收(见 context.py 与 context.py):

from pathlib import Path import bentoml @bentoml.service class Vits: @bentoml.api def to_speech(self, text: str, context: bentoml.Context) -> Path: # Example text-to-speech synthesis implementation audio_bytes = self.tts.synthesize(text) # Writing the audio bytes to a file in the temporary directory with open(Path(context.temp_dir) / "output.mp3", "wb") as f: f.write(audio_bytes) # Returning the path to the generated audio file directly return Path(context.temp_dir) / "output.mp3"

当方法返回指向生成文件的Path对象时,BentoML 会将该文件序列化并包含在响应中发送给客户端。底层实现中,IO.to_http_response会对Path类型输出自动使用FileResponse,并根据 MIME 类型决定是内联展示(如image/*用inline)还是作为附件下载(见 io_models.py)。

更多文件处理的实用示例:

向文件追加字符串

from pathlib import Path from bentoml.validators import ContentType from typing import Annotated # Python 3.9 or above from typing_extensions import Annotated # Older than 3.9 import bentoml @bentoml.service class AppendStringToFile: @bentoml.api() def append_string_to_eof( self, context: bentoml.Context, txt_file: Annotated[Path, ContentType("text/plain")], input_string: str, ) -> Annotated[Path, ContentType("text/plain")]: with open(txt_file, "a") as file: file.write(input_string) return txt_file

把 PDF 的第一页转换为图像

from bentoml.validators import ContentType from typing import Annotated # Python 3.9 or above from typing_extensions import Annotated # Older than 3.9 from PIL import Image as im import bentoml @bentoml.service class PDFtoImage: @bentoml.api def pdf_first_page_as_image( self, pdf: Annotated[Path, ContentType("application/pdf")], ) -> Image: from pdf2image import convert_from_path pages = convert_from_path(pdf) return pages[0].resize(pages[0].size, im.ANTIALIAS)

加速音频文件

from pathlib import Path from bentoml.validators import ContentType from typing import Annotated # Python 3.9 or above from typing_extensions import Annotated # Older than 3.9 import bentoml @bentoml.service class AudioSpeedUp: @bentoml.api def speed_up_audio( self, context: bentoml.Context, audio: Annotated[Path, ContentType("audio/mpeg")], velocity: float, ) -> Annotated[Path, ContentType("audio/mp3")]: import os from pydub import AudioSegment output_path = os.path.join(context.temp_dir, "output.mp3") sound = AudioSegment.from_file(audio) sound = sound.speedup(velocity) sound.export(output_path, format="mp3") return Path(output_path)

如果不想把临时文件落盘,可以直接返回bytes而不是pathlib.Path,并用ContentType正确标注类型。这对于实时生成数据的 Service 更加高效。需要注意的是,ContentType校验器在接收文件时会校验实际上传的媒体类型是否与声明匹配(使用fnmatch通配匹配),不匹配会抛出ValueError(见 validators.py)。

张量(Tensors)

BentoML 支持numpy.ndarray、torch.Tensor、tensorflow.Tensor等多种张量类型。还可以使用bentoml.Shape与bentoml.DType校验器(分别对应bentoml.validators.Shape、bentoml.validators.DType)来强制张量输入的具体形状与数据类型:

import torch from bentoml.validators import Shape, DType from typing import Annotated # Python 3.9 or above from typing_extensions import Annotated # Older than 3.9 from pydantic import Field import bentoml @bentoml.service class IrisClassifier: @bentoml.api def classify( self, input: Annotated[torch.Tensor, Shape((1, 4)), DType("float32")] = Field(description="A 1x4 tensor with float32 dtype") ) -> np.ndarray: ...

示例解读:

  • classify方法期望torch.Tensor输入;
  • Annotated结合Shape与DType校验器,指定期望张量的形状为(1, 4)、数据类型为float32;
  • pydantic.Field为输入参数提供附加描述,提升 API 可读性。

张量校验的底层逻辑在TensorSchema中实现:JSON 模式下张量会被序列化为嵌套数组,校验时会按format(numpy-array/tf-tensor/torch-tensor)分派到对应框架构造张量,并执行reshape与 dtype 转换;序列化时若设备是 GPU 会自动cpu()后再转 numpy(见 validators.py)。

表格数据(Tabular)

Pandas DataFrame 是机器学习中最常用的表格数据处理结构。BentoML 支持 Pandas DataFrame 输入,并允许用校验器注解来确保数据符合预期结构:

from typing import Annotated # Python 3.9 or above from typing_extensions import Annotated # Older than 3.9 import pandas as pd from bentoml.validators import DataframeSchema import bentoml @bentoml.service class IrisClassifier: @bentoml.api def classify( self, input: Annotated[pd.DataFrame, DataframeSchema(orient="records", columns=["petal_length", "petal_width"]) ) -> int: # Classification logic using the input DataFrame ...

示例解读:

  • classify方法接受 Pandas DataFrame 作为输入;
  • Annotated结合DataframeSchema指定 DataFrame 的期望方向和列;
    • orient="records"表示 DataFrame 期望以记录导向(record-oriented)格式传入;
    • columns=["petal_length", "petal_width"]指定 DataFrame 的期望列。

DataframeSchema校验器支持以下两种方向(orient),决定 API 接收到的数据结构:

  • records:每一行表示为一个字典,key 为列名;
  • columns:数据按列组织,字典的每个 key 代表一列,对应值为该列的取值列表。

在源码实现中,records方向使用df.to_dict(orient="records")序列化、columns方向使用df.to_dict(orient="list");校验侧则通过pd.DataFrame(obj, columns=self.columns)重构 DataFrame(见 validators.py)。

图像(Images)

BentoML Service 可以通过PIL.Image.Image和pathlib.Path处理图像。

方式一:直接传递PIL.Image.Image对象

from PIL import Image as im from PIL.Image import Image import bentoml @bentoml.service class ImageResize: @bentoml.api def generate(self, image: Image, height: int = 64, width: int = 64) -> Image: size = height, width return image.resize(size, im.LANCZOS)

方式二:使用pathlib.Path+ContentType处理图像文件

from pathlib import Path from typing import Annotated # Python 3.9 or above from typing_extensions import Annotated # Older than 3.9 from bentoml.validators import ContentType import bentoml @bentoml.service class MnistPredictor: @bentoml.api def infer(self, input: Annotated[Path, ContentType('image/jpeg')]) -> int: ...

PIL 图像的编解码由PILImageEncoder实现:接收bytes、UploadFile、文件对象或图像对象,统一用PILImage.open解码;输出时保存为图片原始格式(缺省 PNG)并返回二进制(见 validators.py)。

根输入(Root input)

根输入是一种特殊的输入类型:API 请求体中不需要 key,输入数据本身直接作为请求体传入。这对处理图像、音频或原始文本等二进制数据特别有用。

定义根输入使用 Python 的仅限位置参数(positional-only arguments),即函数签名中/之前的参数:

重要限制

  • 最多只能有一个仅限位置参数(/之前的参数);
  • 一旦指定了仅限位置参数,除bentoml.Context之外不允许再有其他参数。

示例实现:

from PIL import Image import bentoml @bentoml.service class ImageProcessor: @bentoml.api def upload_image(self, image: Image.Image, /) -> int: # Process the image and return a result ...

在这个示例中,upload_image的image参数是仅限位置参数,意味着调用时必须不带 key 传递。客户端必须把图像数据直接放在 HTTP 请求体中,不带任何 JSON 包装。

使用curl调用示例:

curl -XPOST -sL http://localhost:3000/upload_image --data-binary=@myimage.png

对应的 HTTP 请求如下:

POST /upload_image HTTP/1.1 Content-Type: image/png <image binary>

使用 BentoML 客户端调用带根输入的 API 时,必须用位置参数且不能指定参数名:

client = bentoml.SyncClient("http://localhost:3000") image_path = Path("demo.png") result = client.upload_image(image_path) # CORRECT result = client.upload_image(image=image_path) # WRONG

源码佐证:IODescriptor.from_input在检测到POSITIONAL_ONLY参数时会将其包装为IORootModel并打上__root_input__标记(见 io_models.py);单元测试 test_decorators.py 同时验证了根输入的正确定义,以及"多个仅限位置参数"或"仅限位置参数后还有其他参数"均会抛出TypeError的非法用法。

复合类型(Compound)

高级场景中,单一数据类型往往不够用,复杂场景可能需要组合多种数据类型。例如同时处理图像与 JSON 输入:

from pydantic import BaseModel, Field from PIL import Image as PILImage import bentoml class ImageMetadata(BaseModel): description: str = Field(description="Description of the image") timestamp: str = Field(description="Timestamp of when the image was captured") @bentoml.service class ImageProcessingService: @bentoml.api def process_image(self, image: PILImage, metadata: ImageMetadata) -> dict: # Implementation for processing the image and metadata ...

示例中PILImage处理图像数据,而 Pydantic 模型ImageMetadata处理 JSON 输入。BentoML 在检测到多个字段中存在文件类型时,会将这些 API 的请求媒体类型设为multipart/form-data,非文件字段则以application/json编码传输(见 io_models.py 与 method.py)。

BentoML 还支持复杂类型的列表输入与输出,例如图像和文件路径的列表。以下示例同时处理一批图像和一批文件路径:

from PIL import Image as PILImage from pathlib import Path from typing import List, Dict import bentoml @bentoml.service class BatchImageService: @bentoml.api def enhance_images(self, images: List[PILImage]) -> PILImage: # Process images and return a single image ... @bentoml.api def process_files(self, files: List[Path]) -> List[Dict]: # Process files and return a list of dictionaries ...

当前限制:BentoML 目前不支持输出包含多个原始二进制数据,也不支持将原始二进制数据(如图像或文件)与普通字典数据直接组合输出。

数据校验(Validate data)

对输入数据做正确校验对 BentoML Service 至关重要,它能确保被处理的数据格式符合预期、达到必要的质量标准。BentoML 提供了一套简单的校验机制,并且默认支持 Pydantic 提供的全部校验特性,可对数据的结构、类型和约束进行全面检查。

以下示例使用annotated_types的约束注解:

from typing import Annotated # Python 3.9 or above from typing_extensions import Annotated # older than 3.9 from annotated_types import Ge, Lt, Gt, MultipleOf, MaxLen import bentoml @bentoml.service class LLMPredictor: @bentoml.api def predict( self, prompt: Annotated[str, MaxLen(1000)], temperature: Annotated[float, Ge(0), Lt(2)], max_tokens: Annotated[int, Gt(0), MultipleOf(100)] ) -> int: ...

示例中的校验器确保:

  • prompt字符串长度不超过 1000 个字符(MaxLen(1000));
  • temperature取值介于 0 和 2 之间(Ge(0)且Lt(2));
  • max_tokens是大于 0 且为 100 的倍数(Gt(0)且MultipleOf(100))。

常用 ML 类型的校验

BentoML 为张量、DataFrame 等常见 ML 数据类型提供校验能力,确保喂给模型的数据完整可靠。上文各节已给出这些数据类型的校验示例。下表汇总了 BentoML 额外支持的、专门面向 ML 场景的输入输出类型,以及每种类型可用的注解(用于进一步细化与校验数据):

类型名称说明允许的注解
numpy.ndarray用于数值数据的多维数组,常用于 ML 任务bentoml.validators.Shape、bentoml.validators.DType
torch.TensorPyTorch 中表示张量数据的张量类型bentoml.validators.Shape、bentoml.validators.DType
tensorflow.TensorTensorFlow 中表示张量数据的张量类型bentoml.validators.Shape、bentoml.validators.DType
pandas.DataFrame表格数据结构,常用于数据分析bentoml.validators.DataframeSchema
PIL.Image.ImagePIL 库的图像数据类型,用于图像处理bentoml.validators.ContentType
pathlib.Path文件路径,用于文件输入与输出bentoml.validators.ContentType

此外,BentoML 还支持 Pydantic 的所有注解类型进行校验。bentoml.validators模块的导出定义可在 validators.py 中查看,其核心实现(ContentType、Shape、DType、DataframeSchema、TensorSchema、FileSchema、PILImageEncoder)位于 src/_bentoml_sdk/validators.py。

附录:输入/输出类型速查表

输入类型

类型输入注解HTTP 内容类型HTTP 请求体示例
JSONpredict(self, input1: str, input2: int)application/jsoncurl -XPOST -d '{ "input1": "input_value", "input2": 2 }'
张量predict(self, input1: torch.Tensor)、predict(self, input1: numpy.ndarray)、predict(self, input1: tensorflow.Tensor)application/jsoncurl -XPOST -d '{ "input1": [[1, 1, 1, 1], [2, 2, 2, 2]] }'
表格数据predict(self, input1: pandas.DataFrame)application/jsoncurl -XPOST -d '{ "input1": [{"col1": 1, "col2": 2}, {"col1": 1, "col2": 2}] }'
图像predict(self, input1: str, input2: PIL.Image.Image)multipart/form-data路径:curl -XPOST -F input1="enter_your_prompt_here" -F input2="image=@/path/to/image.jpg";URL:curl -XPOST -F input1="enter_your_prompt_here" -F input2="http://domain/path/to/image.jpg"
文件predict(self, input1: str, input2: pathlib.Path)multipart/form-data路径:curl -XPOST -F input1="enter_your_prompt_here" -F input2="image=@/path/to/image.jpg";URL:curl -XPOST -F input1="enter_your_prompt_here" -F input2="http://domain/path/to/file.mp3"

输出类型

类型输出注解HTTP 内容类型HTTP 响应体示例
纯文本-> str、-> bytestext/plainstring
JSON-> int、-> float、-> dict、-> listapplication/json3、1.1、{}、[]
张量-> torch.Tensor、-> numpy.ndarray、-> tensorflow.Tensorapplication/json[[1, 1, 1, 1], [2, 2, 2, 2]]
表格数据-> pandas.DataFrameapplication/json[{ "col1": 1, "col2": 2 }, { "col1": 1, "col2": 2 }]
图像-> PIL.Image.Imageimage/<auto MIME type>二进制 body
文件-> pathlib.Path<auto MIME type>二进制 body
自定义文件-> Annotated[pathlib.Path, ContentType("custom-type")]custom-type二进制 body

这些 MIME 类型的自动推导逻辑可在IOMixin.mime_type()中找到(见 io_models.py):根输入下字符串类型默认text/plain,ContentType声明的文件类型返回其声明值,图像/音频/视频分别映射到image/*、audio/*、video/*,其余文件默认application/octet-stream。

延伸阅读

  • BentoML Service 定义:了解@bentoml.service与 API 方法组织的完整机制;
  • BentoML 客户端:掌握如何通过同步/异步客户端正确调用带各类 IO 类型的 API;
  • SDK 参考:查看bentoml.api、bentoml.IODescriptor与bentoml.validators的完整 API 说明;
  • IO 校验器单元测试:包含根输入、张量注解、DataFrame 注解的实证用例;
  • HTTP Server 端到端测试:覆盖 multipart 图像与文件输入的真实请求场景。
  • 模型推理服务
  • 人工智能
  • 后端
  • 大模型
  • MLOps
  • LLMOps

【免费下载链接】BentoML

The easiest way to serve AI apps and models - Build Model Inference APIs, Job queues, LLM apps, Multi-model pipelines, and more!

项目地址:https://gitcode.com/gh_mirrors/be/BentoML
点击查看免费下载

相关推荐

上一篇:Robotiq 开源项目教程
下一篇:视频转码API指南:深入浅出video-dev/video-transcoding-api

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询