输入关键词搜索

多模态接入

更新时间: 2026/07/14 16:10:49

多模态接入用于让 AI 硬件智能体在语音对话中理解图像内容。典型交互是用户说“看看这是什么”,智能体判断需要调用拍照能力后,由端上完成拍照或截图上传,再把图片和用户问题一起发送给云端多模态模型,最后以语音或字幕形式返回识别结果。

本文以 Android Demo 和硬件 Demo 中的拍照识图链路为例,说明多模态接入的适用场景、能力范围、接入方式、端上约束、平台配置和常见问题排查。

适用场景

适用场景 典型诉求 推荐接入方式
App / 平板 / 带屏终端 用户通过摄像头让智能体识别眼前物体、商品、文本、环境 音视频通话 NERTC SDK + 截图上传
智能硬件摄像头 用户对设备说“看看这是什么”,设备拍照后由智能体解释 嵌入式 NERTC SDK + 端侧拍照上传
陪伴机器人 / 桌面宠物 结合语音、表情和摄像头实现“看见并回应” 嵌入式 NERTC SDK + Function Call / MCP 工具
客服、导购、教育类助手 识别图片中的商品、作业、说明书或场景 App 端上传图片 + 多模态 LLM 识别

不建议用于需要高精度工业检测、医疗诊断、安防告警等强准确性场景。此类场景需要额外接入专业视觉模型或人工复核流程。

能力范围

当前最佳实践主要覆盖 图片理解

  • 通过语音触发拍照,例如“看看这是什么”。
  • 端上采集当前摄像头画面或视频流截图。
  • 将图片与文本问题一起发送给智能体。
  • 智能体基于图片内容生成自然语言解释。
  • 通过 TTS 语音、字幕或 UI 文案返回结果。

不包含以下能力:

  • 连续视频流理解。
  • 实时目标检测或定位。
  • OCR、商品识别、医学识别等垂类高准确能力。
  • 对图片内容的确定性判断或合规审核结论。

开通服务

接入多模态前,需要确认以下能力已开通或配置完成,部分详情请参考 开通服务

能力 说明
音视频通话 2.0 用于 App 或设备接入 RTC 房间,传输音频、视频和 AI 语音。
实时对话 AI 用于创建 AI 任务,完成 ASR、LLM、TTS 编排。
智能体平台 用于配置智能体、人设、模型、工具调用能力。
多模态 LLM / 视觉理解能力 用于处理图片 + 文本问题。具体供应商和模型以控制台开通结果为准。
设备 License(硬件场景) 智能硬件量产或测试时,需要按设备授权口径接入。

同时,需要在智能体后台完成智能体基础信息、LLM 模型、提示词、TTS 音色、权限与授权等配置。拍照识图 Tool 的配置方式与接入路线有关,详见下文对应接入方式。

生产环境中,RTC Token、AI 任务创建和关闭建议由应用服务器统一处理,避免 AppSecret 或敏感配置下发到 App 端。

接入方式

Android / App 端截图上传(Function Call 方案)

Android / App 端仅支持 Function Call 方案:需要先在 Agent 后台配置拍照识图 Tool,由 LLM 判断用户意图并触发工具调用。App 先加入 RTC 房间并创建 AI 任务,随后监听 onAiData(type="tool"),收到 camera_capture 后调用 SDK 截图上传接口。

配置拍照识图 Tool

网易云信智能体平台 中,进入目标智能体的角色配置页,在 Tools 配置 中增加拍照识图相关 Tool。

13.拍照识图Tool配置.png

以 App 端截图上传为例,可以配置 Tool 名称为 camera_capture,并在 Tool 描述中说明触发条件,例如:

text用户如果说类似“看看这是什么”,执行这个 tool。

配置完成后,智能体在识别到“看看这是什么”“帮我看一下”“识别一下这个”等意图时,会通过 onAiData(type="tool") 下发工具调用。App 端收到 camera_capture 后,再调用 takeSnapShotAndRequestLLM 完成截图上传。

