LLM问答测试内嵌及分享

OpenSearch-LLM智能问答版提供问答测试页面嵌入功能,支持将测试界面集成至自有Web应用并保留用户测试参数,同时可实现页面的免登录共享,便于团队协作测试。

基本原理

登录服务signin.aliyun.com支持通过在URL中拼接登录Token,当用户访问链接时,首先会访问登录服务,登录服务检测到有效的登录Token后,会重定向到目标页面,实现问答测试页面的免登录访问。整体的流程如下。

image
  1. 用户访问您的Web服务。

  2. Web服务端使用RAM用户的AK访问令牌服务(STS)获取安全令牌(STS Token)。

  3. 令牌服务(STS)返回安全令牌(STS Token)。

  4. Web服务端使用安全令牌访问RAM的登录服务SSO,获取登录Token。

  5. 登录服务返回登录Token。

  6. Web服务端拼接免登录链接,返回给客户端,客户端根据拼接的免登录链接访问OpenSearch-LLM智能问答版问答测试页面。

注意事项

在使用问答测试分享功能的过程中,请注意以下事项:

  • 该功能仅支持通过扮演RAM角色进行访问。

  • 由于令牌服务(STS)生成的登录Token为临时凭证,所以共享链接仅支持在浏览器中打开一次。重复打开该链接会提示SigninToken错误。

  • 由于后端存在校验逻辑,建议您在SigninToken失效期前5分钟重新生成问答测试分享链接以持续使用免登的页面。SigninToken的默认有效期为1小时,失效后将导致免登录页面上报SigninToken错误。

操作流程

准备工作

记录下OpenSearch-LLM智能问答版问答测试页面的Destination地址:

image

示例:

https://opensearch4service.console.aliyun.com/llm-chat?hideTopbar=true&hideSidebar=true&region=cn-shanghai&appName={appname}&options=%7B%22retrieve%22%3A%7B%22doc%22%3A%7B%22filter%22%3A%22%22%2C%22top_n%22%3A5%2C%22sf%22%3A%22%22%2C%22dense_weight%22%3A%220.7%22%2C%22formula%22%3A%22%22%2C%22operator%22%3A%22AND%22%7D%2C%22entry%22%3A%7B%22sf%22%3A%22%22%2C%22filter%22%3A%22%22%7D%2C%22image%22%3A%7B%22sf%22%3A%22%22%2C%22dense_weight%22%3A%220.7%22%7D%2C%22qp%22%3A%7B%22query_extend%22%3Afalse%2C%22query_extend_num%22%3A5%7D%2C%22return_hits%22%3Afalse%2C%22graph%22%3A%7B%22enable%22%3Atrue%7D%2C%22web_search%22%3A%7B%22enable%22%3Afalse%7D%2C%22rerank%22%3A%7B%22enable%22%3Atrue%2C%22model%22%3A%22ops-bge-reranker-larger%22%7D%7D%2C%22chat%22%3A%7B%22stream%22%3Atrue%2C%22prompt_config%22%3A%7B%22attitude%22%3A%22normal%22%2C%22rule%22%3A%22detailed%22%2C%22noanswer%22%3A%22sorry%22%2C%22language%22%3A%22Chinese%22%2C%22role%22%3Afalse%2C%22role_name%22%3A%22AI%E5%B0%8F%E5%8A%A9%E6%89%8B%22%2C%22out_format%22%3A%22text%22%7D%2C%22agent%22%3A%7B%22tools%22%3A%5B%5D%7D%2C%22csi_level%22%3A%22strict%22%2C%22history_max%22%3A%22%22%2C%22link%22%3A%22false%22%2C%22rich_text_strategy%22%3A%22%22%2C%22enable_llm_knowledge%22%3Atrue%7D%7D

创建RAM用户

  1. 创建RAM用户的操作步骤请参见创建RAM用户并授权。 创建时请勾选通过使用永久 AccessKey 访问选项,创建完成后记录下AccessKey。

    image

  2. 授予RAM用户AliyunSTSAssumeRoleAccess权限用于调用STS服务。

