新人データサイエンティストがドメイン知識を身につけられたのは、チームで理解を深める「チーム内勉強会」のおかげだった

はじめまして、データサイエンス部の梛木と申します。新卒でテックドクターに入社し、半年が経ちました。学部と修士でデータサイエンスを専攻しておりましたが、ウェアラブルデバイスのデータという長期かつ連続したデータを扱う機会はこれまで少なく、毎日新しいことを学ぶ刺激的な日々を送っています。

このエントリでは、この半年で特に印象深かった経験をみなさまに共有したいと思います。ドメイン知識についてデータサイエンスチームで学ぶ、チーム内勉強会です。

なぜデータサイエンティストにドメイン知識が必要なのか

一般社団法人データサイエンティスト協会は、データサイエンティストをこう定義しています。

データサイエンス力、データエンジニアリング力をベースにデータから価値を創出し、ビジネス課題に答えを出すプロフェッショナル
引用元:次代のデータサイエンティストへ~スキルチェックリスト、タスクリスト更新~

また、この資料ではデータサイエンティストが持つべきスキルについても、①価値創造、②データサイエンス、③データエンジニアリング、④基盤、⑤融合、の5つに大別しています。

この定義に従うと、テックドクターのデータサイエンティストとは

  • データサイエンス力、データエンジニアリング力をベースに
  • ウェアラブルデバイスのデータから価値を創出し
  • 医療(特に注力している製薬・臨床試験)業界の課題に答えを出す

ことのプロフェッショナルであるといえます。

それにあたって重要となってくるものに、課題の存在する業界に関する知識=ドメイン知識があります。
ドメイン知識があってこそ以下のような判断ができるからです。

  • 何が価値として顧客に評価され、お金を払ってもらえるのか
  • なぜそれが価値となるのか
  • どの課題解決方法を選べばより大きな価値が生まれるのか


例えば、外来診療において注意欠如・多動性障害(ADHD)の方の日常生活の客観的な様子を考慮した投薬ができないという課題があったとしましょう。
この課題解決に活かせそうなドメイン知識は、例えば2つあります。

ドメイン知識:
ADHDの方は睡眠障害を併発しやすく睡眠の規則性も乱れやすい
有用性:
ウェアラブルデバイスで睡眠データを取得することで、睡眠の規則性が薬の投与とともにどのくらい変化するかを示すことに価値がある、とわかる

ドメイン知識:
睡眠の規則性に関する研究にてSleep Regularity Index(SRI)という指標が使われている
有用性:
ウェアラブルデバイスで取得した睡眠データでSRIを計算すると薬とともに変化する睡眠の規則性の大きさが伝わりやすくなる

これらにより、データサイエンスのスキルをより大きな価値につなげることができます。

このように、テックドクターのデータサイエンティストとして役割を果たしていくためには、ドメイン知識=課題の存在している医療(特に注力している製薬・臨床試験)業界に関する知識を身につけることがとても重要です。

ドメイン知識を学ぶためのチーム内勉強会

前述の、みなさまに紹介したかった印象深い経験とは、まさにこのドメイン知識についてデータサイエンスチームで学んだチーム内勉強会の取り組みです。

活動内容:
チームとして知識をつけられるよう、持ち回りで疾患分野ごとにGoogleスライドで資料を作成・推敲し、チームメンバーに対して説明を行う

開催頻度:
金曜日の定例ミーティング時に開催

メンバー:
社内のデータサイエンティスト 7名

進行:
前もって割り振られた担当スケジュールとテーマに従って、担当者は事前にスライドを作成しておきます。
当日は最初の30分ほどで担当者がスライドを見せながら説明を行います。
その後、それぞれもっと知りたい箇所や今後の解析で活かせそうなことなどを質問・コメントし、ラフな形で意見交換をします。深い理解を得るため、この意見交換には時間制限を設けません。

取り扱ったテーマ:
各自が担当した疾患領域は以下のとおりで、資料では解剖生理学的な解説 (疾患に関連する体の構造と機能)、測定と診断方法、代表的な疾患、ウェアラブルデバイスのデータを用いた代表的な研究や技術開発の動向を詳細にまとめました。

  • 免疫・膠原病系疾患
  • こころの病気
  • 呼吸器系疾患
  • 感染症
  • 腫瘍性疾患(悪性・良性)
  • 循環器系疾患
  • 神経変性疾患
  • 腎・泌尿器系疾患系疾患
  • 婦人科・生殖器系疾患

感染症に関するスライド

