2025年4月7日

GPT Actions 库 - Salesforce 和 Gong

简介

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

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

价值与示例商业应用场景

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

  • 连接到Salesforce
  • 搜索客户账户
  • 从之前的通话记录中检索Gong转录文本

示例用例:

一位销售代表正在为即将到来的客户会议做准备。通过此集成,他们可以快速从Salesforce检索相关客户详细信息,访问最近的Gong通话记录,并获取围绕MEDPICC或SPICED等成熟销售方法论构建的AI生成摘要和洞察。这使销售代表能在几分钟内清晰、可操作地了解客户当前状态和后续步骤。

应用信息

在这个示例中,我们将连接Salesforce和Gong(通过中间件)。我们将参考现有的操作手册来获取Salesforce的基本设置和认证说明,以及创建中间件的指南。

Salesforce GPT Action

请参考我们关于为Salesforce设置GPT Action的指南。在该指南中需要特别注意的两项设置是:

中间件GPT操作

请参考我们关于创建中间件的任何一本指南:

应用前提条件

除了上述指南中的先决条件外,请确保您拥有Gong API密钥的访问权限

应用设置

部署无服务器函数

该无服务器函数将接收一个callIds数组,从Gong获取转录文本,并清理发送给ChatGPT的响应。以下是它在Azure Functions(Javascript)上的示例

const { app } = require('@azure/functions');
const axios = require('axios');
 
// Replace with your Gong API token
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: "Please provide call IDs in the 'callIds' array."
                };
            }
 
            // Fetch call transcripts
            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;
 
            // Fetch extensive call details
            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;
 
            // Create a map of call IDs to metadata and speaker details
            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;
                });
            });
 
            // Transform transcript data into content and include metadata
            transcriptData.callTranscripts.forEach(call => {
                const meta = callMetaMap[call.callId];
                if (!meta) {
                    throw new Error(`Metadata for callId ${call.callId} not found.`);
                }
 
                let content = '';
                call.transcript.forEach(segment => {
                    const speakerName = meta.speakers[segment.speakerId] || "Unknown Speaker";
 
                    // Combine all sentences for the speaker into a paragraph
                    const sentences = segment.sentences.map(sentence => sentence.text).join(' ');
                    content += `${speakerName}: ${sentences}\n\n`; // Add a newline between speaker turns
                });
 
                // Add metadata and content to the call object
                call.title = meta.title;
                call.started = meta.started;
                call.duration = meta.duration;
                call.url = meta.url;
                call.content = content;
                
                delete call.transcript;
            });
 
            // Return the modified transcript data
            return {
                status: 200,
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(transcriptData)
            };
        } catch (error) {
            context.log('[ERROR]', "Error processing request:", error);
 
            return {
                status: error.response?.status || 500,
                body: {
                    message: "An error occurred while fetching or processing call data.",
                    details: error.response?.data || error.message
                }
            };
        }
    }
});
 

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

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

ChatGPT 步骤

自定义GPT指令

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

# Trigger
User enters the name of an account that they want to prepare for

# Steps
1. Retrieve Account Names - Make a call to the `executeSOSLSearch` custom action searching for a Salesforce Account with that name (SOSL). Retrieve up to 5 accounts. This is what the query should look like - `FIND {Acme} IN ALL FIELDS RETURNING Account(Id, Name) LIMIT 5`

2. Show the accounts in this format - `Account Name - salesforceID`. Ask the user to confirm which account they are interested in. 

3. Get Gong Call IDs for the account - For the confirmed account, make a call to `executeSOQLQuery` to get all the Gong Call IDs. It should look like this - `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. Pass in the callIds to `getTranscriptsByCallIds `

# Trigger
User says "Summarize call"

# Steps

Use both the transcripts and provide the following output

## Account Name
Print out the account name 

## Details of calls
>Please list the calls for which you retrieved the transcripts along with their dates and attendees in this table format:  
>>Headers: <Title of Call>, <Date>, <Attendees>, <Gong URL>

## Recommended Meeting Focus Areas: 
>Analyze the transcripts to identify themes, challenges, and opportunities. Based on this, generate a list of recommended focus areas for the next meeting. These should be actionable and specific to the client’s needs. Explain **why** each item should be a meeting focus.

For each of the following insights, specify **which call and date** you sourced the insight from:

### Metrics
Quantifiable outcomes the customer is trying to achieve. These could be cost reduction, increased revenue, user growth, efficiency gains, etc. Look for KPIs or success measures mentioned.

### Economic Buyer
Identify if the true economic decision-maker was mentioned or involved. This includes titles, names, or hints at budget ownership or final authority.

### Decision Criteria
What are the key factors the customer will use to evaluate solutions? These could include price, performance, support, integrations, ease of use, etc.

### Decision Process
Describe how the customer plans to make the buying decision: stages, stakeholders involved, approval processes, timelines.

### Paper Process
Any mention of legal, procurement, compliance, or contract-related steps and timelines should be captured here.

### Identify Pain
Highlight the core business challenges the customer is facing, ideally in their own words. Understand what’s driving urgency.

### Champion
Is there someone internally who is championing our solution? Mention names, roles, or behaviors that indicate advocacy (e.g., “I’m pushing this internally”).

### (Optional) Competition
Mention any competing vendors, internal builds, or alternative solutions discussed.

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

你现在需要创建两个独立的操作 - 一个用于连接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 Actions库 - Salesforce中显示的步骤操作

连接至Gong的中间件OpenAPI架构

在这个示例中,我们正在为调用Gong API的Azure函数进行设置。请将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或问题,我们会尽快查看。