Upgrade to Pro — share decks privately, control downloads, hide ads and more …

MCPを使ったRAGをPHPで作る

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for uiui uiui
August 09, 2025
60

 MCPを使ったRAGをPHPで作る

MCPを介してRAGをどう実現するのか調査した時のお話。
既存のRAGシステムをMCPを使ってAgentシステムへ昇華できるか模索中です。

Avatar for uiui

uiui

August 09, 2025
Tweet

Transcript

  1. 今日のゴールは 
 Github Copilot Chat (Agent) / VSCode で質問したら PHPアプリDB内のテキストデータをベクトル検索し

    (ベクトル検索は構築済み) 取得した情報をもとに回答できるようにする! *認証・エラーハンドリングなどは全部省きます!
  2. Postgres + pg_vectorで 
 ベクトル型を保存・検索できる 
 Column | Type |

    Collation | Nullable | Default -------------+--------------------------------+-----------+----------+----------------------------------------------- embeddings | vector(1536) | | not null | ベクトルは’[1,2,3]’のような感じで表される select * from items order by embeddings <=> '$vector’ limit 20
  3. ToolsとAgent 
 ・すでに2023年後半くらいからToolsというものがあった /** * 現在の天気を取得する * @param string $location

    日本語の文字列で場所を示す * @return string */ public function getCurrentWeather(string $location): string; /** * 現在の気温を取得する * @param string $location * @param string $format * @return string */ public static function getCurrentTemperature(string $location, string $format="celsius"): string; LLM あなたはこの関数を 使って情報を 取得できます! 私達
  4. ToolsとAgent 
 今日は大阪に行くんだけど 何着て行けばいいかな? 使える関数 getCurrentWeather(string $location) :string getCurrentTemperature(string $location,

    …) 今日の気候に合わせた 服装を提案しよう getCurrentWeather(“osaka”); を実行したい! “晴れのち雨”
  5. ToolsとAgent 
 今日は大阪に行くんだけど 何着て行けばいいかな? 使える関数 getCurrentWeather(string $location) :string getCurrentTemperature(string $location,

    …) 今日の気候に合わせた 服装を提案しよう getCurrentTemperature(“osaka”); を実行したい! “32.3”
  6. MCPの誕生!嬉しい! 
 ツール等を使う AI側(MCPクライアント )と、 ツール等を提供する (MCPサーバ)側 との間の標準プロトコル ! 加えて、構築済みのオープンソース

    MCPサーバも公開される https://github.com/modelcontextprotocol/servers 好きなAIアプリケーションに、好きなデータソースを接続できる。
  7. 受信するjsonの構造 
 { jsonrpc: "2.0"; id: string | number; method:

    string; params?: { [key: string]: unknown; }; } methodでどういう種類のリクエストか判断する
  8. Controller initializeに対応 
 class McpController extends Controller { public function mcp(Request

    $request) { $data = match($request->input('method')) { 'initialize' => $this->initialize(), }; return response()->json([ 'jsonrpc' => '2.0', 'id' => $request->input('id'), 'result' => $data, ]); } private function initialize(): array
  9. private function initialize(): array { return [ "protocolVersion" => "2024-11-05",

    "capabilities" => [ "tools" => [ "listChanged" => true ], ], "serverInfo" => [ "name" => "XX System Document Server", "version" => "1.0.0" ], ["instructions" => "このサーバーはXXシステムのドキュメントから、文章クエリでドキュメントを検索 し取得することができます。"], ]; } Controller initializeに対応 

  10. private function initialize(): array { return [ "protocolVersion" => "2024-11-05",

    "capabilities" => [ "tools" => [ "listChanged" => true ], ], "serverInfo" => [ "name" => "XX System Document Server", "version" => "1.0.0" ], ["instructions" => "このサーバーはXXシステムのドキュメントから、文章クエリでドキュメントを検索 し取得することができます。"], ]; } 使える機能リスト Controller initializeに対応 

  11. notifications/initializedに対応 
 
 class McpController extends Controller { public function

    mcp(Request $request) { $data = match($request->input('method')) { 'initialize' => $this->initialize(), 'notifications/initialized' => http_response_code(202) && exit(), }; return response()->json([ 'jsonrpc' => '2.0', 'id' => $request->input('id'), 'result' => $data, ]); } レスポンスは何でもよい
  12. notifications/cancelledでエラー出ないように 
 
 class McpController extends Controller { public function

    mcp(Request $request) { $data = match($request->input('method')) { .. 'notifications/cancelled' => http_response_code(202) && exit(), }; return response()->json([ 'jsonrpc' => '2.0', 'id' => $request->input('id'), 'result' => $data, ]); } ちゃんと実装するときは、リソースを無駄遣いしないように処理を中断したい!
  13. Controller tools/listに対応 
 $data = match($request->input('method')) { 'initialize' => $this->initialize(), 'notifications/initialized'

    => http_response_code(202) && exit(), 'tools/list' => $this->toolsList(), }; return response()->json([ 'jsonrpc' => '2.0', 'id' => $request->input('id'), 'result' => $data, ]);
  14. private function toolsList(): array { return [ 'tools' => [

    [ "name" => "getDocument", "title" => "Xシステムのドキュメントから情報を取得", "description" => "クエリ文章から関連するドキュメントを取得します。", "inputSchema" => [ "type" => "object", "properties" => [ "query" => [ "type" => "string", "description" => "ドキュメントを検索する文章クエリとして渡してください。", ] ], "required" => ["query"] ] ], ] ]; }
  15. Controller tools/call 
 private function getDocument(?string $query): array { $documents =

    $this->searcher->searchDocuments($query); return [ 'content' => [ ['type' => 'text', 'text' => $documents->toText()] ], 'isError' => false, ]; } 検索したドキュメントの内容をテキストで返す。複数ドキュメントの場合は、わかりや すい区切り文字を入れるなど、適宜対応
  16. 補足:searchDocuments() 
 //クエリをembeddingAPIに投げてベクトル /** @var Vector $vector **/ $vector =

    $this->embedding->getEmbeddings($query); DB::select( "select * from documents order by embeddings <=> '{$vector->__toString()}' limit 5" );
  17. 質問をすると... 
 
 入力:
 生成した文章クエリ 
 出力:
 検索にマッチした情報 
 


    が返ってきた! 
 *MCPツールを使ってくれるようにプロンプトを整えるか、 ツールのdescription等を整える必要あり
  18. prompts/listとprompts/getの追加 
 $data = match($request->input('method')) { .. 'prompts/list' => $this->promptsList(),

    'prompts/get' => $this->getPrompt($request->input('params.arguments.question')), 'completion/complete' => $this->retCompletion(), };
  19. 使えるプロンプト一覧を提供 
 private function promptsList(): array { return [ 'prompts'

    => [ [ "name" => "generateSearchQueryText", "title" => "ドキュメントを検索するためのクエリ文章を生成", "description" => "ドキュメントを検索するためのクエリ文章を生成します。", "arguments" => [ [ "name" => "question", "description" => "システムに対する質問文", "required" => true ] ] ], ] ]; }
  20. system, user のプロンプトを書く 
 $system = “文脈が伝わるようなプロンプトを与える ”; $human =

    <<<EOT ###質問 {$question} ###指示 システムのどんな機能を使いたいのか、もしくはどんな仕様を知りたいのか を文章で出力した後、 getDocumentツールを使って XXシステムのドキュメントを検索して、 その内容から読み取り、クライアントからの 問い合わせに適切な回答をしてください。 必ずドキュメントに記載されている情報を使ってください。 EOT; 質問を代入できる箇所を作る
  21. プロンプトを提供する 
 private function getPrompt(string $question): array { $system =

    "..."; $human = "..."; return [ 'description' => 'XXシステムのドキュメントから情報を 取得するためのクエリ文章を生成し、 XXシステムにRAG検索を行います。 ', 'messages' => [ [ 'role' => 'system', 'content' => [ 'type' => 'text', 'text' => $system ] ], [ 'role' => 'user', 'content' => [ 'type' => 'text', 'text' => $human ] ] ], ]; }