GPT 操作库:Salesforce + Gong

简介

本页面为开发者提供了构建连接到特定应用程序的 GPT 操作的说明和指南。在继续之前,请确保您已熟悉以下信息:

此特定的 GPT 操作概述了如何构建一个从 Salesforce 和 Gong 检索信息的 GPT。这将包括创建多个自定义操作,这些操作已在现有的 cookbook 中进行了记录。我们将在下一节中重点介绍这些 cookbook。

价值 + 示例业务用例

价值:用户现在可以利用 ChatGPT 的功能来:

  • 连接到 Salesforce
  • 搜索客户账户
  • 从 Gong 检索先前的通话记录

示例用例

一位销售代表正在为即将举行的客户会议做准备。通过此集成,他们可以快速从 Salesforce 中检索相关的账户详细信息,访问最近的 Gong 通话记录,并获得围绕经过验证的销售方法(如 MEDPICC 或 SPICED)构建的 AI 生成的摘要和见解。这使代表能够清晰、可操作地了解客户的当前状态和后续步骤——所有这些都在几分钟内完成。

应用程序信息

在此示例中,我们将连接到 Salesforce 和 Gong(通过中间件)。我们将引用现有的 cookbook 来获取 Salesforce 的基本设置和身份验证说明,以及创建中间件。

Salesforce GPT 操作

请参阅我们关于为 Salesforce 设置 GPT 操作的 cookbook。在该 cookbook 中需要注意的两个设置是:

  • 应用程序信息 - 这涵盖了 Salesforce 中需要熟悉的必要概念
  • 身份验证说明 - 这涵盖了在 Salesforce 中创建已连接的应用程序以及配置 OAuth(在 Salesforce 和 ChatGPT 上)

中间件 GPT 操作

请参阅我们关于创建中间件的任何一个 cookbook:

应用程序先决条件

除了上述 cookbook 中的先决条件外,请确保您能够访问 Gong API 密钥。

应用程序设置

部署无服务器函数

此无服务器函数将接受一个 callIds 数组,从 Gong 获取记录,并清理发送到 ChatGPT 的响应。以下是 Azure Functions(Javascript)上的示例:

const { app } = require('@azure/functions');
const axios = require('axios');

// 替换为您的 Gong API 令牌
const GONG_API_BASE_URL = "https://api.gong.io/v2";
const GONG_API_KEY = process.env.GONG_API_KEY;

app.http('callTranscripts', {
    methods: ['POST'],
    authLevel: 'function',
    handler: async (request, context) => {        
        try {            
            const body = await request.json();
            const callIds = body.callIds;

            if (!Array.isArray(callIds) || callIds.length === 0) {
                return {
                    status: 400,
                    body: "请在 'callIds' 数组中提供通话 ID。"
                };
            }

            // 获取通话记录
            const transcriptPayload = { filter: { callIds } };
            const transcriptResponse = await axios.post(`${GONG_API_BASE_URL}/calls/transcript`, transcriptPayload, {
                headers: {
                    'Authorization': `Basic ${GONG_API_KEY}`,
                    'Content-Type': 'application/json'
                }
            });

            const transcriptData = transcriptResponse.data;

            // 获取详细的通话信息
            const extensivePayload = {
                filter: { callIds },
                contentSelector: {
                    exposedFields: { parties: true }
                }
            };

            const extensiveResponse = await axios.post(`${GONG_API_BASE_URL}/calls/extensive`, extensivePayload, {
                headers: {
                    'Authorization': `Basic ${GONG_API_KEY}`,
                    'Content-Type': 'application/json'
                }
            });

            const extensiveData = extensiveResponse.data;

            // 创建一个包含元数据和说话者详细信息的通话 ID 映射
            const callMetaMap = {};
            extensiveData.calls.forEach(call => {
                callMetaMap[call.metaData.id] = {
                    title: call.metaData.title,
                    started: call.metaData.started,
                    duration: call.metaData.duration,
                    url: call.metaData.url,
                    speakers: {}
                };

                call.parties.forEach(party => {
                    callMetaMap[call.metaData.id].speakers[party.speakerId] = party.name;
                });
            });

            // 将记录数据转换为内容并包含元数据
            transcriptData.callTranscripts.forEach(call => {
                const meta = callMetaMap[call.callId];
                if (!meta) {
                    throw new Error(`未找到 callId ${call.callId} 的元数据。`);
                }

                let content = '';
                call.transcript.forEach(segment => {
                    const speakerName = meta.speakers[segment.speakerId] || "未知说话者";

                    // 将说话者的所有句子合并为一个段落
                    const sentences = segment.sentences.map(sentence => sentence.text).join(' ');
                    content += `${speakerName}: ${sentences}\n\n`; // 在说话者回合之间添加换行符
                });

                // 将元数据和内容添加到通话对象
                call.title = meta.title;
                call.started = meta.started;
                call.duration = meta.duration;
                call.url = meta.url;
                call.content = content;

                delete call.transcript;
            });

            // 返回修改后的记录数据
            return {
                status: 200,
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(transcriptData)
            };
        } catch (error) {
            context.log('[ERROR]', "处理请求时出错:", error);

            return {
                status: error.response?.status || 500,
                body: {
                    message: "获取或处理通话数据时出错。",
                    details: error.response?.data || error.message
                }
            };
        }
    }
});

