使用 BentoML 部署 Outlines 结构化生成服务:从本地推理到 BentoCloud 生产部署
【免费下载链接】outlinesStructured Outputs项目地址: https://gitcode.com/GitHub_Trending/ou/outlines
本指南基于仓库 examples/bentoml/ 目录下的完整示例,讲解如何将 Outlines 的结构化输出能力封装为 BentoML Service,先在本地 GPU 上运行调试,再一键部署到 BentoCloud 托管推理平台。读完本文,你将掌握:把 Hugging Face 模型导入 BentoML Model Store、用@bentoml.service与@bentoml.api定义生成服务、通过 HTTP 调用 JSON Schema 约束生成接口,以及使用bentoml serve/bentoml deploy完成本地与云端发布。
整体思路:用 BentoML 托管 Outlines 的约束生成
BentoML 是一个面向 Python AI 应用的开源模型服务库,提供服务化、模型打包与生产部署所需的工具链。将它与 Outlines 结合,核心路径分为三步:
- 导入模型:从 Hugging Face 下载 LLM,写入 BentoML 的 Model Store,实现模型与代码的解耦管理;
- 定义服务:用 BentoML 装饰器把 Outlines 的生成逻辑封装成带 HTTP 端点的 Service;
- 运行与部署:本地用
bentoml serve调试,云端用bentoml deploy一键发布到 BentoCloud。
示例中的模型以 Mistral-7B-v0.1 为例,你也可以替换为任何其他兼容 transformers 的 LLM。完整可运行代码见 examples/bentoml/ 下的import_model.py、service.py、bentofile.yaml与requirements.txt。
导入模型到 BentoML Model Store
安装依赖
在虚拟环境中安装依赖,仓库中 examples/bentoml/requirements.txt 固定了示例所需的版本:
pip install -r requirements.txt该文件的关键依赖如下:
bentoml>=1.2.11 outlines==0.0.37 transformers==4.38.2 datasets==2.18.0 accelerate==0.27.2编写并运行导入脚本
将下面的代码保存为import_model.py(与仓库 examples/bentoml/import_model.py 一致),然后执行python import_model.py:
import bentoml MODEL_ID = "mistralai/Mistral-7B-v0.1" BENTO_MODEL_TAG = MODEL_ID.lower().replace("/", "--") def import_model(model_id, bento_model_tag): import torch from transformers import AutoModelForCausalLM, AutoTokenizer tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) model = AutoModelForCausalLM.from_pretrained( MODEL_ID, torch_dtype=torch.float16, low_cpu_mem_usage=True, ) with bentoml.models.create(bento_model_tag) as bento_model_ref: tokenizer.save_pretrained(bento_model_ref.path) model.save_pretrained(bento_model_ref.path) if __name__ == "__main__": import_model(MODEL_ID, BENTO_MODEL_TAG)要点说明:
- Tag 生成规则:
BENTO_MODEL_TAG = MODEL_ID.lower().replace("/", "--")把 Hugging Face 的组织/模型名转换成 BentoML 允许的标签格式(斜杠替换为双连字符),例如mistralai/Mistral-7B-v0.1→mistralai--mistral-7b-v0.1。后续service.py会通过bentoml.models.get(BENTO_MODEL_TAG)按此标签取回模型。 - 权重精度与内存:加载时指定
torch_dtype=torch.float16(半精度)与low_cpu_mem_usage=True,可显著降低显存占用并避免 CPU 内存峰值。 - Model Store 落盘:
bentoml.models.create(bento_model_tag)会在 Model Store 中创建一条模型记录,把 tokenizer 与权重一并保存到其目录下,使模型可脱离原始下载链接被服务直接引用。
注意:首次下载 Mistral-7B-v0.1 前,需要先在 Hugging Face 上接受该模型的使用条款,否则下载会失败。
校验导入结果
运行以下命令确认模型已进入 Model Store:
$ bentoml models list Tag Module Size Creation Time mistralai--mistral-7b-v0.1:m7lmf5ac2cmubnnz 13.49 GiB 2024-04-25 06:52:39看到类似输出即表示导入成功,记录下 Tag(含版本后缀)供服务加载使用。
定义 BentoML Service:封装 Outlines 生成接口
服务声明:@bentoml.service
service.py首先用@bentoml.service装饰一个普通类(这里叫Outlines),并通过装饰器参数声明流量与资源配置:
import typing as t import bentoml from import_model import BENTO_MODEL_TAG @bentoml.service( traffic={ "timeout": 300, }, resources={ "gpu": 1, "gpu_type": "nvidia-l4", }, ) class Outlines: bento_model_ref = bentoml.models.get(BENTO_MODEL_TAG) def __init__(self) -> None: import outlines import torch from transformers import AutoModelForCausalLM, AutoTokenizer # Load tokenizer and model from the BentoML model reference path hf_tokenizer = AutoTokenizer.from_pretrained(self.bento_model_ref.path) hf_model = AutoModelForCausalLM.from_pretrained( self.bento_model_ref.path, torch_dtype=torch.float16, low_cpu_mem_usage=True, device_map="cuda" ) # Then use the loaded model with Outlines self.model = outlines.from_transformers(hf_model, hf_tokenizer) ...这里有几个关键设计:
- 配置项语义:
traffic.timeout为请求超时时间(秒),设为 300 以容纳长文本生成;resources.gpu指定显卡数量,resources.gpu_type指定云端 GPU 型号(示例为 24GB 显存的nvidia-l4)。这些资源声明在 BentoCloud 部署时生效,用于调度对应的 GPU 实例。 - 模型引用:
bento_model_ref = bentoml.models.get(BENTO_MODEL_TAG)在类体内直接解析 Model Store 中的模型标签,拿到模型目录路径bento_model_ref.path,随后在__init__中从该路径加载 tokenizer 与权重。 - 接入 Outlines:
outlines.from_transformers(hf_model, hf_tokenizer)把 transformers 模型与分词器包装成 Outlines 的可控生成模型。从源码看,from_transformers 会根据传入的是PreTrainedTokenizer还是ProcessorMixin,分别返回Transformers或TransformersMultiModal实例,这里传入 tokenizer 因此得到文本生成模型。
生成端点:@bentoml.api
接下来用@bentoml.api装饰generate方法,把它暴露为 HTTP 端点:
... @bentoml.api async def generate( self, prompt: str = "Give me a character description.", json_schema: t.Optional[str] = DEFAULT_SCHEMA, ) -> t.Dict[str, t.Any]: import json import outlines from outlines.types import JsonSchema generator = outlines.Generator(self.model, JsonSchema(json_schema)) character = generator(prompt) return json.loads(character)对应仓库 examples/bentoml/service.py 中默认使用的 JSON Schema 如下(一个 RPG 角色对象,包含字符串、整数与枚举类型字段):
DEFAULT_SCHEMA = """{ "title": "Character", "type": "object", "properties": { "name": { "title": "Name", "maxLength": 10, "type": "string" }, "age": { "title": "Age", "type": "integer" }, "armor": {"$ref": "#/definitions/Armor"}, "weapon": {"$ref": "#/definitions/Weapon"}, "strength": { "title": "Strength", "type": "integer" } }, "required": ["name", "age", "armor", "weapon", "strength"], "definitions": { "Armor": { "title": "Armor", "description": "An enumeration.", "enum": ["leather", "chainmail", "plate"], "type": "string" }, "Weapon": { "title": "Weapon", "description": "An enumeration.", "enum": ["sword", "axe", "mace", "spear", "bow", "crossbow"], "type": "string" } } }"""该 Schema 展示了结构化约束的典型形态:name限制最长 10 字符、age/strength必须是整数、armor与weapon通过$ref引用definitions中的枚举,模型只能在给定枚举值中选择。这样模型输出天然满足 Schema,无需事后解析纠错。
端点行为与约束原理
- HTTP 语义:
generate接受 JSON 请求体,字段为prompt和可选的json_schema(缺省时使用上面的DEFAULT_SCHEMA)。函数签名中的类型提示(str、t.Optional[str]、t.Dict[str, t.Any])会被 BentoML 用于校验和转换入参、出参。返回前通过json.loads(character)把模型生成的 JSON 字符串解析为 Python 字典。 - 可扩展性:你可以在
Outlines类中继续用@bentoml.api装饰更多方法,定义任意数量的 HTTP 端点。 - 约束生成的底层链路:从源码看,Generator 是工厂函数:对支持可控生成的模型,会构造
SteerableGenerator,在其__init__中把JsonSchema输出类型翻译成 logits 处理器——见 src/outlines/generator.py 的SteerableGenerator.__init__,它调用get_json_schema_logits_processor构建约束处理器(具体实现分发到 xgrammar、llguidance 或 outlines_core 等后端,见 src/outlines/backends/init.py);每次调用生成时处理器会先reset()(generator.py),再传给模型执行受限采样。换言之,所谓"JSON Schema 约束"是在采样阶段通过 logits 掩码实现的,而非提示词工程。 - 关于两种写法:本文档正文中的
outlines.Generator(self.model, JsonSchema(json_schema))与仓库 examples/bentoml/service.py 里的outlines.Generator(self.model, outlines.json_schema(json_schema))等价——outlines.json_schema只是JsonSchema的工厂函数(见 src/outlines/types/dsl.py),两者都接受 Schema 字符串。注意from outlines.types import JsonSchema与顶层outlines.json_schema在 Outlines 顶层命名空间中均可导入(见 src/outlines/init.py)。
BentoML 构建配置
仓库中的 bentofile.yaml 定义了 Bento 的构建方式:
service: "service:Outlines" labels: owner: bentoml-team stage: demo include: - "*.py" python: requirements_txt: "./requirements.txt" lock_packages: falseservice:指定服务入口,格式为模块:类名,即service.py中的Outlines类;include:把目录下所有*.py文件打入 Bento 包(import_model.py中的BENTO_MODEL_TAG常量因此可用);python.requirements_txt:声明运行时依赖清单;lock_packages: false表示构建时不锁定传递依赖版本。
本地运行与调试
启动服务
在包含service.py与bentofile.yaml的目录下运行:
bentoml serve .服务启动后监听 http://localhost:3000,BentoML 会自动提供 Swagger UI 便于交互式调试,也可以用下面两种方式调用。
方式一:CURL
curl -X 'POST' \ 'http://localhost:3000/generate' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "prompt": "Give me a character description." }'方式二:Python 客户端
import bentoml with bentoml.SyncHTTPClient("http://localhost:3000") as client: response = client.generate( prompt="Give me a character description" ) print(response)预期输出
两种方式均返回符合 Schema 的 JSON,例如:
{ "name": "Aura", "age": 15, "armor": "plate", "weapon": "sword", "strength": 20 }注意armor的值必然是leather、chainmail、plate三者之一,weapon必然是六种武器之一——这正是 logits 层面约束生效的直接体现。
部署到 BentoCloud
服务在本地验证通过后,即可发布到 BentoCloud 获得托管、弹性伸缩与统一管理能力:
- 尚未注册的话,先在 BentoCloud 注册账号;
- 确保已登录 BentoCloud(配置好访问令牌);
- 在项目目录下执行一键部署:
bentoml deploy .部署完成后,应用会暴露一个公网 URL,直接通过该 URL 调用/generate端点即可,调用方式与本地完全一致。
提示:如果你希望部署到自有基础设施而非 BentoCloud,可以改用 BentoML 生成 OCI 兼容的容器镜像,在任何支持 OCI 镜像的平台上运行同样的服务。
小结
通过本文的四个步骤——导入模型到 Model Store、用装饰器定义 Service 与端点、bentoml serve本地调试、bentoml deploy云端发布——即可把 Outlines 的 JSON Schema 约束生成能力快速产品化。整套流程中,Outlines 负责在采样阶段把 Schema 编译为 logits 约束处理器(相关源码入口见 src/outlines/generator.py、src/outlines/backends/init.py),BentoML 负责模型管理、HTTP 服务与云端部署,两者各司其职。若需调整生成约束,只需修改json_schema入参或替换DEFAULT_SCHEMA;若需更换模型,更新MODEL_ID并重新执行导入脚本即可。完整示例代码可在 examples/bentoml/ 目录中直接查看或复用。
【免费下载链接】outlinesStructured Outputs项目地址: https://gitcode.com/GitHub_Trending/ou/outlines
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考