精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

基于Agent的金融問答系統:前后端流程打通 原創

發布于 2024-11-25 10:16
瀏覽
0收藏

前言

在上一章??《【項目實戰】基于Agent的金融問答系統:Agent框架的構建》??中,我們已經完成了基于agent的問答系統主流程構建,本章將介紹如何將Agent通過langserve部署為后端服務,同時基于Vue.js+Vite構建前端頁面,完成問答系統前后端聯調。

目標

  • 將Agent部署為后端服務
  • 構建前端頁面,完成問答系統前后端聯調

后端實現

代碼實現

基于《大模型應用開發之Prompt初步了解》中langserve的所學內容,我們需要做的第一步為: 1、安裝langserve依賴包:

pip install "langserve[all]"

2、創建一個server.py文件,具體如下;

代碼文件:??app/server.py??

from fastapi importFastAPI,HTTPException
from fastapi.middleware.cors importCORSMiddleware
from finance_bot_ex importFinanceBotEx

finance_bot_ex =FinanceBotEx()

# 創建 FastAPI 應用
app =FastAPI(
    title="Qwen API",
    version="0.1",
    description="Qwen API",
)

# 添加 CORS 中間件
app.add_middleware(
CORSMiddleware,
    allow_origins=["*"],# 允許所有的來源
    allow_credentials=True,
    allow_methods=["*"],# 允許的HTTP方法
    allow_headers=["*"],# 允許的請求頭
)

@app.post("/queryex", response_model=dict)
asyncdefquery(query: dict):# 使用字典類型代替Query模型
try:
# 從字典中獲取input
        input_data = query.get("input")
        result = finance_bot_ex.handle_query(input_data)

# 返回字典格式的響應
return{"output": result}
exceptExceptionas e:
raiseHTTPException(status_code=500, detail=str(e))

# 運行Uvicorn服務器
if __name__ =="__main__":
import uvicorn

    uvicorn.run(app, host="0.0.0.0", port=8082)

代碼說明:

  • 首先,依賴包引入langserve的必要包:??from fastapi import FastAPI, HTTPException
    from fastapi.middleware.cors import CORSMiddleware?
    ?
  • 其次,將我們之前封裝好的FinanceBotEx類引入進來:??from finance_bot_ex import FinanceBotEx??
  • 然后,初始化FinanceBotEx類,并且創建FastAPI應用:??# 實例化FinanceBotEx類
    finance_bot_ex =FinanceBotEx()
    # 創建 FastAPI 應用
    app =FastAPI(
      title="Qwen API",
      version="0.1",
      description="Qwen API",
    )
    # 添加 CORS 中間件(允許跨域訪問)
    app.add_middleware(
    CORSMiddleware,
      allow_origins=["*"],# 允許所有的來源
      allow_credentials=True,
      allow_methods=["*"],# 允許的HTTP方法
      allow_headers=["*"],# 允許的請求頭
    )?
    ?
  • 最后,給定一個接口,接收用戶輸入,調用FinanceBotEx類的handle_query方法,返回結果: ```python @app.post("/queryex", response_model=dict) async def query(query: dict): # 使用字典類型代替Query模型 try: # 從字典中獲取input input_data = query.get("input") result = finance_bot_ex.handle_query(input_data) # 返回字典格式的響應 return {"output": result} except Exception as e: raise HTTPException(status_code=500, detail=str(e))

### 服務部署
目前,整個工程目錄結構如下:
```bash
smart-finance-bot \
    |- app \   # 該目錄用于服務端代碼
        |- conf
            |- .qwen
        |- utils \  
            |- util.py          # 實現連接大模型的方法
        |- rag \   
            |- pdf_processor.py
            |- chroma_conn.py
        |- test_framework.py      
        |- finance_bot_ex.py    # 封裝好的FinanceBotEx類
        |- server.py            # 新增的服務代碼

啟動后端服務方法

# 切換至項目根目錄
cd smart-finance-bot

# 啟動服務
python app/server.py

運行結果:

基于Agent的金融問答系統:前后端流程打通-AI.x社區

接口測試

服務正常啟動后,我們通過curl命令測試后端接口正常

curl -X POST http://localhost:8082/queryex \
-H "Content-Type: application/json" \
-d '{
  "config": {},
  "input": "在20190211,按照中信行業分類的標準,一級行業中的\"A股公司數量\"最多的是\"機械\"行業,共有549家公司。"
}'

運行結果:

基于Agent的金融問答系統:前后端流程打通-AI.x社區

后端接口可以接收請求,服務一切正常,接下來我們開始構建前端頁面,完成問答系統前后端聯調。

前端實現

前端頁面的實現方案有很多種,可以寫個簡單的HTML頁面,也可以使用Vue.js框架來實現一個前端。 因為Vue.js是目前主流的前端框架,它有豐富的生態和API說明,在github上也有非常多的開源項目供參考,所以本例中我們將使用Vue.js框架來實現前端頁面。

背景知識

Vue.js簡介 Vue.js 是一個用于構建用戶界面的漸進式 JavaScript 框架。它的核心庫專注于視圖層,易于上手,并與其他庫或現有項目進行整合。

Vue.js的特點

  • 響應式數據綁定:Vue.js 提供了雙向數據綁定,能夠實時更新視圖與數據之間的變化。
  • 指令:Vue.js 提供了一些內置指令(如 v-bind、v-model、v-for 等),使得數據綁定和 DOM 操作變得簡單直觀。
  • 生態系統:Vue.js 擁有豐富的生態系統,包括路由管理(Vue Router)、狀態管理(Vuex)等工具,支持構建復雜的應用。

Vue.js的簡單示例

