Salesforce 开发人员指南:利用 Apex 调用 Einstein Language API 实现智能自然语言处理

背景与应用场景

作为一名 Salesforce 开发人员,我们经常面临着处理和理解大量非结构化文本数据的挑战。无论是来自客户邮件、社交媒体帖子、在线聊天记录,还是个案备注,这些文本中都蕴含着宝贵的商业洞察。传统的手动分析方法不仅效率低下,而且容易出错。Einstein Language,作为 Salesforce Einstein Platform Services 的核心组件之一,为我们提供了一套强大的自然语言处理 (Natural Language Processing, NLP) REST API,使我们能够以编程方式将人工智能驱动的文本理解能力无缝集成到我们的 Salesforce 应用中。

Einstein Language API 主要涵盖三个核心功能,每个功能都对应着广泛的应用场景:

1. Einstein Sentiment (情感分析)

此 API 能够分析文本中表达的情感,并将其分类为积极 (Positive)、消极 (Negative) 或中性 (Neutral)。这对于自动化客户服务流程至关重要。

  • 智能个案路由: 自动分析新传入的客户邮件或社交媒体帖子。如果检测到强烈负面情绪,可以立即将个案升级到高级支持团队或标记为高优先级。
  • 客户反馈分析: 批量分析产品评论或调查问卷的开放式回答,快速了解客户的整体满意度,而无需人工逐条阅读。
  • 品牌声誉监控: 监控社交媒体上提及品牌的内容,及时发现并应对潜在的公关危机。

2. Einstein Intent (意图识别)

此 API 旨在识别文本背后的用户意图或目标。您需要使用自己的数据来训练一个自定义分类模型。

  • 聊天机器人与自动化: 当用户在聊天窗口输入“我的密码无法登录”时,系统可以将其识别为“重置密码”意图,并自动触发相应的密码重置流程。
  • 邮件分类: 自动将收到的销售邮件分类为“询价”、“索要报价”或“技术支持”,并分发给相应的团队。
  • 自动化工作流触发: 根据从文本中识别出的特定意图(如“取消订单”),自动在 Apex 中调用相应的方法来处理业务逻辑。

3. Einstein NER (Named Entity Recognition, 命名实体识别)

命名实体识别 API 可以从非结构化文本中提取预定义的实体,例如人名、公司名、地点、产品等。同样,这需要您训练自定义模型。

  • 数据自动填充: 从客户邮件正文中自动提取联系人姓名、公司、电话号码和地址,并用于创建或更新 Salesforce 中的 Lead 或 Contact 记录,极大地提高了数据录入效率。
  • 知识提取: 从大量的技术文档或报告中提取关键信息,如产品名称、版本号和关键特性,用于构建知识库。
  • 竞争情报分析: 分析新闻文章或行业报告,自动识别并提取竞争对手的名称及其相关活动。

对于 Salesforce 开发人员来说,掌握如何通过 Apex 调用这些 API,意味着我们能够构建更智能、更自动化、响应更迅速的应用程序,从而显著提升用户体验和业务效率。


原理说明

从开发人员的角度来看,与 Einstein Language API 的交互本质上是一个标准的 REST API 调用过程。整个流程可以分解为以下几个关键步骤:

1. 认证与授权

Einstein Platform Services API 使用 OAuth 2.0 进行身份验证。对于服务器到服务器的集成(例如从 Apex 调用),最常用和推荐的方式是 JWT (JSON Web Token) Bearer Flow。这个流程不需要用户交互,非常适合后端服务。

流程如下:

  1. 在 Salesforce 中创建一个连接的应用程序 (Connected App),并启用 OAuth 设置。
  2. 为该 Connected App 生成一个数字证书和私钥。将证书上传到 Connected App 中。
  3. 在 Apex 代码中,使用私钥、颁发者(客户端 ID)、主题(用户名)等信息构建一个 JWT。
  4. 使用 Apex 的 `Auth.JWT` 和 `Auth.JWS` 类对 JWT 进行签名。
  5. 将签名的 JWT 发送到 Salesforce 的 OAuth 令牌端点 (`/services/oauth2/token`),以换取一个临时的 `access_token`。

这个获取到的 `access_token` 将作为 Bearer Token 包含在后续所有对 Einstein Language API 的请求头中。

