Vai al contenuto
mcpd
nessun daemon
Codice

Avvio rapido

Daemon avviato, agente strumentato, traccia sullo schermo. Niente a cui iscriversi e niente da installare a fianco.

1. Avvia il daemon

Nessuna release, per ora.

L'immagine, il chart e i pacchetti che i comandi di questo sito nominano sono la distribuzione prevista. Nessuno di essi è stato pubblicato. Quello che funziona oggi è una compilazione dai sorgenti, che richiede Rust e Node:

Compila ed esegui

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

mcpd è un solo processo, con lo storage compilato dentro. Non c’è un database da mettere in piedi e non c’è un file di configurazione da scrivere.

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

Il volume è facoltativo, ma vale la pena aggiungerlo: senza, le tracce spariscono insieme al container. Preferisci un binario? Installalo direttamente.

che cosa dovresti vedere
$ 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

Gli avvertimenti vengono per primi, ed è voluto. Sul tuo portatile non contano. Prima che questa porta sia raggiungibile da qualcos’altro, leggi Deploy.

2. Mandagli qualcosa

Due strade per entrare, e non sei obbligato a prendere la nostra. Se il tuo agente emette già OpenTelemetry, puntalo su mcpd e non scrivere neanche una riga di codice in più:

export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

Altrimenti installa l’SDK del tuo linguaggio. Non ha nessuna dipendenza obbligatoria, e registrare uno span non blocca mai e non solleva mai un’eccezione.

connettersi

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. Strumenta un’esecuzione

Avvolgi il lavoro che fa il tuo agente. Uno span registra che cosa è entrato, che cosa è uscito, quanto tempo ci è voluto e quanto è costato; i figli si annidano dentro da soli, così una chiamata a uno strumento fatta dentro il blocco arriva al posto giusto nell’albero senza che le si debba dire dove va.

registrare un’esecuzione

/**
 * 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. Leggila

Apri localhost:4317. La tua esecuzione è nell’elenco. Aprila e hai tutto l’albero: ogni chiamata al modello con il suo prompt e la sua completion, ogni chiamata a uno strumento con i suoi argomenti e il suo risultato, i tempi, i token e il costo.

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. Trova quella che conta

Non è nell’elenco che passerai il tuo tempo, ma nella ricerca. mcpd indicizza ogni prompt e ogni completion, così vai dritto all’esecuzione in cui il modello ha detto la cosa di cui un cliente si è lamentato:

cercare e analizzare

// 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}`)
}

Dove andare adesso

Adesso hai il giro completo. Le pagine da leggere dopo dipendono da quello che stai facendo: