mmops SDK 使用指南

更新时间:
复制 MD 格式

mmops 是AnalyticDB for MySQL提供的多模态数据处理 Python SDK。安装后,您可以在 Ray Dataset 上调用文本、文档、图片、音视频和行业领域算子,也可以与自定义算子(UDF)组合,并将结果保存为 Parquet 或 JSON 数据集。本文介绍 mmops 的安装、运行方式、数据读写、模型配置以及在 AnalyticDB Ray 上提交作业的方法。

前提条件

  • 本地运行时,Python 版本为 3.11 及以上。已验证的基础环境为 Python 3.12、Ray 2.59 和 PyArrow 23。

  • 在 AnalyticDB Ray 上运行时,已创建 Ray Cluster 资源组。具体操作,请参见托管Ray服务。

  • 已下载 mmops 的 wheel 安装包。具体操作,请参见下文的获取发布包。

安装

获取发布包

当前 0.1.0 版本的 wheel 包对象为公开读,可以直接下载,无需 OSS 凭证:

curl -fL -o ./mmops-0.1.0-py3-none-any.whl \
  'https://adb-mmops.oss-cn-beijing.aliyuncs.com/sdk/0.1.0/mmops-0.1.0-py3-none-any.whl'

也可以直接访问 mmops 0.1.0 wheel 下载地址。

安装 SDK 与依赖

在独立的虚拟环境中安装 wheel 包。以下示例使用 Python 3.12,命令中的 wheel 文件名替换为实际获取到的文件:

python3.12 -m venv .venv-mmops
source .venv-mmops/bin/activate
python -m pip install './mmops-0.1.0-py3-none-any.whl[ray]'
python -m pip check
python -c 'import mmops; print(mmops.__version__)'

[ray] 安装 Ray Dataset 的运行依赖。如果本地已安装同版本 wheel,重新安装时请使用 python -m pip install --force-reinstall --no-cache-dir './mmops-0.1.0-py3-none-any.whl[ray]'。

在 AnalyticDB Ray 上运行时,依赖版本须与资源组的实际 Python 和 Ray 版本一致。

可选依赖组

按实际使用的算子追加 extras。例如同时处理 HTML、图片和 OSS 文件:

python -m pip install './mmops-0.1.0-py3-none-any.whl[ray,office,media,oss]'

各 extra 的用途如下:

extra

用途

额外准备

ray

Ray Dataset 与分布式执行

集群运行时,所有节点使用相同版本

pdf

PDF 解析与渲染

按所选接口准备输入文件

office

HTML、Word、Excel、PowerPoint 等文档处理

转换旧版 DOC 文档另需 LibreOffice

media

图片与部分媒体处理依赖

处理音视频另需 FFmpeg 和 FFprobe

oss

算子访问 oss:// 文件与产物

配置执行节点的 OSS 地址和凭证

ossfs

通过原生 OSS filesystem 读写数据集

配置执行节点的 OSS 地址和凭证

embodied

具身数据预处理

按接口准备轨迹、传感器等数据

lerobot、rlds

导出对应格式的训练数据

分别安装对应 extra,并准备目标运行环境

wheel 包仅包含 SDK,其余依赖由 pip 从可用的包源安装,模型权重和系统工具需另外准备。完全离线部署时,请提前准备所选依赖的安装包。

运行第一个算子

Ray Dataset 将多条记录组织为数据集,每条记录的字段对应数据列。mmops 算子按指定的输入列处理记录,并返回可继续加工的 Dataset。

以下脚本仅需 [ray],不调用模型,也不需要外部素材。将代码保存为 quickstart.py 后,使用已安装 SDK 的 Python 执行 python quickstart.py:

import json
from pathlib import Path
import tempfile

import ray
from mmops.ray import enable_accessor


enable_accessor()
ray.init(num_cpus=4, include_dashboard=False)
try:
    source = ray.data.from_items([
        {"source_id": "doc-1", "text": "第一段。\n\n第二段。"},
    ])
    result = source.mmops.split_text(
        text_column="text",
        keep_columns=["source_id"],
        config={"strategy": "paragraph", "max_chars": 80, "overlap_chars": 0},
    ).materialize()

    rows = result.take(1)
    print(json.dumps(rows, ensure_ascii=False, indent=2))
    assert [chunk["content"] for chunk in rows[0]["chunks"]] == ["第一段。", "第二段。"]

    output_dir = str(Path(tempfile.mkdtemp(prefix="mmops-text-")) / "parquet")
    result.write_parquet(output_dir)
    restored = ray.data.read_parquet(output_dir).take(1)
    assert restored == rows
    print("结果已保存并回读:", output_dir)
