SailPoint如何使用Anthropic的Claude在Amazon Bedrock上自动生成用于SaaS连接器的TypeScript代码 | Amazon Web Services

2024-10-16 19:32:03 英文原文

生成式AI正在通过在整个编码过程中提供智能辅助和自动化来革新开发人员的编程方式。凭借先进的语言模型和机器学习(ML)算法的力量,生成式人工智能能够理解程序员代码背后的背景和意图,提供有价值的建议,完成代码片段,并根据高层次描述生成整个函数或模块。这项技术使开发者能够专注于更高层次的问题解决和架构设计,而AI则处理编码中繁琐和重复的部分。大型语言模型(LLMs)在编程中的一个关键优势是它们能够从训练过程中接触到的大量现有代码和编程模式中学习。这种知识使它们能够生成上下文感知的代码,检测潜在的错误或漏洞,并提供优化以提高代码质量和性能。

在这篇文章中,我们强调了如何使用亚马逊AWS生成式AI创新中心与……合作了 SailPoint技术公司构建一个基于生成式AI的编码助手,使用Anthropic的Claude Sonnet亚马逊Bedrock以加速开发为目标帮助软件即服务(SaaS)连接器.

Amazon Bedrock 是一项完全管理的服务,提供来自领先人工智能公司(如AI21 Labs、Anthropic、Cohere、Meta、Mistral AI、Stability AI和亚马逊)的高性能基础模型(FMs),并通过单一API提供广泛的构建生成式人工智能应用的能力,这些能力包括安全性、隐私保护和负责任的人工智能。

SailPoint专注于企业身份安全解决方案。全球超过3,000家企业使用SailPoint帮助防御当今以身份为中心的动态网络威胁,同时提高生产力和效率。他们的产品旨在通过身份管理的角度,在速度和规模上管理并保护组织内部用户以及外部方(如非员工)对应用程序和数据的访问权限。SailPoint统一、智能且可扩展的环境提供了全面的身份治理功能,包括访问认证、政策管理、访问请求与配置、密码管理和数据访问治理。这有助于组织确保正确的个人在正确的时间拥有对正确资源的适当访问权,从而执行安全策略和合规要求。成立于2005年的SailPoint现已成长为身份安全领域的重要参与者,并在全球各个行业中为客户提供服务。

SailPoint连接器和SaaS连接性

SailPoint的身份安全解决方案与各种软件即服务(SaaS)应用程序接口,以从身份安全的角度检索必要的信息,如账户和访问信息。每个SaaS应用实现这些功能的方式略有不同,并且可能会通过REST基础的Web API公开其实现,这些API通常被支持的开放API规范SailPoint连接器是TypeScript模块,用于与SaaS应用程序进行接口交互,并将相关的身份安全信息(如账户和权限)映射到SailPoint能够理解的标准格式。根据应用程序暴露的API,SailPoint连接器可以在这些账户上创建、更新和删除访问权。SailPoint连接器有助于在组织内的不同环境中管理用户身份及其访问权限,支持组织的合规性和安全工作。

尽管一个典型的连接器暴露了多个功能,但对于这篇帖子,我们专注于开发连接到用于列出用户、检索所有用户并将其转换为SailPoint所需格式的API端点的连接器的列表用户功能。

在以下章节中,我们详细介绍了如何使用Anthropic的Claude Sonnet在Amazon Bedrock上自动创建列表用户连接器,这是更广泛的SaaS连接性的一个关键组件。

理解列表用户连接器

连接器是能够连接到外部服务并从SaaS应用程序中检索和更新相关信息的模块。为了更好地理解连接器是如何构建的,我们以一个连接到外部服务的连接器功能为例。DocuSign的REST API的getUsers端点以下的TypeScript代码定义了一个异步函数listUsers,该函数从外部API获取用户账户列表,并为每个用户构建结构化的输出。

// 导入
import { Context, Response, StdAccountListHandler, StdAccountListOutput } from '@sailpoint/connector-sdk';

const listUsers: StdAccountListHandler = async (context: Context, input: undefined, res: Response<StdAccountListOutput>) => {
    // 从上下文中获取 API 密钥和主机 URL
    let apiKey = context.apiKey;
    let hostUrl = context.hostUrl;
    let hasMore = true;
    
    // 构造 URL
    let url = `https://${hostUrl}/Management/v2/organizations/${context.organizationId}/users`;
    
    // 循环遍历页面
    while (hasMore) {
        // 从端点获取响应
        let response = await fetch(url, {
          headers: {
            'Authorization': `Bearer ${apiKey}`
          }
        });
        let results = await response.json();
  
        // 处理响应中的用户信息
        let users = results.users;
        for (const user of users) {
            const output: StdAccountListOutput = {
                identity: user.id,
                attributes: {
                    user_name: user.user_name,
                    first_name: user.first_name,
                    last_name: user.last_name,
                    user_status: user.user_status, 
                    membership_status: user.membership_status, 
                    email: user.email,
                    created_on: user.created_on, 
                    membership_created_on: user.membership_created_on,                     
                    ds_group_id: user.company_groups.map(group => group.ds_group_id),
                    ds_group_account_id: user.company_groups.map(group => group.ds_group_account_id)                   
                }
            };
        }
        // 分页处理
        if (results.paging.next) {
          url = results.paging.next;
        } else {
          hasMore = false;
        }
    }
}

