为什么 Ruby 可能是你下一个 AI 网页应用的最佳选择?
来自Unsplash的一张红宝石的照片。
🔥 如果你想要定制一个具有生成AI集成的网站应用,可以访问losangelesaiapps.com
📗 这是一系列中的第二篇文章,关于使用生成式AI集成创建Web应用程序。第一部分侧重于解释AI堆栈以及为什么应用程序层是最佳位置。你可以在这里查看第一篇文章: 这里 .
目录(目录)- 前言
- 我以为水疗应该很放松啊?
- 微服务主要针对大型企业
- Ruby和Python:相似却不相同的技术
- 最近一些基于AI的技术
- 结语
在讨论人工智能时,你很少会听到人家提到Ruby语言。
当然,Python 在这个世界中是王者,理由充分。社区已经围绕这门语言凝聚起来,这也是有原因的。如今,大多数模型训练都是用 PyTorch 或 TensorFlow 完成的。Scikit-learn 和 Keras 也很受欢迎。像 LangChain 和 LlamaIndex 这样的 RAG 工具主要支持 Python 语言。
不过说到构建有AI功能的Web应用,我认为Ruby是更好的选择。
作为一家专门打造带有生成式AI集成的MVP的机构的联合创办人,我经常听到潜在客户抱怨,他们通常会抱怨两方面的内容:
- 应用程序构建耗时太久、开发者报价离谱,只为构建定制网页应用。
这些抱怨有一个共同的原因:复杂性。现代的网页应用比以前复杂得多。那么,这种复杂性又是从哪里来的呢?这种复杂性带来的好处值得付出相应的代价吗?
我以为水疗应该是放松的,结果呢?单页应用(SPA)的兴起是近来拼图的一个重要环节。MERN是目前构建现代SPA最流行的栈,MERN指的是MongoDB、Express.js、React.js和Node.js这几个技术栈的组合。MERN之所以受欢迎有几个原因。
- 这是一个仅使用JavaScript的全栈,无论是前端还是后端。只需要用一种语言编写代码真是太棒了!
- 单页应用(SPA)可以提供流畅的用户体验。这里的“流畅”是指当某些数据发生变化时,只需更新页面的一部分,而不是重新加载整个页面。当然,如果你没有现代智能手机,SPAs可能不会感觉那么流畅,因为加载速度会变慢。
- 这个栈拥有庞大的库生态系统和经验丰富的开发人员。这是一个相当有趣的逻辑循环:这个栈是因为生态系统而流行,还是因为它的流行才有了生态系统?不管怎样,这一点是成立的。
- React是由Meta公司创建的。Meta公司为这个库投入了大量资金和精力,以使其更加完善并进行推广。
不幸的是,使用MERN栈工作也存在一些缺点,其中最让人头疼的就是它的复杂度。
传统的 web 开发使用的是模型-视图-控制器(MVC)模式。在 MVC 中,所有用户会话相关的逻辑处理都在后端服务器上进行。例如,用户数据的获取是通过后端的函数调用和 SQL 语句来实现的。后端生成完整的 HTML 和 CSS,然后提供给浏览器,浏览器只需要显示即可。因此得名“服务器端”。
在单页应用(SPA)中,这种逻辑是在用户的浏览器上处理的,即前端。SPA 必须处理 UI 状态、应用状态,有时甚至包括服务器状态,所有这些状态都在浏览器内处理。需要向后端发起 API 请求来获取用户数据。后端仍然保留了相当多的逻辑,主要通过 API 来提供数据和功能。
为了说明这个差异,让我用商业厨房来打个比方。顾客就像是前端,厨房则是后端。
以下是ChatGPT生成的图片:MVCS 和 SPA。
传统的MVC应用就像是在一家提供全方位服务的餐厅用餐。后台确实很复杂(正如《熊出没》所描述的,那里还会有不少争吵),但前段体验却很简单且令人满意:顾客只需拿起叉子吃饭就好了。
SPA就像在一个自助餐厅里吃饭。厨房里还是有很多复杂的事情需要处理。但现在顾客还需要决定要拿哪些食物,如何搭配和组合,如何摆放食物,吃完后盘子放在哪里合适等。
最近,Andrej Karpathy 发了一条推文,讨论了他在2025年尝试构建网站时的挫败感。这可能会让新手感到不知所措。
Andrej Karpathy的推文。来自X(原推特)
为了快速构建带有AI集成的MVP,我们机构决定不使用SPA,而选择传统的MVC方法。特别是,我们发现Ruby on Rails(通常简写为Rails)是快速开发和部署高质量、带有AI集成的应用程序的最佳选择。Ruby on Rails由David Heinemeier Hansson于2004年开发,长期以来一直被认为是一个出色的Web框架,并且,我认为它最近在将AI融入应用程序方面取得了显著的进步,这部分内容后续会详细说明。
Django 是最受欢迎的 Python Web 框架,也具有更传统的开发模式。不幸的是,在我们的测试中发现 Django 并没有像 Rails 那样功能全面或“开箱即用”。例如,Django 没有内置的后台任务系统。我们几乎所有应用程序都包含后台任务,所以这一点令人感到失望。我们更喜欢 Rails 所强调的简洁,Rails 8 鼓励开发者轻松地自我托管应用,而不是通过像 Heroku 这样的提供商。他们还最近发布了一系列工具,旨在替代像 Redis 这样的外部服务。
“ 那流畅的用户体验怎么办呢? ”你可能会问。实际上,现代Rails提供了几种方法来实现类似单页应用(SPA)的体验,但不需要太多的JavaScript。主要的工具是Hotwire,它集成了诸如Turbo和Stimulus这样的工具。Turbo允许你在网页上动态更改HTML片段,而无需编写自定义的JavaScript。当确实需要使用自定义JavaScript时,Stimulus是一个轻量级框架,能够满足你的需求。即使你想用React,也可以通过react-rails gem来实现。所以你既可以拥有蛋糕,又可以享用它!
然而,SPA(单页应用)并不是复杂性增加的唯一原因。另一个原因是微服务架构的兴起。
微服务才是大企业的菜又一次,我们发现我们在比较过去简单的日子与今天复杂的现实。
在过去,软件主要是以单体应用开发的。单体应用指的是,应用的不同部分,像是用户界面、业务逻辑和数据处理,都是作为单一单元进行开发、测试和部署的。代码通常都存放在同一个代码库中。
使用单体系统工作简单且令人满意。测试用的开发设置也很容易。你只在一个数据库模式中操作,该模式包含所有的表,使得查询和连接操作变得非常简单。部署也很简单,因为你只需要操作一个容器。
然而,一旦你的公司发展到像谷歌或亚马逊那样大规模时,真正的问题就开始出现了。成百上千的开发者同时为同一个代码库贡献代码,协调变更和解决合并冲突变得越来越棘手。部署也变得更加复杂和风险重重,因为即使是小小的改动,也可能引发整个应用挂掉,因为即使是小小的改动,也可能导致整个应用崩溃。
为了应对这些问题,大型公司开始围绕微服务架构凝聚在一起。这是一种编程方式,其中代码被设计成一组小型、独立的服务。每个服务拥有自己的代码库、数据库和部署管道。例如,你可以将所有与OpenAI客户端相关的逻辑从主应用中分离出来,放到自己的服务中。为了调用这些服务,你通常会通过REST API调用而不是函数调用。这确实增加了复杂度,但解决了代码合并冲突和部署问题,因为组织中的每个团队都可以在其各自的代码岛屿上工作。
另一个使用微服务的好处在于支持多语言技术栈。这意味着每个团队可以根据自己的喜好选择编程语言来编写服务。无论是JavaScript还是Python,只要团队喜欢,这都不会成为问题。当我们刚开始建立公司时,这种多语言技术栈的思想促使我们选择了微服务架构。这并不是因为我们有大型团队,而是因为我们每个人都希望用最适合每项功能的“最佳”语言。这意味着:
- 使用 Ruby on Rails 进行 web 开发。它在这个领域已经经过了数十年的考验。
- 使用 Python 进行 AI 集成,可能采用 FastAPI 这样的框架部署。我听说,真正严肃的 AI 工作需要使用 Python。
两种不同的语言,各自专注于自己的领域。还能出什么问题呢?
不幸的是,我们发现开发过程很令人沮丧。仅仅设置开发环境就花费了大量时间。不得不应付Docker compose文件和管理服务间的通信,让我们怀念起单体架构的美好与简单。不得不通过REST调用并在FastAPI中设置适当的路由,而不是简单地调用一个函数那么简单,这种做法真是让人头疼。
我心里想,我们肯定不能用纯Ruby来开发AI应用吧。然后我试着做了一下。
我很高兴我做了这个决定。
我发现用Ruby开发集成了AI的MVP的过程非常令人满意。我们能够快速前进,而不再是缓慢前行。我喜欢Ruby社区对简洁、优雅和开发者幸福感的重视。Ruby中AI生态系统非常成熟,并且每天都在进步。
如果你也是个 Python 程序员,像我当时被学习新语言吓到一样,让我来聊聊 Ruby 和 Python 的相似之处,给你点安慰。
Ruby 和 Python:编程界的双面硬币Python和Ruby就像是一对表亲,在设计和功能上。这两种语言都具有:
- 高层次解释: 这表示它们抽象了很多低层次编程细节的复杂性,比如内存管理。
- 动态类型: 这两种语言都不需要你指明变量是
int
、float
、string
等类型。类型检查在运行时进行,这也意味着变量类型可以在运行时发生变化。 - 面向对象编程: 这两种语言都是面向对象的。它们都支持类、继承、多态等特性。Ruby 更加“纯粹”,因为所有的东西都是对象,而 Python 中有一些东西(如
if
和for
语句)不是对象。 - 简洁易读的语法: 两种语言都易于学习,非常适合初学者。
- 广泛的包生态系统: 在 Python 中这些包称为库,而在 Ruby 中则称为 gem。
两种语言之间的主要区别在于它们的哲学理念和设计原则。具体来说,Python的核心理念可以描述为:
“最好只有一种明显的方法来做某事。”
理论上说,应该强调简洁、可读和清晰。Ruby 的哲学是:
“做事常常有多种方法。让开发者感到开心和满足。”
从 Python 转换过来时,这对我来说是个不小的冲击。下面是一个简单的例子,这个例子突显了这种哲学上的差异。
# 哲学上的遍历数组讨论:
# Python 风格
for i in range(1, 6):
print(i)
# Ruby 风格,选项 1
(1..5).each do |i|
puts i
end
# Ruby 风格,选项 2
for i in 1..5
puts i
end
# Ruby 风格,选项 3
5.times do |i|
puts i + 1 # 输出 i 加 1
end
# Ruby 风格,选项 4
(1..5).each { |i| puts i }
另一个区别在于语法风格上的差异。Python 主要使用缩进来表示代码块,而 Ruby 使用 do…end
或 {…}
块。通常,Ruby 块中会包含缩进,但这完全是可选的。在上面展示的代码中,你可以看到这些语法差异的例子。
还有很多小细节需要学习。例如,在 Python 中,字符串插值操作使用 f-string 来实现:f"Hello, {name}!"
,而在 Ruby 中,则使用井号(#):“Hello, #{name}!”
。几个月后,我觉得任何一个熟练的 Python 程序员都能顺利上手 Ruby。
尽管鲁比没有参与关于人工智能的讨论,它在宝石领域却有了不少进展。我将介绍一些我们机构在构建AI应用时使用的最近最引人注目的新发布。
RubyLLM (链接): 任何在发布后的几周内获得超过2000颗星的GitHub仓库都值得被提及,而RubyLLM绝对值得。我用过许多从LangChain和LlamaIndex这类库中笨拙实现的LLM提供者,所以使用RubyLLM简直就像是清新的空气。下面以一个简单的例子来展示一个简单的教程,演示多轮对话功能。
require 'ruby_llm'
# 创建一个模型并给它提供指示
chat = RubyLLM.chat
chat.with_instructions "你是个乐于助人的Ruby专家,喜欢帮助新手。"
# 多轮问答
chat.ask "嗨!Ruby里的attr_reader是用来干嘛的?"
# => "Ruby会为每个符号创建一个getter方法...
# 实时流式回复
chat.ask "你能给我一个简单的例子吗?" do |chunk|
print chunk.content
end
# => "当然!
# ```ruby
# class Person
# attr...
这是一个简单的例子...
简直太棒了。多轮对话自动为您处理好。流式传输轻松搞定。与LangChain中的类似功能相比:
导入langchain_openai的ChatOpenAI
从langchain_core.schema导入SystemMessage, HumanMessage, AIMessage
从langchain_core.callbacks.streaming_stdout导入StreamingStdOutCallbackHandler
SYSTEM_PROMPT = "你是一个友好的Ruby专家,喜欢帮助初学者。"
chat = ChatOpenAI(streaming=True, callbacks=[StreamingStdOutCallbackHandler()]) # (开启了流式传输模式)
history = [SystemMessage(content=SYSTEM_PROMPT)]
def ask(user_text: str) -> None:
"""逐字流式输出答案,并将对话上下文保留在内存中。"""
history.append(HumanMessage(content=user_text))
# (逐块输出消息,每当消息块到达时)
for chunk in chat.stream(history):
print(chunk.content, end="", flush=True)
print() # 在答案后换行
# 最终的chunk包含了完整的消息内容
history.append(AIMessage(content=chunk.content))
ask("嘿!Ruby中的attr_reader是用来做什么的?")
ask("很棒!你能给个使用attr_accessor的小例子吗?")
哎呀。首先要注意的是,这是一个grug实现。想知道LangChain究竟是如何期望你处理内存的吗?想知道的话,可以看看这里和这里,但是先准备好一个桶吧;你可能会晕。
邻居(链接): 这是一个在 Rails 应用程序中进行最近邻搜索的优秀库,非常适合。在 RAG 设置中非常有用。它支持 Postgres、SQLite、MySQL、MariaDB 等数据库。这个库是由 Andrew Kane 编写的,他同样也是 pgvector 扩展的作者,该扩展使 Postgres 具备向量数据库的功能。
Async (链接): 这个 gem 在 2024 年 12 月 发布了它的第一个官方版本,并在 Ruby 社区引起了很大的反响。Async 是一个基于 fiber 的 Ruby 框架,可以在不阻塞 I/O 的情况下并发执行任务,让你编写简单、顺序的代码。Fibers 就像迷你线程,各自拥有独立的小调用栈。虽然它不是专为 AI 设计的 gem,但它帮助我们创建了快速抓取数千个网页的网页爬虫等功能。我们还使用它来从大语言模型 (LLM) 流式传输数据。
Torch.rb (链接): 如果你对深度学习模型的训练感兴趣,那么你肯定听说过 PyTorch。PyTorch 是基于 LibTorch 构建的,而 LibTorch 在底层有大量的 C/C++ 代码来快速执行机器学习操作。安德鲁·凯恩在 Ruby 上创建了 Torch.rb,可以说是 Ruby 版的 PyTorch。安德鲁·凯恩一直是 Ruby AI 世界中的英雄,为 Ruby 编写了数十个机器学习 gem 包。
摘要简而言之:快速且低成本地构建带有AI集成的web应用需要采用单体架构。 单体架构需要使用单一语言的应用,这对于快速交付高质量的应用是必要的。你的主要选择是Python或Ruby。如果你选择Python,你可能会使用Django作为你的web框架。如果你选择Ruby,你将使用Ruby on Rails。在我们公司,我们对Django的功能不足感到有些失望。Rails的丰富功能集及其对简洁性的重视给我们留下了深刻的印象。我们在集成AI方面几乎没遇到什么问题。
当然,有时候你可能不会选择使用Ruby。在进行AI研究或从零开始训练机器学习模型时,你可能会更倾向于使用Python。研究几乎从来不会涉及构建Web应用,而是会在笔记本中搭建一个简单的界面或仪表盘,但这不会是生产就绪的版本。你可能需要最新版的PyTorch来确保训练快速进行。你甚至可能会深入到低级别的C/C++编程,以尽可能提高硬件性能。也许你还会去尝试一下Mojo。
但如果你的目标是将最新的语言模型(不论是开源还是闭源的)集成到网络应用中,我们认为 Ruby 更是绝佳的选择。何不自己试试看?
在这一系列的第三部分里,我将进行一项有趣的实验:我们可以把一个集成人工智能的网页应用做到多简单? 敬请关注。
共同學(xué)習(xí),寫(xiě)下你的評(píng)論
評(píng)論加載中...
作者其他優(yōu)質(zhì)文章