finally:
    ray.shutdown()

结果包含来源标识和两个文本片段,字段顺序可能不同:

[
  {
    "chunks": [
      {"content": "第一段。", "chunk_index": 0, "source_page": null, "title_breadcrumb": null},
      {"content": "第二段。", "chunk_index": 1, "source_page": null, "title_breadcrumb": null}
    ],
    "source_id": "doc-1"
  }
]

enable_accessor() 为当前进程的 Ray Dataset 启用 .mmops 入口。算子接收 Dataset 并返回 Dataset,便于继续加工。take()、materialize() 和 write_parquet() 等操作会触发执行。上述示例先调用 materialize(),使预览和写出复用同一份结果;大规模任务可直接写出,避免将全部结果取回 Driver。

输入列与结果列

以下约定适用于内置 Dataset 算子,各算子的具体字段请参见离线算子概述中的算子目录:

写法

含义

text_column="body"

从每行的 body 列读取文本。

config={...}、schema={...}

本次调用共用的配置或字段定义。

keep_columns=["source_id"]

在标准结果列之外,显式保留来源字段。

batch_size、concurrency、num_cpus

控制 Ray 的执行批次、并发实例数和每个实例申请的 CPU 资源。

算子默认只返回接口声明的结果列,原输入列不会自动全部保留。keep_columns 与标准结果列重名时会报错,需先用 Ray Data 原生的 rename_columns() 改名。

split_text 每行输出一个 chunks 列表,不会自动将一行展开为多行。如需每个片段占一行,请在后续使用 flat_map()。

通用算子从 ds.mmops 调用,行业领域算子分别从 ds.mmops.game、ds.mmops.product 和 ds.mmops.embodied 调用,算子名称与输入输出请参见游戏领域算子概述、商品场景算子概述和具身智能算子概述。

数据集操作与算子组合

创建和加工 Dataset

Ray Data 负责创建、读取和写出数据集。以下示例展示筛选、生成字段、文本分块与展开:

import ray
from mmops.ray import enable_accessor


enable_accessor()
ray.init(num_cpus=4, include_dashboard=False)
try:
    source = ray.data.from_items([
        {"source_id": "doc-1", "body": "  第一段。\n\n第二段。  "},
        {"source_id": "doc-2", "body": "   "},
    ])
    cleaned = source.filter(lambda row: bool(row["body"].strip()))
    cleaned = cleaned.map(lambda row: {
        "source_id": row["source_id"], "text": row["body"].strip(),
    })
    chunks = cleaned.mmops.split_text(
        text_column="text",
        keep_columns=["source_id"],
        config={"strategy": "paragraph", "max_chars": 80, "overlap_chars": 0},
    )
    flattened = chunks.flat_map(lambda row: [
        {"source_id": row["source_id"],
         "chunk_index": chunk["chunk_index"], "text": chunk["content"]}
        for chunk in row["chunks"]
    ])
    result = flattened.select_columns(["source_id", "chunk_index", "text"])
    print(sorted(result.take(2), key=lambda row: row["chunk_index"]))
finally:
    ray.shutdown()

输出如下:

[{'source_id': 'doc-1', 'chunk_index': 0, 'text': '第一段。'},
 {'source_id': 'doc-1', 'chunk_index': 1, 'text': '第二段。'}]

其中 filter()、map()、flat_map() 和 select_columns() 是 Ray Data 原生方法,mmops.split_text() 是 SDK 提供的算子。后续可以继续接入字段提取、向量化或质量筛选等算子。

读取和写出数据

在已初始化 Ray 的脚本中,使用 Ray Data 原生读写接口:

# 输入路径需事先存在,文件中的字段即 Dataset 列。
source = ray.data.read_parquet("/shared/input/documents/")
# 其他常用格式:
# source = ray.data.read_json("/shared/input/documents.jsonl")
# source = ray.data.read_csv("/shared/input/documents.csv")

result = source.mmops.split_text(
    text_column="text", keep_columns=["source_id"],
    config={"strategy": "length", "max_chars": 800, "overlap_chars": 80},
)
result.write_parquet("/shared/output/document-chunks/job-001/")
# 或:result.write_json("/shared/output/document-chunks-json/job-001/")