2. 构建 API 请求

获取到 `access_token` 后,我们就可以向具体的 Einstein Language API 端点发起请求。这些请求通常是 `POST` 请求,并且请求体 (Request Body) 的格式是 `multipart/form-data`。

一个典型的请求包含以下部分:

  • HTTP 方法: `POST`
  • Endpoint URL: 例如,情感分析的端点是 `https://api.einstein.ai/v2/language/sentiment`。
  • 请求头 (Headers):
    • `Authorization: Bearer `
    • `Content-Type: multipart/form-data; boundary=`
  • 请求体 (Body):

    请求体是一个 `multipart/form-data` 结构,其中包含一个或多个部分。对于情感分析,通常包含以下部分:

    • `modelId`: 指定要使用的模型。对于通用的情感分析,可以使用预置的 `CommunitySentiment` 模型。
    • `document`: 您希望分析的文本内容。

3. 处理 API 响应

API 会返回一个 JSON 格式的响应。我们需要在 Apex 中解析这个 JSON 字符串来获取分析结果。

一个成功的情感分析响应 JSON 可能如下所示:

{
  "probabilities": [
    {
      "label": "Positive",
      "probability": 0.987654
    },
    {
      "label": "Negative",
      "probability": 0.001234
    },
    {
      "label": "Neutral",
      "probability": 0.011112
    }
  ],
  "object": "prediction"
}

在 Apex 中,最佳实践是创建一个 Wrapper Class(包装类)来匹配这个 JSON 结构,然后使用 `JSON.deserialize()` 方法将响应字符串自动转换为 Apex 对象,这样可以使代码更具可读性和健壮性。


示例代码

下面的示例将完整展示如何通过 Apex 调用 Einstein Sentiment API 来分析一段文本的情感。这个过程分为两个主要部分:获取 Access Token 和调用情感分析 API。

准备工作:

  1. 创建一个自签名证书。在“设置”中搜索“证书和密钥管理”。
  2. 创建一个连接的应用程序 (Connected App),启用 OAuth,并上传您刚刚创建的证书。确保授予其适当的 OAuth 范围(例如 `api`)。
  3. 将 API 调用端点 `https://api.einstein.ai` 添加到“远程站点设置”中。

第 1 步:获取 Access Token 的 Apex 方法

我们将创建一个可重用的方法来处理 JWT 流程并返回一个 access token。

// EinsteinPlatformService.cls
public class EinsteinPlatformService {

    // 从自定义元数据类型或自定义设置中获取这些值,避免硬编码
    private static final String EINSTEIN_TOKEN_ENDPOINT = 'https://api.einstein.ai/v2/oauth2/token';
    private static final String EINSTEIN_ACCOUNT_ID = 'your_einstein_account_id_or_email'; // 你的 Einstein 平台账户ID或电子邮件
    private static final String CERTIFICATE_NAME = 'Your_Certificate_Developer_Name'; // 证书的开发者名称
    private static final String JWT_AUDIENCE = 'https://api.einstein.ai/v2/oauth2/token';
    private static final Long TOKEN_VALIDITY_SECONDS = 3600;

    /**
     * @description 使用 JWT Bearer Flow 获取 Einstein API 的访问令牌
     * @return String a valid access token
     */
    public static String getAccessToken() {
        // 创建 JWT
        Auth.JWT jwt = new Auth.JWT();
        jwt.setAud(JWT_AUDIENCE);
        // iss 应该是连接应用(Connected App)的 Consumer Key
        jwt.setIss(System.Label.Einstein_Connected_App_Consumer_Key); // 推荐使用自定义标签或元数据
        jwt.setSub(EINSTEIN_ACCOUNT_ID);
        
        // 设置令牌的过期时间(例如,当前时间后一小时)
        Long exp = (System.currentTimeMillis() / 1000) + TOKEN_VALIDITY_SECONDS;
        Map<String, Object> claims = new Map<String, Object>();
        claims.put('exp', exp);
        jwt.setAdditionalClaims(claims);

        // 使用存储在 Salesforce 中的证书对 JWT 进行签名
        Auth.JWS jws = new Auth.JWS(jwt, CERTIFICATE_NAME);
        String signedToken = jws.getCompactSerialization();

        // 准备请求以换取 access token
        String body = 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=' + EncodingUtil.urlEncode(signedToken, 'UTF-8');
        
        HttpRequest req = new HttpRequest();
        req.setEndpoint(EINSTEIN_TOKEN_ENDPOINT);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
        req.setBody(body);
        
        // 发送请求并处理响应
        Http http = new Http();
        HttpResponse res = http.send(req);
        
        if (res.getStatusCode() == 200) {
            Map<String, Object> responseMap = (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
            return (String) responseMap.get('access_token');
        } else {
            // 抛出异常或记录错误
            System.debug('Error getting token: ' + res.getBody());
            throw new CalloutException(res.getStatus() + ': ' + res.getBody());
        }
    }
}

第 2 步:调用情感分析 API

现在我们有了获取 token 的方法,接下来将编写调用情感分析端点的方法,并解析其返回结果。

// EinsteinSentimentService.cls
public class EinsteinSentimentService {

