LangChainの対抗馬？Microsoft Guidanceを触ってみる (1)

# ⓪結論・まとめ

guidenceの```{{}}```コマンドを使用し、動的なプロンプトの作成を試しました。

- {{変数名}}
- {{gen}}
- {{each}}- {{this}}

上記の機能を使って、「**三大栄養素と今日の食事についてアドバイスしてくれるAI君**」
という想定でプロンプトを書いてみました。

```py:
import guidance

# 推論に使用する言語モデルを指定
guidance.llm = guidance.llms.OpenAI("text-davinci-003")

# テンプレート内で使用する変数を定義
nutrient_list = ["炭水化物", "タンパク質", "脂質"]
people = [
    {"name": "田中",  "description": "鶏むね肉と白米を食べました"},
    {"name": "木村",  "description": "ラーメンと餃子を食べました"}
]

# プロンプト①を生成
program1 = guidance('''栄養素リスト:
{{#each nutrient_list}}- {{this}}
{{/each~}}''')

# プロンプト②を作成
program2 = guidance('''食事状況:
{{#each people}} {{this.name}}: {{this.description}}
{{/each}}
''')

# 指示文を作成 (モデルにやってもらいたいこと)
instruct = guidance('''あなたは食事のアドバイザーです。
以下のJSONフォーマットに従い、ユーザへのアドバイスを作成してください。
アドバイスを考える際は、足りない栄養を考慮する必要があります。

$JSON_BLOB = {
    "name": "ユーザ名",
    "help": "アドバイス内容"
}

以下の栄養素リストと食事状況に基づいたアドバイスを行います。
{{>program1}}
{{>program2}}

アドバイス：{{gen 'result' n=1 temperature=0.9 max_tokens=256}}''')

executed_program = instruct(
    # プロンプト①に関する引数
    nutrient_list=nutrient_list,
    program1=program1,
    # プロンプト②に関する引数
    people=people,
    program2=program2
)
```

**上記のコードで生成されるプロンプト**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393221/5c3ce056-ff37-188c-3d60-816e80fb0d8a.png)


**実行結果**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393221/44390157-6fc6-3cdc-06aa-41fc4f5f4307.png)


# ①Guidanceってなに？

Guidanceは、今後さらに複雑になるであろうプロンプト開発を見越しているものと捉えています。(私の主観が入っていますが)
またユーザ目線で考えると、複雑なプロンプトを実行するとその分料金が発生します。複雑なプロンプトを簡略化できれば費用も抑えられますよね。

>ガイダンスプログラムは、従来のプロンプティングやチェイン法よりも効果的かつ効率的に現代の言語モデルを制御することを可能にします。ガイダンスは、言語モデルがテキストを処理する方法と実際に一致するように、生成、プロンプティング、論理的な制御を連続した流れで組み合わせることができます。Chain of Thoughtやその他のバリエーション（ART、Auto-CoTなど）などのシンプルな出力構造は、言語モデルの性能を向上させることが示されています。よりパワフルなGPT-4の登場により、さらに豊かな構造が可能になり、ガイダンスによってその構造をより簡単かつ効率的に活用することができます。

出典:https://github.com/microsoft/guidance/


# ②チュートリアルを試してみる

## テンプレートを動的に作成する(1)

```{{変数名}}```を使うと、**テンプレート内外で使用できる変数**を生成できるイメージですかね。
また、```{{gen}}```コマンドと**テンプレート内で推論の実行**ができます。
動的な振る舞いを設計するときに非常に役立ちそうです。

```py:
import guidance

# 使用する言語モデルを指定
guidance.llm = guidance.llms.OpenAI("text-davinci-003")

# プレースホルダを含むテンプレートを定義
program = guidance('''{{user_input}}について教えて。''')

# プロンプトの生成
executed_program = program(
    user_input="三大栄養素"
)

# {{gen}}コマンドによる動的なプロンプト生成
program = guidance('''
    {{user_input}}について教えて。 
    {{gen 'generated' n=1 temperature=0.7 max_tokens=64 top_p=0.9}}''')

executed_program = program(user_input="三大栄養素")

print(executed_program['user_input'])
print(executed_program['generated'])
```

```print()```の部分の出力は以下のようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393221/513d1a1a-cc02-99b6-e138-a028c6e1e9a5.png)



## テンプレートを動的に作成する(2)

