手把手教你用 AI 做一个“看图学英语”应用:从图像识别到语音播放

336 阅读3分钟

本文介绍一个通过上传图片学习英语单词的交互系统。用户选择一张图片后,系统调用大模型分析内容,提取核心英文单词,并生成描述、例句和对话示例,最后通过语音合成播放该单词的发音,实现“看图学词”的完整流程。

前端通过文件输入框获取用户上传的图片,并将其转换为 Base64 格式的数据 URL,以便在后续请求中使用。图片预览通过 imgPreview 状态实时展示。

系统调用月之恋(Moonshot)的视觉大模型 API 进行图像分析。请求配置如下:

js
浅色版本
const endpoint = 'https://api.moonshot.cn/v1/chat/completions';
const headers = {
  'Authorization': `Bearer ${import.meta.env.VITE_KIMI_API_KEY}`,
  'Content-Type': 'application/json'
};

const response = await fetch(endpoint, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    model: "moonshot-v1-8k-vision-preview",
    messages: [
      {
        role: 'user',
        content: [
          {
            type: 'image_url',
            image_url: { url: data }
          },
          {
            type: 'text',
            text: userPrompt
          }
        ]
      }
    ]
  })
});

提示词

const userPrompt = `
请仔细分析图片内容,并完成以下任务:

1. 找出最能代表该图片的一个核心英文单词。
   - 要求:必须是 CEFR A1~A2 水平的简单词汇(如 child, dog, ball, eat, run),避免复杂词或抽象词。
   - 如果图片有多个元素,请选择最突出、最中心的那个事物对应的单词。

2. 基于图片和所选单词,生成以下 JSON 格式的数据:
{ 
  "image_description": "用一句话简要描述图片内容,客观准确。",
  "representative_word": "选出的那个英文单词(小写)",
  "example_sentence": "一个包含该单词的简单英文句子,主语优先用 'The' 或 'A',语法正确,A1水平。",
  "explanation": "以 'Look at...' 开头,用简单英文解释图片与单词的关系。每句话单独一行(用 \\n 分隔),描述画面细节。最后一行提出一个与日常生活相关的简单问句。",
  "example_replys": [
    "用户可以用这个单词或句子回答的自然回复(英文)",
    "另一个可能的回答,鼓励互动"
  ]
}

📌 注意事项:
- 所有字段必须基于图片实际内容,不要猜测或虚构。
- explanation 中每句话要短(不超过10词为佳),适合英语初学者阅读。
- example_replys 必须是日常口语化表达,能真实用于对话。
- 输出仅返回 JSON 对象,不要额外说明。

现在请开始分析图片:
`;

userPrompt 是一段结构化提示词,要求模型返回 JSON 格式的分析结果,包含图片描述、代表单词、例句、解释和回复示例。AI 返回结果后,前端解析 res.choices[0].message.content,并将各字段存入 React 状态:

const res = await response.json();
const example = JSON.parse(res.choices[0].message.content);

setWord(example.representative_word);
setSentence(example.example_sentence);
setExplanation(example.explanation);
setReplys(example.example_replys);

界面展示方面,核心单词和播放按钮居中显示。通过 detailExpand 状态控制详情展开:

<button onClick={() => setDetailExpand(!detailExpand)}>Talk about it</button>
{
  !detailExpand ? 
    <div className='fold'></div> : 
    (
      <div className="expand">
        <img src={imgPreview} alt="" />
        <div className="explaination">
          <p>{explanation}</p>
        </div>
        <div className="reply">
          <p>{replys[0]}</p>
          {replys[1] && <p>{replys[1]}</p>}
        </div>
      </div>
    )
}

语音播放功能通过调用字节跳动的 TTS 服务实现。为避免跨域问题,Vite 配置了代理:

server: {
  proxy: {
    '/tts': {
      target: 'https://openspeech.bytedance.com',
      changeOrigin: true,
      rewrite: (path) => path.replace(/^/tts/, ''),
    },
  },
}

前端请求 /tts 接口生成音频,收到 Base64 数据后,需转换为 Blob URL 才能播放:

function createBlobUrl(base64AudioData) {
  const byteCharacters = atob(base64AudioData);
  const byteArrays = [];
  for (let i = 0; i < byteCharacters.length; i++) {
    byteArrays.push(byteCharacters.charCodeAt(i));
  }
  const blob = new Blob([new Uint8Array(byteArrays)], { type: 'audio/mp3' });
  return URL.createObjectURL(blob);
}

播放时创建 Audio 实例并调用 play()

const playAudio = async (audio) => {
  const au = new Audio(audio);
  au.play();
};

<div className="playAudio" onClick={() => playAudio(audio)}>
  播放音频
</div>