<div id="app">
  <h1>{{ message }}</h1>
  <input v-model="message" placeholder="編輯我">
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script>
  newVue({
el:'#app',
data:{
message:'Hello Vue!'
}
});
</script>

說明:

  • 在上面的示例中,{{ message }} 用于顯示數據,v-model 用于實現雙向數據綁定,允許用戶通過輸入框修改 message 的內容。
  • 更為詳細的Vue.js使用方法,請查看官方文檔地址:https://cn.vuejs.org/

代碼實現

在本例中,我們參考Github上的一個開源項目進行二次開發,該項目git clone之后目錄如下:

chatweb \
    |- index.html               # 前端入口文件
|- node_modules             # 依賴包目錄
|- package-lock.json        # 鎖定依賴版本的文件
|- package.json             # 項目描述和依賴配置文件
|- postcss.config.js        # PostCSS 配置文件
|- public                   # 公共資源目錄
|- src                      # 源代碼目錄
|-App.vue              # 主應用組件
|- assets               # 資源文件目錄
|- components           # 組件目錄
|-ChatComponents.vue # 聊天組件
|-Home.vue        # 首頁組件
|-Login.vue       # 登錄組件
|-NotFound.vue    # 404 頁面組件
|- main.js              # 應用入口文件
|- routes               # 路由配置目錄
|- tailwind.config.js       # Tailwind CSS 配置文件
|- tsconfig.json            # TypeScript 配置文件
|- vite.config.js           # Vite 配置文件

該項目是仿chatGPT的問答系統,倉庫地址為:SinMu-L/chatweb

修改index.html

編輯index.html,修改如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="./src/assets/icon.jpg">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>金融千問機器人</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

修改路由配置文件

編輯src/routes/routers.js,修改如下:

import ContainerTemplatefrom'../components/ChatComponents.vue'
importNotFoundfrom'../components/NotFound.vue'
importHomefrom'../components/Home.vue'

var routes =[
{path:"/",name:'root',component:Home},
{path:"/chat/:uuid?",component:ContainerTemplate,name:'chat'},
{path:'/404',name:'not_found',component:NotFound}// 捕獲所有未匹配的路徑
];
exportdefault routes;

說明:

  • 配置文件用于控制頁面請求的路由信息
  • 當用戶訪問的路徑為/時,將顯示Home組件;
  • 用戶訪問的路徑為/chat時,將顯示ChatComponents組件;
  • 當用戶訪問的路徑為其他路徑,將顯示404頁面。

由上也可以看到,后續修改的對話頁面在 ??ChatComponents.vue?? 中。

修改聊天組件頁面

由于原始的 ??ChatComponents.vue?? 代碼不夠規范,顯示布局、顯示樣式以及邏輯代碼沒有分離,所以我們對其進行重構,重構內容分為三部分:

重構頁面的顯示布局

<template>
<div>
<div v-if="centerLodding" class="loading-container">
<n-spin size="large" />
</div>
<Login class="border border-red-400" />
<div class="flex flex-row h-min" :class="hasLogin('main')">
<!-- 非移動端模式下側邊欄的樣式 -->
<div class="sidebar" :class="controlSidebarHidden ? 'w-0' : ''">
<div class="hidden sm:flex sm:flex-col sm:h-screen sm:border">
<!-- 新建按鈕 -->
<div class="basis-1/12 flex justify-center items-center">
<n-button class="w-4/5" @click="addLeftListEle">新的會話</n-button>
</div>
<!-- 列表 -->
<div class="basis-10/12 overflow-auto border">
<div v-for="item in left_data.left_list" :key="item.uuid">
<router-link :to="`/chat/${item.uuid}`" class="sidebar-item">
<div class="w-4/5 flex items-center">
<n-icon size="medium">
<game-controller-outline />
</n-icon>
<div class="truncate mx-2" style="color: #000;">
<p v-if="!item.enable_edit" class="truncate h-full">{{ item.title }}</p>
<n-input v-else type="text" size="small" class="h-full" v-model:value="item.title" @keyup.enter="submit(item.uuid)" />
</div>
</div>
<div class="w-1/5 flex justify-center items-center" :class="route.params.uuid != item.uuid ? 'hidden' : ''">
<n-button-group size="small">
<n-button text @click="editLeftListEle(item.uuid)">
<n-icon><Edit /></n-icon>
</n-button>
<n-button text @click="delLeftListEle(item.uuid)">
<n-icon><Delete /></n-icon>
</n-button>
</n-button-group>
</div>
</router-link>
</div>
</div>
<!-- 設置頁面 -->
<div class="footer flex flex-col items-center justify-between p-4 h-15">
<div class="user-info font-bold text-lg">金融千問機器人</div>
<div class="logo-container relative"><!-- 添加一個容器 -->
<img src="../assets/background.png" alt="Logo" class="logo" />
</div>
<div class="robot-description text-sm text-gray-600 mt-2">
              金融千問機器人,通過RAG對既有的PDF招股書建立了知識庫,同時借助大模型+Agent對金融SQL數據庫進行動態查詢,旨在為用戶提供快速、準確的金融信息和咨詢服務。
</div>
<div class="settings-icon">
<n-icon @click="showSettingFunc()">
<SettingsOutline />
</n-icon>
</div>
</div>
</div>
</div>

