Skip to content
LangSmith 文档版本 跟随上游 源快照 8d7e358b 更新于 2026-10-18

从追踪中脱敏密钥

使用 SDK 匿名化器防止 API 密钥、token 和其他密钥出现在 LangSmith 追踪中。

当你的应用处理 API 密钥、token 或其他凭据时,如果这些值作为输入或输出的一部分传递,它们可能会出现在 LangSmith 追踪中。使用 LangSmith SDK 内置的匿名化器在密钥发送到后端之前对其进行脱敏。

NOTE

本页介绍如何通过 SDK 从追踪数据中脱敏密钥(API 密钥、token、凭据)。有关脱敏个人身份信息(PII)(如电子邮件、姓名或 SSN),请参阅防止在追踪中记录敏感数据。要在 LLM Gateway 层脱敏密钥,请参阅数据策略。

Claude Code、OpenAI Codex 和 Cursor 的编码 agent 追踪插件默认应用以下预设。这些插件无需 SDK 代码。

使用内置密钥预设

SDK 附带一套针对常见凭据格式的精选规则集。将 create_secret_anonymizer 传递给 Client 构造函数,它会在运行输入、输出、错误和元数据上传之前从中脱敏检测到的密钥。当你希望覆盖众所周知的密钥格式而无需自己编写模式时,请使用它。

INFO

create_secret_anonymizer / createSecretAnonymizer 函数需要:

  • Python SDK:0.9.0 或更高版本
  • TypeScript SDK:0.7.11 或更高版本
python
from langsmith import Client
from langsmith.anonymizer import create_secret_anonymizer

client = Client(anonymizer=create_secret_anonymizer())
typescript
import { Client } from "langsmith";
import { createSecretAnonymizer } from "langsmith/anonymizer";

const client = new Client({ anonymizer: createSecretAnonymizer() });

每个匹配项都会替换为 [SECRET_DETECTED]。该预设最多遍历 24 层嵌套,而 create_anonymizer 使用 10 层,因为追踪负载嵌套很深。使用 max_depth 覆盖它。

Python 和 TypeScript 预设持有相同的规则,因此由其中一个脱敏的追踪与另一个脱敏的追踪相匹配。

预设中的规则

提供程序规则锚定到已知的密钥前缀。上下文规则仅在敏感名称与赋值配对时触发,从而保留普通代码、UUID 和内容哈希不变。

类别检测到的格式
Anthropicsk-ant-
OpenAIsk-proj-、sk-svcacct-、sk-admin- 和旧版 sk- 密钥
LangSmithlsv2_pt_、lsv2_sk_、ls__
GitHubghp_、gho_、ghu_、ghs_、ghr_ 和 github_pat_
GitLabglpat-
AWS以 AKIA、ASIA、ABIA、ACCA 或 A3T 为前缀的访问密钥 ID
GoogleAIza API 密钥和 ya29. OAuth 访问 token
Slackxoxb-、xoxa-、xoxp-、xoxr-、xoxs-、xapp- 和 hooks.slack.com webhook URL
Stripesk_live_、sk_test_、rk_live_、rk_test_
npmnpm_
PyPIpypi-AgEIcHlwaS 上传 token
SendGridSG.
JSON Web Token以 eyJ 开头的 header.payload.signature 三元组
私钥RSA、EC、OpenSSH、DSA 和 PGP 密钥的 PEM 块
命名赋值API_KEY、SECRET、TOKEN、PASSWORD、PASSWD、PRIVATE_KEY、ACCESS_KEY、AUTH_TOKEN 或 CLIENT_SECRET 后跟 = 或 : 以及六个字符或更长的值
凭据标头Authorization、X-Api-Key 和 X-Auth-Token,以及裸 Bearer <token>
URL 凭据scheme://user:password@host 中的密码

名称规则要求组件边界,因此 TOKEN 匹配 api_token 和 mytoken,但不匹配 tokenizer 或 tokens。标头和 Bearer 规则保留标头名称和方案词,仅脱敏其后的凭据。

向预设添加你自己的规则

传递 extra_rules 以追加预设不知道的凭据模式,例如内部密钥格式。额外规则在内置规则之后运行。

python
import re

from langsmith import Client
from langsmith.anonymizer import create_secret_anonymizer

anonymizer = create_secret_anonymizer(
    extra_rules=[
        {"pattern": re.compile(r"ACME-[A-Z0-9]{16}"), "replace": "[REDACTED_ACME_KEY]"},
    ]
)

client = Client(anonymizer=anonymizer)
typescript
import { Client } from "langsmith";
import { createSecretAnonymizer } from "langsmith/anonymizer";

const anonymizer = createSecretAnonymizer({
  extraRules: [
    { pattern: /ACME-[A-Z0-9]{16}/g, replace: "[REDACTED_ACME_KEY]" },
  ],
});

const client = new Client({ anonymizer });

没有 replace 值的规则会回退到 [redacted],而不是 [SECRET_DETECTED]。当你希望在追踪中区分二者时,请设置 replace。

预设的限制

该预设优先考虑精确性而非全面覆盖,这会产生值得提前规划的影响:

  • 无法识别的格式会到达 LangSmith:不匹配任何规则的凭据(包括没有上下文环境的随机高熵字符串)会按原样上传。
  • 仅脱敏四个字段:匿名化器覆盖 inputs、outputs、error 和 extra.metadata。运行名称、标签和附件会未经修改地上传。
  • 脱敏不是访问控制:已脱敏的追踪仍保留构建它时使用的提示、文件内容和工具结果。限制谁可以读取追踪项目。要省略内容而不是清洗内容,请参阅防止在追踪中记录敏感数据。

