---
title: "Langfuse朝ブリーフィングを実装して試す - 異常traceだけをSlackに流せるか"
description: "Langfuseのtrace、token、cost、latencyをCloudflare Workers Cronで集計し、見るべき異常だけをSlackへ流す実装検証。"
lang: "ja"
canonical: "https://llm-lab.dev/posts/langfuse-morning-briefing-trial/"
source: "https://llm-lab.dev/posts/langfuse-morning-briefing-trial.md"
publishedAt: "2026-06-08"
updatedAt: "2026-06-15"
category: "AgentOps"
tags:
  - "agentops"
  - "slack"
  - "cloudflare-workers"
  - "langfuse"
---

# Langfuse朝ブリーフィングを実装して試す - 異常traceだけをSlackに流せるか

> [!NOTE]
> この記事で確認したこと: Cloudflare Workers CronからLangfuseのtraceを取得し、trace詳細APIとobservation詳細APIでtoken、cost、latencyを補完したうえで、異常候補だけをSlack朝ブリーフィングとして送れました。今回のWorkerは自動修正やIssue化ではなく、人間が開くべきtraceを絞る入口であり、score取得、重複整理、pagination、前日比、Monitorsとの役割分担は別の信号品質改善として扱います。

前回の記事「[Langfuseの異常だけをSlackに流す朝ブリーフィングを作りたい](/posts/langfuse-morning-briefing/)」では、Langfuse の trace を毎日見に行くのではなく、見るべき異常だけを Slack に流す構想を整理しました。

今回は、その実装編です。

結論から言うと、Cloudflare Workers Cron から Langfuse API を叩き、前日分の trace を集計して Slack に流すところまでは動きました。さらに、合成 trace を投入して token spike と latency 異常を検出し、翌朝の Cron 実行で実際に Slack 通知が届くところまで確認できました。

一方で、素直には進みませんでした。trace 一覧だけでは generation の token / cost が取れず、trace 詳細 API、さらに observation 詳細 API までたどる必要がありました。score も投入はできたものの、trace 詳細の `scores` には出てこなかったため、score API の扱いは残課題です。

この記事では、実際に作った構成、詰まった点、最終的に Slack に届いた通知、そして次に直すべき点をまとめます。

## 作ったもの

作ったのは、最小構成の Langfuse 朝ブリーフィングです。

```text
Cloudflare Workers Cron
  ↓
Langfuse API から前日分の trace を取得
  ↓
trace 詳細 API / observation 詳細 API で usage と cost を補完
  ↓
異常候補を抽出
  ↓
Slack Incoming Webhook に投稿
```

実装場所は、既存ブログ repo 内の独立した Worker パッケージにしました。

最初から自動修正や GitHub Issue 化まではやりません。今回の目的は、毎朝 Langfuse を開かなくても、昨日見るべき trace があるかどうかを Slack で判断できる状態を作ることです。

## 実装対象

今回の前提は次の通りです。

| 項目 | 内容 |
|---|---|
| 対象 Langfuse project | 1 project 前提。ID は環境変数で指定 |
| 対象 trace | 全 trace から開始 |
| 通知先 | Slack Incoming Webhook |
| 実行頻度 | 毎朝 1 回 |
| 実行基盤 | Cloudflare Workers Cron Triggers |
| 保存先 | なし。初回は前日比を扱わず、絶対値しきい値だけ |

project ID、API key、Slack webhook URL はすべて環境変数または Workers secret に逃がしています。公開記事に値を貼ると事故るので、本文では具体値を書きません。

Worker は Cron だけでなく、検証用に `POST /run` でも手動実行できるようにしました。deploy 後に workers.dev URL が有効になったため、`/run` と診断用 endpoint は `RUN_TOKEN` の Bearer 認証で保護しています。

## 最初に見たかった指標

Langfuse から取りたかった情報は次の通りです。

| データ | 用途 |
|---|---|
| trace id | Slack から Langfuse へ戻るリンク |
| trace name | どの処理か分かるようにする |
| timestamp | JST の前日分で絞る |
| status / level | 失敗 trace を拾う |
| input / output tokens | token 急増を見る |
| cost | cost 異常を見る |
| latency | 遅い実行を見る |
| score | 評価が低い出力を見る |

抽出条件は、まず絶対値のしきい値にしました。

```text
error: 1件以上
total tokens: 50,000 以上
cost: $1.00 以上
latency: 10秒以上
score: 0.5 未満
```