以下是代码每一部分的功能分解:

  • 导入– 代码从其他地方导入了多种类型和接口@sailpoint/连接器-sdk这些包括上下文、响应、StdAccountListHandler和StdAccountListOutput用于在SailPoint环境中以标准方式处理函数的输入和输出。
  • 函数定义列出用户列表被定义为与兼容的异步函数标准账号列表处理器它使用了上下文访问配置详情如API密钥和基础URL等,和一个响应来结构化输出。
  • 检索API密钥和主机URL这些是从中提取的上下文参数。它们用于认证和构建请求URL。
  • URL构建– 该函数使用hostUrl构建初始URL组织ID上下文此URL指向一个端点,该端点返回与特定组织相关联的用户。
  • 循环遍历页面– 的 while循环在还有更多数据页的时候继续进行hasMore为真)。它具有以下功能:
    • 获取数据– 在里面 while循环,一个获取请求发送到API端点。该请求包括一个授权使用的头部标题 rằng使用的头部使用了apiKeyAPI的响应被转换为JSON格式。
    • 处理用户– 在里面 while在循环中,它从API响应中提取用户数据。该过程对每个用户进行循环,构建一个标准账户列表输出每个对象包含用户标识符和属性,如用户名、名称、状态、电子邮件和组ID。
    • 分页在while循环内部,它检查分页信息中是否存在下一个页面的URL结果分页下一页如果存在,则更新它URL如果条件满足,则继续下一次循环。如果不满足,则将hasMore设置为false以停止循环。

理解这个例子有助于我们了解在连接器中构建此功能的逐步过程。我们的目标是使用提示链策略和大型语言模型来重现这一过程。

使用LLM提示链生成TypeScript连接器

使用预训练的大语言模型进行代码生成有几种方法,复杂程度各不相同:

  • 单一提示您可以使用像Anthropic的Claude这样的模型通过直接提示来生成代码。这些模型可以生成多种语言的代码,包括TypeScript,但它们本身并不具备与构建连接器相关的特定领域知识。所有必要的信息,包括API规范和格式说明,都必须在提示中提供,类似于给开发人员提供的指令。然而,当给出一长串复杂的指令时,LLM往往会遇到困难。对于提示工程师来说,理解哪些步骤对LLM来说具有挑战性也很困难。
  • 具有LLM的代理框架代理是一个复杂的框架,可以使用工具来执行一系列复杂任务。在这种情况下,代理首先将用户请求分解为步骤,然后使用工具(如知识库或网络浏览器)查找所需信息,并自主地从头到尾生成代码。尽管这些框架非常强大,但它们在实现时较为复杂,行为通常不稳定且不如其他方法可控。此外,代理执行任务需要进行多次大模型调用,这使得它们在实际操作中速度较慢。如果完成某项任务的逻辑是一个固定的步骤序列,则使用代理并不是一个高效的选择。
  • 提示链一种在前两种方法之间找到良好平衡的解决方案是使用提示链式技术。这种方法将复杂的问题分解为一系列更易管理的步骤,并将它们整合以生成最终代码。每个步骤都有清晰的指令,使得大型语言模型更容易遵循,而循环中的一个人可以控制每一步的输出并在必要时纠正大型语言模型。这种做法在灵活性和控制之间找到了平衡,避免了其他两种方法的极端情况。

我们最初测试了LLM根据单个提示生成连接器代码的能力,发现它难以生成能够解决所有问题方面的代码,例如分页或嵌套数据结构。为了确保LLM能涵盖连接器功能所需的所有组件,并且由于创建连接器遵循固定的步骤顺序,串联提示是最自然的改进生成代码的方法。

我们用于生成连接器的流程包含以下高层步骤:

  1. 将API响应的数据模型解析为指定的TypeScript类。
  2. 生成用户展平函数,格式符合连接器接口的要求。
  3. 理解API规范的分页并制定高层次的解决方案。
  4. 生成代码的描述缺失,请提供完整信息。如果只是示例要求,则输出原文:"Generate the code for the"列出用户通过结合所有中间步骤的功能。

第一步的输出用作第二步的输入,但第三步是独立的。第二步和第三步的结果都被输送到第四步以产生最终结果。以下图表说明了这一工作流程。

在接下来的部分中,我们将深入介绍我们为每个步骤使用的提示技术。

系统提示

系统提示是LLM提示中的一个核心组成部分,通常提供初始上下文以引导模型的响应。对于链中所有的提示,我们使用了以下系统提示:

你是一位精通网页开发的专家,对REST APIs和使用TypeScript进行后端API开发有很好的理解。你的目标是理解以OpenAPI规范或Swagger规范提供的 YAML 文件形式的 API 规范,并根据任务描述编写TypeScript代码、XML、API 描述等。不要偏离所提供的规范和文档,在规范中如果存在不清楚的地方,请在注释中标明你的疑问,不要编造规范或描述中没有的信息。

