招募活动投稿展示 | 感受科技温度,从一个 LLM 应用开始

news2024/11/26 16:42:38

c91e7129813a1c1f3232abac5ad7709d.png

活动介绍

谷歌开发者招募活动是专为 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…… 如果你对这些名词不太了解,别急,且听我娓娓道来。

技术实现

从用户上传照片,到回复用户的提问,其背后的处理流程是这样的:

74c2e010d21a924dd6cf01b970fcc605.png

  1. 从图片中提取文字内容;

  2. 将提取出的文本内容切分、向量化,存储到向量数据库中;

  3. 用户发送语音问题后,把语音转为文字格式;

  4. 将问题文本与向量数据库进行匹配,检索出高相关度的知识块;

  5. 将相关知识上下文与问题文本一起包装成提示词,发送给 LLM;

  6. LLM 根据提示词生成文本格式的回答内容;

  7. 将回答文本转化为语音,播放给用户听。

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 的创作分享,我们也期待「药童」能够为更多开发者提供灵感,激发更多有温度的创新体验。

如果您也想将自己的实践案例与更多开发者分享,欢迎报名参与谷歌开发者招募活动,积累资源和开发经验,收获灵感和惊喜,探索技术的无限可能!

8ab28bb8f7df6a9639e6dbfeb0070737.png

扫描二维码提交报名

和伙伴们一起探索 Google 技术


bf8954330c80795fa43a541fc1ce4513.gif 点击屏末  | 即刻报名谷歌开发者招募活动

2522cc45ee0e21a9311b47ef6c4cf22d.png

7f44af0ce87e54b31f7f4c621262ec1b.png

de0e2e308cdb4aab6a121dc034b33027.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2063149.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Win开发环境搭建】Redis与可视化工具详细安装与配置过程

🎯导读:本文档提供了Redis的简介、安装指南、配置教程及常见操作方法。包括了安装包的选择与配置环境变量的过程,详细说明了如何通过修改配置文件来设置密码和端口等内容。同时,文档还介绍了如何使用命令行工具连接Redis&#xff…

商品数据获取api接口:电商API接口助力内部平台商品定价!

对于很多电商内部平台来讲,品牌方在为内部平台的商品定价时,通常会获取主流电商平台的商品数据以供参考,具体来看,这主要涉及以下步骤: 选择合适的API接口服务商。电商API接口能够提供来自多个主流电商平台的商品数据…

06--kubernetes.pod管理与投射数据卷

前言:上一章记录了部署k8s常用的两个方式,这一章就简单一些,整理一下k8s资源对象的配置和管理命令。 1、集群状态检查 前天搭建的环境,然后关机了两天今天开启后第一时间需要检查集群环境是否正常 [rootk8s-master1 ~]# kubect…

【html+css 绚丽Loading】000012 五行伸缩杖

前言:哈喽,大家好,今天给大家分享htmlcss 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 &#x1f495…

js禁用浏览器缩放

禁用crtl- &#xff0c;禁用crtl滚轮 但是不能禁用浏览器本身的设置 代码如下&#xff1a; <script> //luwenjie hualunwindow.addEventListener(mousewheel, function(event){if (event.ctrlKey true || event.metaKey) {event.preventDefault();}},{ passive: fals…

数学建模算法总结

数学建模常见算法总结 评价决策类模型 层次分析法 层次分析法根据问题的性质和要达到的总目的&#xff0c;将问题分解为不同的组成因素&#xff0c;并按照因素间的相互关联影响以及隶属关系将因素按不同层次聚集组合&#xff0c;形成一个多层次的分析结构模型&#xff0c;从…

Go 文件操作基本方法大全

前言 在Go语言中&#xff0c;操作文件主要依赖于标准库中的os和io/ioutil&#xff08;注意&#xff1a;io/ioutil在Go 1.16及以后版本中被逐步弃用&#xff0c;推荐使用io和os包中的函数进行替代&#xff09;以及io和bufio等包。以下是一些基于这些基本库操作文件的方法大全&a…

人工智能时代:哪些职业将被取代,哪些职业难以替代?

引言 人工智能&#xff08;AI&#xff09;作为现代科技领域最引人瞩目的创新之一&#xff0c;正以前所未有的速度改变着我们的生活和工作方式。从20世纪50年代AI概念的诞生&#xff0c;到21世纪初的机器学习突破&#xff0c;再到如今深度学习与大数据的结合&#xff0c;人工智能…

