ページナビゲーションエージェントを作りながら、エージェントの為の手間の少ないデータ追従を考える – LayerX エンジニアブログ

タイトルOGP

この記事はLayerX AI Agentブログリレー、27日目の記事です。

こんにちは。25卒として新卒入社し現在はバクラク勤怠の開発をしている@yataと申します。

今回はページナビゲーションエージェントの実装をやってみたという内容と、それに伴って感じた周辺の課題についての記事になります。

あの機能ってどこのページにある…?

バクラク勤怠の開発を始めて半年が経過しましたが、チームに入った当初は「この機能ってどこのページで使えるんだっけ?」となることがたまにありました。

サイドバーから探してみて中身を探してを繰り返して数分ロスしたこともありました。

これは、開発段階でページ分類をどれだけ考えて作っても、ユーザーにドメイン知識がなかったり、コンテンツ量があまりに多くなったりといった要因によって発生してしまう問題だと思います。

そこで今回、「要望を伝えたら勝手にそのページを探してナビゲーションしてくれる」というAI Agentを作ってみました。

実装してみよう

今回作ったエージェントの流れはこんな感じです。

graph TD
    A([ユーザーが要望を伝える]) --> B[エージェントがページパスの一覧を取得];
    B --> C{要望に該当するパスがあるか?};
    C -- ある --> D{slugが存在するか?};
    D -- 存在する --> H[slugに渡す引数をDBから取得];
    D -- 存在しない --> I[完全なURLを組み立ててユーザーに案内、リダイレクト];
    H -- 情報不足がある --> J[ユーザーに不足情報を聞く];
    H -- 情報が揃っている --> I;
    J --> H;

これだけ見てもという感じなので実際に動きを見てみましょう

使い方①:シンプルなパターン

まずはシンプルに会社設定を開きたい場合です。
エージェントに「会社設定」と投げるとパス一覧からそれっぽいものを取ってきて返してくれます。
シンプルなAI ユースケースを紹介している画像

使い方②:slugが必要なパターン

次に少し複雑な使い方で、「yataの出勤簿を開きたい」という場合です。
この場合に最初に見つけてくるパスは/manager/daily_works/[ユーザーのID]ですが、このままではyataのIDがわからないのでDBから引っ張って来てもらう必要があります。

そこで、事前にslugに関する説明をパスに紐付けた状態で保存しておいて、それを参照してユーザー情報を取得するツールを呼んでもらう様にしました。
そして取得したユーザー情報からIDを抜き出してslugが埋まった状態のURLを組み立ててくれるという流れになります。

今回はyataという名前が入っているユーザーが2人いたのでどちらにするか聞き返してきてくれました。
もし実際に組み込むならこの部分は消して決め打ちでもいいかもしれません。

DBから情報を取得する必要がある場合のユースケースを紹介している画像

他にもページの中身を見ないと分からない要望(〇〇があるページ等)に対応するために各ページの中身をまとめて保存しているので、ユーザーがやりたい事を伝えるだけで正確にリダイレクトしてくれる様になっています!

実装めちゃ簡単

見ていただいた通り、そこそこ動くものが作れました。
実際に組み込む場合には表示する文字を削ったり決め打ちでリダイレクトさせるなどが必要になってくるかもしれませんが、一旦機能としては十分な仕上がりだと思います。

しかし今回実装したのは以下のLLM Tools4つくらいで、めちゃくちゃ簡単に実装することができました。(社内で基盤を作ってくださっていたからというのも大きいですが)

  • パス一覧を取得する
  • パス一覧とそれに紐付いたページの内容を取得する
    • コンテキストが大きくなるのでシンプルなパス一覧ツールで事足りない場合に使う
  • 名前から従業員情報を取得する
  • リダイレクト処理をする

ファジーな要望を叶えてくれるエージェントをこんなに簡単に作ることができるんだという驚きがありました。

実際に組み込む際の課題

ここまでの実装は順調に行きましたが、いざ実際にプロダクトに組み込むことを考えるとデータの追従が唯一にして最大の課題だと感じました。

このエージェントを動かす元になるデータとしては以下のものがあります

  1. パスの一覧
  2. パスに紐付いたページの内容
  3. slugとそれに必要な情報の取得方法

しかし、これをリリースごとに毎回チームの誰かが更新して最新の情報に追従するのは余りに不可能です。
日々の業務にプラスして、情報をまとめて反映するタスクを行うのはコスト的にも認知的にも難易度が高すぎます。

ということで、これをどうにかして自動で実装に追従させてコストを下げる必要があります。
そこで以下の様な方法を考えてみました。

「1. パスの一覧」をts-morphから取得する

このエージェントのコアのデータであるパスの一覧は、tsのソースコードを扱うツールキットであるts-morphを使ってsrc/pagesディレクトリ以下を全走査することで網羅的にパスを取得しています。
AIくんがシュッと書いてくれて良さそうだったのでそれをそのまま使いました。