创建RAM角色

  1. 创建RAM角色的操作步骤请参见创建RAM角色并授权。编辑信任策略,授权RAM用户扮演角色。

    {
      "Statement": [
        {
          "Action": "sts:AssumeRole",
          "Effect": "Allow",
          "Principal": {
            "RAM": [
              "acs:ram::106******24:user/<创建的ram用户名称>"
            ]
          }
        }
      ],
      "Version": "1"
    }
  2. 授予RAM角色AliyunOpenSearchReadOnlyAccess权限用于调用STS服务。

  3. 记录下RAM角色的ARN,格式如acs:ram::106******24:role/role-name

获取免登录链接

制作OpenSearch-LLM智能问答版问答测试页面的免登录链接,完整示例请参考代码示例

  1. 通过使用RAM用户的AccessKey访问STS服务获取安全令牌(STS Token),可根据业务的实际需要指定令牌的有效期。获取STS Token的示例代码如下。

            //请填入提前准备的问答测试页面Destination地址
            String DestinationUrl = "";
            //请填入RAM角色的ARN
            String RoleArn = "";
            //请填入RAM角色会话名称
            String RoleSessionName = "";
    
            com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                    // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
                    .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
                    // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
                    .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
            // Endpoint 请参考 https://api.aliyun.com/product/Sts
            config.endpoint = "sts.cn-hangzhou.aliyuncs.com";
    
            com.aliyun.sts20150401.Client client = new com.aliyun.sts20150401.Client(config);
            com.aliyun.sts20150401.models.AssumeRoleRequest assumeRoleRequest = new com.aliyun.sts20150401.models.AssumeRoleRequest()
                    .setRoleArn(RoleArn)
                    .setRoleSessionName(RoleSessionName);
            com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
            com.aliyun.sts20150401.models.AssumeRoleResponseBody resp = client.assumeRoleWithOptions(assumeRoleRequest, runtime).getBody();
  2. 调用RAM单点登录SSO,获取登录Token。拼接链接的形式如下。注意:TicketType必须指定为mini

    http://signin.aliyun.com/federation?Action=GetSigninToken
                        &AccessKeyId=<STS返回的临时AK>
                        &AccessKeySecret=<STS返回的临时Secret>
                        &SecurityToken=<STS返回的安全Token>
                        &TicketType=mini

    返回的结构如下:

    {
        "RequestId": "******c5fd48789d23773af853e9f7_936be_1706585994094_1.229",
        "SigninToken": "svX6LGcBbWLExKD5hcwdLu6RsLQbv36fWZN36WhxkTXpTcDpmzs2K6X8uFvCqGsBTU4KWJMffYz2rAVbdJXHMECgUfyzS869wh2DBdFEQo3e2fJgZ5YtcMSVnoX7pterS2f7926jFvdBXVFEF54JkUCMrDAutNRv1u7ZReC7v8oQoG5UmjJBbHUyvLTn5UDDvDfNowMVyRskrZRFUKT2qA**********"
    }
  3. 将返回的登录Token拼接到准备好的链接中,生成免登录访问链接。

    http://signin.aliyun.com/federation?Action=Login
                                &LoginUrl=<登录失效跳转的地址,一般配置为自建Web配置302跳转的URL。需要使用encodeURLLoginUrl进行转码。>
                                &Destination=<实际访问的OpenSearch-LLM智能问答版问答测试页面。如果有参数,则需要使用encodeURL对参数进行转码。>
                                &SigninToken=<获取的登录Token,需要使用encodeURLToken进行转码。>

嵌入页面

iframe方式将链接嵌入到其他Web页面。

重要

此处测试即为首次在浏览器中打开免登录链接,测试完成后,登录Token会失效,您需要重新生成免登录链接。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>问答测试页面链接分享</title>
</head>
<body>
<iframe width="1280" height="720" src="免登录访问链接"> </iframe>
</body>
</html>

代码示例

您可以参考以下代码示例生成问答测试页面免登录链接。

添加依赖

Java代码的Maven依赖如下:

<dependency>
      <groupId>com.aliyun</groupId>
      <artifactId>sts20150401</artifactId>
      <version>1.1.6</version>
    </dependency>
    <dependency>
      <groupId>com.aliyun</groupId>
      <artifactId>tea-openapi</artifactId>
      <version>0.3.7</version>
    </dependency>
    <dependency>
      <groupId>com.aliyun</groupId>
      <artifactId>tea-console</artifactId>
      <version>0.0.1</version>
    </dependency>
    <dependency>
      <groupId>com.aliyun</groupId>
      <artifactId>tea-util</artifactId>
      <version>0.2.23</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.83</version>
    </dependency>
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5.13</version> 
    </dependency>

