Rubyist歓喜!AI開発が爆速になるRubyLLMがマジ最高らしい
引用元:https://news.ycombinator.com/item?id=43331847
このインターフェース、streamingとの連携もっとうまくならないとだめだね。レスポンスにラグがあるし、多くの人がレスポンスをノンブロッキングなスレッドでstreamしたいと思うはず。プロセスを待たせるんじゃなくてさ。ドキュメントの問題かもしれないけど、streamingは数秒以上かかるIO処理には必須でしょ。でもDSLはマジで最高。
Rubyのasync IOの世界、もっと注目されてもいいのにね。async gemとか、async-http、async-websockets、Falcon web serverとかチェックしてみて!
https://github.com/socketry/falcon
ありがとね!いい指摘だね。実はasync-http-faradayを使ってstreamingを改善しようとしてるところなんだ。デフォルトのアダプターをasync_httpとFalcon、async-jobを使うように設定するんだ。PumaとかSolidQueueみたいなスレッドベースじゃなくてね。これでRubyでのAIワークロードのリソース効率がマジで上がるはず。他のRuby LLMライブラリで実装されてるの知らないけど。今のブロックを使うやり方はRubyっぽいけど、asyncサポートでさらにproductionで使いやすくなるよ!
>https://rubyllm.com/#have-great-conversationsから引用
>”# Stream responses in real-time
chat.ask ”Tell me a story about a Ruby programmer” do |chunk|
print chunk.content
end”
これだと’chat.ask’が返ってくるまで同期的にブロックされるよね。streaming APIが処理を終えるまでアプリ全体のメモリ(数十~数百MB)が無駄になる覚悟は必要だよ。
Railsはグローバルな可変ステートの塊みたいなもんだから、スレッドで頑張って。
RailsのデフォルトのアプリケーションサーバーはPumaで、スレッド使ってるよ。
そうそう。RubyにはGILがあって、インタプリタで複数のスレッドが同時に実行されるのを防いでるんだ。Pumaはスレッド持ってるけど、同時にRubyコードは実行できない。でもIOは隠せる。
GILは、LLMとの通信に使われるHTTPリクエストみたいな一般的なIO処理の間は解放されるよ。
それいいね!前に見なかった。
langchain みたいなクソ DX なライブラリと比べて、マジで新鮮!
Ruby コミュニティって DUX をめっちゃ大事にしてるよね。なんで他の言語コミュニティにはないんだろ?
他の好きなものがある人をけなすつもりはないんだけど、Ruby は趣味の良い人たちのための言語とエコシステムだと思う。
グローバルステートが好きってことだよね、きっと。
特定のケースで単純化できるとしても、グローバルステートを盲目的に拒否するのはセンスないよね。goto でさえ、エレガントな時もあるじゃん。
でも大抵は長期的な問題を引き起こして、重要な作業を後回しにするだけなんだよね。
後回しにした結果、結局やらないってこともあるから、最初から強制的にやらせる言語よりはマシってこと。
でも後回しにしたせいで、グローバルステートを完全に解きほぐしてコードを再利用できるようにするのに何年もかかることもあるよね。そもそも最初にそれを使ったことで生産性が上がった?たぶん違うよね。
もしアプリが1年以上続くとか、3人以上でメンテするとか、50万行以上のコードになるとか(好きな指標に置き換えて)、グローバルステートの除去を後回しにするのはやめとけ。絶対後悔するし、最初からちゃんとやっても大した手間じゃないよ。
あと、グローバルステートを使うなって強制するメインストリームな言語はないよ。Java でさえ、必要ならグローバルステートを簡単に使えるし。
それってグローバルステートのせいじゃなくて、アーキテクチャがダメダメなせいじゃない?ツール自体は悪くないよ。良いナイフも使い方次第ってこと。
グローバルステートって、アーキテクチャが重要なアプリだとほぼ確実に悪いアーキテクチャにつながるんだよね。 例外もあるだろうけど、システム内のどこからでも変数をいじれるようにすると、予測不能な影響が出やすくなって、大規模なコードベースではマジでヤバいことになるからね。グローバルステートを避けることで、腐敗の原因を一つ減らせるんだ。
「ほぼ」ってのがミソだよね。あなたの意見は尊重するけど、いつも絶対ダメとか言うのはどうかなって。長くいればいるほど「場合による」って思うようになるんだ。大規模なコードベースでグローバルステートをうまく使ってる例として、https://dev.37signals.com/globals-callbacks-and-other-sacril… が参考になるよ。 >それはいつも絶対ダメってことじゃないよ! グローバルステートを四肢切断に例えるのは極端すぎない?そこまで悪いものじゃないと思うな。RailsのCurrent singletonみたいに、グローバル変数を使う仕組みが組み込まれてるフレームワークもあるし。確かにグローバル変数は鋭いナイフだけど、使い道もあると思うよ。あなたの言う「ほぼ絶対ダメ」ってほどじゃないかな。だから、私は「場合による」って言うよ。 grepできないコードも忘れちゃ困るぜ!型ヒントって一体何なんだ? Global Interpreter Lockがお好き? 世界が小さいときはグローバルステートって便利だよね。Rubyを使う人たちは世界を小さく保つのが好きみたいで、それには他のメリットもたくさんあるし。 Rubyは好きだけど、これはナンセンスだね。Railsアプリなんてすぐに巨大化するじゃん。どんなフレームワークでも同じだよ。世界を小さく保つなんて宣言できないよ。問題の大きさ次第だよ。 ある種の趣味だね。 MatzさんがRubyは開発者の幸せを最適化するように設計したって言ってたよ。それが作られたときからの言語の核となる原則なんだって。 コードを書く開発者の幸せは、それを読んだりデバッグしたりする人の不幸になることもあるよね。2009年頃に数年間Rubyで仕事してたんだけど、method missingでロジックが実装されたコードを扱った経験は、今でも最悪の記憶だよ。 Rubyを何年も使ってるけど、デバッガで Rubyのforwarded methodsも面倒なんだよね。生成されたインジェクションコードで作られてるから、どのメソッドに転送されるかを実行時に簡単に調べられないんだ。 そうそう、それがまさに’切れ味の良いナイフ’ってやつだよね。俺が勧めるのは(そして目指してるのは)、’ライブラリ’コードにだけそのナイフを使うこと。アプリケーションコードは’シンプル’に保つべき(そうすればずっと扱いやすくなる)。そうしないと、マジでめちゃくちゃになっちゃうよ! どの言語も何か(またはいくつか)を優先してるよね。なぜなら、どの言語も理由を持った人(または人々)によって作られたから。Pythonなら正確さ、Javaなら分業、Goなら’シンプルさ’みたいな感じかな(もちろん、これらがそれぞれの言語の唯一の優先事項ってわけじゃないけどね)。別のコメントにもあるように、Matzさんは開発者の幸せを優先したんだよ。 > python and correctness えーと、Goもそうじゃないの?個人的にはGoのツールの方が使いやすいな。 どっちもDXを最適化してると思うけど、アプローチが全然違うよね。Rubyはコードを書くことに重点を置いてる。つまり、表現力豊かで直感的で楽しいものにするってこと。 Goのエコシステムはマジ良いよね。でもGoってシンタックスがちょっとアレだから、RubyみたいにDSL作れないんだよねー。Rubyは表現力が高い分、ちょっと重いんだよね。個人的にはチーム開発ならGo、個人開発ならRubyLLMかな。 「DSL作れる=開発者体験が良い」ってのは違う気がするなー。RubyとGoは、開発者体験の重視ポイントが違うんだよね。Rubyは最初のコード書く人の体験優先、Goは後からメンテする人の体験優先。どっちが良いかは、新規コード書く時間とメンテ時間の割合で変わるんじゃない? Goがメンテしやすいってのは違うと思うな。コード量が多いほどメンテは大変になるし。Goは一行一行は読みやすいけど、全体像を掴むのが難しいんだよね。Goの変更って、予想以上にアプリの奥深くまで影響することが多いし。でもruntimeは最高だから、人気は落ちないと思うけど。 >More lines of code typically makes maintenance more difficult. >or else just one or two “Go to definition” clicks away RubymineとかGolandのctrl-bがマジ使える。5年以上前から他のエディタより全然良かった。今はlspがあるから、違いは小さくなってるのかな? Goでも同じようなAPI作れるんじゃない?エラー処理とかoptional argumentsがないとか、言語特有の違いはあるけど。WithSomething()をAsk()の前に置く必要があるくらいかな。 昔Langchainに貢献してたんだけど、最初は良かったんだよねー。チャットモデルとかツールとかJSON modeとかがなかった頃の話だけど。LangchainがLLMメーカーに機能追加を促したけど、いつの間にか廃れてゾンビみたいになっちゃった。LLMプロバイダーが色々追加して、walled garden化してるし。Langchainは何度も方向転換しようとしてるけど、投資を受けずにいたら、コアチームはOpenAIとかAnthropicとかに行ってただろうね。 langchainとかllamaindexってマジgarbage librariesだよね。ドキュメントは半分もないし、APIはバージョンごとに壊れるし。 それ言おうと思ってた。これらのライブラリに頼らず、自分で全部作ることにしたんだよね。PythonLLMがあっても良いかもね。Python界隈はdeveloper experienceを気にしてる人がいないみたいだし。 誰かJavaScriptとかTypeScriptで同じくらいDXが良い感じのライブラリ知らない? もしかして: https://llmjs.themaximalist.com/ かな でも例には気を付けてね! ご指摘ありがとう。evalはドキュメントの中だけで、あくまで例として載せてたんだけど、危険なパターンを推奨したくないから修正したよ。 bobby drop table、まだあるあるなんだね これのおかげでRails試してみようかな?Rubyのシンタックスってマジで良いよね。 見た目がめっちゃ良くてクリーンなハイレベルAPIが良い感じだよね(もちろん仕事に合えばだけど)。 マジで美しい。Rubyって表現力豊かで簡潔だよね。 これって本当にRubyなの?インターフェースが良いだけじゃない?TypeScriptの例もそんなに変わらないと思うけど。 余分な括弧、セミコロン、キーワード、型アノテーションが多いんだよね。Rubyは読みやすさを最優先にしてる。TypeScriptも読めるけど、構文をスキャンしたりコードを書いたりするのにRubyより労力がかかると思う。 追加の括弧とかセミコロン、キーワード、型アノテーションのことだよね。細かい文法の違いなんて、重要じゃないと思ってたんだ。構文を勉強中の人とか、色んな言語を見たことない人以外は気にしないんじゃないかな? chat = RubyLLM.chat Rubyistなら完璧に理解できるよ。よくある書き方だし。 「代入か新しいオブジェクトの生成か」ってどういうこと?RubyLLM.chatが返すものをchatに代入するだけじゃない?関数名がもっとわかりやすい方がいいってことかな? Rubyにはmethodしかないし、全部objectだよ。だから問題ない。 そう思うのは勝手だけど、Ruby触ったことある人ならわかるでしょ。Rubyにfield/property/memberなんてないんだから。methodしかない。括弧はmethod呼び出しでは省略可能。 へー、考えさせられるね。 https://github.com/alexrudall/ruby-openai どうもありがとう、気に入ってくれて嬉しいよ!RubyのAIライブラリが増えるのは良いことだと思う。 RubyにはすでにLLMと連携するためのツールがたくさんあるよね。TorchやTensorflowみたいなものへのバインディングも長年あるし。 Rubyは元気いっぱいだよ! LLMとやり取りするためのAPIとして、めっちゃ簡潔だね!OllamaのサポートPRが下書きにあるの楽しみ! LLMベースのアプリのスクリプトを書いてるんだけど、これめっちゃ簡単に感じる! 例えば、 構文がマジで美しい! Rubyから俺が受けた印象は、構文が重要ってこと。良い構文はプログラマーを幸せにするんだよね。 いろんな言語のソフトウェアをサポートしてるけど、Rubyは一番構文的に刺激的だわ。 綺麗さとシンプルさを混同してるんじゃない?あの”美しい”構文の裏にはたくさんの複雑さと魔法が隠されてるんだよ。スクリプトや小さなプログラムには最高だけど、大規模プロジェクトでは悪夢だよ。単純すぎるんだ。 他人の好きなものを否定するのはやめとけってことだね。 こっちの好きにさせてもらうわ。 このライブラリのシンプルさにマジで感動した!もっとコメントを表示(1)
そんなこと言ってないじゃん!例外はあるって言ってるし。ルールを教えて、本当にわかってて破るなら良いけど、そうじゃなければグローバル変数は普通だなんて思わない方が良いよ。 例外になれる可能性は低いんだから、グローバル変数は特別な場合にしか使わないようにしないとね。binding.irb
とshow_source
は、Rubyのデバッグですごく役に立ったな。binding.irb
でブレークポイントを設定して、show_source
でメソッドのソースコードを見つけることができるんだ。生成されたコードでも見つけられるのがすごいよね。show_source
をこんな風に使うなんて考えたこともなかったよ。ありがとう、親切な人。おかげで今日は良い日になったよ!
俺が好きな例は、Rubyの配列演算の素晴らしさと奇妙さ。減算(arr1 - arr2
)は要素ごとの削除だけど、加算(arr1 + arr2
)は単純な追加。ほとんどの場合、これらはまさにやりたいことだけど、数学的には完全に’間違ってる’よね。
Pythonは読みやすさと’一つのことを行うためのただ一つの方法’だと思ってた。
Goは高速で堅牢なシステムを構築しやすくすることに重点を置いてる。でも、コード自体が醜くてボイラープレートだらけでも気にしないんだ。
経験を積むにつれて、Goのトレードオフを本当に評価するようになったよ。最初は楽しくないけど、4時にサーバーアラートを受け取る可能性は低い。何を作るかにもよるけどね。もっとコメントを表示(2)
コード量が多いってのは表面的な問題かもね。Goはファイル単体の読みやすさより、抽象化や間接参照の連鎖を最小限にすることに重点を置いてると思う。ロジックや設定がファイルにまとまってて、”Go to definition”を1、2回クリックすれば大体わかる。ボイラープレートは多いけど、ファイル間の結合は弱い。Rubyの美しいDSLは、壊れたり拡張が必要になったりすると大変。変更が色んなファイルに影響したり、テストを修正する必要が出てくる。
これマジ重要。メンテ担当者はstatic analysisとかgrepでコードを理解する必要がある。Rubyは性質上static analysisが難しいけど、Goはその逆。
https://github.com/crmne/ruby_llm/issues/25
このAPIのセマンティクス(インスタンスビルダーで設定して、ask/paint/embedで言語ネイティブな方法でストリーミングと宣言的なツールを扱う)は、他の言語でも美しくて使いやすいと思うな。例えばPython、C#、Erlangとか。このレベルのAPIじゃ十分じゃないかもしれないけど、開発時間は確実に短縮できるはず。
TypeScriptのオプションを見ると、自分で自分に水責めしてるみたいじゃん。
>// Just ask questions
>const chat: Chat = LLM.chat;
>chat.ask(“What’s the best way to learn TypeScript?”);
>
>// Analyze images
>chat.ask(“What’s in this image?”, { image: “ts_conf.jpg” });
>
>// Generate images
>LLM.paint(“a sunset over mountains in watercolor style”);
>
>// Create vector embeddings
>LLM.embed(“TypeScript is powerful and scalable”);
>
>// Let AI use your code
>class Calculator {
> description = “Performs calculations”;
> params = {
> expression: { type: “string”, desc: “Math expression to evaluate” },
> };
>
> execute(args: { expression: string }): string {
> return eval(args.expression).toString();
> }
>}
>
>chat.withTool(new Calculator()).ask(“What’s 123 * 456?”);
あと、const chat: Chat = LLM.chat;
はクラスのインスタンス化じゃなくて、Rubyは裏でやってるんだよね。ファクトリを作るには、さらに括弧が必要じゃん!
これは主にシンタックススタイルの問題だね!
もちろん人それぞれだけど、APIで色々やったり、一回の呼び出しで便利なものが返ってくる方が嬉しいな。セミコロンとかインデントとか括弧とか、些細なことすぎて気にならない。コードが何をするかしか見てないし(typoは別だけど笑)。
たぶん昔のC vs Pascal vs BASICのsyntax論争のせいかな。SchemeとかLisp書くときも、括弧なんて「見えてなかった」し。でも今Lispのコード見るとsyntaxが変に見えて時間がかかるなー、最近使ってないから。
結局は人それぞれだけど、const chat = new LLM.Chat();
とchat = RubyLLM.chat
は同じことだと思うんだよねー。画面のtokenなんて覚えてないし、どっちも「chat objectを作ってchatに代入する」って認識してる(言葉にはしないけど)。const
とか;
とかで悪くなるとは思わないな。でも実験したわけじゃないから、間違ってるかも。実験方法もわかんないし。
>const chat = new LLM.Chat();
とchat = RubyLLM.chat
は同じことだと思う
みたいな感じかな。
って、曖昧じゃね?代入なのか新しいオブジェクトの生成なのかわかんないし。読みやすいとは思えないな。もっとコメントを表示(3)
chat
がRubyLLMのfield/property/memberなのか、method呼び出しなのかが不明ってこと。
Ruby: パーティーに遅れてきたけど、ビール樽持ってきた。
を長年使ってるけど問題ないよ。良いgemだし。
chat.ask ”What’s being said?”, with: { audio: ”meeting.wav” }
みたいな場合に、本番環境で使うより、何かをテストするための使い捨てコマンドを実行するCLIみたいなものに便利だと感じるな。ユーザーが75%の時間しか有効なレスポンスを得られないのは嫌だよね、たぶん?
Rubyのトレードオフが好きな人はたくさんいるし、Shopify、GitHub、GitLab、Airbnb、Stripeみたいな超巨大企業がRubyをめっちゃうまく使ってるじゃん。
嫌なら使わなきゃいいんだよ。
あんたの承認なんかいらないし。
Rubyは10年やったから、この件に関しては多少は発言権あると思ってる。
レスポンス待ちが問題になるのは確かだね。
そういう用途向けじゃないと思うけど、インプットに基づいて成果物を処理・作成するツールには使えるんじゃないかな。
MistralとローカルLLMが大好きなんで、これはぜひ追加したいところだね。