每次任务使用独立的输出目录,避免重复执行时混入旧分片。多行结果不保证全局顺序,请使用 source_id、chunk_index 等字段关联记录。

文档解析后分块

安装 [ray,office] 后,可以在已初始化 Ray 的脚本中将 HTML 解析与文本分块串联。以下示例的输入文件需事先存在:

source = ray.data.from_items([
    {"source_id": "page-1", "source_uri": "/shared/input/guide.html"},
])
parsed = source.mmops.extract_html(
    document_column="source_uri", keep_columns=["source_id", "source_uri"],
)
result = parsed.mmops.split_text(
    text_column="text", keep_columns=["source_id", "source_uri"],
    config={"strategy": "length", "max_chars": 800, "overlap_chars": 80},
)
result.write_parquet("/shared/output/html-chunks/job-001/")

extract_html 只提取静态正文和表格,不执行页面中的 JavaScript。其他格式请从文档算子概述中选择,每一步均需显式保留后续要用的来源列。

图片处理与文件产物

安装 [ray,media] 后,以下示例会生成一张 1000×800 的示例图片,将其等比缩放到 512×512 的包围盒内,并保存结果表。该示例不依赖模型服务。

import json
from pathlib import Path
import tempfile

from PIL import Image
import ray
from mmops.ray import enable_accessor


root = Path(tempfile.mkdtemp(prefix="mmops-image-"))
input_path = root / "sample.png"
Image.new("RGB", (1000, 800), color="white").save(input_path)

enable_accessor()
ray.init(num_cpus=4, include_dashboard=False)
try:
    source = ray.data.from_items([
        {"source_id": "image-1", "source_uri": str(input_path)},
    ])
    result = source.mmops.resize_image(
        image_column="source_uri",
        output_dir=str(root / "images"),
        width=512, height=512,
        keep_columns=["source_id", "source_uri"],
    ).materialize()
    rows = result.take(1)
    assert (rows[0]["width"], rows[0]["height"]) == (512, 410)
    assert Path(rows[0]["image_uri"]).is_file()
    result.write_parquet(str(root / "metadata"))
    print(json.dumps(rows, ensure_ascii=False, indent=2))
    print("图片和结果表目录:", root)
finally:
    ray.shutdown()

结果列包括 image_uri、width、height、format 以及显式保留的来源字段。image_uri 指向 SDK 生成的 PNG 文件。resize_image 等比缩放不裁剪,因此实际尺寸为 512×410。

output_dir 保存图片等文件产物,write_parquet() 保存结果列和文件地址。如需同时交付处理后的图片与结果表,请保留这两个位置。音视频的转换、裁剪和采样也遵循各接口声明的文件产物约定。

调用模型算子

文本字段提取、图片描述、向量化和语音转写需要配置对应的模型服务。请先在运行环境中设置服务地址、模型名称和凭证。例如使用支持 JSON 输出的 OpenAI 兼容文本服务:

export MMOPS_MODEL_BASE_URL='https://<model-host>/v1'
export MMOPS_TEXT_MODEL='<text-model-name>'
export MMOPS_MODEL_API_KEY='<由模型服务方提供>'

以下示例将上述配置显式传给 Ray 执行进程,再从文本中提取商品名称和价格。服务地址、模型名称和凭证需替换为实际值。

import json
import os

import ray
from mmops.ray import enable_accessor


env_vars = {name: os.environ[name] for name in (
    "MMOPS_MODEL_BASE_URL", "MMOPS_TEXT_MODEL", "MMOPS_MODEL_API_KEY",
)}
enable_accessor()
ray.init(
    num_cpus=4, include_dashboard=False,
    runtime_env={"env_vars": env_vars},
)
try:
    schema = {
        "type": "object",
        "properties": {
            "name": {"type": "string"},
            "price": {"type": ["number", "null"]},
        },
        "required": ["name", "price"],
        "additionalProperties": False,
    }
    source = ray.data.from_items([
        {"source_id": "product-1", "text": "名称:收纳盒;价格:29.9 元。"},
    ])
    result = source.mmops.extract_fields(
        text_column="text", schema=schema, keep_columns=["source_id"],
        config={"prompt": "仅提取原文明示信息,未提供价格时返回 null。"},
    )
    print(json.dumps(result.take(1), ensure_ascii=False, indent=2))
