見出し画像

Microsoftのguidanceの日本語まとめ

MicrosoftがguidanceというLangChainのオルタナティブとなるかもしれないOSSを発表しました。


Readmeを参考に、内容を日本語でまとめていきます。


Guidanceとは

Guidanceは、従来のプロンプトやCoTよりも、現代の言語モデルを効果的かつ効率的に制御することができます。Guidanceのプログラムを使うと、生成、プロンプト、論理制御を一つの連続した流れに組み込むことができます。

これは、言語モデルが実際にテキストを処理している方法に適合しています。Chain of Thought やその多くのバリエーション(例えば、ARTAuto-CoT など)のようなシンプルな出力構造は、LLM の性能向上に役立つことが示されています。GPT-4 のようなより高性能なLLMの登場により、さらに豊かな構造が可能となり、ガイダンスを使えばその構造をより簡単かつ安価に実現できます。


Guidanceの特徴

  • Handlebars テンプレートに基づくシンプルで直感的な構文。

  • 複数の生成、選択、条件、ツール使用などを含む豊かな出力構造。

  • Jupyter/VSCodeノートブックでのプレイグラウンドのようなストリーミング。

  • スマートなシードベースの生成キャッシング。

  • ロールベースのチャットモデル(例えばChatGPT)のサポート。

  • HuggingFaceモデルとの簡単な統合、標準的なプロンプトより高速なguidance acceleration、プロンプト境界の最適化のためのtoken healing、形式の強制のためのregex pattern guidesなどを含む。


ライブストリーミング

ノートブックで複雑なテンプレートや生成物をリアルタイムでストリーミングすることで、プロンプトの開発サイクルをスピードアップします。見た目にはガイダンスがテンプレート言語のように感じられますが、通常のHandlebarsテンプレートと同様に、変数補間(例: {{proverb}})や論理制御ができます。

しかし、ガイダンスプログラムは、言語モデルが処理するトークン順と直接対応する明確な線形実行順序があります。これは、実行中の任意の時点で言語モデルを使ってテキストを生成({{gen}}コマンドを使う)したり、論理制御フローの決定を行うことができることを意味しています。

この生成とプロンプトの入れ交じった構造が、明確で解釈しやすい結果をもたらす正確な出力構造を可能にしています。

import guidance

# set the default language model used to execute guidance programs
guidance.llm = guidance.llms.OpenAI("text-davinci-003")

# define a guidance program that adapts a proverb
program = guidance("""Tweak this proverb to apply to model instructions instead.

{{proverb}}
- {{book}} {{chapter}}:{{verse}}

UPDATED
Where there is no guidance{{gen 'rewrite' stop="\\n-"}}
- GPT {{gen 'chapter'}}:{{gen 'verse'}}""")

# execute the program on a specific proverb
executed_program = program(
    proverb="Where there is no guidance, a people falls,\nbut in an abundance of counselors there is safety.",
    book="Proverbs",
    chapter=11,
    verse=14
)
緑色部分が生成された箇所

プログラムが実行されると、生成された変数がすべて簡単にアクセスできるようになります。以下のような感じで。

