找回密码
 立即注册
搜索
查看: 1530|回复: 8

[其他] 感谢AI Coding, 约一个小时搞定了RetroArch的AI翻译

[复制链接]
     
发表于 2026-5-4 15:43 | 显示全部楼层 |阅读模式
本帖最后由 泰坦失足 于 2026-5-4 16:24 编辑

事前准备:
V P S主机一个: 可以去Oracle申请永久免费的X86主机. 国内直接搜"国内 免费 **"吧. 注意需要公网IP.
AI Coding 软件一个: 我用Codex, 国内免费的Trae大概也行. 毕竟我前半段是基于GPT 5.4 mini跑的, 后面才切到GPT 5.5来debug.
AI API一个: 两个路线, 如果走 V P S端把图片翻译为文字, LLM只负责日文->中文的翻译, 那就任意模型API都行. 如果是要LLM直接完成输入 游戏截图->中文. 那就需要支持图片输入的模型.

几个细节:
1 AI服务器URL必须是 http://xxx.xxx.xxx.xxx:xxx/ 直接复制进去, 只输IP地址:端口 莫名其妙的会失效. 2 可以在服务器端把图片压缩后再给LLM翻译, 使用token量会大幅度缩小 3 V P S端可以做各种预处理, 比如让系统保存过去几次的翻译记录来保证流畅性. 或者导入专门的文本作为词典.



如何工作:
1 打开AI Coding软件, 连接上**. 给AI API充钱
2. RetroArch-输入-快捷键-AI服务  RetroArch-设置-无障碍-AI服务-准备接下来的设置.
3. 把以下的话输入给AI Coding软件, 然后听天由命吧.. Debug的方式是你配置好AI服务URL, 按下AI服务键. 然后问AI是否在**上正常收到截图.

RetroArch AI Service 图像翻译服务器复现规格书

本文是给 LLM agent 或开发者复现一个 RetroArch AI Service 图像翻译服务器用的技术说明。目标是从零实现一个 HTTP 服务,接收 RetroArch 发来的游戏截图,压缩到不超过 1920x1080,调用 OpenAI 兼容的多模态 LLM API 做 OCR 和翻译,再返回 RetroArch Image Mode 可显示的透明 PNG 覆盖图。本文不包含个人路径、真实公网 IP、真实用户名或真实密钥,所有敏感内容均使用占位符。

一、最终目标和约束

服务需要监听 0.0.0.0:43302 或用户指定端口,并允许公网访问。RetroArch 客户端的 AI Service URL 填 http://你的公网IP或域名:端口/。服务不要求 RetroArch 传鉴权 token,但服务端必须实现每日额度上限,默认一天最多 10.0 额度,用于防止公网滥用。服务主链路必须使用支持图像输入的 OpenAI 兼容 chat completions API,让模型直接看截图进行 OCR 和翻译。服务必须支持 RetroArch Image Mode 返回格式,即返回 JSON,其中包含透明 PNG base64 覆盖图。服务还应提供 /health 和 /last 诊断接口,方便判断问题发生在 RetroArch 客户端、网络、服务端、OCR、翻译还是覆盖图显示环节。

二、建议目录结构

在目标 ** 上创建一个独立项目目录,例如 /opt/retroarch-ai-bridge。目录结构建议为:src/ 存放 Python 源码,spec.yaml 存放配置,cache/ 存放每日额度状态,result/ 存放 latest_request.json 和 requests.jsonl,log/ 存放日志。API 密钥不写入源码和 spec.yaml,而是通过环境变量或 systemd EnvironmentFile 注入。例如 EnvironmentFile 可以放在用户 home 下的 .config 目录中,内容形如 LLM_API_KEY=你的API密钥。不要把密钥文件提交到仓库。

三、推荐技术栈

推荐使用 Python 3 实现。HTTP 服务可以用标准库 http.server 中的 ThreadingHTTPServer 和 BaseHTTPRequestHandler,避免引入 Web 框架。图片处理使用 Pillow。可选安装 pytesseract 和系统 tesseract,作为视觉模型失败时的本地 OCR fallback。配置文件使用 YAML,Python 中用 PyYAML 读取。如果环境不方便安装 PyYAML,也可以改用 JSON 配置。HTTP 请求 LLM 后端可以用 urllib.request 或 requests;为了减少依赖,标准库 urllib.request 足够。