省略版コード



function listPages(): string[] {
    
    
    const sourceFiles = project.getSourceFiles(pagesDirPath + "/**/*.tsx");

    
    for (const file of sourceFiles) {
      const filePath = file.getFilePath();
      const relativePath = path.relative(pagesDirPath, filePath);

      
      let pagePath = relativePath.replace(/\.tsx$/, "");
      if (pagePath.endsWith("/index")) {
        pagePath = pagePath.replace(/\/index$/, "");
      }

      pages.push(pagePath);
    }
}

「2. パスに紐付いたページの内容」を、Chrome DevTools MCPで取得する

ここが今回最も試行錯誤した部分です
パス単体だけでは推測できない場合(「〇〇があるページを見たい」など)に備えて、ページの説明を機械的にまとめたかったのですが良い方法がすぐに思いつかず苦戦しました。

最初は一旦パスの時と同様にsrc/pages内のファイルを再帰的に取得して文字列を全て取得し、それを元にLLMに食わせてみたのですが、どうしても一般的な語彙が混ざってしまったり動的な文字列を上手く取得できなかったりして精度が低い出力しか出てこなかったです。

次にバクラクのガイドを取ってきてその内容を圧縮して持っておけばいいんじゃないかと思い試してみたのですが、ガイドはあくまで網羅性よりもお客様に快適に使っていただけるかを重視してまとめられているものなので今回のユースケースには合っていなさそうでした。

そして最後に、「お客様に見えるものから取ってくるのが一番なのでは?」という所に辿り着き、Chrome DevTools MCPを使って実際のバクラク勤怠に表示されているHTMLをそのまま取ってきて、それをLLMにまとめさせる様にしました。MCPから取ってきたHTMLをGemini 2.5 Proにまとめさせてみると以下の様になり、実際の表示と遜色ない満足いく情報としてまとめることができました。

ページ名: 会社設定
# 概要
会社全体の勤怠運用に関する基本ルールを設定するページ。

# 主なアクション:
- 運用開始年月: 通知やアラートを有効にする開始年月を設定。
- 日付変更時刻: 日をまたぐ勤務の際、1日の区切りとなる時刻を設定。 
- 打刻ボタン表示: 打刻画面のボタン表示方法を「状況に応じて切替」か「常に全表示」から選択。

「3. slugとそれに必要な情報の取得方法」はツールを自動で書かせる(将来的に)

ここは時間が足りず実現できませんでした…
slugとその意味自体はコードベースから取得できると思うのですが、実際にその情報を取得するLLM Toolsは自動化することができませんでした。
GraphQLスキーマさえ見ればあとは既存のツールとほぼ同じなのでClaude CodeやCodexを動かして自動でコードを書かせれば恐らく可能だとは思いますが、現状はslugが新しく追加された場合、自分でツールを書くところまでしか行けませんでした。

(この記事のレビュー時に教えていただいたのですが、code interpreter toolというものを使うと自分で生成したコードを自分で実行させるという地産地消ができる様になるそうです)

もし仮にプロダクトに組み込むとしたら、上記の方法をGithub Actionsでリリースタイミングに発火させる様にして、人間の手を離れた状態で自動追従できると運用の現実味が増すのではないかなという印象でした。

今後の展望

以下の点を改善できるともっと良くなりそうでした

  • 必要な情報以外喋らせない:ナビゲーションは頻繁に行うものなのでもっと機械的に必要な情報だけ返すようにしたいです
  • 高速なモデルに変える:実装時はずっとclaude-4-sonnetで試していたんですが、処理自体が軽いのでhaikuなどの高速なモデルの方が快適だな〜と思いました。ただclaude-3.5-haikuで試してみるとレスポンスが高速な代わりに考える回数が多いせいでむしろ少し遅くなった印象でした。AI SDKではまだ4.5-haikuが来ていないので、対応され次第試してみたいです
  • GitHub Actionsに載せてみる:データの追従を自動化してみて実際にワークするのか検証してみたいです

終わりに

AI Agentってなんだか難しそうな印象があったんですが、入り口に立つこと自体は全然敷居が高くないと今回で感じました。
一旦何か作ってみてから深淵な内容に踏み込んでみてもいいのではないでしょうか?

むしろ、AI Agentになっても従来のソフトウェア開発の知見の重要性を強く感じるAI Agent開発でした!

最後に改めて、この記事はLayerX AI Agentブログリレーの27日目の記事です。
今後も毎日AI Agentに関する知見をお届けする予定ですので、LayerXテック公式Xを是非フォローして頂けると嬉しいです!

↓新卒以外の方も大歓迎ですのでぜひお話ししましょ〜
jobs.layerx.co.jp




Source link

関連記事

コメント

この記事へのコメントはありません。