主题
从追踪中脱敏密钥
使用 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 和内容哈希不变。
| 类别 | 检测到的格式 |
|---|---|
| Anthropic | sk-ant- |
| OpenAI | sk-proj-、sk-svcacct-、sk-admin- 和旧版 sk- 密钥 |
| LangSmith | lsv2_pt_、lsv2_sk_、ls__ |
| GitHub | ghp_、gho_、ghu_、ghs_、ghr_ 和 github_pat_ |
| GitLab | glpat- |
| AWS | 以 AKIA、ASIA、ABIA、ACCA 或 A3T 为前缀的访问密钥 ID |
AIza API 密钥和 ya29. OAuth 访问 token | |
| Slack | xoxb-、xoxa-、xoxp-、xoxr-、xoxs-、xapp- 和 hooks.slack.com webhook URL |
| Stripe | sk_live_、sk_test_、rk_live_、rk_test_ |
| npm | npm_ |
| PyPI | pypi-AgEIcHlwaS 上传 token |
| SendGrid | SG. |
| 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 库,请参阅防止在追踪中记录敏感数据。