四、配置文件字段

spec.yaml 至少应包含以下字段。server.host 设置为 0.0.0.0,server.port 设置为 43302 或其他端口。api.base_url 设置为 OpenAI 兼容 API 地址,注意通常应是 https://你的API域名/v1,不要在这里包含 /chat/completions。api.model 设置为支持图像输入的模型名。api.api_key_env 设置为环境变量名,例如 LLM_API_KEY。api.request_timeout_seconds 建议 180。api.max_completion_tokens 建议 512。api.prompt_per_mtoken 和 api.completion_per_mtoken 用来估算额度费用,可按供应商价格填写。quota.daily_credit_limit 默认 10.0。llm_image.max_width 设置 1920,llm_image.max_height 设置 1080,llm_image.format 设置 png,llm_image.jpeg_quality 可设置 92。ocr.lang 可设置 jpn+eng,ocr.config 可设置 --psm 6,ocr.fallback_to_tesseract 设置 true。retroarch.default_output 设置 text 或 image,retroarch.default_text_position 设置 1,retroarch.default_target_language 设置 Simplified Chinese。

五、HTTP 路由

必须实现 GET /health、GET /metrics、GET /last、POST /、POST /translate、POST /ai-service。GET /health 返回服务状态、模型名、额度状态、OCR 依赖状态、请求计数、成功计数、错误计数和 uptime。GET /metrics 可以直接复用 /health。GET /last 返回最近一次请求的完整诊断结果,如果没有请求则返回 {"status":"empty"}。POST /、POST /translate、POST /ai-service 都走同一个处理函数,兼容不同 RetroArch 或插件配置。

六、RetroArch 请求解析

POST 请求体是 JSON 对象。需要从 JSON 中读取 image、format、output、source_lang、target_lang、label、viewport。image 是截图 base64,可能是裸 base64,也可能是 data:image/png;base64,开头的 data URL。format 通常是 png,如果 image 是 data URL,应从 data URL 中解析真实格式。output 可能是字符串 image,png,png-a,也可能是数组,也可能只出现在 query string 中。source_lang 可能是 Japanese,也可能是 Don't care。target_lang 可能在 JSON 中,也可能在 query string 的 target_lang 参数中。label 是游戏或核心标签,只用于提示词上下文和诊断记录。viewport 可能是 [width,height] 数组,也可能是 {"width":宽,"height":高} 对象。

解析 output 的规则建议为:如果 payload.output 是字符串,则按逗号切分并 strip;如果是数组,则逐项转字符串;如果 payload 没有 output,则读取 query string 中的 output;如果仍没有,则使用配置 retroarch.default_output。判断是否需要返回图片时,只要 output_modes 中任一项以 image 开头,就认为是 Image Mode 请求。

七、LLM 图片预处理

实现 prepare_image_for_llm(image_b64, image_format, config)。该函数只处理发给 LLM 后端的图片,不改变原始 image_b64,也不影响返回覆盖图尺寸。步骤是:先去掉 data URL 前缀并 base64 解码;用 Pillow 打开图片;记录 original_width 和 original_height;读取 max_width=1920 和 max_height=1080;计算 scale=min(1.0, max_width/original_width, max_height/original_height);如果 scale 小于 1,则用 LANCZOS 等高质量采样缩放到 new_width=int(original_width*scale)、new_height=int(original_height*scale);根据配置输出 PNG 或 JPEG;PNG 用 optimize=True;JPEG 需要把 RGBA 合成到白底 RGB 后保存,quality 默认 92,optimize=True;重新 base64 编码。若原图未缩放且重编码后 base64 更长,则保留原图,避免“压缩”反而变大。返回值应为 llm_image_b64、llm_image_format、llm_image_meta。meta 至少记录 max_width、max_height、original_width、original_height、llm_width、llm_height、resized、reencoded、llm_image_format、llm_image_base64_chars。

八、视觉模型提示词

