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

クイックスタート

デーモンが動き、エージェントを計装し、トレースが画面に出るまで。登録するものも、横に並べてインストールするものもありません。

1. デーモンを起動する

リリースはまだありません。

このサイトのコマンドが指すイメージ、チャート、パッケージは予定している配布物であり、どれも公開されていません。今動くのはソースからのビルドで、Rust と Node が必要です。

ビルドして実行

git clone https://github.com/mcpd/mcpd && cd mcpd && make build && ./target/release/mcpd demo

mcpd はストレージを組み込んだ 1 つのプロセスです。立ち上げるデータベースも、書く設定ファイルもありません。

docker run -p 4317:4317 -v mcpd-data:/data ghcr.io/mcpd/mcpd

ボリュームは任意ですが、付ける価値があります。付けないと、コンテナが消えるときにトレースも一緒に消えます。バイナリのほうがよいですか。直接インストールする。

表示されるはずの出力
$ docker run -p 4317:4317 ghcr.io/mcpd/mcpdWARN listening on 0.0.0.0:4317 with open ingest. Anyone who can reach this port can write to it. Set --ingest-key (or MCPD_INGEST_KEYS) before exposing it.WARN listening on 0.0.0.0:4317 with an open read API. Stored prompts and completions are readable by anyone who can reach this port. Set --read-key to require a key.WARN listening on 0.0.0.0:4317 with an open run surface. Anyone who can reach this port can start runs and claim steps. Set --execute-key and --worker-key before exposing it.INFO mcpd 0.1.0 listening on http://0.0.0.0:4317INFO   interface   http://0.0.0.0:4317/INFO   OTLP        http://0.0.0.0:4317/v1/tracesINFO   native      http://0.0.0.0:4317/v1/ingestINFO   runs        http://0.0.0.0:4317/v1/api/runsINFO   storage     sqlite at /data/mcpd.db

先に出る警告は意図したものです。手元のノート PC では問題になりません。このポートに他から届くようになる前に、デプロイを読んでください。

2. 何か送ってみる

入口は 2 つあり、私たちのものを選ぶ必要はありません。エージェントがすでに OpenTelemetry を出しているなら、その向き先を mcpd にするだけで、新しいコードは 1 行も書きません。

export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

そうでなければ、使っている言語の SDK をインストールします。必須の依存はなく、スパンの記録がブロックすることも、例外を投げることもありません。

接続する

import { Mcpd } from '@mcpd/sdk'

// Every field here also reads an environment variable, so the same code runs
// unchanged in development and in production. With no arguments at all this
// talks to a daemon on http://localhost:4317.
const mcpd = new Mcpd({
  project: 'support',
  environment: 'development',
  serviceName: 'support-agent',
  serviceVersion: '1.4.0',
})

3. 実行を計装する

エージェントがする仕事を包みます。スパンは、何が入り、何が出て、どれだけ時間がかかり、いくらかかったかを記録します。子は自動で内側に入れ子になるので、そのブロックの中で行われたツール呼び出しは、どこに属するかを教えなくてもツリーの正しい位置に収まります。

実行を記録する

/**
 * One ticket, one trace.
 *
 * `withSpan` opens the span, keeps it active for everything the callback
 * starts, and ends it when the callback settles. Nothing inside has to be
 * passed a handle to be nested under it.
 */
async function handleTicket(ticket: Ticket): Promise<{ answer: string; traceId: string }> {
  let traceId = ''
  const answer = await mcpd.withSpan('support_agent', async (run) => {
    // Keep the id: a score or a piece of feedback that arrives later attaches
    // to this run by it.
    traceId = run.traceId
    // Tags label the whole run, so the trace list can filter on them.
    run.setTags(['support', 'tier-1'])
    run.setCorrelation({ sessionId: ticket.id, userId: ticket.customerId })
    run.setInput({ question: ticket.question })

    const order = await lookUpOrder(ticket)
    const answer = await draftAnswer(ticket, order)

    run.setOutput({ answer })
    return answer
  })
  return { answer, traceId }
}

4. 読む

localhost:4317 を開きます。あなたの実行が一覧にあります。開けばツリー全体が見えます。モデル呼び出しにはプロンプトと補完、ツール呼び出しには引数と結果、そして所要時間、トークン数、コストが付きます。

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.

5. 気になる 1 件を見つける

時間を使う場所は一覧ではなく、検索です。mcpd はすべてのプロンプトと補完をインデックスするので、顧客が苦情を寄せたその発言をモデルがした実行に、そのままたどり着けます。

検索して分析する

// The same client reads. This is the query that finds bad answers: every run
// that a scorer put below the bar.
const suspect = await mcpd.listTraces({
  hasScore: 'answer_relevance',
  scoreBelow: 0.5,
  limit: 10,
})
console.log(`runs scoring under 0.5: ${suspect.traces.length}`)

const recent = await mcpd.listTraces({ limit: 5, sort: 'start_time', order: 'desc' })
for (const trace of recent.traces) {
  const seconds = ((trace.durationNs ?? 0) / 1e9).toFixed(2)
  console.log(
    `${trace.traceId.slice(0, 8)}  ${trace.name.padEnd(16)} ${trace.status.padEnd(6)} ` +
      `${seconds}s  ${trace.spanCount} spans  $${(trace.cost?.totalCost ?? 0).toFixed(6)}`,
  )
}

// One run, with every span in it, ordered so a waterfall renders directly.
const detail = await mcpd.getTrace(traceId, { include: ['scores', 'feedback'] })
for (const span of detail.spans) {
  console.log(`  ${span.kind.padEnd(18)} ${span.name}`)
}

次に読むもの

これで一通りの流れがそろいました。次に読む価値のあるページは、あなたが何をしているかで変わります。