编写自定义模式

INFO

create_anonymizer / createAnonymizer 函数需要:

  • Python SDK:0.1.81 或更高版本
  • TypeScript SDK:0.1.33 或更高版本

当预设不适合时,create_anonymizer 接受正则表达式模式和替换字符串列表,并仅应用这些内容。将生成的匿名化器传递给 Client 构造函数,它将在所有运行输入和输出到达 LangSmith 之前自动应用。

以下示例脱敏常见密钥格式,包括 OpenAI API 密钥、通用 bearer token 和 sk- 前缀密钥:

python
from langsmith.anonymizer import create_anonymizer
from langsmith import Client, traceable

# Redact common secret patterns
anonymizer = create_anonymizer([
    # OpenAI-style keys: sk-... or sk-proj-...
    {"pattern": r"sk-[A-Za-z0-9\-_]{20,}", "replace": "<REDACTED_API_KEY>"},
    # Generic bearer tokens
    {"pattern": r"Bearer\s+[A-Za-z0-9\-_\.]{20,}", "replace": "Bearer <REDACTED_TOKEN>"},
    # Anthropic keys
    {"pattern": r"sk-ant-[A-Za-z0-9\-_]{20,}", "replace": "<REDACTED_API_KEY>"},
    # Generic high-entropy strings that look like secrets (40+ hex chars)
    {"pattern": r"\b[0-9a-fA-F]{40,}\b", "replace": "<REDACTED_TOKEN>"},
])

client = Client(anonymizer=anonymizer)

@traceable(client=client)
def call_external_api(api_key: str, prompt: str) -> str:
    # The api_key value will be redacted in the trace
    return f"Response to: {prompt}"

call_external_api(
    api_key="sk-proj-AbCdEfGhIjKlMnOpQrStUvWxYz1234567890",
    prompt="What is LangSmith?",
)
typescript
import { createAnonymizer } from "langsmith/anonymizer";
import { traceable } from "langsmith/traceable";
import { Client } from "langsmith";

// Redact common secret patterns
const anonymizer = createAnonymizer([
    // OpenAI-style keys: sk-... or sk-proj-...
    { pattern: /sk-[A-Za-z0-9\-_]{20,}/g, replace: "<REDACTED_API_KEY>" },
    // Generic bearer tokens
    { pattern: /Bearer\s+[A-Za-z0-9\-_.]{20,}/g, replace: "Bearer <REDACTED_TOKEN>" },
    // Anthropic keys
    { pattern: /sk-ant-[A-Za-z0-9\-_]{20,}/g, replace: "<REDACTED_API_KEY>" },
    // Generic high-entropy strings that look like secrets (40+ hex chars)
    { pattern: /\b[0-9a-fA-F]{40,}\b/g, replace: "<REDACTED_TOKEN>" },
]);

const client = new Client({ anonymizer });

const callExternalApi = traceable(
    async (apiKey: string, prompt: string): Promise<string> => {
        // The apiKey value will be redacted in the trace
        return `Response to: ${prompt}`;
    },
    { client }
);

await callExternalApi(
    "sk-proj-AbCdEfGhIjKlMnOpQrStUvWxYz1234567890",
    "What is LangSmith?"
);

匿名化器将输入和输出序列化为 JSON,应用每个正则表达式模式,然后在发送到 LangSmith 之前反序列化结果。默认情况下,它最多遍历 10 层嵌套。要更改此设置,请传递 max_depth 参数:

python
anonymizer = create_anonymizer(
    [{"pattern": r"sk-[A-Za-z0-9\-_]{20,}", "replace": "<REDACTED_API_KEY>"}],
    max_depth=5,
)

使用自定义函数

如果你的脱敏逻辑更复杂,请传递函数而不是模式列表。该函数接收字符串并返回脱敏后的字符串:

python
import re
from langsmith.anonymizer import create_anonymizer
from langsmith import Client

# Example: redact any value that follows a known key name in JSON-like payloads
SECRET_KEYS = {"api_key", "apiKey", "token", "secret", "password", "credential"}

def redact_secret_values(text: str) -> str:
    for key in SECRET_KEYS:
        # Match patterns like: "api_key": "some-value"
        pattern = rf'("{key}"\s*:\s*)"[^"]*"'
        text = re.sub(pattern, r'\1"<REDACTED>"', text)
    return text

anonymizer = create_anonymizer(redact_secret_values)
client = Client(anonymizer=anonymizer)
typescript
import { createAnonymizer } from "langsmith/anonymizer";
import { Client } from "langsmith";

const SECRET_KEYS = new Set(["api_key", "apiKey", "token", "secret", "password", "credential"]);

function redactSecretValues(text: string): string {
    for (const key of SECRET_KEYS) {
        // Match patterns like: "api_key": "some-value"
        const pattern = new RegExp(`("${key}"\\s*:\\s*)"[^"]*"`, "g");
        text = text.replace(pattern, '$1"<REDACTED>"');
    }
    return text;
}

const anonymizer = createAnonymizer(redactSecretValues);
const client = new Client({ anonymizer });

与 LANGSMITH_HIDE_INPUTS 结合使用

如果你的用例需要完全抑制所有输入(例如,为了零保留合规性),请改用 LANGSMITH_HIDE_INPUTS=true。当 LANGSMITH_HIDE_INPUTS 或 LANGSMITH_HIDE_OUTPUTS 设置为 true 时,会跳过匿名化器。

有关更多选项,包括隐藏所有输入和输出、隐藏元数据、函数级处理器和第三方 PII 库,请参阅防止在追踪中记录敏感数据。


BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。