本文へスキップ
mcpd
デーモンなし
ソース

概念

わずかな数の言葉が、データモデル全体を支えています。以降のドキュメントはそれを知っている前提で書かれているので、このページでひとつずつ定義し、何のためにあるかを述べます。

スパン

スパンは仕事の 1 単位です。名前、種別、開始時刻、たいていは終了時刻、そしてステータスを持ちます。何が入り、何が出て、トークンをいくつ使い、いくらかかったか、失敗した場合はそのエラーも持てます。

名前はカーディナリティを低く保ってください。search_docs は名前ですが、search_docs(query=hello) は名前と値をくっつけたもので、実行が 2 件になった時点で、名前による集計が役に立たなくなります。

トレース

トレースは 1 回の実行から出たすべてのスパンで、128 ビットの trace id で結ばれています。トレースの中では parent_span_id がツリーの形を決めます。モデルのステップの中で行われたツール呼び出しはそのステップの子になるので、画面上の入れ子は、実際に起きた入れ子です。

id は OpenTelemetry と同じ形で、トレースは小文字の 16 進 32 文字、スパンは 16 文字です。これは飾りではありません。Web 層で始まりエージェントで終わったトレースが、その両方をまたいで 1 つのトレースであり続けられるのはこのためで、すでにテレメトリーを読んでいる別のものと mcpd が並んで動けるのも、このためです。

親が別のトレーシングシステムから来たスパンは、その値を external_parent_span_id に持ちます。mcpd はこの値を相関のために保持しますが、ツリーの構築には決して使いません。mcpd が受け取っていないスパンを指す親 id は、部分木ごと何もない場所にぶら下げてしまうからです。

Trace b3d9e71a Fault
0ms 2.1s 4.2s 6.3s 8.4s
  1. agent support_agent fail
  2. model chat
  3. tool returns_policy
  4. model chat
  5. tool returns_policy
  6. model chat
  7. tool returns_policy

The model called one tool three times and never answered. That is visible in four seconds here, and invisible in a log file.

mcpd は各トレースの射影も保存します。スパン数、エラー数、トークンの合計、コストの合計、モデル呼び出しの数、ツール呼び出しの数です。この行は、加算するのではなく、書き込みのたびにスパンから計算し直します。スパンは順不同で届き、再送され、その場で更新されるからです。カウンターはこの 3 つのどれでもずれますし、一覧のスパン数が明らかに間違っていれば、読む人はページ上のほかのすべての数値も信じなくなります。

ルートスパンとイベントスパン

ルートスパンは、そのトレースの中に親を持たないスパンです。ふつうは agent_run か workflow_run で、トレースの名前、タグ、相関はここから取られます。タグに意味があるのはルートの上だけで、そこでは実行全体にラベルを付けます。

イベントスパンは長さではなく時点なので、終了も所要時間も持ちません。ストリーミングのチャンクが代表例です。この区別は、終了時刻がないことから推測するのではなく、そのまま保存します。「これには最初から終わりがない」と「これはまだ終わっていない」は別の事実で、問題なのは一方だけだからです。

トレースが完了するのは、ルートが終わり、開いたままのスパンが 1 つもなくなったときです。それまでの間も、終わるまで隠すのではなく、その時点で分かる最良の名前を付けて一覧に出します。終わらなかった実行こそ、あなたが見に来たものであることが多いからです。

スパンの種別

種別は、その操作が何であるかを示します。スパンの描かれ方、そのスパンで意味を持つ属性、コスト計算の対象になるかどうかは、これで決まります。mcpd は 33 の種別を系統ごとに定義しています。

系統種別
エージェント agent_run
モデル model_generation, model_step, model_inference, model_chunk
ツール tool_call, client_tool_call, provider_tool_call, mcp_tool_call
ワークフロー workflow_run, workflow_step, workflow_conditional, workflow_parallel, workflow_loop ほか
リトリーバル rag_ingestion, rag_embedding, rag_vector_operation, rag_action, retrieval, memory_operation, graph_action
評価 scorer_run, scorer_step, guardrail, processor_run
その他 mapping, skill_resolution, workspace_action, http_request, generic

トークンを計上する種別は 4 つ、model_generation、model_step、model_inference、rag_embedding です。コスト計算が読むのは、このスパンたちです。generic は常に有効で、推測されることはありません。分類できないスパンには、当て推量ではなく正直にこのラベルが付きます。データモデルに、すべての種別とその意味が並んでいます。

モデルの階層

このうち 4 つの種別は互いに入れ子になり、その入れ子が、このモデルでいちばん役に立つ部分です。

  • model_generation は論理的な生成 1 回分です。最初のリクエストから最終的な答えまでのすべてを指します。
  • model_step はその中の 1 ステップです。ツールを 2 回呼ぶ生成には、ステップが 3 つあります。
  • model_inference はプロバイダーとの往復だけで、プロセッサーもツールの実行も含みません。
  • model_chunk はストリーミングのチャンク 1 つで、イベントとして記録されます。

