10分钟在网站上增加一个AI助手

在阿里云上只需 10 分钟即可为您的网站添加一个 AI 助手,以便全天候(7x24)回应客户咨询,提升用户体验、增强业务竞争力。

方案概览

2024-07-01_09-56-05

在网站中引入一个 AI 助手,只需 4 步:

  1. 创建大模型问答应用:我们将先通过百炼创建一个大模型应用,并获取调用大模型应用 API 的相关凭证。

  2. 搭建示例网站:然后我们将通过函数计算,来快速搭建一个网站,模拟您的企业官网或者其他站点。

  3. 引入 AI 助手:接着我们将通过修改几行代码,实现在网站中引入一个 AI 助手。

  4. 增加私有知识:最后可以通过准备一些私有知识,让 AI 助手能回答原本无法准确回答的问题,帮助您更好的应对客户咨询。

image.png

1. 创建大模型问答应用

首先我们可以通过创建一个百炼应用,来获取大模型的推理 API 服务,用于实现 AI 助手。

百炼提供的新用户免费额度可以完全覆盖本教程所需资源消耗。额度消耗完后按 token 计费,相比自行部署大模型可以显著降低初期投入成本。

1.1 创建应用

  1. 进入百炼控制台的我的应用在页面右侧点击新增应用。在对话框,选择智能体应用并创建。2024-09-22_11-25-00

  2. 应用设置页面,模型选择通义千问-Plus,其他参数保持默认。

    您也可以选择输入一些 Prompt,比如设置一些人设以引导大模型更好的应对客户咨询。
    你叫小助,可以帮助用户解答产品选购、使用等方面的问题。

    image

  3. 在页面右侧可以提问验证模型效果。不过您会发现,目前它还无法准确回答你们公司的商品信息。点击右上角的发布,我们将在后面的步骤中去解决这一问题。

    image

1.2 获取调用 API 所需的凭证

为了在后续通过 API 调用大模型应用的能力,我们需要获取一个百炼应用的 API-KEY 和应用 ID:

  1. 我的应用 > 应用列表中可以查看所有百炼应用 ID。保存应用 ID 到本地用于后续配置。2024-09-18_17-38-41

  2. 在顶部导航栏右侧,点击人型图标,点击API-KEY进入我的API-KEY页面。在页面右侧,点击创建我的API-KEY,在弹出窗口中创建一个新 API-KEY。保存 API-KEY 到本地用于后续配置。2024-09-18_17-23-56

2. 搭建示例网站

在让 AI 助手能准确回答问题之前,我们可以先尝试快速将 AI 助手集成到网站中。

您可以通过我们提前准备好的应用模板,快速搭建一个空白的示例网站,用于模拟您的企业官网或者其他站点。详细步骤如下:

函数计算提供的免费试用额度可以完全覆盖本教程所需资源消耗。额度消耗完后按量计费,对于本教程所涉及的Web服务,只在有访问的情况下会产生费用。

2.1 创建应用

请点击这里打开我们提供的函数计算应用模板,参考下图选择直接部署、并填写前面获取到的百炼应用 ID 以及 API-KEY。

然后其他表单项保持默认,点击页面左下角的创建并部署默认环境,等待项目部署完成即可(预计耗时 1 分钟)。

这里需要填写百炼应用 ID 和 API-KEY,是因为我们预置的应用模板中包含了通过百炼应用调用大模型的代码,以便您在后续快速完成体验。

image.png

2.2 访问网站

应用部署完成后,您可以在应用详情的环境信息中找到示例网站的访问域名,点击即可查看,确认示例网站已经部署成功。

image.png

本方案提供的网站为示例网站,网站本身细节内容仅展示样式,效果如下图。

image.png

3. 为网站增加 AI 助手

在网站中增加 AI 助手非常简单,您只需要在网站的 html 文件中插入几行代码。

3.1 增加 AI 助手相关代码

示例工程中包含了被注释的引入 AI 助手代码,您需要找到并解除注释。详细操作步骤如下:

  1. 回到应用详情页,在环境详情的最底部找到函数资源,点击函数名称,进入函数详情页image.png

  2. 进入函数详情页后,在代码视图中找到public/index.html文件,然后取消③所在位置的代码注释即可。2024-09-09_10-25-15

  3. 最后点击部署代码,等待部署完成即可。

3.2 验证网站上的 AI 助手

现在,您可以重新访问示例网站页面以查看最新效果。此时您会发现网站的右下角出现了 AI 助手图标image.png,点击即可唤起 AI 助手。

本方案提供的网站为示例网站,网站本身细节内容仅展示样式,效果如下图。

image.png

4. 为 AI 助手增加私有知识

通过前面的步骤,您已经拥有了一个可以和客户对话的 AI 助手。但是,如果想让 AI 助手像公司员工一样,更加精准且专业地回答与商品相关的问题,我们还需要为大模型应用配置知识库。

