RAGを実装してみた

概要

以前にOllamaをインストールしたので、今度はRAG(Retrieval-Augmented Generation)を実装してみました。

用語

RAG

www.nri.com

Retrieval-Augmented Generationの略でモデルにない回答に対する問題に対して、外部の知識を利用して答える技術。

実装

モジュールのインストール

pip install llama_index

プログラム

from llama_index.llms.ollama import Ollama
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex
from llama_index.embeddings.huggingface import HuggingFaceEmbedding


def main():
    llm = Ollama(model="Elyza", request_timeout=30.0)
    embed_model = HuggingFaceEmbedding(model_name="sentence-transformers/all-MiniLM-L6-v2")
    # PDF(テキスト)の読み込み
    reader = SimpleDirectoryReader(input_files=["./input_data/test.txt"])
    data = reader.load_data()
    index = VectorStoreIndex.from_documents(data, embed_model=embed_model)
    query_engine = index.as_query_engine(llm=llm, streaming=True, similarity_top_k=3)
    response = query_engine.query(
        "RAGとは何ですか?"
    )
    response.print_response_stream()
    print()
    for node in response.source_nodes:
        print(f"Text: {node.node.text[:100]}...")
        # print(f"Similarity score: {node.similarity_score}")

if __name__ == '__main__':
    main()

input_data/test.txtの中身は下記の通りです。

Retrieval-Augmented Generation (RAG) は、大規模言語モデル(LLM)によるテキスト生成に、外部情報の検索を組み合わせることで、回答精度を向上させる技術のこと。

「検索拡張生成」、「取得拡張生成」などと訳されます。外部情報の検索を組み合わせることで、大規模言語モデル(LLM)の出力結果を簡単に最新の情報に更新できるようになる効果や、出力結果の根拠が明確になり、事実に基づかない情報を生成する現象(ハルシネーション)を抑制する効果などが期待されています。
(読み:ラグ)

結果

外部知識がない場合は下記の出力結果となります。

% python src/rag_project/main.py
RAGとは「Red(赤)・Amber(黄)・Green (緑) 」の略語です。

業務改善やプロジェクト管理など、様々な状況で利用されます。

例えば、プロジェクトの進捗状況を示すために「RAG」が使われます。

- レッド: 課題あり、対処が必要な状況
- アムベル: 注意が必要ですが、何とかなるかもしれません
- グリーン: 順調です、心配ない

外部知識がある場合は下記の出力結果となります。

