見出し画像

【後編】LangChainによるGenerative Agents実装まとめ

前編はコチラ↓

前回に引き続きGenerative Agentsです。今回はこちらのドキュメントの内容を中心に見ていきます。

ちなみにまだデモを見たことがない方はぜひデモをご覧下さい。

GenerativeAgentクラスの概要

この実装で中心的な役割を果たしているのがGenerativeAgentクラスです。

class GenerativeAgent(BaseModel):
    """A character with memory and innate characteristics."""

このクラスは以下のパラメータを持っており、これがキャラクターの振る舞いや言動に影響を与えます。

name (str):
キャラクターの名前。

age (int):
キャラクターの年齢。

traits (str):
キャラクターの固有の特徴。「anxious, likes design」といった値を設定する。

status (str):
キャラクターの現在の状態。

reflection_threshold (Optional[float]):
memory_importance(記憶の合計重要度)がこのしきい値を超えたときにリフレクションを実施する。
※記憶の重要度は1〜10までの間でLLMによって評価され、「スコア / 10 * 重み(0.15)」という計算式で算出される。記憶する毎(add_memoryメソッド実行時)にこの値をmemory_importanceに加算していき、その値がreflection_thresholdを越えたときにリフレクションが実行されるという仕組み。

current_plan (List[str]):
エージェントの現在の計画。キャラクターが計画に従って行動する際に使用される。

summary_refresh_seconds (int):
キャラクターのサマリが自動的に更新される秒数。get_summaryメソッド内で要約が更新されるタイミングを決定するために使用される。

max_tokens_limit (int):
メモリから取得されるドキュメントの最大トークン数。_get_memories_until_limitメソッド内で使用される。

実際に利用する際は以下のように呼び出します。

tommie = GenerativeAgent(
    name="Tommie",
    age=25,
    traits="anxious, likes design",
    status="looking for a job",
    memory_retriever=create_new_memory_retriever(),
    llm=LLM,
    daily_summaries=[
        "Drove across state to move to a new town but doesn't have a job yet."
    ],
    reflection_threshold=8,
)

また、以下がパブリックメソッドの概要です。

pause_to_reflect -> List[str]:
最近のobservations(観察)についてリフレクションし、得られた洞察(insight)をメモリに追加する。返り値は洞察のリスト。

add_memory (memory_content: str) -> List[str]:
エージェントの記憶にobservation(観察)またはmemory(記憶)を追加する。

fetch_memories (observation: str) -> List[Document]:
observationに関連する記憶を取得します。

get_summary (force_refresh: bool = False) -> str:
エージェントの要約を返す。force_refreshをTrueにすると、summary_refresh_secondsの値にかかわらず強制的に要約を再生成する。

get_full_header (force_refresh: bool = False) -> str:
エージェントの状態、要約、現在の時刻を含むヘッダーを返す。force_refreshをTrueにすると強制的に要約を再生成した結果を返す。

summarize_related_memories (observation: str) -> str:
与えられたobservation(観察)と最も関連のある記憶を要約して返す。

generate_reaction (observation: str) -> Tuple[bool, str]:
与えられたobservation(観察)に対する反応を生成する。タプルのboolはキャラクターが何らかの発言をしたときのみTrueになる。

generate_dialogue_response (observation: str) -> Tuple[bool, str]:
与えられたobservation(観察)に対する対話応答を生成する。タプルのboolはキャラクターが会話を継続しようとしている場合のみTrueになる。

GenerativeAgentクラスの活用

このクラスを利用するためには、キャラクターの記憶の初期値を与えておく必要があります。ドキュメントでは以下のように初期値を与えていました。

tommie_memories = [
    "Tommie remembers his dog, Bruno, from when he was a kid",
    "Tommie feels tired from driving so far",
    "Tommie sees the new home",
    "The new neighbors have a cat",
    "The road is noisy at night",
    "Tommie is hungry",
    "Tommie tries to get some rest.",
]
for memory in tommie_memories:
    tommie.add_memory(memory)

