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

MCPを使ったRAGをPHPで作る

Avatar for uiui uiui
August 09, 2025
12

 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 ] ] ], ]; }