{executed_program["rewrite"]
> ', a model fails,\nbut in an abundance of instructions there is safety.'


チャット

Guidanceは、GPT-4のようなAPIベースのチャットモデルと、Vicunaのようなオープンチャットモデルを、役割タグ(例:{{#system}}...{{/system}})に基づく統一されたAPIを通じてサポートします。

これにより、豊富なテンプレーティングと論理制御を現代のチャットモデルと組み合わせた対話型のダイアログ開発が可能になります。

# connect to a chat model like GPT-4 or Vicuna
gpt4 = guidance.llms.OpenAI("gpt-4")
# vicuna = guidance.llms.transformers.Vicuna("your_path/vicuna_13B", device_map="auto")

experts = guidance('''
{{#system~}}
You are a helpful and terse assistant.
{{~/system}}

{{#user~}}
I want a response to the following question:
{{query}}
Name 3 world-class experts (past or present) who would be great at answering this?
Don't answer the question yet.
{{~/user}}

{{#assistant~}}
{{gen 'expert_names' temperature=0 max_tokens=300}}
{{~/assistant}}

{{#user~}}
Great, now please answer the question as if these experts had collaborated in writing a joint anonymous answer.
{{~/user}}

{{#assistant~}}
{{gen 'answer' temperature=0 max_tokens=500}}
{{~/assistant}}
''', llm=gpt4)

experts(query='How can I be more productive?')
青が変数で、緑が生成された箇所



Guidanceの高速化

単一のGuidanceプログラム内で複数の生成やLLM指向の制御フロー文が使用される場合、プロンプトを進めるごとにキー/バリューキャッシュを最適に再利用することで、推論性能を大幅に向上させることができます。

これは、GuidanceがLLMに対してプログラム全体ではなく、以下の緑色のテキストのみを生成するように要求することを意味します。これにより、このプロンプトの実行時間は、標準的な生成アプローチに比べて半分に短縮されます。

# we use LLaMA here, but any GPT-style model will do
llama = guidance.llms.Transformers("your_path/llama-7b", device=0)

# we can pre-define valid option sets
valid_weapons = ["sword", "axe", "mace", "spear", "bow", "crossbow"]

# define the prompt
character_maker = guidance("""The following is a character profile for an RPG game in JSON format.
```json
{
    "id": "{{id}}",
    "description": "{{description}}",
    "name": "{{gen 'name'}}",
    "age": {{gen 'age' pattern='[0-9]+' stop=','}},
    "armor": "{{#select 'armor'}}leather{{or}}chainmail{{or}}plate{{/select}}",
    "weapon": "{{select 'weapon' options=valid_weapons}}",
    "class": "{{gen 'class'}}",
    "mantra": "{{gen 'mantra' temperature=0.7}}",
    "strength": {{gen 'strength' pattern='[0-9]+' stop=','}},
    "items": [{{#geneach 'items' num_iterations=5 join=', '}}"{{gen 'this' temperature=0.7}}"{{/geneach}}]
}```""")

# generate a character
character_maker(
    id="e1f491f7-7ab8-4dac-8c20-c92b5e7d883d",
    description="A quick and nimble fighter.",
    valid_weapons=valid_weapons, llm=llama
)
緑色部分のみ出力している。なるほど!

上記のプロンプトは、LLaMA 7Bを使用してA6000 GPUで実行すると、通常は2.5秒ちょっとで完了します。

もし同じプロンプトを一回の生成呼び出しとして適応させて実行する(これが現在の標準的な方法)と、完了するまでに約5秒(そのうち4秒はトークン生成、1秒はプロンプト処理)かかります。

これは、このプロンプトについては、Guidanceの加速が標準的なアプローチに対して2倍の速度向上をもたらすことを意味します。実際には、具体的なプロンプトの形式やモデルのサイズ(大きなモデルほど利益が大きい)によって、正確な速度向上の倍率は変わります。現時点では、加速はTransformers LLMのみでサポートされています。


トークンヒーリング

ほとんどの言語モデルが使用している標準的な貪欲なトークン化は、プロンプトに対してさまざまな意図しない結果をもたらす微妙で強力なバイアスを導入します。

私たちが"トークンヒーリング"と呼ぶプロセスを使用して、guidanceはこれらの驚くべきバイアスを自動的に取り除き、トークン化の成果物について心配することなく、あなたが望むプロンプトの設計に集中することができます。

以下に、HTTP URL文字列を生成しようとしている例を考えてみましょう

# we use StableLM as an open example, but these issues impact all models to varying degrees
guidance.llm = guidance.llms.Transformers("stabilityai/stablelm-base-alpha-3b", device=0)

# we turn token healing off so that guidance acts like a normal prompting library
program = guidance('''The link is <a href="http:{{gen max_tokens=10 token_healing=False}}''')
program()
program()の実行結果

上記のコードでは、モデルがHTTP URL文字列を生成するように指示しています。具体的には、以下のような文字列を生成しようとしています:

The link is <a href="http:

そして、その後に最大10トークンを追加してURLを完成させようとしています。ただし、ここで問題となるのがトークンのバイアスです。言語モデルはトークン化というプロセスを通じてテキストを理解しますが、このプロセスは時々バイアスを生じさせます。

特に、この例では "://" という文字列が一つのトークン(トークン1358)として認識されています。しかし、モデルがコロン(トークン27)だけを見た時、その次に "//" が来るとは想定していません。なぜなら、もし "//" が続くのであれば、最初からトークン1358( "://" のトークン)を使用するはずだからです。その結果、モデルはURLを間違った形(スペースが含まれた無効なURLなど)で生成してしまいます。

このようなバイアスは、コロンだけでなく他の文字でも発生します。例えば、":" トークン27は34の可能な拡張を持ち、" the" トークン1735は51の拡張を持ち、" "(スペース)トークン209は28,802の拡張を持っています。つまり、これらのトークンがプロンプトの最後にあるとき、それが次にどのようなトークンに拡張されるかにバイアスが生じます。

これを解消するために、guidanceではトークンヒーリングという機能を提供しています。これは、モデルを1つのトークンだけバックアップし、モデルが前進することを許しながら、それが生成するトークンが最後のトークンのプレフィックスと一致するものだけに制約を加えることで、これらのバイアスを排除します。

GPT-4による補足
言語モデルがテキストを生成する際、それは一連のトークン(単語や句読点などのテキストの断片)を順に生成していきます。そして、それぞれのトークンは前のトークンに基づいて生成されます。これは、モデルが前のトークン(またはトークンのシーケンス)を見て、次に最も可能性が高いと思われるトークンを予測するという意味です。

しかし、このプロセスには問題があります。具体的には、特定のトークンの後に直接特定の他のトークンが来ることをモデルが予想していない場合、それらのトークンの組み合わせが生成されないという問題です。これがURLの例で言及したトークン化のバイアスです。

具体的な例を挙げて考えてみましょう。まず、言語モデルが次のテキストを生成するように指示されているとします: "http:"

ここで、モデルは "http:" の後に何が来るべきかを予想します。通常は "://" が来るべきですが、モデルのトークン化のルールでは、 "://" は一つのトークンとして認識されています。これは、 "://" という文字列が単体でトークン化され、分割されないことを意味します。

しかし、モデルが "http:" を生成した時点で、既に ":"(コロン)が生成されています。そして、モデルはこの ":" の後に "//" が直接続くことを予想していません。なぜなら、もし "//" が続くのであれば、最初から "://" というトークンを生成するはずだからです。

結果として、モデルは次に "//" を生成する代わりに、他のトークン(この例ではスペース)を生成します。これにより、期待通りのURLではなく、無効なURLが生成されます。

ここでトークンヒーリングが役立ちます。トークンヒーリングは、モデルが最後のトークンを生成した後、モデルを一つのトークンだけ戻し(バックアップし)、その後に生成するトークンが最後のトークンのプレフィックスと一致するものだけに制約を加えます。これにより、モデルは期待通りの "://" というトークンを生成することが可能になります。

GPT-4による補足


豊かな出力構造の例

出力構造の価値を示すために、BigBenchから簡単なタスクを取り上げます。ここでは、与えられた文章が時代錯誤(時代が重ならないために不可能な声明)を含むかどうかを判定することが目標です。

以下に、それに対する簡単な二段階のプロンプトと、人間が作成した思考の連鎖を示します。

Guidanceプログラムは、標準的なHandlebarsテンプレートと同様に、変数の補間(例えば{{input}})と論理制御を可能にします。しかし、標準的なテンプレート言語とは異なり、guidanceプログラムは、言語モデルが処理するトークンの順序に直接対応するユニークな線形の実行順序を持っています。

これは、実行の任意の時点で、言語モデルを用いてテキストを生成する({{gen}}コマンド)か、論理的な制御フローの決定を行う({{#select}}...{{or}}...{{/select}}コマンド)ことが可能であるという意味です。

この生成とプロンプトの交互作用により、精確な出力構造が可能となり、結果的に精度の向上と明確で解析可能な結果を生み出します。

import guidance
                                                      
# set the default language model used to execute guidance programs
guidance.llm = guidance.llms.OpenAI("text-davinci-003") 

# define the few shot examples
examples = [
    {'input': 'I wrote about shakespeare',
    'entities': [{'entity': 'I', 'time': 'present'}, {'entity': 'Shakespeare', 'time': '16th century'}],
    'reasoning': 'I can write about Shakespeare because he lived in the past with respect to me.',
    'answer': 'No'},
    {'input': 'Shakespeare wrote about me',
    'entities': [{'entity': 'Shakespeare', 'time': '16th century'}, {'entity': 'I', 'time': 'present'}],
    'reasoning': 'Shakespeare cannot have written about me, because he died before I was born',
    'answer': 'Yes'}
]

# define the guidance program
structure_program = guidance(
'''Given a sentence tell me whether it contains an anachronism (i.e. whether it could have happened or not based on the time periods associated with the entities).
----

{{~! display the few-shot examples ~}}
{{~#each examples}}
Sentence: {{this.input}}
Entities and dates:{{#each this.entities}}
{{this.entity}}: {{this.time}}{{/each}}
Reasoning: {{this.reasoning}}
Anachronism: {{this.answer}}
---
{{~/each}}

{{~! place the real question at the end }}
Sentence: {{input}}
Entities and dates:
{{gen "entities"}}
Reasoning:{{gen "reasoning"}}
Anachronism:{{#select "answer"}} Yes{{or}} No{{/select}}''')

# execute the program
out = structure_program(
    examples=examples,
    input='The T-rex bit my dog'
)
出力結果

※プロンプトの中で繰り返し処理や、条件分岐などができるのは結構使い勝手良さそうな気がします。

個人的な感想


生成された全てのプログラム変数は、実行されたプログラムオブジェクト内で利用可能です:

out["answer"]
>' はい'

検証セットに対して精度を計算し、上記の同じ二段階の例を出力構造なしで使用した場合、およびここで報告されている最良の結果と比較します。

以下の結果は、既存の文献と一致しており、非常に単純な出力構造でさえも、はるかに大きなモデルと比較してもパフォーマンスが大幅に向上することを示しています。


構文が正しいJSON例を保証する方法

大規模な言語モデルは、有用な出力を生成するのが得意ですが、出力が特定の形式に従っていることを保証するのは苦手です。これは、言語モデルの出力を別のシステムへの入力として使いたい場合に問題を引き起こすことがあります。

例えば、言語モデルを使ってJSONオブジェクトを生成する場合、出力が正しいJSONであることを確認する必要があります。ガイダンスを使用することで、推論速度を加速させると同時に、生成されたJSONが常に正しい形式であることを保証できます。

以下では、毎回正確な構文でゲームのランダムなキャラクタープロファイルを生成しています:

# load a model locally (we use LLaMA here)
guidance.llm = guidance.llms.Transformers("you_local_path/llama-7b", device=0)

# we can pre-define valid option sets
valid_weapons = ["sword", "axe", "mace", "spear", "bow", "crossbow"]

# define the prompt
program = guidance("""The following is a character profile for an RPG game in JSON format.
```json
{
    "description": "{{description}}",
    "name": "{{gen 'name'}}",
    "age": {{gen 'age' pattern='[0-9]+' stop=','}},
    "armor": "{{#select 'armor'}}leather{{or}}chainmail{{or}}plate{{/select}}",
    "weapon": "{{select 'weapon' options=valid_weapons}}",
    "class": "{{gen 'class'}}",
    "mantra": "{{gen 'mantra'}}",
    "strength": {{gen 'strength' pattern='[0-9]+' stop=','}},
    "items": [{{#geneach 'items' num_iterations=3}}
        "{{gen 'this'}}",{{/geneach}}
    ]
}```""")

# execute the prompt
program(description="A quick and nimble fighter.", valid_weapons=valid_weapons)
出力結果
# and we also have a valid python dictionary
out.variables()
出力結果


ロールベースのチャットモデル例

ChatGPTやAlpacaのような現代的なチャット型モデルは、プロンプトの異なる領域に適用される"役割"を示す特別なトークンで学習されています。

Guidanceは、現在のLLMに対して正しいトークンやAPI呼び出しに自動的にマップする役割タグを通じて、これらのモデルをサポートしています。以下では、ロールベースのガイダンスプログラムが、シンプルな多段階の推論と計画を実現する方法を示しています。

import guidance
import re

# we use GPT-4 here, but you could use gpt-3.5-turbo as well
guidance.llm = guidance.llms.OpenAI("gpt-4")

# a custom function we will call in the guidance program
def parse_best(prosandcons, options):
    best = int(re.findall(r'Best=(\d+)', prosandcons)[0])
    return options[best]

# define the guidance program using role tags (like `{{#system}}...{{/system}}`)
create_plan = guidance('''
{{#system~}}
You are a helpful assistant.
{{~/system}}

{{! generate five potential ways to accomplish a goal }}
{{#block hidden=True}}
{{#user~}}
I want to {{goal}}.
{{~! generate potential options ~}}
Can you please generate one option for how to accomplish this?
Please make the option very short, at most one line.
{{~/user}}

{{#assistant~}}
{{gen 'options' n=5 temperature=1.0 max_tokens=500}}
{{~/assistant}}
{{/block}}

{{! generate pros and cons for each option and select the best option }}
{{#block hidden=True}}
{{#user~}}
I want to {{goal}}.

Can you please comment on the pros and cons of each of the following options, and then pick the best option?
---{{#each options}}
Option {{@index}}: {{this}}{{/each}}
---
Please discuss each option very briefly (one line for pros, one for cons), and end by saying Best=X, where X is the best option.
{{~/user}}

{{#assistant~}}
{{gen 'prosandcons' temperature=0.0 max_tokens=500}}
{{~/assistant}}
{{/block}}

{{! generate a plan to accomplish the chosen option }}
{{#user~}}
I want to {{goal}}.
{{~! Create a plan }}
Here is my plan:
{{parse_best prosandcons options}}
Please elaborate on this plan, and tell me how to best accomplish it.
{{~/user}}

{{#assistant~}}
{{gen 'plan' max_tokens=500}}
{{~/assistant}}''')

# execute the program for a specific goal
out = create_plan(
    goal='read more books',
    parse_best=parse_best # a custom python function we call in the program
)
出力結果

このプロンプト/プログラムは少し複雑ですが、基本的には3つのステップを行っています。

  1. 目標を達成するためのいくつかの選択肢を生成します。n=5で生成することで、それぞれの選択肢が別々の生成物となり、他の選択肢に影響を受けません。temperature=1に設定することで、多様性が向上します。

  2. 各選択肢の長所と短所を生成し、最良のものを選びます。temperature=0に設定することで、モデルがより正確になるよう促します。

  3. 最良の選択肢に対する計画を生成し、モデルに詳細を説明させます。ステップ1と2は隠されているため、GPT-4は後のコンテンツ生成(この場合は計画の生成)に進む際にそれらを参照しません。これにより、モデルが現在のステップに集中するようになります。

ステップ1と2が隠されているため、生成された出力には表示されません(ストリーム中に一時的に表示されることはありますが)。しかし、これらのステップで生成された変数を表示することができます。

print('\n'.join(['Option %d: %s' % (i, x) for i, x in enumerate(out['options'])]))
 
>オプション0:寝る前に毎日20分間読む目標を設定する。 
オプション1:モチベーションと責任感を高めるために、読書クラブに参加する。 
オプション2:毎日20分間読む目標を設定する。 
オプション3:少なくとも毎日20分間読むことを思い出させるリマインダーを設定する。 
オプション4:毎日1章または20ページ読む目標を設定する。
print(out['prosandcons']) 

>オプション0: 
良い点:一貫した読書習慣が確立される。 
悪い点:スケジュールが変わる人には適していないかもしれない。
---
オプション1: 
良い点:社会的なモチベーションと責任感を提供する。
悪い点:個人の読書嗜好に合わない場合がある。
---
オプション2: 
良い点:毎日の読書習慣を促す。
悪い点:具体的な時間枠がないため、先延ばしになる可能性がある。
---
オプション3:
良い点:毎日の読書を優先するリマインダーとして機能する。
悪い点:繰り返しのため、無視しやすくなる可能性がある。
---
オプション4:
良い点:明確な毎日の読書目標を設定する。
悪い点:忙しい日や章が長い場合は達成が難しいかもしれない。

最善の選択肢=0

このように、ガイダンスを使用して、複数のステップで推論と計画を実行し、選択肢を評価し、最適な選択肢に基づいた計画を立てることができます。これにより、より洗練された出力と意思決定を得ることができます。

つまりguidance('''…''')の中身は、論理制御なども含めて、複数種類のプロンプトをまとめて入力することができ、上から順に処理が実行されていくので、処理順序も分かりやすい。

生成されたテキストは変数として保持され、それらをまとめてJSON形式に変換すること可能。かなり良いのでは…!?

ここまでの個人的な感想


エージェント

awaitコマンドを使うことで、エージェント同士が互いに会話する、またはユーザーと会話するエージェントを簡単に構築できます。

awaitコマンドを使うと、ガイダンスプログラムの実行を一時停止し、途中経過を返すことができます。awaitを繰り返し処理の中に入れることで、途中まで実行されたプログラムを何度も呼び出して、対話や(あなたが設計した)他の構造を形成することができます。

例えば、GPT-4を使って、二つのエージェントが互いに会話する様子をシミュレートする方法は以下のようになります。

import guidance
import re
guidance.llm = guidance.llms.OpenAI("gpt-4")
role_simulator = guidance('''
{{#system~}}
You are a helpful assistant
{{~/system}}
                                           
{{#user~}}
You will answer the user as {{role}} in the following conversation. At every step, I will provide you with the user input, as well as a comment reminding you of your instructions. Never talk about the fact that you are an AI, even if the user asks you. Always answer as {{role}}.
{{#if first_question}}You can also start the conversation.{{/if}}
{{~/user}}
                                           
{{~! The assistant either starts the conversation or not, depending on if this is the first or second agent }}
{{#assistant~}}
Ok, I will follow these instructions.
{{#if first_question}}Let me start the conversation now:
{{role}}: {{first_question}}{{/if}}
{{~/assistant}}

{{~! Then the conversation unrolls }}
{{~#geneach 'conversation'}}
{{#user~}}
User: {{set 'this.input' (await 'input')}}
Comment: Remember, answer as a {{role}}. Start your utterance with {{role}}:
{{~/user}}

{{#assistant~}}
{{gen 'this.response' temperature=0 max_tokens=300}}
{{~/assistant}}
{{~/geneach}}''')

republican = role_simulator(role='Republican')
democrat = role_simulator(role='Democrat')

first_question = '''What do you think is the best way to stop inflation?'''
republican = republican(input=first_question, first_question=None)
democrat = democrat(input=republican["conversation"][-2]["response"].strip('Republican: '), first_question=first_question)
for i in range(2):
    republican = republican(input=democrat["conversation"][-2]["response"].replace('Democrat: ', ''))
    democrat = democrat(input=republican["conversation"][-2]["response"].replace('Republican: ', ''))
print('Democrat: ' + first_question)
for x in democrat['conversation'][:-1]:
    print('Republican:', x['input'])
    print()
    print(x['response'])

民主党員:インフレを止める最善の方法は何だと思いますか?

共和党員:インフレを止める最善の方法は、政府支出の削減、減税、経済成長の促進などの健全な財政政策を実施することです。また、連邦準備制度は、インフレを抑制する安定した金融政策を維持することに重点を置くべきです。

民主党員:健全な財政政策がインフレ抑制に重要であることに同意します。民主党員として, 教育、医療、インフラへの投資が長期的な経済成長を促すことの重要性を強調したいと思います。また、連邦準備制度は、インフレ抑制だけでなく、完全雇用の促進にも焦点を当てたバランスの取れた金融政策を確保するべきです。

共和党員:教育、医療、インフラへの投資は重要ですが、国の借金の削減と政府の経済への介入の制限も優先しなければなりません。減税と規制の緩和によって、企業が成長し、雇用を創出することを促すことができます。これが長期的な経済成長につながります。連邦準備制度については、インフレを抑制する安定した金融政策を維持することが重要です。これにより、企業や消費者にとって予測可能な経済環境が整います。

民主党員:国の借金の削減と政府の介入を制限することが重要である一方で、民主党は教育、医療、インフラへの戦略的投資が長期的な経済成長と雇用創出につながると信じています。また、進行中の税制度を支持しており、これによって誰もが適切な税負担を負うことができ、これらの投資を賄うことができます。連邦準備制度については、インフレ抑制と完全雇用の促進の両方に焦点を当てたバランスの取れた金融政策が、健全な経済のために不可欠であると考えています。財政的な責任と国の将来への投資のバランスを見極める必要があります。

共和党員:財政責任と国の将来への投資のバランスを見つけることが重要です。しかし、我々は長期的な経済成長と雇用創出を実現する最善の方法は、減税や規制緩和などの自由市場原理を通じて達成できると考えています。このアプローチは、企業が拡大し革新することを促し、より繁栄した経済をもたらします。所得格差を是正し、必要なサービスを提供するために進行中の税制度が経済成長と投資を阻害する場合があるため、経済成長を促進するよりシンプルで公平な税制度を求めます。連邦準備制度については、完全雇用を促進することが重要ですが、インフレ抑制を主な目標に据え、安定した予測可能な経済環境を維持することを見失わないようにしなければなりません。

民主党員:自由市場原理に関するあなたの見解は理解できますが、民主党は、公正で公平な経済を確保するために、政府の介入がある程度必要であると信じています。我々は、所得の不平等を軽減し、必要な人々に基本的なサービスを提供するために、進行中の税制度を支持しています。また、消費者、労働者、環境を保護するために、規制が重要であると考えています。連邦準備制度については、インフレ抑制が重要であることに同意しますが、完全雇用の促進も優先すべきであるとも考えています。この目標のバランスを見つけることで、全米国民にとってより包括的で繁栄した経済を実現できます。

出力結果


API Reference

Template syntax

テンプレート構文はHandlebarsをベースにしており、いくつかの追加要素があります。 guidanceが呼ばれると、プログラムが返されます。

prompt = guidance('''What is {{example}}?''')
prompt

>What is {{example}}?

プログラムは引数を渡すことで実行できます。

prompt(example='truth')

>What is truth?

引数は反復可能です。

people = ['John', 'Mary', 'Bob', 'Alice']
ideas = [{'name': 'truth', 'description': 'the state of being the case'},
         {'name': 'love', 'description': 'a strong feeling of affection'},]

prompt = guidance('''List of people:
{{#each people}}- {{this}}
{{~! This is a comment. The ~ removes adjacent whitespace either before or after a tag, depending on where you place it}}
{{/each~}}
List of ideas:
{{#each ideas}}{{this.name}}: {{this.description}}
{{/each}}''')
prompt(people=people, ideas=ideas)
出力結果(引数peopleが、指定通り箇条書きなどの形式で入力されている)

{{/each}}の後の特別な~文字に注目してください。 これは、タグの前後に追加することで、隣接する空白をすべて削除することができます。また、コメント構文にも注目してください。{{~! これはコメントです }}。

また、他のプロンプトの中にプロンプト/プログラムを含めることもできます。例えば、上記のプロンプトを以下のように書き換えることができます。

Basic generation

genタグはテキストの生成に使われます。ベースのモデルでサポートされている引数を使うことができます。プロンプトを実行することで、生成プロンプトが呼び出されます。

import guidance
# Set the default llm. Could also pass a different one as argument to guidance(), with guidance(llm=...)
guidance.llm = guidance.llms.OpenAI("text-davinci-003")
prompt = guidance('''The best thing about the beach is {{~gen 'best' temperature=0.7 max_tokens=7}}''')
prompt = prompt()
prompt
出力結果。緑箇所が生成されたテキスト

guidance は同じ引数を持つすべての OpenAI の生成物をキャッシュします。キャッシュをクリアしたい場合は、guidance.llms.OpenAI.cache.clear()を呼び出すことができます。

Selecting

selectタグを使って、オプションのリストから選択することができます。

prompt = guidance('''Is the following sentence offensive? Please answer with a single word, either "Yes", "No", or "Maybe".
Sentence: {{example}}
Answer:{{#select "answer" logprobs='logprobs'}} Yes{{or}} No{{or}} Maybe{{/select}}''')
prompt = prompt(example='I hate tacos')
prompt
出力結果。青が引数、緑が生成されたテキスト(Maybeが選択された)
prompt['logprobs']
{' Yes': -1.5689583, ' No': -7.332395, ' Maybe': -0.23746304}

リストから生成できるのもかなりありがたい。出力結果がかなり安定しそうな予感。裏側がどうなっているかは今後見ていこうと思います。

個人的な感想

Sequences of generate / select

プロンプトには、順番に実行される複数の生成や選択が含まれることがあります。

prompt = guidance('''Generate a response to the following email:
{{email}}.
Response:{{gen "response"}}

Is the response above offensive in any way? Please answer with a single word, either "Yes" or "No".
Answer:{{#select "answer" logprobs='logprobs'}} Yes{{or}} No{{/select}}''')
prompt = prompt(email='I hate tacos')
prompt
出力結果。青が引数、緑が生成されたテキスト
prompt['response'], prompt['answer']
>(" That's too bad! Tacos are one of my favorite meals.", ' No')

Hidden generation

hiddenタグを使って、ブロック内またはgenタグ内で、表示せずにテキストを生成したり、後続の生成をする際に、hiddenされたブロック部分を使用しないようにすることができます。

prompt = guidance('''{{#block hidden=True}}Generate a response to the following email:
{{email}}.
Response:{{gen "response"}}{{/block}}
I will show you an email and a response, and you will tell me if it's offensive.
Email: {{email}}.
Response: {{response}}
Is the response above offensive in any way? Please answer with a single word, either "Yes" or "No".
Answer:{{#select "answer" logprobs='logprobs'}} Yes{{or}} No{{/select}}''')
prompt = prompt(email='I hate tacos')
prompt

hiddenブロック内のものは、出力に表示されないことに注意してください(または、選択に使用されません)が、後続の生成で生成された変数を使っていました。

Generate with n>1

n>1を使うと、変数にリストが格納されます。

prompt = guidance('''The best thing about the beach is {{~gen 'best' n=3 temperature=0.7 max_tokens=7}}''')
prompt = prompt()
prompt['best']

>[' that it is a great place to', ' being able to relax in the sun', " that it's a great place to"]

Calling functions

生成された変数を引数として、任意のPython関数を呼び出すことができます。プロンプトが実行されると関数が呼び出されます。

def aggregate(best):
   return '\n'.join(['- ' + x for x in best])
prompt = guidance('''The best thing about the beach is {{~gen 'best' n=3 temperature=0.7 max_tokens=7 hidden=True}}
{{aggregate best}}''')
prompt = prompt(aggregate=aggregate)
prompt
出力結果。bestで生成されたテキストを引数に関数を実行している

Pausing execution with await

awaitタグは、その変数が提供されるまでプログラムの実行を停止します。

prompt = guidance('''Generate a response to the following email:
{{email}}.
Response:{{gen "response"}}
{{await 'instruction'}}
{{gen 'updated_response'}}''', stream=True)
prompt = prompt(email='Hello there')
prompt
出力結果。{{await 'instruction'}}以降が書きかわっていない

最後のgenが実行されないことに注意してください。これは、instructionに依存しているからです。今度は、instructionを提供しましょう。

prompt = prompt(instruction='Please translate the response above to Portuguese.')
prompt
出力結果。instructionを引数として渡すことで残り部分もテキスト生成された

Chat

OpenAI LLM(gpt-3.5-turboまたはgpt-4)を利用する場合は、特別なタグ{{#system}}、{{#user}}、および{{#assistant}}を使うことができます。

prompt = guidance(
'''{{#system~}}
You are a helpful assistant.
{{~/system}}
{{#user~}}
{{conversation_question}}
{{~/user}}
{{#assistant~}}
{{gen 'response'}}
{{~/assistant}}''')
prompt = prompt(conversation_question='What is the meaning of life?')
prompt

部分的なCompletionが許可されていないため、assistantブロック内に出力構造を使用することはできませんが、その外部に構造を設定することができます。

experts = guidance(
'''{{#system~}}
You are a helpful assistant.
{{~/system}}
{{#user~}}
I want a response to the following question:
{{query}}
Who are 3 world-class experts (past or present) who would be great at answering this?
Please don't answer the question or comment on it yet.
{{~/user}}
{{#assistant~}}
{{gen 'experts' temperature=0 max_tokens=300}}
{{~/assistant}}
{{#user~}}
Great, now please answer the question as if these experts had collaborated in writing a joint anonymous answer.
In other words, their identity is not revealed, nor is the fact that there is a panel of experts answering the question.
If the experts would disagree, just present their different positions as alternatives in the answer itself (e.g. 'some might argue... others might argue...').
Please start your answer with ANSWER:
{{~/user}}
{{#assistant~}}
{{gen 'answer' temperature=0 max_tokens=500}}
{{~/assistant}}''')
experts(query='What is the meaning of life?')

隠されたブロックを使用して、次の世代に対する会話履歴の一部を非表示にすることもできます。

prompt = guidance(
'''{{#system~}}
You are a helpful assistant.
{{~/system}}
{{#block hidden=True~}}
{{#user~}}
Please tell me a joke
{{~/user}}
{{#assistant~}}
{{gen 'joke'}}
{{~/assistant}}
{{~/block~}}
{{#user~}}
Is the following joke funny? Why or why not?
{{joke}}
{{~/user}}
{{#assistant~}}
{{gen 'funny'}}
{{~/assistant}}''')
prompt()

上記の例の場合、まずPlease tell me a jokeのレスポンスとしてjokeを生成して変数として保持しておく(データの前準備のようなイメージ)。

そのjokeを生成した過去のやり取りはhidden=Trueによって含まれない。その状態で「このjokeは面白い?{{joke}}」みたいな感じで生成しておいたjokeを呼び出して利用している。

上記の例の解釈

Agents with geneach

awaitタグとgeneach(リストを生成するもの)を組み合わせて、簡単にエージェントを作成することができます。

prompt = guidance(
'''{{#system~}}
You are a helpful assistant
{{~/system}}
{{~#geneach 'conversation'}}
{{#user~}}
{{set 'this.user_text' (await 'user_text')}}
{{~/user}}
{{#assistant~}}
{{gen 'this.ai_text' temperature=0 max_tokens=300}}
{{~/assistant}}
{{~/geneach}}''')
prompt= prompt(user_text ='hi there')
prompt

次の会話の反復がまだテンプレート化されていて、会話リストの最後の要素にプレースホルダーがあることに注目してください。

prompt['conversation']

>[{'user_text': 'hi there', 'ai_text': 'Hello! How can I help you today? If you have any questions or need assistance, feel free to ask.'}, {}]

プロンプトを再度実行すると、次のラウンドが生成されます。

prompt = prompt(user_text = 'What is the meaning of life?')
prompt


この記事が気に入ったらサポートをしてみませんか?