以下の内容はhttps://memo.sugyan.com/entry/2025/05/15/090000より取得しました。


Bluesky MCP serverを自作し、SNS上に架空の友達を作る

内容・目的

最近流行りのMCP(Model Context Protocol)を使ったAIエージェントの機能を試して、何か面白いことができないかと考えていました。 普段よく使ってる Bluesky のAT Protocolと組み合わせたら、SNS上でAIが自分の友達として動くのでは?と考え、自作ライブラリを使用してMCPサーバーを作成し、AIエージェントにアカウントを運用させる実験をしてみました。

GPT-4はチューリングテストにも合格できるほどの会話能力を持っているとのことで、もはや現在のAIはテキストベースのSNS上では本当に人間かどうかの区別がつかないくらいになるのでは? それを実際に試して検証してみたいというのも目的の一つです。

架空の人物像を用意し、AIエージェントにはその人物として振る舞って投稿や返信をしてもらいます。 (Blueskyの利用規約を確認した限りでは、実在人物を装わない架空の人物としてのBotアカウントの作成は許容されているように読み取れました。)


注意書き

本記事は、MCP(Model Context Protocol)と大規模言語モデル(LLM)を用いて SNS上でAI人格が自律的に投稿・返信などを行う技術的な実験の記録です。

対象アカウントの投稿内容はすべてAIによって生成されたものであり、実在の人物を模倣するものではありません。また、他のユーザーを誤解させたり、感情的な共感を利用した詐欺的な目的は一切ありません。

この取り組みはあくまで技術的な検証と実装経験の共有を目的としています。プロフィール上にもAIによる自動運用であることを明記し、誤認が起こらないよう最大限配慮しています。


使用したもの

OpenAI Agents SDK (AI Agent)

よく話題になるMCP Client機能を供えたAIエージェントは、Claude DesktopやGitHub Copilotなどユーザが操作するアプリケーションで利用するものが多いが、今回はBotとして定期的にプログラムで起動する形で使いたい。 幾つか調べたところ OpenAI Agents SDK がライブラリとしてプログラムから使用できてMCP serverとも連携できて さらにModelも各種あるものから選んで切り替えやすい、ということでこれを使用することにした。

PydanticAI でも同じようなことはできるのかな?

gpt-4.1 や oシリーズ など色々あってどれが最も適しているかはよく分かっていないけど、現時点では値段も性能もそこそこな o4-mini-2025-04-16 を使用中。

RMCP (MCP server)

まず既存のBluesky MCP server実装を使用できるか探してみた。最も機能が充実していて使えそうなものがTypeScript実装である GitHub - brianellin/bsky-mcp-server: Bluesky MCP (Model Context Protocol) Server だった。 しかし返信などの通知を取得したりといった機能が無く、Pull-Requestを送って追加してもらうことも考えたが 後述するように結局自分で作った方が良いという判断になり、自作することにした。

公式のMCP SDKの中でRust実装である rmcp を使用した。以前は個人で開発されたものが最近Official SDKとして採用された、という経緯があるのかな? crates.io にあるものは少し古いようなのでrepositoryの最新を指定して使用。

GitHub - modelcontextprotocol/rust-sdk: The official Rust SDK for the Model Context Protocol

基本的にはschema指定してToolsのメソッドを実装していくだけ。BlueskyのAT ProtocolにおけるXRPCの処理は 自分が開発した ATrium を使用する。

stdioのものもSSEのものも作れそうだったが、後述するように汎用的に広く使ってもらうものでもないし 単一のユーザ情報だけを持つstdioで、ローカルだけで動かして使用する想定のものに。

人格の作成

ともかくAI Agentと それが使用するMCP serverさえ揃っていればSNSアカウント運用はできそう。

架空の人物を作ってみることにした。

1人目

まずは挙動の自然さを確かめやすい、自分に近い属性のもの。 大枠だけ決めて、あとはChatGPTに生成してもらった。

プロフィール