finally:
    ray.shutdown()

结果直接包含 name、price 和 source_id 列。Schema 决定固定的结果列集合,缺少必填字段、出现额外字段或类型不符都会报错。模型提取的内容需结合实际输入校验。

各模型算子的配置入口如下,完整参数请参见对应的接口文档:

算子

配置参数

默认模型环境变量

extract_fields

config

MMOPS_TEXT_MODEL

caption_image

model_config

MMOPS_VISION_MODEL

embed_text

config

MMOPS_EMBEDDING_MODEL

embed_image

model_config

MMOPS_IMAGE_EMBEDDING_MODEL

transcribe_audio

model_config

MMOPS_ASR_MODEL

例如,在已初始化 Ray 并配置视觉模型服务的脚本中调用图片描述:

images = ray.data.from_items([
    {"source_id": "image-1", "source_uri": "/shared/input/sample.png"},
])
captions = images.mmops.caption_image(
    image_column="source_uri", keep_columns=["source_id", "source_uri"],
    prompt="描述可见物体、颜色和位置。",
    model_config={"api_key_env": "MMOPS_MODEL_API_KEY"},
)
print(captions.take(1))

图片描述的结果为 text 列。使用该示例前,还需向执行进程注入 MMOPS_VISION_MODEL 和服务地址:服务地址可使用 MMOPS_VISION_BASE_URL,未配置时回退到 MMOPS_MODEL_BASE_URL。图片向量化和语音识别的服务协议、地址可能与文本服务不同,请按对应接口配置。各环境变量的完整说明,请参见离线算子概述。

重要

SDK 不会自动加载 .env 文件,配置需注入每个执行进程。请勿将密钥写入 Dataset 的数据列。

使用自定义算子(UDF)

函数 UDF

自定义处理通过 Ray Data 原生接口加入流水线:逐行处理使用 map(),批量处理使用 map_batches()。以下示例先规范文本空白,再调用内置的分块算子:

import re

import ray
from mmops.ray import enable_accessor


def normalize_text(batch):
    return {
        "source_id": batch["source_id"],
        "text": [re.sub(r"\s+", " ", text).strip() for text in batch["text"]],
    }


enable_accessor()
ray.init(num_cpus=4, include_dashboard=False)
try:
    source = ray.data.from_items([
        {"source_id": "doc-1", "text": "  Ray   Data\n 使用说明  "},
    ])
    cleaned = source.map_batches(
        normalize_text, batch_format="numpy", batch_size=32, num_cpus=1,
    )
    result = cleaned.mmops.split_text(
        text_column="text", keep_columns=["source_id"],
        config={"strategy": "length", "max_chars": 80, "overlap_chars": 0},
    )
    print(result.take(1))
finally:
    ray.shutdown()

类 UDF

需要在批次之间复用模型、客户端或其他状态时,使用可调用类。__init__ 在每个 Actor 内初始化,__call__ 处理后续批次。以下类复用编译后的正则表达式:

class NormalizeText:
    def __init__(self):
        self.whitespace = re.compile(r"\s+")

    def __call__(self, batch):
        return {
            "source_id": batch["source_id"],
            "text": [self.whitespace.sub(" ", text).strip() for text in batch["text"]],
        }


# 在上面的脚本中,用这一段替换 cleaned = source.map_batches(...)。
cleaned = source.map_batches(
    NormalizeText,
    compute=ray.data.ActorPoolStrategy(size=1),
    batch_format="numpy", batch_size=32, num_cpus=1,
)

模型权重也可以在类的构造方法中加载,供该 Actor 的后续批次复用。使用本地 GPU 模型时,请在 map_batches() 中按需设置 num_gpus。完整的输入校验、状态管理和重试约定,请参见自定义离线算子(UDF)。

在集群中运行

AnalyticDB Ray 提供作业执行与资源调度,mmops 在作业中提供 ds.mmops 算子。生产批处理建议通过 Ray Jobs 提交,使 Driver 和 Worker 使用同一份作业依赖。

在 AnalyticDB Ray 上提交作业

先按托管Ray服务准备资源组,并在资源组详情中获取可访问的 HTTP 连接地址,例如 http://<ADB_RAY_HOST>:8265。提交端须能访问该地址,执行节点须能访问 wheel 包地址和所用的 pip 源。

