본문으로 건너뛰기
mcpd
데몬 없음
소스

빠른 시작

데몬을 띄우고, 에이전트를 계측하고, 화면에서 트레이스를 봅니다. 가입할 것도, 옆에 같이 설치할 것도 없습니다.

1. 데몬 실행

아직 릴리스가 없습니다.

이 사이트의 명령이 가리키는 이미지, 차트, 패키지는 예정된 배포물이며 아직 아무것도 공개되지 않았습니다. 현재 동작하는 방법은 소스에서 빌드하는 것이고, Rust와 Node가 필요합니다.

빌드하고 실행

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

mcpd는 스토리지가 안에 컴파일된 프로세스 하나입니다. 따로 세울 데이터베이스도, 작성할 설정 파일도 없습니다.

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

경고가 맨 먼저 나오는 것은 의도한 것입니다. 노트북에서라면 문제가 되지 않습니다. 이 포트에 다른 무언가가 닿을 수 있게 되기 전에 배포 문서를 읽으십시오.

2. 무언가 보내기

들어오는 길은 두 가지이고, mcpd 쪽 길을 골라야 하는 것도 아닙니다. 에이전트가 이미 OpenTelemetry를 내보내고 있다면, 그것을 mcpd로 향하게 하기만 하면 됩니다. 새로 쓸 코드는 한 줄도 없습니다.

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. 눈여겨볼 실행 찾기

시간을 쓰게 되는 곳은 목록이 아니라 검색입니다. 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}`)
}

다음에 읽을 곳

이제 한 바퀴를 다 돌았습니다. 다음에 읽을 만한 페이지는 무엇을 하려는지에 따라 다릅니다.