前日比も見たいところですが、前日比を見るには保存先が必要です。初回から Workers KV や D1 を入れると検証対象が増えるため、今回は「昨日の trace を取り、絶対値しきい値で異常候補を出す」ところに絞りました。

## 実装した構成

Cloudflare Cron は UTC なので、JST 07:30 に動かすために `22:30 UTC` を指定しました。

```toml
[triggers]
crons = ["30 22 * * *"]
```

Slack 投稿は Incoming Webhook を使います。検証中に Discord 互換 Webhook も試しましたが、payload の key が違いました。

| 通知先 | payload |
|---|---|
| Slack Incoming Webhook | `{ "text": "message" }` |
| Discord 互換 Webhook | `{ "content": "message" }` |

この違いで一度 `Cannot send an empty message` に当たりました。Slack 本線の記事では `text` を使い、実装上は `WEBHOOK_KIND=slack|discord` で切り替えられるようにしています。

## 1つ目の詰まり：remote secretはローカルdevに入らない

最初のローカル検証では、Langfuse API が 401 になりました。

```text
Invalid credentials. Confirm that you've configured the correct host.
```

原因は、Cloudflare に登録した remote secret がローカルの `wrangler dev` には自動で渡らないことでした。ローカル検証では `.dev.vars` に `LANGFUSE_SECRET_KEY` や `SLACK_WEBHOOK_URL` を置く必要があります。

切り分け用に `/debug/langfuse` endpoint を追加し、秘密値そのものは出さずに、base URL、request URL、key の有無、key prefix だけ確認できるようにしました。secret の値を出さずに疎通条件だけ確認できる endpoint は、こういう検証ではかなり役に立ちます。

## 2つ目の詰まり：trace一覧だけではtokenが取れない

最初は `/api/public/traces` の一覧から、token、cost、latency、score を全部拾える想定でした。

しかし実データを見ると、trace 一覧だけで generation の usage まで取れるとは限りませんでした。そこで合成 trace を投入する seed script を作り、Langfuse ingestion API に次のイベントを送るようにしました。

```text
trace-create
generation-create
score-create
```

seed 自体は成功し、Langfuse 画面でも trace と generation は見えました。ただし、最初の Slack 通知では trace が 0 件のままでした。

ここでややこしかったのが timezone です。サンプル trace の timestamp は `2026-06-13T22:10:56Z` でしたが、JST では `2026-06-14 07:10:56` です。つまり、朝に手動実行する場合は `daysAgo=0` の対象であり、`daysAgo=1` では拾えません。

Slack 本文に `対象範囲: from - to` を出すようにしたのは、このズレを見落とさないためです。

## 3つ目の詰まり：observation詳細APIまで必要だった

`GET /api/public/traces/{traceId}` を呼ぶと、trace 詳細は取れました。しかし、最初に返ってきた `observations` は generation の詳細オブジェクトではなく、observation id の配列でした。

そのため、最終的には次の3段階で補完する形にしました。

```text
/api/public/traces
  ↓
/api/public/traces/{traceId}
  ↓
/api/public/observations/{observationId}
```

observation 詳細 API まで呼ぶと、次のような値が取れました。

```json
{
  "type": "GENERATION",
  "usageDetails": {
    "input": 30000,
    "output": 25000,
    "total": 55000
  },
  "costDetails": {
    "input": 0.0045,
    "output": 0.015,
    "total": 0.0195
  },
  "latency": 12.3
}
```

この時点で、token spike と latency 異常は Slack 通知に出せる状態になりました。

## 実際に届いたSlack通知

翌朝の Cron 実行後、Slack には次のような通知が届きました。

```text
Langfuse Morning Briefing

対象日: 2026-06-14
対象範囲: 2026-06-13T15:00:00.000Z - 2026-06-14T15:00:00.000Z
traces: 8
errors: 0
total tokens: 440,000
estimated cost: $0.1560

見るべき異常:
1. sample-morning-briefing-token-spike - tokens 55,000, latency 12.3s
   https://jp.cloud.langfuse.com/project/***redacted***/traces/***redacted***
2. sample-morning-briefing-token-spike - tokens 55,000, latency 12.3s
   https://jp.cloud.langfuse.com/project/***redacted***/traces/***redacted***
3. sample-morning-briefing-token-spike - tokens 55,000, latency 12.3s
   https://jp.cloud.langfuse.com/project/***redacted***/traces/***redacted***
4. sample-morning-briefing-token-spike - tokens 55,000, latency 12.3s
   https://jp.cloud.langfuse.com/project/***redacted***/traces/***redacted***
5. sample-morning-briefing-token-spike - tokens 55,000, latency 12.3s
   https://jp.cloud.langfuse.com/project/***redacted***/traces/***redacted***

取れなかった項目: score
```