<!-- 移動端模式下側邊欄的樣式 -->
<div class="sm:hidden absolute top-1 left-1 h-full w-full flex flex-col">
<div>
<n-button text style="font-size:32px" @click="controlSidebarHidden = !controlSidebarHidden">
<n-icon class="text-black">
<Menu />
</n-icon>
</n-button>
</div>
<div v-if="!controlSidebarHidden" class="mobile-sidebar">
<div class="w-full flex flex-col h-screen border">
<div class="basis-1/12 flex justify-center items-center">
<n-button class="w-4/5" @click="addLeftListEle">新的對話</n-button>
</div>
<div class="basis-10/12 overflow-auto border">
<div v-for="item in left_data.left_list" :key="item.uuid">
<router-link :to="`/chat/${item.uuid}`" class="sidebar-item">
<div class="w-4/5 flex items-center">
<n-icon size="medium">
<game-controller-outline />
</n-icon>
<div class="truncate mx-2">
<p v-if="!item.enable_edit" class="truncate h-full">{{ item.title }}</p>
<n-input v-else type="text" size="small" class="h-full" v-model:value="item.title" @keyup.enter="submit(item.uuid)" />
</div>
</div>
<div class="w-1/5 flex justify-center items-center" :class="route.params.uuid != item.uuid ? 'hidden' : ''">
<n-button-group size="small">
<n-button text @click="editLeftListEle(item.uuid)">
<n-icon><Edit /></n-icon>
</n-button>
<n-button text @click="delLeftListEle(item.uuid)">
<n-icon><Delete /></n-icon>
</n-button>
</n-button-group>
</div>
</router-link>
</div>
</div>
<div class="footer flex items-center justify-between p-4 h-15">
<div class="user-info font-bold">金融千問機器人</div>
<div class="settings-icon">
<n-icon @click="showSettingFunc()">
<SettingsOutline />
</n-icon>
</div>
</div>
</div>
</div>
</div>

<div class="w-full sm:w-4/5 h-full">
<div class="flex flex-col h-screen">
<div v-if="left_list_is_empty" class="empty-chat-message">
            Hi
</div>
<div v-else class="chat-area" id="msgArea">
<div v-for="(msglist, index) in getMsgList(route.params.uuid)" :key="index" class="flex flex-col mt-1 msgItem">
<div :class="msglist.reversion ? 'flex-row-reverse' : 'flex-row'" class="flex justify-start items-center h-10">

<img
                    class="rounded-full avatar"
                    :src="msglist.reversion ? userAvatar : robotAvatar"
                    alt="頭像"
                />
<span class="ml-4 text-sm">{{ msglist.create_time }}</span>
</div>
<div class="flex" :class="msglist.reversion ? 'flex-row-reverse' : 'flex-row'">
<div class="message-container">
<n-spin v-if="msglist.msgload" size="small" stroke="red" />
<Markdown v-else :source="msglist.content"></Markdown>
</div>
</div>
</div>
</div>
<div v-if="!left_list_is_empty" class="input-area">
<div class="p-2">
<n-input-group>
<n-tooltip trigger="hover">
<template #trigger>
<n-button text size="large" class="px-2" @click="deleteChatItemHistory(route.params.uuid)">
<n-icon class="text-black">
<Delete />
</n-icon>
</n-button>
</template>
                  刪除當前會話記錄
</n-tooltip>
<n-tooltip trigger="hover">
<template #trigger>
<n-button text size="large" class="pr-4" @click="dom2img()">
<n-icon class="text-black">
<Download />
</n-icon>
</n-button>
</template>
                  下載當前會話為圖片
</n-tooltip>
<a href="" id="link" class="hidden"></a>
<n-input show-count @keyup.ctrl.enter="addMessageListItem(route.params.uuid)" placeholder="Ctrl+Enter 發送消息,發送消息長度需要大于2個字" v-model:value="input_area_value" type="textarea" size="tiny" :autosize="{ minRows: 2, maxRows: 5 }" />
<n-button ghost class="h-auto" @click="addMessageListItem(route.params.uuid)">
                  發送
</n-button>
</n-input-group>
</div>
</div>
</div>
</div>
</div>

<!-- 設置模態框 -->
<n-modal v-model:show="showSetting" style="width: 600px" class="custom-card" preset="card" title="設置">
<n-tabs type="line" animated>
<n-tab-pane name="about" tab="關于">
<div>這是一個demo項目,僅用于學習。</div>
<div class="my-4">技術棧:Vue3 + Vite + tailwindCss3 + NaiveUi</div>
</n-tab-pane>
<n-tab-pane name="settings" tab="設置">
<div class="grid grid-rows-3 gap-4">
<div>
<span class="mr-4">Server URL: </span>
<n-input v-model:value="setting.server_url" placeholder="請輸入服務器 URL" />
</div>
<div>
<span class="mr-4">type: </span>
<n-select :style="{ width: '80%' }" :options="selectOptions" v-model:value="setting.type" />
</div>
<div class="flex justify-end">
<n-button
                  @click="confirmSettings"
                  class="custom-button">
                確認
</n-button>
</div>
</div>
</n-tab-pane>
<n-tab-pane name="other" tab="其他">
          其他
</n-tab-pane>
</n-tabs>
</n-modal>
</div>
</template>

重構頁面的顯示樣式

<style scoped>
.router-link-active{
border-color:#18a058;
color:#18a058;
}

.loading-container{
position: absolute;
top:33%;
left:33%;
background-color: gray;
width:33%;
height:33%;
display: flex;
justify-content: center;
align-items: center;
}

.sidebar{
width:20%;
height:100%;
}

.sidebar-item{
margin:0.5rem;
display: flex;
justify-content: space-between;
align-items: center;
border:1px solid gray;
border-radius:0.375rem;/* rounded-md */
padding:0.5rem;
color: white;
}

.settings-container{
display: flex;
justify-content: flex-start;
align-items: center;
height:50%;
}

.user-info{
width:50%;
height:100%;
display: grid;
grid-template-rows:repeat(2,1fr);
}

.settings-icon{
width:25%;
height:100%;
display: flex;
justify-content: center;
align-items: center;
}

.mobile-sidebar{
width:80%;
background-color: white;
  dark:bg-black;
}