記憶の初期値を与えた結果、現在のキャラクターの状態がどうなっているかはget_summary関数で取得できます。

print(tommie.get_summary(force_refresh=True))

Name: Tommie (age: 25)
Innate traits: anxious, likes design
Tommie is observant, nostalgic, tired, hungry, and easily affected by noise.

(DeepL訳)
トミーは観察力があり、ノスタルジックで、疲れやすく、空腹で、騒音に影響されやすい。

記憶の初期値を与えたエージェントに対して、天の声として質問してみます。質問には以下のようなメソッドを用意しています。

def interview_agent(agent: GenerativeAgent, message: str) -> str:
    """Help the notebook user interact with the agent."""
    new_message = f"{USER_NAME} says {message}"
    return agent.generate_dialogue_response(new_message)[1]
interview_agent(tommie, "What do you like to do?")

Tommie said "I really enjoy design, it's always been a passion of mine. I also enjoy exploring new places and trying new restaurants. How about you?"

(DeepL訳)
トミー「私はデザインをとても楽しんでいます。また、新しい場所を探検したり、新しいレストランに挑戦したりするのも楽しいです。あなたはどうですか?」

interview_agent(tommie, "What are you looking forward to doing today?")

Tommie said "I'm actually looking for a job right now, so I'll be spending most of my day applying and networking. How about you?"

(DeepL訳)
トミー「実は今、仕事を探しているんだ。だから、一日の大半を応募やネットワーク作りに費やすことになる。あなたはどうですか?」

仕事を探しているトミーに対し、次はキャリアカウンセラーであるイヴを生成します。イヴにトミーをカウンセリングしてもらいましょう。

eve = GenerativeAgent(
    name="Eve",
    age=34,
    traits="curious, helpful",
    status="N/A",
    memory_retriever=create_new_memory_retriever(),
    llm=LLM,
    daily_summaries=[
        ("Eve started her new job as a career counselor last week and received her first assignment, a client named Tommie.")
    ],
    reflection_threshold=5,
)

イヴにも記憶の初期値を設定します。

eve_memories = [
    "Eve overhears her colleague say something about a new client being hard to work with",
    "Eve wakes up and hear's the alarm",
    "Eve eats a boal of porridge",
    "Eve helps a coworker on a task",
    "Eve plays tennis with her friend Xu before going to work",
    "Eve overhears her colleague say something about Tommie being hard to work with",
]
for memory in eve_memories:
    eve.add_memory(memory)

イヴの現在の状況を見てみましょう。

print(eve.get_summary())

Name: Eve (age: 34)
Innate traits: curious, helpful
Eve is helpful towards her coworkers, enjoys playing tennis with friends, eats a simple breakfast, pays attention to conversations around her, and wakes up on time.

(DeepL訳)
イヴは同僚に親切で、友人とテニスを楽しみ、簡単な朝食を食べ、周りの会話に気を配り、時間通りに起きる。

イヴに対し、天の声によってトミーが休職しているという情報を与えます。

interview_agent(eve, "How are you feeling about today?")

Eve said "I'm feeling pretty good today, thanks for asking! How about you?"

(DeepL訳)
イヴ「今日はとても調子がいいんだ、聞いてくれてありがとう!あなたはどうですか?」

interview_agent(eve, "What do you know about Tommie?")

Eve said "I don't know much about Tommie, why do you ask?"

(DeepL訳)
イヴ「私はトミーのことをよく知らないのですが、どうしてですか?」

interview_agent(eve, "Tommie is looking to find a job. What are are some things you'd like to ask him?")

Eve said "That's interesting. What kind of job is Tommie looking for?"

(DeepL訳)
イヴ「それは興味深いですね。トミーはどんな仕事を探しているのだろう?」