工具名称需要与端上代码保持一致。Android Demo 中监听的是 camera_capture。实际项目应统一工具名、入参和返回结构,避免智能体下发的工具名与端上实现不一致。

链路如下:

text用户说“看看这是什么”
-> ASR 识别用户语音
-> LLM 判断需要调用拍照工具
-> App 收到 onAiData(type="tool")
-> 解析 toolCalls 中的 camera_capture
-> 调用 takeSnapShotAndRequestLLM(text, uid, streamType)
-> SDK 截取本地视频画面并上传
-> 多模态模型根据图片和问题生成回答
-> TTS 返回语音,App 播放并展示字幕

关键配合点:

环节 App / SDK 配合 说明
入会 初始化 NERTC SDK,开启本地音频和视频,加入 RTC 房间 本地视频必须可用,否则无法截图。
创建 AI 任务 调用客户端 API POST https://rtc-ai.yunxinapi.com/ai/task/create,配置 ASR、LLM、TTS 和 Agent 需要使用 RTC Token 鉴权。参考 开启实时对话
开启字幕 调用 ASR Caption 能力 用于展示用户问题和 AI 回复。
工具调用 监听 onAiData(type, data) type="tool" 且工具名为 camera_capture 时触发截图。
截图上传 调用 takeSnapShotAndRequestLLM(text, 0, 1) uid=0 表示本地用户,streamType=1 表示走小流。

建议开启双流:大流用于本地预览,小流用于截图上传。这样可以保证预览清晰,同时降低上传图片大小和网络耗时。

如需完整 Android / App 端 Demo,可以联系网易云信技术支持获取。

Android 示例代码

以下示例展示 App 如何解析 onAiData 中的工具调用,并在收到 camera_capture 后调用截图上传接口。

1. 开启本地视频和双流

在入会前初始化 SDK,并开启本地视频。建议开启双流,小流用于截图上传。

javaprivate void enableVideoForSnapshot() {
    // 伪代码:初始化 NERTC SDK 并加入 RTC 房间。
    // NERtcEx.getInstance().init(context, appKey, callback, options);
    // NERtcEx.getInstance().joinChannel(token, channelName, uid);

    // 开启双流:大流用于本地预览,小流用于截图上传。
    NERtcEx.getInstance().enableDualStreamMode(true);

    // 开启本地音视频,确保后续可以截取本地视频画面。
    NERtcEx.getInstance().enableLocalAudio(true);
    NERtcEx.getInstance().enableLocalVideo(true);
}

2. 解析 onAiData 工具调用

当智能体判断需要拍照识图时,会通过 onAiData 下发工具调用数据。App 需要判断 type 是否为 tool,并解析 toolCalls 中的工具名称。

javaprivate static final String TOOL_CAMERA_CAPTURE = "camera_capture";

@Override
public void onAiData(String type, String data) {
    if (!"tool".equals(type) || data == null) {
        return;
    }

    try {
        JSONObject json = new JSONObject(data);
        String text = json.optString("request", "看看这是什么。");
        JSONArray toolCalls = json.optJSONArray("toolCalls");
        if (toolCalls == null) {
            return;
        }

        for (int i = 0; i < toolCalls.length(); i++) {
            JSONObject toolCall = toolCalls.optJSONObject(i);
            if (toolCall == null) {
                continue;
            }

            JSONObject function = toolCall.optJSONObject("function");
            if (function != null && TOOL_CAMERA_CAPTURE.equals(function.optString("name"))) {
                doSnapShotAndRequestLLM(text);
                return;
            }
        }
    } catch (JSONException e) {
        Log.e("AICapacity", "parse onAiData failed", e);
    }
}

onAiData 中的工具调用数据示例:

json{
  "request": "看看这是什么。",
  "toolCalls": [
    {
      "type": "function",
      "function": {
        "name": "camera_capture",
        "arguments": "{}"
      }
    }
  ]
}

3. 调用截图上传接口

收到 camera_capture 后,调用 takeSnapShotAndRequestLLM 截取本地视频画面,并将图片和文本问题一起发送给智能体。

