데이터 삭제(Data Deletion)

데이터 삭제(Data Deletion)

개발 흐름에서 실수로 만든 trace, PII용 사용자 데이터, 또는 프로젝트 전체 등 Langfuse에서 선택한 데이터를 제거하고 싶은 경우가 있습니다. 이 문서는 단일 trace, trace 배치, 세션, 쿼리 필터, 프로젝트, 조직, 사용자 계정을 삭제하는 방법을 설명해요. 최근 데이터만 유지하고 싶다면 데이터 보존 기능을 사용할 수 있습니다.

출처: 문서

본문

선택한 데이터를 Langfuse에서 제거하고 싶은 경우가 있습니다. 예를 들어 개발 흐름에서 잘못 생성된 trace, PII에 해당하는 사용자 데이터, 또는 전체 프로젝트 등이죠. 최근 데이터만 유지하고 싶다면 Data Retention 기능을 사용할 수 있습니다.

원치 않는 데이터를 Langfuse에서 삭제하는 방법:

  • 단일 trace 삭제
  • trace 배치 삭제
  • API를 통한 세션의 모든 trace 삭제
  • 쿼리 필터와 일치하는 모든 trace 삭제
  • 프로젝트 삭제
  • 조직 삭제
  • 사용자 계정 삭제

아래에서 각 옵션과 보장 사항을 살펴보겠습니다.

Trace 삭제

모든 trace 삭제는 scores, observations 같은 관련 엔터티를 모든 데이터 스토리지에서 함께 삭제합니다.

단일 Trace

단일 trace를 삭제하려면 상세 뷰를 열고 Delete 버튼을 누르세요. 해당 trace를 삭제할지 확인합니다.

DELETE /api/public/traces/{traceId}

참고 문서

Trace 배치

trace 배치를 삭제하려면 trace 목록에서 선택하고 Actions 드롭다운에서 Delete를 선택하세요.

DELETE /api/public/traces

참고 문서

API로 세션의 trace 삭제

이 기능은 어디에서 쓸 수 있나요?

플랜 사용 가능 여부
Hobby 사용 가능
Core 사용 가능
Pro 사용 가능
Enterprise 사용 가능
Self Hosted Langfuse v4+

세션의 trace를 삭제하려면 그 ID를 조회해 기존 배치 삭제 엔드포인트에 전달하세요:

  • 현재 달부터 시작해 GET /api/public/v2/observationssessionId와 한 달 단위 시간 창으로 조회하여, 전체 역월에 일치하는 observation이 없을 때까지 반복합니다. 이 읽기 전용 과정은 어떤 trace도 삭제하기 전에 중지 경계를 고정합니다.
  • 결과로 나온 전체 시간 범위를 limit=100으로 조회하고, cursor가 반환되지 않을 때까지 meta.cursor를 따라갑니다.
  • 페이지에 걸쳐 고유한 traceId 값을 수집합니다. observation ID가 아니라 trace ID를 사용하세요.
  • 고유 trace ID가 1,000개 모이면 DELETE /api/public/traces 에 JSON 바디 {"traceIds": [...]}로 보낸 뒤 계속 읽습니다. 마지막 페이지 이후 남은 ID도 삭제하세요.

아래 Python 예시는 requests를 사용합니다 (pip install requests). LANGFUSE_BASE_URL을 내 Langfuse 리전이나 셀프 호스팅 URL로, LANGFUSE_PUBLIC_KEYLANGFUSE_SECRET_KEY를 프로젝트 API 키로 설정하세요. 세션 ID는 내 값으로 바꾸세요. 예시는 now부터 역방향으로, 현재의 부분 달부터 시작해 전체 역월을 확인하며 스캔합니다. 각 창은 시작은 포함하고 끝은 제외하므로 경계가 겹치지 않습니다. 가정: API에서 접근 가능한 기록 내에서 일치하는 observation이 없는 전체 역월이 있다면 그보다 오래된 세션 데이터는 더 없다는 뜻입니다. 예시는 거기서 멈추고 더 이른 달은 확인하지 않습니다. API 읽기 창 안에 들어오는 최근 세션에 이 워크플로우를 사용하고, 한 달 이상의 공백 이후에는 재개하지 마세요. 현재의 빈 부분 달은 중지 조건을 트리거하지 않습니다.

Langfuse Cloud에서 Observations API v2 읽기 창은 Hobby에서 30일, Core에서 90일입니다. 이 워크플로우는 그 창 안에서 반환되는 trace만 발견할 수 있습니다. Pro, Team, Enterprise 및 셀프 호스팅 배포에는 이 API 읽기 창 제한이 적용되지 않습니다. 구성된 프로젝트 수준 보존 정책은 사용 가능한 기록을 더 짧게 만들 수 있습니다.

cursor는 시간 경계와 ID를 모두 추적하므로 삭제가 비동기로 실행되는 동안에도 페이지네이션이 계속될 수 있습니다. 페이지네이션 중에는 원래 fromStartTimetoStartTime을 고정해 두세요. 배타적 toStartTime을 마지막 observation의 타임스탬프로 수동 설정하면 같은 타임스탬프를 가진 다른 observation을 건너뛸 수 있습니다.

import os
from datetime import datetime, timedelta, timezone

import requests

session_id = "your-session-id"
scan_end = datetime.now(timezone.utc)
base_url = os.environ["LANGFUSE_BASE_URL"].rstrip("/")

def previous_window(window_end):
    month_start = window_end.replace(
        day=1, hour=0, minute=0, second=0, microsecond=0,
    )
    if month_start == window_end:
        month_start = (month_start - timedelta(days=1)).replace(day=1)
        return month_start, window_end, True
    return month_start, window_end, False