提交前请核对资源组的 Python 和 Ray 版本。基于DMS Notebook进行Ray交互式开发使用 Python 3.11.13 和 Ray 2.49.2,以下示例按 Ray 2.49.2 配置,实际版本不同时请同步调整。建议先用下面的无模型任务验证目标资源组。

在独立目录 adb-mmops-job 中准备两个文件:

adb-mmops-job/
  main.py
  runtime_env.yaml

runtime_env.yaml 安装 SDK,并固定资源组的实际 Ray 版本:

pip:
  packages:
    - "ray[data]==2.49.2"
    - "mmops[ray] @ https://adb-mmops.oss-cn-beijing.aliyuncs.com/sdk/0.1.0/mmops-0.1.0-py3-none-any.whl"
  pip_check: true

main.py 连接作业所在的集群,执行文本分块并检查输出:

import ray
from mmops.ray import enable_accessor


ray.init(address="auto")
enable_accessor()
try:
    source = ray.data.from_items([
        {"source_id": "doc-1", "text": "ADB Ray 上运行 mmops。"},
    ])
    result = source.mmops.split_text(
        text_column="text", keep_columns=["source_id"],
        config={"strategy": "length", "max_chars": 800, "overlap_chars": 80},
    )
    rows = result.take(1)
    assert rows[0]["chunks"][0]["content"] == "ADB Ray 上运行 mmops。"
    print(rows)
finally:
    ray.shutdown()

在独立的提交环境中安装匹配版本的 CLI,再进入作业目录提交作业。连接地址替换为实际值:

python -m pip install 'ray[default]==2.49.2'
cd adb-mmops-job
ray job submit \
  --address 'http://<ADB_RAY_HOST>:8265' \
  --working-dir . \
  --runtime-env runtime_env.yaml \
  -- python main.py

提交作业时请注意以下事项:

  • 工作目录只放作业文件,不要放虚拟环境或密钥,因为 Ray 会将该目录打包上传。

  • 作业提交层的 runtime_env 同时应用于 Driver、Task 和 Actor。仅在 ray.init(runtime_env=...) 中配置依赖不会安装到 Driver 本身。更多信息,请参见 Ray 依赖管理。

  • 按任务需要添加 pdf、office、media、oss 等 extras。FFmpeg、LibreOffice 等系统工具需在执行环境中另外准备。

  • 模型配置通过提交层的 env_vars 或节点环境提供,凭证不要写入上传目录。

  • 同版本 wheel 包被覆盖后,Ray 仍可能复用已有的依赖环境。此时可在 wheel 包地址末尾添加或更新 #sha256=<本次发布的 SHA256>,使 pip 配置随产物变化并校验下载内容。

交互调试可以使用 ray://<ADB_RAY_HOST>:10001 连接,但 Notebook 与集群的 Python、Ray 版本须一致,且 SDK 须安装在 Notebook 自身的环境中。批处理请使用上述 Ray Jobs 方式。

连接其他 Ray 集群

上述本地示例使用 ray.init(num_cpus=4, ...)。连接已准备好的 Ray 集群时,将初始化改为:

import os
import ray
from mmops.ray import enable_accessor


enable_accessor()
env_vars = {name: value for name, value in os.environ.items()
            if name.startswith("MMOPS_")}
ray.init(address="auto", runtime_env={"env_vars": env_vars})

运行前,请在 Driver 和每个 Worker 的镜像或环境中安装相同版本的 wheel 包、Ray 与所需依赖。runtime_env.env_vars 传递环境配置,但文件路径指向的模型上下文或权重仍需在节点上实际可读。如果代码中通过 api_key_env 指定了其他凭证变量名,也需将该变量传入执行环境。

文件与对象存储

集群中的 /shared 表示所有 Worker 节点均可读写的共享挂载目录。该目录需要实际准备,不是 AnalyticDB Ray 默认提供的目录。Driver 的临时目录、下载目录和 file:// 路径不会自动分发到其他节点,因此前文的临时文件示例仅适用于本地运行。AnalyticDB Ray 的 Worker 磁盘用于临时数据,正式结果请写入已配置的持久化存储。

访问对象

使用方式

算子的输入文档、图片、音视频

按接口提供 Worker 节点可访问的本地路径、file://、HTTP(S) 或 oss:// URI。

算子的媒体文件产物