假设您在一家售卖智能手机的公司工作。您的网站上会有很多与智能手机相关的信息,如支持双卡双待、屏幕、电池容量、内存等信息。不同机型的详细配置清单参考:百炼系列手机产品介绍.docx

4.1 配置知识库

接下来,我们可以尝试让大模型在面对客户问题时参考这份文档,以产出一个更准确的回答和建议。

  1. 上传文件:在百炼控制台的数据管理中的非结构化数据页签中点击导入数据,根据引导上传我们虚构的百炼系列手机产品介绍:

    根据您上传的文档大小,百炼需要一定时间解析,通常占用1~6分钟,请您耐心等待。

    image

  2. 建立索引:进入知识索引,根据引导创建一个新的知识库,并选择刚才上传的文件,其他参数保持默认即可。知识库将为上一步骤中准备的文档建立索引,以便后续大模型回答时检索参考。

    image

    选择向量存储类型时,如果您希望集中存储、灵活管理多个应用的向量数据,可选择ADB-PG

    image

    image

  3. 引用知识:完成知识库的创建后,可以返回我的应用进入到刚才创建的应用设置界面,打开知识检索增强开关、选择目标知识库,测试验证符合预期后点击发布。Prompt 中会被自动添加一段信息,以便大模型在后续回答时参考检索出来的信息。

    image

4.2 检验效果

有了参考知识,AI 助手就能准确回答关于您公司的商品的问题了。

image

总结

通过前面的学习,您已经能搭建一个大模型 RAG 应用,并且将其以 AI 助手的形式添加到网站中来应对客户咨询,过程仅需 0 元(免费试用额度内) 10 分钟。

应用于生产环境

在正式的将 AI 助手引入到您的生产环境之前,建议您了解如下信息:

前端代码

前面创建的网站 AI 助手,是基于NLUX(一个用于开发大模型对话机器人的前端库)开发的示例,功能还比较简单。

如果您对于 AI 助手有更多定制化的需求,如希望调整样式、支持历史会话管理等,可以参考 NLUX 的文档进行定制开发。

本文档的 AI 助手组件前端源代码从 web-chatbot-ui 仓库获取。

参考:定制前端组件的样式和内容

<link rel="stylesheet" crossorigin href="https://g.alicdn.com/aliyun-documentation/web-chatbot-ui/0.0.24/index.css" />
<script type="module" crossorigin src="https://g.alicdn.com/aliyun-documentation/web-chatbot-ui/0.0.24/index.js"></script>
<script>
  window.CHATBOT_CONFIG = {
    endpoint: "/chat", // 可以替换为 https://{your-fc-http-trigger-domain}/chat
    displayByDefault: false, // 默认不展示 AI 助手聊天框
    aiChatOptions: { // aiChatOptions 中 options 会传递 aiChat 组件,自定义取值参考:https://docs.nlkit.com/nlux/reference/ui/ai-chat
      conversationOptions: { // 自定义取值参考:https://docs.nlkit.com/nlux/reference/ui/ai-chat#conversation-options
        conversationStarters: [
          {prompt: '哪款手机续航最长?'},
          {prompt: '你们有哪些手机型号?'},
          {prompt: '有折叠屏手机吗?'},
        ]
      },
      displayOptions: { // 自定义取值参考:https://docs.nlkit.com/nlux/reference/ui/ai-chat#display-options
        height: 600,
      },
      personaOptions: { // 自定义取值参考:https://docs.nlkit.com/nlux/reference/ui/ai-chat#chat-personas
        assistant: {
          name: '你好,我是你的 AI 助手',
          // AI 助手的图标
          avatar: 'https://img.alicdn.com/imgextra/i2/O1CN01Pda9nq1YDV0mnZ31H_!!6000000003025-54-tps-120-120.apng',
          tagline: '您可以尝试点击下方的快捷入口开启体验!',
        }
      }
    },
    dataProcessor: {
      /**
       * 在向后端大模型应用发起请求前改写 Prompt。
       * 比如可以用于总结网页场景,在发送前将网页内容包含在内,同时避免在前端显示这些内容。
       * @param {string} prompt - 用户输入的 Prompt
       * @param {string}  - 改写后的 Prompt
       */
      rewritePrompt(prompt) {
        return prompt;
      }
    }
  };
</script>
<style>
  :root {
    /* webchat 工具栏的颜色 */
    --webchat-toolbar-background-color: #1464E4;
    /* webchat 工具栏文字和按钮的颜色 */
    --webchat-toolbar-text-color: #FFF;
  }
  /* webchat 对话框如果被遮挡,可以尝试通过 z-index、bottom、right 等设置来调整位置 */
  .webchat-container {
    z-index: 100;
    bottom: 10px;
    right: 10px;
  }
  /* webchat 的唤起按钮如果被遮挡,可以尝试通过 z-index、bottom、right 等设置来调整位置 */
  .webchat-bubble-tip {
    z-index: 99;
    bottom: 20px;
    right: 20px;
  }
