[omni] OpenAI gpt-audio-1.5 多模态听写输出被包成 JSON 对象(final_text 等字段)后直接上屏

Đang mở
#1,304 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@Breadswim đang làm issue này rồi.

Từ ngày 31/10/2026.

  • #1310 của @Breadswim — đang mở

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
52/100
Loại issue
Lỗi
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
rust
Lĩnh vực
ai, backend-api-design

Hướng nghiên cứu

Start by reading openless-core/src/cloud_providers.rs around the cited build_messages and OpenAICompatibleOmni paths, then compare the Gemini handling in openless-core/src/llm_gemini.rs and its clean_polish_output call. Check how streaming deltas are handled and identify suitable tests; the issue does not name existing test files. Done means the listed acceptance criteria are met, including ordinary-text passthrough, JSON-shaped output coverage, and validation with real gpt-audio-1.5.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

现象
  • 触发条件:

    • 识别管线:多模态模式;供应商 OpenAI(支持音频);模型 gpt-audio-1.5;接口地址 https://api.openai.com/v1
    • 风格:内置「清晰结构」「轻度润色」均复现;流式上屏开启
    • 平台 / 版本:macOS;2.0.1+build.20261016 与 2.0.2-Beta.3+build.20261026-c9f49c60 均复现。代码定位基于 beta 最新提交 85b243ab,多模态相关代码自 c9f49c60 起没有改动
  • 当前表现:插入的不是纯文本,而是模型自行组织的 JSON 风格包裹:键名不固定,有时连合法 JSON 都不是,甚至会改写正文。已观察到的形态:

    形态 次数
    {"final_text": "…"} 29
    {"final_text": "…", "translated_text": "…"}(开启翻译时,见 #1306) 2
    {"original_text": "…", "final_text": "…"}(两个字段内容相同) 1
    {"raw_transcript": "…"}(内容像未整理的原始转写,句末没有标点) 1
    {"…"}:有大括号和引号,但没有键名,不是合法 JSON 1
    修改说明被包在大括号里输出,后面才是正文,开头为 {"这里面"平板因为使用"这部分不太通顺,可能是口误,去掉后整理如下:} 1
    正文被改写:口述的 .env 变成了 {"env"},例如 {"env"} 文件就是刚才提到的环境配置文件。 1

    示例:

    {"final_text": "现在我已经换成轻度润色了。"}
    {"raw_transcript": "你可以把我现在发的信息和包括上一条的信息使用轻度润色"}
    

    多行结果里的换行以字面 \n 出现(JSON 转义没有还原)。流式上屏开启时,JSON 字符随 SSE delta 逐段打进目标 app。

  • 证据:

    • 本机全部 36 条非空多模态记录(清晰结构 14 条、轻度润色 22 条)都带有 JSON 风格的包裹:33 条是 JSON 对象,3 条是只有大括号、不是合法 JSON 的输出(其中 1 条改写了正文),可稳定复现。

    • 日志示例:

      [omni] POST https://api.openai.com/v1/chat/completions provider=openai model=gpt-audio-1.5 audio=true stream=true
      [omni] stream done; total chars=35
      
初步定位
  1. 请求没有要求 JSON。omni_body 只包含 model / stream / messages / temperature 和 thinking 控制参数,没有 response_format;提示词模板和请求构造代码里也没有这些键名。JSON 外壳来自模型输出本身。
  2. 多模态提示词没有针对音频输入适配。听写结束时以「传统润色 system prompt + 空 user 文本」调用模型,user 消息里只有 input_audio:

https://github.com/Open-Less/openless/blob/85b243abd83abb295cd99e0ec31ba483c3f5c6fe/openless-all/app/crates/openless-core/src/cloud_providers.rs#L2531-L2533

传统模式 user 消息末尾的「只输出整理后的文本正文。」(prompts::user_prompt)在这条路径上被丢掉;system prompt 末尾的安全约定(prompts::polish_injection_defense)仍在描述 <raw_transcript> 信封,但请求里没有这个信封。
3. OpenAI 兼容多模态通道没有输出清洗。传统润色和 Gemini 多模态(complete_omni)都会经过 clean_polish_output:

https://github.com/Open-Less/openless/blob/85b243abd83abb295cd99e0ec31ba483c3f5c6fe/openless-all/app/crates/openless-core/src/llm_gemini.rs#L266

而 OpenAICompatibleOmni 直接返回原始文本,听写结束时只做 trim()(补上 clean_polish_output 本身也解不开 JSON 外壳):

https://github.com/Open-Less/openless/blob/85b243abd83abb295cd99e0ec31ba483c3f5c6fe/openless-all/app/crates/openless-core/src/cloud_providers.rs#L2547

  1. 键名都取自提示词里的用词,而且出现了直接违反输出要求的结果,说明模型是在借用提示词词汇自行组织 JSON,而不是遵循输出要求;问题与具体风格无关:

    键名 提示词中的对应内容
    final_text 风格模板中的「直接输出最终文本正文」(清晰结构)/「直接输出最终正文」(轻度润色)
    raw_transcript 安全约定 polish_injection_defense 中的 <raw_transcript>;该词不出现在任何风格模板里,多模态请求里也没有这个信封
    original_text 轻度润色模板中的「不输出原文」
    translated_text 多模态翻译段中的「只输出译文,不要输出源文」

    OpenAI 将 gpt-audio-1.5 定位为 Chat Completions 中「音频输入、音频输出」的语音模型,模型页没有列出转写端点和结构化输出(模型页)。

影响
  • 用 OpenAI 音频模型的多模态听写基本不可用:每次都要手动删掉 JSON 外壳和字面 \n。
  • 流式上屏开启时,JSON 字符在模型返回过程中就已经插入目标 app,只在最后清洗最终文本不够。
  • 正文可能被改写:.env 变成 {"env"} 这类改动不显眼,容易混进命令、配置或代码;修改说明也会被当作正文插入。事后清洗只能剥掉套在整段外面的壳,修不好正文中间的改动,所以修复重点必须放在提示词侧。
  • OpenAI 兼容多模态通道也被其他供应商(如百炼 / Qwen Omni)共用,它们同样没有输出清洗和面向音频的提示词;是否出现同样现象未验证。
建议接受标准
  • OpenAI gpt-audio-1.5 多模态听写(流式上屏开 / 关;清晰结构、轻度润色风格)最终插入纯文本,不含 JSON 外壳、键名或字面 \n。
  • 多模态请求为音频输入提供专门的指令:例如在 input_audio 旁附一段文字(build_messages 已支持音频 + 文字),明确只输出纯文本正文,不要 JSON、字段名或代码块;安全约定改为描述音频输入,而不是 <raw_transcript>。
  • OpenAI 兼容多模态通道的输出与 Gemini 通道一样经过 clean_polish_output。
  • 如果模型仍返回 JSON 对象,最终文本和流式 delta 都能还原为纯文本:不依赖固定键名,至少覆盖上表中整段被包裹的五种形态(多字段时按明确规则选取字段);不是 JSON 对象的输出原样保留。
  • 正文中不出现模型自行添加的大括号、引号或修改说明(例如 .env 不会被改写成 {"env"})。这类问题无法靠事后清洗可靠修复,需要由提示词改造保证,并用真实模型验证。
  • 单元测试覆盖:JSON 外壳解包(含转义字符、跨 delta 切分、多种键名)、普通文本原样透传。
  • 用真实 gpt-audio-1.5 做修复前后对照。
TODO / 不确定项
  • 「正式表达」「原文」风格尚未实测;「清晰结构」「轻度润色」已确认复现。
  • 代码中 OpenAI 多模态的默认模型 gpt-4o-audio-preview,以及其他 OpenAI 兼容多模态供应商是否有同样现象,尚未验证。
  • 对 JSON 外壳做兜底解包需要取舍(用户确实口述 JSON 的情况);多字段时选取哪个字段的规则需维护者确认。
  • 翻译相关的问题(译文时有时无、以 translated_text 字段返回)另见 #1306。
  • 我可以负责修复,并用自己的 OpenAI Key 做真实模型验证,之后提 PR 关联本 issue。
Ngôn ngữ chính
Rust
Star
3.7k
Fork
349
Merge trung bình
2 ngày 14 giờ
Pull request đã merge (30 ngày)
97

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của Open-Less/openless

Tất cả issue của Open-Less/openless

Issue tương tự

Thêm issue về Rust

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.