以下是您将在 package.json 文件中包含的依赖项:

"dependencies": {
    "@azure/functions": "^4.0.0",
    "axios": "^1.7.7"
  }

ChatGPT 步骤

自定义 GPT 说明

创建自定义 GPT 后,请将以下文本复制到“说明”面板中。有疑问吗?请查看 入门示例,了解此步骤的详细信息。

# 触发器
用户输入他们想准备的账户名称

# 步骤

1. 检索账户名称 - 调用 `executeSOSLSearch` 自定义操作,使用 SOSL 搜索具有该名称的 Salesforce 账户。检索最多 5 个账户。查询应如下所示 - `FIND {Acme} IN ALL FIELDS RETURNING Account(Id, Name) LIMIT 5`

2. 以此格式显示账户 - `账户名称 - salesforceID`。询问用户他们感兴趣的账户。

3. 获取账户的 Gong 通话 ID - 对于已确认的账户,调用 `executeSOQLQuery` 以获取所有 Gong 通话 ID。它应如下所示 - `SELECT XXX, YYY, ZZZ
FROM Gong__Gong_Call__c 
WHERE Gong__Primary_Account__c = '<AccountId>' 
ORDER BY Gong__Call_Start__c DESC 
LIMIT 2
`

4. 将 callIds 传递给 `getTranscriptsByCallIds `

# 触发器
用户说“总结通话”

# 步骤

使用通话记录并提供以下输出

## 账户名称
打印出账户名称

## 通话详情
> 请在此表格格式中列出您检索了通话记录的通话,以及它们的日期和与会者:
>> 表头:<通话标题>, <日期>, <与会者>, <Gong URL>

## 推荐的会议焦点领域:
> 分析通话记录以识别主题、挑战和机遇。基于此,生成一份关于下次会议推荐焦点领域的列表。这些应是可操作的,并且针对客户的需求。解释为什么每一项都应该是会议的焦点。

对于以下每一项见解,请指定您从中获取见解的通话和日期:

### 指标
客户试图实现的量化结果。这些可能是成本降低、收入增加、用户增长、效率提升等。查找提到的 KPI 或成功指标。

### 经济买家
确定是否提到了真正的经济决策者或是否涉及他们。这包括职位、姓名或关于预算所有权或最终决策权的提示。

### 决策标准
客户将用于评估解决方案的关键因素是什么?这些可能包括价格、性能、支持、集成、易用性等。

### 决策流程
描述客户计划如何做出购买决定:阶段、涉及的利益相关者、批准流程、时间表。

### 文件流程
捕获任何提及的与法律、采购、合规或合同相关的步骤和时间表。

### 识别痛点
突出客户面临的核心业务挑战,最好用他们自己的话来说。了解是什么驱动了紧迫性。

### 拥护者
内部是否有人在倡导我们的解决方案?提及姓名、角色或表明支持的举止(例如,“我正在内部推动此事”)。

### (可选)竞争
提及讨论过的任何竞争对手、内部构建或替代解决方案。

在上面的示例中,请替换查询(3)以从您的自定义 Salesforce 对象中检索 Gong 通话 ID。

您现在将创建 2 个单独的操作 - 一个用于连接到 Salesforce,另一个用于连接到调用 Gong API 的中间件。

Salesforce 自定义操作的 OpenAPI 架构