当你编写TypeScript代码时,提供简明而有意义的注释。

更具体地说,系统提示用于建立大模型(专家网页开发人员)的角色,给出一个总体目标(理解API规范并编写TypeScript代码),给予高层次的指令(在代码中添加注释),并设定界限(不要编造信息)。

数据模型解析

在此步骤中,我们提示大语言模型(LLM)理解API响应的结构,并为响应中的对象创建TypeScript类。虽然此步骤对于生成响应并非严格必要,但它可以大大帮助LLM生成正确的连接器。类似于思维链对于算术问题的推理,它迫使LLM在回应之前“思考”。

这一步提供了两个主要好处:

  • 详细的API响应简化文档中指定的API响应可能相当冗长。通过将响应结构转换为TypeScript类,我们可以将信息压缩到更少的代码行中,使其更加简洁且不易混淆,从而使大语言模型更容易理解。这一步骤有助于确保重要内容在一开始就突出显示。
  • 处理片段化的用户回复在某些 API 中,用户响应由多个片段组成,因为数据结构的重复使用。OpenAPI 规范使用了$ref标签以引用这些可重用组件。通过将用户响应转换为TypeScript类,我们可以将所有相关信息集中在一个位置。这种集中化简化了下游步骤,提供了信息的集中来源。

我们将使用以下任务提示将API响应转换为规定的TypeScript类:

"""
您将获得一个用于软件即服务(SaaS)应用程序的REST API端点的OpenAPI或Swagger API规范,该端点提供用户列表。您需要从API端点中提取JSON响应中的User结构。

在<api-spec> </api-spec> XML标签之间提供的以下是API规范。
请理解并记住此API规范。

<api-spec>
{api_spec}
</api-spec>

使用以下说明基于User的结构创建TypeScript接口:
<instructions>
- 在<data-model> </data-model> XML标签之间提供代码。
- 如果存在嵌套对象,请将其展开为自己的接口。
- 详尽无遗,包括所有属性。
- 获取所有属性,包括标记为非必需、不必要或可为空的属性。
- 属性列在`properties`部分下。
- 只输出一个包含从任何扩展接口继承的所有属性的User接口。
</instructions>

期望的输出格式如下:
<data-model> 
// User
interface User {{
  id: number;
  first_name: string;
  last_name: string;
  email: string;
  is_active: boolean;
  company_groups_ids: number[];
  other_attribute: string;
  nested_type: NestedType[];
}}

// Some complex type
interface NestedType {{
    type_id: string;
    some_string_attribute: string;
    some_number_attribute: number;
}}

</data-model>
"""

在前面的提示模板中,变量{API规范}被端点的API规范替换。DocuSign的具体示例为:列出用户附录中提供了端点。

以下代码是当LLM应用于DocuSign API规范时生成的类的一个示例。这是从……中解析出来的数据模型标签。

// 用户接口
interface User {
    id: string; // 格式:uuid
    user_name: string;
    first_name: string;
    last_name: string;
    user_status: string; // 取值之一:'active' | 'created' | 'closed'
    membership_status: string; // 取值之一:'activation_required' | 'activation_sent' | 'active' | 'closed' | 'disabled'
    email: string;
    created_on: string; // 格式:date-time
    membership_created_on: string; // 格式:date-time
    ds_groups: DsGroup[];
}
  
// 数据源组接口
interface DsGroup {
    ds_group_id: string; // 格式:uuid
    account_id: string; // 格式:uuid
    source_product_name: string;
    group_id: string;
    group_name: string;
    description: string;
    is_admin: boolean;
    last_modified_on: string; // 格式:date-time
    user_count: number; // 格式:int32
    external_account_id: number; // 格式:int64
    account_name: string;
    membership_id: string; // 格式:uuid
}

用户扁平化函数生成

每个用户的预期结构是一个包含两个属性的对象:标识符和属性字典。属性字典是一个将字符串键与原始属性(数字、布尔值或字符串)或原始属性数组关联的映射,因为响应中可能存在任意嵌套的JSON对象结构,我们利用大语言模型的能力来生成用户扁平化和转换函数。从响应中提取用户ID和属性。通过采用这种方法,我们将REST API响应中的用户结构转换为SailPoint连接器SDK(以下简称连接器SDK)所需的格式的任务有效地分离出来。

这种方法的好处是双重的。首先,它允许设计更干净、更模块化的代码,因为复杂的转换过程被抽象到了主代码库之外。其次,它能够提供更大的灵活性和适应性,因为转换函数可以修改或重新生成以适应API响应结构或连接器SDK需求的变化,而无需对周围的代码库进行广泛的修改。

我们使用以下提示生成转换函数,该函数以先前步骤中生成的数据模型作为输入。

理解并记住以下数据模型,它是以一组TypeScript类定义的形式提供的。
你将需要根据在<data-model> </data-model> XML标签之间提供的数据模型生成一个TypeScript函数。

<data-model>
{data_model}
</data-model>

请勿使用上述代码中定义的TypeScript接口,在编写代码时用类型`any`来代替,以确保在严格的TypeScript环境中能够运行代码。

