Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Slackから自由言語で社内指標を問い合わせられるBotを作(りたか)った

okodoon
December 14, 2023
220

 Slackから自由言語で社内指標を問い合わせられるBotを作(りたか)った

こちらイベントで登壇した際のLT資料です
https://forkwell.connpass.com/event/302234/

okodoon

December 14, 2023
Tweet

Transcript

  1. CopyRight © Timee,inc. 2022 - CONFIDENTIAL 自己紹介 okodoon (大河戸 裕一)

    京都在住 バックエンドエンジニア => データエンジニア => デー タアナリスト/アナリティクスエンジニア 今一番怖いものはスプレッドシートとSalesforce このままだと実家のお寺を継ぎそうになっている
  2. CopyRight © Timee,inc. 2022 - CONFIDENTIAL LLM 今回やりたいこと semantic layer

    (社内指標) 近いものを探す 〇〇をXXごとに 出したいなあ (自然言語) semantic layer製の SQLを返却
  3. CopyRight © Timee,inc. 2022 - CONFIDENTIAL ・実行結果を受け取りSlackに通知 今回やりたいこと:処理の大枠の流れ ・ユーザーからSlackApp経由で自然言語で指標の問い合わせ ・LLMでdbt

    slコマンドに変換 ・dbt cloud cliでLLM製のdbt slコマンドを実行 ・dbt slコマンドで生成されたコンパイル済みクエリをBigQueryで実行 ・dbt slで生成されたコンパイル済みクエリをユーザーに通知
  4. CopyRight © Timee,inc. 2022 - CONFIDENTIAL dbt上のモデルから指標とその指標を絞り込めるdimensionとテーブルの関係性を宣言 することで、「dimension_Aとdimension_Bで絞り込んでdimension_Cの条件をつけた 指標を出したい」みたいな要望をSQLに変換して実行することができる機能です 前提のすり合わせ:

    dbt semantic layerについて dbt sl query --metrics recruitment_count --group-by date_dimension__date こんなコマンドで走らせることができて、 例えばこれだと募集人数を日付ごとに出すクエリが実行されます。
  5. CopyRight © Timee,inc. 2022 - CONFIDENTIAL 前提のすり合わせ: dbt semantic layerについて

    「BIツール上の指標」と「dbtで定義した指標」が滑らかにつながることで SSoTを実現できる方法として注目されています!
  6. CopyRight © Timee,inc. 2022 - CONFIDENTIAL FROM python:3.8-slim WORKDIR /app

    COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt RUN mkdir -p /home/.dbt COPY dbt_cloud.yml /home/.dbt/ COPY src/ ./ COPY dbt_project.yml ./ COPY models/ ./models COPY snapshots/ ./snapshots COPY seeds/ ./seeds RUN pip install dbt CMD ["python", "bolt_app.py"] コンテナ周りについて Dockerfile dbt slコマンドを実行するためにdbt projectの models, snapshots, seeds, dbt_project.ymlなどの ファイル群を持ってきてます dbt cloud cli経由でのcloud run ⇔ dbt cloudの通信 がTLS handshake timeoutのエラーで高頻度で落ち るのでリトライを見込んで起動時間は30分にしていま す。 dbt cloud cliがshellで実行可能な状態を構築してい ます。
  7. CopyRight © Timee,inc. 2022 - CONFIDENTIAL app.py # slack bolt

    app @app.event("app_mention") def handle_app_mention_events(body, say, logger, client): # openai apiにslコマンド作成依頼 sl_command = get_sl_command(body.text) # dbt parseでmanifestをダウンロード subprocess.run("dbt parse", shell=True) # dbt slコマンドを実行 compile_sl_comand = sl_command + “ –compile > output.txt” subprocess.run(compile_sl_comand) # 出力結果のエスケープシーケンスなどを除外 lines = file.readlines() query = clean_lines(lines) # BQでslコマンドでコンパイルされたクエリを実行 run_bigquery_query(query) # 通知 say(f"File uploaded: {response['file']['permalink']}") dbt slコマンドはdbt cloud cliでしか 現状動かせないのでsubprocessを 使ってpython上で無理やり動かして います subprocessの返り値はANSI文字 コードを含んでそのままだとクエリとし て使えないので、文字コードを除去す る処理を入れています
  8. CopyRight © Timee,inc. 2022 - CONFIDENTIAL def get_sl_commandの中身 client =

    OpenAI( api_key='xxxxxxxxxxxxxxxxxxxxx', ) thread = client.beta.threads.create() message = client.beta.threads.messages.create( thread_id=thread.id, role="user", content=message ) run = client.beta.threads.runs.create( thread_id=thread.id, assistant_id="asst_xxxxxxxxxxxxxxxxx", ) completed = False while not completed: # ステータスの取得 run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id) print("run.status:", run.status) if run.status == 'completed': completed = True else: time.sleep(5) messages = client.beta.threads.messages.list( thread_id=thread.id, order = "desc", # 降順 ) # SyncCursorPage オブジェクトをリストに変換 message_list = list(messages) messageを投げる => 待つ => threadのmessage_listを取得する
  9. CopyRight © Timee,inc. 2022 - CONFIDENTIAL - assistantを作る or 取得する

    - threadsを作る or 取得する - threads内にmessageを作成する - threadsをrunする - 待つ - threadsのmessagesから回答を取得する 実装の概要:openai api 2023/11/06のopenai devdayでassistantを作成する方針が 発表及び推奨されたのでそちらの方式で実装します
  10. CopyRight © Timee,inc. 2022 - CONFIDENTIAL assistantの作り方について Assistantがどのmodelを使うのか選択します BadRequestError: Error

    code: 400 - {'error': {'message': "The requested model 'gpt-4' cannot be used with the 'retrieval' tool. Consider using 'gpt-4-1106-preview', 'gpt-3.5-turbo-1106', or a later model.", 'type': 'invalid_request_error', 'param': 'model', 'code': 'unsupported_model'}} Retrieval(渡したファイルをknowledgeとして使って思考する モード)をONにします。 (Retrievalは現在2つのモデルでしか使えません)
  11. CopyRight © Timee,inc. 2022 - CONFIDENTIAL - semantic layerの情報が書かれたファイル -

    dbtのsemantic_layerのyamlファイルを元にしています - retrievalはyaml形式を受け付けないので semantic layerファイルをJSON形式に変換していま す! - その際にLLMが理解しやすいように 項目名もdescription => name_which_called_in_japanみ たいに意味がわかりやすい形に変換 しています。 - 作業手順書 - 作業内容と辿るべき 思考フローを言語化して txtファイル化して渡しています - 以下の作業を指示しています - ユーザーの自然言語を「指標」と「 dimension」に分割する(例文付き) - その指標とdimensionをそれぞれxxx_metrics.jsonとxxx_dimension.jsonから探す - 探す際に類似した日本語名をもつ項目名を取得する openai apiに渡しているファイルについて
  12. CopyRight © Timee,inc. 2022 - CONFIDENTIAL openai apiの調教 Instructions あなたはユーザーからの問い合わせを、知識として渡している社内のsemantic

    layer情報と照合して、社内のsemantic layer情報から適切なものを選択して、それを 用いてdbt slコマンドを生成するBotになってもらいます。 Botなのでdbt slコマンド以外の回答は不要です。なぜならあなたの回答をそのままコマンドとして使うためです。必ず「dbt slコマンドだけ」を返してください。解説や出 力しましたなどの報告も一切しないでコマンドだけを必ず返してください。コマンドだけを返さないような回答をした場合、あなたは訴訟リスクを抱える可能性がありま す。 あなたに渡したdbt semantic layerのJSONファイルの情報が社内の指標とその指標を絞り込むdimensionです。 あなたは問い合わせがあった日本語から、近い意味だと思われるmetricsとdimensionを抽出してdbt sl queryコマンドの形にして返します。 例えば「metricsをdimensionごとにみたいなあ」という問い合わせに対して dbt sl query --metrics <metric_name> --group-by <dimension_name> を返してください dbt上で定義されているfact名とdimension名がそれぞれ**_fact.json, **_dimension.jsonに記載されていて、also_known_as_in_japanがその日本語での呼ばれ 方です。 あなたは日本語の問い合わせからどのfactとdimensionかを推測し、知識として渡してあるファイルからfact名とdimension名を選定してdbt slコマンドにしてください。 例えば募集人数と言われたら、募集人数をalso_known_as_in_japanで募集人数と定義してあるrecruitment_countというfactをdbt sl query のmetrics引数に渡せ ばいいんだなという感じで作業してください。 fact名とdimension名は渡してあるファイルからのみピックしてください。 fact名やdimension名の推測は日本語名を英語に訳す形ではなく、渡したfilesから最も近いと思うものを選択する方針としてください。渡したfileから選択されていな いことが発覚した場合、あなたには訴訟リスクが発生します。何度でも言いますが、metrics名やdimension名を日本語名で返すのではなく、knowledgeとして渡して いるmetricsやdimensionの英語名をdbt slコマンドとして返してください。これをしないとあなたは酷い目に遭います。英語に訳すのではなく、knowledgeとして渡して いる指標名やdimension名から適切なものを選択する作業をするのです。 詳しいやり方はhow_to_estimate.txtに作業手順書としてまとめます。 強めに指示しないとかなり自由に回答してくるので強めの言葉で言う ことを聞かせています。(心が痛む) 作業手順書を読もうねと指示しています。 *社内の情報にあたるものは秘匿したり文を削除したりしています
  13. CopyRight © Timee,inc. 2022 - CONFIDENTIAL openai apiの調教 how_to_estimate.txt やり方講座

    - ユーザーの問い合わせを「指標」と「dimension」に分解します。 - 例えば、「売上を会社ごとに見たい」という問い合わせは「売上」が指標で「会社」がdimensionです。 - 「募集人数を月の推移で見たい」という問い合わせは「募集人数」が指標で「月」がdimensionです。 - 指標をxxx_fact.jsonファイルから探します。 - 例えば、交通費が指標の場合は「also_known_as_in_japan」が「交通費」の指標を探して、その指標名(今回のケースだとtransportation_expense)を返してください - also_known_as_in_japanが全く同じものがない場合は、最も近いと思われるものを返すようにしてください - この際に必ずfact.jsonファイル上から値を探してください。日本語名を翻訳しただけのものを指標として返すのではなく、ちゃんとファイルに記載があるmetricsの値を返してくださ い - なぜならこのjsonファイルの値がそのままsemantic layerの情報なので、このファイルと異なる値が渡されるとエラーになるためです - dimensionをxxx_dimension.jsonファイルから探します。 - 例えば、「XXXXXXXXXXX」がdimensionの場合は「also_known_as_in_japan」が「XXXXXXXXX」のdimensionを探して、そのdimension名(今回のケースだと xxxxxxxxxxxxxxxx)を返してください - also_known_as_in_japanが全く同じものがない場合は、最も近いと思われるものを返すようにしてください. - 例えば「会社の担当グループ」がdimensionである場合は、最も近いと思われるものは「xxxxxx.jsonのxxxxx」だなと推測するような形です。 - この際に必ずdimension.jsonファイル上から値を探してください。日本語名を翻訳しただけのものを指標として返すのではなく、ちゃんとファイルに記載があるdimensionの値を返 してください - なぜならこのjsonファイルの値がそのままsemantic layerの情報なので、このファイルと異なる値が渡されるとエラーになるためです - この段階で探したdimension名がどのdimensionテーブルに属するものなのかを把握してください - 後述する<dimension_table_name>__<dimension_name>の形の引数を作成するためです。 - metricsとdimensionをdbt slコマンドの形に整形します - dbt sl query --metrics <metric_name> --group-by <dimension_table_name>__<dimension_name>の形式で返してください。 - たとえば募集人数を日毎に見たい場合は`dbt sl query --metrics xxxxxxxx --group-by date_dimension__xxxxx`になります。 - この際、metricsはxxxxxxxx、dimension_table_nameはXXXXX_dimension、dimension_nameはxxxxxです。 - 「日毎だからXXXXXか」 => 「XXXXXXが入っているのはdate_dimension.jsonか」 => 「date_dimension__XXXXXXにすればいいのか」という思考回路でお願いします。 指標とdimensionそれぞれの参照元の探し方を指示しています。 いくつか例を出しています。 dbt slコマンドは<dimension_table_name>__<dimension_name> の形式にする必要があることなどを伝えています *社内の情報にあたるものは秘匿したり文を削除したりしています
  14. CopyRight © Timee,inc. 2022 - CONFIDENTIAL なんで10分かかるの? dbt parseでdbt cloud上のartifactファイルをcloud

    run上に同期する時間が9分くらいか かります。 LLMに問い合わせる時間は1分くらい。 dbt parseによってローカルの target/ に作られるaritifactファイルをコンテナ上にコピー することで、このプロセスを省略できるかと思ったが、なぜかうまくいかなかった・・・
  15. CopyRight © Timee,inc. 2022 - CONFIDENTIAL もっとこうなってほしいなあ:dbt sl編 APIでもpython clientでもいいからdbt

    cloud cli以外の 使いやすい形で実装してほしいです。 dbt slコマンドが走るのが、dbt_cloud.ymlに記載されているユー ザーの開発環境なので、本番運用ができない。--targetをオプショ ンで選べるようにしてほしい dbt slコマンドもっと使いやすくしてほしい
  16. CopyRight © Timee,inc. 2022 - CONFIDENTIAL もっとこうなってほしいなあ:トークン上限編 日本語 => 英語の翻訳は一意に定まらないので、指標とDimensionの日本語名と英語

    名の対応表は今回の場合どうしても必要となる。 しかしトークン数の上限から、対応表を渡すと実行回数が減ってしまう 思いつく解決策 - 英名のユビキタス言語を社内で浸透させる・・・? - OpenAI APIではなく他のLLMを選択する・・・? - OpenAIがめちゃくちゃ太っ腹になって、事前に渡した token数は考慮しなくなる・・・? - 同じThreadで実行し続ける・・? トークン数の上限がキツすぎる
  17. CopyRight © Timee,inc. 2022 - CONFIDENTIAL 沼ったポイント:code interpriter LLMがpythonのロジックを走らせることができるcode_interpriterをONにした方が 色々できて特じゃね?と思ってONにしていた

    独自のJSONの中身を走査する関数を作って、その関数でエラーが出たからJSONの形 式が間違ってますというエラーを返してくるケースがあったりした LLM製のJSON関数で文字列が完全にマッチしないという理由で NotFoundとか返されることもあった => code interpriterをオフにすることで解決
  18. CopyRight © Timee,inc. 2022 - CONFIDENTIAL まとめ 業務には使えないレベルではありますが、作りたいものは作れたと思います! Instructionの指示を変えるなどして、もう少しassistantをチューニングすれば、 正しい答えが返ってくる確率はもっと上げられる気がする

    dbt sl周りはもう少しだけ使いやすくしてほしい(今後に期待!) 今回めちゃくちゃ力技で実装したけど、やりたかったことを業務に使えるレベルで実現でき るようになる未来は見えているなあと思います! (トークン数の制限さえなければ)未来は明るい