初めに
LLM・LLM活用アドカレ 18日目です!
genagentsは、生成的エージェントを作成・操作するためのPythonライブラリです。こちらを使って仮想環境でのNPCの一定時間内でのシミュレーションをおこなっていきます
以下で動く環境を 作成していますので、是非触ってみてください。以下の内容はforkしたリポジトリを前提に進めていきます
github.com
開発環境
セットアップ
uv venv -p 3.11
.venv\Scripts\activate
uv pip install -r requirements.txt
OpenAIのAPIを使うため、以下の .env
ファイルを ルートに作成します
OPENAI_API_KEY = "api key"
KEY_OWNER = "name"
一人のエージェントにユーザー質問をする
まずはシンプルにコードを動かしていきます。エージェントの設定を行って質問に回答してもらいます
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from genagents.genagents import GenerativeAgent
agent = GenerativeAgent()
agent.update_scratch({
"first_name": "太郎",
"last_name": "山田",
"age": 30,
"occupation": "ソフトウェアエンジニア",
"interests": ["読書", "ハイキング", "プログラミング"]
})
questions = {
"あなたはアウトドア活動が好きですか?": ["はい", "いいえ", "時々"]
}
response = agent.categorical_resp(questions)
print(response["responses"])
questions = {
"プログラミングはどのくらい好きですか?(1から10のスケール)": [1, 10]
}
response = agent.numerical_resp(questions, float_resp=False)
print(response["responses"])
dialogue = [
("インタビュアー", "あなたの好きな趣味について教えてください。"),
]
response = agent.utterance(dialogue)
print(response)
実行した結果は以下のようになります
['はい']
[9]
私の好きな趣味は読書です。特に小説を読むのが好きで、いろいろなジャンルに挑戦しています。また、ハイキングも好きで、自然の中で過ごすのは心が落ち着きます。プログラミングは仕事の一部ですが、趣味としても
新しい技術を学ぶのが楽しいです。
記憶システムを追加
以下のメソッドを使ってメモリ機能を追加します
- agent.remember(text, time_step) : エージェントの記憶ストリームに新しい記憶を追加
- agent.reflect(anchor, time_step) : エージェントが自身の記憶を振り返り、新たな洞察や結論を得るためのプロセスを実行
- agent.save(path) : エージェントの現在の状態(記憶ストリーム、個人情報、内省の結果など)を保存
- GenerativeAgent(agent_folder="saved_agents/agent1") : 保存したエージェントを再度使用
これらの機能を追加して、エージェントに対して質問を行ってみます。
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from genagents.genagents import GenerativeAgent
agent = GenerativeAgent()
agent.update_scratch({
"first_name": "太郎",
"last_name": "山田",
"age": 30,
"occupation": "ソフトウェアエンジニア",
"interests": ["読書", "ハイキング", "プログラミング"]
})
agent.remember("昨日、友人と一緒に山でハイキングを楽しんだ。", time_step=1)
agent.remember("新しいプログラミング言語を学び始めた。", time_step=2)
agent.reflect(anchor="アウトドア活動", time_step=3)
agent.reflect(anchor="プログラミング", time_step=4)
categorical_questions = {
"あなたはアウトドア活動が好きですか?": ["はい", "いいえ", "時々"]
}
categorical_response = agent.categorical_resp(categorical_questions)
print("カテゴリカルな質問の回答:", categorical_response["responses"])
numerical_questions = {
"プログラミングはどのくらい好きですか?(1から10のスケール)": [1, 10]
}
numerical_response = agent.numerical_resp(numerical_questions, float_resp=False)
print("数値的な質問の回答:", numerical_response["responses"])
dialogue = [
("インタビュアー", "あなたの好きな趣味について教えてください。"),
]
open_ended_response = agent.utterance(dialogue)
print("オープンエンドの質問の回答:", open_ended_response)
save_directory = "saved_agents/agent_taro"
if not os.path.exists(save_directory):
os.makedirs(save_directory)
agent.save(save_directory)
print(f"エージェントを '{save_directory}' に保存しました。")
loaded_agent = GenerativeAgent(agent_folder=save_directory)
print("保存したエージェントを読み込みました。")
dialogue = [
("インタビュアー", "最近学んだことについて教えてください。"),
]
loaded_response = loaded_agent.utterance(dialogue)
print("読み込んだエージェントからの回答:", loaded_response)
実行結果は以下のようになります
カテゴリカルな質問の回答: ['はい']
数値的な質問の回答: [8]
オープンエンドの質問の回答: 私は読書とハイキングが大好きです。特に、自然の中で過ごす時間が心をリフレッシュさせてくれます。最近、友人と一緒に山でハイキングを楽しんだのですが、その経験を通じて友情も深
まったと感じています。また、プログラミングも趣味の一つで、新しいスキルを学ぶのが楽しいです。
エージェントを 'saved_agents/agent_taro' に保存しました。
保存したエージェントを読み込みました。
読み込んだエージェントからの回答: 最近、新しいプログラミング言語を学び始めました。具体的には、Pythonに挑戦しています。最初は少し難しく感じましたが、実際にコードを書いてみると、だんだん楽しくなってき
ました。
複数人のエージェントに対して質問をする
次に複数人に対して質問を投げます。基本的にエージェントの設定を増やして各エージェントに対して質問を投げています
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from genagents.genagents import GenerativeAgent
agent_profiles = [
{
"first_name": "太郎",
"last_name": "山田",
"age": 30,
"occupation": "ソフトウェアエンジニア",
"interests": ["読書", "ハイキング", "プログラミング"]
},
{
"first_name": "花子",
"last_name": "佐藤",
"age": 28,
"occupation": "デザイナー",
"interests": ["イラスト", "カフェ巡り", "映画鑑賞"]
},
{
"first_name": "健一",
"last_name": "高橋",
"age": 35,
"occupation": "教師",
"interests": ["歴史", "旅行", "料理"]
},
{
"first_name": "美咲",
"last_name": "鈴木",
"age": 26,
"occupation": "看護師",
"interests": ["音楽", "ランニング", "写真"]
},
{
"first_name": "一郎",
"last_name": "田中",
"age": 40,
"occupation": "営業",
"interests": ["ゴルフ", "ワイン", "ビジネス書"]
},
{
"first_name": "由美",
"last_name": "伊藤",
"age": 32,
"occupation": "エディター",
"interests": ["読書", "ヨガ", "アート"]
},
{
"first_name": "直樹",
"last_name": "渡辺",
"age": 29,
"occupation": "エンジニア",
"interests": ["ゲーム", "アニメ", "プログラミング"]
},
{
"first_name": "理恵",
"last_name": "中村",
"age": 31,
"occupation": "マーケティング",
"interests": ["ファッション", "SNS", "料理"]
},
{
"first_name": "健二",
"last_name": "小林",
"age": 27,
"occupation": "カメラマン",
"interests": ["写真", "登山", "ドキュメンタリー"]
},
{
"first_name": "美香",
"last_name": "加藤",
"age": 33,
"occupation": "弁護士",
"interests": ["映画", "ジョギング", "旅行"]
}
]
agents = []
for profile in agent_profiles:
agent = GenerativeAgent()
agent.update_scratch(profile)
agents.append(agent)
for agent in agents:
memory_text = f"{agent.scratch['first_name']}は最近、{agent.scratch['interests'][0]}に関する新しい経験をした。"
agent.remember(memory_text, time_step=1)
agent.reflect(anchor=agent.scratch['interests'][0], time_step=2)
categorical_questions = {
"あなたはアウトドア活動が好きですか?": ["はい", "いいえ", "時々"]
}
numerical_questions = {
"あなたの仕事への満足度はどのくらいですか?(1から10のスケール)": [1, 10]
}
dialogue = [
("インタビュアー", "最近の出来事について教えてください。"),
]
for idx, agent in enumerate(agents):
print(f"\n===== エージェント {idx + 1}: {agent.scratch['first_name']} {agent.scratch['last_name']} =====")
categorical_response = agent.categorical_resp(categorical_questions)
print("カテゴリカルな質問の回答:", categorical_response["responses"])
numerical_response = agent.numerical_resp(numerical_questions, float_resp=False)
print("数値的な質問の回答:", numerical_response["responses"])
open_ended_response = agent.utterance(dialogue)
print("オープンエンドの質問の回答:", open_ended_response)
実行結果は以下になります
===== エージェント 1: 太郎 山田 =====
カテゴリカルな質問の回答: ['はい']
数値的な質問の回答: [8]
オープンエンドの質問の回答: 最近、新しい本を読んでみたんです。特に心理学に関する本で、他の人の考えや感情を理解する手助けになりました。それによって、周りの人との関係がより深まったと感じています。
===== エージェント 2: 花子 佐藤 =====
カテゴリカルな質問の回答: ['いいえ']
数値的な質問の回答: [8]
オープンエンドの質問の回答: 最近、イラストを描くことに挑戦してみたんです。自分の表現力が広がったと感じていて、特に新しい技法を使うことで、作品に深みが出てきました。カフェ巡りをしながら、インスピレー
ションを得ることも多くて、本当に楽しいです。
===== エージェント 3: 健一 高橋 =====
カテゴリカルな質問の回答: ['時々']
数値的な質問の回答: [8]
オープンエンドの質問の回答: 最近、地元の歴史博物館を訪れたんです。そこで、私の故郷の文化や歴史についての特別展があって、とても感動しました。自分のルーツを知ることができ、今まで以上に歴史が身近に感じ
られるようになりました。
===== エージェント 4: 美咲 鈴木 =====
カテゴリカルな質問の回答: ['はい']
数値的な質問の回答: [8]
オープンエンドの質問の回答: 最近、友達と一緒に音楽フェスに行ったんです。そこでたくさんのアーティストの演奏を聴いて、音楽が持つ力を改めて感じました。人々が同じメロディに合わせて踊ったり歌ったりする姿
を見て、音楽がどれだけ人を結びつけるかを実感しました。
===== エージェント 5: 一郎 田中 =====
カテゴリカルな質問の回答: ['時々']
数値的な質問の回答: [8]
オープンエンドの質問の回答: 最近、友人たちと一緒にゴルフをプレイする機会がありました。自分のスキルを見直す良いチャンスになったんです。特に、ショットの精度やコースマネジメントについて考える時間が増え
ました。それに、ゴルフを通じて友人たちとの絆も深まりました。
===== エージェント 6: 由美 伊藤 =====
カテゴリカルな質問の回答: ['時々']
数値的な質問の回答: [8]
オープンエンドの質問の回答: 最近、素晴らしい本に出会いました。それは私にとって新たな視点を提供してくれるもので、内容に深く感動しました。読書を通じて、自分の考えや感情を整理できることがとても大切だと
感じています。あなたは最近、どんな本を読みましたか?
===== エージェント 7: 直樹 渡辺 =====
カテゴリカルな質問の回答: ['いいえ']
数値的な質問の回答: [8]
オープンエンドの質問の回答: 最近、友人たちと一緒に新しいゲームを始めました。最初はちょっと難しかったけれど、みんなで協力しながらプレイすることで、色々な戦略やプレイスタイルを学ぶことができて、とても
楽しかったです。それに、ゲームを通じて友達との絆も深まったと感じています。
===== エージェント 8: 理恵 中村 =====
カテゴリカルな質問の回答: ['時々']
数値的な質問の回答: [7]
オープンエンドの質問の回答: 最近、ファッションに関する新しい経験をしました。新しいトレンドに触れることで、自分のスタイルを見つめ直す良い機会になったと思います。特に、個性を表現する方法が広がったと感
じています。
===== エージェント 9: 健二 小林 =====
カテゴリカルな質問の回答: ['はい']
数値的な質問の回答: [8]
オープンエンドの質問の回答: 最近、写真に関する新しい経験をしたんです。特に、日常の中で見過ごしがちな美しさに気づくことができて、自分の視点や感性を再発見できた気がします。そのおかげで、もっと積極的に
シャッターを切りたくなりました。
===== エージェント 10: 美香 加藤 =====
カテゴリカルな質問の回答: ['時々']
数値的な質問の回答: [8]
オープンエンドの質問の回答: 最近、映画に関する新しい経験をしました。それが私の映画の楽しみ方を広げてくれたんです。具体的には、映画の制作過程や監督の意図を理解する機会があったんです。それによって、映
画が私に与える影響や感情について深く考えるようになりました。
数年単位の複数エージェントに対しての文化シミュレーション
架空の村に住むエージェントたちの生活を100年間にわたってシミュレートするものです。エージェントは個々のプロフィール(名前、年齢、職業、興味など)を持ち、毎年発生するイベントや相互作用を通じて変化していきます。
import os
import sys
import random
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from genagents.genagents import GenerativeAgent
agent_profiles = [
{
"first_name": "たかし",
"last_name": "すずき",
"age": 25,
"occupation": "農民",
"interests": ["農業", "漁業", "物語"]
},
{
"first_name": "あいこ",
"last_name": "たなか",
"age": 23,
"occupation": "織物師",
"interests": ["織物", "薬草", "音楽"]
},
{
"first_name": "ひろし",
"last_name": "やまだ",
"age": random.randint(20, 40),
"occupation": "狩人",
"interests": ["狩猟", "追跡", "登山"]
},
{
"first_name": "ゆみ",
"last_name": "さとう",
"age": random.randint(20, 40),
"occupation": "陶芸家",
"interests": ["陶芸", "芸術", "デザイン"]
},
{
"first_name": "けんた",
"last_name": "こばやし",
"age": random.randint(20, 40),
"occupation": "漁師",
"interests": ["漁業", "船作り", "水泳"]
},
{
"first_name": "さくら",
"last_name": "わたなべ",
"age": random.randint(20, 40),
"occupation": "大工",
"interests": ["木工", "建築", "絵画"]
},
{
"first_name": "だいち",
"last_name": "いとう",
"age": random.randint(20, 40),
"occupation": "薬草師",
"interests": ["薬草学", "園芸", "料理"]
},
{
"first_name": "ゆうこ",
"last_name": "なかむら",
"age": random.randint(20, 40),
"occupation": "語り部",
"interests": ["物語", "音楽", "踊り"]
},
{
"first_name": "さとし",
"last_name": "かとう",
"age": random.randint(20, 40),
"occupation": "交易商",
"interests": ["交易", "探索", "交渉"]
},
{
"first_name": "めぐみ",
"last_name": "よしだ",
"age": random.randint(20, 40),
"occupation": "庭師",
"interests": ["農業", "植栽", "自然"]
}
]
names = [
("ひろし", "やまだ"),
("ゆみ", "さとう"),
("けんた", "こばやし"),
("さくら", "わたなべ"),
("だいち", "いとう"),
("ゆうこ", "なかむら"),
("さとし", "かとう"),
("めぐみ", "よしだ")
]
occupations = ["狩人", "陶芸家", "漁師", "大工", "薬草師", "語り部", "交易商", "庭師"]
interests = [
["狩猟", "追跡", "登山"],
["陶芸", "芸術", "デザイン"],
["漁業", "船作り", "水泳"],
["木工", "建築", "絵画"],
["薬草学", "園芸", "料理"],
["物語", "音楽", "踊り"],
["交易", "探索", "交渉"],
["農業", "植栽", "自然"]
]
agents = []
for profile in agent_profiles:
agent = GenerativeAgent()
agent.update_scratch(profile)
agents.append(agent)
total_years = 10
current_year = 3000
time_step = 0
for year in range(total_years):
current_year += 1
time_step += 1
print(f"\n===== 年 {current_year} =====")
community_events = [
"豊作に恵まれ、食料が潤沢になった。",
"厳しい冬が訪れ、集落を試練が襲った。",
"近隣の部族が交易に訪れた。",
"大雨による洪水が田畑を襲った。",
"新年を祝う祭りが開催された。",
"地震が地域を揺るがした。"
]
event = random.choice(community_events)
print(f"集落イベント: {event}")
for agent in agents:
agent.remember(f"{current_year}年に、{event}", time_step)
for agent in agents[:]:
agent_age = agent.scratch.get('age', 25) + 1
agent.scratch['age'] = agent_age
if agent_age > 60 and random.random() < 0.1:
print(f"{agent.scratch['first_name']} {agent.scratch['last_name']} が {agent_age} 歳で亡くなりました。")
agents.remove(agent)
continue
if 18 <= agent_age <= 45 and random.random() < 0.3:
child_first_name = random.choice(["はると", "ゆい", "かいと", "はな", "そら", "まお", "れん", "みこ"])
child_last_name = agent.scratch['last_name']
child_profile = {
"first_name": child_first_name,
"last_name": child_last_name,
"age": 0,
"occupation": "子供",
"interests": ["遊び", "学び"]
}
child_agent = GenerativeAgent()
child_agent.update_scratch(child_profile)
agents.append(child_agent)
agent.remember(f"{child_first_name}という子供が生まれた。", time_step)
print(f"{agent.scratch['first_name']} に {child_first_name} という子供が生まれました。")
agent.reflect(anchor="年次イベント", time_step=time_step)
for i in range(len(agents)):
for j in range(i + 1, len(agents)):
agent_a = agents[i]
agent_b = agents[j]
if random.random() < 0.2:
dialogue = [
(agent_a.scratch['first_name'], "最近どうですか?"),
(agent_b.scratch['first_name'], "元気です。最近の出来事は興味深いですね。"),
]
agent_a.utterance(dialogue)
agent_a.remember(f"{agent_b.scratch['first_name']} と会話した。", time_step)
agent_b.remember(f"{agent_a.scratch['first_name']} と会話した。", time_step)
print("\n===== シミュレーション完了 =====")
print(f"シミュレーション終了時のエージェント数: {len(agents)}")
for agent in agents:
print(f"{agent.scratch['first_name']} {agent.scratch['last_name']}, 年齢: {agent.scratch['age']}, 職業: {agent.scratch['occupation']}")
recent_memories = agent.memory_stream[-3:]
print("最近の記憶:")
for memory in recent_memories:
print(f"- {memory}")
print()
実行結果は以下のようになります
===== 年 3001 =====
集落イベント: 地震が地域を揺るがした。
ひろし に ゆい という子供が生まれました。
けんた に まお という子供が生まれました。
さくら に れん という子供が生まれました。
===== 年 3002 =====
集落イベント: 新年を祝う祭りが開催された。
ゆみ に はな という子供が生まれました。
さくら に はると という子供が生まれました。
===== 年 3003 =====
集落イベント: 厳しい冬が訪れ、集落を試練が襲った。
===== 年 3004 =====
集落イベント: 地震が地域を揺るがした。
たかし に ゆい という子供が生まれました。
ゆみ に みこ という子供が生まれました。
===== 年 3005 =====
集落イベント: 豊作に恵まれ、食料が潤沢になった。
あいこ に みこ という子供が生まれました。
さくら に みこ という子供が生まれました。
ゆうこ に れん という子供が生まれました。
さとし に ゆい という子供が生まれました。
===== 年 3006 =====
集落イベント: 地震が地域を揺るがした。
けんた に はると という子供が生まれました。
===== 年 3007 =====
集落イベント: 厳しい冬が訪れ、集落を試練が襲った。
ゆうこ に そら という子供が生まれました。
===== 年 3008 =====
集落イベント: 大雨による洪水が田畑を襲った。
ひろし に ゆい という子供が生まれました。
けんた に かいと という子供が生まれました。
===== 年 3009 =====
集落イベント: 地震が地域を揺るがした。
たかし に まお という子供が生まれました。
あいこ に かいと という子供が生まれました。
めぐみ に はると という子供が生まれました。
===== 年 3010 =====
集落イベント: 厳しい冬が訪れ、集落を試練が襲った。
ゆうこ に そら という子供が生まれました。
===== シミュレーション完了 =====
シミュレーション終了時のエージェント数: 29
たかし すずき, 年齢: 35, 職業: 農民