根据上面给出的TypeScript类定义和数据模型,你的目标是写一个TypeScript函数,将用户转换为包含两个键的对象:一个是`identity`,另一个是`attributes`。 `attributes`是一个列表,除了标识符属性之外的所有属性都在这里列出。

以下是映射用户对象到`attributes`字段的规则:

<instructions-for-mapping>
- 函数接收一个用户并返回StdAccountListOutput。
- 提取在<data-model>标签中指定的用户模式中的所有属性
- 属性只能包含原始值或原始值数组。
- 从上面的数据模型中确定属性类型,不要根据属性名称猜测。例如,如果名称是复数形式,请勿假设它是数组。
- 所有的原始属性(如字符串和数字)将不经过转换直接复制。
- 所有原始类型的属性数组也将不经过转换直接复制。
- 对象被压平,并且它们的属性作为简单属性复制。
- 对象数组在转换过程中变为多个属性,每个属性都是原语类型数组。具体请参阅<object-array-instructions>标签之间的进一步说明。
- 在函数和箭头函数定义中使用`any`类型,以便与TypeScript兼容。
</instructions-for-mapping>

<object-array-instructions>
考虑以下响应结构,其中`obj_array`是一个类型为`MyObj`的对象数组属性。在压平过程中,对于响应来说,你需要将`obj_array`属性转换为多个属性如 `obj_array_attribute1`, `obj_array_attribute2`等,并且每个属性都是字符串类型的数组。

接口 User {{
  id: string; // 格式:uuid
  user_name: string;
  first_name: string;
  obj_array: MyObject[];
}}

接口 MyObj {{
  attribute1: string; // 格式:uuid
  attribute2: string; // 格式:uuid
  attribute3: string;
}}
</object-array-instructions>

根据下面的模板程序,在<user-mapping-function> XML标签之间输出代码。

此函数是更大程序的一部分,因此请保持函数中的StdAccountListOutput不变。
不要过度复杂化,编写简单易读的代码。

<user-mapping-function>
function flattenUser(user: any): StdAccountListOutput {{
  return {{
    identity: user.id,
    attributes: {{
      first_name: user.first_name,
      last_name: user.last_name,
      user_type: user.type,
      login: user.login,
      email: user.email,
      location: user.location,
      employee_id: user.employee_id,
      reviewer_id: user.reviewer_id,
      is_active: user.is_active,
      struct_array_attribute: user.struct_array.map( (val: any) => val.struct_attribute)
    }}
  }}
}};
</user-mapping-function>

在前面的提示模板中,我们用替换掉了{数据模型}包含从之前的解析数据模型步骤中提取的TypeScript类的数据模型的变量。

以下代码是当应用于DocuSign API时由大型语言模型生成的用户展平函数示例:

function flattenUser(user: any): StdAccountListOutput {
    const identity = user.id;
    const attributes: { [key: string]: any } = {};
  
    // 复制原始属性
    attributes.user_name = user.user_name;
    attributes.first_name = user.first_name;
    attributes.last_name = user.last_name;
    attributes.user_status = user.user_status;
    attributes.membership_status = user.membership_status;
    attributes.email = user.email;
    attributes.created_on = user.created_on;
    attributes.membership_created_on = user.membership_created_on;
  
    // 将对象数组转换为多个属性
    const dsGroupAttributes: { [key: string]: any[] } = {};
    user.ds_groups.forEach((dsGroup: any) => {
      dsGroupAttributes.ds_group_id = dsGroupAttributes.ds_group_id || [];
      dsGroupAttributes.ds_group_id.push(dsGroup.ds_group_id);
  
      dsGroupAttributes.account_id = dsGroupAttributes.account_id || [];
      dsGroupAttributes.account_id.push(dsGroup.account_id);
  
      dsGroupAttributes.source_product_name = dsGroupAttributes.source_product_name || [];
      dsGroupAttributes.source_product_name.push(dsGroup.source_product_name);
  
      dsGroupAttributes.group_id = dsGroupAttributes.group_id || [];
      dsGroupAttributes.group_id.push(dsGroup.group_id);
  
      dsGroupAttributes.group_name = dsGroupAttributes.group_name || [];
      dsGroupAttributes.group_name.push(dsGroup.group_name);
  
      dsGroupAttributes.description = dsGroupAttributes.description || [];
      dsGroupAttributes.description.push(dsGroup.description);
  
      dsGroupAttributes.is_admin = dsGroupAttributes.is_admin || [];
      dsGroupAttributes.is_admin.push(dsGroup.is_admin);
  
      dsGroupAttributes.last_modified_on = dsGroupAttributes.last_modified_on || [];
      dsGroupAttributes.last_modified_on.push(dsGroup.last_modified_on);
  
      dsGroupAttributes.user_count = dsGroupAttributes.user_count || [];
      dsGroupAttributes.user_count.push(dsGroup.user_count);
  
      dsGroupAttributes.external_account_id = dsGroupAttributes.external_account_id || [];
      dsGroupAttributes.external_account_id.push(dsGroup.external_account_id);
  
      dsGroupAttributes.account_name = dsGroupAttributes.account_name || [];
      dsGroupAttributes.account_name.push(dsGroup.account_name);
  
      dsGroupAttributes.membership_id = dsGroupAttributes.membership_id || [];
      dsGroupAttributes.membership_id.push(dsGroup.membership_id);
    });
  
    Object.assign(attributes, dsGroupAttributes);
  
    return {
      identity,
      attributes
    };
}