.empty-chat-message{
flex-basis:91%;
width:100%;
padding:3rem;
overflow:auto;
display: flex;
justify-content: center;
align-items: flex-start;
color: gray;
font-style: italic;
}

.chat-area{
flex-basis:91%;
width:100%;
padding:3rem;
overflow:auto;
}

.message-container{
background-color:#bfdbfe; /* bg-blue-200 */
color: black;/* dark:text-black */
width:auto;
max-width:80%;
min-width:1%;
break-words:break-word;
overflow: hidden;
border-radius:0.375rem;/* rounded-sm */
padding:0.5rem;
margin:0.25rem0;
}

.input-area{
flex-basis:8%;
width:100%;
}

.avatar{
width:50px;
height:50px;
}

.footer{
text-align: center;/* 居中顯示 */
}

.robot-description{
margin-top:8px;/* 描述與 Logo 之間的間距 */
text-align: left;/* 左對齊 */
width:100%;/* 使描述占滿可用寬度 */
padding-left:16px;/* 可選:增加左側內邊距 */
}

.logo-container{
position: relative;/* 設置容器為相對定位 */
}

.background-logo{
position: absolute;/* 設置背景圖為絕對定位 */
top:0;/* 頂部對齊 */
left:0;/* 左側對齊 */
width:240px;/* 設置背景圖寬度為240px */
height:auto;/* 保持比例 */
z-index:1;/* 確保背景圖在logo下方 */
}

.logo{
position: relative;/* 設置logo為相對定位 */
z-index:2;/* 確保logo在背景圖上方 */
width:200px;/* 根據需要調整 Logo 大小 */
height:auto;/* 保持比例 */
top:30%;/* 垂直居中 */
left:50%;/* 水平居中 */
transform:translate(-50%,-50%);/* 使 logo 在背景圖中心 */
}

.custom-button{
background-color:#007bff; /* 按鈕背景色 */
color: white;/* 字體顏色 */
border: none;/* 去掉邊框 */
padding:10px20px;/* 內邊距 */
border-radius:5px;/* 圓角 */
cursor: pointer;/* 鼠標樣式 */
transition: background-color 0.3s;/* 添加過渡效果 */
}

.custom-button:hover{
background-color:#0056b3; /* 懸停時的背景顏色 */
}

</style>

重構頁面的JS代碼

<script setup>
import{
NButton,NInput,NIcon,NButtonGroup,NSpin,
NInputGroup,NCard,NModal,NTabs,NTabPane,NInputNumber,NSelect,
NTooltip,
    useMessage
}from'naive-ui'
import{GameControllerOutline,GameController}from'@vicons/ionicons5'
import{LogInOutlineasLogInIcon,SettingsOutline,Menu}from'@vicons/ionicons5'
import userAvatar from'@/assets/user-avatar.png';
import robotAvatar from'@/assets/robot-avatar.png';

import{Edit,Delete,Download}from'@vicons/carbon'
importMarkdownfrom'vue3-markdown-it';

importLoginfrom'./Login.vue'

import{ reactive, ref, getCurrentInstance, watch, watchEffect, nextTick }from'vue';
import{ useRouter, useRoute }from'vue-router'
import html2canvas from"html2canvas";


const router =useRouter()
const route =useRoute()
const instaceV =getCurrentInstance()
const message =useMessage()

import{ onMounted }from'vue';

// 在組件的 setup 函數中添加
onMounted(() =>{
// 檢查是否有會話記錄
if(left_data.chat.length>0){
// 獲取最后一個會話的 UUID
const lastChatUuid = left_data.chat[left_data.chat.length-1].uuid;
// 路由跳轉到最后一個會話
    router.push({name:'chat',params:{uuid: lastChatUuid }});
}else{
// 如果沒有會話,設置左側列表為空
    left_list_is_empty.value=true;
}
});

// 控制側邊欄顯示隱藏
var controlSidebarHidden =ref(true)
// 移動端下側邊欄顯影

const showSetting =ref(false)

varLLM_APIKEY=ref("sk-cwtdeSy4Ownmy6Uh5e9b6a67Fe4c4454A3Dc524876348eB1")

var setting =reactive({
server_url:'http://localhost:8082/query',// 默認值
type:'query',
})

let left_list_is_empty =ref(false)

var is404 =ref(false)

var segmented ={
content:'soft',
footer:'soft'
}

var selectOptions =ref([
{
label:'Query式訪問',
value:'query'
},
{
label:'流式訪問',
value:'stream_log'
}
])

var centerLodding =ref(false)

var input_area_value =ref('')
var left_data =reactive({
left_list:[
// { uuid: 1, title: 'New Chat1', enable_edit: false },
// { uuid: 2, title: 'New Chat2', enable_edit: false },
],
chat:[
// {
//     uuid: 1, msg_list: [
//         { content: 'hello1', create_time: '2023-11-09 11:50:23', reversion: false, msgload: false },
//     ]
// },
// {
//     uuid: 2, msg_list: [
//         { content: 'xxx', create_time: '2023-11-09 11:50:23', reversion: false, msgload: false },
//     ]
// },
],

})

// 監聽響應式數據
watch(left_data,(newValue, oldValue) =>{
if(newValue.chat.length>0){
        left_list_is_empty.value=false
}else{
        left_list_is_empty.value=true
}
localStorage.setItem('chatweb',JSON.stringify(newValue))
})

// 創建響應式變量后只執行一次輸出的需求
watchEffect(() =>{
// 讀取 localstorage
const data =localStorage.getItem('chatweb')
if(data){

const history =JSON.parse(data)
        left_data.left_list= history.left_list
        left_data.chat= history.chat
}else{
        left_list_is_empty.value=true
}
})