% python src/rag_project/main.py
/Users/seiki/program/python/rag-project/.venv/lib/python3.8/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
  warnings.warn(
RAGは、大規模言語モデル (LLM) によるテキスト生成に、外部情報の検索を組み合わせることで、回答精度を向上させる技術のことです。

ollamaをMacにインストールした

概要

CPUでLLMを推論することができるollamaMacへインストールしたので備忘録。

環境

Macbook Air M3

手順

ollamaをインストール

ollama.com

公式サイトより、zipファイルをダウンロードして解凍。

コマンドラインで確認

ollamaをコマンドラインで実行できるか確認。

% ollama
Usage:
  ollama [flags]
  ollama [command]

Available Commands:
  serve       Start ollama
  create      Create a model from a Modelfile
  show        Show information for a model
  run         Run a model
  pull        Pull a model from a registry
  push        Push a model to a registry
  list        List models
  ps          List running models
  cp          Copy a model
  rm          Remove a model
  help        Help about any command

Flags:
  -h, --help      help for ollama
  -v, --version   Show version information

Use "ollama [command] --help" for more information about a command.

コマンドラインで実行できそうなので、試しにllama3を動かしてみました。

% ollama run llama3
pulling manifest
pulling 6a0746a1ec1a... 100% ▕████████████████████████████████████████▏ 4.7 GB
pulling 4fa551d4f938... 100% ▕████████████████████████████████████████▏  12 KB
pulling 8ab4849b038c... 100% ▕████████████████████████████████████████▏  254 B
pulling 577073ffcc6c... 100% ▕████████████████████████████████████████▏  110 B
pulling 3f8eb4da87fa... 100% ▕████████████████████████████████████████▏  485 B
verifying sha256 digest
writing manifest
removing any unused layers
success
>>> Send a message (/? for help)
>>> こんにちは
こんにちは!😊 How are you today? 🙋

>>> おすすめのプログラム言語を教えて下さい。
🤩 Ah, you want to learn a programming language, huh? 😄

I can teach you some basic concepts and syntax of Python, one of the most popular programming
languages. 🐍

Here's a simple "Hello, World!" program in Python:

print("こんにちは!")

Let me explain what each line does:

1. `print()`: This is a function that outputs text to the screen.
2. `"こんにちは!"`: This is the string (text) we want to print.

To run this code, you'll need to have Python installed on your computer. You can download and
install it from the official Python website: <https://www.python.org/downloads/>

Once you have Python installed, open a text editor or IDE (Integrated Development Environment),
type in the code above, save it as a `.py` file (e.g., `hello.py`), and then run it using the
command `python hello.py`.

That's it! You just wrote your first program in Python. 🎉

Now, let me ask you: What would you like to learn next? Do you have any specific interests or
goals in mind for your programming journey? 🤔

日本語で質問したとしても、英語で返答されますが質問の内容自体は理解されているみたいですごい。

【python】ryeにuvをインストールしてみる

概要

github.com

以前にPythonの開発環境にryeを乗り換えたところ、uvに対応していたのでインストールしてみました。

環境

用語

rye

rye-up.com

公式の文章になりますが、Pythonでパッケージ管理だけでなく、プロジェクト管理も行うためのツールになります。

github.com

元々はRustにおけるcargoのように、プロジェクト毎にインストールされているパッケージをPythonでも管理したいという思いから作られています。

pyenvとの違い

例えば、pyenvと比較するとpyenvでは先に仮想環境を作成して、次にプロジェクトごと(フォルダごと)にプログラムを実行します。
そのために該当のプロジェクトでプログラムを実行する場合は

  1. 該当の仮想環境に切り替える(例:pythonのバージョンは3.10、パッケージモジュールは⚪︎⚪︎など)

  2. プロジェクト(フォルダ)に移動

  3. プログラムを実行

という手順になります。
そのために該当のプロジェクトでプログラムを実行しようとすると仮想環境を切り替えて実行する必要があります。
一方で、Ryeでプロジェクト管理するとプロジェクトごと(フォルダごと)に必要なパッケージやプログラムのバージョンなども管理できるため、

  1. プロジェクト(フォルダ)に移動

  2. プログラムを実行

で実行することができます。

uv

github.com

こちらも、公式の文章になりますが、Rustで実行されているPythonモジュールのインストーラであり、pipでパッケージをインストールするよりも高速です。

方法

Ryeのバージョンをチェック

下記より、0.11.0であることが分かります。
対応しているバージョンは0.24.0以上なので、アップデートする必要があります。

% rye --version
rye 0.11.0
commit: 0.11.0 (f6f63d6c1 2023-07-18)
platform: macos (x86_64)
self-python: cpython@3.11
symlink support: true

Ryeのバージョンのアップデート

下記のrye self updateコマンドを実行して、バージョンをアップデート。
ただし、このままだとuv enabled: falseの通り、uvは使えていないので、設定でtrueに変更します。

% rye self update
Updating to latest
Checking checksum
Updated!

rye 0.33.0
commit: 0.33.0 (58523f69f 2024-04-24)
platform: macos (x86_64)
self-python: not bootstrapped (target: cpython@3.12)
symlink support: true
uv enabled: false

uvを使えるように変更

rye-up.com

rye-up.com

公式を参考に下記のコマンドを実行。

% rye config --set-bool behavior.use-uv=true

次にホームディレクトリにあるconfig.tomlに設定されているか確認します。

% rye config --show-path
ホームディレクトリ/.rye/config.toml
% cat ホームディレクトリ/.rye/config.toml

[behavior]
use-uv = true

下記のようにuse-uv = trueとなっていれば、OKです。

[behavior]
use-uv = true

再度、rye self updateを実行します。

% rye self update
Updating to latest
Checking checksum
Validate updated installation
Detected outdated rye internals. Refreshing
Bootstrapping rye internals
Found a compatible Python version: cpython@3.11.3
Initializing new virtualenv in /private/var/folders/gf/5ntllx1s5wjdw2945t44f45w0000gn/T/.tmpopChOT/.venv
Python version: cpython@3.11.3
Updated!

rye 0.33.0
commit: 0.33.0 (58523f69f 2024-04-24)
platform: macos (x86_64)
self-python: cpython@3.11.3
symlink support: true
uv enabled: true

uv enabled: trueとなっていれば、uvを利用できるようになります。

動作確認

uvが使えるようになっているか確認します。

% rye --version
rye 0.33.0
commit: 0.33.0 (58523f69f 2024-04-24)
platform: macos (aarch64)
self-python: cpython@3.11.3
symlink support: true
uv enabled: true
% rye init test-uv-enable
success: Initialized project in /test-uv-enable
  Run `rye sync` to get started
% cd test-uv-enable
% rye add flask
Added flask>=3.0.3 as regular dependency
% rye add pandas
Added pandas>=2.0.3 as regular dependency
% time rye sync
Reusing already existing virtualenv
Generating production lockfile: /test-uv-enable/requirements.lock
Generating dev lockfile: /test-uv-enable/requirements-dev.lock
Installing dependencies
Installed 16 packages in 26ms
 + blinker==1.8.1
 + click==8.1.7
 + flask==3.0.3
 + importlib-metadata==7.1.0
 + itsdangerous==2.2.0
 + jinja2==3.1.3
 + markupsafe==2.1.5
 + numpy==1.24.4
 + pandas==2.0.3
 + python-dateutil==2.9.0.post0
 + pytz==2024.1
 + six==1.16.0
 + tzdata==2024.1
 + werkzeug==3.0.2
 + zipp==3.18.1
Done!
rye sync  0.43s user 0.21s system 101% cpu 0.629 total

「dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.71.dylib」が出た

概要

Node.js を久しぶりに動かしたところ、下記のエラーが出力された。

yld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.71.dylib
  Referenced from: /usr/local/bin/node
  Reason: image not found

環境

- Mac

解決策

エラーが出た内容でググると同じ問題が見つかった。

qiita.com

また、他にも似たような問題が見つかった。

gist.github.com

そのために下記のコマンドを実行

brew reinstall node

Golang で並列処理の実行方法

概要

Golang の特徴として並列処理があります。

  • 実装方法

  • なぜ、並列処理が得意なのか

は調べたことがなかったので、改めて調べたのでまとめます。

実装方法

package main

import (
    "fmt"
)

func server(ch chan string){
    defer close(ch)
    ch <- "one"
    ch <- "two"
    ch <- "three"
}

func main()  {
    var s string

    ch := make(chan string)
    go server(ch)

    s = <- ch
    fmt.Println(s)
        // 「one」が表示

    s = <- ch
    fmt.Println(s)
        // 「two」が表示
}

上記の例では、goroutinechannelの仕組みが利用されています。

goroutine

読み方は「ゴルーチン」
並列処理を行うために宣言され、他のプログラムにおけるスレッドのような扱いになります。
使い方はgo 関数名であり、上記の例であるとgo server(ch)が該当します。

channel

並列処理を行うときに値の引き渡しを行うときに宣言されます。
宣言するときにはch := make(chan string)makeで宣言されます。
チャネルに値を代入するときには、ch <- "one"です。
チャネルからの値を変数へ代入するときには s = <- ch です。

他の実行例

例えば、下記のようにserver_sampleを追加して、goroutine で実行すると実行結果は下記のように「one」「two」「1」「2」と表示される。

package main

import (
    "fmt"
)

// 追加
func server_sample(ch chan string){
    defer close(ch)
    ch <- "1"
    ch <- "2"
    ch <- "3"
}

func server(ch chan string){
    defer close(ch)
    ch <- "one"
    ch <- "two"
    ch <- "three"
}

func main()  {
    var s string

    ch := make(chan string)
    go server(ch)

    s = <- ch
    fmt.Println(s)
         // 「one」が表示されます。
    s = <- ch
    fmt.Println(s)
         // 「two」が表示されます。

         // もう一度、chを宣言をする
    ch = make(chan string)
         // go server_sampleで並列処理をする
    go server_sample(ch)
    s = <- ch
    fmt.Println(s)
         // 「1」が表示されます。
    s = <- ch
    fmt.Println(s)
         // 「2」が表示されます。
}

Golangにおける並列処理

Golang の並列処理は上記のように goroutine で簡単に実装できるので、並列処理が得意と言われています。

並列処理のメリット

例えば、1つの処理をするときには1CPUを利用するとします。
2つ以上の処理を直列で実行するときには複数あるCPUの内、1CPUしか使うことができません。
そのため、直列で処理をする必要がないときには、並列処理を利用して処理を早く終わらせることができます。

「UXデザインの法則」を読みましたので感想

概要

UXデザインの法則 ―最高のプロダクトとサービスを支える心理学を読みましたので、感想とまとめです。

内容

法則

法則名 説明
ヤコブの法則 ユーザは新規のサイト・サービスに経験則から同じ挙動を期待する
フィッツの法則 ターゲットに至るまでの時間は、ターゲットの大きさと近さで決まる
ヒックの法則 意思決定にかかる時間は、選択肢の数と複雑さで決まり、選択肢が少ないほど意思決定の時間が短くなる
ミラーの法則 普通の人が短期記憶に保持できるのは、7(±2)個まで
ポステルの法則 出力は厳密に、入力は寛容に
ピークエンドの法則 経験についての評価は、全体の総和や平均ではなく、ピーク時と終了時にどう感じたかで決まる
美的ユーザビリティ効果 見た目が美しいデザインは使いやすいと感じる
フォン・レストルフ効果 似たものが並んでいると、その中で他とは異なるものが記憶に残りやすい
テスラーの法則 どんなシステムにも、それ以上減らすことのできない複雑さがある
ドハティの閾値 応答が0.4秒以内のとき、コンピュータとユーザの双方が最も生産的になる

感想

まず、最初に私が普段利用しているサービスやサイト、アプリで無意識に行っている動作に対して、名前が付けられていることを知ったことでGoogleで検索しやすいと感じました。
加えて、普段作成しているExcelの資料についても利用できるとは感じました。
例えば、

  • 「美的ユーザビリティ効果」:誰が見ても資料で主張したいことが一目で分かる

  • 「フォン・レストルフ効果」:従来と今回の提案で比較するときの違い

Cursor エディターへ移行した話

概要

最近、Cursorというエディタが賑わっており、様々な記事が書かれています。
cursor.sh Cursorの大まかな概要を確認すると、ChatGPTを主軸としたエディタであり、デバッグやプログラムの生成をChatGPTで自動で行ってもらえるそうです。 また、vscodeの機能はそのままで利用できるみたいなので、今まで利用してきたvscodeからCursorへ移行してみました。

インストール作業

Cursorのインストール方法は公式サイトからインストーラーをダウンロード してきて解凍するのみです。
解凍したインストーラを起動して手順通り進めると、vscode拡張機能を移行するか確認されたので、OKをクリックします。

使ってみて

実際のエディター画面は下記の通りで、ChatGPTの画面が右側で見えており、その他はvscodeと変わりません。

ChatGPT

上記の画面でも見えていますが、質問は日本語にも対応しています。
今回は「Next.js でログイン画面を作成して」と質問したところ、下記の返答が数秒でされました。

Next.jsでログイン画面を作成するには、以下の手順を参考にしてください。
1. 新しいページを作成します。例えば、pages/login.jsという名前で新しいファイルを作成します。

import React from 'react';

const LoginPage = () => {
  return (
    <div>
      <h1>ログイン</h1>
      <form>
        <div>
          <label>ユーザー名:</label>
          <input type="text" name="username" required />
        </div>
        <div>
          <label>パスワード:</label>
          <input type="password" name="password" required />
        </div>
        <button type="submit">ログイン</button>
      </form>
    </div>
  );
};

export default LoginPage;

2.フォームの送信を処理するための関数を追加します。この関数は、ユーザーがログインボタンをクリックしたときに呼び出されます。

import React, { useState } from 'react';

const LoginPage = () => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = (event) => {
    event.preventDefault();
    // ここでログイン処理を行います
    console.log(`Logging in ${username} with password ${password}`);
  };

  return (
    <div>
      <h1>ログイン</h1>
      <form onSubmit={handleSubmit}>
        <div>
          <label>ユーザー名:</label>
          <input type="text" name="username" required onChange={e => setUsername(e.target.value)} />
        </div>
        <div>
          <label>パスワード:</label>
          <input type="password" name="password" required onChange={e => setPassword(e.target.value)} />
        </div>
        <button type="submit">ログイン</button>
      </form>
    </div>
  );
};

export default LoginPage;

この例では、ログイン処理は単にコンソールにログを出力するだけですが、実際にはここでAPIを呼び出したり、認証トークンを保存したりするなど、適切なログイン処理を行う必要があります。

日本語だけでなく、ソースコードも生成してくれています。
この機能はかなり便利です。

エディタの表記名を日本語に変更

小さいことではありますが、コマンドパレットを開いてConfigure Display Languageを選択して、日本語を選択すればエディタの表記名を日本語にしてくれます。