分页理解

如前所述,REST API 可以实现一种或多种分页方案。通常情况下,并没有明确提及分页的细节。在开发过程中我们发现,当存在多种分页方案时,LLM 会混淆不同分页方案中的元素,输出不连贯甚至包含错误的代码。由于遍历分页结果是一个关键步骤,在代码生成过程中我们会单独处理这一步骤,让 LLM 理解 API 实现的分页方案,并在高层次上制定其响应之后再输出代码。这样可以让 LLM 在制定响应时逐步思考。这个步骤会产生中间推理,然后将这些信息传递到下一步也是最后一步:生成 list users 函数代码。

我们使用以下提示来获取分页逻辑。因为我们使用的是亚马逊Bedrock上的Anthropic的Claude Sonnet,所以我们要求LLM以XML格式输出逻辑,这是已知的一种对该模型结构信息的有效方式。

理解并记住以下在<api-spec></api-spec> XML标签之间提供的OpenAPI规范。您将根据此规范回答问题,该规范描述了一个从SaaS应用程序获取用户列表的端点。

<api-spec>
{api_spec}
</api-spec>

除了规范之外,请使用在<api-info></api-info>XML标签之间的以下信息来了解规范中不可用的细节。

<api-info>
{api_info}
</api-info>

列出用户的API用于从API获得用户列表。此API可能实现一个或多个分页方案以检索用户列表。分页是逐页获取结果而不是一次性全部获取的方法。

您的目标是理解在API端点上实施的多种分页方案,并使用最简单的方案循环获取所有页面中的用户。

首先详细描述API是如何逐步实施分页的。包括如何获取第一页和随后的各页的细节。请将输出包含在<pagination-logic></pagination-logic> XML标签之间。如果API没有实现分页,则输出"No pagination implemented"(未实施分页)。

如果API实现了多个分页方案:
- 选择最容易的一种,并仅使用该种方案。
- 如果API响应中包含下一个URL(无论是响应体还是头信息中的),请优先考虑这种分页方案,而不是基于偏移量的更复杂的方案。

如果没有足够的或缺失的信息,请不要推测,在输出中标明这一点。

以以下格式输出结果:

<pagination-logic>
    <pagination-schemes>列出所有的分页方案<pagination-schemes>
    <selected-scheme>选择一种用于循环实现的方案</selected-scheme>
    <steps-first-page>获取第一页的步骤</steps-first-page>
    <steps-next>获取下一页的步骤</steps-next>
    <termination-logic>退出循环的条件</termination-logic>
    <information-complete>信息是否完整(是/否)</information-complete>
</pagination-logic>

在前面的提示模板中,变量{api_spec}被API规范替换。附录中提供了DocuSign API的一个示例,位于本文末尾。变量{api信息}可以使用自然语言编写的额外API文档来替换,而在DocuSign的例子中这被留为空字符串。

以下是从DocuSign API中解析出的关于分页逻辑提取的大型语言模型的回答:<pagination-逻辑>标签:

"""
<pagination-schemes>
1. 使用start和take查询参数的基于偏移量的分页。 
2. 使用start和end查询参数的基于索引的分页。
3. 使用响应中的next和previous链接进行基于链接的分页。
</pagination-schemes>

<selected-scheme>
使用响应中的next和previous链接进行基于链接的分页。
</selected-scheme>

<steps-first-page>
1. 对/v2/organizations/{organizationId}/users端点发起一个不带任何分页参数的GET请求。 
2. 响应将包含第一页面的结果以及指向下一页的next链接(如果有更多结果的话)。
</steps-first-page>

<steps-next>
1. 检查响应中是否包含next链接。 
2. 如果存在next链接,则对提供在该链接中的URL发起一个GET请求。 
3. 响应将包含下一个页面的结果以及指向后续页面的next链接(如果有更多结果的话)。
</steps-next>

<termination-logic>
继续请求下一页,直到响应不再包含next链接为止,这表示没有更多的页面了。
</termination-logic>

<information-complete>
yes
</information-complete>
"""

生成列表用户功能

这个链中的最后一步结合了前几步中提取的信息以及之前步骤生成的用户展平函数,以形成最终响应,即从提供的API获取用户列表的TypeScript函数。

我们使用以下提示生成完整的TypeScript函数:

