📘

guidanceでgoogle custom search api試してみた

2023/06/10に公開

はじめに

Microsoftが開発されたguidanceを少し触ってみたメモです。
langchainだと自分の思うようにコントロールできないことが多かったので、guidanceだとその辺り解決するのかなぁと思って今回検証してみました。

今回検証してみたコードはこちらのリポジトリにまとめております。

やったことは、

  • guidanceの動かし方の検証
  • guidanceからgoogle searchができるかどうかの検証
    です。

guidanceの公式サンプルにbing searchを使った実装例があったのでそれを参考にしています。

準備

  1. OpenAI API Keyの取得
  2. google custom searchの設定・Key取得
    • この辺りはこのサイトを参考にさせていただきました。
  3. .envの用意
    OPENAI_API_KEY=""
    GOOGLE_CUSTOM_SEARCH_API_KEY=""
    CUSTOM_SEARCH_ENGINE_ID=""
    
  4. 検証環境の用意
    • ライブラリのインストール
      pyenv local 3.10.9
      python -m venv venv
      venv/Script/activate
      (venv)pip install guidance==0.0.61 jupyterlab python-dotenv
      

実装について

今回はノートブックで実装しました。

ライブラリとAPI KEYを設定

import os

import guidance
import requests
from dotenv import load_dotenv


load_dotenv()
GOOGLE_CUSTOME_SEARCH_API_KEY=os.getenv("GOOGLE_CUSTOM_SEARCH_API_KEY")
GOOGLE_CUSTOME_SEARCH_ENGINE_ID=os.getenv("CUSTOM_SEARCH_ENGINE_ID")

GOOGLE_CUSTOME_SEARCH_BASE_URL = "https://www.googleapis.com/customsearch/v1"

Google Search周りを実装

top=n件の検索結果を[{"title": "", "snippet": ""}, ...]として返すように実装

def google_search(search_term: str, num: int = 3) -> dict:
    params = {
        'q': search_term,
        'key': GOOGLE_CUSTOME_SEARCH_API_KEY,
        'cx': GOOGLE_CUSTOME_SEARCH_ENGINE_ID,
        'num': num,
    }
    response = requests.get(GOOGLE_CUSTOME_SEARCH_BASE_URL, params=params)
    return response.json()["items"]


def get_top_snippets(query: str, n: int = 3):
    results = google_search(query, num=n)[:n]
    return [{'title': x['title'], 'snippet': x['snippet']} for x in results]

guidanceの実装

検索するか否か判定する関数と、検索関数を実装

def is_search(completion: str) -> bool:
    return '<search>' in completion


def search(query: str) -> dict:
    return get_top_snippets(query)

assistantの回答が事実に依存する場合はsearch関数を使うように指示

guidance.llm = guidance.llms.OpenAI("gpt-3.5-turbo")

prompt = guidance('''{{#system~}}
あなたは親切なアシスタントです。
{{~/system}}
{{#user~}}
今後、あなたの回答が事実関係に依存する場合は、回答前に<search>query</search>という機能でウェブを検索してください。そうすれば、私がウェブ検索結果を貼り付けますので、あなたはそれに答えることができます。
{{~/user}}
{{#assistant~}}
わかりました、そうさせていただきます。
{{~/assistant}}
{{#user~}}
さぁ始めましょう!
{{~/user}}
{{#assistant~}}
よし、準備OKだ。
{{~/assistant}}
{{#user~}}
{{user_query}}
{{~/user}}
{{#assistant~}}
{{gen "query" stop="</search>"}}{{#if (is_search query)}}</search>{{/if}}
{{~/assistant}}
{{#if (is_search query)}}
{{#user~}}
Search results: {{#each (search query)}}
<result>
{{this.title}}
{{this.snippet}}
</result>{{/each}}
{{~/user}}
{{#assistant~}}
{{gen "answer"}}
{{~/assistant}}
{{/if}}''')

試す

天気を聞いてみる

query = "今日の大阪の天気は?"
p1 = prompt(user_query=query, search=search, is_search=is_search)
p1

おわりに

今回はguidanceのサンプルを見ながら、ユーザーからの質問のリファレンスとしてGoogle Search APIを使うことができるかの検証を行いました。
今の実装ではlangchainの精度には程遠くはありますが、guidanceを使うと自分の任意のタイミングで処理を差し込むことができるので、かなり個人的には使いやすいと思いました。

Discussion