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(...) 또는 프로바이더 래퍼에 의해 방출됩니다.사전 조건
- 에이전트 분석이 활성화된 Amplitude 프로젝트.
- 에이전트 분석 객체 보기 권한입니다. 관리자는 역할기반 액세스 제어(RBAC)를 통해 액세스를 부여합니다.
- Node.js 또는 Python(또는 Amplitude HTTP API를 호출할 수 있는 런타임)으로 계측하기 위한 에이전트 코드베이스입니다.
- 올바른 데이터 센터에 대한 프로젝트의 API 키입니다. Agent 분석은 미국과 유럽에서 운영됩니다.
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 대신 가져오십시오. 필요에 따라 더 많은 래핑된 공급자를 추가하십시오.
개발 다음 기간동안 이벤트를 전송하지 않고 유효성을 검사하려면 dryRun: true에 dry_run=True (Node) 또는 AIConfig (Python)을 설정하십시오.
SDK 구성
AmplitudeAI 생성자에 AIConfig를 전달하십시오. 모든 옵션은 선택 사항이며, 기본값은 대부분의 앱에서 작동합니다.
| 옵션 | 설명 |
|---|---|
contentMode | 'full' (기본값), 'metadata_only', 또는 'customer_enriched'. 어떤 메시지 콘텐츠가 Amplitude에 도달할지 제어합니다. 개인정보 보호 모드 선택을 참조하십시오. |
redactPii | 이벤트가 프로세스를 떠나기 전에 추적된 콘텐츠에서 이메일, 전화번호, SSN, 신용카드 번호 및 IP 주소를 삭제합니다. 기본값은 true입니다. 옵트아웃하려면 false로 설정하십시오. |
customRedactionPatterns | 추가 비식별화 패턴. 명명된 레이블에 대해 정규식 문자열([REDACTED]로 대체됨) 또는 { pattern, replacement } 객체를 허용합니다. |
customRedactionFn | (text) => string사용자 정의 편집 논리(예: NER 라이브러리)에 대한 콜백입니다. 모든 정규식 기반 편집 후에 실행됩니다. |
debug | 추적된 모든 이벤트를 stderr에 기록합니다. |
dryRun | Amplitude로 전송하지 않고 이벤트를 빌드하고 기록하세요. 개발 다음 기간동안 사용하십시오. |
validate | 필수 필드에 대한 엄격한 유효성 검사를 시행합니다. |
onEventCallback | (event, statusCode, message) => void전달 경로에서 추적된 이벤트당 정확히 한 번씩 호출되는 콜백입니다. |
propagateContext | 교차 서비스 컨텍스트 전파를 활성화합니다. 서비스 간 컨텍스트 전파를 참조하십시오. |
비식별화 레시피(명명된 교체물, 사용자 지정 스크러버, 국제 지역)에 대해서는 개인정보 보호 모드 선택을 참조하십시오.
에이전트 세션 계측
각 에이전트 호출을 세션으로 래핑합니다. 세션은 모든 이벤트(사용자 메시지, 모델 응답, 도구 호출, 스팬)를 단일 레코드로 상관 관계를 유지합니다.
에이전트 세션은 사용자가 처음부터 끝까지 에이전트에게 전달하는 하나의 작업으로, 실제 결과를 내는 작업 단위입니다. 새로운 ID를 만들지 말고, 이미 추적하고 있는 ID를 사용하여 sessionId를 설정하세요:
- 채팅봇 또는 코파일럿: 대화 스레드 ID입니다.
- 코딩 에이전트: 작업 또는 작업 세션 ID입니다.
- 지원 에이전트: 티켓 ID입니다.
- 음성 에이전트: 통화 ID입니다.
- 백그라운드 또는 자율 에이전트: 실행 또는 작업 ID입니다.
에이전트 세션 대 표준 분석 세션:
에이전트 세션은 Amplitude의 표준 분석 세션과 다릅니다. 에이전트 세션 [Agent] Session ID은 사용자가 에이전트에게 전달하는 하나의 작업입니다. Amplitude의 표준 분석 세션은 세션 리플레이 및 제품 보고서를 지원하는 사용자의 앱 또는 웹 방문입니다.$session_id 사용자 고유의 ID에서 에이전트 세션을 설정하고, 두 세션을 연결하려면 표준 분석 세션 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)을 호출합니다. 서버측 평가는 종료 시 실행됩니다. 세션 종료는 완료를 표시하는 것이며, 이후의 이벤트를 차단하는 기능은 아닙니다. 종료 후 들어오는 이벤트도 계속 수집되어 저장되지만, 보강 작업은 세션당 한 번만 실행되므로 늦은 턴은 세션의 신호, 롤업 또는 세션 기록에 나타나지 않습니다. - 유휴 시간 초과로 인해 세션을 종료합니다: 기본 유휴 시간 초과는 30분이며, 세션에서 마지막 에이전트 이벤트를 수신한 시점부터 비활성 상태가 지속된 시간을 기준으로 합니다. 이 기능은
idleTimeoutMinutes(노드) 또는idle_timeout_minutes(파이썬)을 사용하여 세션별로 구성할 수 있습니다. 몇 시간 동안 작업하는 지원 티켓과 같이 자연스러운 공백이 긴 작업의 경우240이를 높이십시오.-1유휴 기간을 최대 90일로 늘리고 기본 24시간 기간 제한을 무시하도록 설정하면, 실제로는 명시적인 세션 종료(Session End)만이 유일한 종료 방법이 됩니다.
유휴 시간 초과 설정 위치입니다. 세션 매개 변수를 설정하고 에이전트 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로 새로운 세션을 시작하십시오.
최소한의 실행 가능한 계측
에이전트 분석에는 이벤트를 상호 연관시키기 위해 API 키, 사용자 식별자(userId 또는 deviceId), agentId, 및 sessionId의 네 가지 필드가 필요합니다. 권장 패턴은 공급자 래퍼를 자동으로 추가하므로 SDK는 모델, 토큰, 비용 및 지연 시간 데이터를 캡처합니다.
두 가지 ID 규칙은 단일 사용자가 두 사용자로 분리되는 것을 방지합니다.
"anonymous",""과 같은 위치 표시자 또는 임시 ID를 전달하지userId마십시오. 대신userId을 생략하십시오. Amplitude는userId가 설정된 후에는 변경할 수 없으므로 위치 표시자는 나중에 병합되지 않는 별도의 사용자를 생성합니다.- 사전 계정 세션 전체에서 동일한 정보를 재사용하십시오
deviceId. 백엔드가 요청마다 새deviceId항목을 생성하면 병합이 중단됩니다. 브라우저 SDK에서deviceId를 읽고 전달하십시오.
공급자 호출 자동 계측
SDK는 공급자 활동을 캡처하기 위한 두 개의 제로 코드 경로를 제공합니다.
공급자 래퍼
구축 시 공급자 클라이언트를 래핑하십시오. 래퍼는 호출을 기본 클라이언트로 전달하고 요청, 응답, 토큰, 지연 시간 및 비용을 기록합니다.
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 API) | new Bedrock({ amplitude: ai, client }) |
| 미스트랄 | new Mistral({ apiKey, amplitude: ai }) |
생성 코드를 수정하지 않고 기존 클라이언트를 계측하려면, 생성 위치를 변경할 수 없는 경우에 wrap(existingClient, ai) 를 사용하십시오.
서비스 범위는 공급업체에 따라 다릅니다. 모든 래퍼는 스트리밍, 시스템 프롬프트 및 비용을 캡처합니다. 나머지는 공급자의 API가 노출하는 내용에 따라 달라집니다.
| 기능 | 오픈AI | Anthropic | 제미니 | Azure OpenAI | Bedrock | 미스트랄 |
|---|---|---|---|---|---|---|
| 스트리밍 | 예 | 예 | 예 | 예 | 예 | 예 |
| 도구 호출 추적 | 예 | 예 | 아니요 | 예 | 예 | 아니요 |
| TTFB 측정 | 예 | 예 | 아니요 | 예 | 아니요 | 아니요 |
| 캐시 토큰 통계 | 예 | 예 | 아니요 | 아니요 | 아니요 | 아니요 |
| 응답 API | 예 | — | — | — | — | — |
| 콘텐츠 추론 | 예 | 예 | 아니요 | 예 | 아니요 | 아니요 |
| 비용 산정 | 예 | 예 | 예 | 예 | 예 | 예 |
Bedrock 모델 ID와 같은 항목은 가격 조회를 위해 자동으로 정규화됩니다us.anthropic.claude-3-5-sonnet.
patch()
제로 코드 계측을 위해 스타트업 시 patch({ amplitudeAI: ai })한 번 호출하십시오. SDK는 지원되는 클라이언트를 몽키 패치하고 OpenAI Chat Completions, OpenAI Responses 및 Anthropic Messages의 메시지 배열에서 이벤트를 자동으로 [Agent] Tool Call추출합니다. 메시지 검사를 통해 실행 타이밍을 확인할 수 없으므로 추출된 도구 호출은 latencyMs: 0에 저장됩니다. 실제 도구 지연 시간이 필요한 경우 tool() 또는 trackToolCall()를 사용하십시오.
patch() 는 활성 세션 컨텍스트를 필요로 합니다. 세션 컨텍스트 외부에서 발생한 패치된 호출은 아무런 알림 없이 삭제됩니다. 이벤트가 발생하지 않으며 오류가 발생하지 않고, 공급자 호출 자체도 영향을 받지 않습니다. 여기에는 AMPLITUDE_AI_AUTO_PATCH=true와 amplitude-ai-instrument의 조합이 포함됩니다. 이 조합은 프로세스 시작 시 패치를 적용하지만, 앰비언트 세션을 설정하지 않습니다. 이는 "계측을 적용했지만 이벤트를 볼 수 없습니다"라는 문제의 가장 일반적인 원인입니다. 패치된 설정이 제대로 적용되었는지 확인하려면, 다음과 같이 단일 세션 내에서 호출을 실행하십시오.
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이벤트를 발생시키고 추적의 ID를 공유합니다.
OTEL 지원 동작
OTEL이 enable_otel() / enableOtel()을 통해 활성화된 경우 observe() / @observe는 이벤트를 직접 발생시키는 대신 실제 OTEL 스팬을 생성합니다. SpanEventMapper는 이러한 범위를 적절한 [Agent] 이벤트 유형으로 변환합니다. [Agent] Span매개변수를 사용하여 라우팅을 제어하십시오. [Agent] Tool Call이 매개변수는 범위를 @observe(type="tool")가 아닌 type로 라우팅합니다.
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 분석 턴 카운트 및 상호작용 뷰는 범위가 아닌 [Agent] User Message및 [Agent] AI Response에 의해 결정됩니다. 내부 단계 주위의 범위만 방출하면 대시보드에 턴레벨 분석 기능이 없는 추적이 표시됩니다. 항상 사용자에게 표시되는 각 주기에 대해 사용자 메시지/AI 응답 쌍을 방출하고 맨 위에 범위를 사용하세요.
수동 계측
사용자 지정 흐름이나 지원되지 않는 공급자의 경우 세션 객체에서 직접 수동 메서드를 사용하십시오. 각 이벤트는 단일 [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 로 표시되므로, 모든 통화에서 조직 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, 확장된 사고력을 가진 클로드 | 복잡한 추론 작업. |
계층을 직접 확인하려면 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",
});
첨부 파일 추적
메시지와 함께 전송된 attachments파일(이미지, 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 | 사용자가 재실행을 요청했으므로 부정적인 신호가 발생했습니다. 사용자 메시지 호출에 설정합니다. |
| 편집 | [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 });
기존 대화 가져오기
한 번의 호출에서 전체 메시지 기록을 다시 채우려면 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",
});
이진 thumbs 컨트롤의 이름으로 user-feedback을 사용하세요. 이 이름에는 특별한 의미가 적용되어 세션에서 감지된 부정적 피드백 신호보다 우선합니다. 전체 패턴(세션 내 및 요청 후 경로, CSAT 척도)에 대해서는 설정 페이지의 엄지손가락 위쪽/엄지손가락 아래쪽 섹션을 참조하십시오.
SDK를 사용하는 대신 이벤트를 직접 수집하는 경우, [Agent] Score Name를 귀하의 점수 이름(예: user-feedback)으로 설정하여 [Agent] Score이벤트를 전송하십시오.
멀티 에이전트 아키텍처
상위 에이전트는 하위 에이전트에게 위임할 수 있습니다. 하위 에이전트는 상위 세션을 상속하므로 모든 이벤트는 하나의 세션 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()감싸십시오. 수동 추적 호출 및 콜백 내부의 공급자 래퍼는 모두 자식의 ID를 자동으로 수집합니다. 기본 위임 형태에 대해서는 멀티 에이전트 아키텍처를 참조하십시오.
작동 방식 runAs / arun_as :
- 상위 세션의
sessionId,traceId및 턴 카운터를 공유합니다. - 콜백이 지속되는 동안 자식에게
agentId및 부모에게parentAgentId를 설정합니다. - 자동 사용자 메시지 추적을 억제하므로 위임 통화의 내부
role: "user"프롬프트가 가짜 사용자 전환을 유발하지 않습니다. [Agent] Session End를 방출하지 않습니다. 자식은 부모 세션 내에서 실행되며, 이는 하나의 세션 종료를 방출합니다.- 콜백이 완료되면 오류가 발생한 경우에도 상위 컨텍스트를 복원합니다.
- 중첩 지원: 자식이 손자(또는 하위)를
runAs가질 수 있습니다.
팬아웃(병렬 자식 통화, 단일 사용자 턴)
한 번의 사용자 턴이 여러 개의 병렬 LLM 호출을 트리거하면 newTrace()/new_trace()로 새로운 추적을 열고, Promise.all(Node) 또는 asyncio.gather (Python)으로 자식을 디스패치하며, 참여한 후 단일 AI 응답을 발생시킵니다. 이를 통해 내부 호출의 실행 횟수에 관계없이 하나의 추적, 하나의 사용자 턴, 하나의 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 });
백엔드 서비스 대상 구간 교차 서비스 전파의 경우 아웃바운드 측에서 사용하고 extractContext(headers)인바운드 측에서 injectContext()사용하십시오.
서비스 전체에 컨텍스트 전파
한 백엔드 서비스가 다른 백엔드 서비스를 호출할 때 활성 ID와 세션을 전파하여 다운스트림 이벤트가 새 추적을 시작하는 대신 동일한 추적에 참여하도록 합니다. 아웃바운드 측에서는 활성 컨텍스트(세션 ID, 추적 ID, 사용자 ID)를 요청 헤더로 injectContext()직렬화합니다. 인바운드 측에서는 이를 다시 읽습니다.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() 는 새로운 헤더 객체를 반환하며 원본을 변경하지 않습니다. 활성 세션이 없으면 헤더를 변경되지 않고 반환하므로 무조건적으로 호출하는 것이 안전합니다.
지원되는 공급자 및 프레임워크
네이티브 래퍼를 제공하는 공급자: OpenAI(채팅 완료 + 응답), Anthropic, Azure OpenAI, Gemini(@google/generative-ai), Google Gen AI(@google/genai), Mistral, Bedrock(Converse API).
자사 통합을 포함한 에이전트 프레임워크: LangChain, LlamaIndex, OpenAI SDK, Anthropic Tool Use, Claude Agent SDK(ClaudeAgentSDKTracker), Anthropic Managed Agents, CrewAI(Python 전용).
프레임워크 통합
아래의 통합 기능은 에이전트 프레임워크의 자체 콜백 또는 추적 시스템을 에이전트 분석에 연결합니다. 각각은 인스턴스와 ID 필드를 가져온 다음 프레임워크에 연결됩니다ai. CrewAI는 Python 전용입니다. Node에서는 설계상 AmplitudeCrewAIHooks에러를 발생시킵니다. 대신 LangChain 또는 OpenTelemetry 경로를 사용하십시오.
랭체인
LangChain의 콜백에 이를 전달합니다AmplitudeCallbackHandler.
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 Tool Use
AmplitudeToolLoop Anthropic의 멀티 턴 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이 항목만 있는 경우 직접 종속성으로 추가하거나 patch()로 폴백하십시오openai. 스트리밍 응답의 경우 onFinish를 사용하여 await ai.flush()를 호출합니다(스트림 응답 참조).
클로드 에이전트 SDK
@amplitude/ai/integrations/claude-agent-sdk에서 ClaudeAgentSDKTracker사용하십시오. 이벤트가 유용하려면 agentId 두 개의 필드가 필요합니다. ai.agent() 하나는 agent.session() on(LLM 사용 애플리케이션 레지스트리에서 AI 기능을 식별함)과 다른 하나는 userId+ sessionId on(이벤트를 단일 상호작용으로 연결함)입니다.
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 응답과 사용자 메시지의 메시지 스트림을 처리합니다.
앤스로픽 관리 에이전트
LLM 호출은 사용자 코드가 아닌 Anthropic 클라우드에서 발생하기 때문에 공급자 래퍼는 작동하지 않습니다. 수동 추적 및 폴링 사용client.beta.sessions.events.list(). 이벤트 유형을 SDK 메소드에 매핑합니다.
| 앤스로픽 이벤트 | 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 | 건너뛰기(tool_use 시간에 지연 시간이 캡처됨) |
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가 필요합니다.
OpenAI 어시스턴트 API
공급자 래퍼는 Assistants API를 자동으로 계측하지 않습니다(비동기식/폴링 기반). 메시지를 생성할 때, 완료 이벤트를 폴링할 때 trackAiMessage()수동 추적 사용trackUserMessage().
MCP 서버
MCP 프로토콜은 원본 사용자 프롬프트를 도구에 전달하지 않으므로 MCP 서버는 이를 캡처할 수 없습니다. 각 도구에 선택적 rationale 매개변수를 추가하면 LLM이 해당 의도를 자체적으로 설명할 수 있으며 세션 콘텐츠를 사용할 수 있습니다.
프레임워크 참고 사항
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 또한 호환되지 않습니다(노드의 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중복 제거에 사용하며, AMPLITUDE_TRACKING_DISABLED환경 변수 뒤에서 이를 비활성화하기 위한 crypto.randomUUID()게이트 추적도 수행합니다.
서버리스 환경에서 실행
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 앱 내에서 인프로세스(in-process)로 실행됩니다. 스팬이 이미 OpenTelemetry Collector에 도달한 경우 SDK를 완전히 건너뛰고 대신 Collector를 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을 추가합니다.customRedactionFnmetadata_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)
관리형 에이전트 아키텍처의 경우 full를 사용하는 것이 좋습니다. redactPii: true관리형 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로 전송됩니다. 두 가지 방법으로 연결하십시오.
- 추적 시간에
labels/trackUserMessage()로 전달하여 인라인으로track_user_message(). - 소급적으로, 세션 후에 분류기 결과가 도착할 때 각 추적 호출에서 반환된 메시지 ID를 키로 하여
SessionEnrichments.messageLabels를 통해 처리할 수 있습니다.
비용 및 토큰 관리
s.trackAiMessage(...) 는 번들로 제공되는 Pydantic genai-prices 카탈로그를 통해 모델 이름과 토큰 수를 기준으로 [Agent] Cost USD자동으로 계산합니다.
자동 가격을 방해하는 세 가지 요소는 다음과 같습니다:
- 인식할 수 없는 모델 이름입니다.
claude-sonnet-4-6와 같은 Vertex AI 별칭은 정규claude-sonnet-4-20250514과 일치하지 않습니다. 내부 게이트웨이 레이블은 확인되지 않습니다. 새로운 모델은 아직 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에는 상위 카탈로그에 없는 캐시 쓰기 프리미엄과 긴 컨텍스트 구간 요금을 추가하고 Fireworks의fast라우터는 API 응답에서 기본 모델 ID가 반환되더라도 빠른 등급의 요금을 적용합니다. 또한_MODEL_PRICE_ALIASES매핑을 통해 상위 카탈로그의 업데이트가 OpenAI 릴리스보다 늦어지는 경우gpt-5.4를gpt-5.2요금으로 연결합니다.
| 공급자 | 원시 API 동작 | inputTokens로 전달할 항목 |
|---|---|---|
| 오픈AI | 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이를 직접 처리해야 합니다. cacheReadTokens/cacheCreationTokens를 별도로 전달하면 SDK가 차등적 가격을 적용합니다.
비용을 직접 계산해야 할 경우 calculateCost({ modelName, inputTokens, outputTokens, cacheReadInputTokens, cacheCreationInputTokens })를 totalCostUsd 호출하여 결과를 로 전달하십시오.
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) 튜플당 하나의 경고를 기록하며, 프로세스당 고유 튜플 수는 최대 100개입니다. 로그에서 Unable to calculate cost for model=을 검색하여 실패한 조회와 그 이유를 확인하십시오.
비대화형 에이전트의 운영 비용 리포트
공급자 래퍼는 매번 마다 자동으로 [Agent] Cost USD를 방출합니다[Agent] AI Response. 배치 작업 및 아티팩트 생성기와 같이 최종 텍스트 응답 없이 실행을 완료한 [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 카탈로그에 따라 달라지므로, 새로 출시된 모델은 카탈로그가 업데이트될 때까지 0의 [Agent] Cost USD를 리포트할 수 있습니다. 런타임에 최신 요금 정보를 가져오려면 시작 시 Node에서는enableLivePriceUpdates(), Python에서는 enable_live_price_updates()를 사용하도록 설정하십시오.
- 기본 상태: 꺼짐. 반드시 옵트인해야 합니다.
- 기본 새로 고침 간격: 1시간(Node의 경우
3_600_000ms, Python의 경우3600s). 첫 번째 인수를 통해 구성 가능합니다. - 상위 엔드포인트: 공개
raw.githubusercontent.com, Pydanticgenai-pricesGitHub 저장소. Amplitude 소유의 엔드포인트가 아닙니다. - 실패 모드: 알림 없음 네트워크 오류는 무시되고 번들로 제공된 카탈로그는 계속 사용 중입니다.
- 멱등성: 두 번째 호출에서는 아무 작업도 수행하지 않습니다.
- 작동 여부 확인 방법: 디버그 플래그나 상태 출력이 없습니다. 유일하게 확인할 수 있는 신호는 이전에 비용 데이터가 누락되었던 모델에
[Agent] Cost USD가 표시되기 시작한다는 것입니다.
활성화해야 하는 경우: 번들 카탈로그에 아직 포함되지 않은 새로 출시된 모델로 업그레이드한 경우. 이 설정을 활성화하지 않으면 SDK 버전이 업데이트될 때까지 해당 모델의 [Agent] Cost USD가 누락된 상태로 유지됩니다.
활성화하지 않아야 하는 경우: 프로세스가 에어갭 화경에서 실행되거나, 사용자 환경에서 raw.githubusercontent.com로의 아웃바운드 HTTPS 연결을 차단하는 경우. 이점은 없으며 모든 시작 시 실패한 네트워크 왕복에 대한 비용이 발생합니다.
배포 메타데이터 캡처
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에서 경고를 한 번 기록합니다. |
저장소 URL은 env var를 통해서만 옵트인할 수 있습니다. 이전 SDK 릴리즈 타임라인에서는 git 메타데이터로부터 저장소를 자동으로 캡처했으나 이는 제거되었습니다. SDK 업그레이드 후 [Agent] Git Repo 값이 비어 있는 경우 AMPLITUDE_GIT_REPO를 직접 설정하십시오.
시맨틱 캐시 적중 추적
자체 의미 또는 응답 캐시에서 전체 응답을 제공하는 경우 AI 메시지 호출에 Node wasCached: true(Node) 또는 Python was_cached=True(Python)을 전달하십시오. 이는 토큰 수준의 프롬프트 캐싱과는 구별되므로 캐시 적중률과 절감하는 비용을 [Agent] Was Cached차트로 표시할 수 있습니다.
메시지 콘텐츠 형성
첫 번째 인수는 trackUserMessageon[Agent] User Message에서 $llm_message.text가 됩니다. 세션 목록, 세그멘테이션 및 보강 기능은 이를 "사용자가 말한 것"으로 간주합니다. 두 가지 실질적인 규칙:
자연어 표현의 짧은 줄을 메시지 본문으로 전달하십시오. ****예를 들어 실제 프롬프트나 헤드리스 작업에 대한 표준 요약은 다음과 같습니다.
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옵션에 구조화된 세그멘테이션 차원을 입력하십시오(JSON이 되며 차트에서 쿼리 가능)[Agent] Context. 서버 측 보강 작업에서 구조화된 데이터를 바탕으로 추론할 수 있도록, 중요한 정보는 콘텐츠에도 포함하세요. 보강은 [Agent] Context가 아니라 주로 턴 텍스트에서 eval 입력을 파생합니다.
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 예제는 에이전트 분석 택소노미를 참조하십시오.
이벤트를 직접 전송할 때는 SDK가 달리 처리하는 작업에 대한 책임은 사용자에게 있습니다.
| 우려 | 여러분이 해야 할 일 |
|---|---|
| 세션 ID | 대화당 하나의 ID를 생성하고 모든 이벤트에 해당 ID를 설정하십시오[Agent] Session ID. |
| 에이전트 ID | 모든 이벤트에 [Agent] Agent ID를 설정합니다. 이 정보가 없으면 Agent Analytics에서 이벤트를 세션에 연결할 수 없어, HTTP API에서 200을 반환하더라도 이벤트가 잘못된 그룹으로 분류됩니다. |
| 데이터 중복 제거 | 재시도해도 중복이 발생하지 않도록 이벤트당 고유 insert_id값을 설정하십시오. |
| 속성 접두어 붙이기 | 모든 속성 이름 앞에 [Agent] (또는 세션 리플레이 ID의 경우 [Amplitude] )를 접두사로 붙이십시오. |
| 비용 및 토큰 | [Agent] Cost USD 직접 계산하십시오. SDK의 자동 가격은 사용할 수 없습니다. |
| 서버 기능 향상 | 콘텐츠가 있을 때 [Agent] Session End로드된 후에도 여전히 자동으로 실행됩니다. |
데이터 검증
Doctor를 실행하여 환경 변수, 종속성 및 이벤트-파이프라인 연결을 확인하십시오.
npx amplitude-ai doctor
기본적으로 AMPLITUDE_AI_API_KEY의 값을 읽어옵니다. 앱에서 키 이름을 다르게 지정할 경우 -key-env를 사용해 올바른 변수를 지정하십시오.
npx amplitude-ai doctor -key-env MY_KEY_NAME
-key-env를 값 없이 전달하면 기본값으로 조용히 대체하는 대신 사용법을 안내하는 오류 메시지를 표시합니다. 이 규칙은 플래그가 있는 경우에만 적용됩니다.
그런 다음 이벤트가 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이 없습니다 | 브라우저 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를 발생시키므로(throw), 운영 환경에 도달하기 전에 테스트에서 이를 포착할 수 있습니다. 가장 엄격한 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 업그레이드로 인해 새 필드가 추가된 후 안전하게 다시 실행할 수 있습니다.
디버그 및 드라이런
두 개의 AIConfig 플래그는 이벤트를 로컬로 검사하는 데 도움을 주며, 각 플래그는 자동 계측과 함께 사용할 수 있도록 해주는 환경 변수와 동등한 값을 갖습니다.
debug: true (노드) / debug=True(파이썬)은 모든 이벤트에 대한 한 줄의 요약을 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 (노드) / dry_run=True(파이썬)은 전체 이벤트 JSON을 stderr에 기록하고 아무것도 전송하지 않습니다. 이 기능을 사용하여 라이브 API 키 없이 로컬 개발 및 CI에서 이벤트 쉐이프를 검증할 수 있습니다. 자동 계측의 경우 대신 명령에서 AMPLITUDE_AI_DEBUG=true설정합니다.
문제 해결
| 문제 | 솔루션: |
|---|---|
| 이벤트 없음 | 가장 일반적인 원인은 LLM 호출이 활성 세션 컨텍스트 외부에서 발생하기 때문이며, 여기서 패치된 호출은 조용히 삭제됩니다. 호출을 session.run()로 감싸거나, 미들웨어를 사용하거나, patch()를 참조하십시오. |
| 이벤트는 200 응답을 반환하지만 세션으로 생성되지 않습니다. | 이벤트에 [Agent] Agent ID가 누락되어 있어 HTTP API에서 수신을 확인한 후 Agent Analytics에서 이벤트를 삭제합니다. 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({...}) | 세션 수준 향상(customer_enriched 모드) |
고차함수
| HOF | 이벤트 | 사용 |
|---|---|---|
tool(fn, { name }) | [Agent] Tool Call | 랩핑 도구 기능 |
observe(fn, { name, type? }) | [Agent] Span | 옵저버빌리티를 위해 전체 함수를 래핑(OTEL을 사용하면 실제 스팬을 생성하고 type이벤트 라우팅을 제어함) |
기타 API
| API | 사용 |
|---|---|
patch({ amplitudeAI: ai }) / unpatch() | 제로코드 계측, 메시지 배열에서 도구 호출을 자동으로 추출 |
wrap(client, ai) | 생성 로직을 수정하지 않고 기존 공급자 클라이언트를 래핑 |
injectContext() / extractContext(headers) | 교차 서비스 전파 |
usingAttributes(attrs, fn) / using_attributes(**attrs) | ID 및 세션 컨텍스트를 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 연동 |
이 내용이 도움이 되었나요?