生成免登录链接

运行以下代码生成免登录链接:

package com.aliyun.sample;


import com.aliyun.tea.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;

import java.net.URLEncoder;

import static java.lang.System.exit;

public class Sample {

    /**
     * <b>description</b> :
     * <p>使用AK&amp;SK初始化账号Client</p>
     * @return Client
     * 
     * @throws Exception
     */
    public static com.aliyun.sts20150401.Client createClient() throws Exception {
        // 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。建议使用更安全的 STS 方式,以下代码示例仅供参考。
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
                .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
                // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
                .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
        // Endpoint 请参考 https://api.aliyun.com/product/Sts
        config.endpoint = "sts.cn-hangzhou.aliyuncs.com";
        return new com.aliyun.sts20150401.Client(config);
    }

    public static void main(String[] args_) throws Exception {
        //请填入提前准备的问答测试页面Destination地址
        String DestinationUrl = "";
        //登录失效跳转的地址,一般配置为自建Web配置302跳转的URL。
        String LoginUrl = "";
        //请填入RAM角色的ARN
        String RoleArn = "";
        //请填入RAM角色会话名称
        String RoleSessionName = "";

        java.util.List<String> args = java.util.Arrays.asList(args_);
        com.aliyun.sts20150401.Client client = Sample.createClient();
        com.aliyun.sts20150401.models.AssumeRoleRequest assumeRoleRequest = new com.aliyun.sts20150401.models.AssumeRoleRequest()
                .setRoleArn(RoleArn)
                .setRoleSessionName(RoleSessionName);
        com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
        try {
            com.aliyun.sts20150401.models.AssumeRoleResponseBody resp = client.assumeRoleWithOptions(assumeRoleRequest, runtime).getBody();

            //获取所需字段
            String accessKeyId = resp.getCredentials().getAccessKeyId();
            String accessKeySecret = resp.getCredentials().getAccessKeySecret();
            String securityToken = resp.getCredentials().getSecurityToken();

            //调用RAM单点登录SSO,获取登录Token。
            String signInTokenUrl = "http://signin.aliyun.com" + String.format(
                    "/federation?Action=GetSigninToken"
                            + "&AccessKeyId=%s"
                            + "&AccessKeySecret=%s"
                            + "&SecurityToken=%s&TicketType=mini",
                    URLEncoder.encode(accessKeyId, "utf-8"),
                    URLEncoder.encode(accessKeySecret, "utf-8"),
                    URLEncoder.encode(securityToken, "utf-8")
            );

            //System.out.println(signInTokenUrl);
            HttpGet signInGet = new HttpGet(signInTokenUrl);
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpResponse httpResponse = httpClient.execute(signInGet);
            String signInToken = "";
            if (httpResponse.getStatusLine().getStatusCode() == 200) {
                String signInRes = EntityUtils.toString(httpResponse.getEntity());
                //System.out.println(signInRes);
                signInToken = JSON.parseObject(signInRes).getString("SigninToken");

                if (signInToken == null) {
                    System.out.println("Invalid response message, contains no SigninToken: " + signInRes);
                    exit(-1);
                }
            } else {
                System.out.println("Failed to retrieve signInToken");
                exit(-1);
            }

            //将返回的登录Token拼接到准备好的链接中,生成免登录访问链接。
            String signInUrl = "http://signin.aliyun.com" + String.format(
                    "/federation?Action=Login"
                            + "&LoginUrl=%s"
                            + "&Destination=%s"
                            + "&SigninToken=%s",
                    URLEncoder.encode(LoginUrl, "utf-8"),
                    URLEncoder.encode(DestinationUrl, "utf-8"),
                    URLEncoder.encode(signInToken, "utf-8"));
            //最终生成的问答测试分享链接 
            System.out.println(signInUrl);

        } catch (TeaException error) {
            // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
            // 错误 message
            System.out.println(error.getMessage());
            // 诊断地址
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
        } catch (Exception _error) {
            TeaException error = new TeaException(_error.getMessage(), _error);
            // 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
            // 错误 message
            System.out.println(error.getMessage());
            // 诊断地址
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
        }
    }
}