Implementing a retrospective agent: how it works

Alex O'Callaghan

レトロスペクティブエージェントの実装:その仕組み

原文は Alex O'Callaghan により に公開されました。 このブログを購読する

前回の記事では、エンジニアリングの時間がどこに費やされているかを計測しようとしている理由と、小さなLLMエージェントがその計測をチームのスプリントレトロスペクティブに組み込むのにどう役立つかについて書いた。数か月が経ち、エージェントを各チームに展開したところ、これまで起きていなかった議論を生み出すのに役立っている。

今回は、エージェントがどのように動作し、どのようにデータを受け取り、プロンプトがどのように進化したかに焦点を当てる。その基盤となるデータプラットフォームについては、DORAメトリクスをどのように導出しているかも含め、次回の記事で詳しく扱う。

概要

エージェントは生産性メトリクスデータプラットフォームからメトリクスとアクティビティデータを取得し、スプリントを要約して議論のたたき台を提示する、事前共有用のレトロスペクティブレポートを生成する。

すべての生データをモデルに流し込むのではなく、エージェントには3スプリント分のメトリクスのサマリーと、特定のチケットやマージリクエスト、失敗したリリースの詳細を照会するためのツールが与えられる。

Metrics summary(sprint + 2 previous)AgentDetail: tickets, MRs,failed releasesRetro report(3-section markdown)always in the promptasks via 3 lookup tools

エージェントの設計

初期プロンプト

初期プロンプトでは、3つのスプリントにわたるメトリクスを要約し、それぞれに[BASELINE]または[LATEST - FOCUS SPRINT]のタグを付ける。メトリクスはJiraのデリバリーとDORAメトリクスをカバーしている。

DORAメトリクスJiraメトリクス
デプロイ頻度課題およびストーリーポイントの完了率
変更のリードタイム(コーディング+レビュー+デプロイ)コミットしたスコープとスプリント途中で追加されたスコープ
変更失敗率キャリーオーバー
サービス復旧時間サイクルタイム

すべてのメトリクスはデータプラットフォーム側で決定的なコードによって算出されており、モデルに生データから計算させようとはしていない。プロンプトでもこの点は明示している。「メトリクスのサマリーが正とする。再計算したり疑ったりしてはならない」

サマリーには、サイクルタイムやリードタイムが75パーセンタイルを超えるなどの外れ値となったチケットやマージリクエストのリスト、そして注目スプリントで発生した失敗したリリースのリストも含まれる。エージェントは、これらのリストを分析の出発点として使うよう指示される。チケットはKEY (16.8d, 3.0SP)、MRはproject!iid (308.7h: coding 116.3h, review 170.0h, deploy 22.4h)という形式でインラインに記述される。

ルックアップツール

PydanticAIエージェントには、詳細を照会するための3つのツールが登録されている。

@agent.tool
def lookup_jira_ticket(ctx: RunContext[AgentDeps], ticket_key: str) -> str:
    """Look up a Jira ticket by its key (e.g. PROJ-1363).

    Returns summary, status, assignee, story points, sprint, dates, cycle time,
    and full status transition history.
    """

@agent.tool
def lookup_merge_request(ctx: RunContext[AgentDeps], query: str) -> str:
    """Search merge requests by title substring, Jira key, or MR reference (e.g. !1049).

    Returns up to 10 matching MRs with title, author, dates, lead time, and release info.
    """

@agent.tool
def lookup_failure(ctx: RunContext[AgentDeps], query: str) -> str:
    """Search detected failed releases by tag, project name, evidence, or alert tiny id.

    Returns up to 10 matching failures with signal (alert/revert), evidence,
    resolution time, and correlated alert detail where available.
    """

docstringはモデルが推論する際のスキーマを兼ねており、返却されるペイロードがエージェントが物語を構築する起点となる。チケットはstatus_history全体が読みやすいチェーン(例:「In Progress → Code Review → Awaiting Release → Done」)として返され、「Blocked」に出入りを繰り返したチケットの特定に役立つ。MRはコーディング/レビュー/デプロイの時間分割と、それをリリースしたリリース情報を持ち、MR間の共通パターンやリリースサイクルへの影響を把握するのに役立つ。

システムプロンプトでは、エージェントにこれらのツールを積極的に使うことが促されている。「これらのツールを積極的に使うこと……結論を出す前に調べておくこと」

出力の構造

エージェントの出力タイプは単なる文字列で、構造化された出力スキーマも後処理もない。レポートを形作るのはプロンプトのタスク定義だけだ。すなわち、最新スプリントの簡潔なサマリー、ベースラインと比較した2〜4つの注目すべきトレンド(「良いものも悪いものも含めて」)、そして3〜5つの具体的な話し合いのポイントや議論のための問いを、Sprint Summary、Notable Trends、Retrospective Talking Pointsという3つの必須見出しの下にまとめることである。