构造视觉提示词 build_visual_translation_prompt(source_lang, target_lang, label, output_modes)。提示词应明确告诉模型:你是 RetroArch AI Service 的实时游戏截图 OCR 与翻译引擎;当前内容标签是什么;源语言是什么;目标语言是什么;请求输出模式是什么;请直接查看截图,识别所有可见文字并翻译为目标语言;优先识别游戏菜单、对白、按钮、状态栏文字;忽略扫描线、图标、边框和非文字纹理;翻译要短,适合实时覆盖显示;不要解释;不要 Markdown;不要代码块;只返回紧凑 JSON;JSON 必须是 {"original":"识别到的原文","translation":"目标语言译文"};如果没有可读文字,返回 {"original":"","translation":""}。

九、OpenAI 兼容视觉 payload

很多第三方 OpenAI 兼容平台对图片输入格式有差异。复现时必须先用 1x1 PNG 或写有 HELLO WORLD 的小图测试。已知一种常见可用格式如下:POST 到 base_url.rstrip("/") + "/chat/completions";请求头包含 Authorization: Bearer 你的API密钥,Content-Type: application/json,Accept: application/json,User-Agent: RetroArch-AI-Bridge/1.0。JSON body 包含 model、stream:false、max_completion_tokens、messages。messages 是一个数组,里面有一个 user 消息。该 user 消息的 content 是数组,第一项为 {"type":"input_text","text":提示词},第二项为 {"type":"input_image","image_url":"data:image/png;base64,图片base64"}。注意这里 image_url 是字符串,不是 {"url":...} 对象。某些平台要求 {"type":"image_url","image_url":{"url":...}},但也有平台会因此报 unknown parameter 或 invalid type,所以必须实际测试你所用平台。

十、调用 LLM 后端

实现 request_llm_completion(config, api_key, payload)。该函数把 payload 用 json.dumps ensure_ascii=False 编码为 UTF-8,POST 到 /chat/completions。超时时间使用配置 api.request_timeout_seconds。HTTPError 时读取响应体,抛出包含状态码和响应体前 2000 字符的 RuntimeError。URLError 时抛出请求失败。成功后解析 JSON,并检查返回是 dict。不要在日志中打印 API key。为了兼容第三方平台,解析响应文本时从 choices[0].message.content 读取,如果 content 是字符串就 strip;如果 content 是数组,则拼接其中 type 为 text 或 output_text 的 text 字段;如果 choices[0].text 存在,也可以作为兜底。

十一、解析模型返回

实现 parse_visual_translation(content)。先去掉可能的 Markdown JSON fence,例如 ```json 和 ```。如果整段是 JSON 对象,则直接 json.loads;否则提取第一个从 { 到最后一个 } 的片段再尝试 json.loads。成功后读取 original、ocr 或 source 作为原文,读取 translation、translated_text 或 text 作为译文。失败时返回 original="",translation=原始 content。实现 looks_like_vision_failure(original, translation),如果内容包含“看不到图片”“无法查看图片”“不能查看图片”“i can't see”“i cannot see”“cannot view”“unable to view”“no image”等字样,则认为视觉链路失败,触发 fallback。

十二、Tesseract fallback

如果视觉请求失败,或者模型明显表示看不到图片,并且配置 ocr.fallback_to_tesseract 为 true,则走本地 OCR 兜底。实现 ocr_image_text(image_b64, image_format, config):解码原始截图,不要用压缩后的 LLM 图;Pillow 打开后转灰度;如果最大边小于 1600,则放大 2 倍;应用 SHARPEN;应用 autocontrast;调用 pytesseract.image_to_string(image, lang="jpn+eng", config="--psm 6");strip 后得到 OCR 文本。随后构造文本翻译 prompt,调用同一个 chat completions 接口,但 content 用普通字符串,不带图片。fallback 的 ocr_engine 记录为 tesseract_fallback,vision_error 记录视觉链路失败原因。

十三、额度估算和计费