使用各接口的 output_dir,例如共享挂载目录。支持 OSS 的接口也可以使用 OSS 前缀。

Parquet、JSON、CSV 结果数据集

使用 Ray Data 原生读写接口,远端存储按 Ray 对应的 filesystem 配置。

SDK 访问 OSS 文件时,需要安装 [oss],并向执行进程注入 MMOPS_OSS_ENDPOINT、MMOPS_OSS_ACCESS_KEY_ID 和 MMOPS_OSS_ACCESS_KEY_SECRET。使用 STS 临时凭证时,还需提供 MMOPS_OSS_SECURITY_TOKEN。这些变量仅用于 SDK 的文件访问,不会自动配置 Ray 或 Arrow 的数据集 filesystem。快速上手时,可以先将结果表写入共享挂载目录;如需直接在 OSS 上读写结果数据集,请参见下文。

在 OSS 上读写数据集

读写 OSS 上的 Parquet 等数据集时,使用 mmops.storage.oss_filesystem() 显式创建原生 OSS filesystem,并将其传给 Ray Data 的读写接口。该接口需要安装 ossfs extra。如果已安装不含该接口的早期同版本 wheel 包,请加 --force-reinstall 重新安装:

python -m pip install --force-reinstall './mmops-0.1.0-py3-none-any.whl[ray,ossfs]'
python -m pip check

运行前,在执行环境中配置 MMOPS_OSS_ENDPOINT(OSS 原生 Endpoint)、MMOPS_OSS_ACCESS_KEY_ID 和 MMOPS_OSS_ACCESS_KEY_SECRET。使用 STS 临时凭证时,还需配置 MMOPS_OSS_SECURITY_TOKEN。

以下代码片段需在已初始化 Ray 的脚本中运行,先写入两行数据,再回读并逐行比对。请将 your-bucket 替换为有访问权限的 Bucket:

import uuid

import ray
from mmops.storage import oss_filesystem

# 前置条件:已初始化 Ray,并已配置 MMOPS_OSS_* 环境变量。
filesystem = oss_filesystem()
path = f"your-bucket/mmops/test/check-{uuid.uuid4().hex}/"
expected = [{"id": 1, "text": "第一条数据"}, {"id": 2, "text": "第二条数据"}]

ray.data.from_items(expected).write_parquet(path, filesystem=filesystem)
rows = ray.data.read_parquet(path, filesystem=filesystem).take_all()
assert sorted(rows, key=lambda row: row["id"]) == expected
print(f"OSS 写入、回读校验成功:oss://{path}")

显式传入 filesystem 时,路径格式为 bucket/prefix,对应的对象地址为 oss://bucket/prefix。该方式通过 ossfs 和 oss2 调用 OSS 原生 API,无需配置 S3 兼容 Endpoint 或 AWS checksum。每次任务请使用独立的前缀,避免混入旧数据。

批次与资源

内置算子的 batch_size 是 Ray 每批处理的输入行数,增大该值不代表模型请求会自动合并。concurrency 控制执行实例数,num_cpus 是每个实例申请的资源。实际并行度还受数据分块和集群可用资源的限制。

内置的图片理解、语音识别等算子通过远端模型服务调用,不在 Worker 节点上申请本地 GPU。自定义 UDF按需声明 num_gpus,并确保模型权重对所有执行节点可见。

常见问题

现象

处理方法

找不到 .mmops 属性

在 Driver 中先执行 from mmops.ray import enable_accessor 和 enable_accessor()。

缺少可选依赖或系统命令

按所选算子安装对应 extra。媒体处理检查 FFmpeg 和 FFprobe,DOC 转换检查 LibreOffice。

Worker 节点无法读取输入或模型上下文

检查路径在执行节点是否存在,改用共享挂载目录或可访问的 URI。

来源字段消失

每个内置算子调用都通过 keep_columns 显式保留所需字段。

保留输入列时报重名错误

用 Ray Data 的 rename_columns() 将原列改名,再传入 keep_columns。

模型地址、模型名称或凭证缺失

检查 Worker 节点的环境变量。SDK 不会自动加载 .env 文件。

模型结果不符合字段 Schema

核对必填、可空和类型约束,以及模型服务的 JSON 输出能力。

无效输入、文件读写、模型调用和结果校验失败都会抛出错误,失败不会被替换为空文本、空向量或有效样本。

相关文档