ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Ray Serve 应用构建器(Application Builder)指南:通过参数化灵活配置 Serve 应用

Ray Serve 应用构建器(Application Builder)指南:通过参数化灵活配置 Serve 应用 人工智能分布式训练强化学习任务调度模型推理服务【免费下载链接】rayRay is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.项目地址https://gitcode.com/gh_mirrors/ra/ray点击查看免费下载导读在 Ray Serve 中应用构建器application builder是连接部署代码与部署配置的桥梁它将一个接收参数字典或 Pydantic 模型的普通 Python 函数变成 Serve 应用的工厂从而让你无需修改代码即可通过serve run命令行或 YAML 配置文件动态传入模型权重路径、超参数等配置。读完本文你将掌握如何定义无类型与带类型的应用构建器、如何通过 CLI 和配置文件两种方式传参、如何使用 Pydantic 做参数校验以及如何利用同一构建器派生多个参数化应用和组合多个 Deployment 的实战模式。本文对应的仓库文档为 app-builder-guide.md完整示例代码见 app_builder.py。为什么需要应用构建器从.bind()到参数化在 Ray Serve 中一个应用Application是由一个或多个 Deployment 通过.bind()组合而成的。当应用存在可变参数时——例如trained model weights 的路径、要打印的消息、模型推理的超参数——最常见的做法是直接在 Python 代码里把这些参数传给 Deployment 的构造函数from ray import serve serve.deployment class HelloWorld: def __init__(self, message: str): self._message message HelloWorld.bind(Hello from code)这种写法用普通 Python 代码完成配置简单直观。但它的缺点是每当参数需要变化就必须修改并重新部署代码。在开发想快速试一个新训练的模型和生产同一份代码服务多个模型版本场景下这都不可接受。应用构建器正是为消除这一痛点而生的它定义一个builder 函数接收一个参数字典或 Pydantic 对象返回构建好的Application。之后参数可以从serve run命令行或配置文件外部注入代码本身保持零改动。从源码看这一机制的核心入口是 python/ray/serve/_private/api.py 中的call_user_app_builder_with_args_if_necessary()当传入的 builder 是函数而非已构建好的Application时它会校验函数签名、把参数字典转换为用户标注的 Pydantic 模型如果存在调用函数并强制校验返回值为Application类型。定义一个应用构建器最简单的应用构建器是一个接收Dict[str, str]并返回Application的普通函数。以下示例来自 doc/source/serve/doc_code/app_builder.py将代码保存为hello.py# hello.py from typing import Dict from ray import serve from ray.serve import Application serve.deployment class HelloWorld: def __init__(self, message: str): self._message message print(Message:, self._message) def __call__(self, request): return self._message def app_builder(args: Dict[str, str]) - Application: return HelloWorld.bind(args[message])关键点函数签名builder 必须且只能接收一个参数。从源码 python/ray/serve/_private/api.py 可见Ray 会通过inspect.signature检查参数个数若不是恰好一个参数会抛出TypeError提示 Application builder functions should take exactly one parameter...。返回类型builder 的返回值必须是Deployment.bind()产生的Application实例否则同样会抛出TypeError见 api.py。参数读取在无类型 builder 中args就是外部传入的字符串字典取值后传给HelloWorld.bind(...)作为构造函数参数。把这个 builder 函数的名字模块路径hello:app_builder作为serve run的 import path 或配置文件中的import_path即可由外部注入参数。若要避免手写类型转换和缺参校验逻辑推荐直接使用下文用 Pydantic 给参数定义类型一节中的带类型写法。关于传入参数为字典的说明CLI 传入的keyval参数会被解析成普通字典config 文件中的args字段同理。参数在构建时被解析并传给 builder你可以把任意 JSON 可序列化的值放进 config 的args中注意CLI 方式下所有值都是字符串config 方式则保留 YAML 的类型语义。通过serve run命令行传参在serve run中import path 之后以keyval的形式传入参数serve run hello:app_builder key1val1 key2val2参数会被收集为一个字典传递给 builder即上面命令等价于传入{key1: val1, key2: val2}。例如给上面hello.py中的HelloWorld应用传一条新消息% serve run hello:app_builder messageHello from CLI 2023-05-16 10:47:31,641 INFO scripts.py:404 -- Running import path: hello:app_builder. 2023-05-16 10:47:33,344 INFO worker.py:1615 -- Started a local Ray instance. View the dashboard at http://127.0.0.1:8265 (ServeController pid56826) INFO 2023-05-16 10:47:35,115 controller 56826 deployment_state.py:1244 - Deploying new version of deployment default_HelloWorld. (ServeController pid56826) INFO 2023-05-16 10:47:35,141 controller 56826 deployment_state.py:1483 - Adding 1 replica to deployment default_HelloWorld. (ProxyActor pid56828) INFO: Started server process [56828] (ServeReplica:default_HelloWorld pid56830) Message: Hello from CLI 2023-05-16 10:47:36,131 SUCC scripts.py:424 -- Deployed Serve app successfully.注意日志中(ServeReplica:default_HelloWorld pid56830) Message: Hello from CLI——消息从 Deployment 构造函数中打印出来证明参数确实被注入到了部署内部。CLI 侧的参数解析实现位于 python/ray/serve/scripts.py 的convert_args_to_dict()def convert_args_to_dict(args: Tuple[str]) - Dict[str, str]: args_dict dict() for arg in args: split arg.split(, maxsplit1) if len(split) ! 2 or len(split[1]) 0: raise click.ClickException( fInvalid application argument {arg}, must be of the form keyval. ) args_dict[split[0]] split[1] return args_dict几点实用提示参数必须以分隔键值且值不能为空字符串否则会报Invalid application argument错误使用maxsplit1切分因此值中允许包含字符如model_uris3://bucket/pathv2中的会被保留在值里CLI 方式下所有参数值都是字符串需要整数/布尔等类型时应在 builder 内自行转换或者改用 Pydantic 类型化 builder 自动完成转换。通过配置文件传参除了 CLI也可以在 Serve 部署配置文件的args字段中传参applications: - name: MyApp import_path: hello:app_builder args: message: Hello from configargs字段的定义见 python/ray/serve/schema.py它属于ApplicationConfigPydantic 模型类型为Dict默认值为{}其字段描述即Arguments that will be passed to the application builder.。运行配置文件的命令及输出% serve run config.yaml 2023-05-16 10:49:25,247 INFO scripts.py:351 -- Running config file: config.yaml. 2023-05-16 10:49:26,949 INFO worker.py:1615 -- Started a local Ray instance. View the dashboard at http://127.0.0.1:8265 2023-05-16 10:49:28,678 SUCC scripts.py:419 -- Submitted deploy config successfully. (ServeController pid57109) INFO 2023-05-16 10:49:28,676 controller 57109 controller.py:559 - Building application MyApp. (ProxyActor pid57111) INFO: Started server process [57111] (ServeController pid57109) INFO 2023-05-16 10:49:28,940 controller 57109 application_state.py:202 - Built application MyApp successfully. (ServeController pid57109) INFO 2023-05-16 10:49:28,942 controller 57109 deployment_state.py:1244 - Deploying new version of deployment MyApp_HelloWorld. (ServeController pid57109) INFO 2023-05-16 10:49:29,016 controller 57109 deployment_state.py:1483 - Adding 1 replica to deployment MyApp_HelloWorld. (ServeReplica:MyApp_HelloWorld pid57113) Message: Hello from config与 CLI 方式对比维度serve run传参配置文件传参参数格式命令行keyvalYAML 的args字段值类型全部为字符串保留 YAML 类型int、bool、list、dict 等适用场景快速开发、临时测试生产部署、版本管理、多应用编排部署方式直接以 import path 运行先经serve deploy/serve run config.yaml提交配置与 CLI 传参共享的底层路径两种方式最终殊途同归serve run在 scripts.py 中把 CLI 参数解析成字典后调用call_user_app_builder_with_args_if_necessary()config 文件方式则在 ServeController 构建应用时以相同路径处理。也就是说无论参数来自命令行还是 YAMLbuilder 函数接收到的都是同一个字典后续校验和调用逻辑完全一致。用 Pydantic 给参数定义类型Pydantic v1 弃用提醒注意Pydantic v1 已被弃用Ray 将在版本 2.56 中移除对它的支持。如果你还在使用 Pydantic v1请通过pip install -U pydantic升级到 Pydantic v2。详见相关 issue 说明。类型化 builder 的定义手写args[message]这种取值方式需要自己处理缺参、类型转换等问题。更优雅的做法是定义一个 Pydantic 模型并把它作为 builder唯一参数的类型注解# hello.py from pydantic import BaseModel from ray import serve from ray.serve import Application class HelloWorldArgs(BaseModel): message: str serve.deployment class HelloWorld: def __init__(self, message: str): self._message message print(Message:, self._message) def __call__(self, request): return self._message def typed_app_builder(args: HelloWorldArgs) - Application: return HelloWorld.bind(args.message)使用方法与无类型版本完全一致参数传递方式不变% serve run hello:typed_app_builder messageHello from CLI 2023-05-16 10:47:31,641 INFO scripts.py:404 -- Running import path: hello:typed_app_builder. 2023-05-16 10:47:33,344 INFO worker.py:1615 -- Started a local Ray instance. View the dashboard at http://127.0.0.1:8265 (ServeController pid56826) INFO 2023-05-16 10:47:35,115 controller 56826 deployment_state.py:1244 - Deploying new version of deployment default_HelloWorld. (ServeController pid56826) INFO 2023-05-16 10:47:35,141 controller 56826 deployment_state.py:1483 - Adding 1 replica to deployment default_HelloWorld. (ProxyActor pid56828) INFO: Started server process [56828] (ServeReplica:default_HelloWorld pid56830) Message: Hello from CLI 2023-05-16 10:47:36,131 SUCC scripts.py:424 -- Deployed Serve app successfully.底层原理Pydantic 自动校验Ray 会检测 builder 唯一参数的类型注解若它是BaseModel的子类就把传入的参数字典交给 Pydantic 构造模型实例。当前版本使用model_validate()Pydantic v2 语义相关代码见 python/ray/serve/_private/api.py# If the sole argument to the builder is a pydantic model, convert the args dict to # that model. This will perform standard pydantic validation (e.g., raise an # exception if required fields are missing). param signature.parameters[list(signature.parameters.keys())[0]] if inspect.isclass(param.annotation) and issubclass(param.annotation, BaseModel): args param.annotation.model_validate(args)这意味着你免费获得的能力包括必填字段校验HelloWorldArgs中的message没有默认值若外部漏传Pydantic 会抛出ValidationError构建立即失败而不是等到运行时才暴露问题类型转换与校验例如字段声明为intCLI 传入的字符串42会被自动转成整数非法值如abc会被拒绝默认值给字段设置默认值即可实现可选参数缺省时自动填充嵌套与复杂类型字段可以是List、Dict、嵌套的 Pydantic 模型等适用于复杂的配置结构。利用这些能力参数定义本身就成了应用的配置契约schema比手写if key not in args之类的防御代码更可靠、更易维护。实战模式一同一构建器派生多个参数化应用在生产中最常见的需求是一份代码多个配置多个应用共享同样的预处理与 HTTP 处理逻辑但各自加载不同的模型权重。此时可以把同一个 builder 的 import path 配合不同args写出多个应用条目applications: - name: Model1 import_path: my_module:my_model_code args: model_uri: s3://my_bucket/model_1 - name: Model2 import_path: my_module:my_model_code args: model_uri: s3://my_bucket/model_2 - name: Model3 import_path: my_module:my_model_code args: model_uri: s3://my_bucket/model_3其中my_model_code是my_module中的一个应用构建器函数它接收model_uri参数并返回加载对应权重的应用。三个应用共用同一份代码只是参数不同。这种模式的好处模型版本切换零代码改动要换模型只需改 YAML 中的model_uri重新部署即可多模型并存同一集群内可同时部署多个模型实例各自独立路由对应多应用部署参见 multi-app.md配置可版本化应用清单可以放进 Git 做版本管理便于审计与回滚。实战模式二用参数配置多个组合 Deployment应用构建器接收的参数不仅可以传给单个 Deployment还可以用来配置一个应用内多个 Deployment 的组合关系。例如一个模型组合model composition应用需要两个模型的权重就可以在参数里同时声明两条 URI# 完整示例见 doc/source/serve/doc_code/app_builder.py 的 composed_app_builder from pydantic import BaseModel from ray.serve import Application class ComposedArgs(BaseModel): model1_uri: str model2_uri: str def composed_app_builder(args: ComposedArgs) - Application: return IngressDeployment.bind( Model1.bind(args.model1_uri), Model2.bind(args.model2_uri), )此时配置文件中只需传两个参数即可驱动整条调用链applications: - name: ComposedApp import_path: my_module:composed_app_builder args: model1_uri: s3://my_bucket/model_a model2_uri: s3://my_bucket/model_bIngressDeployment.bind(Model1.bind(...), Model2.bind(...))会构建一个由入口 Deployment 与两个模型 Deployment 组成的应用 DAG。由于ComposedArgs的两个字段都没有默认值任何缺失都会在构建阶段被 Pydantic 拦截报错。这正是参数集中管理 部署结构灵活组装的组合威力参数变化换模型与结构变化改组合逻辑被清晰分离。使用注意事项与最佳实践builder 只接收一个参数Ray 强制 builder 恰好只有一个参数。想传多个配置项时把它们放进同一个字典/Pydantic 模型即可Pydantic 模型天然支持任意数量的字段。返回值必须是Applicationbuilder 必须以Deployment.bind()的返回值返回不能返回 Deployment 或裸函数。不要对已构建好的 Application 传参如果 import path 指向的不是 builder 函数而是已构建的Application对象再传任何参数都会触发 api.py 中的ValueErrorArguments can only be passed to an application builder function, not an already built application.。CLI 值都是字符串CLI 传参时值一律是字符串需要数字或布尔值时要么在 builder 里转换要么用 Pydantic 字段类型自动转换推荐要么改用配置文件传参以保留 YAML 原生类型。优先使用 Pydantic 类型化 builder它能替你完成缺参校验、类型转换和默认值处理减少手写解析逻辑同时把参数契约显式化方便团队协作与文档生成。生产环境推荐配置文件方式CLI 适合开发调试生产部署建议把args写进 YAML 配置清单配合serve deploy做声明式、可版本化的部署参见 develop-and-deploy.md 与 production-guide。参数变更触发滚动更新修改args后重新部署ServeController 会为相关 Deployment 构建新版本并替换副本日志中的 Deploying new version of deployment ... 即此过程无需手工删除旧部署。小结应用构建器把代码与配置解耦serve run hello:app_builder message...适合开发期快速迭代config.yaml的args字段适合生产期声明式部署无类型 builder 简单直接Pydantic 类型化 builder 则提供开箱即用的校验与类型转换。结合同一 builder 多参数多应用多 Deployment 组合配置两个实战模式你可以用一份代码灵活支撑多个模型版本与多种组合拓扑这正是 Ray Serve 生产化部署的核心能力之一。更多进阶主题可继续阅读仓库中的 autoscaling-guide.md、model-composition 文档 与 http-guide.md。赞分享人工智能分布式训练强化学习任务调度模型推理服务【免费下载链接】rayRay is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.项目地址https://gitcode.com/gh_mirrors/ra/ray点击查看免费下载相关推荐Ray项目应用构建指南如何优雅传递参数到Serve应用Ray项目应用构建指南如何优雅传递参数到Serve应用 引言 在构建分布式应用时参数配置是一个常见但容易被忽视的重要环节。Ray Serve作为Ray项目的人工智能分布式训练强化学习任务调度模型推理服务Ray Serve 多应用部署指南用多应用配置在单集群上部署与管理多个独立 Serve 应用Ray Serve 多应用部署指南用多应用配置在单集群上部署与管理多个独立 Serve 应用 本指南以 Ray Serve 的多应用multi applic人工智能分布式训练强化学习任务调度模型推理服务Ray Serve 项目教程构建流式响应的聊天机器人应用Ray Serve 项目教程构建流式响应的聊天机器人应用 概述 本教程将介绍如何使用 Ray Serve 框架构建支持流式输入输出的聊天机器人应用。我们将通过人工智能分布式训练强化学习任务调度模型推理服务创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进