"""
您的任务是编写一个TypeScript程序,从REST API获取用户列表。详细的任务说明如下:API通常一次返回一组用户,您需要通过分页来检索所有用户,直到没有更多结果为止。

请理解并记住以下在<api-spec></api-spec> XML标签之间的OpenAPI YAML规范,您将根据该规范编写TypeScript程序。

<api-spec>
{api_spec}
</api-spec>

除了规范外,还在<api-info></api-info> XML标签之间提供了有关API的文档和实现细节:

<api-info>
{api_info}
</api_info>

在<pagination-logic></pagination-logic> XML标签之间的以下分页逻辑提供了一般级别的详细信息,说明如何遍历分页结果以检索所有用户。根据下面提供的分页逻辑中的偏好选择分页。

<pagination-logic>
{pagination_logic}
</pagination-logic>

现在,请实现一个TypeScript函数来获取所有用户,并遵循以下指示
<instructions>
- 不要修改`flattenUser` 函数,而是按原样复制它。
- 只更改`listUsers` 函数的主体部分,但不要修改其签名,因为它属于大型程序的一部分,该程序期望此签名。
- 不创建或发明任何新的类型。
- 对于分页,请遵循提供的<api-spec> 和 <pagination-logic> 指令。
- 如果分页需要结果数量,则一次获取50个用户。
- 假设输入上下文中的`context.hostUrl` 将提供API主机名和端口,格式为“hostname:port_number”或“host_ip_address:port_number”。
- 如果API使用基于链接的分页,则通常无需通过使用 `hostUrl` 重新构造URL,因为在下一个URL或链接头中将提供完整的链接。
- 使用规范提供的端点 URL 替换`endpoint_url`。
- 假设任何所需的值(如apiKey)都将在 `context` 变量中提供。
- 更改授权头以匹配标头中的信息。
- 假设用于构建路径的必要参数将通过 `context` 变量提供。这些变量的名称将与API规范中的模板路径中指定的变量完全一致。
</instructions>

以下是示例输出:

<example-template>
import {{ Context, Response, StdAccountListHandler, StdAccountListOutput }} from '@sailpoint/connector-sdk';

{flatten_user_function}

const listUsers: StdAccountListHandler = async (context: Context, input: undefined, res: Response<StdAccountListOutput>) => {{
    let apiKey = context.apiKey;
    let hostUrl = context.hostUrl;
    let hasMore = true;

    let url = 'http://${{hostUrl}}/{{endpoint_path}}';
    while (hasMore) {{

        // 从端点获取响应
        let response = await fetch(url, {{
          headers: {{
            'Authorization': `${{apiKey}}`
          }}
        }});
        let results = await response.json();

        // 从返回的结果中获取用户
        let users = results.users;
        for (let user of users) {{
            flattened_user = flattenUser(user)
            res.send(flattened_user);
        }}

        // 如果有可用的“next”URL,则将其设置为url
        if (results.next) {{
          url = results.next;
        }} else {{
          hasMore = false;
        }}
    }}
}}
</example-template>

在<list-users-code></list-users-code> XML标签之间输出生成的代码,并且仅输出代码,不要添加任何其他格式化如三重引号。
"""

在这个提示中,我们替换{展平用户函数}原文:with the扁平化用户(或)展开用户那之前生成的{分页逻辑}与之前生成的那个一起。我们提供一个模板用于列出用户列表用于确保最终输出符合连接器函数要求的函数。生成的结果如下所示列出用户列表函数,该函数使用了扁平化用户(或)展开用户之前的函数:

const listAccounts: StdAccountListHandler = async (context: Context, input: undefined, res: Response<StdAccountListOutput>) => {
    let apiKey = context.apiKey;
    let hostUrl = context.hostUrl;
    let organizationId = context.organizationId;
    let hasMore = true;

    let url = `https://${hostUrl}/Management/v2/organizations/${organizationId}/users`;
    while (hasMore) {

        // 从端点获取响应
        let response = await fetch(url, {
            headers: {
                'Authorization': `Bearer ${apiKey}`
            }
        });
        let results = await response.json();

        // 获取返回结果中的用户信息
        let users = results.users;
        for (let user of users) {
            let flattened_user = flattenUser(user)
            res.send(flattened_user);
        }

        // 如果可用,将 URL 设置为响应中的 "next" URL
        if (results.paging.next) {
            url = results.paging.next;
        } else {
            hasMore = false;
        }
    }
}

经验教训

在这篇文章中,我们展示了大型语言模型如何通过运用各种核心提示原则和提示链技术来解决复杂的代码生成问题。虽然LLM在遵循明确定义的指令和生成小段代码方面表现出色,但这个用例涉及了大量的上下文信息,包括API规范和用户说明。从这项练习中得出的结论如下:

  • 分解复杂问题将一个复杂的代码生成问题分解为几个较低复杂度的中间步骤可以提升大语言模型的表现。提供一个单一的复杂提示可能导致大语言模型忽略一些指令。提示链式方法增强了生成的鲁棒性,更好地遵守指令。
  • 迭代优化此方法允许对中间步骤进行迭代优化。链条的每一部分都可以在独立调整后继续推进到下一步。大型语言模型可能对指令中的细微变化敏感,调整一个方面可能会无意中影响其他目标。提示链式提供了一种系统化的方法来独立优化每一步。
  • 处理复杂决策在理解分页的部分,我们展示了LLM如何通过各种选项进行推理并做出复杂决策后再生成代码。例如,在输入API规范支持多种分页方案的情况下,我们提示LLM先决定分页方法再实现代码。如果没有采用中间的推理步骤而直接生成代码,LLM往往会混合不同分页方案中的元素,导致输出不一致。通过首先在自然语言中强制做出决策,我们实现了更一致和准确的代码生成。