创建自定义 GPT 后,请将以下文本复制到“操作”面板中。有疑问吗?请查看 入门示例,了解此步骤的详细信息。

以下是连接到 Salesforce 的示例。您需要在此部分插入您的 URL。

openapi: 3.1.0
info:
  title: Salesforce API
  version: 1.0.0
  description: API for accessing Salesforce sObjects and executing queries.
servers:

  - url: https://<subdomain>.my.salesforce.com/services/data/v59.0
    description: Salesforce API server
paths:
  /query:
    get:
      summary: Execute a SOQL Query
      description: Executes a given SOQL query and returns the results.
      operationId: executeSOQLQuery
      parameters:

        - name: q
          in: query
          description: The SOQL query string to be executed.
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Query executed successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QueryResult'

  /search:
    get:
      summary: Execute a SOSL Search
      description: Executes a SOSL search based on the given query and returns matching records.
      operationId: executeSOSLSearch
      parameters:

        - name: q
          in: query
          description: The SOSL search string (e.g., 'FIND {Acme}').
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Search executed successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchResult'

components:
  schemas:
    QueryResult:
      type: object
      description: Result of a SOQL query.
      properties:
        totalSize:
          type: integer
          description: The total number of records matching the query.
        done:
          type: boolean
          description: Indicates if the query result includes all records.
        records:
          type: array
          description: The list of records returned by the query.
          items:
            $ref: '#/components/schemas/SObject'

    SearchResult:
      type: object
      description: Result of a SOSL search.
      properties:
        searchRecords:
          type: array
          description: The list of records matching the search query.
          items:
            $ref: '#/components/schemas/SObject'

    SObject:
      type: object
      description: A Salesforce sObject, which represents a database table record.
      properties:
        attributes:
          type: object
          description: Metadata about the sObject, such as type and URL.
          properties:
            type:
              type: string
              description: The sObject type.
            url:
              type: string
              description: The URL of the record.
        Id:
          type: string
          description: The unique identifier for the sObject.
      additionalProperties: true

Salesforce 自定义操作的身份验证说明

请遵循 GPT 操作库 - Salesforce 中显示的步骤。

连接到 Gong 的中间件的 OpenAPI 架构

在此示例中,我们将为调用 Gong API 的 Azure Function 进行设置。请将 url 替换为您自己的中间件 URL。

openapi: 3.1.0
info:
  title: Call Transcripts API
  description: API to retrieve call transcripts and associated metadata by specific call IDs.
  version: 1.0.1
servers:

  - url: https://<subdomain>.azurewebsites.net/api
    description: Production server
paths:
  /callTranscripts:
    post:
      operationId: getTranscriptsByCallIds
      x-openai-isConsequential: false
      summary: Retrieve call transcripts by call IDs
      description: Fetches specific call transcripts based on the provided call IDs in the request body.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                callIds:
                  type: array
                  description: List of call IDs for which transcripts need to be fetched.
                  items:
                    type: string
              required:

                - callIds
      responses:
        '200':
          description: A successful response containing the requested call transcripts and metadata.
          content:
            application/json:
              schema:
                type: object
                properties:
                  requestId:
                    type: string
                    description: Unique request identifier.
                  records:
                    type: object
                    description: Metadata about the pagination.
                    properties:
                      totalRecords:
                        type: integer
                        description: Total number of records available.
                      currentPageSize:
                        type: integer
                        description: Number of records in the current page.
                      currentPageNumber:
                        type: integer
                        description: The current page number.
                  callTranscripts:
                    type: array
                    description: List of call transcripts.
                    items:
                      type: object
                      properties:
                        callId:
                          type: string
                          description: Unique identifier for the call.
                        title:
                          type: string
                          description: Title of the call or meeting.
                        started:
                          type: string
                          format: date-time
                          description: Timestamp when the call started.
                        duration:
                          type: integer
                          description: Duration of the call in seconds.
                        url:
                          type: string
                          format: uri
                          description: URL to access the call recording or details.
                        content:
                          type: string
                          description: Transcript content of the call.
        '400':
          description: Invalid request. Possibly due to missing or invalid `callIds` parameter.
        '401':
          description: Unauthorized access due to invalid or missing API key.
        '500':
          description: Internal server error.

您是否有希望我们优先处理的集成?我们的集成中是否存在错误?在我们的 GitHub 上提交 PR 或 issue,我们会进行查看。