def delete_batch(client, trace_ids):
    response = client.delete(
        f"{base_url}/api/public/traces",
        json={"traceIds": trace_ids},
        timeout=60,
    )
    response.raise_for_status()

with requests.Session() as client:
    client.auth = (
        os.environ["LANGFUSE_PUBLIC_KEY"],
        os.environ["LANGFUSE_SECRET_KEY"],
    )

    # Find the first originally empty full month before deleting anything.
    window_end = scan_end
    while True:
        window_start, current_window_end, full_month = previous_window(window_end)
        response = client.get(
            f"{base_url}/api/public/v2/observations",
            params={
                "sessionId": session_id,
                "fromStartTime": window_start.isoformat(),
                "toStartTime": current_window_end.isoformat(),
                "fields": "core",
                "limit": 1,
            },
            timeout=60,
        )
        response.raise_for_status()
        if not response.json()["data"] and full_month:
            scan_start = current_window_end
            break
        window_end = window_start

    seen_trace_ids = set()
    pending_trace_ids = []
    submitted = 0
    params = {
        "sessionId": session_id,
        "fromStartTime": scan_start.isoformat(),
        "toStartTime": scan_end.isoformat(),
        "fields": "core",
        "limit": 100,
    }

    while True:
        response = client.get(
            f"{base_url}/api/public/v2/observations",
            params=params,
            timeout=60,
        )
        response.raise_for_status()
        page = response.json()
        for observation in page["data"]:
            trace_id = observation["traceId"]
            if not trace_id or trace_id in seen_trace_ids:
                continue
            seen_trace_ids.add(trace_id)
            pending_trace_ids.append(trace_id)
            if len(pending_trace_ids) == 1000:
                delete_batch(client, pending_trace_ids)
                submitted += len(pending_trace_ids)
                pending_trace_ids = []

        cursor = page["meta"].get("cursor")
        if not cursor:
            break
        params["cursor"] = cursor

    if pending_trace_ids:
        delete_batch(client, pending_trace_ids)
        submitted += len(pending_trace_ids)

print(f"Submitted {submitted} traces for deletion.")

삭제는 각 선택된 trace와 관련 observations, scores를 제거합니다. 조회한 시간 범위 밖의 observation도 포함됩니다. 비동기이며, 타이밍과 검증에 대해서는 삭제 제한 사항을 참고하세요.

이 워크플로우는 같은 sessionId에 대한 향후 데이터를 차단하지 않습니다. 세션의 현재 trace를 모두 제거해야 한다면 세션의 수집을 일시 중지하고, in-flight 데이터가 쿼리 가능해질 때까지 기다린 뒤 ID를 수집하세요. 읽기 이후에 도착한 데이터는 다시 통과해야 할 수 있습니다.

쿼리로 삭제

쿼리 필터와 일치하는 모든 trace를 삭제하려면 trace 목록에서 원하는 필터를 구성하세요. 현재 페이지의 모든 항목을 선택하고 상단 바에서 모든 항목으로 변경합니다. 그런 다음 Actions 드롭다운에서 Delete를 선택합니다.

제한 사항

Trace 삭제(예: 데이터 삭제 요청으로 userId로 사용자의 trace 삭제)는 trace와 관련 observations, scores를 제거합니다. 하지만 데이터셋 같은 다른 객체에 저장될 수 있는 개인 데이터는 제거하지 않습니다. 사용자 데이터를 완전히 지우려면 그 객체들도 삭제하거나 전체 프로젝트를 삭제 하세요.

Langfuse의 대부분의 삭제는 즉시 발생하지만 tracing 데이터의 삭제는 그렇지 않습니다. 데이터 웨어하우스에서 그 레코드를 제거하는 것은 리소스 집약적인 작업이므로, 한 번에 처리하는 삭제 수를 rate limit합니다. 보통 delete 호출 후 15분 내에 trace 데이터가 시스템에서 삭제됩니다. 삭제 확인이나 알림은 없으며, 데이터가 삭제됐는지 확인하려면 다시 조회해 보세요.

정기적으로 오래된 데이터를 정리해야 한다면 Data Retention 을 사용해 구성된 일수보다 오래된 traces, observations, scores, media assets를 자동 삭제하는 것이 좋습니다.

프로젝트 삭제

프로젝트를 삭제하려면 프로젝트 설정으로 이동해 General 섹션의 Danger Zone까지 스크롤하세요. 프로젝트를 삭제할지 확인합니다. 이 작업은 모든 API 키를 즉시 폐기하고 프로젝트를 삭제 스케줄에 올립니다. 몇 분 안에 모든 관련 데이터가 시스템에서 되돌릴 수 없게 제거됩니다. 프로젝트 삭제는 되돌릴 수 없으며 모든 데이터가 제거됩니다. 이 작업을 실행할 때는 주의하세요. 삭제를 확인하면 프로젝트 삭제에 최대 5분이 걸릴 수 있습니다.

조직 삭제

조직에 프로젝트가 하나도 없다면 조직 설정에서 조직을 삭제할 수 있습니다. 조직 설정으로 이동해 General 섹션의 Danger Zone까지 스크롤하세요. 조직을 삭제할지 확인합니다. 조직과 모든 관련 사용자 정보가 시스템에서 제거됩니다.

사용자 계정 삭제(Cloud)

사용자는 Account Settings 페이지에서 자신의 계정을 삭제할 수 있습니다. 오른쪽 아래의 사용자 메뉴에서 Account Settings로 이동하세요. 조직의 유일한 소유자라면 계정을 삭제하기 전에 먼저 다른 사용자에게 소유권을 이전하거나 조직을 삭제해야 합니다.

사용자 계정 삭제(Self-Host)

users 테이블에서 해당 사용자 레코드를 제거하고 cascade로 그에 대한 모든 외래 키를 삭제하세요.

더 알아보기 (Learn more)