背景

なんか適当なテキストデータ食わせたものをインデックスするサービス考える

データ量

LLMが出力するのでそのLLMの出力の最大を検討すればいいのでは?

現状多く見積もっても10Kぐらいが最大。input はもちろんもっと多い

1万くらいの文書だと 10k * 10 K だから大体 100M ぐらいなので余裕感ある。 全部メモリに乗せられそう

検索手段

  • 全文検索:
    • 文書内の単語ごとに出現頻度や位置を記録したインデックスに対して検索
    • 完全一致に強い。コンテキスト把握は困難
    • ElasticSearch / Lucene / etc
  • Vector検索
    • 文書/単語単位でベクトル化(座標化)して保存。検索クエリもベクトル化(座標化)し、ベクトル空間上の2点距離を比較
    • PineCone / Qdrant / 既存のサービスの追加機能(pgVector/ElasticSearch)

性能関係

https://www.elastic.co/jp/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster

平均シャードサイズは最小で数GB、最大で数十GBに保つようにしましょう。時間ベースのデータを使用するケースでは、シャードサイズを20GBから40GBにするのが一般的です。

1GB以下だと多分余裕

料金

https://aws.amazon.com/jp/kendra/pricing/

これなら Qdrant とかを ECS にホスト→S3からロードが一番良さそう? S3に置いといて、Batch Task で入れるのが強そう。 でも永続ストレージいるからコンテナとは相性そこまでよくなさそうなんだよな。

フロント

WEBサービスとして使うなら認証が欲しい 認可も必要ならアプリと直結だけど、認証だけならアプリと分離した方が良さげ

https://github.com/aws-samples/bedrock-claude-chat/tree/main/frontend Vite 使ってるな

うーーーん、フロントはよくわからんな Amplify 使っちゃうかも

テキトーに作ってみる

Qdrant 導入

Docker image 確認 https://github.com/qdrant/qdrant/blob/master/Dockerfile

wget https://raw.githubusercontent.com/qdrant/qdrant/master/Dockerfile
code Dockerfile

ストレージだけは最低限変えたい https://qdrant.tech/documentation/concepts/storage/

  • in-memory (on RAM)
  • mmap storage (on Storage)

中身確認

ARG APP=/qdrant

RUN if [ "$USER_ID" != 0 ]; then \
        groupadd --gid "$USER_ID" qdrant; \
        useradd --uid "$USER_ID" --gid "$USER_ID" -m qdrant; \
        mkdir -p "$APP"/storage "$APP"/snapshots; \
        chown -R "$USER_ID:$USER_ID" "$APP"; \
    fi

EXPOSE 6333
EXPOSE 6334

なるほどね〜 データ量少ないなら、起動時に毎回pullしてくる感じでもいいのかも? とりあえず一旦そのままにしとく

推奨フローはやっぱり ローカルpython での操作っぽい https://qdrant.tech/documentation/

finch vm start
cd server 
finch build .

OSX上だとビルドエラー。イメージ持ってくる

finch pull qdrant/qdrant
finch run -p 6333:6333 qdrant/qdrant

finch ps
CONTAINER ID    IMAGE                             COMMAND              CREATED           STATUS    PORTS                     NAMES
8c7113488132    docker.io/qdrant/qdrant:latest    "./entrypoint.sh"    32 seconds ago    Up        0.0.0.0:6333->6333/tcp    qdrant-8c711
touch test.py
pip install qdrant-client

python test.py

レスポンス返ってきた

from qdrant_client import QdrantClient

client = QdrantClient(url="http://localhost:6333")

# # Prepare your documents, metadata, and IDs
# docs = ["Qdrant has Langchain integrations", "Qdrant also has Llama Index integrations"]
# metadata = [
#     {"source": "Langchain-docs"},
#     {"source": "Linkedin-docs"},
# ]
# ids = [42, 2]

# # Use the new add method
# client.add(
#     collection_name="demo_collection",
#     documents=docs,
#     metadata=metadata,
#     ids=ids
# )

search_result = client.query(
    collection_name="demo_collection",
    query_text="This is a query document"
)
print(search_result)

streamlint で フロント作ってみるか

# main.py
from qdrant_client import QdrantClient

client = QdrantClient(url="http://localhost:6333")

# main.py
import streamlit as st

def search():
    name = st.session_state["text_keyword"]
    st.title("Search Result!")
    st.write(name)

    search_result = client.query(
        collection_name="demo_collection",
        query_text=name
    )
    st.write(search_result)

def main():
    st.title("Qdrant Example Search page")
    st.button("Search", on_click=search)
    st.text_input("Keyword", key="text_keyword")

if __name__ == "__main__":
    main()

streamlit run test.py

これ両方デプロイすれば概ねOKそう?

  • 8501
  • 6333

ECSの場合どうするんやっけ? タスク定義作って、ポート開ければ良さげか

フロントのイメージ