记一次在工作中发现的Esper引擎底层逻辑问题

一、问题描述 在最近工作过程中本人遇到客户反馈一个问题&#xff1a;在某SOC平台关联分析规则新建的规则语句能查询到相关日志&#xff0c;但无法触发规则告警。 规则语句大致为&#xff1a; 数据源 "IPS" and not 目的地址 belong 白名单 客户需求是对于IPS告警…

Arduino复制的方法快速搭建ESP32低版本2.0.10开发环境

Arduino复制的方法快速搭建ESP32低版本2.0.10开发环境 1.概述 这篇文章介绍如何使用复制安装包的方式快速搭建好ESP32开发环境&#xff0c;而且是指定的2.0.10低版本。 2.搭建开发环境 1.下载安装包 首先在百度网盘下载ESP32安装包 通过百度网盘分享的文件&#xff1a;esp…

Biomamba教程01-绪论

目录 1、课程目录 2、单细胞技术背景 3、单细胞数据挖掘平台&#xff08;不用代码&#xff09; 1、课程目录 重点是寻找差异基因&#xff0c;进一步用KEGG、GO等进行疾病相关分析 2、单细胞技术背景 3、单细胞数据挖掘平台&#xff08;不用代码&#xff09; loupe.10xgenom…

会话跟踪方案:Cookie Session Token

什么是会话技术&#xff1f; Cookie 以登录为例&#xff0c;用户在浏览器中将账号密码输入并勾选自动登录&#xff0c;浏览器发送请求&#xff0c;请求头中设置Cookie&#xff1a;userName:张三 ,password:1234aa &#xff0c;若登录成功&#xff0c;服务器将这个cookie保存…

「Python程序设计」基础语法:程序的注释、行与缩进

在我们编写程序代码的过程中&#xff0c;加入程序的注释&#xff0c;是一个很重要的过程。特别是对于软件工程来说&#xff0c;加入程序的注释&#xff0c;可以提高程序代码的可读性&#xff0c;让程序的维护难度降低。 程序注释的种类 首先要知道的是&#xff0c;程序的注释…

为什么要用云手机进行海外社交营销

随着科技的飞速发展&#xff0c;云手机这一新兴技术正逐渐在海外社交营销中占据一席之地。云手机的灵活性、成本效益和全球化特点使其成为海外社交营销中不可或缺的工具。本文将深入剖析云手机的优势&#xff0c;并探讨其在海外社交营销中的重要作用。 首先&#xff0c;云手机的…

Admin.NET源码学习(3:LazyCaptcha使用浅析)

Admin.NET项目前端登录页面的验证码图片默认使用动态图&#xff0c;且图形内容为阿拉伯数字运算&#xff08;如下图所示&#xff09;&#xff0c;用户输入正确的计算结果才能正常登录。项目采用LazyCaptcha模块生成验证码及动态图。   在Admin.NET.Core项目中添加了Lazy.Cap…

TOP刊录用接不接?初审仅1天,国人还友好,质量在线,3个月录用,光环直接拉满

【SciencePub学术】本期&#xff0c;给大家介绍的是1本工程机械类的SCI&#xff0c;位于JCR1区中科院1区TOP&#xff0c;影响因子4.5分。 本刊在机械设计和制造领域认可度极高&#xff0c;近年来影响因子逐步上升&#xff0c;这和稿件质量的大幅度提升有很大的关系。 MMT期刊发…

考驾照需要多长时间?你考驾照用了多长时间?

正常考驾照时长 有人问橙子&#xff1a;一个月可以拿到驾照吗&#xff1f; 答&#xff1a;理论上&#xff0c;一个月能拿到驾驶证。按照《机动车驾驶证申领和使用规定》显示&#xff0c;通过科目一10天后即可预约科目二&#xff0c;而通过科目二后同样需要10天后才能预约科目…

GitHub新手使用指南

第一节 GitHub新手使用指南 一、打包下载源码及资源 下载源码及资源不需要登录。 二、查看作者主页 第1步&#xff1a;点击作者头像 第2步&#xff1a;查看作者的项目 第3步&#xff1a;查看作者项目的基本信息 第4步&#xff1a;搜索项目 第5步&#xff1a;把代码复制到自…

Leetcode-高频面试题-143.重排链表

解法都在代码里&#xff0c;不懂就留言或者私信 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val …

Ajax基础案例

接口文档 欢迎使用 - AJAX阶段 地区查询 图解 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewpor…