活动介绍
谷歌开发者招募活动是专为 Google 技术的爱好者及开发者们开展的活动,旨在鼓励大家通过多种形式 (文章/视频/coding 等) 创作与 Google 技术相关的讲解分享、实践案例或活动感受等内容,展示代码、框架、平台在真实世界中的生动表现,以及分享您应用 AI 技术的故事经历与成果。
作者简介
本文作者 Jax,从事 Web 开发工作已有 7 年,也从中获得了很多乐趣。
文章导读
源自近期的观察和思考,作者萌生了借助 AI 为老年人提供关怀的想法。在谷歌开发者社区举办的 Gemma Hackathon 中将自己的想法付诸了实践,打造了一款 "药童" 的 Web 应用,巧用 LLM 特性帮助老年人轻松读懂药品说明书,展示了科技世界的温度。
*以下为投稿原文,有改动
引言
"变老很糟糕 (Getting old sucks)",这是电影《勇敢者的游戏》里的一句台词。据统计,截止 2023 年底,60 岁以上人口占全国总人口的 21.1%。若干年后,这个比重可能会更大,而我们自己也将成为构成这一比重的分子之一。如何帮助老年人活得更舒适一些?如何帮助若干年以后的我们自己?是我最近在思考的问题。
某天偶然间,我看到谷歌开发者社区将举办 Gemma 黑客松开发者竞赛,鼓励开发者基于 Gemma 模型技术来展示科技温度。这几乎在一瞬间就激发了我的灵感 —— LLM 擅长处理大段文字,这不正适合用来帮老年人提升阅读体验吗?对视力不好,甚至识字不全的长辈来说,当身边无人帮助时,吃药之前读说明书往往是非常困难的事情。用大语言模型来处理药品说明书的文字内容,并以语音问答的形式来辅助理解,无论是从技术可行性还是用户友好性的角度来考虑,似乎都非常合理!
于是,热爱编程的我和队友在竞赛中完成了我们的作品 —— 药童,一个帮助爷爷奶奶们读懂药品说明书的 Web 应用。
△ 作品演示
如录屏视频所示,爷爷奶奶们只需要用手机拍下药品说明书,然后就可以像语音聊天一样了解药品的信息了。这样的极简交互,尽可能地降低了学习和操作成本,让用户可以即开即用。
接下来,我将会从技术实现层面拆解各个模块,希望其中的点滴也能激发你的灵感。你将会看到 OCR、RAG、ASR、TTS…… 如果你对这些名词不太了解,别急,且听我娓娓道来。
技术实现
从用户上传照片,到回复用户的提问,其背后的处理流程是这样的:
从图片中提取文字内容;
将提取出的文本内容切分、向量化,存储到向量数据库中;
用户发送语音问题后,把语音转为文字格式;
将问题文本与向量数据库进行匹配,检索出高相关度的知识块;
将相关知识上下文与问题文本一起包装成提示词,发送给 LLM;
LLM 根据提示词生成文本格式的回答内容;
将回答文本转化为语音,播放给用户听。
OCR: 从图片提取文字
想要提取图片中的文字,有多种方式可以实现,我了解到的有两个方向: 一个是经典的光学字符识别 (Optical Character Recognition) 思路,比如 Tesseract、PaddleOCR 等;另一个则是视觉问答 (Visual Question Answering) 模式,多模态的 Gemini API*、PaliGemma 等都有能力做到。
*适用于出海开发者
Tesseract
https://github.com/tesseract-ocr/tesseract
PaddleOCR
https://github.com/PaddlePaddle/PaddleOCR
Gemini
https://gemini.google.com/
PaliGemma
https://huggingface.co/google/paligemma-3b-ft-ocrvqa-896
在比赛时间有限的情况下,我选择了个人比较熟悉的 PaddleOCR。处理过程的核心逻辑如下:
```python
# 开启方向检测、设置语言
ocr = PaddleOCR(use_angle_cls=True, lang="ch")
# 传入图片的文件路径
result = ocr.ocr(img, cls=True)
# 逐行输出识别结果
for idx in range(len(result)):
res = result[idx]
for line in res:
print('py_ocr_res', line[1][0])
```
得到识别结果后,将文本内容传递给向量化服务。
向量化存储
现在我们有了说明书文本内容,那么如何让 LLM 基于这些文本内容去回答用户的问题呢?
我们当然可以把说明书内容整个嵌在提示词里,要求 LLM 根据这些内容作答。但引发的问题就是,在每一轮对话中,LLM 都要处理全量的文本,会降低生成速度和质量,进而影响用户体验。因此,我们需要检索增强式生成 (Retrieval-Augmented Generation,RAG),也就是把问题以及和问题相关度高的知识一起发送给 LLM,帮助 LLM 快速而准确地生成回答。
在这一环节,我们要先把通过 OCR 提取出来的文本进行处理,搭建一个知识库,以供后续问答时检索之用。我们借助 LangChain 框架来实现这一处理过程。
LangChain
https://github.com/langchain-ai/langchainjs
首先是切分文本。虽然一张照片内的说明书内容有限,篇幅不会过长,但是为了更快、更精准地匹配问题内容,我们还是要把整段文本切开成一个个小块。
```javascript
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 100,
});
const docs = await splitter.splitDocuments(ocrText);
```
然后,我们把切分好的文本块进行向量化 (Embedding) 处理,并存到向量数据库中:
```javascript
import { OllamaEmbeddings } from '@langchain/community/embeddings/ollama';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
const embedder = new OllamaEmbeddings({
model: 'gemma2’ // 使用本地模型
});
const store = await MemoryVectorStore.fromDocuments(docs, embedder);
const retriever = store.asRetriever();
```
在上面的 Embedding 逻辑中,我们使用了本地模型。你需要提前安装并启动 Ollama,然后把一个模型下载到本地,如 ollama pull gemma2,就像拉取一个 npm 包一样简单。
Ollama
https://ollama.com/
至此,我们就把用于检索的知识库准备好了,可以开门迎接用户的提问了。
ASR: 语音转文字
当用户用语音提问后,我们需要把音频内容转为文字,你可能在 HuggingFace 上看到过 ASR (Automatic Speech Recognition) 或者 STT (Speech to Text),指的都是这个转换过程。以下是核心逻辑:
```python
import torch
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
# 加载模型
model_id = "openai/whisper-base"
model = AutoModelForSpeechSeq2Seq.from_pretrained( model_id)
processor = AutoProcessor.from_pretrained(model_id)
pipe = pipeline(
"automatic-speech-recognition",
model=model,
tokenizer=processor.tokenizer,
feature_extractor=processor.feature_extractor,
)
# 传入音频文件路径,生成文本
result = pipe(audio)
print('py_asr_res', result["text"])
```
得到用户问题的文本内容后,接下来就要进入到一个 LLM 应用的核心流程了。
RAG: 检索生成
在这个环节,我们要做的是拿用户的提问内容去之前准备好的向量知识库里做相似性匹配,再把检索到的相关文本块与问题一起传送给 LLM,LLM 会根据提示词生成回答。
首先是组织提示词模板。我们参考 LangChain Hub 中的提示词,编写模板:
LangChain Hub
https://smith.langchain.com/
```javascript
import { ChatPromptTemplate } from '@langchain/core/prompts';
const prompt = ChatPromptTemplate.fromMessages([
['system', `你是一个问答任务助手。使用下面 context 中的检索上下文来回答问题。如果你不知道答案,就直接回答不知道。最多用三句话回答。`],
['placeholder', '{context}'],
['user', '{question}']
]);
```
接着,我们引入本地 LLM:
```javascript
import { ChatOllama } from '@langchain/community/chat_models/ollama';
const model = new ChatOllama({
model: 'gemma2',
});
```
由于 LLM 自有其特定的输出格式,我们还需要一个工具来把模型给出的回答解析成字符串:
```javascript
import { StringOutputParser } from '@langchain/core/output_parsers';
const parser = new StringOutputParser();
```
最后,我们要用链条把 prompt、model、parser 串起来。什么?你说还没把问题和知识库做检索匹配?放心,LangChain 框架会帮我们处理这些,这正是「链条 (chain)」的奥义!
```javascript
import { RunnablePassthrough, RunnableSequence } from '@langchain/core/runnables';
import { formatDocumentsAsString } from 'langchain/util/document’;
const ragChain = RunnableSequence.from([
{
question: new RunnablePassthrough(),
context: retriever.pipe(formatDocumentsAsString), // retriever 就是此前生成的知识库
},
prompt,
model,
parser,
]);
```
我们只需要执行这个链条,就可以坐等 LLM 输出答案了:
```javascript
const output = await ragChain.invoke(userQuestionFromASR);
```
TTS: 文本转语音
现在,我们只需要把文本格式的回答内容给转成语音,然后播放给用户听即可:
```python
import ChatTTS
chat = ChatTTS.Chat()
if chat.load():
pass
else:
sys.exit(1)
wavs = chat.infer(texts, use_decoder=True)
# 保存音频为 .mp3 文件
for index, wav in enumerate(wavs):
save_mp3_file(wav, index)
```
大功告成!!!
其他
我们使用了 Nuxt 搭建了这个 Web App 的前后端,上述的 OCR、ASR、TTS 都串联其中,非常方便!
Nuxt
https://www.notion.so/https-bruno-simon-com-d63010e58be2449e84e54e0a4c557a40?pvs=21
GUI 方面,我们使用了 LangUI 这个 TailwindCSS 组件库,找到想用的组件,直接复制粘贴代码就行了,非常方便 +1!
LangUI
https://github.com/LangbaseInc/langui
我和队友都是前端工程师,我们本着「应 JS 尽 JS」的信条,在作品中最大限度地用 Web 技术栈来实现功能。前端切图没在怕的,非常自豪!
规划和展望
「药童」这个作品,在 Gemma Hackathon 中得到了评委的认可,在故事性、创造性、实用性三个维度的综合评分名列前茅。这印证了我们的创意和理念确实有其价值。
在人工智能能力突飞猛进的今天,我们很容易就能感受到 AI 技术的强大力量,也都在积极乐观地畅想它将在各个领域掀起变革。然而在这样的狂欢氛围中,我们也可能很容易就忽略了那些身处热潮之外的群体,忽略了一点点巧妙的技术应用就能给他们带来帮助。技术可以在高空中绽放出绚烂夺目的光芒,也同样可以在细微处散发温热,暖人心窝。作为使用技术的人,我们开发者的目光要投向哪里,这至关重要。
以现实的眼光来审视,「药童」距离成为一个成熟产品,还有些技术性问题需要解决、优化,比如提升响应速度、支持多轮对话、固定音色、云端部署等等。我会发挥 Web 技术的灵活优势,「药童」可以是微信小程序、网页或任何形态,可以读药品说明书、家电说明书或任何文字内容,运用到任何需要它的地方。我会延续初心,尽我所能用技术提供帮助,哪怕用户群体再少、应用场景再窄。
写在最后
感谢 Jax 的创作分享,我们也期待「药童」能够为更多开发者提供灵感,激发更多有温度的创新体验。
如果您也想将自己的实践案例与更多开发者分享,欢迎报名参与谷歌开发者招募活动,积累资源和开发经验,收获灵感和惊喜,探索技术的无限可能!
扫描二维码提交报名
和伙伴们一起探索 Google 技术
点击屏末 | 阅读原文 | 即刻报名谷歌开发者招募活动