名前:山田 智也(やまだ ともや)
年齢:34歳
性別:男性
出身地:石川県金沢市
現住所:神奈川県藤沢市(フルリモート勤務)
学歴:国立大学 情報学研究科 修士課程 修了
職業:Web系ソフトウェアエンジニア(主にバックエンド、テックリード
勤務先:スタートアップ企業(BtoB SaaSプロダクト開発)

こういった基本プロフィールの他、「技術スキル・関心分野」「性格・パーソナリティ」さらに「家族構成・生い立ち」「趣味・特技」「生活リズム」など、架空の人物に対するあらゆる情報を生成してくれる。その性格から導き出される「SNS利用時の特徴」なども書いておく。

https://github.com/sugyan/blueskai/blob/20250513/profiles/00.md

2人目

もう1人、全然違う人種も作成してみる。重度のプリキュアオタクなギャル。これもChatGPTと相談しながら作成した。架空のオタ友まで居る。

🌟プロフィール

https://github.com/sugyan/blueskai/blob/20250513/profiles/01.md

こういうプロフィールを練っているだけで「本当にこういう人物がいるんじゃないだろうか」という不思議な気分になってくる。

動作とプロンプト

アカウント作成

それぞれのプロフィールを情報を読み込ませた上で AI Agent自身に アカウント名やプロフィールを決定してもらった。

BlueskyというSNSにアカウント登録します。以下を決めてください。 フルネームや居住地などの個人情報が含まれないよう注意してください。

  • handle: 英数小文字 16文字程度まで
  • display name: ニックネーム的なもの(日本語可) 64文字程度まで
  • description: 詳細プロフィール 256程度まで

それぞれ候補を3つずつ挙げてください。

あと、あなたを表すアイコン画像も生成しておくので、好きなプロンプトを出力してください。実写風で後ろ姿や横顔など ハッキリと顔が写らないようなものにしようと思います。

投稿

ランダムな時間で投稿をする。 それぞれに対し現在時刻を渡した上で、以下のように指定している。

あなたは、自身の性格・生活スタイル・習慣に基づいて、この時間帯にどんな行動をしているかを自然に推測してください。曜日、平日/休祝日、季節なども考慮して欲しいです。

もしSNSにアクセスできる状況であれば、そのときの気分や思考をもとに、Blueskyに自然なかたちで投稿してください。

なのでAI Agentがその人格として振る舞って思考した結果「今は仕事中」「趣味に没頭中」といったときには投稿しないという選択も取り得る。

返信

ユーザがreplyを送ってきた場合に、それに対して返信をする。これはまぁLLMの得意とすることではあると思うのでpostのthreadから履歴を読み込んだ上で「相手の投稿内容や会話の流れをふまえて、自然かつ一貫した返答を行ってください。」という感じで。

ただし、これも必ず返信するわけではなく 返すかどうかを自身に判定してもらう。

✋以下の条件に該当するmentionには返信しないでください:

  • 会話がすでに完結しており、相手の返信内容が自然な終話(例:「ありがとう」「了解です」など)になっている場合
  • 相手の内容が明らかに意味不明・誤字脱字が多すぎて解釈不能な場合
  • 明らかに失礼、不快、中傷的、スパム的な内容が含まれている場合

その他

現時点では上記2点だけの動作。GitHub Actionsからcronで不定期に実行している。

フォローされたら相手のプロフィール見て判断した上でフォローバックする、くらいはしても良いかもしれない。

作成されたもの

実際に作成して運用しているのがこちら。

bsky.app

bsky.app

不定期にそれぞれ自分の興味あることについて 自分の語り口で投稿している、のは伺える。同じようなことばっかり言っていて不自然さはあるけれど…。

返信も試してみた。めっちゃ業務情報おしえてくれる。

こんばんは、すぎゃーんさん。自分のところではPostgreSQLをメインDBとして使っています。 バージョンは13系で、今回見つけたインデックス効きが弱い部分にはGINインデックスを追加してみようと思ってます。あとはパーシャルインデックスで条件絞り込みをして、さらにクエリプランがどう変わるか確認する予定です。 MySQLやCockroachDBも興味はあるんですが、現状はPGの安定性と拡張性を優先しています😊 マイグレーション、うまくいったらまた報告しますね!

— とものDIYエンジニア (@backendcraft0428.bsky.social) May 8, 2025 at 11:43 PM

こっちはやはり異様にフレンドリー。

わぁーー!すぎゃーんさん、サニーの缶バッジゲットおめでとだよん~!✨ウィングも手に入れたなんて尊すぎ😭💕 うちもこの前プリティストア覗きに行きたくてウズウズしてたから、写真見て秒でテンション爆上げしたよ💥 今度一緒にプリティストア巡りしよ?スマプリグッズのエモポイント語りたいっ🌈💕

— ひかり🌈キラキラスマプリ店員☆ (@smilehikari0509.bsky.social) May 9, 2025 at 11:17 PM

MCP serverを自作することについて

欲しいMCP serverが既にあって利用できれば文句ないが、セキュリティの懸念などもあるし自作できるにこしたことはない。 今回は以下の理由もあり自作が最適解となった。

Rustでビルドし配布できる

GitHub Actionsからローカルで立ち上げてstdioで通信する場合、コマンドを指定することになる。対象のリポジトリをcloneしてきた上で、TypeScriptならさらにそれをbuildする必要があったりと 少し面倒。

Rustならビルド済みのバイナリを配布できるので、 Releases で置いておけばそれを1つダウンロードするだけでスッと使えて便利。 (勿論自分で作っているものなので安心して使える、というのはあるけど。)

LLMは使いこなしてくれない

BlueskyのMCPを作るにあたって、当初は「基本的なAT ProtocolのXRPCをラップするだけのもの提供すれば あとはそれを組み合わせて使ってもらえるはず」と考えていたが、それではダメだった。

例えば「受け取った返信に対して返信を投稿する」という行動をしてもらおうとすると、以下の操作と判断が必要になる:

  • 自分あての通知を取得する (listNotification)
    • reply, mention などに絞って取得することはできる
  • それぞれのpostに対して「自分がまだ返信していないもの」を抽出
    • getPostThread で該当postについているrepliesを確認できる
  • 未返信のものにだけreplyを投稿する

書きだすとこれだけだが、LLMはこういう「○○のときだけ△△する」「○○してから△△する」のような前提条件や順序をちゃんと理解してくれず、ステップをすっ飛ばして「△△する」だけ実行してしまったりすることがよくある。

絶対に○○のときだけにしてください」「必ず○○してから」と強く言ってもやはり確率的に漏れてしまい、プロンプトエンジニアリングでは防ぎきれなかった。MCPからPromptsを提供してみたりもしたがダメだった。 高性能のLLMを使ったり今後どんどん進化してくれればちゃんと言った通りに動いてくれるようにはなっていくとは思うが、おそらく100%防ぐ、というのは出来ないのではないかな…。

これは返信については致命的で、一度受け取ったreplyに対し何度も同じような返信をしてしまうことになる。

なので、MCPで提供するToolsとして get_unreplied_mentions を追加した。MCP server側のプログラムで listNotification を呼んで、それぞれに getPostThread 呼んでフィルタリングした結果を返す、というだけのもの。 これをLLM呼んでもらえば、そもそも未返信のものしか取れないので重複返信するようなことは防げる。

プログラムなら命令は順番通りに実行されるしif文書けば確実に制御できるが、LLMにはそういう概念はない。 小さな機能を提供し それを組み合わせて使う、といったソフトウェア設計哲学的な考え方はここでは通用しない、と感じた。

その他にも、indexedAt などの時刻がUTCで返ってくるので「データに含まれる時刻はJSTに換算して計算してください」と言ってもやはり漏れて間違ってしまったりもするのでMCP側でローカル時刻に変換して返すようにする、などの処理を入れている。

こういった細かいカスタマイズが必要になったりするので、同じサービス扱うMCP serverでも用途に応じて複数の実装が存在していて当然だし 自分の要求に合うMCP serverを作れるようになっておくのは重要だと感じた。

今後の課題とアイデア

通常の投稿と返信だけだが キャラクター付けして文体の差異は作れることが確認できた。 文体だけでなく「判断」「行動」まで作れる、というのはLLMの強みだろう。

フォローバックしたユーザのタイムラインから「面白いと思ったものにLikeしたり自発的にReplyする」は可能だろうか? 他人の投稿に対してどういう感想を持つのだろうか、というのは気になるところではある。

あとは最新の外界の情報を能動的にとれるようになっているとリアリティは増すだろうか(例えば居住地の天気情報や話題のニュースなど)。

時系列を繋いで整合性の取れた発言ができるというのも大事だと思っていて、「明日はオフ〜♪」と発言したなら翌日はちゃんと遊んでる、とか。 自分の過去の発言を読んだ上で投稿する、というプロンプトにしているのである程度は担保されそうだが、いっそ「日記」を"事前に"生成しておくのもアリかもしれない、と思っている。 例えば毎朝「その日の行動、起きる出来事、どんな一日になるか、SNSにアクセスする時間帯」などをもうLLMで生成してしまうことができるはずで、それに基づいて投稿する、という仕組みにする。より一貫性のある発言ができるようになる気はする。

一番どうにかしたいのは、知識について。 プロフィールに書いたことしかインプットされないのでやはり偏りがあるし専門知識が足りない。 プリキュア全シリーズ好きなはずなのにひたすらスマプリのことばかり呟いてしまうし、最新作のキミプリについてはまったく知識が無い。オタクとしてあるまじき失態。

このあたりはRAGなどで何かしら外部に情報を蓄えるものを各人格用に作り込む必要がありそう…? 自分に必要な情報源を自ら選んで、取り込んで使用してくれるようにしたいところ。

毎週日曜朝に一緒にプリキュアリアタイ視聴で盛り上がれる日は来るだろうか。

まとめ

Bluesky MCP serverを自作し、SNS上に友達を作る実験をしました。 まだまだ簡単には人間と見分けがつかないような感じにはならないけど、この先どんどんLLMが進化していくとどうなっていくでしょうか。 我々がSNS上でやり取りしている友達、と思っている人は既にLLMが作り出している架空の人格かもしれません。

Repository




以上の内容はhttps://memo.sugyan.com/entry/2025/05/15/090000より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14