感染症に関するスライド
私が作成した、感染症に関するスライドより抜粋
(※発表内容をイメージしていただくためのサンプルであり、内容の正確性を保証するものではありません。参考資料:ウェアラブルデバイスのデータを活用した米国におけるインフルエンザ様疾患の州レベルでのリアルタイム・サーベイランスの改善:集団ベースでの研究

チーム内勉強会に参加して得られた気づき

勉強会形式のメリット

半年間の取り組みで、キャリアがシニアにあたる人も新卒も、医療業界のバックグラウンドを持つ人もそうでない人も、それぞれが「全員に伝わるように1から学び直し、伝える工夫をする」ことを徹底していたと思います。

その場で答えられない質問があれば調べて後から共有したり、小さな感想でも今後の研究に繋がりそうなことは伝えたり。学びに対して貪欲で、キャリアを重ねても謙虚に勉強を続ける先輩方の姿勢から、データサイエンティストとして業界の知識を学び続けることの重要性を改めて感じました。

また、チーム全体の知識がボトムアップされること、発表者は質問への回答を通して個人で調査する場合以上に知識が深まることなど、勉強会の形で実施したことには多くのメリットがありました。

ドメイン知識を学ぶこと自体のメリット

ドメイン知識を学ぶこと自体からも、4つの気づきを得られました。

まず、医療業界には先人たちの経験が積み重なった、独自の知識体系があるということ。治療法の選択や、測定・診断におけるデータの扱いも歴史が重なったものであるということを知りました。

2つ目は、疾患領域によってウェアラブルデバイスを使った研究の量が異なるということ。ウェアラブルデバイスのデータを用いて課題解決を試みる私たちにとっては、どこに課題があるのかヒントを得るきっかけになりました。

3つ目は、業界へのコロナの影響は計り知れないということ。コロナ前後で研究における業界の流行のようなものが大きく変わっていることを知りました。

4つ目は、解剖生理学の重要性。全ての疾患領域において、体のどんな部分がどんな機能を持っているのか、それがどんな理屈でどんな状態になると問題が起こるのか、解剖生理学を土台に理解を進めることができました。

ドメイン知識が活きる

私が入社してからの半年間でも、ドメイン知識が活きたことが何度もありました。

まず、前述の外来診療におけるADHDの方の課題の例は、自分が実際に研究を行ったときのものです。

他の場面では、データセットに含まれている異常なデータに気がつくことができました。例えば、心拍数のデータに220越えの値が入っていたとします。これは、かなり強度の高い運動をした、若い人しか超えにくい数値です。この知識をもとに、このデータをどのように扱うか考えることができます。

さらに、探索的な解析においてデータの優先度をつけるヒントになりました。例えば…

  • 感染症領域では安静時心拍数が重要な結果と結びつく研究が多い
    →炎症の起こる疾患のウェアラブル研究で、安静時心拍数の変化を中心にデータを見る
  • 月経は周期による皮膚温の変化を引き起こす
    →婦人科・生殖器系疾患のウェアラブル研究で、皮膚温の変化が重要な指標となるかもしれないと見通してデータを見る

全ての項目に時間をかけてデータを見ることができたら良いですが、現実でそのようなわけにはいきません。
ウェアラブルデバイスからは歩数・心拍数・睡眠以外にも呼吸数・SpO2・皮膚温・カロリーなど様々なデータが取れるので、探索的な解析の場合は優先度をつけて作業することが必要になります。その際に、ドメイン知識が活きるのです。

まとめ

チーム内勉強会での経験は、テックドクターのデータサイエンティストとして一歩を踏み出すためにとても重要なものでした。

作成した資料を定期的に読み直したり、人に説明する機会を設けるなど形を工夫して、今後も定期的に学び直す時間を作っていきたいです。

似顔絵
書いた人:梛木

AIはsupersede済みADRに惑わされるのか。175試行で確かめて運用ルールを決めた

こんにちは、テックドクターでバックエンドエンジニアをしている筧です。

AI エージェントを使って開発するようになってから、ADR(Architecture Decision Record)をこまめに残すようになりました。文書にして渡すことで、エージェントに設計判断を守らせることが狙いです。

ただ、ひとつ疑問に思っていることもあります。
設計判断は、後から覆ることがありますよね。その場合、古い ADR に「Superseded by 〜(〜に置き換えられた)」という印を付けて新しい ADR を起こすのが一般的な流儀ですが、AI はこの置き換え(supersede)をちゃんと追ってくれるのでしょうか。古い決定がまだ有効だと思い込み、廃止済みの設計でコードを書いたりしないのでしょうか。

今回のエントリでは、それを実際に検証してみました。
社内の実リポジトリを対象として Claude 系4モデルと OpenAI Codex の両方で検証、結果を踏まえて ADR の運用ルールを作り、チームで v0.1 として採択したところまで含めてご紹介します。

社内のADR運用は1年でバラバラに独自進化

検証の前に、まず社内での ADR の運用状況を確認してみましょう。
社内で ADR を持つリポジトリは 8 つ。ADR の数は合計 119 本でした。ほとんどがこの1年で書かれたもので、AI エージェントとの開発が本格化した時期と重なります。

そして 8 リポジトリを比較すると、それぞれバラバラの書き方で ADR が独自進化していることもわかりました。

観点 社内の実態
置き場所 docs/adr/ 配下、リポジトリ直下の adr/、エージェント設定ディレクトリ配下の3流派
採番 3桁連番、4桁連番、プレフィックス付き連番、タイムスタンプの4方式
ステータスの語彙 採用 / Accepted / accepted / 承認 / 承認済み の5表記
ルールの明文化 詳細な規約を持つリポジトリから、ルール文書なしまで混在

また書き方の不統一以外にも、運用上いくつか気になっている点もあります。

  • ステータス管理。提案中の ADR を PR で出し、マージされた後に「承認済みに変えるだけの PR」を出す運用に違和感がある。
  • 仕様を改訂したときの記録方法。supersede で新しい ADR を起こすのか、部分改訂で済ませるのか。そもそも AI は改訂の時系列を追えるのか?
  • 粒度。障害対応メモに近いものから基盤方針まで、書かれる決定の大きさがばらばら
  • フォーマット統一。チームで統一したい気持ちと、読めればそれでいいという気持ちの間で揺れている
  • 量とトークン。ADR 同士が相互参照するので、AI に読ませる量が膨らむ

この中で、特に気になっていたのが改訂でした。どういった運用がAIにとって追いやすいのかは、検証してみる必要がありそうです。

実験: AIはsupersede済みADRに騙されるのか

では実際に、実験してみましょう。実験方法はこのように設計しました。

素材:
社内の実リポジトリ3つ、ADR 計70本を使用します。
3つのリポジトリは supersede の記法が異なります。

  • ステータス行に「Superseded by 〜」と書き、後継側にもメタデータ行を持たせ、索引(全 ADR のタイトル・ステータス・置き換え関係を表として並べたファイル。index.md)に取り消し線を引く
  • 置き換えられた側の冒頭に撤回理由を数行書き足し、新旧を双方向でリンクする
  • ステータス行は簡素にして、索引の関係列で「置き換え / 部分置き換え(節単位)」を管理する

質問:
AI エージェントに対して、仕様に関する7つの質問をしました。
質問には現行の方針を答えさせるものと、方針の変遷を説明させるものを混ぜました。

条件:
3通りの条件を用意し、それぞれ実験を行います。

  • ADR をそのまま渡すもの
  • 索引も含めて渡すもの
  • (対照実験として)supersede の印をすべて機械的に除去するもの
  • (対照条件は、ステータスの更新を忘れたリポジトリを模してもいます)

実験対象:
回答するのは実験意図を知らされていない素の AI エージェントです。

  • Claude で4モデル(fable-5、Opus 4.8、Sonnet 5、Haiku 4.5)各条件2試行ずつ
  • 同モデルに対する追試
  • OpenAI Codex 上の2モデル(gpt-5.4 と gpt-5.4-mini)で縮小版

追試では、「現行の方針はどれか」という調査型ではなく、「編集途中の状態を失わないように下書き保存を追加したい。どこで保持すべきか」のような実装相談の形で、かつ廃止済みの設計の側に誘導する言葉を仕込んで質問しました。より実務上のユースケースに近い形で検証を行うためです。

試行回数は合計で、Claude には175試行、Codex には50試行となります。

※採点は正解を知る筆者による非盲検で、実験の実行と採点には Claude Code の支援を使っています。被験モデルの答案を同系統の AI が手伝って採点する構図になるため、先に基準を固定して裁量を狭めました。

結果:AIエージェントは騙されなかった

実際に実験をしてみた結果……。

予想外の結果が出ました。明示的に「廃止済み」の仕様を現行として答えた試行は、4モデル175試行を通じてゼロでした。

さらに意外なことに、fable-5 では、supersede の印をすべて除去した対照実験でさえも(!)誤答が出ませんでした。後継 ADR の本文に「前の方針をなぜやめたのか」という経緯が書かれており、そこから時系列を読み取ったようです。くわえて、ファイル名の連番や、後続 ADR の参照関係も手がかりになっていました。
撤回理由を本文に書き残す文化は、保険として大いに機能することが分かりました。

そして誘導を仕込んだ追試でも、多くの試行は釣られず、むしろ「その方向は撤回済みの設計への逆戻りになる」と経緯を引用して警告してきました。AI を釣るつもりが、逆にたしなめられてしまいました……。

索引の有無による影響は、正答率、探索コストのどちらにも見受けられませんでした。 エージェントは、索引をもっぱら本文で得た結論の確認と補強に使っていました。

コスト面では、fable-5 の本試行の探索コストは、1回の回答でツール呼び出し3〜10回、数万トークン規模でした。

また Codex でも結果は同じでした。 廃止済みを現行と答えた回答はゼロ、減点となった12件はすべて例の ADR ペアが原因でした。
ただし、supersede の印を除去した対照実験でも正解できたのは Claudeの上位モデルだけで、Codex の2モデルはうまくいきませんでした。

唯一の落とし穴は……運用不備

一方で、Claude 系の175試行のうち、13件で減点が発生しました。原因をたどると、すべて同一の新旧 ADR のペアに起因していました。

その ADR ペアは、

  • 旧 ADR が Accepted のまま
  • 新 ADR は実装済みなのに Proposed のまま
  • 新 ADR の関連 ADR にも、置き換え元として旧 ADR が示されていない

という状態になっていました。

新 ADR よりも後の関連 ADR を見ると新 ADR の仕様が前提になっていることが分かるのですが、ステータスを信じると古い方が現行に見えます(実験用に仕込んだ罠ではなく、人間がたまたま改訂を忘れていた、実在する ADR でした)。

これに対して、上位モデルは「後続の ADR 群が新しい方を前提にしている。更新漏れではないか」と自発的に警告してきました。一方でHaiku はステータスをもとに古い仕様を現行と答えました。軽量なモデルほど、ステータスの正確さが重要になるようです。

また、質問の仕方も回答に影響するようでした。調査型の質問では正解できる Claude の上位モデルも、「できるだけ早く反映したい」といった相談の仕方をすると間違った回答をします。

検証の結果は以上です。AI の読解力は、思っていたより正確でした。むしろ問題は人間側の運用の方で、ステータスの更新を忘れないこと、その重要性が再確認できました。

※試行数は小規模で、かつCodex 側は縮小版のため、モデル差・基盤差が完全に検証できていない可能性はあります。また今回の検証は撤回理由をしっかり書く文化のリポジトリなので、そうでない ADR 群では結果が変わる可能性があります。

実験結果をもとに運用ルールを整備

その後、上記の検証の結果を踏まえて、新たな運用ルールを定めることにしました。
下記が、筆者が暫定案として起票し、チームレビューを経て v0.1 として採択された新たなルールです。

  1. マージ=採用とし、ステータスを変えるだけの PR をやめる。
    最初に挙げた「『承認済みに変えるだけの PR』を出す運用への違和感」が解消されるとともに、誤答の原因となった「提案中のままマージされ、承認済みへの変更を忘れる」が構造的に起きなくなります。
  2. 方針変更の宣言は新旧双方に書き、理由を本文に書く。
    万が一ステータス更新忘れがあった場合でも、変更経緯が書かれていれば保険になることが、実験で検証できました。
  3. 粒度は既存ガイドの基準を借りる
    この記事が良いガイドになりました。中でも「現場で遭遇する罠と対策」のパートに書かれたADR に書く基準、「複数コンポーネントに影響するか」「6ヶ月後に説明を求められるか」「複数の選択肢を検討したか」は非常に参考になりました。
  4. 索引を整備する
    索引の有無で正答率は大きく変わりませんでしたが、エージェントは結論の確認に使っていました。人間の一覧性のためにも整備します。
  5. 置き場所と採番は統一しない、と決める
    連番は複数人運用時に衝突しますし、タイムスタンプはあとで統合したときに時系列が失われます。統一したい気持ちはありつつも、いったん「読めればよい」としました。
  6. 統一しないのは外側だけで、中身は揃える
    新規 ADR は Status、Context、Decision、Consequences に相当する項目を持ち、main 上のステータスは Accepted、Rejected、Deprecated、Superseded by リンク、Amended by リンクの5つのいずれか1つに固定します。
    また、Proposed は PR 中にだけ存在可能とします。これで実験中に問題となったステータス更新漏れも、今後は CI で機械的に検出できるようになります。

今後は、AI レビューが差分に ADR の追加や変更を見つけた際、このルールへの準拠を自動確認するフローも実装していく予定です。
また、コードと ADR の乖離を継続監視していく仕組みも検討しています。エージェントの定期実行により検出し、修正を提案するところまでの実装を検討中です。

決めなかったこと

まだ解決していない問題もあります。

  • 既存119本への遡及適用(新規からか、棚卸しするか。チームで相談中)
  • プロダクトをまたぐ決定の置き場所と、エージェントへの渡し方
  • 古い ADR の統合。他リポジトリからの参照があるため安易にできません
  • コードと ADR の乖離の継続監視。エージェントの定期実行で検出する案を、検出と提案までという線引きで検討中です

おわりに

少なくとも今回の実験の範囲では、ステータスと撤回の経緯を正しく書いてさえいれば、AI は設計の変遷を追ってくれました。

実験を通じて見えてきたのは、AI の読解力というよりも、むしろ ADR の運用という組織側の課題でした。
みなさんのリポジトリにも、Accepted のまま残った古い決定が眠っていないでしょうか。

今回の v0.1 は、あくまで改善の第一歩です。運用してまた新たな課題が見つかれば更なる改訂を行い、ADR がチームの設計判断を支える基盤になるよう、今後も手入れを続けていきます。

似顔絵
書いた人:筧

Cloud Run × FastAPI でアプリログに trace を付ける(google-cloud-logging だけでは足りなかった話)

テックドクターでエンジニアをしている星野です。

弊社では Python と FastAPI を使うことが多く、アプリケーションの実行環境として Cloud Run を利用しています。

Cloud Run で障害調査をしていると、たくさんのログの中でアプリケーションのログが混ざってしまい、どの HTTP リクエストで発生したものなのか分からず苦労することがあります。

Cloud Run のリクエストログは自動でリクエストごとに出力しますが、アプリケーションのログは標準ではリクエストログと紐づきません。

この記事ではその解決策として、

  • アプリケーションのログとリクエストを紐づけるには trace が使えること
  • ただしFastAPI では google-cloud-logging を使うだけでは trace が付かない。OpenTelemetry を追加すれば紐づけ可能であること

の2点を紹介します。

trace でログを紐づける方法

Cloud Run はリクエストを受け取ると、traceparent ヘッダを付与し、その trace ID をリクエストログに記録します。

アプリケーションのログにも同じ trace ID を付与すれば、Cloud Logging でコンテナログとリクエストログが関連付けられ、1 リクエスト分のログをまとめて確認できます。

trace ID はリクエストのヘッダから自前で取り出すこともできますが、google-cloud-logging を使えばライブラリ側で付与してくれます。

google-cloud-logging を試す

依存パッケージの追加
fastapi==0.115.6
uvicorn[standard]==0.34.0
google-cloud-logging==3.11.3
setup_logging() を呼ぶ

基本的な書き方は次の通りです

import logging
import google.cloud.logging

client = google.cloud.logging.Client()
client.setup_logging()

logger = logging.getLogger(__name__)

setup_logging() は Python 標準の logging に Cloud Logging 用のハンドラを追加します。出力したログは Cloud Logging で見られるようになります。
こうすることにより、Flask の場合は、setup_logging() を呼ぶだけでアプリケーションのログがリクエストログと同じ trace に紐づきます。

ただし……FastAPI では trace が付かない

次に、FastAPI でも同じように書いてみます。

import logging

import google.cloud.logging
from fastapi import FastAPI

client = google.cloud.logging.Client()
client.setup_logging()
logger = logging.getLogger(__name__)

app = FastAPI()


@app.get("/log")
def emit_logs():
    logger.info("info level log from /log")
    logger.warning("warning level log from /log")
    logger.error("error level log from /log")
    return {"status": "ok"}

これを Cloud Run にデプロイします。

gcloud run deploy cloud-logging-trace-demo \
  --source . --region asia-northeast1 --allow-unauthenticated

デプロイしたエンドポイントに /log でリクエストを送り、そのリクエストの trace で Cloud Logging を検索したところ、アプリケーションのログには trace が付いていませんでした。

画面キャプチャ

リクエストログ(GET /log)の下に、アプリケーションのログがぶら下がっていません。リクエストログには trace が付いていますが、アプリケーションのログには付いていないため、リクエストログとアプリケーションのログは紐づきませんでした。

公式ドキュメント Integration with Python Web Frameworks によると、google-cloud-logging がリクエストから trace を自動取得できるのは FlaskDjango のみで、FastAPI はサポート対象に含まれていなかったためです。

trace の取得元は次のいずれかです。

  • OpenTelemetry のアクティブな span
  • Flask または Django のリクエストコンテキスト

FastAPI はどちらにも該当しないため、setup_logging() だけではアプリケーションのログに trace が付きませんでした。

逆に言えば、OpenTelemetry の span を用意すれば trace を取得できます。次は OpenTelemetry で span を生成してみます。

OpenTelemetry で trace を付与する

FastAPI でリクエストごとに OpenTelemetry の span を生成します。setup_logging() はその span から trace を取得します。

依存パッケージの追加

OpenTelemetry 関連を追加します。

opentelemetry-sdk==1.29.0
opentelemetry-instrumentation-fastapi==0.50b0
OpenTelemetry の設定

FastAPIInstrumentor を使って、リクエストごとに span を生成します。traceparent ヘッダはデフォルトで読み込まれるため、追加の設定は不要です。

import logging

import google.cloud.logging
from fastapi import FastAPI
from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.sdk.trace import TracerProvider

trace.set_tracer_provider(TracerProvider())

# google-cloud-logging は OpenTelemetry のアクティブな span から trace を取得する
client = google.cloud.logging.Client()
client.setup_logging()
logger = logging.getLogger(__name__)

app = FastAPI()
FastAPIInstrumentor.instrument_app(app)  # リクエストごとに span を生成する


@app.get("/log")
def emit_logs():
    logger.info("info level log from /log")
    logger.warning("warning level log from /log")
    logger.error("error level log from /log")
    return {"status": "ok"}

 

検証結果

この状態で再デプロイし、/log にリクエストを送ったところ、アプリケーションのログとリクエストログが同じ trace に紐づきました。

画面キャプチャ

リクエストログ(GET /log)の下に、アプリケーションのログがぶら下がって表示されています。アプリケーションのログを JSON で確認すると、trace / spanId / traceSampled が付与されています。

{
  "logName": "projects/<project>/logs/run.googleapis.com%2Fstderr",
  "severity": "INFO",
  "trace": "projects/<project>/traces/c744c3b23c8f0b9acbff17b154197e73",
  "spanId": "e3eccc394a71c4d7",
  "traceSampled": true,
  "labels": { "python_logger": "app.main" }
}

同じ trace のリクエストログは以下の通りです。

{
  "logName": "projects/<project>/logs/run.googleapis.com%2Frequests",
  "httpRequest": { "status": 200 },
  "trace": "projects/<project>/traces/c744c3b23c8f0b9acbff17b154197e73",
  "spanId": "3229e032d02683b5"
}

spanId はアプリケーションのログとリクエストログで異なりますが、trace が一致していれば、リクエストログの下にアプリケーションのログをまとめて表示することができます。

なお今回はログに trace を付けるのが目的のため、span を Cloud Trace に送る exporter は設定していません。Cloud Trace のスパンツリーで span の親子関係まで見たい場合は、別途 exporter の設定が必要です。

trace を自前で付与する場合

依存パッケージを増やしたくない場合は、ライブラリを使わず、リクエストの traceparent ヘッダから trace ID を取り出し、ログの logging.googleapis.com/trace フィールドに設定して出力する方法もあります。traceparent がない環境では、フォールバックとして X-Cloud-Trace-Context ヘッダから取り出します。

# traceparent: 00-<trace-id>-<span-id>-<flags>
traceparent = request.headers.get("traceparent")
if traceparent:
    trace_id = traceparent.split("-")[1]
else:
    # フォールバック: X-Cloud-Trace-Context は <trace-id>/<span-id>;o=<flags>
    trace_id = request.headers.get("X-Cloud-Trace-Context", "").split("/")[0]

log_entry = {
    "message": "manual trace example",
    "logging.googleapis.com/trace": f"projects/{project}/traces/{trace_id}",
}
print(json.dumps(log_entry))  # 構造化ログとして出力する

 

まとめ

Flask や Django では setup_logging() だけで trace が付きますが、FastAPI では OpenTelemetry の追加が必要になります。

リクエストログとアプリケーションのログが同じ trace でまとまると、原因のリクエストを見つけやすくなります。

Cloud Run で FastAPI を動かしていてまだ設定されていないなら、今回のように google-cloud-logging と OpenTelemetry を組み合わせる構成を試すのがよさそうです。

補足として、setup_logging() は実行環境を自動判定するため、ローカルで動かすとログはターミナルに出力されず Cloud Logging API へ直接送信しようとします。送信には Application Default Credentials(ADC)による認証が必要で、認証がないとエラーになります。ローカルでターミナルに出力したい場合は、setup_logging() を呼ばないようにする必要があります。

データサイエンスチームのAI活用進化記 (2026年5月版)── コピペから一気通貫へ

データサイエンス(DS)チームのマネジメントを担当している深見です。

この数ヶ月で、データサイエンティストの仕事のやり方が大きく変わり始めました。かつては「エンジニアが中心」と思っていたAIの普及による変化が、データサイエンティストを含む幅広い職種に及んでいると感じられます。

今回は、テックドクターの社内研究業務におけるAIツールの活用について、導入の経緯・業務の変化・課題と今後の展望をご紹介します。

DSチームでも、ChatGPTやGeminiの導入に始まり、最近ではClaudeを日常的に使うようになっています。つい先日まで「研究へのAI活用はまだ先の話」と思っていたのが嘘のように、今ではなくてはならない存在になっています。

Claudeの導入で何が変わったか

詳細は後述しますが、Claude導入後に起きた変化を先にお伝えします。

  • 解析用のコードを自分で書かなくてもよくなった
  • 「ちょっと試してみたい」ことが、すぐに・簡単に実現できるようになった
  • 特徴量の選定やパラメータの設定など、無数のパターンを網羅的に探索できるようになった
  • HTMLファイルでの結果出力により、わかりやすさ・インタラクティブ性が向上した

これらの積み重ねにより、 新たな技術の導入や基礎研究に充てられる時間が増加しました。基礎研究から面白い成果が生まれるなど、正のスパイラルが回り始めています。

テックドクターにおけるDSチームの研究業務

変化の背景を理解していただくため、まずテックドクターのDSチームが担う業務内容を紹介します。

  • 論文調査・仮説立案
  • データ前処理・解析
  • QC(Quality Control)
  • 結果報告 
  • 学会・論文発表

テックドクターはウェアラブルデータの医療活用という新しい分野に挑戦しており、研究だけでなく、社内外へのわかりやすい説明・アピールも重要な業務です。幅広い業務をこなす必要があるため、AIとの連携による効率化の恩恵を受けやすい環境でもありました。

AI活用の変遷

DSチームがどのようにAIツールを使いこなしてきたか、3つのフェーズに分けて振り返ります。

フェーズ1(初期):コードをコピペする時代

最初はChatGPTやGeminiを使い、可視化や解析のためのコードを生成してColabに貼り付けて実行していました。エラーが出ればエラー内容を転記してコードを再生成・再貼り付けという繰り返し。自分でゼロからコードを書くよりは多少早い、という程度でした。会話と実行環境が分断されているため、コンテキストを保ちながら作業することが難しく、生産性の向上は限定的でした。

画面キャプチャ
ChatGPTによるコード作成

フェーズ2(中期):環境に組み込む

転機となったのは、Google Colaboratory(Colab)に搭載されたGeminiがある程度実用的に使えるようになったことです。解析方針の立案・コード実行・結果考察まで、一通りの作業を同じ環境の中で進められるようになりました。特に結果考察の文章まで生成できるようになったのは大きな変化だったと思います。正確性については検証が必要とはいえ、これまで計画立案や解析以上に時間を取られることも多かった部分を一定サポートしてもらえるようになったのは非常に助かりました。

こうしてデータの把握や簡単な解析であれば任せられるレベルになりましたが、一方で一連のタスクの途中で頻繁にフリーズしたり、エラーを解消できず同じ処理を繰り返すなど、まだ「任せ切る」には実力不足という場面も多くありました。

画面キャプチャ
Colab with Gemini

フェーズ3(現在):一気通貫の時代へ

現在はClaude Codeを中心に活用しており、業務の幅と質が大きく変わりました。具体的には以下のような使い方をしています。

  • BigQueryからのデータ抽出の半自動化
    • BQとの通信、基本SQLスクリプトなどをskillとして用意しagentに実行させる
  • データ加工 → 解析 → 報告資料作成までを一貫して依頼
    • 解析指示書を作成して実行させる 
  • QC(データ品質チェック)の自動化
    • チェック項目や外れ値条件などをskillとして用意してagentに実行させる
  • 論文作成の支援
    • 関連分野の論文検索や要約、初期文面案の作成を行い人がチェックする

以前は「コードを書く→実行する→結果を整形する→資料を作る」という各ステップを人が繋いでいましたが、今はそのフロー全体をAIと協働して進められるようになりました。

QC作業においても、人手でチェックしていた内容をAIに任せられるようになり、見逃しが減るとともに網羅性が高まっています。多くのメンバーはデータの品質チェックまでを含めた「解析指示書」を最初に作成し、それを実行させるような使い方をしています。

画面キャプチャ
Claudeによる自動QC:1,000件を超えるチェックでもミスを確実に検出

大きく変わったところ

フェーズ3を迎えて特に大きく変わったと感じる3つの点について、詳しくご紹介します。

研究業務の効率化

最も大きな変化は、解析完了までの時間が大幅に短縮されたことです。

一例としては、 Claude導入前は特徴量ごとに細かい規定をしたい場合はそれに対応したPythonコードを自分で書く必要がありました。小さなミスや抜け漏れが発生しやすく、QCにも多くの時間がかかっていました。こういった非効率をClaudeは解決してくれました。

また、関連する文献の調査で知り得た解析手法などのコードの実装も、ある程度任せられるようになりました。論文などで見かけたアルゴリズムの実装は、ひとまず動く状態のコードを作成するだけでも時間がかかることが多かったですが、この時間が大幅に短縮されました。

こういった時間が大幅に削減された結果、「やってみたいが時間がかかる」として見送っていたアイデアを、隙間時間に気軽に試せるようになりました。大量の組み合わせを網羅的に探索することも現実的になり、解析の深さ・幅が広がっています。

アウトプットの変化:スライドからHTMLファイルへ

地味ながら効いているのが、アウトプット形式の変化です。従来はスライドでの結果共有が主流でしたが、現在はインタラクティブなHTMLファイルでの共有が増えています。

HTMLファイルであれば、グラフの表示範囲を変更したり、表でフィルタリングをかけたりがブラウザ上でインタラクティブに可能です。これまでは解析者が代表的なものを選んでスライドにまとめるか、大量の画像をAppendixとして貼り付けるしかありませんでしたが、閲覧者がその場で見たいものだけを選んで確認できるようになりました。結果として報告の「伝わりやすさ」が大幅に向上しています。

画面キャプチャ
これまでのスライドイメージ
HTMLによるアウトプットイメージ。プルダウンやフィルタリングによる動的な表示が可能に。

分析以外の業務への展開

Claudeの活用は解析業務にとどまりません。ドキュメント整備・社内外への説明資料の作成など、これまでDSが時間を取られていた周辺業務にも活用が広がりました。作成できるコンテンツの幅が広がったことで、ウェアラブルデータの医療活用という専門性の高い内容を、より多くのステークホルダーにわかりやすく届けられるようになっています。

画面キャプチャ
理解促進用HTMLコンテンツ:特徴量カタログ

課題

活用が進む一方で、課題も見えてきています。

  • 成果物のクオリティチェックは依然として人が必須
  • 論文・ドキュメントには厳密な確認が不可欠
    • 架空の引用やロジックのすり替えが頻発する
  • 大量に生産される結果に対して、人のレビューが追いつかない

AIは優秀な補助者ではありますが、すべてを任せられる状態にはまだありません。特に機械学習モデルの構築では、データリークを発生させてでも結果をよく見せようとするなど、指示の意図を汲みすぎる傾向が見られます。Claudeだけに頼るのではなく、GeminiなどほかのAIによる相互チェックの導入も検討しています。

まとめ

冒頭に書いたことの繰り返しになりますが、この数ヶ月で、データサイエンティストの仕事のやり方は本当に大きく変わりました。
特に感じるのは、ジュニアとシニアの差が開いているという点です。AIは知識・経験の差を補うどころか、もともと「できる人」がさらに多くのことを実現できる道具になっています。また、意欲の差も結果に直結するようになりました。これまでは「時間がかかる」「学習コストが高い」として見送っていたアイデアが、隙間時間で実行できるようになったからです。

その結果、「これをやってみたい」「こうしてみたらどうだろう」という意志・意欲のある人がどんどん成果を出す一方、言われたことだけをこなしている人は相対的に埋もれていく状況が生まれています。

AIは道具です。その道具を最大限に活かすのは、使い手の好奇心と主体性だと改めて感じています。

似顔絵
書いた人:深見

AI時代に、チケットというUIはまだ最適なのか

バックエンドエンジニアの伊藤です。

最近、AIコーディングエージェントを使った開発が当たり前になる中で、改めてチケットには何を書くのが最適なんだろう、そもそもチケットが生まれた経緯・役割はなんだったのだろう、AI時代にはチケットはどう変わるのだろう、ということを考えています。

ここ1、 2年で、AIによってコードを書く速度が格段に速くなってきました。簡単な修正であれば、実装そのものは数分で終わります。バグの再現条件を渡し、関連しそうなファイルを読ませ、修正案を出させ、テストまで書かせることもできます。

一方で、その作業をチケットに起こし、背景を書き、スコープを書き、受け入れ条件を書き、担当者を設定し、ステータスを動かす時間の方が重く感じることがありました。

ここで疑問が生まれます。

チケットは、AI時代のソフトウェア開発においても、最適なインターフェースといえるだろうか?

本稿では、チケットというUIそのものについて再考します。

本記事の概略

本記事の概略を先に書いておきます。

  • チケットは、実装が遅く、人間同士のハンドオフ(人から人への作業の引き渡し)が多く、文脈を一箇所に圧縮して渡す必要があった時代に適したインターフェース
  • AIによって実装、分解、レビュー補助、テスト生成が速くなると、チケットにすべてを詰め込むモデルは、少しずつ重くなっていく。
  • 短期的にはチケットの構造化が重要になるが、中長期では、チケットは開発の中心的なUIではなく、意図や証跡を見るための一つのビューになっていく
  • これから必要なのは、チケットだけを中心に置く開発ではなく、意図(Intent)を意識した開発。Ticket-firstではなく Intent-aware の開発。

チケットは何を解決していたのか

チケット、Issue、Bug report、Work item。呼び方はいろいろありますが、起源をたどると、もともとは未解決の不具合や変更要求を追跡するための仕組みでした。

たとえば Bugzilla は、自らを defect-tracking system または bug-tracking system と説明しています。Bugzilla の説明では、開発チームが未解決の bugs、problems、issues、enhancement requests、change requests を追跡するための仕組みとされています*1

しかし、チケットは単なるバグ管理表にとどまりませんでした。現代のIssue Tracking Systemは、requirements、development tasks、maintenance itemsなど、ソフトウェア開発に関わる複数の成果物を管理する基盤として使われています*2

つまりチケットは、以下のような役割を持っています。

役割 内容
入口 要望、バグ、問い合わせ、改善案を受け取る
キュー 後でやるかもしれない作業を貯める
優先順位づけ 何を先にやるべきかを決める
ハンドオフ PdM、エンジニア、QA、CS(カスタマーサクセス/カスタマーサポート)の間で仕事を渡す
責任の明確化 誰が担当するかを決める
進捗管理 Todo / Doing / Review / Done を可視化する
合意形成 仕様やスコープの認識を揃える
検証条件 何をもって完了とするかを定義する
証跡 なぜその変更をしたかを後から追えるようにする
メトリクス 件数、滞留、リードタイム、担当負荷を見る

言い換えると、チケットは「作業にまつわる不確実性を、一つのカードにまとめたもの」とも言えます。

チケットは文脈圧縮インターフェースだった

顧客の不満、PMの判断、エンジニアへの作業依頼、仕様のメモ、優先度、担当者、受け入れ条件、関連 PR、QA 結果……などなど、関係者ごとに散らばる前提や判断をチケットという一枚のカードに圧縮する。この働きは、「文脈圧縮」と呼ぶことができそうです。

チケットが多様な文脈を一枚のカードに圧縮することで、チームは非同期に仕事を進めることができました。

一方で、これまでチケットが有効だった背景には、いくつかの前提があったと考えています。

まず、実装には時間がかかりました。そのため、事前に何を作るかを整理し、優先順位をつけ、担当者を決める価値がありました。

次に、仕事は人間が順番に処理していました。そのため、チケットはキューとして機能しました。

さらに、開発には多くのハンドオフがありました。PMが背景を説明し、エンジニアが仕様を解釈し、QAが期待挙動を確認し、CSが顧客影響を追います。その間で文脈が失われないように、一枚のチケットに情報を圧縮して渡す必要がありました。

つまりチケットは、実装が遅く、ハンドオフが多く、非同期コミュニケーションにコストがかかる時代に適したインターフェースだった、と整理できます。

少し強く言うと、チケットは「実装が遅い時代の文脈圧縮フォーマット」とも言い換えられるかもしれません。しかし、AIコーディングエージェントが高速でコードを生成していく今、その前提はいま崩れつつあります。

※もちろん、規制対応、監査、大規模分散開発のように、チケットが提供してきた他の機能、たとえば証跡や責任の所在の明示は、AI時代でも変わらず必要になる場面があります。チケットの存在意義そのものを否定したいわけではありません。
これからのチケットの在り方については後半で触れたいと思います。

チケット以外の思想から学べること

チケット中心の開発を相対化する視点は私だけの意見ではなく、すでにいくつかの言説が存在しています。

ひとつはShape Upです。Basecampは、未着手アイデアを大量に抱えるバックログを「大きな重荷」だと表現しています*3。古いアイデアを定期的に整理することに時間が取られ、現在のプロジェクトを進める力が削がれる。だからこそ、少数のwell-shaped、リスクを下げた選択肢にbetするべき、という考え方です。

もうひとつはSciitという研究です。Issue trackerとSCM(ソースコード管理)が分離していると、開発者が両方の情報を手作業で整合させる必要があり、手間が大きいと指摘しています*4。Sciit はそのうえで、issueをSCMの中で(コードと同等に扱う)構成要素として置き、進捗や担当者をリポジトリの状態から推測できるようにする方向を示しています。

そしてGitHubのSpec Kitがあります。Spec Kitは、Spec-driven developmentという方法論を実装するためのツールキットです*5。これまでは「コードが正で、仕様は足場のようなもの」だったが、Spec Kitではむしろ「仕様が実装を生む中心的な成果物になる」という整理が示されています。Microsoft for Developersのブログでも、AI エージェントに正しい出力を出させるためには、まず良い文脈(context)が必要であり、決めないまま進めると codebase が de-facto specification になってしまう、と指摘されています*6

これらに共通するのは、チケットを保存形式・操作 UI・実行単位として一枚にまとめる前提を疑う動きだと考えます。バックログを軽くする、issueをSCMに埋め込む、仕様を実装の中心に置く。それぞれアプローチは違いますが、チケットが暗黙に担ってきた多くの責務を、別のレイヤーに分けていく方向性で重なっています。

チケットが担っていた役割を分解する

つまり、すべての情報をチケットが持ち続ける必要はないのかもしれません。
改めて、チケットがこれまで担ってきた役割を、AI時代に合う形で考え直してみましょう。

役割 従来の置き場 AI時代の移行先(候補)
入口
・要望
チケット ドキュメント、対話、フィードバック
背景
・意図
チケット 設計ドキュメント、ADR
仕様
・制約
チケット 実行可能な仕様(executable spec)、テスト
作業の実行 チケット AI エージェントが動的に生成する作業単位、PR
進捗管理 チケット PR、commit、test、deploy、AI エージェントのログから自動観測
検証 チケット テスト、CI、合成テスト、AI レビュー
証跡 チケット PR、commit、AI エージェントの実行履歴
振り返り チケット メトリクス、ダッシュボード
決定 チケット ADR、設計ドキュメント

具体的な移行先(情報の管理方法)については後で考えたいと思います。

ここでは一旦、役割の方に注目してください。これらの役割に共通するのは、作業そのものではなく、その背後にある「何を実現したいのか」という意図と、それを支える判断・制約・検証だった、と整理できます。チケットが守っていた中核は、作業(Task)よりも、その背後にある意図(Intent)だったとも言い換えられるのではないでしょうか。

AI時代のソフトウェア開発では、この「意図(Intent)」がより重要になってきていると感じます。

AIによって変わったこと

AI時代のソフトウェア開発では、単にコードを書くだけではなく、調査、分解、実装、テスト生成、レビュー補助、要約、トリアージ。ソフトウェア開発の周辺工程全体にAIが入り始めています。

象徴的な発信もあります。Linear は、2026 年 3 月 24 日にマーケティング上のメッセージとして "Issue tracking is dead." を打ち出しました*7。Linear によれば、従来のIssue trackingはPMが作業をスコープし、エンジニアが後から拾うハンドオフモデルのために作られたものだったとされています。今後のシステムは、ハンドオフではなく、文脈(context)とAIエージェント(agents)を中心に設計される、というのがLinearの主張です。

DORAの2025年の報告書も、AIは組織の既存の強みと弱みを増幅するものであり、AI導入による効果はツール単体ではなく組織システム全体に依存すると指摘しています*8。同報告書は、速度向上が下流の不安定さを露わにする、内部プラットフォーム品質がAI価値を決定づける、といった具体的な論点も提示しています。

ここから読み取れるのは、AI時代のボトルネックが、実装そのものから、意図の明確化、検証、責任分界へと移りつつある可能性です。
何を作るべきか、どこまで作るべきか、何を守るべきか、どうなれば正しいと言えるのか。そういった「意図(Intent)」こそが、より重要になっていく。このことはここ1, 2年で急速に共通認識になりつつあります。

なぜIntentが重要なのか

ここでいうIntentは、単なる目的文ではありません。

「なぜこれをやるのか」「何を満たせば成功か」「どこまでやれば終わりか」「どの制約を守るのか」「どんな副作用を避けたいのか」これらをまとめた、開発の起点です。

作業(Task)と意図(Intent)の違いを下記の表にまとめました。

Task Intent
粒度 作業単位 目的単位
寿命 完了したら閉じる 達成後も参照されることが多い
多い 少ない
主な担い手 AI エージェント 人間
役割 実行 判断・検証

AI時代にIntentの重要性が増すと考えられる理由は、AIが作業を高速・大量に実行できる場面が増えてきたからです。

人間の開発者であれば、曖昧なチケットでも文脈から意図を補完して、「たぶんこういうことだろう」と判断できます。AIも確認はできますが、人間より速く・大量にコードを生成できるため、曖昧な指示を誤って解釈してしまった場合に積み上がる量が桁違いに大きくなります。

そのため、AI時代には「実装できること」よりも、「意図に沿って実装されていること」の方が重要になっていくと考えています。

Taskは完了すれば閉じます。しかしIntentは、作業が完了した後も、後続の意思決定で参照されることが多い情報です。「なぜこれを作ったのか」「制約は何だったか」「どうなれば成功か」を、後から検証・参照できることが重要です。

この考え方に立つと、開発の中心にあるのが必ずしもチケットである必要はなくなります。

むしろ、これまでどおりチケットを中心に扱うと、Intentはチケット内に閉じ込められることになります。こういった方法では、複数のチケットやPRが同じIntentを参照する場面で、参照の起点がチケットの中に隠れてしまいます。また、チケットが閉じた後も生き続ける情報の追跡もしにくくなる問題もあります。

では、Intentを軸にすると、具体的にどういう情報の置き方になるのか。次節で見ていきます。

Intentを軸に、開発情報をつなげて考える

本稿では、大それた新しいシステムや大きな方法論を提案したいわけではありません。

たとえば、ある改善について次のようなIntentがあったとします。

Intent:
ユーザーが問い合わせをせずに、自分で退会手続きを完了できるようにする

この Intent の周辺には、次のような情報が紐づきます。

例えばこういった形で必要な情報を管理可能です。

  • 背景や意思決定はNotionや設計ドキュメントに置けばよい
  • 作業単位や担当はLinearやGitHub Issuesに置けばよい
  • 実装の証跡はPRに残る
  • 検証結果はCIやテストレポートに残る
  • リリース状況はDeploymentに残る
  • 運用後の変化はメトリクスや外形監視で確認できる

情報を(従来のチケットのように)一箇所に集めてはいません。適切に残されたそれぞれの情報が、どのIntentに紐づいているのか。そのつながりを追えることが重要です。(ここで新しいシステム名や方法論を提案したいわけではありませんが、 便宜的に「Intent Graph的な構造」と呼ぶことはできそうです)

もちろん、これをきれいに整備するのは簡単ではありません。Intentを軸にして、開発に関わる情報をつなげて扱いたい、という思いはありつつも、私たち自身もこの形をきれいに整備できているわけではないのです。現実には、背景はNotionにあり、作業はLinearにあり、実装はGitHubのPRにあり、結果はアプリ上にあり、それらのつながりは人間の記憶に依存していることも多いのが実情です。

実運用上は、Intent IDをどこに置くかという規約と、ツール間のwebhookやリンクの整備が必要になります。メトリクスからIntentへの逆引き(ある数値変動がどのIntentに紐づくか)を機械的に追うのも、現状ではまだ弱い部分です。このあたりの人間の規律と運用コストの部分で課題は残りますが、今後あるべき姿としてはこういった構造なのではないか、と考えています。

※「Linearや Jira の階層機能(Initiative / Project / Issue / Sub-issue)で十分では?」と思われるかもしれません。
たしかに、既存ツールの組み合わせで近い構造は組めるはずです。そもそも、ある程度成熟したチーム・組織であれば、すでに似たようなことはやっているのではないかと思います。ただ、今あるツールをうまく使うよりもより良いUIがあれば、より広く実践される可能性はあると思います。

Ticket-firstからIntent-awareへ

昨今、チケットの構造化に関する話題が増えています。背景、目的、制約、非スコープ、受け入れ条件を今まで以上にきちんと書きましょう、という流れです。これは短期的には正しい方向です。曖昧なチケットは、曖昧な実装を生みます。AIは曖昧な指示に対しても、それらしい実装を高速に返してしまうことがあるため、目的と制約を明示する価値はむしろ高まります。

しかし中長期では、状況は変わってくると考えています。

「良いチケットを書きましょう」という議論は、チケットが開発の中心にあることを前提にしています。本当に考えたいのは、チケットというUIが、AI時代の開発における最適な中心であり続けるのか、という問いです。

チケットはこれまで、意図、作業、責任、進捗、検証、証跡を一枚にまとめる便利な箱でした。その便利さは、実装が遅く、ハンドオフが多く、文脈を一箇所に圧縮する必要があった時代には大きな価値を持っていました。

しかしAIが文脈を保持し、関連するIntent、過去の判断、制約、テスト結果を踏まえて作業できるようになると、すべてをチケットに書く必要は薄れていきます。

細かい実装ToDoは、AIエージェントが動的に生成するようになっていきます。進捗ステータスは、PRやCI/CDから自動観測される方向に進みます。バグ調査は、ログや監視アラートから直接AIエージェントが始める運用も、商用ツール(SentryのAutofix、DatadogのBits AI等)で部分的に現れ始めています。

仕様は、ドキュメントだけでなく、実行可能な仕様(executable spec)としてCIに組み込まれていきます。

一方で、人間の判断は残ります。何を作るべきか。なぜ今やるのか。どんな副作用を避けたいのか。法務・セキュリティ・倫理上、何を許容しないか。これらはIntentの領域であり、AIが代替するのは難しい部分です。

AIが高速に実装できるようになるほど、「何を作るか」よりも、「なぜ作るのか」「何を守るのか」「どうなれば成功なのか」が重要になります。DORA の 2025 年の報告書が指摘するように、速度向上は下流の不安定さを露わにし、その不安定さを支えるのは、内部プラットフォームの品質や、小さく変更を進める運用のような、組織システム側の力です*9

だからこそ、これから必要なのは、チケットが担っていた役割を分解し、Intentを軸に再配置することです。Intentを意識して、ドキュメント、チケット、PR、テスト、監視、メトリクスをつなげていく方向に、少しずつ組み直していくことです。

その過程で、チケットは消えるのではなく、置かれる位置が変わると考えています。

チケットは、作業管理の唯一の中心ではなくなっていきます。作業の記録(record of work)であると同時に、Intentを軸につながった情報を、人間が見るための一つのビューになっていくのではないかと思います。

似顔絵
書いた人:伊藤

参考文献・引用元

*1:Bugzilla Project, "About - What is Bugzilla?", Bugzilla.org. URL: https://www.bugzilla.org/about/ (閲覧日: 2026-04-27)。Bugzilla が defect-tracking / bug-tracking system であり、bugs、problems、issues、enhancement、change requests を追跡するための仕組みである、という説明の出典です。

*2:Lloyd Montgomery, "Issue Tracking Ecosystems: Context and Best Practices," arXiv:2507.06704, 2025. URL: https://arxiv.org/abs/2507.06704 (閲覧日: 2026-04-27)。Issue Tracking Systems が requirements、development tasks、maintenance items などを扱う基盤であり、issue 間リンクや外部ツールとのリンクによって traceability を支える、という整理の出典です。

*3:Ryan Singer / Basecamp, "Bets, Not Backlogs," Shape Up, Chapter 7. URL: https://basecamp.com/shapeup/2.1-chapter-07 (閲覧日: 2026-04-27)。バックログを大きな重荷と捉え、古いアイデアの整理ではなく少数の well-shaped な options に bet する、という考え方の出典です。

*4:Edwards Nystrom, Dhitiwat Jongsuebchoke, Tim Storer, "Sciit: Embedding issue tracking in source control management," Science of Computer Programming, Vol. 206, Article 102628, 2021. DOI: 10.1016/j.scico.2021.102628. URL: https://doi.org/10.1016/j.scico.2021.102628 (閲覧日: 2026-04-27)。Issue tracker と SCM を分離して管理する摩擦、issue を SCM 内の first-class change control item として扱い、status や participants などを SCM の状態から推測する設計の出典です。

*5:GitHub, "Spec Kit Documentation," GitHub Pages. URL: https://github.github.com/spec-kit/ (閲覧日: 2026-04-27)。Spec-Driven Development が仕様を実行可能なものとして扱い、仕様が working implementation を直接生成するという説明、および intent-driven development の整理の出典です。

*6:Den Delimarsky, "Diving Into Spec-Driven Development With GitHub Spec Kit," Microsoft for Developers Blog, 2025-09-15. URL: https://developer.microsoft.com/blog/spec-driven-development-spec-kit (閲覧日: 2026-04-27)。AI agent に正しい出力を出させるには良い context が必要であり、何をなぜ作るのかを決めないまま進めると codebase が de-facto specification になる、という説明の出典です。

*7:Linear, "Issue tracking is dead," Linear.app/next, 2026-03-24. URL: https://linear.app/next (閲覧日: 2026-04-27)。従来の issue tracking が PM からエンジニアへの handoff model に合わせて作られたこと、今後は context と agents を中心に開発システムが変わる、という主張の出典です。

*8:DORA / Google Cloud, "State of AI-assisted Software Development 2025," DORA Research, 2025. URL: https://dora.dev/dora-report-2025/ (閲覧日: 2026-04-27)。AI は組織の既存の強みと弱みを増幅するものであり、AI 導入の効果はツール単体ではなく組織システムに依存する、という整理の出典です。

*9:DORA / Google Cloud, "State of AI-assisted Software Development 2025," DORA Research, 2025. URL: https://dora.dev/dora-report-2025/ (閲覧日: 2026-04-27)。AI は組織の既存の強みと弱みを増幅するものであり、AI 導入の効果はツール単体ではなく組織システムに依存する、という整理の出典です。

LLMアプリケーションを改善したければLangfuseを使ってみてほしい~トレース確認からプロンプトの評価までの流れ

1. はじめに

LLMアプリケーションを開発・運用していると、ぶつかりがちな壁があります。「なぜこの出力になったのか分からず改善が進まない」「プロンプトやモデルを変更したいけれど影響範囲が読めない」などです。

私たちテックドクターでは、こうした課題に対して、LLMアプリケーションのオブザーバビリティ・評価プラットフォームであるLangfuseを使うことで対処しています。

この記事では、弊社のヘルスケアアプリケーション開発の実例を交えながら、トレースによる挙動の可視化、プロンプトの一元管理、データセットの構築、そして簡易的なプロンプト評価(Prompt Experiments)の実行まで、Langfuse活用の一連の流れを解説していきます。

こんな人に読んでほしい
  • LLMアプリケーションの開発・運用に携わるエンジニア
  • Langfuseの導入を検討している方



2. 導入背景

弊社ではLLMを活用したアプリケーションを開発しています。チャットボットへの相談、バイタルデータの要約、メッセージ履歴のサマリー生成などの機能を提供しており、運用中のプロンプトは結構な数になります。
その開発を進める中で、次の2つの課題が目立つようになりました。

LLMの出力の原因を追いにくい

ユーザーから「回答がおかしい」と報告を受けても、そのときの入力や会話履歴を追跡するのが困難でした。
アプリケーションログを手がかりにLLMへの入力を再構成していたのですが、手作業となり時間がかかるうえに会話のコンテキストが欠落しやすいです。原因の特定がなかなか進みませんでした。

プロンプト変更の効果を簡単に確認する手段がない

プロンプトの改善にはエンジニアだけでなく、ドメイン知識を持つ非エンジニアメンバーも関わることがあります。そのため、非エンジニアでも簡単にGUIからプロンプトの検証ができる環境がほしいと考えました。
弊社ではこうした課題への対策として、Langfuseを導入しました。


3. Langfuseの概要

Langfuseとは

Langfuse は、LLMアプリケーション向けのオープンソースのLLMエンジニアリングプラットフォームです。

LLMへの入出力やトークン消費量をトレースとして記録・可視化できるほか、プロンプトのバージョン管理やデータセットを用いた評価まで、一つのプラットフォームで完結して行うことができます。SaaS版(US / EU / HIPAAリージョン)およびセルフホスティング環境で利用可能です。

Langfuseを導入すると、主に次のようなことができるようになります。

  • オブザーバビリティ……LLMへの入出力、処理の流れ、レイテンシ、トークン数・コストをWeb UIで確認できます。ユーザーIDやセッションIDでフィルタリングして、特定ユーザーの会話を追跡することも可能です。
  • プロンプトの管理……プロンプトをコードから分離し、Langfuse上で作成・編集できます。バージョン管理やラベル付け(production / staging)にも対応しており、コードのデプロイなしにプロンプトの切り替えやロールバックが行えます。
  • 評価……データセット(入力と期待出力のペア)を用意し、プロンプトやLLMアプリケーションの出力品質を評価できます。

参考: Langfuse Overview

なお、本記事で扱う主な機能は以下の4つです。

名称 機能
Tracing LLMの入出力やレイテンシ、トークン数・コストを階層的に記録・可視化します
Prompt Management プロンプトのバージョン管理とラベル付け(production / staging)を行います
Datasets 評価用の入力データ(と期待出力)をまとめて管理します
Prompt Experiments プロンプト × データセットの組み合わせで評価を実行し、結果を比較します

このほかにも、ユーザーフィードバックをスコアとして記録する機能、LLM-as-a-Judge(LLMを評価者として出力品質を自動判定する手法)による自動評価、Annotation Queue(評価対象をキューに溜めて人手でラベリングを進める機能)などがありますが、本記事ではとくに上記4つに焦点を当てます。

導入方法

Python SDKを使う場合

(SaaS版の場合)パッケージをインストールして環境変数を設定するだけで利用を開始できます。

pip install langfuse

# .env
LANGFUSE_SECRET_KEY="sk-lf-..."
LANGFUSE_PUBLIC_KEY="pk-lf-..."
LANGFUSE_BASE_URL="<https://cloud.langfuse.com>"  # EU region
# LANGFUSE_BASE_URL="<https://us.cloud.langfuse.com>"  # US region


Langfuseクライアントは get_client() で初期化します。環境変数を設定していれば、引数なしで認証情報が自動的に読み込まれます。

from langfuse import get_client

langfuse = get_client()


Langfuse Python SDKにはトレースの方法が複数用意されています。
以下はコンテキストマネージャを使用する例です。start_as_current_observation() を使うとブロック内の処理が自動的にトレースとして記録されます。 ほかにも、デコレータ を使用したり、手動で観測値を設定 したりすることもできます。

from langfuse import get_client

langfuse = get_client()

# コンテキストマネージャを使用してスパンを作成する
with langfuse.start_as_current_observation(
    as_type="span",
    name="process-request"
) as span:
    span.update(output="Processing complete")

    # LLM呼び出しにおいて、ネストされた生成を作成する. LLM呼び出しを記録する場合は as_type="generation" を指定
    with langfuse.start_as_current_observation(
        as_type="generation",
        name="llm-call",
        model="gpt-4o"
    ) as generation:
        # ここにLLMを呼び出すためのロジックを記述します
        generation.update(output="Generated response")

# すべてのスパンは、それぞれのコンテキストブロックを抜ける際に自動的にクローズされます。

参考: Python SDK - Getting Started / Instrumentation


フレームワークインテグレーションを使う場合

別のトレース方法として、フレームワークインテグレーションを使う方法も紹介します。

Langfuse SDKはOpenTelemetryベースで構築されているため、Pydantic AIやGoogle ADKなどのOTel対応フレームワークを使っている場合は、@observe() デコレータやコンテキストマネージャを使わなくてもトレースを自動記録できます。Langfuseはインテグレーションが充実しており、さまざまなLLMアプリケーションフレームワークと手軽に統合できます。

例えば、以下はPydantic AIを使用する例です。

from langfuse import get_client
from pydantic_ai import Agent

# Langfuseクライアントを初期化
langfuse = get_client()

# Pydantic AIのすべてのエージェントでインストルメンテーションを有効化
Agent.instrument_all()

# エージェントを作成して実行するだけでトレースが自動記録される
agent = Agent("openai:gpt-4o", instrument=True)
result = agent.run_sync("こんにちは")

参考: Pydantic AI Integration

以上がLangfuseの概要です。次のセクションからは、LLMアプリケーション改善の流れを追う形で、Langfuseの各機能について詳しくご紹介していきたいと思います。
トレースの確認、プロンプトの管理、プロンプト評価の実行の順で見ていきましょう。


4. トレースでLLMの挙動を確認する(Tracingについて)

なにか問題が起きたとき、トレース機能によってLLMへの入出力や処理の流れを追跡できます。

トレースの階層構造

Langfuseのトレースは、LLMアプリケーションの1回のリクエスト処理(ユーザー入力から応答生成まで)の実行を階層構造で記録します。 トレースの中にはobservationと呼ばれる個々の処理ステップがネストされており、それぞれのobservationには処理の種類を表すtypeが割り当てられます。 代表的なobservation typeは以下の3つです。

  • Span: 任意の処理区間(前処理、後処理など)
  • Generation: LLM呼び出し。フレームワークインテグレーション経由であれば入力プロンプト、出力テキスト、モデル名、トークン数、コストが記録される。
  • Tool: 天気予報のAPI呼び出しのような、ツール呼び出しを表す

このほかにも用途に応じた複数のobservation typeが用意されており、フレームワークインテグレーション経由では、自動的に適切なtypeが設定されます。(コンテキストマネージャ経由の場合は自分でtypeを指定することになります)

ここからは、記録されたトレースを管理画面上でどのように確認・活用するかを見ていきましょう。

一覧表示からトレースを確認する

管理画面のトレース一覧を開くと、記録されたトレースが表示されます。

画面キャプチャ

一覧画面では、トレース名・実行日時・レイテンシ、実際の入出力、使用トークン数とコスト、付与されたタグやメタデータ、ユーザーID・セッションIDなどを確認できます。

フィルター機能を使えば、ユーザーID・セッションID・タグ・日時範囲などの条件でトレースを絞り込めます。

また、入出力を含む各カラムに対してテキスト検索も可能なので、目当てのログ情報を容易に探し当てることができます。

一覧から任意のトレースを選択すると詳細画面が開き、内容がツリー形式で表示されます。 各ノードを選択すると、その処理ステップの入力・出力・実行時間・トークン数などが表示されます。

このように詳細画面で関連するトレース情報を一覧できることで、いわゆるAgentic Workflowと呼ばれるような、LLMを多段階的に呼び出すアプリケーションを構築している場合は、デバッグがとても捗ります。

セッション単位の会話追跡

弊社では活用していませんが、トレースにセッションIDを付与しておくと、管理画面から同一セッションに属する複数のトレースをまとめて閲覧できます。

会話の流れの中で「どの時点から出力がおかしくなったのか」を時系列で追えるので、こちらもデバッグに役立ちそうです。

ユーザーフィードバックの記録と活用

Langfuseでは、トレースに対して スコア(Score) を紐づけて記録できます。SDK経由でプログラムから記録したり、管理画面のUI上で人手で記録することも可能です。

弊社のアプリではユーザーがLLMの回答に対してアプリ内でGood/Badの評価を行える仕組みを実装しており、その結果をLangfuse上でトレースに紐づくスコアとして記録しています。

from langfuse import get_client

langfuse = get_client()

langfuse.create_score(
    trace_id="trace-id",
    name="user-feedback",
    value=1,       # Good: 1, Bad: 0
    data_type="NUMERIC",
    comment="ユーザーからのフィードバック",
)


記録されたスコアはトレース詳細画面に表示されます。トレース一覧画面ではスコアによるフィルタリングも可能で、たとえば「Bad評価が付いたトレースだけを抽出して、共通するパターンを分析する」といった使い方もできます。
参考: Tracing / Scores

5. プロンプトを管理する(Prompt Managementについて)

トレースで問題のあるプロンプトを特定できたら、改善を行います。そのとき役に立つのがプロンプト管理の機能です。

Prompt Managementの概要

Langfuseの Prompt Management 機能を使うと、プロンプトをコードから分離してLangfuse上でバージョン管理できます。 各バージョンには productionstaging といったラベルを付けられ、コードのデプロイなしにプロンプトの切り替えやロールバックが可能です。
プロンプトの形式にはtext(単一文字列)とchat(メッセージ配列)の2種類があります。チャットボットのようなアプリケーションでは chat 形式を使い、各メッセージの rolecontent を定義する必要があります。

バージョン管理とラベル

プロンプトを保存するたびにバージョン番号が自動でインクリメントされます。

各バージョンにはラベルを付与でき、アプリケーションからは「production ラベルの付いた最新バージョン」を取得する、という運用が可能です。

たとえば、新しいプロンプトをまず staging ラベルで作成し、評価(後述するPrompt Experiments)を経て問題がなければ production ラベルに昇格させる、というワークフローが実現できます。

variablesとmessage placeholder

プロンプト内に動的な値を埋め込むことで、プロンプトテンプレートとして使用できます。埋め込む値としてはvariables、prompt references、message placeholders がサポートされています。本記事では、主に variables と message placeholder を扱います。

  • variables: {{変数名}} の形式でプロンプト本文に記述し、実行時に文字列へ置換します。
あなたはヘルスケアアシスタントです。
ユーザーのタイムゾーンは {{TIMEZONE}} です。
現在時刻は {{CURRENT_TIME}} です。
{{LANGUAGE}} で回答してください。

  • message placeholder: chat形式のプロンプトで使う仕組みです。会話履歴のような可変長のメッセージ配列を動的に展開するためのもので、 placeholder タイプのメッセージを追加することで設定できます。

ここで定義したmessage placeholderの使用方法は、次のDatasetのセクションで説明します。

[
  { "role": "system", "content": "あなたはヘルスケアアシスタントです。" },
  { "type": "placeholder", "name": "MESSAGE_HISTORY" },
  { "role": "user", "content": "{{USER_PROMPT}}" }
]


UIでのプロンプト作成・編集

管理画面のプロンプト一覧から新規作成ボタンをクリックすると、プロンプトの作成画面が開きます。

ここでプロンプトの識別名、形式(text / chat)、プロンプト本文を入力して保存することで、プロンプトの追加が行えます。必要に応じて、モデル名や temperature といったパラメータも設定できます。

既存のプロンプトを編集する場合は、プロンプト一覧から対象を選択し、本文を修正して保存します。保存するたびに自動的に新しいバージョンとして追加され、過去のバージョンもすべて保持されるため、いつでも差分の確認やロールバックが可能です。

SDKでのプロンプト作成

プロンプトはUIだけでなく、Python SDKからもプログラムで作成・更新できます。

from langfuse import get_client

langfuse = get_client()

# text形式のプロンプトを作成
langfuse.create_prompt(
    name="health-assistant",
    type="text",
    prompt="あなたはヘルスケアアシスタントです。{{LANGUAGE}} で回答してください。",
    commit_message="create initial version",
    tags=["base"],
)

# chat形式のプロンプトを作成
langfuse.create_prompt(
    name="health-assistant-chat",
    type="chat",
    prompt=[
        {"role": "system", "content": "あなたはヘルスケアアシスタントです。"},
        {"type": "placeholder", "name": "MESSAGE_HISTORY"},
        {"role": "user", "content": "{{USER_PROMPT}}"},
    ],
    commit_message="create initial version",
    tags=["eval"],
)

弊社では、アプリケーションが実際に使用するプロンプトはGoogleのCloud Spannerで管理しており、Langfuse上には評価中のプロンプトを保存しています。両者のプロンプトの不整合を防ぐために、Spanner側のプロンプトをマスタデータとして、これをLangfuse側に反映するスクリプトを用意しています。
参考: Prompt Management Overview / Variables / Message Placeholders

6. データセットを用意する(Datasetについて)

ここまで、Langfuseのトレースおよびプロンプトの管理機能について説明してきました。これから説明するDataset、Prompt Experimentsはプロンプトの評価に関する機能です。

データセットの概要

データセット(Dataset)は、プロンプト評価に使用する入力データの集合です。
Langfuseのデータセットは、おもに以下の要素で構成されています。

  • Dataset: DatasetItemの集合。
  • DatasetItem: 個別の入出力ペア。input(入力値)、expected_output(期待する出力、任意)、metadata(カスタム属性、任意)を持つ
  • DatasetRun: データセットに対して評価を実行した結果。

主なデータセットの構築方法としては、トレースから取り込む方法、1から手動で作成する方法、CSVファイルからインポートする方法などがあります。

トレースからデータを追加する

本番やステージング環境で記録されたトレースをもとに、評価用データを作成できます。

具体的には以下の手順でDatasetを作成できます。

  1. トレース詳細画面で、データセットに追加したいobservationを選択する
  2. データセットへの追加ボタンをクリックする
  3. 次項「データセットアイテムの構造」に記載の形式に合わせて整形し、保存する

実際のユーザー入力をそのまま評価用データとして再利用できるため、現実的なテストケースを手早く蓄積できるのが利点です。

ただし、トレースに記録されたデータはプロンプトのvariablesやplaceholderの構造とは形式が異なることが多いため、データセットに追加する段階でプロンプトの変数構造に合わせた整形が必要になります。

手動でデータを作成する

プロダクトの要件を満たす典型的な入力パターンや、特定のエッジケースをあらかじめ用意しておく方法です。まだトレースが十分に蓄積されていない初期段階のデータセット構築に向いています。

なお、CSVファイルからデータセットを一括インポートすることも可能です。大量のテストケースをスプレッドシート等で管理している場合に便利です。

データセットアイテムの構造

データセットアイテムの inputexpected_output には値を格納できます。UIのPrompt Experimentsで利用する場合は、input はJSON objectである必要があり、実行時にそのJSONキーを見て、プロンプトテンプレートの同名のvariablesやmessage placeholderに値が自動で埋め込まれます。そのため、input のキー名はプロンプト側の変数名と一致させておく必要があります。

たとえば、セクション5で紹介したchat形式のプロンプトは次のような構造でした。

[
  { "role": "system", "content": "あなたはヘルスケアアシスタントです。" },
  { "type": "placeholder", "name": "MESSAGE_HISTORY" },
  { "role": "user", "content": "{{USER_PROMPT}}" }
]


このプロンプトに対してPrompt Experimentsを実行するには、データセットアイテムの inputMESSAGE_HISTORYUSER_PROMPT というキーが含まれている必要があります。弊社ではこれに加え、プロンプト内で使用している TIMEZONECURRENT_TIMELANGUAGE も共通で含めるようにしています。

フィールド 説明
USER_PROMPT ユーザーの入力テキスト
MESSAGE_HISTORY 会話履歴のメッセージ配列(会話履歴を使うプロンプトの場合)
TIMEZONE ユーザーのタイムゾーン(例: Asia/Tokyo)
CURRENT_TIME エージェント実行時の日時
LANGUAGE ユーザーの言語(例: 日本語)

実際の input は以下のようになります。

{
  "MESSAGE_HISTORY": [
    { "role": "user", "content": "寝つきが悪くて困っています" },
    { "role": "assistant", "content": "最近の生活習慣はいかがですか?" }
  ],
  "TIMEZONE": "Asia/Tokyo",
  "USER_PROMPT": "何か改善できることはありますか?",
  "CURRENT_TIME": "2026-03",
  "LANGUAGE": "日本語"
}

参考: Datasets


7. プロンプトを評価する(Prompt Experimentsについて)

UIから評価を実行する

プロンプトとデータセットの準備ができたら、Prompt Experimentsで評価を実行します。

  1. 管理画面のデータセット一覧から対象のデータセットを開く
  2. Experiment実行ボタンをクリックし、Prompt Experimentを選択する
  3. 名称、評価対象プロンプト、LLMのAPI接続設定、対象データセットなどを入力する。(LLM-as-a-Judgeによる自動評価を行いたい場合はEvaluatorもあわせて設定する)
  4. 実行を開始する

結果の確認と比較

実行が完了したら、Dataset Runs画面で結果を確認します。以下はテスト的に評価を実行した際の結果画面です。

画面キャプチャ

結果画面では、前述した inputexpected_output に加え、LLMからのレスポンス、そのコストとレイテンシなどを横並びで確認できます。

期待どおりの結果が得られなければ、Prompt Managementでプロンプトを修正し、再度Experimentを実行します。このサイクルを繰り返すことで、プロンプトの品質を段階的に高めていくことができます。
参考: Prompt Experiments (UI) / Datasets


8. まとめ

本記事では、Langfuseを使ったLLMアプリケーション改善の流れを、トレースの確認からプロンプト評価の実行まで一通り紹介しました。

トレースによって、問題が起きたときにLLMへの入出力や処理の流れを管理画面上で追跡できるようになります。

そのうえで、Prompt Managementでプロンプトをバージョン管理し、データセットを用意してPrompt Experimentsで修正前後の出力を並べて比較することで、プロンプトの改善サイクルを効率的に回すことができます。

今回は目視での確認・比較を中心に紹介しましたが、Langfuseにはこのほかにも、LLMを評価者として出力品質を自動スコアリングするLLM-as-a-Judgeや、Python SDKによるLLMアプリケーションの評価といった機能も用意されています。更に評価を自動化したい場合はこれらの機能の使用を検討すると良いかもしれません。




書いた人:大瀧

「自由な解析」から「確かなエビデンス」へ。ウェアラブルデータ解析におけるSAP(統計解析計画書)の重要性

はじめまして。テックドクターでデータ解析を担当している藤野です。

私はテックドクターへ参画する前は、約10年間にわたり医薬品開発における治験の統計解析に従事してきました。現在はその経験を活かし、ウェアラブルデバイスのデータを用いて、医療現場で活用されるデジタルバイオマーカー(※1)の開発に取り組んでいます。

ウェアラブルデータは、従来の臨床試験のデータに比べてデータ量が膨大で、項目の種類も多岐にわたります。そのため、解析の自由度が非常に高く、データサイエンティストとして非常に面白い領域です。しかしその反面、解析者の意図によって「都合の良い結果」を導き出してしまうバイアス(P-hacking等)が入り込みやすいという危険性もあります。

このように都合の良い解析とならないために、治験における解析の「規律」であるSAP(統計解析計画書)の考え方をウェアラブルデータ解析においても実践しています。
本記事では、ウェアラブルデータ解析においてなぜSAPが必要なのか、そして具体的にどのような点に留意して作成すべきかをご紹介します。

※1 デジタルバイオマーカー……デジタルデバイスで測定した『日常データ』をもとにした、病気の早期発見や治療につながる客観的指標(過去記事参照

SAP(統計解析計画書)とは

SAP(Statistical Analysis Plan:統計解析計画書)とは、解析を始める前に「どのようなデータを使い、どのような解析を行うか」を細かく記載した文書のことです。
医薬品開発で必須とされる国際的なガイドラインである「ICH-E9(臨床試験のための統計的原則)」(※2)に基づいて、解析の客観性と再現性を担保することを目的としています。

※2 PMDA(独立行政法人 医薬品医療機器総合機構)「ICH-E9(臨床試験のための統計的原則)」, URL: https://www.pmda.go.jp/int-activities/int-harmony/ich/0031.html

なぜ機械学習全盛の今、SAPが必要なのか

最新のアルゴリズム開発とは対極にある「古い慣習」のように思えるかもしれません。しかし、実はアルゴリズム開発(機械学習)の前段階として、統計的な検証を行うことには大きなメリットがあります。

①客観性の担保
解析前に解析方法を決定することで、有意差が出るまで条件を変えて解析し直すことを防ぎます。

②エビデンスの作成
解析プロセスを文書化しておくことで、社内外のステークホルダーに信頼されるエビデンスを作成します。

これらはSAPの一般的なメリットですが、くわえてウェアラブルデータ解析特有の課題への対策としてもSAPは有効です。次にご説明します。

ウェアラブルデータ解析における「信頼性」の課題

ウェアラブルデータ解析においては、その自由度の高さから、特有のリスクが存在します。

多重性の問題
心拍、睡眠、歩数など、ウェアラブルデバイスからは大量の変数が得られます。
これらを総当たりで検定にかければ、偶然有意差が見つかるリスクが高まります。

欠測バイアス(※3)の問題
日常生活では、未装着や充電切れなどによるデータの欠測が発生します。
欠測の扱いで結果が大きく変わってしまうことがあります。

これらの日常のデータを信頼できるエビデンスとするために、SAPによる事前の規定がとても有効です。

※3 バイアス……解析者の思い込みやデータの偏りによって、真実とは異なる結果が出てしまうことを指します。

イメージ図

SAPの実践例と重要なポイント

具体的に、どのような点をSAPで定義すべきでしょうか。事例をもとに3点紹介します。

有効なデータと解析対象の定義

ウェアラブルデータの場合、「データが存在する=解析に使える」とは限りません。解析前に、評価の基盤となるデータの質を定義しておく必要があります。

歩数データの例:
次のような基準をあらかじめ規定しておきます。

  • 心拍データが記録されている時間を装着時間とし、「1日の装着時間が70%(16.8時間)以上」の日を有効装着日と規定する
  • さらに、評価期間7日間のうち、少なくとも4日以上の有効装着日が存在する被験者のみを解析対象とする
  • 最後に、各有効装着日の1日の歩数を算出し、評価期間中の平均値を解析に用いる

データ有効性の判定チャート

生データから加工変数への変換ロジック

高頻度な原データを解析可能な形に集約する計算式を、SAPで明文化します。

心拍変動の例:
以下のように基準を設定します。

  • 15分間隔の区間集計で算出するロジック
  • 「30bpm以下の心拍数」など生理学的にあり得ない値を異常値として除外する閾値
「探索」と「検証」の切り分け

自由にデータを深掘りして分析することで新たな知見を得るための探索的な解析項目と、事前に決められた手順で仮説を検証するための解析項目を明確に分けます。

SAP導入の効果とメリット

ここまで主にSAPがデータ解析作業にもたらす利点を紹介してきましたが、それ以外にもSAPの導入によって、プロジェクト全体に様々なメリットがあります。

解析結果の頑健性(ロバストネス)の向上
解析を事前に規定することで、「データを見てからルール(データの採用基準や統計手法)を決める」という後出しジャンケンを防ぎます。これにより、誰が解析しても結論の変わらない安定した解析結果を得ることができます。

ロバストネス向上のイメージ図

チーム内外の円滑な議論
解析方針が詳細にドキュメント化されているため、社内のレビューはもちろん、外部のステークホルダー(スポンサー・アカデミア・CRO等)や規制当局(PMDA等)との議論がスムーズになります。

将来的な治験への布石
探索的な臨床試験の段階からSAPを導入することで、将来的に治験に進む際に、スムーズに移行できる下地となります。

また、現場で実務を担うステークホルダーにも具体的なメリットがあります。

ステークホルダー 具体的なメリット
データサイエンティスト 個別のデータ採否に迷う必要がなく、解析の再現性と効率が向上します。
統計解析担当者 欠測や中間事象(服薬不遵守や併用薬の使用)に対して、治験の標準的な考え方を適用でき、ウェアラブルデータ以外の解析と整合性が取れます。
薬事・マーケティング担当者 事前に合意・規定されたプロセスに基づくエビデンスとなるため、社内外への説明に客観的な裏付けができます。

実践を通じた気づき

10年の治験解析経験を経て、今ウェアラブルデータ解析の舞台で改めて感じるのは、SAPは決して「自由な解析を制限するもの」ではないということです。むしろ、解析結果に胸を張って「これは信頼できる結果です」と言うための武器になります。

まとめ

ウェアラブルデバイスという新しい領域だからこそ、先人が臨床試験で築き上げてきた「統計の作法」が非常に強力な武器になります。

テックドクターでは、これからも最新のテクノロジーと統計学的手法を組み合わせ、デジタルバイオマーカーの社会実装に向けた信頼性の高い解析を追求していきます。


似顔絵
書いた人:藤野