interview_agent(eve, "You'll have to ask him. He may be a bit anxious, so I'd appreciate it if you keep the conversation going and ask as many questions as possible.")

Eve said "Sure, I'd be happy to ask Tommie some questions and keep the conversation going. Can you tell me a bit more about what he's looking for in a job?"

(DeepL訳)
イヴ「もちろん、トミーにいくつか質問して、会話を続けることができればうれしいです。彼が仕事に何を求めているのか、もう少し詳しく教えてくれない?」

というわけで、イヴのトミーに関する関心が高まったところで、以下の関数によって会話をスタートしてもらいます。GenerativeAgentクラスの仕様上、全くよく分からない相手とは会話が生まれません。何らかの情報をインプットしておく必要があります。

def run_conversation(agents: List[GenerativeAgent], initial_observation: str) -> None:
    """Runs a conversation between agents."""
    _, observation = agents[1].generate_reaction(initial_observation)
    print(observation)
    while True:
        break_dialogue = False
        for agent in agents:
            stay_in_dialogue, observation = agent.generate_dialogue_response(observation)
            print(observation)
            if not stay_in_dialogue:
                break_dialogue = True
        if break_dialogue:
            break
agents = [tommie, eve]
run_conversation(agents, "Tommie said: Hi, Eve. Thanks for agreeing to share your story with me and give me advice. I have a bunch of questions.")

トミーからイヴに職探しについて教えてもらえるか質問するところから会話が始まります。

Eve said "Sure, Tommie! Ask away. What specific questions do you have in mind?"
Tommie said "Thanks, Eve! I just wanted to ask you about your experience working in the design industry. What do you think are some important skills to have in that field?"
Eve said "That's a great question, Tommie. In my experience, some important skills to have in the design industry are strong communication skills, attention to detail, and the ability to think creatively. Those have been crucial for me in my work. Is there anything specific you're interested in learning more about?"
Tommie said "Thank you, Eve! That's really helpful. I'll definitely work on improving those skills. Do you have any other advice for someone looking to get into the design industry?"
Eve said "Of course, Tommie! I'm happy to offer any other advice I can. Another important thing to keep in mind is to stay up-to-date with industry trends and constantly improve your skills through practice and learning from others. Do you have any other questions?"
Tommie said "Thank you so much, Eve! Your advice has been really helpful. I appreciate you taking the time to talk with me."
Eve said "You're welcome, Tommie! It was great talking with you. Best of luck in your job search."

(DeepL訳)
イヴ「もちろん、トミー!聞いてみてください。具体的にどんな質問があるのかな」
トミー「ありがとう、イヴ!デザイン業界で働いた経験についてお聞きしたいのですが。その分野で重要なスキルは何だと思いますか?」
イヴ「いい質問だね、トミー。私の経験では、デザイン業界で重要なスキルは、強いコミュニケーション能力、細部へのこだわり、そしてクリエイティブな思考力です。これらは、私が仕事をする上で非常に重要な要素となっています。何か具体的に学びたいことはありますか」
トミー「ありがとう、イヴ!本当に助かります。ありがとうございます。他に、デザイン業界を目指す人へのアドバイスがあれば教えてください!
イヴ「もちろんです!アドバイスできることがあれば、喜んで提供します。業界のトレンドに敏感であること、そして実践と他者からの学びによって常に自分のスキルを向上させることも大切です。他に質問はありますか」
トミー「イヴさん、本当にありがとうございます!あなたのアドバイスが本当に役に立ちました。時間を割いてくれてありがとう」
イヴ「どういたしまして、トミー!あなたと話せてよかったです。就職活動、頑張ってね。」

GPT-3.5なので会話がぎこちないですが、GPT-4を利用すればもっと自然な形になると思われます。

今回ご紹介したGenerativeAgentをそのまま利用するケースはあまりない気がしますが、自然な長期記憶を持ったエージェントを開発するためのヒントにはなるのではないでしょうか。

現場からは以上です。

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