階層が 1 つではなく 4 つある理由は、「モデルの呼び出しが遅かった」がそのままの意味で正しいことは、ほとんどないからです。ツールが絡めば、あなたのコードからの 1 回の呼び出しは HTTP の 1 往復ではありません。リクエスト、ツール、もう 1 回のリクエスト、ガードレール、さらにもう 1 回のリクエストです。これを 1 つのスパンにまとめてしまうと、自分のリトライループで 6 秒使った実行と、プロバイダーの応答を 6 秒待った実行が、まったく同じに見えます。inference と step と generation を分けているからこそ、この違いが読み取れます。そして time to first token は、最初のチャンクのイベントから分かります。

トークンの使用量は、プロバイダーを呼んだスパンに一度だけ記録してください。トレースの射影は、トレース内のすべてのスパンについて入力と出力のトークンを合計します。同じ数を generation にも各 step にも付ければ、画面上のトークン合計もコストも 2 倍になります。階層は時間のためのもので、トークンは、実際にプロバイダーと話した階層のものです。

6 つのシグナル

スパンは、mcpd が保存する 6 つのもののうちの 1 つです。ログは実行が出した行。メトリクスは数値の観測で、カウンター、ゲージ、ヒストグラムの形を取ります。スコアはスパンやトレースに対する機械の評価。フィードバックは人の評価。観点ノートは、終わった実行についての 1 文です。ほとんどはモデルが書きます。outcome のノートは、実行のステータスと、すでに持っているほかの事実から、デーモンが書きます。

  • Spans

    What ran, nested, with timings

  • Logs

    Lines the run emitted

  • Metrics

    Numbers, with exemplars back to a run

  • Scores

    Machine evaluation

  • Feedback

    What a person thought

  • Aspect notes

    A sentence a model wrote about the run

trace_id Store Search Export

6 つとも、自分が属する trace id を持ち、ほとんどは span id も持ちます。そして 6 つとも、何かが書き込まれる前に同じ秘匿化のパイプラインを通ります。秘密は、プロンプトからと同じようによく、ログの 1 行からも漏れます。

スコアは必ず尺度を宣言します。値と、その値が収まる最小値と最大値です。どの SDK も呼び出し側で両方を要求します。尺度の付かない 0.7 という値だけでは、何とも比べられないからです。先週の同じスコアラーの出力とも比べられません。

フィードバックはスコアとは別の場所に置き、スコアと平均を取ることはありません。形は 5 つ、thumbs、rating、correction、tag、comment です。5 つのうちいちばん価値があるのは correction です。正しい答えを運ぶ唯一のシグナルだからです。

相関

相関とは、シグナルが属する文脈のことです。ただ積み上がった実行と、切り分けられる集合との違いが、ここにあります。

フィールド保持する内容
projectテナントのキー。どのシグナルも、ちょうど 1 つに属します。
environmentどこで動いたか。production や staging など。
service_name送り出したサービス。
service_versionそのバージョン。リグレッションをリリースに結び付けられます。
session_id利用者から見たセッション。多くのトレースを 1 つの会話にまとめます。
thread_idそのセッションの中のスレッド。
user_idその実行の対象だったエンドユーザー。
resource_idあなたのアプリケーションの中のテナントやワークスペース。
run_idこの実行に対する、あなた自身の識別子。
request_idその仕事を引き起こした、受信したリクエスト。
experiment_id実験がその実行を生んだときに設定します。
releaseリリース名、または git の sha。

必須のフィールドは project だけです。送信側が指定しなかった場合、デーモンは --project の既定値の下に振り分けるので、居場所のないまま保存されるものはありません。

冗長に見えても session_id は設定してください。エージェントの 1 回の実行はトレースですが、苦情はたいてい会話についてのものです。session id がなければ、ばらばらの 11 本のトレースを、その人が実際に交わしたやり取りに戻すものが何もありません。

トレースの一覧は、environment、session_id、thread_id、user_id でそのまま絞り込めます。ステータス、種別、名前、タグ、モデル、期間、最小の所要時間、最小のコスト、スコアも同じように使えます。実行を探すで、これらを組み合わせます。

リンク

スパンは親を 1 つと、いくつでもリンクを持てます。リンクは別のスパンや別のトレースを指し、その理由を述べます。follows_from、retry_of、replay_of、あるいはあなたが付けた名前です。

リンクがあるのは、親子関係ではファンインを表せないからです。20 件のリクエストが引き起こしたバッチ処理には、リンクが 20 本と親が 1 つあります。これを無理にツリーへ収めれば、重複した部分木を 19 個でっち上げるか、原因を 19 個捨てるかのどちらかになります。

次に

  • すべてのエンティティとフィールドを網羅:データモデル。
  • これらの言葉がコードでどう現れるか:SDK。
  • スパンが OTLP として届いたとき mcpd が何を推測するか:OpenTelemetry。
  • この用語をクエリにする:実行を探す。