// 添加側壁欄item
functionaddLeftListEle(){
const uuid =randomUuid()
    left_data.left_list.push({
uuid: uuid,
title:`新的對話${uuid}`,
enable_edit:false
})
    left_data.chat.push({
uuid: uuid,
msg_list:[]
})

// 路由跳轉到最新的item
    router.push({name:'chat',params:{uuid: uuid }})

}
// 點擊側邊欄某個item的編輯按鈕
functioneditLeftListEle(uuid){
const index = left_data.left_list.findIndex(v => v.uuid== uuid)
    left_data.left_list[index].enable_edit=!left_data.left_list[index].enable_edit

// all_data.left_list[index].enable_edit = !all_data.left_list[index].enable_edit
// ls.updateLeftListItemEnableEditButton(uuid)

}

// 點擊側邊欄某個item的刪除按鈕
asyncfunctiondelLeftListEle(uuid){


var index = left_data.left_list.findIndex(v => v.uuid== uuid)
    left_data.left_list.splice(index,1)

const chat_index = left_data.chat.findIndex(v => v.uuid== uuid)
    left_data.chat.splice(chat_index,1)

if(left_data.chat.length==0){
        left_list_is_empty =true
}else{
awaitnextTick()
// 默認跳轉到最新的 chat
const last_chat_uuid = left_data.chat[left_data.chat.length-1].uuid

        router.push({name:'chat',params:{"uuid": last_chat_uuid }})
}
}

// 獲取每個 chat 的msg list
functiongetMsgList(uuid){

if(uuid && uuid !=undefined){
var index = left_data.chat.findIndex(v => v.uuid== uuid)
if(index ==-1){
return[]
}
return left_data.chat[index].msg_list
}
return[]

}

functionrandomUuid(){
var len =9
var uuid ='';
for(let i =0; i < len; i++){
        uuid +=Math.floor(Math.random()*10)
}
returnNumber(uuid,10)
}

// 監聽側邊欄item的回車事件
functionsubmit(index){
editLeftListEle(index)
}

// 發送消息
functionaddMessageListItem(uuid){
if(input_area_value.value.length<=2){
        message.info('內容長度不得小于2')
returnfalse
}
var index = left_data.chat.findIndex(v => v.uuid== uuid)
constnow_t=(newDate()).toLocaleString('sv-SE',{"timeZone":"PRC"})
    left_data.chat[index].msg_list.push({
content: input_area_value.value,
create_time:now_t,
reversion:true,
msgload:false
})

const body ={
config:{},
input: input_area_value.value// 按要求的格式拼接

};

// 清空編輯框
    input_area_value.value=''
var ele =document.getElementById("msgArea")
    ele.scrollTop= ele.scrollHeight+ ele.offsetHeight

// 添加一個占位消息,表示正在加載
    left_data.chat[index].msg_list.push({
content:'',
create_time:now_t,
reversion:false,
msgload:true
})
console.info("開始發送消息...")


// 使用編輯框中的 URL 進行請求
const url = setting.server_url;// 獲取 URL

if(setting.type==='stream_log'){
startStream(index, body, url);// 傳遞 URL
}else{
startRequest(index, body, url);// 傳遞 URL
}
}

functionbuildMessagePromt(index){
const res =[]
    left_data.chat[index].msg_list.forEach(v =>{
let role = v.reversion?'user':'assistant'
        res.push({
role: role,
content: v.content
})
})
    res.pop()
return res
}

asyncfunctionstartRequest(index, body, url){
const key =LLM_APIKEY.value;
let response =null;

try{
    response =awaitfetch(url,{
method:"POST",
headers:{
"Content-Type":"application/json",
"Accept":"application/json",// 使用 JSON 作為接受格式
"Authorization":`Bearer ${key}`,// 如果需要 API 密鑰
},
mode:"cors",
body:JSON.stringify(body),
});

    left_data.chat[index].msg_list[left_data.chat[index].msg_list.length-1].msgload=false;
}catch(error){
    left_data.chat[index].msg_list[left_data.chat[index].msg_list.length-1].content+=`發生了一些錯誤:${error.message}`;
returnfalse;
}

if(response.status!==200){
    left_data.chat[index].msg_list[left_data.chat[index].msg_list.length-1].content+=`發生了一些錯誤:${response.status}-${response.statusText}`;
returnfalse;
}

try{
const data =await response.json();
if(data && data.output){// 檢查數據是否存在并包含 output
const outputText = data.output;
const currentContent = left_data.chat[index].msg_list[left_data.chat[index].msg_list.length-1].content;
if(!currentContent.includes(outputText)){
        left_data.chat[index].msg_list[left_data.chat[index].msg_list.length-1].content+= outputText;
}
}else{
thrownewError('返回的數據結構不符合預期');
}
}catch(e){
console.error('Error parsing JSON:', e);
    left_data.chat[index].msg_list[left_data.chat[index].msg_list.length-1].content+='解析響應時發生錯誤';
}

returntrue;// 返回成功狀態
}