javaprivate void doSnapShotAndRequestLLM(String text) {
    String question = (text == null || text.trim().isEmpty()) ? "看看这是什么。" : text;

    // 伪代码:调用前请先确认已入会、本地视频已开启、AI 任务已创建成功。
    // if (!hasJoinedChannel || !localVideoEnabled || !aiTaskCreated) return;

    // uid 传 0 表示截取本地用户画面。
    // streamType 传 1 表示使用小流截图,降低图片大小和上传耗时。
    int result = NERtcEx.getInstance().takeSnapShotAndRequestLLM(question, 0, 1);
    if (result != NERtcConstants.ErrorCode.OK) {
        Log.e("AICapacity", "takeSnapShotAndRequestLLM failed, result=" + result);
    }
}

接口参数说明:

参数 示例值 说明
text 看看这是什么。 随图片一起发送给智能体的问题。
uid 0 截图目标用户。传 0 表示本地用户。
streamType 1 截图流类型。建议传 1 使用小流。

调用前请确认:

  • App 已加入 RTC 房间。
  • 本地视频已开启。
  • AI 任务已创建成功。
  • 智能体已配置并能下发 camera_capture 工具调用。

智能硬件端拍照上传(Function Call / 端侧 MCP 方案)

智能硬件端支持两条技术路线:

  • Function Call 方案:与 App 端类似,在 Agent 后台配置拍照识图 Tool,由 LLM 判断用户意图并触发工具调用,设备收到工具调用后拍照上传。
  • 端侧 MCP 方案:参考当前硬件 Demo,在设备侧注册 self.photo_explainself.camera.take_photo 等 MCP 工具,由设备侧工具处理拍照、编码和发送图片。

当前硬件 Demo 采用端侧 MCP 方案实现拍照识图。可参考 GitHub 上的 ESP32 示例工程:nertc-iot-demo/esp32/nertc_demo

链路如下:

text用户说“看看这是什么”
-> 设备进入语音对话链路
-> 智能体触发拍照识图工具
-> 设备调用 PhotoExplain
-> 摄像头 Capture 当前画面
-> 将 JPEG 转为 Base64 或通过视觉 URL 上传
-> 调用 SendLlmImage 发送图片和问题
-> 多模态模型生成解释
-> 设备播放 TTS 回复

硬件 Demo 中存在两类实现:

实现方式 说明 适用情况
PhotoExplain + SendLlmImage 端侧手动触发拍照后,将 JPEG 转为 Base64,并通过 NERTC 协议发送图片和问题 适合嵌入式 NERTC 接入链路。
端侧 MCP 方式 在设备侧注册 self.photo_explainself.camera.take_photo 等工具 适合参考当前硬件 Demo、由设备侧承载工具实现的场景。

设备需要实现摄像头初始化、拍照、图片编码、内存释放和异常处理。ESP32-S3 等设备通常需要 PSRAM 支持,图片分辨率和质量需要根据内存、网络和识别效果权衡。

智能硬件示例代码

以下示例展示智能硬件如何从“看看这是什么”触发拍照识图。示例基于硬件 Demo 的实现方式,重点展示触发、拍照、编码和发送图片的关键逻辑。

1. 触发拍照识图

如果设备上有独立摄像头按键,可以在按键回调中调用拍照识图方法。若由智能体识别“看看这是什么”等意图,也可以复用同一个方法。

cppvoid RequestPhotoExplain(const std::string& question) {
    // 伪代码:调用前请先确认设备已联网、已建立实时对话链路、摄像头可用。
    // if (!deviceReady || !cameraReady) return;

    Application::GetInstance().PhotoExplain(question, "我来看一下");
}

2. 注册端侧 MCP 拍照工具

如果采用端侧 MCP 方案,可以在设备侧注册工具。例如,当用户说“看看这是什么”时,智能体调用 self.photo_explain,设备收到工具调用后执行拍照识图。