通过自动化代码生成,SailPoint能够将连接器开发时间从数小时或数天大幅减少到仅仅几分钟。这种方法还使得代码开发更加民主化,因此不需要深入了解TypeScript或者对SailPoint的连接器SDK有深入的了解。通过加速连接器的生成,SailPoint显著缩短了整个客户上线流程。这一优化的工作流不仅节省了宝贵的开发者时间,还能够更快地集成不同的系统,最终使客户能够更快速、更有效地使用SailPoint的身份安全解决方案。

结论

我们的AI驱动的生成连接器代码解决方案为与REST API集成开启了新的可能性。通过从API规范自动生成连接器,开发人员可以快速构建任何REST API的强大连接,节省开发时间并缩短新客户上手的时间。正如本文所示,这项技术可以显著简化处理各种API的过程,使团队能够专注于利用这些API提供的数据和功能,而不是被连接器代码细节困扰。考虑这样一个解决方案如何能增强您自己的API集成工作——它可能是更高效、更有效地使用当今互联数字环境中众多可用API的关键。


关于作者

埃里克·霍尔克尔Erik 是 SailPoint 公司 AI 产品的负责人,他致力于通过生成式人工智能和数据技术解决身份安全生态系统中的关键客户问题。在加入 SailPoint 之前,Erik 创立了一家机器人自动化初创公司,并随后加入 AWS,成为 Amazon One 的第一位产品团队成员。Erik 还在当地初创企业中担任导师,并在一个教育科技非营利组织中担任董事会成员和技术委员会负责人。

泰YPD注意,给出的字符串"Ty"没有明确的上下文或含义可以直接翻译成中文且保持原意。如果这里的"Ty"是指某个专有名词或者缩写,请提供更多信息以便准确翻译。如果没有实际内容需要翻译,则按照指示输出原文:Ty勒麦当nell(注意这个名字可能没有标准的中文译名,原文可能是人名"Ler McDonnell")泰勒是SailPoint人工智能工程的负责人,他领导开发AI解决方案以推动身份安全领域的创新和影响。在加入SailPoint之前,泰勒曾在几家早期到晚期的初创公司领导机器学习研究和工程团队,并在软件维护、信息检索和深度学习等领域发表过研究成果。他对使用AI技术为真正的人和问题带来积极影响的产品充满热情。

安维希·查鲁瓦卡是一位高级应用科学家,任职于生成式人工智能创新中心,他帮助客户通过实施解决方案来采用生成式人工智能,以应对他们关键的业务挑战。拥有机器学习博士学位和超过十年的经验,他在应用创新的机器学习和生成式人工智能技术解决复杂的现实世界问题方面有着专长。

奥德·热内瓦ysz<|im_start|> ------------------------------------------------------------------------ 若无具体内容需翻译,直接输出“奥德·热内瓦”即可。注意到名字通常不进行直译或解释,所以直接给出的是音译形式。如需要确切的中文表达,请告知具体要求或是提供更多信息以便准确处理。在此情况下,原文输出为: Aude Genevay是一位高级应用科学家,任职于生成式人工智能创新中心,她帮助客户解决关键的商业挑战,并利用生成式人工智能创造价值。她拥有理论机器学习博士学位,并且喜欢将前沿研究转化为实际解决方案。

穆菲朱尔·伊斯兰姆是亚马逊AWS生成式人工智能创新中心的应用科学家II,他帮助客户使用生成式AI、大型语言模型(LLM)、多代理学习和跨模态学习解决复杂的以客户为中心的研究挑战。他在弗吉尼亚大学获得机器学习博士学位,研究重点是跨模态机器学习、多语种自然语言处理和多任务学习。他的研究成果发表在NeurIPS、ICLR、AISTATS和AAAI等顶级会议上,以及IEEE和ACM期刊上。

亚辛· bonus(备注:人名有时没有直接对应的中文译名,Khatami可能需要根据具体语境或官方译名来确定)Khatami是一位高级应用科学家,任职于生成式人工智能创新中心。拥有超过十年的人工智能(AI)经验,他为AWS客户实施最先进的AI产品,以提高效率和为客户平台创造价值。他的专长包括生成式人工智能、大型语言模型(LLM)、多代理技术以及跨模态学习。

卡西克 拉姆Karthik 是位于俄亥俄州哥伦布市的亚马逊网络服务的主要解决方案架构师。他与独立软件供应商(ISV)合作,构建安全且创新的云解决方案,包括帮助他们使用数据驱动的方法解决产品和业务问题。Karthik 的专长领域是云安全,重点关注基础设施安全和威胁检测。


附录

本文中实验使用的API规格如下:

版权所有 (c) 2017- DocuSign, Inc. (https://www.docusign.com)
swagger: '2.0'
信息:
  标题: DocuSign API
  版本: v2.1
主机: api.docusign.net
basePath: "/Management"
方案:
- https
消费类型:
- application/json
生产类型:
- application/json
路径:
  "/v2/organizations/{organizationId}/users":
    get:
      标签:
      - Users
      摘要: 返回组织中用户的详细信息。
      描述: |-
        返回组织中的用户详细信息。

        必须在请求中至少包含以下查询参数之一:

        - `account_id`: 组织关联账户的ID。
        - `organization_reserved_domain_id`: 组织预留域的一个ID。
        - `email`: 要返回用户的电子邮件地址。
      操作标识符: OrganizationUser_OrganizationUsers_GetV2
      生产类型:
      - application/json
      参数:
      - 名称: organizationId
        在: path
        描述: 组织的GUID
        必需: true
        类型: string
        格式: uuid
      - 名称: start
        在: query
        描述: 响应中包含的第一个项索引。默认值为0。
        必需: false
        类型: integer
        格式: int32
      - 名称: take
        在: query
        描述: 响应中的条目数。默认值根据请求和资源而变化。
        必需: false
        类型: integer
      - 名称: email
        在: query
        描述: 用户的电子邮件地址。
        必需: false
        类型: string
      - 名称: membership_status
        在: query
        描述: 用户成员身份的状态。可选值包括:activation_required、activation_sent、active、closed和disabled。
        必需: false
        类型: string
      - 名称: account_id
        在: query
        描述: 组织账户的ID。如果提供此参数,则不使用`organizationId`或`organization_reserved_domain_id`参数,而是使用该组织的默认账户。
        必需: false
        类型: string
      - 名称: organization_reserved_domain_id
        在: query
        描述: 组织预留域的一个ID。如果提供此参数,则不使用`organizationId`或`account_id`参数,而是通过组织预留域名访问用户。
        必需: false
        类型: string
      - 名称: created_on_date_time_range
        在: query
        描述: 用户账户创建时间的日期范围。格式为:{createdOn} {开始日期} 至 {结束日期},例如:2016-05-31T08:49:37Z 2016-01-01T08:49:37Z。
        必需: false
        类型: string
      - 名称: membership_created_on_date_time_range
        在: query
        描述: 用户加入组织的时间范围。格式为:{createdOn} {开始日期} 至 {结束日期},例如:2016-05-31T08:49:37Z 2016-01-01T08:49:37Z。
        必需: false
        类型: string
      - 名称: last_name
        在: query
        描述: 用户的姓氏。
        必需: false
        类型: string
      - 名称: first_name
        在: query
        描述: 用户的名字。
        必需: false
        类型: string
      - 名称: group_ids
        在: query
        描述: 组织中组的ID列表,以逗号分隔。例如:group1Id,group2Id。
        必需: false
        类型: string
      - 名称: include_group_memberships
        在: query
        描述: 如果设置为`true`,则返回成员关系详细信息。默认值为`false`。
        必需: false
        类型: boolean
      响应:
        200:
          描述: 成功操作。
          内容类型: application/json
          模板: |
            {
              "users": [
                {
                  "user_id": "string",
                  "first_name": "string",
                  "last_name": "string",
                  "email": "string",
                  "created_on": "2016-05-31T08:49:37Z",
                  "membership_created_on": "2016-05-31T08:49:37Z",
                  "user_status": "string",
                  "user_type": "string",
                  "user_role": "string",
                  "is_admin": true,
                  "ds_groups": [
                    {
                      "ds_group_id": "string",
                      "account_id": "string",
                      "source_product_name": "string",
                      "group_id": "string",
                      "group_name": "string",
                      "description": "string",
                      "is_admin": false,
                      "last_modified_on": "2016-05-31T08:49:37Z",
                      "user_count": 0,
                      "external_account_id": 0
                    }
                  ]
                }
              ],
              "paging": {
                "result_set_size": 0,
                "result_set_start_position": 0,
                "result_set_end_position": 0,
                "total_set_size": 0,
                "next": "string",
                "previous": "string"
              },
              "description": "A response containing information about users."
            }
        其他: |
          - 错误代码和消息的详细描述,请参考官方文档。
          - API响应格式包括用户列表以及分页信息,以支持大数量级的数据处理。
关于《SailPoint如何使用Anthropic的Claude在Amazon Bedrock上自动生成用于SaaS连接器的TypeScript代码 | Amazon Web Services》
暂无评论

摘要

生成式AI正在通过在整个编码过程中提供智能辅助和自动化来改变开发人员的编程方式。Amazon Bedrock是一项完全托管的服务,它提供了来自AI21 Labs、Anthropic、Cohere、Meta、Mistral AI、Stability AI以及Amazon等领先AI公司的高性能基础模型(FMs)的选择,并通过单一API提供广泛的构建生成式AI应用程序的能力,包括安全性、隐私性和负责任的AI。函数定义——listUsers被定义为一个与StdAccountListHandler兼容的异步函数,它使用Context来访问配置详情如API密钥和基础URL,并使用Response来结构化输出。例如,在输入API规范支持多种分页方案时,我们提示LLM在实现代码前决定分页方法。 required: false type: string format: uuid - name: last_modified_since in: query description: 选择自指定日期以来数据被修改过的用户。