asyncfunctionstartStream(index, body, url){
let response =null;

try{
    response =awaitfetch(url,{
method:"POST",
headers:{
"Content-Type":"application/json",
"Accept":"text/event-stream",
"Accept-Encoding":"gzip, deflate, br, zstd",
"Accept-Language":"zh-CN,zh;q=0.9"
},
mode:"cors",
body:JSON.stringify(body),
});

    left_data.chat[index].msg_list[left_data.chat[index].msg_list.length-1].msgload=false;
}catch(error){
    left_data.chat[index].msg_list[left_data.chat[index].msg_list.length-1].content+=`發生了一些錯誤:${error.message}`;
returnfalse;
}

if(response.status!==200){
    left_data.chat[index].msg_list[left_data.chat[index].msg_list.length-1].content+=`發生了一些錯誤:${response.status}-${response.statusText}`;
returnfalse;
}

const reader = response.body.getReader();
const decoder =newTextDecoder('utf-8');
let buffer ='';

constreadStream=async()=>{
const{ done, value }=await reader.read();

if(done){
console.log('Stream reading complete');
return;
}

    buffer += decoder.decode(value,{stream:true});

let completeData ='';
let separatorIndex;
while((separatorIndex = buffer.indexOf('\n'))!==-1){
      completeData = buffer.slice(0, separatorIndex).trim();
      buffer = buffer.slice(separatorIndex +1);

if(completeData.startsWith('data: ')){
const jsonString = completeData.slice(6);// 去掉 'data: '
try{
const data =JSON.parse(jsonString);
          data.ops.forEach(op =>{
if(op.op==='add'&& op.path.includes('/streamed_output')){
const outputText = op.value;
if(outputText){
const currentContent = left_data.chat[index].msg_list[left_data.chat[index].msg_list.length-1].content;
if(!currentContent.includes(outputText)){
                  left_data.chat[index].msg_list[left_data.chat[index].msg_list.length-1].content+= outputText;
}
}
}
});
}catch(e){
console.error('Error parsing JSON:', e,'Complete Data:', completeData);
}
}else{
console.log('No valid JSON found in:', completeData);
}
}

returnreadStream();
};

returnreadStream();
}




functionhasLogin(compomentName = 'Login'){
if(compomentName =='login')return instaceV.proxy.hasLogin?'hidden':'';
if(compomentName =='main')return instaceV.proxy.hasLogin?'':'hidden';
}

functionshowSettingFunc(){
    showSetting.value=!showSetting.value
}

// 刪除當前會話記錄
functiondeleteChatItemHistory(uuid){
const index = left_data.chat.findIndex(v => v.uuid== uuid);
    left_data.chat[index].msg_list=[]
    message.success('當前會話記錄已清理')
}

functionconfirmSettings(){
// 這里可以添加保存設置的邏輯
console.log("設置已確認:", setting);
// 例如,你可以關閉模態框
  showSetting.value=false;
}

// 當前會話下載為圖片
asyncfunctiondom2img(){
    centerLodding.value=true

var ele =document.querySelectorAll(".msgItem")
var msgAreaDom =document.getElementById("msgArea")

const width = msgAreaDom.offsetWidth*2
const height = msgAreaDom.scrollHeight*1.5


let canvas1 =document.createElement('canvas');
let context = canvas1.getContext('2d');
    canvas1.width= width;
    canvas1.height= height;
// 繪制矩形添加白色背景色
    context.rect(0,0, width, height);
    context.fillStyle="#fff";
    context.fill();

let beforeHeight =0
for(let i =0; i < ele.length; i++){
const dom_canvas =awaithtml2canvas(ele[i],{
scrollX:0,
scrollY:0,
height: ele[i].scrollHeight,
width: ele[i].scrollWidth,
})

// var image = dom_canvas.toDataURL("image/png");
        context.drawImage(dom_canvas,0, beforeHeight, dom_canvas.width, dom_canvas.height)
        beforeHeight = beforeHeight + dom_canvas.height;

}
var image = canvas1.toDataURL("image/png").replace("image/png","image/octet-stream");
var link =document.getElementById("link");
    link.setAttribute("download",`chatweb-${(new Date()).getTime()}.png`);
    link.setAttribute("href", image);
    link.click();

    centerLodding.value=false
    message.success('圖片下載完成')

}

</script>

服務部署

# 切換目錄到chatweb目錄
cd chatweb

# 安裝依賴的組件
npm install

# 啟動服務
npm run dev

運行效果:

基于Agent的金融問答系統:前后端流程打通-AI.x社區

接口聯調

接下來,通過瀏覽器的開發者工具進行接口調試

  1. 在瀏覽器中打開 http://localhost:5173/
  2. 點擊開始按鈕切換頁面至聊天頁面,打開瀏覽器的開發者工具

基于Agent的金融問答系統:前后端流程打通-AI.x社區

  1. 輸入問題后點擊發送,觀察右側開發者工具的Console面板,觀察請求發送情況,以及返回的響應數據。


基于Agent的金融問答系統:前后端流程打通-AI.x社區


具體調試遇到的問題不盡相同,所以本章不再贅述具體調試方式,請讀者根據問題自行調試。

待整體調試通過之后,問答系統的前后端即完成主流程。


本文轉載自公眾號一起AI技術 作者:Dongming

原文鏈接:??https://mp.weixin.qq.com/s/7NjAO2a_6j8QU_3069_Jbw??