实现 DailyQuotaStore,状态文件为 cache/daily_quota.json。状态包含 day、spent、reserved、updated_at。按服务器配置的时区或本地日期分桶,如果日期变化,自动重置 spent 和 reserved。reserve(estimated_units) 时需要加文件锁,读取状态,检查 spent + reserved + estimated_units 是否超过 daily_credit_limit,超过则抛 QuotaExceededError,HTTP 返回 429。未超过则增加 reserved 并保存。finalize(reserved_units, actual_units) 时再次加锁,reserved 减去本次预留,spent 增加 actual_units。如果 LLM 返回 usage,则按 prompt_tokens/1_000_000*prompt_per_mtoken + completion_tokens/1_000_000*completion_per_mtoken 计算真实费用;如果没有 usage,则用预估费用。预估费用可以用 token_count_estimate(prompt_text)=max(1,len(prompt_text)//4),image_token_estimate(image_b64)=max(256,len(image_b64)//220),再加 max_completion_tokens 的费用,最后乘 1.2 安全系数。注意预估时应使用压缩后的 llm_image_b64,而不是原始截图 base64。

十四、生成 RetroArch 覆盖图

实现 render_overlay_image(translated_text, target_lang, viewport)。如果 viewport 合法,画布尺寸使用 viewport,否则使用 1920x1080。创建 RGBA 透明画布。字体大小可以设置为 max(28, min(58, height//18))。字体优先选择 Noto Sans CJK,其次 DejaVu Sans,如果系统没有 CJK 字体,可用 fc-match 搜索。底部绘制一个半透明黄色或深色圆角矩形,留 padding,文字用黑色或白色。实现 wrap_text_for_width,逐字符测量 textbbox,超过最大宽度就换行。绘制完后用 PNG 保存到 BytesIO,再 base64 编码返回。Image Mode 返回 JSON 应包含 image、text、text_position、auto。text_position 可用配置 default_text_position,auto 可以固定为 continue。即使返回 image,也保留 text 字段用于兼容和调试。

十五、请求处理主流程

handle_retroarch_request 的主流程如下:请求计数加一;解析 image 和 format;调用 prepare_image_for_llm 得到 llm_image_b64、llm_image_format、llm_image_meta;解析 source_lang、target_lang、label、output_modes、viewport;构造视觉 prompt;根据 prompt 和 llm_image_b64 估算费用并 reserve;默认 ocr_engine 为 packy_vision,vision_error 为空;先调用 build_visual_chat_payload 和 request_llm_completion;解析 original 和 translation;如果模型看不到图片则抛错进入 fallback;如果视觉失败且允许 fallback,则对原始 image_b64 做 Tesseract OCR,再调用文本翻译;如果最终 translated_text 为空,则抛错;根据 usage 计算 actual_units 并 finalize;记录 latest_request.json 和 requests.jsonl;如果 output_modes 需要 image,则返回覆盖图 JSON,否则返回纯 text JSON;如果任意异常发生,释放或 finalize 额度,错误计数加一,HTTP 返回 500,QuotaExceededError 返回 429。

十六、/last 诊断记录

每次成功请求都写 result/latest_request.json,并追加 result/requests.jsonl。latest_request.json 应至少包含 status、client_ip、path、output_modes、target_lang、source_lang、label、image_format、image_base64_chars、llm_image、ocr_engine、ocr_text、translated_text、vision_error、response_kind、usage、usage_units、ts。写 JSON 文件时建议先写临时文件,再原子 replace,权限可设置为仅当前用户可读写。追加 jsonl 时可用文件锁,避免并发写交错。/last 直接读取 latest_request.json 返回,如果不存在则返回 {"status":"empty"}。

十七、HTTP 响应细节

所有 JSON 响应使用 Content-Type: application/json; charset=utf-8,Cache-Control: no-store,并设置正确 Content-Length。POST 请求读取 Content-Length,如果没有 body 则当作空 JSON,但 RetroArch 正常会发送 image。JSON 解析失败时返回 500 或 400 均可,建议返回 {"error":"请求体不是合法 JSON"}。路径不匹配时返回 404 和 {"error":"not_found","path":路径}。额度不足时返回 429 和 {"error":"今日额度不足...","auto":"continue"}。服务端日志不要输出 base64 全图,也不要输出 API key。

十八、systemd 部署模板

建议创建 systemd service,WorkingDirectory 指向项目目录,EnvironmentFile 指向密钥文件,ExecStart 使用 /usr/bin/python3 -u 项目目录/src/服务脚本.py --config 项目目录/spec.yaml serve。Restart 设置为 always,RestartSec 设置为 3,KillSignal 设置为 SIGINT 或 SIGTERM,TimeoutStopSec 设置为 30。使用 ThreadingHTTPServer 时,signal handler 中不要直接同步调用 server.shutdown,因为它可能让 systemd restart 等待很久。正确做法是在 signal handler 里启动 daemon 线程执行 server.shutdown。安装后执行 systemctl daemon-reload 或 systemctl --user daemon-reload,再 enable --now 服务。重启测试时记录 restart_seconds,正常应接近 0 秒。

十九、防火墙和公网访问

服务必须监听 0.0.0.0:端口。本机测试访问 http://127.0.0.1:端口/health,公网测试访问 http://你的公网IP或域名:端口/health。如果本机通但公网不通,检查系统防火墙和云平台安全组。系统防火墙需要放行 端口/tcp;云平台 Security List、NSG 或安全组也要放行同样端口。RetroArch AI Service URL 填 http://你的公网IP或域名:端口/,不要误填 https,除非你确实配置了 TLS 反向代理。

二十、RetroArch 客户端配置

打开 RetroArch 的 AI Service 设置。AI Service Enabled 设置为 ON。AI Service URL 设置为 http://你的公网IP或域名:端口/。AI Service Output 设置为 Image Mode。Source Language 可设置 Japanese 或 Don't care。Target Language 设置 zh-CN 或你需要的目标语言。进入游戏后按 AI 翻译热键。若音乐短暂停顿,通常说明 RetroArch 正在截图并发 HTTP 请求。此时立即访问 /last,检查 ts 是否更新。

二十一、端到端验证

第一步访问 /health,确认 status 为 ok,模型名正确,remaining 大于 0。第二步用人工图片测试:生成一张 2560x1440 白底 PNG,上面写 HELLO WORLD,base64 后 POST 到 /?output=image,png,png-a&target_lang=zh-CN,请求体包含 image、format:"png"、output:"image,png,png-a"、source_lang:"en"、target_lang:"zh-CN"、label:"test"、viewport:[2560,1440]。期望响应 JSON 包含 image 和 text,text 应类似“你好,世界”。第三步访问 /last,确认 ocr_engine 为视觉模型,ocr_text 为 HELLO WORLD,translated_text 为你好,世界,llm_image.original_width 为 2560,llm_image.original_height 为 1440,llm_image.llm_width 为 1920,llm_image.llm_height 为 1080,resized 为 true。第四步在 RetroArch 中真实按热键,再看 /last 是否更新为真实游戏 label 和真实截图 OCR。

二十二、常见错误判断

如果 /last 没更新,说明 RetroArch 请求没有到服务器,检查 URL、端口、防火墙、云安全组、AI Service Enabled 和是否填错 http/https。如果 /last 更新但 image_base64_chars 为 0 或缺失,说明请求格式不符合预期,需要检查 image 字段解析。如果 /last 中 vision_error 非空且 ocr_engine 为 tesseract_fallback,说明视觉链路失败但 fallback 生效。如果 translated_text 正常但 RetroArch 屏幕不显示,说明服务器端已经完成,重点检查 RetroArch 是否是 Image Mode、当前核心是否支持 AI overlay、覆盖图是否被 UI 设置遮挡。如果 HTTP 429,说明每日额度用尽。如果 LLM 返回看不到图片,优先检查视觉 payload 格式,尤其 image_url 是字符串还是对象。

二十三、安全和隐私注意事项

不要把真实 API key 写入源码、README、日志或诊断接口。不要在 /last 中记录 Authorization header。不要保存完整原始截图,除非用户明确需要,因为游戏截图可能包含隐私信息;通常只保存 OCR 文本、译文和图片尺寸即可。公网无鉴权服务必须有每日额度限制。更严格的部署可以加 IP 白名单、简单 token、反向代理限流或只绑定内网再通过 ** 访问。

二十四、最小可交付标准

一个复现成功的版本应满足:服务能启动并监听 0.0.0.0:端口;/health 返回 ok;POST 人工图片能得到正确翻译;/last 能记录最近请求;大于 1080p 的输入图在发给 LLM 前被压缩到不超过 1920x1080;Image Mode 返回 JSON 中有 image 字段;超过每日额度返回 429;systemd restart 后服务能自动恢复;RetroArch 真实按键后 /last 会更新。如果这些条件都满足,服务器端功能就已经完成。若屏幕仍不显示,应继续排查 RetroArch 客户端显示设置,而不是优先怀疑 OCR 或翻译链路。




评分

参与人数 1战斗力 +1 收起 理由
2026-03-29注册 + 1 思路广

查看全部评分

回复

使用道具 举报

     
发表于 2026-5-4 17:28 来自手机 | 显示全部楼层
本帖最后由 时空之旅 于 2026-5-4 17:32 编辑

感谢,先收藏,以后试试
略好奇,pc98这一块难道可以畅玩了?
回复

使用道具 举报

     
发表于 2026-5-4 18:06 | 显示全部楼层
时空之旅 发表于 2026-5-4 17:28
感谢,先收藏,以后试试
略好奇,pc98这一块难道可以畅玩了?

用之前的ocr 翻译就可以了吧,主要还是要求翻译端能联系上下文。

ai 眼镜应该也行。
回复

使用道具 举报

     
发表于 2026-5-4 18:32 来自手机 | 显示全部楼层
这一套做完的翻译时延呢?用多模态还要重新复写图片感觉快不了

—— 来自 Xiaomi 25042PN24C, Android 16, 鹅球 v3.5.99
回复

使用道具 举报

     
发表于 2026-5-4 20:14 来自手机 | 显示全部楼层
精钢魔像 发表于 2026-5-4 18:06
用之前的ocr 翻译就可以了吧,主要还是要求翻译端能联系上下文。

ai 眼镜应该也行。

不知道现在能不能实现语音翻译,有些老游戏比如是互动类的,或者没字幕,这一类不知道能不能翻译出来
回复

使用道具 举报

     
发表于 2026-5-4 20:20 | 显示全部楼层
时空之旅 发表于 2026-5-4 20:14
不知道现在能不能实现语音翻译,有些老游戏比如是互动类的,或者没字幕,这一类不知道能不能翻译出来 ...

ai 眼镜是可以的。
回复

使用道具 举报

发表于 2026-5-5 03:32 | 显示全部楼层
厉害,收藏了
回复

使用道具 举报

     
 楼主| 发表于 2026-5-5 05:50 | 显示全部楼层
pf67 发表于 2026-5-4 18:32
这一套做完的翻译时延呢?用多模态还要重新复写图片感觉快不了

—— 来自 Xiaomi 25042PN24C, Android 16, ...

凑合吧, 毕竟我玩Avg也不在乎时延. 还有就是RetroArch不像SakuraTranslator支持实时翻译, 必须按一下AI翻译快捷键进行一次翻译, 所以天生的就有时延.
在**端你也可以修改配置, 让图片本地OCR后走LLM翻译, 然后选择返回文本, RetroArch支持图片和文本两种回传模式(虽然iOS端我只看到图片/语音/讲述人三个模式).
如果是电脑上跑SakuraTranslator可能是更优解, 这套解决方案主要可以给手机平板等非PC设备使用
回复

使用道具 举报

     
 楼主| 发表于 2026-5-5 05:52 | 显示全部楼层
本帖最后由 泰坦失足 于 2026-5-5 06:28 编辑
时空之旅 发表于 2026-5-4 20:14
不知道现在能不能实现语音翻译,有些老游戏比如是互动类的,或者没字幕,这一类不知道能不能翻译出来 ...

市面上挺多实现实时字幕和翻译的软件, PC和非PC上都有.
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|上海互联网违法和不良信息举报中心|网上有害信息举报专区|962110 反电信诈骗|举报电话 021-62035905|Stage1st ( 沪ICP备13020230号-1|沪公网安备 31010702007642号 )

GMT+8, 2026-5-5 07:34 , Processed in 0.057897 second(s), 7 queries , Gzip On, Redis On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表