Aller au contenu
mcpd
aucun daemon
Source

Prise en main

Daemon lancé, agent instrumenté, trace à l’écran. Rien à quoi s’inscrire, et rien à installer à côté.

1. Lancez le daemon

Aucune version publiée pour l'instant.

L'image, le chart et les paquets que nomment les commandes de ce site sont la distribution prévue. Aucun n'est publié. Ce qui fonctionne aujourd'hui est une compilation depuis les sources, qui demande Rust et Node :

Compiler et lancer

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

mcpd est un seul processus, avec le stockage compilé dedans. Il n’y a pas de base de données à monter et pas de fichier de configuration à écrire.

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

Le volume est facultatif, mais il vaut la peine de l’ajouter : sans lui, les traces disparaissent en même temps que le conteneur. Vous préférez un binaire ? Installez-le directement.

ce que vous devriez voir
$ 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

Les avertissements viennent en premier, et c’est voulu. Sur votre portable, ils n’ont aucune importance. Avant que ce port ne soit joignable par quoi que ce soit d’autre, lisez Déploiement.

2. Envoyez-lui quelque chose

Deux voies d’entrée, et vous n’êtes pas obligé de prendre la nôtre. Si votre agent émet déjà de l’OpenTelemetry, pointez-le vers mcpd et n’écrivez pas une seule ligne de code de plus :

export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

Sinon, installez le SDK de votre langage. Il n’a aucune dépendance obligatoire, et enregistrer un span ne bloque jamais et ne lève jamais d’exception.

se connecter

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. Instrumentez une exécution

Encadrez le travail que fait votre agent. Un span enregistre ce qui est entré, ce qui est sorti, le temps que cela a pris et ce que cela a coûté ; les enfants s’imbriquent dedans automatiquement, si bien qu’un appel d’outil fait à l’intérieur du bloc atterrit au bon endroit dans l’arbre sans qu’il faille lui dire où il va.

enregistrer une exécution

/**
 * 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. Lisez-la

Ouvrez localhost:4317. Votre exécution est dans la liste. Ouvrez-la et vous avez l’arbre entier : chaque appel de modèle avec son prompt et sa complétion, chaque appel d’outil avec ses arguments et son résultat, les temps, les tokens et le coût.

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. Trouvez celle qui compte

Ce n’est pas dans la liste que vous passerez votre temps, c’est dans la recherche. mcpd indexe chaque prompt et chaque complétion, si bien que vous allez droit à l’exécution où le modèle a dit ce dont un client s’est plaint :

chercher et analyser

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

Où aller ensuite

Vous avez maintenant la boucle complète. Les pages à lire ensuite dépendent de ce que vous faites :