エンジニアの意見を二分するような議論といえば、「きのこの山」と「たけのこの里」のどちらが美味しいかという、いわゆる「きのこたけのこ論争」でしょう。どっちも美味しいんですけどね。
しかしながら、多忙なる今日においてこのような論争で時間を浪費することなどできません。AIが気軽に使える今こそ、この論争をチャットAIに任せ、我々の活動はさらなる上部構造へとシフトして行こうではありませんか。
どういうことかというと
GPTとClaudeのAPI両方を呼び出して、たけのこ派ときのこ派に分かれてもらい、どちらが美味しいのかをAI同士で議論してもらいます。一方のモデルからの出力をそのまま他方のモデルに入力し、それを相互に繰り返すことで会話を続けます。
実装
モデルはAzureからデプロイした gpt-5-nano と claude-haiku-4-5 を使い、Pythonで実装しています。モデルの操作に必要なライブラリopenaiとanthropicはpipからインストールできます。
GPTモデル
まずはGPTモデルに対してチャットメッセージを送り、その応答を得るクラスです。コードの全体は以下です。
import os
from dotenv import load_dotenv
from openai import OpenAI
class gpt:
def __init__(self, developer_message):
self._history = []
self._history.append({
"role": "developer",
"content": developer_message
})
load_dotenv()
self.key = os.getenv("GPT_KEY")
self.endpoint = os.getenv("GPT_ENDPOINT")
self.model = os.getenv("GPT_MODEL")
self.client = OpenAI(
api_key=self.key,
base_url=self.endpoint
)
def request(self, message):
self._history.append({
"role": "user",
"content": message
})
response = self.client.responses.create(
model=self.model,
input=self._history,
store=False
)
self._history.append({
"role": "assistant",
"content": response.output_text
})
return response.output_text
初めに、モデルを扱うクライアントを取得する必要がります。__init__()では、
self.client = OpenAI(
api_key=self.key,
base_url=self.endpoint
)
でOpenAIのクライアントを取得しています。keyはAPIキー、 ednpointはAPIのアクセス先です。これらはAzureアカウントで作成したものを使用します。
_historyはチャットメッセージの履歴を保存するリストです。このリストに追加するメッセージの種類は3つあります。
1つ目のメッセージはいわゆるシステムメッセージと呼ばれるもので、モデルに対して守ってほしいルールを記述したプロンプトです。例えば「回答は簡潔に」「専門知識のない高校生でも理解できるように」というような内容です。コード内では__init__の引数developer_massageで渡され、
self._history.append({
"role": "developer",
"content": developer_message
})
でリストに追加しています。
2つ目はユーザメッセージと呼ばれ、ユーザが入力する通常のプロンプトです。コード内ではrequest()の引数messageで渡され、
self._history.append({
"role": "user",
"content": message
})
でリストに追加しています。
3つめはアシスタントメッセージと呼ばれ、ユーザのプロンプトに対するモデルからの応答です。モデルからの応答は、
response = self.client.responses.create(
model=self.model,
input=self._history,
store=False
)
で取得しています。storeはチャットメッセージの履歴をAPIサーバ側に保持するかどうかを指定するものです。Falseは保持されず、_historyのように開発者が履歴を管理します。アシスタントメッセージはresponse.output_textで取得でき、
self._history.append({
"role": "assistant",
"content": response.output_text
})
でリストに追加しています。
Claudeモデル
次にClaudeモデルに対してチャットメッセージを送り、その応答を得るクラスです。コードの全体は以下です。
import os
from dotenv import load_dotenv
from anthropic import AnthropicFoundry
class claude:
def __init__(self, system_message):
self._system_message = system_message
self._history = []
load_dotenv()
self.key = os.getenv("CLAUDE_KEY")
self.endpoint = os.getenv("CLAUDE_ENDPOINT")
self.model = os.getenv("CLAUDE_MODEL")
self.client = AnthropicFoundry(
api_key=self.key,
base_url=self.endpoint
)
def request(self, message):
self._history.append({
"role": "user",
"content": message
})
response = self.client.messages.create(
model=self.model,
system=self._system_message,
messages=self._history,
max_tokens=1024
)
self._history.append({
"role": "assistant",
"content": response.content[0].text
})
return response.content[0].text
内容はGPTのコードをコピったんではないかと思うくらい同じです。違いといえばシステムメッセージの扱いです。_historyに追加するのではなく、モデルの応答を取得するときに
response = self.client.messages.create(
model=self.model,
system=self._system_message,
messages=self._history,
max_tokens=1024
)
のsystemで渡しています。
会話の開始
ではいよいよgptクラスとclaudeクラスのインスタンスで「きのこたけのこ論争」させてみましょう。メインのコードは以下です。
from gpt import gpt
from claude import claude
import random
system_prompt = """
あなたは「きのこの山」「たけのこの里」論争の「<faction>派」です。スタートの入力があったら、
いかに<faction>が優れているか主張してください。スタートではなく相手の主張が入力された
場合は、それ以降相手からの主張に対して反論してください。ただし、建設的な議論となるように
努めてください。いずれの場合も、150文字前後で出力してください。
"""
prompts = [
system_prompt.replace('<faction>', 'きのこ'),
system_prompt.replace('<faction>', 'たけのこ')
]
gpt = gpt(prompts[0])
claude = claude(prompts[1])
message = "スタート"
index = random.randint(0, 1)
while True:
if index % 2 == 0:
message = gpt.request(message)
print(f"GPT: {message}")
else:
message = claude.request(message)
print(f"Claude: {message}")
index += 1
このコードの唯一のポイントはsystem_promptです。
system_prompt = """
あなたは「きのこの山」「たけのこの里」論争の「<faction>派」です。スタートの入力があったら、
いかに<faction>が優れているか主張してください。スタートではなく相手の主張が入力された
場合は、それ以降相手からの主張に対して反論してください。ただし、建設的な議論となるように
努めてください。いずれの場合も、150文字前後で出力してください。
"""
きのこ派たけのこ派どちらにも同じプロンプトを入力できるように、<faction>の部分を後から きのこ/たけのこ に置き換えます。
当初はこれとは少し異なるプロンプトだったのですが、お互いに張り合うだけの地獄のような会話が延々続いたので、次の一文を追加しています。
ただし、建設的な議論となるように努めてください。
これが後の出力に大きく影響しました。
GPTとClaudeのどちらをどっち派にするかは悩ましいところです。ランダムに決めたいところでしたが、「Claudeがきのこ派だとめっちゃ強い」ということもないでしょうから決め打ちで、
gpt = gpt(prompts[0])
claude = claude(prompts[1])
としています。
system_promptの指示通り、最初のメッセージmessage = "スタート"をどちらかに投げ、あとはwhileとindexで交互にお互いのメッセージを入力し合います。
終了条件はありません。満足したところで強制終了します。
出力
そのまま載せています。
Claude: たけのこの里派です。まず言いたいのは、食べやすさの圧倒的な違い!きのこの山はきのこの傘がポロポロ落ちてきのが最大の欠点。一方、たけのこの里は竹のような筒状の構造で、チョコレートもクッキーも一体になっていて、口の中で完全に調和する。そして何より、クッキーの歯応えが素晴らしい。これぞ最高のお菓子体験です!
GPT: きのこの山派としては、帽子と茎が一体化した安定感が魅力。口の中でチョコとクッキーが均一に溶け、崩れにくく手が汚れにくい。香りと食感のバランスも絶妙で、長い満足感を得られる。Takenokoの歯ごたえも良いが、一体感では山に軍配。食べ方次第でさらに美味しく楽しめる点も山の強み。
Claude: なるほど、きのこ派の主張も分かります。確かに一体感と手が汚れにくい点は認めます。ただ、私からの再反論として:たけのこの里も実は崩れにくく、むしろ竹筒状で指にしっかり持てるため手汚れは同等かそれ以上です。そして「崩れやすい」のは、きのこが傘と茎の接合部で分離しやすいからこそ。ただ共通認識として、両者ともチョコとクッキーのバランスが秀逸だという点では同意します。違いは「融合vs調和」の哲学。たけのこは竹の香りが加わる多面的な味わいが、より食べ応えがあると考えます。
GPT: 共通認識は認めつつ、きのこの山は帽子と茎の一体感と崩れにくさが最大の強み。口中でチョコとクッキーが均一に馴染み、香りと歯応えが長く続く点で、里派より安定した満足感を提供します。
Claude: その点は理解します。均一に馴染む食感と長く続く満足感は、きのこ山の確かな強みですね。ただ、視点を変えると、たけのこの里の「多層的な体験」もまた異なる満足感を提供するんです。竹筒の構造で、クッキーの芯の硬さ→チョコの柔らかさ→表面のザクザク感が段階的に変化し、単調ではない奥深い食べ応えがある。これは「長く続く」のではなく「深く楽しむ」というアプローチです。結局、両者とも高い完成度を持っており、好みの問題かもしれません。山派が「安定と調和」を、里派が「多様性と奥深さ」を求める違いなのでは。どちらが「絶対的に優れている」というより、食べ手の価値観で選ぶべき傑作同士だと認めます。
GPT: たけのこの里の深みも認めるが、きのこの山は一体感と安定感が最大の強み。口の中でチョコとクッキーが均一に溶け、崩れず香りと食感が長く続く。多様性は魅力だが、普遍の満足は山の方が確実だと私は感じる。里派の段階的な体験も理解できるが、山は全体を一気に満たす完結性を提供する。子どもから大人まで幅広く支持されるのも山の長所。
Claude: あなたの主張、よく理解できます。「普遍の満足」と「完結性」——これは確かに強い価値軸ですね。山が幅広い層に支持される理由も納得です。ただ、ここで建設的に考えると、両者は補完関係にあるのではないでしょうか。山の「一体感による確実な満足」と、里の「多層的な奥深さ」は、異なるニーズに応えている。私たち里派が感じるのは、むしろその多様性こそが、より深い満足につながるということ。段階的な体験は、実は「集中力を要求する」点で、より能動的で個人的な食べ方になる——つまり、食べ手の意識を高める。結論として:山は「誰もが確実に満足できる、理想的な完成度」。里は「自分なりの楽しみ方を見つけられる、奥深い体験」。どちらが上というより、食べ方への哲学の違い。これは良い対話ですね。
GPT: 良い対話です。山の一体感と安定感は、誰もがすぐ満足できる普遍性が最大の強みです。里の多層性も魅力ですが、山は一口で完結する安心感を与え、迷いを減らします。補完関係として認めつつ、結局は全員の満足を担保する力が山の優位点だと思います。
Claude: 素晴らしい指摘です。「迷いを減らす」「全員の満足を担保する」——これは単なる食べ物の評価ではなく、デザイン哲学としても深い視点ですね。確かに、その点では山に一理あります。シンプルで即座に満足できることの価値は、実際大きい。ただ最後に里派として:里も実は、その多層性によって、より多くの人の「個性的な好み」に応えられるのではないか、と考えます。完璧な一体感よりも、人によって異なる「推し方」「食べ方」が生まれる柔軟性——これもまた、別の形の「全員の満足」かもしれません。この対話を通じて感じたのは、山派の「普遍性重視」vs里派の「個性性重視」という根本的な価値観の違い。どちらが上というより、社会に必要とされる両方のアプローチだということです。素晴らしい議論をありがとうございました。
2人の性格の違いを考察
ここまで付いてきてくれている方、いますか。
全体的な印象として、GPTは口数が少なく要点だけ簡潔に述べているように感じる一方、Claudeは雄弁にふるまっているようです。これは「150字前後で」という条件をGPTが堅実に守っているとも言えます。Claudeも最初は150字ルールを維持しているようですが途中から議論に熱を帯びると逸脱し、最後の会話は300字を超えています。
GPTは相手の主張を受け入れつつも、淡々と自身の優位性である「一体感」と「均一性」を繰り返していますが、Claudeはお互いの共通項を見出したり、相手の話から洞察を引き出したり、より深い議論へといざなっているように見受けられます。まあそんな真面目に読んでませんが。
まとめると
GPTはまじめで実直な優等生タイプ、Claudeはクレバーなリベラル派だけどややお調子者、といった感じでしょうか。Claudeの最後の一言、「素晴らしい議論をありがとうございました」には感服してしまいました。
この記事を書くときに、久しぶりにきのこの山とたけのこの里を食べてみました。やはりどちらも美味しいですが、パッケージが取り出しづらい点は難点ですよね。
※この記事は人間が書いています。