トレンドを2〜4つ、話し合いのポイントを3〜5つと指定することで選択性が強制され、第3セクションを結論ではなく問いとして枠付けることで、レポートは評決ではなく事前共有資料としての位置づけを保つ。トレンドを解釈し、どう対応するかを決めるための十分なコンテキストはチームが持っているという信頼に基づいている。

典型的な実行例

ここでは、典型的なエージェントの実行例を示す。サマリーでは11件のチケットとMRが名前付きで挙げられ、エージェントは必要な詳細を取得するために11件の照会を並列で実行した。レポートは1回のLLM呼び出しで生成され、出力は3セクションからなるMarkdownレポートだった。

Metrics summaryAgentLookup tools3 sprints of metrics - 11 tickets & MRs namedlookup_jira_ticket ×8 (outliers, carry-overs, addition)lookup_merge_request ×3 (the p75 MRs)status histories, lead-time legswrites the three-section reportMetrics summaryAgentLookup tools

照会によってレポートはより詳細になった。サイクルタイムの最悪の外れ値(16.8日)は2つのスプリントにまたがっていたことが分かり、そのマージリクエストは最初のコミットからリリースまで308.7時間(内訳はコーディング116.3時間、レビュー170.0時間、デプロイ22.4時間)を要していた。別のMRは99.7時間のうち99.5時間をレビューに費やしていた。

レポートでは、ボトルネックはデプロイではなくレビューの遅延(中央値58時間、前スプリントの約3倍)であると特定し、議論すべき具体的なチケットやMRを指摘できた。また、リリース数が23から6に減少する一方で変更失敗率が8.7%からゼロに改善したことも指摘し、フローの鈍化が意図的なリスク管理によるものなのか、単に変更がレビュー待ちで滞留しているだけなのかを問いかけた。

実行全体ではLLM呼び出しは2回で、入力トークンは約11,400(うち3分の1は2回目の呼び出しでのキャッシュ読み取り)、出力は約2,200トークンだった。執筆時点のGPT-5.4のAPI価格では、約0.05ドルで、その大半は出力トークンによるものだ。

実践におけるプロンプトエンジニアリング

エンジニアの名前は出さない

初期のプロトタイプでは、エージェントが遅延したチケットについて議論する際にエンジニアを名指しすることがよくあった。非難のないレトロスペクティブの環境を保つため、これを避けるガードレールをプロンプトに追加した。エージェントには、個人のパフォーマンスではなく、チーム全体のトレンドと作業そのものに焦点を当てるよう指示している。

これは決定的なevalでもカバーできた。evalは入力からすべての担当者とMR作成者を抽出し、出力にそれらが含まれていれば失敗とする。プロンプトとevalの組み合わせでこのルールを徹底している。

スキーマもプロンプトの一部である

エージェントはフィールド名に基づいて推論するため、命名は重要だ。初期バージョンでは、Jiraのstatus_categoryフィールドに基づいてチケットのステータスや完了したかどうかを誤認することがあった。チケットスキーマにis_doneという真偽値を追加することで、曖昧なフィールド名や値をモデルに解釈させるのではなく、決定的なロジックとデータ構造で意味をエンコードしてこの問題を解決した。

指示よりも構造が勝る

プロンプトでは、仮説こそが成果物であるにもかかわらず、「hypothesis」という言葉は一度も使われていない。第3セクションは結論ではなく、話し合いのポイントと議論のための問いだ。レトロスペクティブは人間が主導すべきであり、そのことをモデルに覚えさせるのではなく、レポートのフォーマット自体にその思想を埋め込んでいる。

当初のプロンプトでは主要メトリクスを一目で分かるサマリーテーブルとしても求めていたが、モデルは毎回異なるフォーマットでサマリーを生成していた。エージェントの周りにWeb UIを構築することで、サマリーテーブルは生成されたナラティブの隣に置かれる決定的なコンポーネントになった。毎回同じであるべき出力があるなら、LLMに生成させるべきではない。

おわりに

レトロスペクティブエージェントは、私たちのチームにとって小さくも有用なツールであり、エンジニアリングのレトロスペクティブにおける議論や振り返りを促すのに役立っている。プロンプトエンジニアリングとルックアップツールの活用によって、チームのダイナミクスを尊重し、実行可能な洞察に焦点を当てた価値あるAIアシスタントを作れることを示している。

その有用性の大きな部分は、エージェントが推論の拠り所とする信頼できるデータを提供する基盤のメトリクスデータプラットフォームにある。そこでは、どのメトリクスを追跡し、開発ワークフローからどう計算するかを慎重に選ぶ必要がある。次回の記事では、そのプラットフォームがどのように動作し、DORAメトリクスをどのように導出し、それらがどのように連携してエンジニアリングチームを支えているかを深掘りする。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント