For AI agents: a documentation index is available at /docs/llms.txt. Append .md to any page URL for markdown, or send Accept: text/markdown.
エージェント アナリティクス SDK
このページは、Amplitude AI SDKの開発者向けリファレンスです。 プロダクトレベルの設定の概要については、「エージェント アナリティクスの設定」を参照してください。プロダクトの概念とAmplitudeがデータをどのように使用するかについては、「エージェント アナリティクスの概要」と「エージェント結果の分析」を参照してください。
以下のタイムラインは、インストルメンテーションによって何が生成されるかを示しています。任意のイベントをクリックして、その形状と、そのイベントを発信する呼び出しを確認します。
| [Agent] Session ID | 4ddcc6b2-1041-432a-aa8c-ebe3eccac40b |
| [Agent] Agent ID | support-chatbot |
| [Agent] Trace ID | b4f63d43-d752-4b1f-8489-d234ddf586b2 |
| $llm_message.text | I can help. Your subscription renews on Aug 15… |
| [Agent] Model Name | gpt-4o-mini |
| [Agent] Provider | openai |
| [Agent] Input Tokens | 1245 |
| [Agent] Output Tokens | 87 |
| [Agent] Latency Ms | 3420 |
| [Agent] Cost USD | 0.0012 |
s.trackAiMessage(...)またはプロバイダーラッパーによって発行されます。前提条件
- Agentアナリティクスが有効になっているAmplitudeプロジェクト。
- エージェントアナリティクスオブジェクトの表示権限。管理者は、ロールベースのアクセス制御(RBAC)を通じてアクセスを許可します。
- Node.jsまたはPythonで計測するためのエージェントのコードベース(または、Amplitude HTTP APIを呼び出すことができるランタイム)。
- 適切なデータセンター用のプロジェクトの API キー。 Agent アナリティクスは米国とEUで動作します。
SDKをインストールする
Instrument this app with @amplitude/ai. Follow node_modules/@amplitude/ai/amplitude-ai.md
このプロンプトを AI コーディング エージェント (Cursor、Claude Code、Windsurf、GitHub Copilot、または Codex) に貼り付けます。 エージェントはリンクされた命令ファイルを読み取り、コードベースをスキャンし、すべての LLM コールサイトとセッション ライフサイクルを検出し、それらを計測します。
SDKの初期化
アプリケーションのエントリポイントで一度初期化し、インスタンスを再利用してください。 推奨されるパターンは、aiおよびラップされたプロバイダークライアントをエクスポートするブートストラップモジュールです。
// src/lib/amplitude.ts
import { AmplitudeAI, AIConfig, OpenAI } from "@amplitude/ai";
export const ai = new AmplitudeAI({
apiKey: process.env.AMPLITUDE_AI_API_KEY!,
config: new AIConfig({ contentMode: "full", redactPii: true }),
});
export const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY!,
amplitude: ai,
});
'openai'から直接インポートするのではなく、このモジュールからopenaiをインポートしてください。必要に応じて、ラップされたプロバイダーをさらに追加してください。
イベントを送信せずに開発中に検証するには、AIConfig で dryRun: true (Node) または dry_run=True (Python) を設定します。
SDK を設定する
AIConfigをAmplitudeAIコンストラクターに渡します。すべてのオプションは任意です。ほとんどのアプリでデフォルト設定が機能します。
| オプション | 概要 |
|---|---|
contentMode | 'full' (デフォルト)、'metadata_only'、または 'customer_enriched'。 どのメッセージコンテンツがAmplitudeに届くかを制御します。 『プライバシーモードの選択』を参照してください。 |
redactPii | イベントがプロセスを終了する前に、追跡対象コンテンツからメール、電話番号、SSN、クレジットカード番号、IPアドレスを削除します。 **デフォルトはtrue**です。 オプトアウトするには falseに設定します。 |
customRedactionPatterns | 追加の編集パターン。 名前付きラベル用の正規表現文字列 ([REDACTED] に置換) または { pattern, replacement } オブジェクトを受け入れます。 |
customRedactionFn | カスタム編集ロジック(NERライブラリなど)用の(text) => stringコールバック。正規表現ベースの編集がすべて行われた後に実行されます。 |
debug | 追跡されたすべてのイベントをstderrに記録します。 |
dryRun | イベントをAmplitudeに送信することなくビルドして記録できます。 開発中に使用してください。 |
validate | 必須フィールドの厳格な検証を実施してください。 |
onEventCallback | (event, statusCode, message) => void追跡対象イベントごとに配信パスから正確に1回呼び出されるコールバック。 |
propagateContext | クロスサービスコンテキスト伝搬を有効にします。『サービス間でコンテキストを伝播する』を参照してください。 |
編集レシピ(名前付き置換、カスタムスクラバー、国際ロケール)については、「プライバシーモードの選択」を参照してください。
エージェントセッションの計測
各エージェント呼び出しをセッションにラップします。 セッションはすべてのイベント(ユーザーメッセージ、モデルレスポンス、ツール呼び出し、スパン)を1つのレコードに関連付けます。
エージェントセッションとは、ユーザーが最初から最後までエージェントに手渡す 1 つのジョブです。つまり、実際の結果をもたらす作業単位です。 新しい ID を作成するのではなく、すでに追跡している ID から sessionId を設定してください。
- チャットボットまたはコパイロット: 会話スレッドID。
- コーディング エージェント: タスクまたはワークセッション ID。
- サポートエージェント:チケットID。
- 音声エージェント:コール ID。
- バックグラウンドまたは自律エージェント:実行またはジョブ ID。
エージェントセッションと標準分析セッション
エージェントセッションはAmplitudeの標準分析セッションではありません。エージェントセッションは、[Agent] Session ID、ユーザーがエージェントに渡すジョブの1つです。Amplitudeの標準的な分析セッション$session_idは、セッションリプレイとプロダクトレポートの基盤となる、ユーザーのアプリまたはウェブへの訪問です。エージェント セッションを自分の ID から設定し、この 2 つをリンクさせる場合は、標準アナリティクス セッション ID をネットワーク境界を越えて転送します。
import { ai, openai } from "@/lib/amplitude";
const agent = ai.agent("chat-handler", {
description: "Customer support chatbot",
});
export async function POST(req: Request) {
const { messages, userId } = await req.json();
return agent.session({ userId }).run(async (s) => {
s.trackUserMessage(messages[messages.length - 1].content);
const response = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages,
});
return Response.json(response);
});
}
Python SDKはai.agent(...).session(...)と同じパターンに従います。run()で開かれたセッションは、コールバックが返ったときに終了します。複数の要求にまたがるセッションの場合は、次のいずれかの方法でセッションを終了します。
- **明示的に閉じる(推奨):**チケットのクローズや実行の完了など、ジョブが終了したときに
trackSessionEnd()(Node) またはtrack_session_end()(Python) を呼び出します。サーバ側の評価はクローズ時に実行されます。クローズは完了マーカーであり、厳密なロックではありません。クローズ後に到着したイベントは引き続き受け入れられて保存されますが、エンリッチメントはセッションごとに1回だけ実行されるため、遅れて到着したターンがセッションのシグナル、ロールアップ、またはセッションレコードに反映されることはありません。 - アイドル タイムアウトで終了させる: タイムアウトのデフォルト値は、セッションに対して最後に受信されたエージェント イベントから測定された 30 分間の非アクティブ状態です。 これは、
idleTimeoutMinutes(Node) またはidle_timeout_minutes(Python) を使用してセッションごとに設定できます。数時間にわたって対応されるサポートチケットの例(240)のように、自然な空白期間が長いジョブの場合は、その値を上げてください。これを-1に設定すると、アイドルウィンドウが最大90日まで延長され、デフォルトの24時間の期間上限がなくなります。そのため、実際には明示的な「セッション終了」のみがクローズの条件となります。
アイドル状態の上書きを設定する場所。 セッションパラメータを設定し、エージェントのcontextにidle_timeout_minutesキーも含めます。現在、コンテキスト ルートは、明示的に閉じられることがないセッションについて、確実にサーバに到達するためのルートです。両方を設定することで、オーバーライドが有効になり、サーバ側の修正プログラムが出荷された後も機能し続けることが保証されます。
const agent = ai.agent("support-bot", {
context: { idle_timeout_minutes: 240 }, // reliable today
});
const session = agent.session({
userId,
sessionId: ticketId,
idleTimeoutMinutes: 240, // the intended parameter
});
同じユーザーが新しい目的で戻ってきた場合は、古いセッションを継続するのではなく、新しいsessionIdを使用して新しいセッションを開始してください。
最小限の実行可能なインストルメンテーション
Agentアナリティクスでは、イベントを関連付けるためにAPIキー、ユーザー識別子(userIdまたはdeviceId)、agentId、sessionIdの4つのフィールドが必要です。推奨パターンは自動的にプロバイダーラッパーを追加するため、SDK はモデル、トークン、コスト、レイテンシのデータをキャプチャします。
2 つの ID ルールにより、1 人のユーザーが次の 2 つに分割されるのを防ぎます。
"anonymous"や""などのプレースホルダuserId、または一時的なIDを渡さないでください。代わりにuserIdを省略してください。Amplitudeは設定後にuserIdを変更できないため、プレースホルダは後でマージされない別のユーザーを作成します。- プレアカウントセッション全体で、同じ
deviceIdを再利用してください。バックエンドがリクエストごとに新しいdeviceIdを生成すると、マージが機能しなくなります。ブラウザSDKからdeviceIdを読み取り、それを転送します。
プロバイダーの呼び出しを自動計測する
SDK は、プロバイダーのアクティビティをキャプチャするための 2 つのゼロコードパスを提供します。
プロバイダーラッパー
構築時にプロバイダークライアントをラップします。ラッパーは、コールを基盤となるクライアントに転送し、要求、応答、トークン、遅延、およびコストを記録します。
import OpenAI from "openai";
const openaiWrapped = new OpenAI({ amplitude: ai });
| プロバイダー | ラッパー |
|---|---|
| OpenAI(チャット完了数と応答数) | new OpenAI({ apiKey, amplitude: ai }) |
| Anthropic | new Anthropic({ apiKey, amplitude: ai }) |
| Azure OpenAI | new AzureOpenAI({ apiKey, amplitude: ai }) |
ジェミニ(@google/generative-ai) | new Gemini({ apiKey, amplitude: ai }) |
Google Gen AI(@google/genai) | new GoogleGenAI({ apiKey, amplitude: ai }) |
| Bedrock (Converse APIs) | new Bedrock({ amplitude: ai, client }) |
| ミストラル | new Mistral({ apiKey, amplitude: ai }) |
コンストラクションサイト(生成箇所)を変更できない場合は、既存のクライアントの生成コードを修正することなく、wrap(existingClient, ai)を使用してインスツルメントします。
サービス範囲はプロバイダによって異なります。 すべてのラッパーはストリーミング、システムプロンプト、コストをキャプチャします。残りはプロバイダーの API が公開している内容に依存します:
| 機能 | OpenAI | Anthropic | ジェミニ | Azure OpenAI | Bedrock | ミストラル |
|---|---|---|---|---|---|---|
| ストリーミング | はい | はい | はい | はい | はい | はい |
| ツールコール追跡 | はい | はい | いいえ | はい | はい | いいえ |
| TTFB測定 | はい | はい | いいえ | はい | いいえ | いいえ |
| トークン統計をキャッシュする | はい | はい | いいえ | いいえ | いいえ | いいえ |
| レスポンスAPI | はい | — | — | — | — | — |
| 推論コンテンツ | はい | はい | いいえ | はい | いいえ | いいえ |
| コスト見積り | はい | はい | はい | はい | はい | はい |
us.anthropic.claude-3-5-sonnetなどの基盤モデル ID は、価格検索用に自動的に正規化されます。
patch()
ゼロコード計装のために、スタートアップ時に patch({ amplitudeAI: ai }) を一度呼び出してください。SDK はサポート対象のクライアントにモンキーパッチを適用し、OpenAI Chat Completions、OpenAI Responses、および Anthropic Messages のメッセージ配列から[Agent] Tool Callイベントを自動的に抽出します。メッセージ検査からは実行タイミングが分からないため、抽出されたツールコールは latencyMs: 0 となります。実際のツールレイテンシが必要な場合は、tool()またはtrackToolCall()を使用してください。
**patch() にはアクティブなセッションコンテキストが必要です。**セッション コンテキスト外で発火されたパッチ適用済みコールは、サイレントに廃棄されます。イベントは発生せず、エラーも発生しません。また、プロバイダー コール自体も影響を受けません。 これにはamplitude-ai-instrumentを使用するAMPLITUDE_AI_AUTO_PATCH=trueも含まれており、プロセス開始時にパッチを適用しますが、アンビエントセッションは確立しません。これは、「計測を行ったがイベントが表示されない」という最も一般的な原因です。 パッチ適用済みの設定を確認するには、セッションで 1 つのコールをラップします。
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
patch({ amplitudeAI: ai });
const agent = ai.agent("verify", { userId: "verify-user-1" });
await agent.session().run(async () => {
await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Hello" }],
});
}); // one [Agent] AI Response
リクエスト・スコープの作業の場合、Express ミドルウェアはセッション・コンテキストを自動的に確立します (フレームワークのノートを参照してください)。
ツールの追跡
tool()高階関数はツール関数をラップするため、SDK は各呼び出しを記録します。
import { tool } from "@amplitude/ai";
const searchProducts = tool(searchDB, { name: "search_products" });
// Inside session.run, call as usual:
const result = await searchProducts(query);
// [Agent] Tool Call event emitted with duration, success, input/output
インラインツールコールまたはサポートされていないフローの場合は、s.trackToolCall(name, latencyMs, success, { input, output })を直接使用してください。
スパンの追跡
スパンは、ベクトル検索、再ランク、ガードレール、またはターン内に存在する時間指定された作業などの内部サブオペレーションをラップします。 これらは[Agent] Spanイベントを発行し、トレースのアイデンティティを共有します。
OTEL 有効時の動作
OTEL が enable_otel() / enableOtel() を通じて有効になっている場合、observe() / @observe はイベントを直接送信する代わりに実際の OTEL スパンを作成します。SpanEventMapperはこれらのスパンを適切な[Agent]イベント タイプに変換します。type パラメータを使用してルーティングを制御します。@observe(type="tool") は、スパンを [Agent] Span ではなく [Agent] Tool Call としてルーティングします。
import { observe } from "@amplitude/ai";
// As a higher-order function:
const runSubAgent = observe(
async (prompt: string) => {
return await subAgent.execute(prompt);
},
{ name: "sub-agent-execution" },
);
// Or explicitly when you need error capture:
const start = Date.now();
try {
const result = await subAgent.execute(prompt);
s.trackSpan({
name: "sub-agent-execution",
latencyMs: Date.now() - start,
inputState: { prompt: prompt.slice(0, 1000) },
outputState: { response: result.slice(0, 1000) },
});
} catch (e) {
s.trackSpan({
name: "sub-agent-execution",
latencyMs: Date.now() - start,
isError: true,
errorType: (e as Error).name,
errorMessage: (e as Error).message,
});
throw e;
}
スパンはターンレベルのイベントを置き換えるものではありません。
エージェントアナリティクスのターン数とインタラクションビューは、スパンではなく、[Agent] User Message および [Agent] AI Responseによって決定されます。内部ステップ周辺のスパンのみを送信する場合、ダッシュボードにはターンレベルのアナリティクスが行われないトレースが表示されます。ユーザーに表示される各サイクルに対して常にユーザーメッセージとAIレスポンスのペアを出力し、その最上位でスパンを使用してください。
手動計装
カスタムフローまたはサポートされていないプロバイダーの場合は、セッションオブジェクトに対して直接手動メソッドを使用してください。それぞれが 1 つの[Agent]イベント タイプにマップされます。
| メソッド | イベント | 使用するタイミング |
|---|---|---|
s.trackUserMessage(text) | [Agent] User Message | ユーザー作成の入力が届く |
s.trackAiMessage(text, model, provider, latencyMs, opts?) | [Agent] AI Response | プロバイダラッパーは自動キャプチャできません |
s.trackToolCall(name, latencyMs, success, opts?) | [Agent] Tool Call | 外部ツールを呼び出す tool() |
s.trackSpan({ name, latencyMs, ... }) | [Agent] Span | 内部サブステップをラップする |
s.runAs(childAgent, fn) | (委譲) | 子エージェントへのルーティング |
ラッパー(プロキシ、カスタムゲートウェイ)を経由しない AI レスポンスの場合、完了レスポンスから使用状況を渡します。
s.trackAiMessage(completedMessage.content, "gpt-4o", "openai", latencyMs, {
inputTokens: usage.prompt_tokens,
outputTokens: usage.completion_tokens,
totalTokens: usage.total_tokens,
});
コストが正しく自動計算されるように、内部ゲートウェイラベルではなく、正規プロバイダーモデルID(gpt-4o-mini、claude-sonnet-4-20250514)を渡してください。
コンテキストによるセグメンテーションを追加
contextディクショナリをai.agent(...)に渡して、すべてのイベントに任意のセグメンテーションディメンションを付加します。SDKはそれを[Agent] Contextにシリアル化するため、新しいグローバルプロパティを登録することなく、AIセッションをセグメント化できます。
const agent = ai.agent("support-bot", {
context: {
agent_type: "executor",
experiment_variant: "reasoning-enabled",
surface: "chat",
},
});
これらのキーは、最も一般的なセグメンテーションのニーズに対応しています。
| キー | 値の例 | ユースケース |
|---|---|---|
agent_type | "planner"、"executor"、"retriever"、"router" | マルチエージェントシステムにおけるエージェントの役割ごとにアナリティクスをグループ化します。 |
experiment_variant | "control", "treatment-v2" | A/Bテスト群全体で品質、放棄状況、またはコストを比較します。 |
feature_flag | "new-rag-pipeline" | セッション中にアクティブだったフラグを追跡します。 |
surface | "chat", "search", "copilot" | インタラクションをトリガーしたUIサーフェスを特定します。 |
prompt_revision | "v7", "2026-02-15" | プロンプトのバージョンを追跡し、agentVersion と並行して回帰を検出します。 |
deployment_region | "us-east-1", "eu-west-1" | レイテンシーやコンプライアンス分析のための地域ごとのセグメント化。 |
canary_group | "canary", "stable" | ロールアウト中にカナリアを安定した導入環境から切り離します。 |
子エージェント間でコンテキストをマージする
子エージェントは親のコンテキストを継承します。 子のキーは一致する親キーよりも優先されます。子が設定していない親キーは保持されます。
const parent = ai.agent("orchestrator", {
context: { experiment_variant: "treatment", surface: "chat" },
});
const child = parent.child("researcher", {
context: { agent_type: "retriever" },
});
// child context = { experiment_variant: "treatment", surface: "chat", agent_type: "retriever" }
Amplitudeでのクエリコンテキスト
[Agent] Context はJSON文字列です。 個々のキーをクエリするには:
- 派生プロパティ:頻繁に使用されるキーの場合、値を永続的に抽出する派生イベントプロパティ([データ] > [プロパティ] > [派生] > [新規])を作成します。
- フィルター: チャートフィルターでの文字列照合には
[Agent] Context contains "key":"value"を使用します。
複数のテナントを使用する
マルチテナントプラットフォームでは、ai.tenant(orgId, opts?)(Node)またはai.tenant(org_id, ...)(Python)を使用してテナントスコープのハンドルを作成します。ハンドルから作成されたすべてのエージェントは customerOrgId を事前にバインドし、各イベントで [Agent] Customer Org ID として反映されるため、すべてのコールで org ID を引き回すことなく、エンドカスタマーごとに使用状況をセグメント化できます。
const tenant = ai.tenant("org-456", { env: "production" });
const agent = tenant.agent("support-bot", { userId: "user-123" });
// agent.track* calls carry [Agent] Customer Org ID = "org-456"
モデル階層の分類
SDK はモデル名からモデル階層を推測し、すべての [Agent] AI Response に [Agent] Model Tier としてそれを関連付けます。階層を使用すると、すべてのモデルをリストすることなく、モデルクラス間でコストとパフォーマンスを比較できます。
| 階層 | 例 | 使用するタイミング |
|---|---|---|
fast | gpt-4o-mini、claude-3-haiku、gemini-flash、gpt-3.5-turbo | 大量の、遅延に敏感なワークロード。 |
standard | gpt-4o, claude-3.5-sonnet, gemini-pro, llama, command | 汎用目的。 |
reasoning | o1, o3-mini, deepseek-r1, Claude with extended thinking | 複雑な推論作業。 |
階層を直接解決するには inferModelTier() / infer_model_tier() を呼び出します:
import { inferModelTier } from "@amplitude/ai";
inferModelTier("gpt-4o-mini"); // 'fast'
inferModelTier("claude-3.5-sonnet"); // 'standard'
inferModelTier("o1-preview"); // 'reasoning'
名前から判別できないカスタムモデルや微調整済みモデルの場合は、AIメッセージ呼び出しで modelTier / model_tier を渡して、推論された値を上書きします。
s.trackAiMessage(response.content, "ft:gpt-4o:my-org:custom", "openai", latencyMs, {
modelTier: "standard",
});
添付ファイルを追跡する
ユーザーメッセージ呼び出しにattachments1 />配列を渡して、メッセージとともに送信されたファイル(画像、PDF、URL)を記録します。各エントリには、type、name、およびsize_bytesが含まれています。
s.trackUserMessage("Analyze this document", {
attachments: [
{ type: "image", name: "chart.png", size_bytes: 102400 },
{ type: "pdf", name: "report.pdf", size_bytes: 2048576 },
],
});
SDKはこれらのプロパティを配列から取得し、添付ファイルのメタデータのみを記録し、ファイルのコンテンツは記録しません:[Agent] Has Attachments、[Agent] Attachment Types、[Agent] Attachment Count、[Agent] Total Attachment Size Bytes、および[Agent] Attachments。添付ファイルは、モデル生成画像などのAI応答にも適用されます。同じattachmentsオプションをAIメッセージコールに渡します。
暗黙的なフィードバックの取得
行動信号は、レスポンスがユーザーのニーズを満たしているかどうかを示すものであり、明示的な評価は必要ありません。 これらのオプションは関連するトラックコールに設定してください。SDKはそれらをクエリ可能な品質プロパティにマップします。
| シグナル | プロパティ | 解釈 |
|---|---|---|
| コピー | [Agent] Was Copied | ユーザーが出力内容をコピーしました。これは肯定的な信号です。 AIメッセージ通話で設定します。 |
| 再生 | [Agent] Is Regeneration | ユーザーがやり直しを要求しました。これはネガティブなシグナルです。 ユーザーメッセージコールに対して設定します。 |
| Edit | [Agent] Is Edit + [Agent] Edited Message ID | ユーザーが以前のプロンプト(フリクションシグナル)を改良しました。ユーザーメッセージコールに対して設定します。 |
| 離脱 | [Agent] Abandonment Turn | ユーザーはNターン後に離脱しました。値が低い場合 (1など) は最初のレスポンスに不満を示します。セッション終了時に設定されます。 |
// AI response the user copied (positive)
s.trackAiMessage("To create a funnel, go to...", "gpt-4o", "openai", latencyMs, { wasCopied: true });
// User regenerates (negative — first response fell short)
s.trackUserMessage("How do I create a funnel?", { isRegeneration: true });
// User edits and resubmits their prompt
s.trackUserMessage("How do I create a conversion funnel for signups?", {
isEdit: true,
editedMessageId: originalMsgId,
});
// User left after the first AI response
agent.trackSessionEnd({ sessionId: "sess-1", abandonmentTurn: 1 });
既存の会話をインポート
1 回の呼び出しで完全なメッセージ履歴をバックフィルするには、trackConversation()(Node) または track_conversation()(Python) を使用します。 { role, content }メッセージの配列を渡します。各メッセージは [Agent] User Messageまたは [Agent] AI Responseとなり、ターン ID は順番に自動的に増分されます。systemメッセージはスキップされます。
import { trackConversation } from "@amplitude/ai";
import * as amplitude from "@amplitude/analytics-node";
trackConversation({
amplitude,
userId: "user-123",
sessionId: "sess-abc",
agentId: "support-bot",
messages: [
{ role: "user", content: "How do I reset my password?" },
{
role: "assistant",
content: "Go to Settings > Security > Reset Password.",
model: "gpt-4o",
provider: "openai",
latency_ms: 1200,
input_tokens: 15,
output_tokens: 42,
},
{ role: "user", content: "Thanks, that worked!" },
],
});
これを使用して、会話履歴をインポートしたり、外部システムからデータを移行したりできます。 この関数は、個々のトラッキングメソッドと同じコンテキストフィールドを受け入れます。
ユーザーフィードバック(スコア)の送信
応答に対する親指のアップまたはダウンやオプションの評価など、ユーザーの明示的なフィードバックを[Agent] Scoreイベントとしてキャプチャします。 スコアはアプリケーションからのみ得られます。Amplitudeのエンリッチメント・パイプラインはスコアを生成しません。
// Thumbs up/down on a specific AI response
ai.score({
userId: "user-123",
name: "user-feedback",
value: 1.0,
targetId: aiMessageId,
targetType: "message",
source: "user",
});
バイナリ評価コントロールにuser-feedbackという名前を使用してください。この名前には特別な意味があり、セッションで検出された否定的なフィードバック信号を上書きします。完全なパターン(セッション中およびリクエスト後のパス、CSAT のスケール)については、セットアップ ページの「サムズアップ/サムズダウン」セクションを参照してください。
SDKを使用せずにイベントを直接取り込む場合は、[Agent] Score Nameをスコア名(例:user-feedback)に設定した[Agent] Scoreイベントを送信してください。
マルチエージェントアーキテクチャ
親エージェントは子エージェントに委任できます。 子エージェントは親のセッションを継承するため、すべてのイベントは1つのセッションIDの下で相互に関連付けられたままになります。
const orchestrator = ai.agent('shopping-agent', { description: 'Orchestrates shopping requests' });
const recipeAgent = orchestrator.child('recipe-agent', { description: 'Finds recipes' });
await orchestrator.session({ userId }).run(async (s) => {
s.trackUserMessage(userInput);
const result = await s.runAs(recipeAgent, async (cs) => {
cs.trackUserMessage(delegatedQuery);
return openai.chat.completions.create({ model: 'gpt-4o', messages: [...] });
});
});
子のLLMコールだけでなく、ディスパッチ自体についてレイテンシーとエラーのメトリックが必要な場合は、委任コールをラップobserve()してください。trackSpan
連携パターン
シングルリクエストAPIエンドポイント
サーバーレス関数やワンショットエンドポイントの場合、ハンドラー内にセッションを作成し、戻る前にフラッシュしてください。そうすることで、イベントが送信される前にランタイムがフリーズしないようになります。
app.post("/chat", async (req, res) => {
const agent = ai.agent("api-handler", { userId: req.userId });
const result = await agent.session({ sessionId: req.sessionId }).run(async (s) => {
s.trackUserMessage(req.body.message);
const start = performance.now();
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages: req.body.messages,
});
s.trackAiMessage(
response.choices[0].message.content ?? "",
"gpt-4o",
"openai",
performance.now() - start,
{
inputTokens: response.usage?.prompt_tokens,
outputTokens: response.usage?.completion_tokens,
},
);
return response.choices[0].message.content;
});
await ai.flush();
res.json({ response: result });
});
長時間持続するセッション(チャットボット)
複数ターンでの会話の場合、セッションを一度作成し、複数のターンにわたって再利用してください。ユーザーとAIのペアをターンごとに追跡します。セッションはrun()が値を返すか、アイドルタイムアウトが発生すると終了します。
const agent = ai.agent("chatbot", { userId: "user-123", env: "production" });
await agent.session({ sessionId: conversationId }).run(async (s) => {
s.trackUserMessage("What is Amplitude?");
const r1 = await llm.chat("What is Amplitude?");
s.trackAiMessage(r1.content, "gpt-4o", "openai", r1.latencyMs, {
inputTokens: r1.usage.input,
outputTokens: r1.usage.output,
});
s.trackUserMessage("How does it track events?");
const r2 = await llm.chat("How does it track events?");
s.trackAiMessage(r2.content, "gpt-4o", "openai", r2.latencyMs, {
inputTokens: r2.usage.input,
outputTokens: r2.usage.output,
});
});
マルチエージェントのオーケストレーション
親エージェントが専門の子に委任する場合、各委任を runAs() / arun_as() でラップします。手動による追跡コールとコールバック内のプロバイダーラッパーの両方が、チャイルドのアイデンティティを自動的に取得します。基本的な委任形態については、「マルチエージェントアーキテクチャ」を参照してください。
runAs/ arun_as の仕組み:
- 親セッションの
sessionId、traceId、およびターンカウンターを共有します。 - コールバックの期間中、子に
agentIdを、親にparentAgentIdを設定します。 - 自動ユーザーメッセージ追跡を抑制し、委任コール内の内部
role: "user"プロンプトが意図しないユーザーターンを作成しないようにします。 [Agent] Session Endは発行されません。子は親セッション内で実行され、その親セッションが 1 つのセッション終了を発行します。- エラーが発生した場合でも、コールバックが完了したときに親コンテキストを復元します。
- 入れ子構造(ネスティング)をサポートしています。子は孫を
runAsことができます。
ファンアウト(並列の子コール、シングル ユーザー ターン)
1回のユーザーターンで複数の並列LLM呼び出しがトリガーされると、newTrace()/new_trace()を使用して新しいトレースを開き、Promise.all(Node)またはasyncio.gather(Python)を使用して子をディスパッチし、参加後に単一のAI応答を送信します。これにより、内部コールの実行数に関係なく、1つのトレース、1つのユーザーターン、1つのAIレスポンスが維持されます。
await orchestrator.session({ sessionId }).run(async (s) => {
s.newTrace();
s.trackUserMessage("Generate plan from quiz results", { context: structuredState });
const [a, b] = await Promise.all([
s.runAs(scorer, () =>
openai.chat.completions.create({ model: "gpt-4o", messages: scorerMessages }),
),
s.runAs(matcher, () =>
openai.chat.completions.create({ model: "gpt-4o", messages: matcherMessages }),
),
]);
s.trackAiMessage(assemble(a, b), "gpt-4o", "openai", totalLatencyMs);
});
ストリームレスポンス
ストリーミングセッションは、ストリームが完全に消費されるまで開いたままにしておく必要があります。ストリームが終了する前にセッションを閉じると、AI レスポンスイベントがドロップされます。
// WRONG: session ends before stream is consumed
return agent.session({ userId }).run(async (s) => {
const stream = await openai.chat.completions.create({
model: "gpt-4o",
messages,
stream: true,
});
return new Response(stream.toReadableStream());
});
// CORRECT: session stays open until stream completes
return agent.session({ userId }).run(async (s) => {
const stream = await openai.chat.completions.create({
model: "gpt-4o",
messages,
stream: true,
});
const readable = stream.toReadableStream();
const [passthrough, forClient] = readable.tee();
const reader = passthrough.getReader();
(async () => {
while (!(await reader.read()).done) {}
})();
return new Response(forClient);
});
Vercel AI SDKを使用して、onFinishコールバック内でフラッシュします:
const result = await streamText({
model: openai("gpt-4o"),
messages,
onFinish: async () => {
await ai.flush();
},
});
標準分析セッションへのリンク
セッションがネットワーク境界を越えると、リクエストヘッダーにAmplitude IDを渡すことで、サーバー側のイベントがユーザーの標準分析セッションに参加するようになります($session_id)。 値をセッションのbrowserSessionIdフィールドとして渡します:
const browserSessionId = req.headers.get("x-amplitude-session-id");
const deviceId = req.headers.get("x-amplitude-device-id");
const session = agent.session({ userId, browserSessionId, deviceId });
バックエンドサービス間のクロスサービス伝搬には、送信側でinjectContext()を、受信側でextractContext(headers)を使用します。
サービス間でコンテキストを伝播
あるバックエンドサービスが別のバックエンドサービスを呼び出す場合、ダウンストリームイベントが新しいトレースを開始するのではなく、同じトレースに参加するように、アクティブなIDとセッションを伝播します。アウトバウンド側では、injectContext()はアクティブなコンテキスト(セッションID、トレースID、ユーザーID)を要求ヘッダーにシリアル化します。受信側では、extractContext(headers) がこれらを読み返します。
// --- Service A (outbound) ---
import { injectContext } from "@amplitude/ai";
await agent.session({ userId, sessionId }).run(async (s) => {
s.trackUserMessage(message);
const headers = injectContext({ "content-type": "application/json" });
await fetch("https://service-b/internal/enrich", {
method: "POST",
headers,
body: JSON.stringify({ message }),
});
});
// --- Service B (inbound) ---
import { randomUUID } from "node:crypto";
import { extractContext, runWithContextAsync, SessionContext } from "@amplitude/ai";
export async function POST(req: Request) {
const extracted = extractContext(Object.fromEntries(req.headers));
const ctx = new SessionContext({
sessionId: extracted.sessionId ?? randomUUID(),
traceId: extracted.traceId ?? null,
userId: extracted.userId ?? null,
});
return runWithContextAsync(ctx, async () => {
await handleEnrichment(req);
});
}
injectContext()は新しいheadersオブジェクトを返し、オリジナルを決して変更しません。アクティブなセッションがない場合、ヘッダーを変更せずに返すため、無条件に呼び出すことは安全です。
サポートされているプロバイダーとフレームワーク
ネイティブラッパーを提供するプロバイダー: OpenAI(Chat Completions + Responses)、Anthropic、Azure OpenAI、Gemini(@google/generative-ai)、Google Gen AI(@google/genai)、Mistral、Bedrock(Converse APIs)。
ファーストパーティとの統合を備えたエージェントフレームワーク: LangChain、LlamaIndex、OpenAI Agents SDK、Anthropic Tool Use、Claude Agent SDK(ClaudeAgentSDKTracker)、Anthropic Managed Agents、CrewAI(Pythonのみ)。
フレームワークとの統合
以下の統合は、エージェントフレームワーク独自のコールバックまたはトレースシステムをAgent アナリティクスに橋渡しします。それぞれはaiインスタンスとアイデンティティフィールドを受け取り、フレームワークにフックします。 CrewAIはPythonのみに対応しています。Nodeでは、設計上AmplitudeCrewAIHooksがエラーをスローします。代わりに、LangChain または OpenTelemetry パスを使用してください。
LangChain
AmplitudeCallbackHandlerをLangChainのコールバックに渡します。
import { AmplitudeCallbackHandler } from "@amplitude/ai";
const handler = new AmplitudeCallbackHandler({ amplitudeAI: ai, userId: "user-123", sessionId: "sess-1" });
// Pass handler to any LangChain runnable via { callbacks: [handler] }
LlamaIndex
import { AmplitudeLlamaIndexHandler } from "@amplitude/ai";
const handler = new AmplitudeLlamaIndexHandler({ amplitudeAI: ai, userId: "user-123", sessionId: "sess-1" });
OpenAIエージェントSDK
AmplitudeTracingProcessorをトレース プロセッサとして登録します。
import { AmplitudeTracingProcessor } from "@amplitude/ai";
const processor = new AmplitudeTracingProcessor({ amplitudeAI: ai, userId: "user-123", sessionId: "sess-1" });
// Register with the OpenAI Agents SDK trace provider.
Anthropicツールの使用
AmplitudeToolLoopAnthropicのマルチターンtool_useループを実行し、各AIの応答とツール呼び出しを追跡します。
import { AmplitudeToolLoop } from "@amplitude/ai";
const loop = new AmplitudeToolLoop({ amplitudeAI: ai, userId: "user-123", sessionId: "sess-1" });
await loop.run({ client, model: "claude-sonnet-4-20250514", messages, tools, toolExecutor });
OpenTelemetry属性マッピング
フレームワークがすでにOpenTelemetry GenAIスパンを発行している場合、SDKはそれらを[Agent]プロパティにマップします。この機能を有効にする方法(スパンファーストenableOtel()/enable_otel()パスやマニュアルAmplitudeGenAIExporter/AmplitudeAgentExporterエクスポータなど)については、「OpenTelemetryスパンの取り込み」を参照してください。エクスポータのマッピングは次のように適用されます。
| OTELスパン属性 | [Agent] プロパティ | メモ |
|---|---|---|
gen_ai.response.model / gen_ai.request.model | [Agent] Model Name | レスポンスモデルが優先されます。 |
gen_ai.system / gen_ai.provider.name | [Agent] Provider | 必須です。それがないスパンは無視されます。 |
gen_ai.usage.input_tokens | [Agent] Input Tokens | |
gen_ai.usage.output_tokens | [Agent] Output Tokens | |
gen_ai.usage.total_tokens | [Agent] Total Tokens | 欠落している場合は、入力 + 出力から派生します。 |
gen_ai.request.temperature | [Agent] Temperature | |
gen_ai.request.top_p | [Agent] Top P | |
gen_ai.request.max_tokens | [Agent] Max Output Tokens | |
gen_ai.response.finish_reasons | [Agent] Finish Reason | 最初の理由は配列の場合です。 |
gen_ai.tool.name | [Agent] Tool Name | スパンを [Agent] Tool Call としてルーティングします。 |
gen_ai.input.messages | $llm_message | ユーザロールメッセージのみで、かつプライバシーモードで許可されている場合にのみ表示されます。 |
| スパンの持続時間 | [Agent] Latency Ms | |
スパンのステータス ERROR | [Agent] Is Error, [Agent] Error Message |
一部の信号にはOTELと同等のものがなく、ネイティブプロバイダーのラッパーが必要です。これには、推論コンテンツとトークン、TTFB、ストリーミング検出、暗黙的なフィードバック、添付ファイル、および[Agent] Parent Message IDを通じたイベントグラフのリンクが含まれます。同じコールに対してOTELとネイティブラッパーを一緒に実行できます。SDK は重複除外を実行するため、二重イベントは発生しません。
プロバイダー固有の注意事項
Vercel AI SDK
プロバイダーラッパーは、Vercelの抽象化ではなく、基盤となるSDK (openai) をインスツルメントします。@ai-sdk/openaiのみが存在する場合は、openaiを直接の依存関係として追加するか、patch()にフォールバックしてください。ストリーミング応答の場合は、onFinish を使用して await ai.flush() を呼び出します(ストリーム応答を参照してください)。
Claude Agent SDK
@amplitude/ai/integrations/claude-agent-sdkからClaudeAgentSDKTrackerを使用します。イベントを有効にするには、2つのフィールドが必要です。agentId43 />ai.agent()(44 />上:LLM使用アプリケーションレジストリ内のAI機能を識別します)と、userId45 />sessionId + 46 />agent.session()(47 />上:イベントを1つのインタラクションに結びつけます)です。
import { AmplitudeAI } from "@amplitude/ai";
import { ClaudeAgentSDKTracker } from "@amplitude/ai/integrations/claude-agent-sdk";
import { query } from "@anthropic-ai/claude-agent-sdk";
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
const agent = ai.agent({ agentId: "code-reviewer" });
const tracker = new ClaudeAgentSDKTracker();
await agent.session({ userId: "u1", sessionId: "sess-abc" }).run(async (s) => {
for await (const message of query({
prompt: "Analyze this codebase",
options: { hooks: tracker.hooks(s) },
})) {
tracker.process(s, message);
}
});
tracker.hooks(session) 正確なツールレイテンシを持つ PreToolUse / PostToolUse フックを返します。tracker.process(session, message) は、AI 応答とユーザーメッセージのメッセージストリームを処理します。
Anthropic管理対象エージェント
LLM呼び出しはユーザーのコードではなくAnthropicのクラウド上で行われるため、プロバイダーのラッパーは機能しません。手動による追跡とポーリングclient.beta.sessions.events.list()を使用してください。イベントタイプをSDKメソッドにマップするには:
| Anthropicイベント | SDK呼び出し |
|---|---|
user.message | trackUserMessage(text)(ポーリング時ではなく送信時に追跡) |
agent.message | trackAiMessage(text, model, 'anthropic', latencyMs) |
agent.tool_use / agent.mcp_tool_use / agent.custom_tool_use | trackToolCall(name, latencyMs, success) |
agent.tool_result / agent.mcp_tool_result | スキップ(ツールの使用時に遅延がキャプチャされます) |
session.error | trackAiMessage(errorMsg, model, 'anthropic', latencyMs, { isError: true }) |
events.list() は以前に取得されたイベントを返すため、ポーリング間でイベントの重複を排除します。
const seenIds = new Set<string>(savedState.seenIds);
for (const event of response.data) {
if (seenIds.has(event.id)) continue;
seenIds.add(event.id);
// track event
}
レイテンシーは、session.status_runningポーリングの往復時間ではなく、processed_atイベントとイベントの間の壁時計時間として測定してください。events.list()Anthropic Admin API には使用状況やトークン数が含まれないため、コスト追跡には Anthropic Admin API が必要です。
OpenAI Assistants API
プロバイダーのラッパーは、Assistants API を自動的に計測しません(非同期/ポーリングベース)。 手動追跡を使用してください:メッセージの作成時にtrackUserMessage()、完了イベントのポーリング時にtrackAiMessage()を使用します。
MCPサーバ
MCPプロトコルは、送信元のユーザープロンプトをツールに渡さないため、MCPサーバはそれをキャプチャできません。LLM がその意図を自己説明できるようにするため、各ツールにオプションのrationaleパラメータを追加し、使用可能なセッションコンテンツを維持できます。
フレームワークに関するメモ
Next.js (アプリルーター)
SDK はサーバー側モジュールで初期化してください。クライアントコンポーネントでは初期化しません。 next.config.ts内のserverExternalPackagesに@amplitude/aiを追加します。セッション作成は各ルートハンドラ内でラップしてください。サーバーレス環境では、イベントが送信される前にランタイムがフリーズしないよう、ハンドラが戻る前に await ai.flush() を呼び出してください。
Express / Fastify / Hono
バンドルされているミドルウェアを使用して、すべてのリクエストにaiをアタッチします。
import { createAmplitudeAIMiddleware } from "@amplitude/ai";
app.use(
createAmplitudeAIMiddleware({
amplitudeAI: ai,
userIdResolver: (req) => req.headers["x-user-id"] ?? null,
}),
);
エッジランタイムとCloudflare Workers
@amplitude/aiはCloudflare Workersではバンドルできません。 SDKはnode:async_hooks、node:module、および node:cryptoに依存しています。 Workers Buildsはnodejs_compat_v2が有効な場合でもアップロードを拒否します。@amplitude/analytics-nodeも互換性がありません(Nodeのhttpに依存します)。
唯一安全なインポートは import type { ... } from '@amplitude/ai/types' です。これはコンパイル時に消去されます。 ランタイム追跡のために、[Agent]イベントを直接構築するフェッチベースのトランスポートを使用してください:
import type { AmplitudeClientLike, AmplitudeEvent } from "@amplitude/ai/types";
class FetchAmplitudeClient implements AmplitudeClientLike {
private _apiKey: string;
private _buffer: AmplitudeEvent[] = [];
constructor(apiKey: string) {
this._apiKey = apiKey;
}
track(event: AmplitudeEvent): void {
this._buffer.push(event);
}
async flush(): Promise<void> {
if (!this._buffer.length) return;
const events = this._buffer.splice(0);
try {
const resp = await fetch("https://api2.amplitude.com/2/httpapi", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ api_key: this._apiKey, events }),
});
if (!resp.ok) console.error(`[Amplitude] Flush failed: ${resp.status}`);
} catch (err) {
console.error(`[Amplitude] Flush error: ${(err as Error).message}`);
}
}
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
if (env.AMPLITUDE_TRACKING_DISABLED) return handleRequest(request, env);
const transport = new FetchAmplitudeClient(env.AMPLITUDE_API_KEY);
transport.track({
event_type: "[Agent] User Message",
user_id: userId,
event_properties: {
"[Agent] Session ID": sessionId,
"[Agent] Agent ID": "my-agent",
$llm_message: { text: content },
},
});
// After the LLM call completes:
transport.track({
event_type: "[Agent] AI Response",
user_id: userId,
event_properties: {
"[Agent] Session ID": sessionId,
"[Agent] Agent ID": "my-agent",
"[Agent] Model Name": model,
"[Agent] Provider": "anthropic",
"[Agent] Latency Ms": latencyMs,
$llm_message: { text: responseText },
},
});
// Non-blocking flush so events ship before the isolate terminates
ctx.waitUntil(transport.flush());
return new Response("ok");
},
};
リクエストごとにFetchAmplitudeClient構築することで、リクエスト間のバッファ漏れを防ぐことができます。 イベントのinsert_id重複排除には crypto.randomUUID() を使用し、トラッキングを無効にできるよう AMPLITUDE_TRACKING_DISABLED 環境変数で制御します。
サーバレス環境での実行
SDKはサーバーレスプラットフォーム(Vercel、AWS Lambda、Netlify、Google Cloud Functions、Azure Functions、Cloudflare Pages)を環境変数から自動検出します。それを検出すると、promiseが解決される前にsession.run()が保留中のイベントをフラッシュするため、明示的なai.flush()は必要ありません。長時間稼働しているサーバーでは、セッションごとのフラッシュをスキップし、アナリティクスクライアントが通常どおりバッチ処理を行うようにします。
autoFlushオプション(Node)またはauto_flush(Python)を使用して、これをセッションごとに制御できます。 自動検出を使用するには未設定のままにするか、セッション終了時に常にフラッシュするにはtrueに、決してフラッシュしない場合にはfalseに設定してください。
外部でイベントを追跡する場合session.run()、ハンドラーが戻る前にフラッシュします。そうしないと、イベントがバッファリングされたままランタイムがプロセスをフリーズさせる可能性があります。
ai.flush()とai.shutdown()は、それぞれ異なるライフサイクルに対応しています。
ai.flush()はバッファリングされたイベントを送信し、SDK を稼働させ続けます。 サーバーレスハンドラやAPIエンドポイントで使用することで、応答する前に配信を保証できます。ai.shutdown()は、基盤となるアナリティクスクライアントをフラッシュしてから閉じます。プロセス終了時に一度呼び出してください。たとえば、SIGTERMハンドラです。apiKeyを通じてクライアントを作成した場合にのみクライアントを閉じます。独自のインスタンスを渡した場合、そのライフサイクルを所有することになります。
process.on("SIGTERM", () => {
ai.shutdown();
process.exit(0);
});
Cloudflare Workers(エッジ分離)はサポートされているサーバーレスターゲットではありません。完全なSDKをWorkerにバンドルすることはできません。フェッチベースの転送については、EdgeランタイムとCloudflare Workersを参照してください。
OpenTelemetryスパンの取り込み
以下のエクスポーターは、このSDKを既に使用しているNodeまたはPythonアプリ内でインプロセスで実行されます。スパンがすでに OpenTelemetry Collector に到達している場合は、SDK を完全にスキップし、代わりにコレクタを Amplitude の OTLP エンドポイントに向けることができます。『OpenTelemetryトレースを直接送信』を参照してください。
OpenTelemetry GenAIスパンを既に出力しているスタック(OpenLIT、Traceloop、OpenAI の OTel 計測)の場合、それらを[Agent]イベントにマッピングします。
AmplitudeGenAIExporter(インバウンド、本番環境対応):GenAI のセマンティック・コンベンション・スパンを取り込み、[Agent]イベントを送信します。 GenAI 以外のスパンを無視するため、混在パイプラインでも安全です。AmplitudeAgentExporter(アウトバウンド、実験用):AmplitudeイベントをフラットなOTelスパンに変換して、他のバックエンドに転送します。 トレース階層を保持しません。
プライバシーモードを選択する
AIConfigでcontentModeを設定します。SDK 内のすべてのコンテンツ送信チャネルは単一のプライバシーゲートを経由するので、このモードは直接の SDK 呼び出し、プロバイダーラッパー、observe()スパン、OTel 例外記録、およびあらゆるフレームワーク統合に均一に適用されます。
full(デフォルト): プロンプトと応答テキストをキャプチャします。redactPii: trueはデフォルトでオンになっていて、イベントがプロセスを終了する前に電子メール、電話番号、SSN、クレジットカード番号、IP アドレス、および base64 でエンコードされた画像データをスクラブします。SDKは電話とSSNの検出を米国形式用に調整します。国際ロケール用には、customRedactionPatternsまたはcustomRedactionFnを追加してください。metadata_only: 会話テキスト、例外メッセージ、スタックトレース、またはシステム命令がAmplitudeに到達することはありません。トークン数、レイテンシ、モデル、コスト、セッショングループ化は引き続き表示されます。機密データまたは規制対象データに使用してください。customer_enriched:デフォルトではテキストはありません。 事前に採点された概要をtrackSessionEnrichment()経由で送信してください。 既存の評価スタックを使用しているチーム向けに設計されています。
metadata_onlyは次のチャネルをゲートします。これは、シングルゲート契約の顧客に表示される表現です。
- ユーザーメッセージとAIレスポンスのメッセージテキスト(
$llm_message.text) - ツールの入力と出力 (
[Agent] Tool Input,[Agent] Tool Output) - システム プロンプト (
[Agent] System Prompt) observe()およびOTelスパンのエラーパスによって記録される例外メッセージとスタックトレース- フレームワークによって発行されたコンテンツ属性 (
gen_ai.system_instructionsおよびgen_ai.input.messages/gen_ai.output.messagesなど) - スコアコメント (
[Agent] Comment)
マネージドエージェントアーキテクチャの場合、redactPii: true を full とともに使用することをお勧めします。マネージドAPIはすでにメッセージコンテンツをサーバー側に保存しているため、metadata_only はプライバシー上のメリットを追加することはありません。
独自のセッションエンリッチメントを提供
customer_enrichedモードでは、SDK はメッセージテキストを送信しません。 お客様は独自の評価パイプラインを実行し、その結果を構造化されたセッションレベルの強化として返送します。 コンプライアンスにコンテンツの送信がゼロであることが必要である場合や、評価ロジックがAmplitudeの組み込みサーバー側の強化機能を超えている場合に使用します。
SessionEnrichments オブジェクトを構築し、trackSessionEnrichment() (Node) または track_session_enrichment() (Python) を使用して送信します。エンリッチメントは[Agent] Session Enrichmentイベントとして格納され、[Agent] Enrichmentsプロパティにシリアル化されます。セッションを閉じる前にエンリッチメントを設定すると、同じフィールドが[Agent] Session Endにも追加されます。
SessionEnrichmentsオブジェクトの主要なフィールド
| フィールド | 目的 |
|---|---|
qualityScore, sentimentScore | セッションの数値の質とセンチメント。 |
overallOutcome | resolvedまたは escalated などのターミナル結果です。 |
topicClassifications | タクソノミー名とTopicClassification(トピック、信頼度、サブカテゴリ)のマッピング。 |
rubricScores | RubricScore(名前、スコア、根拠、証拠)の配列。 |
agentChain, rootAgentName | マルチエージェント実行用のエージェントトポロジ。 |
requestComplexity | low、medium high、 などの難易度バケット。 |
errorCategories | パイプラインからの分類済みの障害信号。 |
messageLabels | 各トラッキングコールから返されたメッセージIDによってキーが設定されたメッセージごとのラベル。 |
customMetadata | 独自のアナリティクスに使用できる任意のキー/値データ。 |
import {
AmplitudeAI,
AIConfig,
ContentMode,
SessionEnrichments,
RubricScore,
TopicClassification,
} from "@amplitude/ai";
const ai = new AmplitudeAI({
apiKey: process.env.AMPLITUDE_AI_API_KEY!,
config: new AIConfig({ contentMode: ContentMode.CUSTOMER_ENRICHED }),
});
const agent = ai.agent("support-bot", { agentVersion: "2.1.0" });
// 1. Run the conversation — no content is sent, only metadata.
const { sessionId } = await agent.session({ userId: "user-42" }).run(async (s) => {
s.trackUserMessage("Why was I charged twice?");
s.trackAiMessage(aiResponse.content, "gpt-4o", "openai", latencyMs);
return { sessionId: s.sessionId };
});
// 2. Score the raw messages with your own pipeline.
const evalResults = await myEvalPipeline(conversationHistory);
// 3. Ship the enrichments back to Amplitude.
const enrichments = new SessionEnrichments({
qualityScore: evalResults.quality,
sentimentScore: evalResults.sentiment,
overallOutcome: evalResults.outcome,
topicClassifications: {
billing: new TopicClassification({ topic: "billing-dispute", confidence: 0.92 }),
},
rubricScores: [new RubricScore({ name: "accuracy", score: 4, maxScore: 5 })],
customMetadata: { eval_model: "gpt-4o-judge-v2" },
});
agent.trackSessionEnrichment(enrichments, { sessionId });
これにより、Amplitudeの組み込みエンリッチメントと同じイベントプロパティ(トピック、ルーブリック、結果、メッセージラベル)が生成されますが、代わりにパイプラインから取得されます。
メッセージラベル
メッセージ ラベルは、フィルタリングとセグメンテーションのために個々のメッセージに付加されたキーと値のペアです。たとえば、ルーティング タグ (flow、surface)、分類子出力 (intent、sentiment)、ビジネス コンテキスト (tier、plan)などです。 メッセージイベントで[Agent] Message Labelsとして送信されます。これらを接続するには、2 つの方法があります。
- トラッキング時にインラインで、
labelsをtrackUserMessage()/track_user_message()に渡すことによって。 - さかのぼって、セッション後に分類器の結果が到着したときに、各トラッキングコールから返されたメッセージIDを
SessionEnrichments.messageLabelsキーとするを通じて実行されます。
コストとトークンの管理
s.trackAiMessage(...)は、バンドルされているPydantic genai-pricesカタログを通じてモデル名とトークン数から[Agent] Cost USDを自動計算します。
自動料金設定を妨げる要因は 3 つあります。
- 認識できないモデル名です。 Vertex AI エイリアスは正規の
claude-sonnet-4-20250514と一致しませんclaude-sonnet-4-6。 内部ゲートウェイラベルは解決されません。 真新しいモデルはまだ genai-prices にはないかもしれません。正規プロバイダー ID を渡すか、上書きするためにtotalCostUsdを明示的に設定してください。 - ファインチューニング済みモデル。
ft:のモデル名は自動で料金計算が行われません。ファインチューニング済みモデルやカスタムモデルについては、totalCostUsdを明示的に渡してください。 - **プロンプト キャッシュを使用した不正確な。
inputTokens**SDKはキャッシュインクルーシブであることを想定していますinputTokens(キャッシュされたトークンはサブセットであり、決して加算的ではありません)。プロバイダの慣習は異なります:
プロバイダー名の注意点:
- **Gemini → google (Node)。**Node SDK は料金検索のために内部で
geminiをgoogleにマッピングします。defaultProvider: 'gemini'を渡しても料金取得できますが、生のカタログを調べてgeminiを探す場合は、代わりにgoogleを探してください。 - **Bedrockの候補生成。**ルックアップではドット区切りのプレフィックスが段階的に(
region.vendor.model→vendor.model→model)取り除かれ、regional.とglobal.のバリアントが試行されます。そのため、新しいAWSリージョンやBedrockベンダーはカタログを更新することなく自動的に機能します。 - ローカルの Python レートオーバーライド。 Python SDK には、アップストリームカタログのギャップを埋めるため、
costs.pyにローカル料金表が含まれています。具体的には、gpt-5.6(アップストリームではCache-Writeプレミアムとロングコンテキストの料金階層が不足しています)、Fireworksのfastルーター(API レスポンスがベースモデル IDを返す場合でも高速な料金階層で課金されます)、およびアップストリームのOpenAIのリリースへの対応が遅れている場合に、例えばgpt-5.4をgpt-5.2の料金に対応付ける_MODEL_PRICE_ALIASESマップなどが含まれます。
| プロバイダー | 未処理のAPIの動作 | inputTokensとして渡すもの |
|---|---|---|
| OpenAI | prompt_tokens にはすでに cached_tokens が含まれています | 直接使用する |
| Anthropic / Bedrock (Converse) | キャッシュトークンをinput_tokens除外 | input_tokens + cache_read_input_tokens + cache_creation_input_tokens |
| ジェミニ | キャッシュされたものを含みます。promptTokenCountレポートは個別にcachedContentTokenCount行われます。 | promptTokenCountを直接使用してください |
組み込みのAnthropicラッパー、Bedrockラッパー、およびGeminiラッパーがこの正規化を代わりに処理します。手動での発信者はtrackAiMessage自分で処理する必要があります。SDKが差額料金を適用するように、cacheReadTokens / cacheCreationTokens を別々に渡してください。
自分でコストを計算する必要がある場合は、totalCostUsd を呼び出し、その結果をcalculateCost({ modelName, inputTokens, outputTokens, cacheReadInputTokens, cacheCreationInputTokens }) として渡してください。
calculateCost()のルール:
inputTokensは、キャッシュ読み取りとキャッシュ作成を含む入力の合計です。 Anthropicの場合は、input_tokens + cache_read_input_tokens + cache_creation_input_tokensを渡してください。OpenAI の場合、prompt_tokensにはキャッシュされたトークンが既に含まれているため、そのまま渡してください。outputTokensは、プロバイダーが出力する推論トークンを含む出力の合計です。 OpenAIのcompletion_tokensには既に推論が含まれているため、別途追加しないでください。cacheReadInputTokensおよびcacheCreationInputTokensはinputTokensのサブセットであり、差別料金率を適用するためだけに使用されます。reasoningTokensパラメータは非推奨であり、無視されます。 これは下位互換性のために保持されています。別々に渡すとリクエストのコストが過剰になります。
欠落コストのデバッグ
SDKが料金を計算できない場合、一意の(model, provider, reason)タプルごとに警告を1件記録します。プロセスごとに記録される一意のタプルは最大100個に制限されます。ログをgrepしてUnable to calculate cost for model=を検索すると、どのルックアップが失敗したのか、その理由を確認できます。
非対話型エージェントのランコストをレポートする
プロバイダーラッパーはすべての [Agent] AI Response で自動的に出力を生成します。 [Agent] Cost USDバッチジョブやアーティファクトジェネレータなど、最終的なテキスト応答なしで実行を終了するエージェントは、[Agent] AI Response を発生しないことがありますので、そのコストは発生しません。 実行の最後に trackRunCost() を呼び出して、コールごとのイベントではカバーされなかったコストを出力します:
s.trackRunCost(totalCostUsd, inputTokens, outputTokens, model, "openai", {
latencyMs,
content: "[Artifact: batch run]", // optional; omit for a cost-only event
});
s.track_run_cost(
total_cost_usd,
input_tokens,
output_tokens,
model,
"openai",
latency_ms=latency_ms,
content="[Artifact: batch run]", # optional; omit for a cost-only event
)
trackRunCost() はデルタコストのみを出力するため、実行中にすでに追跡されているコールごとのコストと調整されます。 デフォルトではコンテンツは空になるため、コストのみのバランシングイベントの場合は content を省略するか、セッションビューアにバブルを表示したい場合は短い文字列を渡します。実行完了のコストは、ライフサイクル専用でコストフィールドを持たない [Agent] Session End ではなく、この方法で付随させてください。
料金データを最新の状態に保つ
コストはバンドルされているgenai-pricesカタログに依存するため、新しくリリースされたモデルは、カタログが更新されるまで[Agent] Cost USD/0とレポートする場合があります。実行時に最新の料金を取得するには、起動時にenableLivePriceUpdates()(Node)またはenable_live_price_updates()(Python)を使用してオプトインしてください。
- デフォルトの状態はオフです。 オプトインする必要があります。
- デフォルトの更新間隔は1時間です(Nodeでは
3_600_000ミリ秒単位、Pythonでは3600秒単位)。最初の引数で設定可能です。 - アップストリーム エンドポイント: パブリック
raw.githubusercontent.com、Pydanticgenai-pricesGitHubリポジトリ。Amplitudeが所有するエンドポイントではありません。 - 障害モード:サイレント。 ネットワークエラーは無視され、バンドルされたカタログが引き続き使用されます。
- Idempotent: 2回目の呼び出しは何も行いません。
- 動作していることを確認する方法: デバッグ フラグやステータス出力はありません。 唯一の信号は、
[Agent] Cost USD以前はコストが不足していたモデルにデータが入力され始めていることです。
有効にするタイミング: バンドルカタログにまだ含まれていない新しくリリースされたモデルにアップグレードした場合。トグルがない場合、SDK のバージョンが変わるまで、[Agent] Cost USDそのモデルについては省略されたままになります。
プロセスがエアギャップ状態で実行されている場合や、環境が へのアウトバウンド HTTPS をブロックしている場合には、この機能を有効にしないでくださいraw.githubusercontent.com。 これには何のメリットもなく、すべてのスタートアップはネットワークの往復に失敗するコストを負担することになります。
デプロイメタデータのキャプチャ
SDK はスタートアップ時にデプロイを識別する環境変数を読み取り、すべての[Agent]イベントにその値をスタンプします。これにより、コード全体で値をスレッド化することなく、SHA またはリリースでグラフをフィルタリングできます。
| 環境変数 | フォールバック | 出力形式: | 行動 |
|---|---|---|---|
AMPLITUDE_GIT_SHA | GIT_SHA | [Agent] Git SHA | 逐語的に読み上げます。 |
AMPLITUDE_GIT_REF | GIT_REF | [Agent] Git Ref | 逐語的に読み上げます。通常はブランチ名またはタグです。 |
AMPLITUDE_GIT_REPO | GIT_REPO | [Agent] Git Repo | オプトインのみです。サニタイズ済み:userinfo (user:pass@) は削除され、認証情報が削除された場合に SDK は 1 つの警告を記録します。 |
リポジトリのURLのオプトインは、環境変数でのみ行えます。以前の SDK リリースでは、git メタデータからリポジトリを自動的にキャプチャしていましたが、これは削除されました。SDK のアップグレード後に空になった場合は、[Agent] Git Repo明示的に設定してくださいAMPLITUDE_GIT_REPO。
セマンティックキャッシュヒットの追跡
独自のセマンティックキャッシュまたはレスポンスキャッシュから完全なレスポンスを提供するときは、AIメッセージの呼び出しで wasCached: true (Node) または was_cached=True (Python) を渡します。これはトークンレベルのプロンプトキャッシュとは異なり、[Agent] Was Cachedにマッピングされるため、キャッシュヒット率とそれによって節約されるコストをチャートで確認できます。
メッセージコンテンツの構成
trackUserMessageへの最初の引数は、[Agent] User Message上の$llm_message.textになります。これは、セッションリスト、セグメンテーション、およびエンリッチメントが「ユーザーの発言」として扱うものです。 2つの実践的なルール:
**メッセージ本文として、自然言語による短い行を渡すようにしてください。**たとえば、実際のプロンプトやヘッドレス ジョブの標準的な概要:
s.trackUserMessage(
"Summarize the attached design doc and list open questions",
{
context: { structuredPayload: payloadRecord },
},
);
メッセージ本文として大きな JSON ブロブを渡さないでください。 このプロダクトはセッションタイトルとしてJSONを使用し、グラフを未加工のJSONで分類します。
// Session label becomes the JSON
s.trackUserMessage(JSON.stringify(payloadRecord));
構造化されたセグメンテーションのディメンションをcontextオプションに追加します([Agent] Context JSONになり、グラフでクエリ可能になります)。サーバー側のエンリッチメントが構造化データに基づいて推論できるように、コンテンツに重要な事実も保持してください。エンリッチメントは eval 入力を [Agent] Context ではなく、主にターンテキストから導き出します。
SDKを使用しないインスツルメント
サポートされていないランタイム(Java、Go、Ruby、エッジ環境)の場合、イベントを直接Amplitude HTTP APIに送信してください。
curl -X POST https://api2.amplitude.com/2/httpapi \
-H 'Content-Type: application/json' \
-d '{
"api_key": "YOUR_API_KEY",
"events": [{
"event_type": "[Agent] User Message",
"user_id": "user-123",
"event_properties": {
"[Agent] Session ID": "sess-abc",
"[Agent] Agent ID": "support-chatbot",
"$llm_message": { "text": "How do I cancel my subscription?" }
}
}]
}'
メッセージコンテンツには $llm_message.text を使用してください(取り込みパイプラインはインタラクションテキスト用にこのプロパティを読み取ります)。完全なプロパティ参照とイベント JSON の例については、Agent アナリティクスのタクソノミーを参照してください。
イベントを直接送信する場合、SDK がそれ以外の方法で処理する内容については、お客様が責任を負います。
| 懸念事項 | あなたがすべきこと |
|---|---|
| セッションID | 会話ごとに1つのIDを生成し、すべてのイベントで[Agent] Session IDとして設定します。 |
| エージェントの識別情報 | すべてのイベントに[Agent] Agent IDを設定してください。この情報がないと、Agent Analyticsコンシューマーがイベントをセッションに関連付けることができないため、HTTP APIが200を返してもイベントは誤ってグループ化されます。 |
| 重複除外 | イベントごとにユニークな値insert_idを設定することで、再試行しても重複が発生しないようにします。 |
| プロパティ接頭辞 | すべてのプロパティ名の前に[Agent] (またはセッションリプレイIDの場合は[Amplitude] )を付けてください。 |
| コストとトークン | ご自身で [Agent] Cost USD 算出してください。SDKの自動料金設定は利用できません。 |
| サーバの拡張 | コンテンツが存在する場合、[Agent] Session Endが表示されると、引き続き自動的に実行されます。 |
データを検証する
doctor を実行して環境変数、インストール済みの依存関係、および event-pipeline への接続を検証します。
npx amplitude-ai doctor
デフォルトの動作ではAMPLITUDE_AI_API_KEYを読み取ります。 アプリでキーに別の名前を付ける場合は、-key-envを使用してdoctorに正しい変数を指定してください:
npx amplitude-ai doctor -key-env MY_KEY_NAME
値を指定せず-key-envを渡した場合、doctorはデフォルトにサイレントフォールバックするのではなく、使用方法のメッセージを表示してエラーになります。この規則は、フラグが存在する場合にのみ適用されます。
次に、イベントがAmplitudeに到着することを確認します:
- プロジェクトのライブイベントストリームを開きます。
- 計装済みコードからテストセッションを送信します。
- 数秒以内に、これらのプロパティが入力された
[Agent] AI Responseイベントが表示されるはずです。[Agent] Session ID,[Agent] Agent ID[Agent] Model Name,[Agent] Provider[Agent] Latency Ms[Agent] Input Tokens,[Agent] Output Tokens[Agent] Cost USD
summary() を使用したローカル検証
導入前に、MockAmplitudeAI.summary() を使用して、キャプチャされたすべてのイベントのフィルレートレポートを取得します。データがAmplitudeに到達する前に、8つの検証ゲートをチェックし、ギャップにフラグを立てます。
import { AIConfig } from "@amplitude/ai";
import { MockAmplitudeAI } from "@amplitude/ai/testing";
const mock = new MockAmplitudeAI(new AIConfig({ contentMode: "full" }));
const agent = mock.agent("test-agent", { userId: "u1" });
await agent.session({ sessionId: "s1" }).run(async (s) => {
s.trackUserMessage("hello");
s.trackAiMessage("response", "gpt-4o-mini", "openai", 150);
});
console.log(mock.summary());
サマリー出力は次のようになります。
Agent Analytics fill-rate report
================================
Events captured: 2
[Agent] User Message: 1
[Agent] AI Response: 1
Verification gates (8/8 passing):
✓ user_id or device_id present
✓ [Agent] Session ID present
✓ [Agent] Agent ID present
✓ [Agent] Model Name present
✓ [Agent] Provider present
✓ [Agent] Latency Ms > 0
✓ [Agent] Input Tokens > 0
✓ [Agent] Output Tokens > 0
✓ [Agent] Cost USD > 0
一般的な問題の修正:
| ゲート障害 | 原因 | 修正 |
|---|---|---|
user_id 欠落 | セッションに渡された userIdまたは deviceIdはありません | Browser SDKを使用して、agent.session()でuserIdを設定するか、deviceIdを転送します |
Session ID 欠落 | ID なしでセッションが作成されました | sessionIdをagent.session()に渡す |
Model / Provider | サポートされているプロバイダーまたはカスタムゲートウェイなしでpatch()を使用しています | モデルとプロバイダーを明示的に trackAiMessage() に渡すか、プロバイダー ラッパーを使用してください |
Input/Output Tokens = 0 | プロバイダーはストリーミングモードでの使用状況を返しません | onFinish / stream_options: { include_usage: true } を使用して最終的なトークン数をキャプチャする |
Cost USD = 0 | 認識できないモデル名 | 正規のプロバイダーモデルIDを使用するか、明示的に totalCostUsd を設定してください |
モッククライアントに対するテスト
CIの場合、イベントが正しく発行されることをアサートするために、@amplitude/ai/testingのMockAmplitudeAIを使用します:
import { AIConfig } from "@amplitude/ai";
import { MockAmplitudeAI } from "@amplitude/ai/testing";
const mock = new MockAmplitudeAI(new AIConfig({ contentMode: "full" }));
const agent = mock.agent("test-agent", { userId: "u1" });
await agent.session({ sessionId: "s1" }).run(async (s) => {
s.trackUserMessage("hello");
s.trackAiMessage("response", "gpt-4o-mini", "openai", 150);
});
mock.assertEventTracked("[Agent] User Message", { userId: "u1" });
mock.assertSessionClosed("s1");
// Data quality gate: every AI Response must carry the eight verification fields
for (const e of mock.eventsOfType("[Agent] AI Response")) {
const p = e.event_properties ?? {};
expect(e.user_id || e.device_id).toBeTruthy();
expect(p["[Agent] Session ID"]).toBeTruthy();
expect(p["[Agent] Model Name"]).toBeTruthy();
expect(p["[Agent] Provider"]).toBeTruthy();
expect(p["[Agent] Latency Ms"]).toBeGreaterThan(0);
expect(p["[Agent] Input Tokens"]).toBeGreaterThan(0);
expect(p["[Agent] Output Tokens"]).toBeGreaterThan(0);
expect(p["[Agent] Cost USD"]).toBeGreaterThan(0);
}
不適切なモデル名やトークン数の欠落など、実行時に例外をスローせずにダッシュボードを破損させるサイレントなインスツルメンテーションの回帰を検出するため、このテストをCIに維持してください。
信頼性とエラー処理
インスツルメンテーションによってアプリケーションがダウンすることはありません:
- **トラッキングコールが例外をスローすることはありません。**すべての
track*メソッドは、内部的に独自のエラーをキャッチして記録します。 シリアル化のバグや不正なフィールドがエージェントのリクエストパスを中断することはありません。 - **SDKはイベントをバッファリングし、再試行します。**基盤となる
@amplitude/analytics-nodeクライアントは、イベントをバッチ処理し、失敗した送信をそのトランスポート層から再試行します。 - **障害が発生しても、適切に機能を縮小します。**Amplitudeに到達できない場合、SDKは再試行回数を使い果たした後、イベントをサイレントにドロップします。 アプリケーションは引き続き動作します。
開発時には、userId や sessionId などの不足している必須フィールドを早期に顕在化させるために、AIConfig で validate: true (Node) または validate=True (Python) を設定してください。検証エラーは ValidationErrorをスローするため、本番環境に到達する前にテストでそれらをキャッチできます。最も厳格な CI チェックを行うには dryRun / dry_run と組み合わせてください。
自動計測ツールとCLIツール
コール サイトを編集せずに計測するには、プロセス開始時にサポートされているプロバイダーに自動パッチを適用します。 これは SDK が接続されていることを確認する最も速い方法です。完全なイベントモデル (ユーザーメッセージ、セッション、スコア) については、「SDKの初期化」に示されているように、エージェントとセッションを使用します。
# Wrapper command
AMPLITUDE_AI_API_KEY=xxx AMPLITUDE_AI_AUTO_PATCH=true amplitude-ai-instrument node app.js
# Or Node's ESM preload flag directly
AMPLITUDE_AI_API_KEY=xxx AMPLITUDE_AI_AUTO_PATCH=true node --import @amplitude/ai/register app.js
どちらのランタイムも同じ環境変数を読み取ります。
| 変数 | 概要 |
|---|---|
AMPLITUDE_AI_API_KEY | 自動パッチ適用を有効にするには必須です。 |
AMPLITUDE_AI_AUTO_PATCH | 自動パッチ適用をオンにするには、"true" である必要があります。 |
AMPLITUDE_AI_CONTENT_MODE | full (デフォルト)、metadata_only、または customer_enriched。 |
AMPLITUDE_AI_DEBUG | "true" 各イベントを stderr に記録します。 |
アプリを実行せずに環境を検査するには、amplitude-ai status を使用します。 これにより、インストールされている SDK のバージョン、検出されたプロバイダー パッケージ、および現在の環境変数の設定が出力されます。依存関係とイベント パイプライン接続を検証するには、doctorコマンドの「データの検証」を参照してください。
データカタログにイベントスキーマを登録する
SDKには、すべての[Agent]イベントタイプとそのプロパティをAmplitudeのデータカタログに登録するCLIが付属しているため、イベントは取り込みから推測されるのではなく、説明、タイプ、および必須フラグとともにドキュメント化された状態で反映されます。
前提条件: タクソノミー API にアクセスできるプランと、[設定] > [プロジェクト] からプロジェクトの API キーとシークレットキーが必要です。
バンドルされている CLI は、イベントカタログを読み取り、実行可能な curl コマンドを出力します。これ自体はネットワーク要求を行わないため、コマンドを実行する前に確認できます。
# Print commands with your keys
npx amplitude-ai-register-catalog --api-key YOUR_KEY --secret-key YOUR_SECRET
# Execute immediately
npx amplitude-ai-register-catalog --api-key YOUR_KEY --secret-key YOUR_SECRET | bash
# EU data residency
npx amplitude-ai-register-catalog --api-key YOUR_KEY --secret-key YOUR_SECRET --eu | bash
これらのコマンドはべき等です。欠落しているイベントやプロパティを作成し、既存のものを更新するため、SDK のアップグレードで新しいフィールドが追加された後でも安全に再実行できます。
デバッグとドライラン
2 つのAIConfigフラグはローカルでのイベント検査に役立ち、それぞれ自動計装で使用できる対応する環境変数が用意されています。
debug: true (Node) / debug=True (Python) は、すべてのイベントの 1 行サマリーを stderr に出力し、引き続き Amplitude にイベントを送信します:
[amplitude-ai] [Agent] AI Response | user=user-123 session=sess-abc agent=my-agent model=gpt-4o latency=1203ms tokens=150→847 cost=$0.0042
dryRun: true (Node) / dry_run=True (Python) はイベント JSON 全体を stderr に記録し、何も送信しません。これを使用して、ライブAPIキーなしでローカル開発やCIでのイベントシェイプを検証できます。自動計装の場合、代わりにコマンドでAMPLITUDE_AI_DEBUG=trueを設定してください。
トラブルシューティング
| 問題点 | 解決策 |
|---|---|
| イベントが全くありません | 最も一般的な原因は、アクティブなセッション コンテキストの外でLLM呼び出しが実行されることです。この場合、パッチ適用済みの呼び出しは記録されずにドロップされます。呼び出しをsession.run()でラップするか、ミドルウェアを使用するか、または patch()を参照してください。 |
| イベントは200を返しますが、セッションにはなりません | イベントに[Agent] Agent IDが含まれていないため、HTTP API が受信を確認した後、Agentアナリティクスコンシューマーはこれらのイベントをドロップします。ai.agent()にagentIdを設定するか、HTTP APIに送信する際にすべてのイベントに[Agent] Agent IDを含めてください。 |
[Agent] Cost USD は $0 であるか、欠落しています | モデル名がgenai-pricesに存在しないか、微調整されたft:モデルです。正規のプロバイダーIDを使用するか、totalCostUsdを明示的に設定してください。以前のSDKリリースでは$0が記録されますが、現在のリリースではこのプロパティは省略されています。 |
| Anthropicキャッシュトークンの不一致 | cache_read_input_tokensとcache_creation_input_tokensをinputTokensに追加します(コストとトークンの管理に移動します)。 |
| 空のセッションレコード | 最新のSDKに更新。セッションは実際のアクティビティでのみ実現するようになりました。 |
| イベントはライブイベントに表示されない | APIキーがAgent Analyticsプロジェクトと一致することを確認します。 |
node:async_hooks Cloudflare Workersでのエラー | FetchAmplitudeClientパターンを使用してください。 |
ツールコールには latencyMs: 0 | これらは、patch() によってメッセージ配列から抽出されました。 実際の遅延にはtool()またはtrackToolCall()を使用します。 |
| ストリームが終了する前にセッションが終了する | ストリームレスポンスを参照し、ストリームが消費されるまでセッションを開いたままにしてください。 |
API リファレンス
コアクラス
| API | 目的 |
|---|---|
new AmplitudeAI({ apiKey, config? }) | SDKの初期化 |
new AIConfig({ contentMode?, redactPii?, customRedactionPatterns?, customRedactionFn?, dryRun?, debug? }) | プライバシーとデバッグ設定 |
ai.agent(agentId, opts?) | バインドされたエージェントを作成する |
agent.child(agentId, opts?) | 委任のための子エージェントを作成する |
agent.session(opts?) | セッションを作成する(サーバーレスでの自動フラッシュ) |
session.run(fn) | セッションコンテキストを使用して作業を実行する |
s.runAs(childAgent, fn) | 子エージェントへの委任 |
ai.enableOtel() / ai.enable_otel() | OTELスパンファースト計装を有効にする |
ai.otelEnabled / ai.otel_enabled | OTELモードがアクティブかどうか(読み取り専用) |
ai.flush() | バッファリングされたイベントをフラッシュ(サーバーレス/ストリーミング) |
ai.shutdown() | フラッシュしてからアナリティクスクライアントを閉じます(プロセス終了) |
ai.tenant(orgId, opts?) | customerOrgIdを事前バインドするテナントスコープのハンドル |
ai.score({ userId, name, value, targetId?, targetType?, source? }) | ユーザーからの明確なフィードバックを次のように記録する [Agent] Score |
セッション追跡方法
| メソッド | イベント |
|---|---|
s.trackUserMessage(content, opts?) | [Agent] User Message |
s.trackAiMessage(content, model, provider, latencyMs, opts?) | [Agent] AI Response |
s.trackToolCall(name, latencyMs, success, opts?) | [Agent] Tool Call |
s.trackSpan({ name, latencyMs, ... }) | [Agent] Span |
s.trackSessionEnrichment({...}) | セッションレベルの拡張(カスタマーエンリッチドモード) |
高階関数
| HOF | イベント | 用途 |
|---|---|---|
tool(fn, { name }) | [Agent] Tool Call | ツール関数をラップする |
observe(fn, { name, type? }) | [Agent] Span | 任意の関数をラップしてオブザーバビリティに対応させます(OTELを使用:実際のスパンを作成し、typeイベントルーティングを制御します) |
その他のAPI
| API | 用途 |
|---|---|
patch({ amplitudeAI: ai }) / unpatch() | ゼロコード計装により、メッセージ配列からツールコール(Tool Calls)を自動抽出します。 |
wrap(client, ai) | 既存のプロバイダクライアントをその構造を変更せずにラップする |
injectContext() / extractContext(headers) | サービス間の伝播 |
usingAttributes(attrs, fn) / using_attributes(**attrs) | アイデンティティとセッションコンテキストをOTELスパンに付加する |
updateCurrentSpan(attrs) / update_current_span(**attrs) | アクティブな OTEL スパン上の属性を更新する |
createAmplitudeAIMiddleware(opts) | Express / Fastify / Hono ミドルウェア |
calculateCost({ modelName, ... }) | オーバーライドが必要な場合は、コストを直接計算します totalCostUsd |
trackRunCost(...) / track_run_cost(...) | 非会話型エージェントの実行終了デルタ[Agent] Cost USDを出力する |
trackConversation({ ... }) | メッセージ履歴全体をイベントとしてバックフィルする |
inferModelTier(model) | モデルの階層を解決する (fast / standard / reasoning) |
enableLivePriceUpdates() | 実行時にコストデータをgenai-prices更新する |
MockAmplitudeAI (@amplitude/ai/testing) | 決定的なテストダブル。充填率レポートのために.summary()を呼び出します |
ClaudeAgentSDKTracker (@amplitude/ai/integrations/claude-agent-sdk) | Claude Agent SDK の連携 |
これは役に立ちましたか?