cppvoid RegisterPhotoExplainTool() {
    AddTool("self.photo_explain",
        1,
        PropertyList({
            Property("question", kPropertyTypeString),
            Property("pre_answer", kPropertyTypeString)
        }),
        [](const PropertyList& properties) -> ReturnValue {
            std::string question = properties["question"].value<std::string>();
            std::string preAnswer = properties["pre_answer"].value<std::string>();

            if (question.empty()) {
                question = "看看这是什么。";
            }

            Application::GetInstance().PhotoExplain(question, preAnswer);
            return "{\"success\":true}";
        });
}

工具入参建议:

参数 说明
question 用户关于图片的问题,例如“看看这是什么”。
pre_answer 可选。拍照前先播报的提示语,例如“我来看一下”。

3. 拍照、编码并发送图片

PhotoExplain 中完成摄像头拍照、获取 JPEG、Base64 编码,并通过 SendLlmImage 把图片和问题发送给智能体。以下代码保留核心逻辑,摄像头初始化、任务调度和异常提示可按设备工程实现补充。

cppvoid Application::PhotoExplain(const std::string& question,
                               const std::string& preAnswer) {
    // 伪代码:如需先播报“我来看一下”,可在这里调用 TTS。
    // if (!preAnswer.empty()) protocol_->SendTTSText(preAnswer, 2, false);

    Camera* camera = Board::GetInstance().GetCamera();
    if (camera == nullptr || !camera->Capture()) {
        ESP_LOGE("PhotoExplain", "camera capture failed");
        return;
    }

    JpegChunk jpeg = {nullptr, 0};
    if (!camera->GetCapturedJpeg(jpeg.data, jpeg.len) || jpeg.data == nullptr || jpeg.len == 0) {
        ESP_LOGE("PhotoExplain", "invalid JPEG data");
        if (jpeg.data != nullptr) {
            heap_caps_free(jpeg.data);
        }
        return;
    }

    const char* prefix = "data:image/jpeg;base64,";
    size_t prefixLen = strlen(prefix);
    size_t outputLen = ((jpeg.len + 2) / 3) * 4 + prefixLen + 1;
    unsigned char* image = static_cast<unsigned char*>(malloc(outputLen));
    if (image == nullptr) {
        heap_caps_free(jpeg.data);
        return;
    }

    memset(image, 0, outputLen);
    memcpy(image, prefix, prefixLen);

    size_t encodedLen = 0;
    int result = mbedtls_base64_encode(
        image + prefixLen,
        outputLen - prefixLen,
        &encodedLen,
        jpeg.data,
        jpeg.len
    );

    if (result == 0) {
        // 第 1 个参数:图片内容,格式为 data:image/jpeg;base64,...
        // 第 2 个参数:图片内容长度。
        // 第 4 个参数:随图片一起发送给 LLM 的问题。
        // 第 5 个参数:0 表示 Base64 图片内容。
        protocol_->SendLlmImage(
            reinterpret_cast<const char*>(image),
            prefixLen + encodedLen,
            0,
            question,
            0
        );
    }

    free(image);
    heap_caps_free(jpeg.data);
}

4. 使用图片 URL 方式发送

如果图片已上传到自有对象存储,也可以直接向 LLM 发送图片 URL。此方式适合图片由业务服务器统一存储、审核或加签的场景。

cppvoid Application::SendImageUrlToLLM(const std::string& imageUrl,
                                    const std::string& question) {
    // 最后一个参数传 1,表示图片来源为网络图片 URL。
    protocol_->SendLlmImage(imageUrl.c_str(), imageUrl.size(), 0, question, 1);
}

其中最后一个参数用于区分图片来源类型,0 表示 Base64 图片内容,1 表示网络图片 URL。具体取值以当前 SDK / 协议版本为准。

