ADL API 概述
Agentforce Data Libraries (ADL) 通过连接可信数据源增强 AI Agent 的准确性。Data Library 利用非结构化或半结构化数据,将 Web、文档或字段中的大量文本信息转化为更有用、可搜索的知识。使用 ADL Connect API 资源编程式创建和管理 Data Library,确保 Agent 始终能访问最新、最相关的信息。
两种源类型:SFDRIVE(文件库——上传 PDF/DOCX 等文件到 Salesforce 管理的 S3 桶)和 KNOWLEDGE(知识库——索引 Knowledge Article 的指定字段)。
参见 Connect REST API: ADL Resource Reference 和 Help: Agentforce Data Library
前提条件与完整设置
必备工具
- Salesforce CLI:安装 CLI,用于认证和获取 Org 信息
- jq:JSON 解析工具。检查
jq --version,未安装则brew install jq - PDF 测试文件:任意 PDF 文件即可(内容不重要)
- Test Org:已配置 Data Library 的 Org。参见 Setting Up Data Libraries
创建 External Client App(ECA)
- Setup → External Client Apps Manager → New
- 命名 + 邮箱
- OAuth Scopes:chatter_api / api / web / refresh_token, offline_access
- OAuth 设置:Enable Client Credentials Flow + Issue JWT Web Token
- Policies → Enable Client Credentials Flow → Run As 设为 API Only 用户 → Issue JWT tokens(默认 30min 过期)
获取 Access Token(完整脚本)
# 设置环境变量
export SF_CLIENT_ID={CONSUMER_KEY}
export SF_CLIENT_SECRET={CONSUMER_SECRET}
export SF_INSTANCE_URL=https://{MY_DOMAIN}.my.salesforce.com
# 获取 Token
RESPONSE=$(curl -s -X POST \
"$SF_INSTANCE_URL/services/oauth2/token" \
-d "grant_type=client_credentials" \
-d "client_id=$SF_CLIENT_ID" \
-d "client_secret=$SF_CLIENT_SECRET")
ACCESS_TOKEN=$(echo $RESPONSE | jq -r '.access_token')
ORG_URL=$(echo $RESPONSE | jq -r '.instance_url')
# 验证 ADL API 可用
curl -s "$ORG_URL/services/data/v66.0/einstein/data-libraries" \
-H "Authorization: Bearer $ACCESS_TOKEN" | jq '.'
# 收到 JSON 响应(即使是空数组)表示 OAuth 工作正常
设置 Shell 变量
# 生成唯一 Library 名称
ADL_PREFIX="your_prefix"
ADL_SUFFIX=$(date +%m%d)_$(LC_ALL=C tr -dc 'a-z0-9' < /dev/urandom | head -c3)
ADL_DevName="${ADL_PREFIX}_${ADL_SUFFIX}"
ADL_Name="${ADL_PREFIX} ${ADL_SUFFIX}"
# 指向测试文件(修改此路径)
FILE_NAME="/Users/yourname/Downloads/file1.pdf"
以上命名约定仅用于快速入门,生产环境可使用任何命名方案。
文件库(SFDRIVE)完整示例
本示例演示完整的文件库生命周期:创建→上传就绪→获取 Presigned URL→S3 上传→索引→轮询→追加文件。
Step 1-3:创建、等待就绪与获取上传 URL
# Step 1: 创建 SFDRIVE Library
RESPONSE_ADL=$(curl -s -X POST "$ORG_URL/services/data/v66.0/einstein/data-libraries" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"masterLabel":"'"${ADL_Name}"'","developerName":"'"${ADL_DevName}"'","groundingSource":{"sourceType":"SFDRIVE"}}')
echo "$RESPONSE_ADL" | jq '.'
LIBRARY_ID=$(echo "$RESPONSE_ADL" | jq -r '.libraryId')
# 预期响应: JSON 含 libraryId 和 groundingSource 字段
# Step 2: 等待上传就绪(Data 360 资源预配,最多 2 分钟)
echo "Checking upload readiness..."
READINESS=$(curl -s --max-time 130 \
"$ORG_URL/services/data/v66.0/einstein/data-libraries/$LIBRARY_ID/upload-readiness?waitMaxTime=120000" \
-H "Authorization: Bearer $ACCESS_TOKEN")
echo "$READINESS" | jq '.'
# 预期: {"ready": true}
# Step 3: 获取 Presigned Upload URLs(最多 1000 个文件)
FILE_BASENAME=$(basename "$FILE_NAME")
RESPONSE=$(curl -s -X POST \
"$ORG_URL/services/data/v66.0/einstein/data-libraries/$LIBRARY_ID/file-upload-urls" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"files":[{"fileName":"'"${FILE_BASENAME}"'"}]}')
echo "$RESPONSE" | jq '.'
PRESIGNED_URL=$(echo "$RESPONSE" | jq -r '.uploadUrls[0].uploadUrl')
FILE_PATH_S3=$(echo "$RESPONSE" | jq -r '.uploadUrls[0].filePath')
注意:Step 2 需要等待 Data 360 资源预配。如果在预配完成前调用 file-upload-urls(Step 3),会返回 HTTP 400。最多上传 1000 个文件。
Step 4-6:上传、索引与轮询
# Step 4: 上传文件到 S3(解析 Presigned URL 的 Headers)
UPLOAD_HEADERS=()
while IFS='=' read -r key value; do
UPLOAD_HEADERS+=(-H "$key: $value")
done < <(echo "$RESPONSE" | jq -r '.uploadUrls[0].headers | to_entries[] | "\(.key)=\(.value)"')
curl -X PUT "${PRESIGNED_URL}" \
"${UPLOAD_HEADERS[@]}" \
--data-binary @"${FILE_NAME}" \
-w "\nHTTP Status: %{http_code}\n"
# 预期: HTTP Status: 200
# Step 5: 触发索引
FILE_SIZE=$(stat -f%z "$FILE_NAME" 2>/dev/null || stat -c%s "$FILE_NAME")
curl -s -X POST "$ORG_URL/services/data/v66.0/einstein/data-libraries/$LIBRARY_ID/indexing" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"uploadedFiles":[{"filePath":"'"${FILE_PATH_S3}"'","fileSize":'"$FILE_SIZE"'}]}' | jq '.'
# 预期: {"status": "IN_PROGRESS"}
# Step 6: 轮询直到 READY(每 10 秒一次)
while true; do
STATUS_RESPONSE=$(curl -s \
"$ORG_URL/services/data/v66.0/einstein/data-libraries/$LIBRARY_ID/status" \
-H "Authorization: Bearer $ACCESS_TOKEN")
STATUS=$(echo "$STATUS_RESPONSE" | jq -r '.indexingStatus.status')
ALL_STAGES_SUCCESS=$(echo "$STATUS_RESPONSE" | jq -r '[.indexingStatus.stages[]?.status] | all(. == "SUCCESS") and length > 0')
echo "Status: $STATUS | All stages SUCCESS: $ALL_STAGES_SUCCESS"
if [ "$STATUS" = "READY" ] || [ "$STATUS" = "FAILED" ] || [ "$ALL_STAGES_SUCCESS" = "true" ]; then
echo "$STATUS_RESPONSE" | jq '.'
break
fi
sleep 10
done
状态说明:READY 需要 SearchIndex 的分块/嵌入处理完成。ALL_STAGES_SUCCESS=true 表示 ADL 管道完成(即使 SearchIndex 处理仍在进行中)。通常几分钟后转换到 READY。
Step 7-9:追加更多文件
# Step 7: 获取新文件上传 URL
NEW_FILE="/Users/yourname/Downloads/file2.pdf"
NEW_FILE_BASENAME=$(basename "$NEW_FILE")
RESPONSE=$(curl -s -X POST \
"$ORG_URL/services/data/v66.0/einstein/data-libraries/$LIBRARY_ID/file-upload-urls" \
-H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"files":[{"fileName":"'"${NEW_FILE_BASENAME}"'"}]}')
PRESIGNED_URL=$(echo "$RESPONSE" | jq -r '.uploadUrls[0].uploadUrl')
FILE_PATH_S3=$(echo "$RESPONSE" | jq -r '.uploadUrls[0].filePath')
# Step 8: 上传新文件(同上 Headers 解析+curl PUT)
# Step 9: 注册新文件(自动触发搜索索引重建,无需单独 indexing 调用)
NEW_FILE_SIZE=$(stat -f%z "$NEW_FILE" 2>/dev/null || stat -c%s "$NEW_FILE")
curl -s -X POST "$ORG_URL/services/data/v66.0/einstein/data-libraries/$LIBRARY_ID/files" \
-H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"uploadedFiles":[{"filePath":"'"${FILE_PATH_S3}"'","fileSize":'"$NEW_FILE_SIZE"'}]}' | jq '.'
# 预期: {"filesAccepted": 1}
关键区别:第一个文件需要单独的 indexing 调用(Step 5)。追加文件使用 files 端点(Step 9),自动触发索引重建。
Knowledge 库完整示例
创建 KNOWLEDGE Library 时需要指定索引字段。关键要求:
primaryIndexField1和primaryIndexField2必填且创建后不可修改- 所有指定字段必须存在于 Knowledge 对象(KnowledgeKAV)且为 STRING 或 TEXTAREA 类型
contentFields可后续通过 PATCH 更新- 可选:通过 Data Categories 限制知识库搜索范围(
isDataCategoryRuleEnabled)
创建含 Data Categories 的 Knowledge Library
# 创建 Knowledge Library
RESPONSE_ADL=$(curl -s -X POST "$ORG_URL/services/data/v66.0/einstein/data-libraries" \
-H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"masterLabel":"'"${ADL_Name}"'","developerName":"'"${ADL_DevName}"'",
"groundingSource":{"sourceType":"KNOWLEDGE",
"knowledgeConfig":{
"primaryIndexField1":"Title",
"primaryIndexField2":"Summary",
"contentFields":["UrlName"]}}}')
LIBRARY_ID=$(echo "$RESPONSE_ADL" | jq -r '.libraryId')
# 创建含 Data Categories 的 Knowledge Library(按分类名称)
# 格式: groupDeveloperName.categoryDeveloperName
curl -s -X POST "$ORG_URL/..." \
-d '{"groundingSource":{"sourceType":"KNOWLEDGE",
"knowledgeConfig":{
"primaryIndexField1":"Title","primaryIndexField2":"Summary",
"contentFields":["UrlName"],
"isDataCategoryRuleEnabled":true,
"dataCategorySelectionNames":["Products.Hardware","Products.Software"]}}}'
# 也可按分类 ID(不能同时提供 names 和 IDs)
# "dataCategorySelectionIds":["ka01234567890ABCAA","ka01234567890ABCBB"]
Category 处理:分类名称在创建时转换为 ID,重复条目自动去重。
Knowledge Library 管理操作
| 操作 | 方法 | 端点 | 说明 |
|---|---|---|---|
| 索引 | POST | /indexing | 触发 Knowledge Article 索引 |
| 状态 | GET | /status | 含 indexingStatus.stages 详情(DATA_LAKE_OBJECT/DATA_MODEL_OBJECT/SEARCH_INDEX/DATA_STREAM/RETRIEVER) |
| 更新 | PATCH | /{id} | 可更新 contentFields 和 Data Category 配置;索引进行中时不能更新 |
| 详情 | GET | /{id} | 返回完整配置(含 retrieverId/retrieverLabel/status) |
| 删除 | DELETE | /{id} | 返回 HTTP 204。被 Agent 引用的库不能删除 |
索引状态阶段详解
GET /status 返回的 indexingStatus.stages 包含五个处理阶段:
- DATA_LAKE_OBJECT — Data 360 数据湖对象创建
- DATA_MODEL_OBJECT — 数据模型对象
- SEARCH_INDEX — 搜索索引构建(最耗时,需分块+嵌入)
- DATA_STREAM — 数据流处理
- RETRIEVER — 检索器配置
每个阶段有独立的 completedAt 时间戳和 status(SUCCESS/IN_PROGRESS/FAILED)。
OTel API:导出会话追踪数据(Beta)
Agentforce Session Trace OTel API 提取单个 Agent 会话的完整追踪 JSON,格式符合 OpenTelemetry (OTLP) v1.0 规范。从两个数据源合并数据:Agentforce Session Tracing(轮次、消息、LLM 调用、动作)和 Generative AI Audit & Feedback(Metric 分数、反馈信号)。所有数据是预关联的——无需 Data Cloud SQL。
Beta 限制
- 仅支持单会话查询(每次一个 session ID),不支持批量
- Connect API 标准速率限制适用
- 返回 StartTimestamp 在 72 小时内的会话数据
OTel API 设置、查询与导出
Step 1-2:启用数据收集 + 认证
- Setup → Einstein Generative AI → Einstein Audit, Analytics, and Monitoring Setup
- 开启:Agentforce Session Tracing 和 Audit and Feedback
- 创建 ECA 获取 OAuth 2.0 Token
Step 3:提交 REST API 查询
GET /services/data/v66.0/einstein/audit/otel/{session-id}
# 返回 OTel ResourceSpans 格式的完整会话追踪
# 含: 所有 turns, messages, LLM calls, action executions, metric scores, feedback
Step 4:导出到 Observability 平台
数据已是 OTel 格式,可直接导入 Splunk/Datadog/New Relic 等支持 OTLP 的平台,无需转换。也可通过 OTLP Collector 轮询 API 并转发:
# otel-collector-config.yaml 示例
receivers:
salesforce_otel:
endpoint: https://your-instance.salesforce.com
auth:
authenticator: oauth2client
processors:
batch:
timeout: 10s
exporters:
otlp/datadog:
endpoint: https://api.datadoghq.com
headers:
DD-API-KEY: ${DD_API_KEY}
service:
pipelines:
traces:
receivers: [salesforce_otel]
processors: [batch, attributes]
exporters: [otlp/datadog]
FAQ 要点
- 需要 Data Cloud?Beta 中需要(数据存储在 Data 360 中,API 通过 Data 360 SQL 查询)。长期计划 Unified Planner native OTel 路径减少此依赖
- 数据治理?底层 Data 360 查询服务基于 API 调用用户上下文执行 Dataspace 和治理规则
- 替换 STDM DMOs?不替换。OTel JSON API 是增量式的,面向 pro-code 和 API 场景(调试/自定义评估/第三方导出)。STDM DMOs 仍是 Agentforce Studio 分析和可观测性的基础
- 批量场景?调用查询服务获取会话列表 → 逐个调用 API。原生 push/connector 支持计划中









