本文介绍如何在 Flink 中使用 ML_PREDICT 函数调用 AI 模型,涵盖语法与入参说明、调用级参数、内容类型配置、列级参数设置,以及文本、图片、多模态等多种调用示例。
快速开始
前提条件
已创建 Flink 工作空间,详情请参见开通实时计算Flink版。
已开通 Flink AI 服务,详情请参见Flink AI服务(内置模型)。
使用 Flink AI 服务(内置模型)时,需使用 VVR 11.7 及以上版本引擎。
以下示例演示如何使用 ML_PREDICT 调用 Flink 内置模型。请在页面,新建作业、复制代码后点击调试。
CREATE TEMPORARY TABLE text_source (
user_input STRING
) WITH ('connector' = 'datagen');
CREATE TEMPORARY TABLE result_sink (
user_input STRING,
ai_analysis STRING
) WITH ('connector' = 'print');
CREATE TEMPORARY MODEL text_model
INPUT (user_input STRING)
OUTPUT (content STRING)
WITH (
'provider' = 'openai-compat',
'model' = 'qwen3.6-flash',
'task' = 'chat/completions',
'system-prompt' = '分析内容乱码程度,给0-100的评分'
);
INSERT INTO result_sink
SELECT user_input, content as ai_analysis FROM
ML_PREDICT(
TABLE text_source,
MODEL text_model,
DESCRIPTOR(user_input)
);使用限制
仅实时计算引擎 VVR 11.1 及以上版本支持。
部分参数仅在使用 Flink AI 服务(内置模型)时支持,仅支持 VVR 11.8及以上版本。
ML_PREDICT 算子的吞吐量受百炼平台限流限制。触及流量上限时,作业会出现以 ML_PREDICT 算子为瓶颈的反压现象,严重时可能触发超时报错及作业重启。详情参见百炼平台限流。
content-types中的类型数量及 Descriptor 中的列数,必须与 CREATE MODEL 中 INPUT 的列数一致。image_url类型的列必须为 STRING,multi_image_urls类型的列必须为ARRAY<STRING>。base64 图片必须包含
data:image/<格式>;base64,前缀,不支持纯 base64 字符串和本地文件路径。
语法
ML_PREDICT(TABLE <table_name>, MODEL <model_name>, DESCRIPTOR(<input_columns>) [, CONFIG => MAP[...]]) 入参
参数 | 数据类型 | 说明 |
TABLE | TABLE | 模型推断的输入数据流,可以是物理表名或视图名。 |
MODEL | MODEL | 已注册的模型服务名称。详情参见模型设置。 |
DESCRIPTOR() | — | 用于模型推断的输入列。 说明 VVR 11.8 及以上版本支持输入多列,且仅在使用 Flink AI 服务(内置模型)时支持。强制要求 DESCRIPTOR 列数 = CREATE MODEL INPUT 列数。 |
CONFIG => MAP[...] | MAP | 可选。详情参见调用级参数。 说明 仅VVR 11.8 及以上版本支持,且使用 Flink AI 服务(内置模型)。 |
调用级参数
用户可以在使用 ML_PREDICT 时指定调用级参数。若 CREATE MODEL 中已设置参数值,则调用 ML_PREDICT 时以新指定的参数为准,但不会持久化改写 MODEL 中的参数值。
参数 | 说明 | 示例 |
| 指定用户提示词。传空字符串时则不使用MODEL 级参数值。 |
|
| 仅输入单列时,指定内容类型。 |
|
| 输入多列时,指定内容类型。 |
|
| 指定列级参数。 |
|
| 指定附加参数(JSON 字符串)。 |
|
| Bundle 大小。 | 说明 Bundle 处理仅支持 VVR 11.9 及以上版本。 |
| Bundle 允许的延迟时间。 |
内容类型参数
仅输入单列时,可通过
content-type指定内容类型,支持的值:text、image_url。输入多列时,可通过
content-types指定每列内容类型,支持的值和 Flink SQL 列类型的映射关系:
content-types 内容类型 | Flink SQL 类型 | 说明 |
|
| - |
|
| - |
|
| VVR 11.9 及以上版本支持 |
|
| VVR 11.9 及以上版本支持 |
|
| VVR 11.8 及以上版本支持 |
|
| VVR 11.9 及以上版本支持 |
|
| VVR 11.9 及以上版本支持 |
请在参数中优先指定
content-types,若同时设置content-type和content-types框架会报错提示。注意:content-type和content-types在模型参数中设置和调用时设置是相互影响的:
创建模型时指定参数 | 调用级参数可使用 | 调用级参数不可使用 | 说明 |
|
|
| 可在 |
|
|
| 可改变类型组合,如 |
未指定 |
| — | 同上 |
列级参数
参数 | 说明 | 参数值 | 示例 |
| 设定输入图像或视频帧的最小像素阈值。当输入图像或视频帧的像素小于 |
|
|
| 设定输入图像或视频帧的最大像素阈值。当输入图像或视频的像素在 |
|
|
| 限制从视频中抽取的所有帧的总像素(单帧图像像素 × 总帧数)。如果视频总像素超过此限制,系统将对视频帧进行缩放,但仍会确保单帧图像的像素值在 |
|
|
| 用于开启显式缓存。 |
|
|
| 用户设置视频理解的抽帧频率 | 取值范围为 [0.1, 10],默认值为 2.0 |
|
Bundle 处理
处理方式:Bundle 处理会先将多条输入数据聚合成一个批次,再统一调用 AI 模型。这种处理方式可以减少模型调用次数,但会增加单次请求的模型推理时间,在数据被有效聚合时能明显提升算子吞吐量。
参数说明:
bundle-size用于指定单个批次最多积累的数据条数;bundle-allow-latency用于指定批次未达到bundle-size时允许等待的最长时间。达到批次大小或等待时长任一条件时触发处理。支持条件:(仅支持内置模型)Bundle 处理按模型的输入输出形态生效。
文本生成:模型任务类型为
chat/completions时,输入恰好一列STRING文本列,输出恰好一列STRING或VARIANT列。多列输入以及图片、视频、音频等多模态输入不支持。文本向量化:模型任务类型为
embeddings时,输入恰好一列STRING列,输出恰好一列ARRAY<FLOAT>列。
适用函数:
ML_PREDICT、文本分类(AI_CLASSIFY)、情感分析(AI_SENTIMENT)、信息提取(AI_EXTRACT)、文本摘要(AI_SUMMARIZE)、文本翻译(AI_TRANSLATE)、数据脱敏(AI_MASK)及文本向量化(AI_EMBED)。其中,ML_PREDICT仅在所注册模型满足上述形态要求时支持 Bundle 处理。全局配置:对作业内满足上述形态要求的所有
ML_PREDICT和垂类 AI Function 调用生效。使用以下全局参数开启 Bundle 处理:-- Bundle 处理仅支持 VVR 11.9 及以上版本。 SET 'table.exec.ml-predict.bundle-size' = '10'; SET 'table.exec.ml-predict.bundle-allow-latency' = '5s';调用级配置:仅对配置了参数的函数调用生效;
ML_PREDICT与上述垂类函数的配置方式相同。具体参数和示例请参见调用级参数。
错误处理
使用限制
仅实时计算引擎 VVR 11.9 及以上版本支持。
不支持在模型调用同步执行模式下,开启错误处理能力。
暂不支持与向量化结果缓存(
CACHE_TABLE)一起使用。
开启方式
在使用 AI 函数时增加参数FAIL_ON_ERROR => FALSE。
前置条件:保持模型默认的重试配置 error-handling-strategy='retry'、retry-fallback-strategy='failover'。模型参数详情请参见模型设置。
参数 | 说明 | 是否必填 | 默认值 |
FAIL_ON_ERROR | 最终调用失败时是否让作业报错。设为 | 否 |
|
FAIL_ON_ERROR 默认值为 TRUE,重试耗尽后作业报错。设为 FALSE 仅改变失败记录的处理方式,不改变重试行为。
输出变化
开启后,系统会在原有输出字段末尾追加错误信息列 err_msg,类型为可空的 VARIANT。该列由系统生成,无需在输入表或 CREATE MODEL 的 OUTPUT 中声明。
调用成功:
err_msg为NULL。最终失败:模型结果字段均为
NULL,err_msg包含错误信息。
使用 SELECT * 时,结果会包含新增的错误信息列;显式选择字段时,需手动指定该列。默认列名为 err_msg;若与已有输出列重名,系统会自动追加数字后缀(如 err_msg0),引用时请使用实际列名。
请以错误信息列是否为 NULL 判断调用是否失败:非 NULL 表示失败。模型结果字段为 NULL 不一定表示调用失败。
对象内字段 | 内容 | SQL 读取方式 |
message | 错误原因,日常排查先看这一项。 | CAST(err_msg['message'] AS STRING) |
stack_trace | 更详细的排查信息,需要进一步定位时查看。 | CAST(err_msg['stack_trace'] AS STRING) |
details(optional字段) | 当底层报错有实际有额外信息绑定时,会有该字段。 | err_msg['details'],若没有字段会返回NULL, 若存在该字段返回的类型为Variant。 |
示例
AI_SENTIMENT 保留失败记录
准备一个已创建、支持异步调用的情感分析模型 ai_sentiment_model。
CREATE TEMPORARY VIEW input_data AS
SELECT *
FROM (VALUES
(1, CAST('xxxx(反人类的暴力冲突描述)' AS STRING), 'negative'),
(2, CAST('中国的治安一直是世界上数一数二的级别' AS STRING), 'positive')
) AS t(id, input_text, expected_label);
CREATE TEMPORARY VIEW ai_results AS
SELECT i.id, i.input_text, i.expected_label,
r.score, r.label, r.confidence, r.err_msg
FROM input_data AS i,
LATERAL TABLE(AI_SENTIMENT(
MODEL => MODEL ai_sentiment_model,
INPUT => i.input_text,
FAIL_ON_ERROR => FALSE,
CONFIG => MAP['async', 'true']
)) AS r(score, label, confidence, err_msg);
SELECT * FROM ai_results;查询输出依次为以下列。
输出列 | 类型 | 来源与含义 |
id | INT | 保留输入记录编号。 |
input_text | STRING | 保留待处理的原文。 |
score / label / confidence | STRING | AI_SENTIMENT 返回内容,失败时为 |
err_msg | VARIANT,可为 | 开启错误处理后自动增加。成功时为 |
上述实际输出如下。摘要和错误内容为示意,实际以运行结果为准。
id | input_text | score / label / confidence | err_msg |
1 | xxxx(反人类的暴力冲突描述) |
| {"message":"com.openai.errors.BadRequestException: 400: <400> InternalError.Algo.DataInspectionFailed: Input text data may contain inappropriate content..... "stack_trace":"org.apache.flink.table.api.TableException: Failed to execute asynchronously prediction with input row xxx} |
2 | 中国的治安一直是世界上数一数二的级别 | 1.0 / positive / 0.99 |
|
其他报错示例
以通用调用为例子,展示timeout示例。
message:
java.util.concurrent.TimeoutException: AsyncPredictFunction did not complete within the configured timeout. Increase the ML_PREDICT timeout or check external service connectivity.
stack_trace:
java.util.concurrent.TimeoutException: AsyncPredictFunction did not complete within the configured timeout. Increase the ML_PREDICT timeout or check external service connectivity.
at org.apache.flink.table.runtime.operators.predict.AsyncMLPredictRunner.timeout(AsyncMLPredictRunner.java:153)
at org.apache.flink.table.runtime.operators.predict.AsyncMLPredictRunner.timeout(AsyncMLPredictRunner.java:51)
at org.apache.flink.table.runtime.operators.TableKeyedAsyncWaitOperator$KeyedResultHandler.timerTriggered(TableKeyedAsyncWaitOperator.java:462)
at org.apache.flink.table.runtime.operators.TableKeyedAsyncWaitOperator$KeyedResultHandler.lambda$registerTimeout$1(TableKeyedAsyncWaitOperator.java:457)
at org.apache.flink.table.runtime.operators.TableKeyedAsyncWaitOperator.lambda$registerTimer$4(TableKeyedAsyncWaitOperator.java:486)
at org.apache.flink.streaming.runtime.tasks.StreamTask.invokeProcessingTimeCallback(StreamTask.java:2285)
at org.apache.flink.streaming.runtime.tasks.StreamTask.lambda$deferCallbackToMailbox$28(StreamTask.java:2276)
at org.apache.flink.streaming.runtime.tasks.StreamTaskActionExecutor$1.runThrowing(StreamTaskActionExecutor.java:50)
at org.apache.flink.streaming.runtime.tasks.mailbox.Mail.run(Mail.java:115)
at org.apache.flink.streaming.runtime.tasks.mailbox.MailboxProcessor.runMail(MailboxProcessor.java:415)
at org.apache.flink.streaming.runtime.tasks.mailbox.MailboxProcessor.processMailsWhenDefaultActionUnavailable(MailboxProcessor.java:384)
at org.apache.flink.streaming.runtime.tasks.mailbox.MailboxProcessor.processMail(MailboxProcessor.java:369)
at org.apache.flink.streaming.runtime.tasks.mailbox.MailboxProcessor.runMailboxLoop(MailboxProcessor.java:229)
at org.apache.flink.streaming.runtime.tasks.StreamTask.runMailboxLoop(StreamTask.java:1273)
at org.apache.flink.streaming.runtime.tasks.StreamTask.invoke(StreamTask.java:1217)
at org.apache.flink.runtime.taskmanager.Task.runWithSystemExitMonitoring(Task.java:1133)
at org.apache.flink.runtime.taskmanager.Task.restoreAndInvoke(Task.java:1110)
at org.apache.flink.runtime.taskmanager.Task.doRun(Task.java:914)
at org.apache.flink.runtime.taskmanager.Task.run(Task.java:716)
at java.base/java.lang.Thread.run(Thread.java:991)各类错误的处理方式
以下为已开启错误处理、保持默认重试配置时的行为。错误行指保留输入、模型结果为空、err_msg 包含失败原因的一行数据。
错误类型/原因 | 系统如何处理 | 最终输出 |
网络连接失败、限流或部分临时服务错误(429、500、502、503) | 按配置等待后重试。 | 重试成功则正常输出;最终失败则输出错误行。 |
参数不合法、输入过长、密钥错误、无权限或模型不存在(400、401、403、404、422) | 通常不重试,直接返回失败。 | 输出错误行,记录服务返回的原因。 |
内容审核拒绝,或余额、配额不足 | 按服务实际返回的错误决定是否重试。 | 恢复成功则正常输出;最终失败则输出错误行。 |
要求 JSON 结果,但模型回复无法解析 | 尝试重新请求。 | 成功则正常输出;最终失败则输出错误行。 |
超过设定的等待时间 | 结束本次等待。 | 输出错误行,提示超时。 |
批量处理时,最终失败或超时 | 按批次内的输入分别记录失败。 | 每条输入各输出一条错误行,记录同一个失败原因。 |
示例
文本
以下示例注册并引用 Flink AI 服务提供的内置模型,对输入文本进行情感分类。调用示例 1 复用 MODEL 级参数,调用示例 2 在调用时指定参数 user-prompt。
-- 注册引用内置模型,不指定 content-type 时默认为 text
CREATE MODEL sentiment_model
INPUT (prompt STRING)
OUTPUT (response STRING)
WITH (
'provider' = 'openai-compat',
'task' = 'chat/completions',
'model' = 'qwen3.6-flash',
'system-prompt' = '你是一个情感分类模型,输出标签:负面、正面、中性'
);
-- 创建源表:模拟商品评价数据
CREATE TEMPORARY VIEW input_table(id, content)
AS VALUES
(1, '质量很好,面料柔软亲肤,尺码也很标准,好评'),
(2, '收到就有线头,洗了一次严重褪色,不值这个价'),
(3, '衣服已收到,和图片一致'),
(4, '穿了两周起球严重,客服还不给退货,差评'),
(5, '版型显瘦,颜色比图片还好看,已经回购第三件了');
-- 创建结果表
CREATE TEMPORARY TABLE output_table (
id INT,
content STRING,
sentiment STRING
) WITH (
'connector' = 'print'
);
-- 使用 ML_PREDICT 进行实时推理
-- 调用示例 1:复用 MODEL 级参数
INSERT INTO output_table
SELECT
id,
content,
response AS sentiment
FROM ML_PREDICT(
TABLE input_table,
MODEL sentiment_model,
DESCRIPTOR(content));
-- 调用示例 2:指定调用级参数
INSERT INTO output_table
SELECT
id,
content,
response AS sentiment
FROM ML_PREDICT(
TABLE input_table,
MODEL sentiment_model,
DESCRIPTOR(content),
MAP['user-prompt', '请用英文回复']);调用示例 1 输出结果:
id | content | sentiment |
1 | 质量很好,面料柔软亲肤,尺码也很标准,好评 | 正面 |
2 | 收到就有线头,洗了一次严重褪色,不值这个价 | 负面 |
3 | 衣服已收到,和图片一致 | 正面 |
4 | 穿了两周起球严重,客服还不给退货,差评 | 负面 |
5 | 版型显瘦,颜色比图片还好看,已经回购第三件了 | 正面 |
调用示例 2 输出结果:
id | content | sentiment |
1 | 质量很好,面料柔软亲肤,尺码也很标准,好评 | Positive |
2 | 收到就有线头,洗了一次严重褪色,不值这个价 | Negative |
3 | 衣服已收到,和图片一致 | Positive |
4 | 穿了两周起球严重,客服还不给退货,差评 | Negative |
5 | 版型显瘦,颜色比图片还好看,已经回购第三件了 | Positive |
图片
以下示例注册一个多模态模型,对输入图片进行分类。
图片输入列(content-type为image_url)为STRING类型,支持以下两种内容形式:
输入形式 | 用法 | 限制 |
公网URL | 直接传入模型服务可通过公网访问的HTTP/HTTPS URL字符串,由模型服务自行拉取图片。 | URL必须可被模型服务所在环境通过公网访问;若URL具有时效性,有效期需覆盖模型调用耗时;图片格式与大小受模型服务侧限制。 |
Base64 Data URL | 传入以 | 图片字节需经Flink作业传输,会增加作业网络开销;Base64编码后数据体积增大;数据量受模型服务侧输入限制约束。 |
如果图片存储在使用Paimon Catalog的Blob列中,可以利用Paimon提供的Blob预签名URL功能生成公网URL作为输入,详情请参见流式数据湖仓Paimon。
-- 注册引用内置模型,指定 content-type 为 image_url
CREATE TEMPORARY MODEL sentiment_model
INPUT (prompt STRING)
OUTPUT (response STRING)
WITH (
'provider' = 'openai-compat',
'task' = 'chat/completions',
'model' = 'qwen3.6-flash',
'content-type' = 'image_url'
);
-- 使用 ML_PREDICT 进行实时推理
INSERT INTO output_table
SELECT
id,
content,
response AS sentiment
FROM ML_PREDICT(
TABLE input_table,
MODEL sentiment_model,
DESCRIPTOR(content));视频
以下示例注册一个多模态模型,对输入视频进行理解。
CREATE TEMPORARY MODEL sentiment_model
INPUT (video_url STRING)
OUTPUT (response STRING)
WITH (
'provider' = 'openai-compat',
'task' = 'chat/completions',
'model' = 'qwen3.6-plus',
'content-types' = 'video_url',
'system-prompt' = '请简单描述下视频内容'
);
CREATE TEMPORARY VIEW input_table(content)
AS VALUES ('https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20251111/ckgcxt/%E5%8D%95%E4%BA%BA%E8%AF%B4%E8%AF%9D-1.mp4');
SELECT
content,
response AS sentiment
FROM ML_PREDICT(
TABLE input_table,
MODEL sentiment_model,
DESCRIPTOR(content));文本 + 单张图片
以下示例注册一个多模态模型,对输入的文本和图片进行对话推理。
CREATE MODEL vl_model
INPUT (text_input STRING, image_input STRING)
OUTPUT (content STRING)
WITH (
'provider' = 'openai-compat',
'model' = 'qwen3.5-plus',
'task' = 'chat/completions',
'content-types' = 'text;image_url'
);
INSERT INTO result_sink
SELECT content FROM TABLE(ML_PREDICT(
TABLE image_source,
MODEL vl_model,
DESCRIPTOR(text_input, image_input)
)); 文本 + 多张图片(多列)
通过多个 INPUT 列分别传入每张图片,content-types 中为每列指定 image_url。
CREATE MODEL vl_model_multi
INPUT (prompt STRING, img1 STRING, img2 STRING)
OUTPUT (content STRING)
WITH (
'provider' = 'openai-compat',
'model' = 'qwen3.5-plus',
'task' = 'chat/completions',
'content-types' = 'text;image_url;image_url'
);
SELECT content FROM TABLE(ML_PREDICT(
TABLE my_source,
MODEL vl_model_multi,
DESCRIPTOR(prompt, img1, img2)
)); 文本 + 多张图片(数组)
通过 ARRAY<STRING> 类型列传入多张图片,content-types 中使用 multi_image_urls。
CREATE MODEL vl_model_array
INPUT (prompt STRING, images ARRAY<STRING>)
OUTPUT (content STRING)
WITH (
'provider' = 'openai-compat',
'model' = 'qwen3.5-plus',
'task' = 'chat/completions',
'content-types' = 'text;multi_image_urls'
);
SELECT content FROM TABLE(ML_PREDICT(
TABLE my_source,
MODEL vl_model_array,
DESCRIPTOR(prompt, images)
)); 文本 + 视频
CREATE TEMPORARY MODEL sentiment_model
INPUT (video_url STRING, question STRING)
OUTPUT (response STRING)
WITH (
'provider' = 'openai-compat',
'task' = 'chat/completions',
'model' = 'qwen3.6-plus',
'content-types' = 'video_url;text'
);
CREATE TEMPORARY VIEW input_table(content, question)
AS VALUES ('https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20251111/ckgcxt/%E5%8D%95%E4%BA%BA%E8%AF%B4%E8%AF%9D-1.mp4', '有几个人');
SELECT
content,
response AS sentiment
FROM ML_PREDICT(
TABLE input_table,
MODEL sentiment_model,
DESCRIPTOR(content, question));调用时指定参数
示例 1:指定内容类型参数
-- CREATE MODEL 时未指定 content-type / content-types,默认 content-type = text
CREATE MODEL model_single
INPUT (input STRING)
OUTPUT (content STRING)
WITH (
'provider' = 'openai-compat',
'model' = 'qwen3.5-plus',
'task' = 'chat/completions'
);
-- 调用 1:沿用 MODEL 参数,内容类型为 text
SELECT content FROM TABLE(ML_PREDICT(
TABLE source_text,
MODEL model_single,
DESCRIPTOR(input)
));
-- 调用 2:调用时指定内容类型为图片
SELECT content FROM TABLE(ML_PREDICT(
TABLE source_img,
MODEL model_single,
DESCRIPTOR(input),
MAP['content-type', 'image_url']
));示例 2:指定 user-prompt 及列级参数
-- CREATE MODEL 时设置默认配置(多列模型)
CREATE MODEL vl_model
INPUT (text_input STRING, image_input STRING)
OUTPUT (content STRING)
WITH (
'provider' = 'openai-compat',
'model' = 'qwen3.5-plus',
'task' = 'chat/completions',
'content-types' = 'text;image_url',
'user-prompt' = '请描述图片内容'
);
-- 调用 1:使用默认配置
SELECT content FROM TABLE(ML_PREDICT(
TABLE source_a, MODEL vl_model, DESCRIPTOR(text_input, image_input)
));
-- 调用 2:覆盖 user-prompt 和列级参数
SELECT content FROM TABLE(ML_PREDICT(
TABLE source_b, MODEL vl_model, DESCRIPTOR(text_input, image_input),
MAP[
'user-prompt', '请用英文回答',
'image_input.min_pixels', '100',
'image_input.max_pixels', '5000'
]
));
-- 调用 3:覆盖 content-types,改变类型组合(两列都当文本处理)
SELECT content FROM TABLE(ML_PREDICT(
TABLE source_c, MODEL vl_model, DESCRIPTOR(text_input, image_input),
MAP['content-types', 'text;text']
));
-- 调用 4:覆盖 content-types,改变类型组合(翻转顺序)
SELECT content FROM TABLE(ML_PREDICT(
TABLE source_c, MODEL vl_model, DESCRIPTOR(text_input, image_input),
MAP['content-types', 'image_url;text']
));