5. 调用前检查项

  • 设备已完成联网,并已建立实时对话链路。
  • 摄像头已初始化,Board::GetInstance().GetCamera() 能返回有效对象。
  • 设备状态允许拍照识图,例如不在 Wi-Fi 配网、启动中或关闭对话状态。
  • 设备有足够内存完成 JPEG 缓冲和 Base64 编码,建议使用带 PSRAM 的硬件。
  • 图片尺寸和 JPEG 质量经过验证,不会导致上传过慢或内存不足。
  • 工具名、工具入参和智能体平台中的工具配置保持一致。

端上约束

App 端

  • 需要摄像头和麦克风权限,并在隐私协议中说明图片上传用途。
  • 调用截图上传前,用户必须已入会、本地视频已开启、AI 任务已创建。
  • 建议开启双流,截图上传走小流,避免高清图导致上传慢或卡顿。
  • 截图上传失败时,需要给用户明确提示,例如“我没看到画面,可以再试一次吗”。
  • AppSecret 不应内置在 App 端,生产环境应由服务端签发 RTC Token。

硬件端

  • 设备必须带摄像头,并完成摄像头驱动、方向、镜像、曝光等适配。
  • 建议支持 PSRAM,否则 Base64 编码和 JPEG 缓冲可能失败。
  • 图片分辨率和 JPEG 质量不宜过高,需兼顾识别效果、内存占用和上传耗时。
  • 需要处理拍照失败、编码失败、网络失败、模型无响应等异常。
  • 若设备无屏,需要通过灯效、提示音或 TTS 告知用户“正在看”或“识别失败”。

常见问题排查

问题 可能原因 排查建议
用户说“看看这是什么”后没有拍照 智能体未触发工具调用,或工具名不匹配 检查 onAiData / MCP 日志,确认是否下发 camera_capture 或约定工具名。
App 截图上传失败 未入会、本地视频未开启、AI 任务未创建 检查入会状态、视频状态、task/create 响应和 SDK 返回码。
图片上传慢或卡顿 图片分辨率过高,走了大流截图 开启双流,截图上传使用小流;降低图片质量。
硬件拍照失败 摄像头未初始化、帧缓冲为空、设备状态不允许拍照 检查摄像头初始化日志、Capture 返回值和设备状态。
硬件编码失败或重启 内存不足,Base64/JPEG 缓冲过大 降低分辨率和 JPEG 质量,确认 PSRAM 可用,释放临时内存。
AI 回复“我看不到图片” 图片未成功发送到 LLM,或模型不支持视觉输入 检查 SendLlmImage 调用、上传响应、模型是否支持多模态。
识别结果不准确 图片模糊、光线不足、问题不明确 引导用户对准目标、保持稳定,问题中补充识别目标。
线上无法创建 AI 任务 鉴权错误或能力未开通 检查 RTC Token、AppKey、Cname、Uid、实时对话 AI 和多模态能力开通状态。

上线建议

  • 明确告知用户摄像头和图片上传用途,获得必要授权。
  • 对“看看这是什么”等触发词设置自然提示,但不要强依赖固定口令。
  • 工具调用失败时准备兜底话术,例如“我刚刚没拍到,可以再让我看一次吗”。
  • 建议在端侧记录 requestId、工具调用时间、SDK 返回码和图片大小,便于线上排查。
  • 硬件量产前,需要在弱网、暗光、移动画面、连续触发拍照等场景下验证稳定性。
此文档是否对你有帮助?
有帮助
去反馈
  • 适用场景
  • 能力范围
  • 开通服务
  • 接入方式
  • Android / App 端截图上传(Function Call 方案)
  • 配置拍照识图 Tool
  • Android 示例代码
  • 1. 开启本地视频和双流
  • 2. 解析 onAiData 工具调用
  • 3. 调用截图上传接口
  • 智能硬件端拍照上传(Function Call / 端侧 MCP 方案)
  • 智能硬件示例代码
  • 1. 触发拍照识图
  • 2. 注册端侧 MCP 拍照工具
  • 3. 拍照、编码并发送图片
  • 4. 使用图片 URL 方式发送
  • 5. 调用前检查项
  • 端上约束
  • App 端
  • 硬件端
  • 常见问题排查
  • 上线建议