</style>

服务端代码

前面创建的示例网站代码中,包含了一个调用大模型获取答案的接口POST /chat,具体实现代码在文件index.js中。

函数计算应用在没有访问时不产生任何费用,您完全可以保留此函数应用,在后续用作调用大模型的转发服务。

如果您想要使用JavaPython SDK在自己的服务实现服务端,可以参考下面的代码:

package com.aliyun.webdemo.controller;

import com.alibaba.dashscope.app.Application;
import com.alibaba.dashscope.app.ApplicationParam;
import com.alibaba.dashscope.app.ApplicationResult;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.reactivex.Flowable;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * pom 配置依赖项
 * <dependency>
 *   <groupId>com.alibaba</groupId>
 *   <artifactId>dashscope-sdk-java</artifactId>
 *   <!-- 请将 'the-latest-version' 替换为查询到的最新版本号:https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java -->
 *   <version>the-latest-version</version>
 * </dependency>
 *
 * 将API Key配置到环境变量 https://help.aliyun.com/zh/model-studio/developer-reference/configure-api-key-through-environment-variables
 */
@RestController
@RequestMapping("/chatbot")
public class ChatbotController {

    /**
     * 实现 chat 接口,支持流式返回数据
     *
     * @param query
     * @return
     */
    @RequestMapping(value = "/chat", method = RequestMethod.POST)
    public ResponseBodyEmitter streamData(@RequestBody String query) {
        ResponseBodyEmitter emitter = new ResponseBodyEmitter(180000L);
        JsonObject jsonObject = new JsonParser().parse(query).getAsJsonObject();
        try {
            streamCall(emitter, jsonObject.get("prompt").getAsString());
        } catch (NoApiKeyException | InputRequiredException e) {
            e.printStackTrace();
        }

        return emitter;
    }

    /**
     * 调用百炼应用,封装流式返回数据
     * 返回数据格式
     * id:1
     * event:result
     * :HTTP_STATUS/200
     * data:{"output":{"session_id":"xxx","finish_reason":"null","text":"相关的问题"}}
     *
     * @param emitter
     * @param query
     * @throws NoApiKeyException
     * @throws InputRequiredException
     */
    public void streamCall(ResponseBodyEmitter emitter, String query) throws NoApiKeyException, InputRequiredException {
        // appId 填入百炼应用 ID
        ApplicationParam param = ApplicationParam.builder()
                .appId("xxx")
                .prompt(query)
                .incrementalOutput(true)
                .build();

        Application application = new Application();
        Flowable<ApplicationResult> result = application.streamCall(param);
        AtomicInteger counter = new AtomicInteger(0);
        result.blockingForEach(data -> {
            int newValue = counter.incrementAndGet();
            String resData = "id:" + newValue + "\nevent:result\n:HTTP_STATUS/200\ndata:" + new Gson().toJson(data) + "\n\n";
            emitter.send(resData.getBytes(java.nio.charset.StandardCharsets.UTF_8));

            if ("stop".equals(data.getOutput().getFinishReason())) {
                emitter.complete();
            }
        });

    }
}
from flask import Flask, request, render_template, Response
from http import HTTPStatus
from dashscope import Application
import json

app = Flask(__name__)


@app.route("/")
def home(path=""):
    """
    首页
    """
    return render_template("index.html")


def call_with_stream(query):
    """
    流式调用百炼 API
    需要将API Key配置到环境变量
    https://help.aliyun.com/zh/model-studio/developer-reference/configure-api-key-through-environment-variables
    返回数据格式
    id:1
    event:result
    :HTTP_STATUS/200
    data:{"output":{"session_id":"xxx","finish_reason":"null","text":"相关的问题"}}
    """
    # appId 填入百炼应用 ID
    responses = Application.call(
        app_id="xxx",
        prompt=query,
        stream=True,
        incremental_output=True,
    )
    i = 0
    for response in responses:
        if response.status_code != HTTPStatus.OK:
            print(
                "request_id=%s\n output=%s\n usage=%s\n"
                % (response.request_id, response.output, response.usage)
            )
        else:
            data = (
                "id:"
                + str(i)
                + "\nevent:result\n:HTTP_STATUS/200\ndata:"
                + json.stringfy(response)
                + "\n\n"
            )
            yield data


@app.route("/chat", methods=["POST"])
def create():
    raw_data = request.data
    data = json.loads(raw_data.decode("utf-8"))

    return Response(
        call_with_stream(data["prompt"]), mimetype="application/octet-stream"
    )