    private static final String SENTIMENT_API_ENDPOINT = 'https://api.einstein.ai/v2/language/sentiment';
    
    // 用于反序列化JSON响应的内部包装类
    public class SentimentPrediction {
        public List<SentimentProbability> probabilities;
    }

    public class SentimentProbability {
        public String label; // "Positive", "Negative", "Neutral"
        public Double probability;
    }

    /**
     * @description 调用 Einstein Sentiment API 分析文本情感
     * @param textToAnalyze 需要分析的文本
     * @return SentimentPrediction 包含情感分析结果的对象
     */
    public static SentimentPrediction analyzeSentiment(String textToAnalyze) {
        // 首先,获取 access token
        String accessToken = EinsteinPlatformService.getAccessToken();

        // 构建 multipart/form-data 请求体
        // 这是在 Apex 中构建这种请求体的标准方法
        String boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW';
        String body = '';
        body += '--' + boundary + '\r\n';
        body += 'Content-Disposition: form-data; name="modelId"\r\n\r\n';
        body += 'CommunitySentiment\r\n'; // 使用预置的通用情感模型
        body += '--' + boundary + '\r\n';
        body += 'Content-Disposition: form-data; name="document"\r\n\r\n';
        body += textToAnalyze + '\r\n';
        body += '--' + boundary + '--';

        HttpRequest req = new HttpRequest();
        req.setEndpoint(SENTIMENT_API_ENDPOINT);
        req.setMethod('POST');
        req.setHeader('Authorization', 'Bearer ' + accessToken);
        req.setHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
        req.setBody(body);
        req.setTimeout(120000); // 设置超时时间

        Http http = new Http();
        HttpResponse res = http.send(req);

        if (res.getStatusCode() == 200) {
            // 将成功的响应 JSON 反序列化为我们的包装类对象
            return (SentimentPrediction) JSON.deserialize(res.getBody(), SentimentPrediction.class);
        } else {
            System.debug('Error during sentiment analysis callout: ' + res.getBody());
            throw new CalloutException(res.getStatus() + ': ' + res.getBody());
        }
    }
}

// 示例调用方法
public static void testSentimentAnalysis() {
    String customerFeedback = 'The support I received was absolutely fantastic! The issue was resolved in minutes.';
    try {
        EinsteinSentimentService.SentimentPrediction result = EinsteinSentimentService.analyzeSentiment(customerFeedback);
        for (EinsteinSentimentService.SentimentProbability p : result.probabilities) {
            System.debug('Label: ' + p.label + ', Probability: ' + p.probability);
            // Label: Positive, Probability: 0.987654 (示例值)
        }
    } catch (Exception e) {
        System.debug('An error occurred: ' + e.getMessage());
    }
}

注意事项

在您的项目中实施 Einstein Language API 时,请务必考虑以下几点:

  • 权限与配置:
    • 执行调用的用户需要被分配一个包含 "API Enabled" 系统权限的 Profile 或 Permission Set。
    • 用于 JWT 流程的用户必须在连接的应用程序的策略中被预先授权。
    • 不要忘记在远程站点设置中添加 `https://api.einstein.ai`。
  • API 限制 (API Limits):
    • Salesforce Governor Limits: Apex 事务中的 HTTP Callout 数量是有限的(同步为100个)。如果您需要处理大量记录,请务必使用异步 Apex,如 `Queueable` 或 `Batch Apex`,将调用分散到不同的事务中。
    • Einstein Platform Services Limits: 您的 Salesforce 组织每月可以进行的 Einstein API 调用总数是有限额的。这个限额取决于您的许可证类型。请在“设置”中的“公司信息”页面查看您的使用情况,并设计相应的节流或缓存机制以避免超出限制。
  • 错误处理 (Error Handling):
    • 网络是不可靠的。始终将您的 HTTP Callout 代码包裹在 `try-catch` 块中,以捕获 `CalloutException` 或其他潜在异常。
    • 检查 HTTP 响应状态码 (`res.getStatusCode()`)。`200` 表示成功,但其他代码如 `400` (Bad Request), `401` (Unauthorized), 或 `429` (Too Many Requests) 都需要被妥善处理。
    • 当发生错误时,API 的响应体通常会包含一个描述错误的 JSON。解析这个错误信息并记录下来,以便于调试。
  • 安全与凭证管理:
    • 绝对不要在 Apex 代码中硬编码任何敏感信息,如 Consumer Key, Consumer Secret, 或私钥。使用自定义元数据类型 (Custom Metadata Types)、自定义设置 (Custom Settings) 或命名凭证 (Named Credentials) 来安全地存储这些配置。在示例中,我们使用了自定义标签,这也是一种好方法。
    • 证书和私钥是高度敏感的。确保只有授权的管理员才能访问和管理它们。
  • 数据隐私:

    请注意,调用这些 API 意味着您将数据发送到 Salesforce 的 Einstein 服务进行处理。虽然这些服务是安全的,但在处理受 PII (Personally Identifiable Information, 个人可识别信息) 或其他法规(如 GDPR、HIPAA)约束的敏感数据时,请务必与您的公司法务和安全团队确认这是否符合合规性要求。


总结与最佳实践

Einstein Language API 为 Salesforce 开发人员打开了一扇通往人工智能世界的大门。通过简单的 REST API 调用,我们就可以在 Apex 中集成强大的自然语言处理功能,构建出能够理解、分析和响应人类语言的智能应用。

以下是一些在项目中应用 Einstein Language API 的最佳实践:

  1. 封装逻辑,提高复用性:

    创建专门的 Apex 服务类 (Service Class),如示例中的 `EinsteinPlatformService` 和 `EinsteinSentimentService`。将认证逻辑和各个 API 的调用逻辑分离开来。这使得代码更易于维护、测试和在不同地方重用。

  2. 使用命名凭证 (Named Credentials):

    虽然我们的示例代码手动构建了 HTTP 请求,但对于更复杂的场景,强烈推荐使用命名凭证。它可以为您处理大部分的认证细节(包括令牌的自动刷新),并简化 Callout 代码,使其更具可读性和安全性。您只需在 Apex 中引用命名凭证即可,Salesforce 会在后台完成认证过程。

  3. 采用异步处理模式:

    对于任何可能涉及批量处理文本数据的场景(例如,分析一个 Campaign 下所有成员的社交媒体评论),都应使用异步 Apex (`@future`, `Queueable`, `Batchable`)。这可以有效避免触及 Governor Limits,并为用户提供更流畅的前端体验。

  4. 从预置模型开始:

    对于情感分析,始终先从 Salesforce 提供的 `CommunitySentiment` 预置模型开始。它的通用性很强,通常足以满足大部分业务需求。只有当您发现该模型在您的特定行业术语或语境下表现不佳时,才考虑投入时间和资源去训练一个自定义模型。

  5. 迭代优化自定义模型:

    对于 Intent 和 NER,模型的效果高度依赖于您提供的训练数据的质量和数量。这是一个持续迭代的过程。从一个小的、高质量的数据集开始,部署第一个版本的模型,然后在实际应用中收集更多的数据和反馈,不断地对模型进行重新训练和优化。

总之,通过遵循这些原则和实践,您可以高效、安全地将 Einstein Language 的强大功能集成到您的 Salesforce 解决方案中,为您的客户和用户创造前所未有的价值。

评论

此博客中的热门博文

Salesforce 登录取证:深入解析用户访问监控与安全

Salesforce Experience Cloud 技术深度解析:构建社区站点 (Community Sites)

Salesforce Einstein AI 编程实践:开发者视角下的智能预测