{{gen}}コマンドの**n**を増やすことで、テンプレート内での推論回数が増えます。
n=2で試してみました。

```py:
# {{gen}}コマンドによる動的なプロンプト生成②
program = guidance('''{{user_input}}を摂取する上で大事なことは {{gen 'things' n=2 temperature=0.7 max_tokens=32}}''')

executed_program = program(user_input="三大栄養素")

for thing in executed_program['things']:
    print(thing)
```

```print()```の部分の出力は以下のようになりました。
1,2回目で推論結果が少し変わっていることが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393221/b0646991-2762-737e-f036-8921bcfa3b18.png)


## リストや辞書型の変数をテンプレート内で展開する

**リスト**

```py:
# テンプレート内で使用したいリストを定義
nutrient_list = ["炭水化物", "タンパク質", "脂質"]

# テンプレート内でリストを展開
program = guidance('''栄養素リスト:
{{#each nutrient_list}}- {{this}}
{{/each~}}''')

# プロンプトを生成
executed_program = program(
    nutrient_list=nutrient_list
)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393221/3bf0fc60-aff5-4437-1b5b-10a8943eea82.png)


**辞書型リスト List[Dict]**

```py:
# テンプレート内で使用したい辞書型リストを定義
people = [
    {"name": "田中",  "description": "鶏むね肉と白米を食べました"},
    {"name": "木村",  "description": "ラーメンと餃子を食べました"}
]

# テンプレート内で辞書型リストを展開
program = guidance('''食事状況:
{{#each people}} {{this.name}}: {{this.description}}
{{/each}}''')

# プロンプトを生成
executed_program = program(
    people=people
)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393221/56448eaf-93be-191e-1db4-2510bbf9ba04.png)


## プロンプト同士を組み合わせる


```py:
# テンプレート内で使用する変数を定義
nutrient_list = ["炭水化物", "タンパク質", "脂質"]
people = [
    {"name": "田中",  "description": "鶏むね肉と白米を食べました"},
    {"name": "木村",  "description": "ラーメンと餃子を食べました"}
]

# プロンプト①を生成
program1 = guidance('''栄養素リスト:
{{#each nutrient_list}}- {{this}}
{{/each~}}''')

# プロンプト②の中でプロンプト①を展開する
program2 = guidance('''{{>program1}}
食事状況:
{{#each people}} {{this.name}}: {{this.description}}
{{/each}}
''')

# プロンプトを生成
executed_program = program2(
    program1=program1, 
    nutrient_list=nutrient_list,
    people=people
)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393221/01470047-165f-5a0b-4e54-58f7daa5dc9c.png)


## 指示を組み込む

```py:
# テンプレート内で使用する変数を定義
nutrient_list = ["炭水化物", "タンパク質", "脂質"]
people = [
    {"name": "田中",  "description": "鶏むね肉と白米を食べました"},
    {"name": "木村",  "description": "ラーメンと餃子を食べました"}
]

# プロンプト①を生成
program1 = guidance('''栄養素リスト:
{{#each nutrient_list}}- {{this}}
{{/each~}}''')

# プロンプト②を作成
program2 = guidance('''食事状況:
{{#each people}} {{this.name}}: {{this.description}}
{{/each}}
''')

# 指示文を作成 (モデルにやってもらいたいこと)
instruct = guidance('''あなたは食事のアドバイザーです。
以下のJSONフォーマットに従い、ユーザへのアドバイスを作成してください。
アドバイスを考える際は、足りない栄養を考慮する必要があります。

$JSON_BLOB = {
    "name": "ユーザ名",
    "help": "アドバイス内容"
}

以下の栄養素リストと食事状況に基づいたアドバイスを行います。
{{>program1}}
{{>program2}}

アドバイス：{{gen 'result' n=1 temperature=0.9 max_tokens=256}}''')
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393221/5c3ce056-ff37-188c-3d60-816e80fb0d8a.png)



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393221/44390157-6fc6-3cdc-06aa-41fc4f5f4307.png)


# ③まとめ
個人的にめちゃくちゃプロンプトを書きやすいと感じました。
リスト・辞書型をプログラム上で変数を用意してテンプレート内で展開できるのが便利そうですね。
次回はもっと複雑なプロンプトを試していきます。
Githubのチュートリアルに色々載っていますので、みなさんもぜひ試してみてください！


https://github.com/microsoft/guidance/blob/main/notebooks/tutorial.ipynb