if __name__ == "__main__":
    app.run(debug=True, port=8080)
<?php  
// 开始新会话或恢复现有会话
session_start();

// 非请求 退出
if(!isset($_SERVER['REQUEST_METHOD'])){
    exit;
}

// 初始化首页
if ($_SERVER['REQUEST_METHOD'] === 'GET') {  
    home();
}  

// AI助手post请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {  
    chat();
}  

/**
 * 展示首页
 * 首页内容请参考上述前端代码
 * 注:测试的时候请将修改前端代码中endpoint为"/"
 */
function home() {
    // HTML文件路径
    $file = 'home.html';

    if (file_exists($file)) {  
        // 文件存在,则显示内容
        header('Content-Type: text/html');
        readfile($file);
    } else {  
        // 提示文件不存在
        echo "404 Not Found. The file does not exist.";  
    }  
}

/**
 * AI助手post请求
 */
function chat() {  
    /**
     * 设置请求的URL和百炼API-KEY,将<APP_ID>替换为百炼应用ID
     */
    $url = 'https://dashscope.aliyuncs.com/api/v1/apps/<APP_ID>/completion'; // 请求的URL
    $apiKey = 'sk-xxx';

    /**
     * 设置请求头信息
     */
    $headers = [ // 请求头数组
        'Authorization: Bearer ' . $apiKey, // 认证头部
        'Content-Type: application/json', // 内容类型
        'X-DashScope-SSE: enable' // SSE启用标志
    ];

    // 初始化提示词为空字符串
    $prompt = ''; 

    // 获取输入流的内容
    $json = file_get_contents('php://input'); 
    // 尝试将JSON转换为关联数组
    $data = json_decode($json, true);
     // 检查JSON解析是否成功
     if (json_last_error() === JSON_ERROR_NONE) {
         $prompt = $data['prompt'] ?? null; // 提取prompt字段值
     }
   
    /**
     * 构建请求体
     */
    $requestBody = [ // 请求体数组
        'input' => ['prompt' => $prompt], // 输入项
        'parameters' => ['incremental_output' => true], // 参数设置
        'debug' => new stdClass(), // 调试选项(空对象)
    ];

    // 流式调用百炼 API
    call_with_stream($url, $headers, $requestBody);
} 

// 流式调用百炼 API
function call_with_stream($url, $headers, $requestBody){
    /**
     * 初始化cURL会话并设置相关属性
     */
    $ch = curl_init();

    curl_setopt_array($ch, [ // 设置cURL选项
        CURLOPT_URL => $url, // 目标URL
        CURLOPT_POST => true, // 使用POST方法
        CURLOPT_POSTFIELDS => json_encode($requestBody), // POST数据
        CURLOPT_HTTPHEADER => $headers, // 请求头
        CURLOPT_WRITEFUNCTION => function ($curl, $data) { // 自定义写回调
            echo $data; // 输出数据
            ob_flush(); // 刷新输出缓冲
            flush(); // 刷新所有输出缓冲
            return strlen($data); // 返回数据长度
        },
        CURLOPT_BUFFERSIZE => 128, // 缓冲区大小
        CURLOPT_TIMEOUT => 300, // 超时时间
        CURLOPT_RETURNTRANSFER => true, // 是否返回传输数据
        CURLOPT_HEADER => false // 不包含HTTP头部
    ]);

    /**
     * 执行cURL会话并获取响应
     */
    $response = curl_exec($ch);

    /**
     * 错误处理及响应输出
     */
    if (curl_errno($ch)) { // 检查是否存在cURL错误
        http_response_code(500); // 设置HTTP响应状态码为500
        echo 'Curl error: ' . curl_error($ch); // 输出错误信息
    } else {
        echo $response; // 输出响应结果
    }

    curl_close($ch); // 关闭cURL会话
}
?>

函数计算应用部署时附带的 **.devsapp.net 域名会在下发后 30 天内回收,且不支持HTTPS访问,只适合于测试验证。如果您希望在您的网站上直接调用函数计算中部署的POST /chat接口,建议使用函数计算HTTP触发器中提供的域名,如:https://web-chat****.fcapp.run/chat。与此同时,建议您修改index.js中的 cors 配置,禁止其他站点对此接口的访问。

image

image

应用评测

建议在正式上线 AI 助手前,组织业务人员一起参与应用评测,确保大模型应用的回答效果符合预期。如果不符合预期,可以通过优化提示词、完善补充私有知识、调整文档切分策略等方法来改进回答效果。

持续改进

大模型课程

系统体验的改进优化永远没有终点,您可以考虑学习并通过阿里云大模型 ACA 认证,该认证配套的免费课程能帮助您进一步了解大模型的能力和应用场景,以及如何优化通过大模型的应用效果。