外部ライブラリ使っているAWS Lambda関数(Python)のzipファイル作成シェルスクリプト(Poetry管理プロジェクト)
Poetryで管理しているPythonで書いたAWS Lambdaの関数(外部ライブラリも使っている)を、zipにまとめるスクリプトを書いたので備忘録として残しておく。
フォルダ構成
アプローチ
Poetryで管理している場合、 .venv/lib/python*/site-packages/ に poetry add したライブラリがインストールされるので、site-pckages 下のファイルと、自分で書いたソースコードをガッチャンコしたフォルダを作って、それをzipコマンドでzipにまとめるというやり方をした。
ただし .venv フォルダをプロジェクトディレクトリ内に作成するには、
poetry config virtualenvs.in-project true
で、Poetryの仮想環境をプロジェクトディレクトリ内に作成するよう設定が必要なので注意。
ワイはDockerfileに
RUN poetry config virtualenvs.in-project true
って書いてる。
スクリプト
build.sh の全文
#!/bin/sh cd `dirname $0` cd ../ rm -rf ./dist/ mkdir -p ./dist/package/ cp --recursive ./.venv/lib/python*/site-packages/. ./dist/package/ cp --recursive ./src/. ./dist/package/ cd ./dist/package/ zip -r ../package ./ cd ../ rm -rf ./package/
ポイント
srcディレクトリと.venv/lib/python*/site-packages/ を dist/package/ ディレクトリにコピーしている。
cp --recursive ./.venv/lib/python*/site-packages/. ./dist/package/ cp --recursive ./src/. ./dist/package/
zipを作成した後
zipを作成した後は、マネジメントコンソールからアップロードするなり、aws cli からアップロードするなり好きにしましょうという感じ。
ワイは以下のようなシェルスクリプト(deploy.shに該当)を書いて、S3にアップロードしてからLambda関数を更新するようにしてみた。
#!/bin/sh cd `dirname $0` cd ../ aws s3 cp ./dist/package.zip s3://<Amazon S3バケット名>/<S3キー> aws lambda update-function-code \ --function-name <AWS Lambda関数名> \ --s3-bucket <バケット名> \ --s3-key <S3キー>
LangChain(TypeScript)のRetrievalQAChainとOpenAI APIで、自前のドキュメントに関する質問に答えてくれるプログラムを作ってみた。
概要
OpenAIのChatGPTさん、めっちゃ頭良いけど、自分しか持って無くてインターネットに公開していない情報については回答してくれないので、自分しか持ってないドキュメントの内容に関する質問に回答してくれるプログラムを作ってみたので、その備忘録。
GitHubリポジトリ
使う技術
Node.js(バージョン18.14.2)
TypeScript
LangChain(TypeScript/JavaScript 版)
ダミーファイルを作成
そんなすぐに、良い感じのドキュメントを用意できないので、BingのAIさんに、いくつか社内用語を出力してもらって、それをテキストファイルとして保存しました。
テキストファイルはすべて documentsディレクトリに保存しています。
ディレクトリ構成
こんな感じ
create-vector フォルダ
documentsフォルダ内のテキストをvectorストアに保存するプログラム
call-query フォルダ
保存済のvectorストアを読み込み、質問の回答を得るプログラム
documents フォルダ
テキストファイルを保存しておくフォルダ(ここでは架空の企業の社内用語と説明のテキストファイル)
database フォルダ
ベクターストアの内容を保存しておくフォルダ
今回はベクターストアとして、ローカルに保存できるHNSWLibを利用します。
実行の流れ
ドキュメントの内容をベクターストアに保存するプログラムを実行する ↓ 保存済のvectorストアを読み込み、質問の回答を得るプログラムを実行する
プログラムのコード
create-vector-store/index.ts
documentsフォルダ内のドキュメントの内容をベクターストアに保存するプログラム
RecursiveCharacterTextSplitterで、ドキュメントの内容を100token以下になるまで分割しVectorStoreに保存しています。
import path from 'path'; import dotenv from 'dotenv'; import { OpenAIEmbeddings } from 'langchain/embeddings/openai'; import { DirectoryLoader } from 'langchain/document_loaders/fs/directory'; import { TextLoader } from "langchain/document_loaders/fs/text"; import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; import { HNSWLib } from "langchain/vectorstores/hnswlib"; // .envファイルから環境変数を読み込み dotenv.config(); const main = async function () { console.log("start"); // 自前で準備したドキュメントを保存しているディレクトリの絶対パス const documentPath = path.join(__dirname, '../documents/'); const directoryLoader = new DirectoryLoader(documentPath, { '.txt': (path) => { return new TextLoader(path) } }); const documents = await directoryLoader.load(); const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 100, chunkOverlap: 10, }); const texts = await splitter.splitDocuments(documents); const embeddings = new OpenAIEmbeddings({ openAIApiKey: process.env.OPENAI_API_KEY }); const vectorStore = await HNSWLib.fromDocuments(texts, embeddings); const databasePath = path.join(__dirname, '../database/'); await vectorStore.save(databasePath); console.log("end"); }; main();
call-query/index.ts
保存済のvectorストアを読み込み、質問の回答を得るプログラム
ここで、保存済のベクターストアから読み取り、RetrievalQAChainを使って自前のドキュメントの内容に対する質問に関する質問の回答を得ます。
import path from 'path'; import dotenv from 'dotenv'; import { OpenAIEmbeddings } from 'langchain/embeddings/openai'; import { OpenAI } from "langchain/llms/openai"; import { HNSWLib } from "langchain/vectorstores/hnswlib"; import { RetrievalQAChain } from "langchain/chains"; // .envファイルから環境変数を読み込み dotenv.config(); const main = async function () { // 実行時引数が指定されているか確認 if (process.argv.length < 3) { return; } const databasePath = path.join(__dirname, '../database/'); const embeddings = new OpenAIEmbeddings(); // 保存済のベクターストアから読み込み const vectorStore = await HNSWLib.load(databasePath, embeddings); const model = new OpenAI({ openAIApiKey: process.env.OPENAI_API_KEY }); const chain = RetrievalQAChain.fromLLM(model, vectorStore.asRetriever()); // 実行時引数から質問内容を取得 const query = process.argv[2]; const { text } = await chain.call({ query }); // 質問内容と回答内容をコンソールに表示 console.log(`Q: ${ query }\nA: ${ text }`); }; main();
実行手順
ベクターストアの作成
$ cd <プロジェクトディレクトリ>/create-vector-store/ $ npm run start
質問をする
$ cd <プロジェクトディレクトリ>/create-vector-store/ $ npm run start --query=<質問>
実行結果例
ちゃんとdocumentsフォルダに保存しているドキュメントの内容に沿った回答をもらえました。
また、試しにdocumentsフォルダ内のドキュメントには記載の無い質問をすると、ちゃんと "分からない" 旨の回答がかえってきました。素直だな。
まとめ
とりあえず、自前で準備したドキュメントに関する回答を得ることができた。
今回はDirectoryLoaderの中で、テキストファイルに対してのみ読み取りを行っているが、これをPDFやWord、PowerPointに対応させれば、より汎用的に使えるようになるかもしれないなと思いました。
Dockerfileで、PATH通らなかった
最初こう書いてたんだけど、Node.jsのPATHが上手く通ってなくって、
ENV PATH $HOME/.nodebrew/current/bin:$PATH
↓のように書き直したら通りました。
ENV PATH /root/.nodebrew/current/bin:$PATH
以上でした。おやすみなさい。
技術書典2に出店しました
技術書典2に出店してきました。
参加された方とか運営の方とかおつかれさまでした。
今回は『TypeScriptでつくるシングルページアプリケーション』という本のみ販売しました。
TypeScriptでつくるシングルページアプリケーション内のソースは以下のリポジトリで公開しています。 また、今回印刷した30部しか刷らなかったため、すぐに売り切れてしまい、 「githubの方には本文は載ってないんですか?」って結構聞かれたので、リポジトリの方には、本文のソースとしてワードファイルをあげました。
Microsoft Office ワードを持っていない場合、Microsoft Office OnlineやLibre Officeなどで開けば、若干デザインは崩れるかもですが、一応読むことはできると思います。
また、同様の内容の本を4月30日の超技術書典でも販売する予定です。
次はポスター的なものとか、机の上にかけるシート的なものとか準備して、もう少しブースっぽく構えたいなーと思いました。
まる。