?著作權歸作者所有,如需轉載,請注明出處,否則將追究法律責任
標簽
已于2024-11-25 10:35:30修改
收藏
回復
舉報
回復
相關推薦
亚洲风情亚aⅴ在线发布| 国产网红主播福利一区二区| 久久99精品视频一区97| 中文字幕第3页| 国偷自产一区二区免费视频| 亚洲欧洲日产国产综合网| 99中文视频在线| 日日夜夜操视频| 亚洲成av人电影| 亚洲精品videossex少妇| 婷婷六月天在线| 国产一线二线在线观看| 久久亚区不卡日本| 亚洲aa在线观看| 精品人妻一区二区三区免费看| 99精品电影| 亚洲女人被黑人巨大进入al| 少妇愉情理伦片bd| 亚洲承认视频| 精品国产乱码久久久久久天美| 黄瓜视频免费观看在线观看www| 污视频在线免费观看| 久久草av在线| 国产成人精品在线| 国产在线综合网| 91久久国产| 亚洲毛片在线免费观看| 99999精品| 久久99国产精品二区高清软件| 欧美日韩国产精品一区二区三区四区| www亚洲国产| 国产一区电影| 91年精品国产| 国产成人一区二区三区免费看| 在线免费看毛片| 久久aⅴ乱码一区二区三区| 欧美激情18p| 91人妻一区二区三区蜜臀| 国产成人1区| 精品视频—区二区三区免费| 一级黄色片毛片| 日韩一区二区三区精品视频第3页| 欧美吞精做爰啪啪高潮| 免费男同深夜夜行网站| 爱情电影社保片一区| 午夜精品久久久久久久99樱桃| 麻豆传媒网站在线观看| 精产国品自在线www| 国产精品入口麻豆原神| 日韩欧美手机在线| 黄色软件在线观看| 国产亚洲综合性久久久影院| 鲁丝一区二区三区免费| 日本天堂影院在线视频| 91啪亚洲精品| 农村寡妇一区二区三区| 九九热视频在线观看| 久久久久久亚洲综合影院红桃 | 一区二区国产欧美| 蜜桃av噜噜一区| 国产精品丝袜白浆摸在线| 亚洲在线免费观看视频| 老鸭窝一区二区久久精品| 国产日韩欧美中文| 亚洲性在线观看| 韩国一区二区三区| 亚洲影视九九影院在线观看| www.热久久| proumb性欧美在线观看| 欧美韩国日本精品一区二区三区| 欧洲一区av| 国产欧美日韩在线视频| 偷拍盗摄高潮叫床对白清晰| 伊人手机在线| 五月天一区二区| 日本成人中文字幕在线| 国产人妖一区| 欧美电影免费观看完整版 | 久久精品一区二区三区四区| 日韩亚洲一区在线播放| 含羞草www国产在线视频| 亚洲激情校园春色| 日韩欧美视频网站| 欧美日韩国产网站| 日韩一级完整毛片| 星空大象在线观看免费播放| 中文字幕精品影院| www.xxxx欧美| 日韩欧美中文字幕一区二区| 久久经典综合| 亚洲最大av网站| 头脑特工队2在线播放| 国产欧美1区2区3区| 中文字幕色一区二区| 青草av在线| 色综合天天做天天爱| 日韩av.com| 美女一区2区| 日韩亚洲一区二区| 国产视频91在线| 精品制服美女丁香| 国产一区视频观看| 香蕉视频在线看| 性久久久久久久久| 中文字幕永久视频| 亚洲综合网狠久久| 在线免费看av不卡| 国产精品第九页| 美女视频黄a大片欧美| www.成人av| 超碰免费在线| 亚洲超丰满肉感bbw| 污污网站免费看| 麻豆一区一区三区四区| 欧美精品在线免费| 久久久国产免费| av在线免费不卡| 91精品国产吴梦梦| 桃花岛tv亚洲品质| 精品国产不卡一区二区三区| 五月婷婷综合激情网| 久久久水蜜桃av免费网站| 不卡视频一区二区| 日本三级视频在线观看| 色八戒一区二区三区| 亚洲黄色小说在线观看| 91精品久久久久久久蜜月| 国产精品com| 五月婷婷丁香花| 亚洲国产精品尤物yw在线观看| 99九九精品视频| 欧美一站二站| 国产成人一区二区在线| 亚洲日本国产精品| 亚洲电影一级黄| 手机在线观看日韩av| 欧美xxav| 国产日韩欧美成人| 在线激情免费视频| 欧美无乱码久久久免费午夜一区| 波多野结衣一本| 老鸭窝91久久精品色噜噜导演| 国产精品一区免费观看| 中国av在线播放| 欧美一区二区三区视频免费 | 亚洲国产清纯| 97超级碰碰| 91精品久久久| 欧美成人猛片aaaaaaa| 欧美激情国产精品免费| 国产乱码精品1区2区3区| 自拍亚洲欧美老师丝袜| 高清久久一区| 欧美成人免费大片| 丰满人妻一区二区三区四区53 | 97碰在线视频| 99久久婷婷国产综合精品青牛牛 | 国产精品迅雷| 国产一区二区三区欧美| 最近中文字幕在线观看| 亚洲国产精品高清| 欧美成人手机在线视频| 亚洲v在线看| 99视频网站| 日本不良网站在线观看| 亚洲码在线观看| 国产一级片一区二区| 中文字幕电影一区| 国产精品久久久久久久av福利| 一区二区三区午夜探花| 国产精品久久久久久久天堂第1集| 欧美亚洲日本精品| 伊人伊成久久人综合网站| 中文字幕在线播放不卡| 亚洲精品视频在线| 黄色国产在线视频| 免费欧美日韩| 亚洲免费av网| 欧美激情影院| 国产精品视频色| av在线免费网站| 日韩精品日韩在线观看| 中文字幕 自拍偷拍| 亚洲人成7777| 久久精品老司机| 紧缚奴在线一区二区三区| 成人黄色大片网站| 国产一区二区三区四区五区传媒 | 亚洲精品久久久狠狠狠爱| 福利微拍一区二区| 国产人与禽zoz0性伦| 成人免费高清视频| 中文字幕在线综合| 亚洲国产免费看| 在线无限看免费粉色视频| 国产主播性色av福利精品一区| 国产99视频精品免视看7| h网站久久久| 亚洲色图第一页| 亚洲奶汁xxxx哺乳期| 精品视频资源站| 日本三级2019| 国产精品电影一区二区| 中国一级特黄录像播放| 久久99精品国产麻豆婷婷| 日韩激情免费视频| 亚洲香蕉av| 日韩久久不卡| 欧美挤奶吃奶水xxxxx| 91久久精品国产| 欧洲一级精品| 性视频1819p久久| www免费在线观看| 自拍偷拍亚洲一区| 亚洲 欧美 激情 另类| 日韩亚洲欧美在线观看| 91丨porny丨在线中文 | 一区二区三区影院| 超碰人人干人人| 久久久精品免费免费| 日韩精品人妻中文字幕有码 | 精品人妻一区二区三区香蕉 | 欧美日韩亚洲综合一区二区三区| 日韩欧美性视频| 一区二区三区在线免费观看| 99久久99久久精品免费看小说.| 99re热视频精品| 麻豆tv在线观看| 国产黄色精品视频| 在线能看的av网站| 久久99精品国产麻豆婷婷| 超碰av在线免费观看| 久久国产欧美| 日韩免费毛片视频| 香蕉成人久久| 播放灌醉水嫩大学生国内精品| 精品成人在线| 亚洲国产精品无码av| 欧美1区3d| 乱子伦一区二区| 亚洲高清资源在线观看| 中文字幕一区二区三区在线乱码 | 337p粉嫩大胆噜噜噜鲁| 亚洲国产日本| 青娱乐自拍偷拍| 亚洲一区日本| 99久久久无码国产精品6| 国产欧美一区二区色老头 | 欧美a级理论片| 亚洲精品怡红院| 日本不卡视频在线| 污视频免费在线观看网站| 秋霞影院一区二区| 99热一区二区| 黄页网站大全一区二区| 人妻少妇偷人精品久久久任期| 国产成人在线观看免费网站| 伊人av在线播放| 不卡视频一二三| 中文字幕在线免费看线人| 国产亚洲综合性久久久影院| 后入内射无码人妻一区| 亚洲三级在线免费观看| 国产一级大片在线观看| 欧美日韩国内自拍| 亚洲欧美一二三区| 正在播放一区二区| 日本xxxxxwwwww| 亚洲人成在线一二| 久热国产在线| 国外色69视频在线观看| 高清电影在线观看免费| 性色av香蕉一区二区| 色尼玛亚洲综合影院| 91美女高潮出水| 国产精品videossex| 欧美亚洲另类在线一区二区三区| 日韩一区二区在线免费| 最新不卡av| 在线日韩欧美| 99热这里只有精品在线播放| 国产精品 日产精品 欧美精品| 日本黄色免费观看| 国产精品久久久久天堂| 国产精品.www| 在线免费不卡电影| 午夜精品久久久久久久第一页按摩 | 午夜一区在线观看| 中文字幕在线观看日韩| ririsao久久精品一区| 日韩av理论片| 欧美国产亚洲精品| 奇米精品在线| 午夜欧美理论片| 丰满少妇在线观看| 成人国产精品免费| 999精品视频在线观看播放| 欧美日韩国产专区| 国产裸体永久免费无遮挡| 日韩高清av在线| 四虎av在线| 国产精品久久视频| 黄色免费大全亚洲| 天堂av免费看| 日本午夜精品视频在线观看| 亚洲精品第二页| 亚洲欧洲一区二区在线播放| 久久久久久91亚洲精品中文字幕| 91精品黄色片免费大全| 免费在线观看一级毛片| 97国产精品免费视频| 国产亚洲精aa在线看| 日韩av一区二区三区在线观看| 激情一区二区| 能看毛片的网站| 国产精品免费人成网站| 亚洲天堂男人av| 亚洲国产97在线精品一区| 在线免费观看a视频| 国产精品视频在线播放| 最新国产精品视频| 日韩欧美一区二| 丁香六月综合激情| 在线免费日韩av| 4438成人网| 尤物在线视频| 国产精品午夜国产小视频| 校园春色另类视频| 六月婷婷在线视频| 高清不卡一二三区| 欧美又粗又大又长| 91精品国产欧美一区二区18| 成人高潮成人免费观看| 日韩免费观看网站| 伊人精品一区| 日本精品免费在线观看| 波多野结衣中文字幕一区| 久久久99精品| 欧美精品一区二区三区久久久| 欧美1—12sexvideos| 成人xxxxx色| 国产精品av一区二区| 日韩大尺度视频| 亚洲一区av在线| 丰满人妻av一区二区三区| 午夜精品免费视频| 清纯唯美亚洲经典中文字幕| 131美女爱做视频| 久久在线免费观看| 国产一区二区视频网站| 亚洲欧美日韩精品| 日韩成人高清| 亚洲激情一区二区三区| 蜜桃久久久久久久| 精品无码一区二区三区蜜臀| 欧美成人伊人久久综合网| 中文字幕有码在线观看| 国产精品免费在线| 在线观看国产精品入口| 亚洲少妇一区二区| 激情懂色av一区av二区av| 天堂a√中文在线| 国产精品第100页| 希岛爱理一区二区三区| 人妻换人妻a片爽麻豆| 色综合天天综合网天天看片| 国产视频二区在线观看| 国产男人精品视频| 亚洲中无吗在线| 大尺度做爰床戏呻吟舒畅| 欧美亚洲愉拍一区二区| 国产午夜精品久久久久免费视| 国产精品对白刺激久久久| 久久五月激情| 内射一区二区三区| 亚洲高清久久网| 久久青草免费| 欧美人成在线观看| 国产精品无码永久免费888| 国产老女人乱淫免费| 97人人模人人爽人人喊中文字| 国产一区二区三区网| 日本wwww色| 日本韩国一区二区| 国产一区久久精品| 精品一区二区三区日本| 久久精品国产久精国产| 五月天综合在线| 在线观看欧美日韩| 成午夜精品一区二区三区软件| 北条麻妃在线一区| 亚洲激情欧美激情| 蜜桃视频在线入口www| 亚洲free性xxxx护士白浆| 亚洲综合好骚| 一区二区在线观看免费视频| 亚洲人精品午夜在线观看| 国内精品麻豆美女在线播放视频| 亚洲这里只有精品| 黑丝美女久久久|