検証用 seed を複数回打っていたため、sample trace が複数並んでいます。実運用では、Agent 名や prompt 名で絞る、同名 trace をまとめる、上位件数を調整するといった改善が必要です。

それでも、今回の目的である「毎朝 Langfuse を開かなくても、昨日見るべき異常候補が Slack に流れるか」は確認できました。

## うまくいったこと

今回うまくいった点は次の通りです。

- Cloudflare Workers Cron で毎朝実行できた
- Slack Incoming Webhook へ投稿できた
- JST の前日範囲を UTC ISO に変換して集計できた
- trace 一覧、trace 詳細、observation 詳細をたどって token / cost / latency を取得できた
- token 55,000、latency 12.3秒の合成 trace を異常候補として検出できた
- Cron 実行後に実際の Slack 通知が届いた
- `RUN_TOKEN` による手動実行 endpoint の保護も入れられた

個人的には、trace 詳細 API だけで終わらず observation 詳細 API まで見に行く必要があった点が一番の学びでした。Langfuse 画面では一体に見える情報でも、API では trace、observation、score の取得粒度が分かれています。

## うまくいかなかったこと

一方で、詰まりもかなりありました。

- remote secret がローカル dev に渡らず、最初は 401 になった
- Discord 互換 Webhook を Slack と同じ payload で叩き、400 になった
- 本番 `/run` で未捕捉例外により Cloudflare の `error code: 1101` が出た
- trace 一覧だけでは token / cost / latency が揃わなかった
- trace 詳細の `observations` が詳細オブジェクトではなく id 配列だった
- `score-create` は成功したが、trace 詳細の `scores` は空だった
- pagination はまだ未実装で、trace が 100 件を超える日は取りこぼす可能性がある

特に score はまだ整理できていません。今回の Worker では `score` を missing field として Slack に出すだけにしています。品質スコア監視までやるなら、score 用 endpoint の確認、score 名ごとの集計、no data の扱いを別途設計する必要があります。

## この実装で分かった設計上の論点

朝ブリーフィングは、アラート基盤そのものではありません。

今回作ったものは、あくまで「昨日の実行を朝にざっと見る入口」です。即時検知したい cost spike や latency 悪化であれば、評価窓を持つ monitor の方が向いています。実際、この記事を書いている途中で Langfuse 側に Monitors 機能が出てきたため、しきい値監視の一部は公式機能へ寄せられる可能性があります。

一方で、自前 Worker にはまだ役割があります。

- 複数の異常条件を1つの朝レポートにまとめる
- trace URL を優先度順に並べる
- 自分の運用に合わせたメッセージに整形する
- Langfuse 以外の情報と合わせて日次サマリーにする

つまり、公式 Monitors は「鳴らす」機能、自前 Worker は「読む」機能として分けるのがよさそうです。

## 残課題

公開時点で残っている課題は次の通りです。

| 課題 | 状態 |
|---|---|
| pagination | 未実装。`limit=100` を超える日は取りこぼす可能性がある |
| score 取得 | 未完了。`score-create` 後も trace 詳細の `scores` は空だった |
| 前日比 | 未実装。KV や D1 など日次 summary の保存先が必要 |
| 通知の重複整理 | 未実装。同名 sample trace が複数並ぶため、実運用では集約したい |
| Monitors との役割分担 | 調査中。cost / latency の即時監視は Monitors に寄せる余地がある |

これらは未完成ですが、今回の記事の主題である「Langfuse の異常 trace を Slack の朝ブリーフィングとして流せるか」に対しては、検証完了と言ってよい状態です。

## まとめ

Langfuse の trace を毎朝 Slack に流す朝ブリーフィングは、最小構成なら Cloudflare Workers Cron で実装できました。

ただし、Langfuse API のデータ取得は思ったより階層的です。trace 一覧だけで完結するのではなく、trace 詳細、observation 詳細までたどって初めて、generation の token、cost、latency が安定して取れました。

この仕組みは、毎日ダッシュボードを開く代わりに「昨日見るべき trace があるか」を Slack で判断する入口としては十分使えそうです。一方で、即時性のあるメトリクス監視や score 監視は、Langfuse Monitors など公式機能との役割分担を考えた方がよさそうです。

次は、Monitors を使ったコスト・レイテンシ監視と、この朝ブリーフィング Worker の責務を切り分けます。