1.DockerでStreamlitのwebアプリを動かす https://zenn.dev/kthrlab_blog/articles/2250b74ec16206 https://docs.streamlit.io/knowledge-base/tutorials/deploy/docker

test.py を main.py に変えとく

touch Dockerfile
code Dockerfile

以下だけ変える

# RUN git clone https://github.com/streamlit/streamlit-example.git .
ADD ./main.py /app/main.py
finch build -t streamlint .

一回起動した後、app url を環境変数にしとく requirements.txt 確認 https://github.com/streamlit/streamlit-example

touch requirements.txt
code requirements.txt
altair
pandas
streamlit
qdrant-client
fastembed
finch build -t streamlint .
finch images
REPOSITORY       TAG       IMAGE ID        CREATED          PLATFORM       SIZE         BLOB SIZE
streamlint       latest    66792fbf0c7e    6 minutes ago    linux/amd64    1.1 GiB      434.9 MiB
qdrant/qdrant    latest    5bd665cb6e9b    4 weeks ago      linux/amd64    161.3 MiB    56.0 MiB
finch run -p 80:8501 streamlint
finch exec -it 76c42b0609c0 /bin/bash
curl localhost:8501

http://localhost/ 見えたOK fastembedもいるっぽい…! 導入してやりなおし-> OK

finch kill $(finch ps -a)

ローカルで行けたので、ECSにデプロイ

finch run -p 6333:6333 qdrant/qdrant
finch run -p 80:8501 streamlint

OK? あ〜〜当たり前だけどdocker compose使わんとコンテナ間通信無理ですわ

ECRに push する https://ap-northeast-1.console.aws.amazon.com/ecr/get-started?region=ap-northeast-1

AWS_REGION=ap-northeast-1
REPOSITORY_NAME=docsearch
ACCOUNTID=hoge

aws ecr create-repository --repository-name ${REPOSITORY_NAME} --region ${AWS_REGION}

REGISTRY_ID=hoge
aws ecr get-login-password --region ${AWS_REGION} | finch login --username AWS --password-stdin ${ACCOUNTID}.dkr.ecr.${AWS_REGION}.amazonaws.com

タグ付け

finch tag qdrant/qdrant:latest  ${ACCOUNTID}.dkr.ecr.${AWS_REGION}.amazonaws.com/docsearch:qdrant
finch tag streamlint:latest ${ACCOUNTID}.dkr.ecr.${AWS_REGION}.amazonaws.com/docsearch:streamlint
finch push ${ACCOUNTID}.dkr.ecr.${AWS_REGION}.amazonaws.com/docsearch:qdrant

finch push ${ACCOUNTID}.dkr.ecr.${AWS_REGION}.amazonaws.com/docsearch:streamlint

アップロード完了

Task definition 作成

参考 https://github.com/aws-samples/aws-containers-task-definitions/blob/master/kibana/kibana_fargate.json

JSONじゃなくて YAML にして欲しいな

{  
   "memory":"2048",
   "networkMode":"awsvpc",
   "cpu":"1024",
   "family":"docsearch",
   "requiresCompatibilities": [
      "FARGATE"
   ], 
   "executionRoleArn": "arn:aws:iam::xxx:role/ECSTaskExecutionRole",
   "containerDefinitions":[  
      {  
         "portMappings":[  
            {  
               "hostPort":6333,
               "containerPort":6333,
               "protocol":"tcp"
            }
         ],
         "essential":true,
         "name":"qdrant",
         "environment":[],
         "cpu": 512,
         "memory": 1536,
         "image":"xxx.dkr.ecr.ap-northeast-1.amazonaws.com/docsearch:qdrant",
         "logConfiguration":{
            "logDriver":"awslogs",
            "options":{  
               "awslogs-group":"/ecs/qdrant",
               "awslogs-region":"ap-northeast-1",
               "awslogs-stream-prefix":"ecs"
            }
         }
      },
      {  
         "portMappings":[  
            {  
               "hostPort":8501,
               "containerPort":8501,
               "protocol":"tcp"
            }
         ],
         "essential":true,
         "name":"streamlint",
         "environment":[
            {
                "name": "APPURL",
                "value": "http://localhost:6333"
            }
         ],
         "cpu": 512,
         "memory": 512,
         "image":"xxx.dkr.ecr.ap-northeast-1.amazonaws.com/docsearch:streamlint",
         "logConfiguration":{
            "logDriver":"awslogs",
            "options":{  
               "awslogs-group":"/ecs/streamlint",
               "awslogs-region":"ap-northeast-1",
               "awslogs-stream-prefix":"ecs"
            }
         }
      }
   ]
}

ECRコンソールから定義作成 https://ap-northeast-1.console.aws.amazon.com/ecr/get-started?region=ap-northeast-1

CreateService

ECS でデプロイしているけど、ロールやCWL周りで色々トラシュー

デプロイ完了

ecs exec

nodeIPに接続→いけた

http://x.x.x.x:6333/

データ挿入 一応レスポンス返ってきた いけた