核工业西南物理研究院知识库AI客户端
xiangpei
2025-04-18 7789aeaad9032763805da324d743bc664bddd2e8
改为对接java而不是直接调langchain
6个文件已修改
1个文件已添加
324 ■■■■■ 已修改文件
src/api/chat.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/request.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/session.js 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/AiChat.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/SessionConfig.vue 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Index.vue 194 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/chat.js
@@ -3,7 +3,7 @@
// 发送知识库问题
export const sendKbMsg = (data) => {
    return axios({
        url: "chat/kb_chat",
        url: "chat/send/msg",
        method: "POST",
        data: data
    })
src/api/request.js
@@ -28,14 +28,13 @@
instance.interceptors.response.use(function (response) {
    console.log("正常响应结果",response)
    // 处理自定义状态码
    if(response.status === 200) {
    if(response.data.code === 200) {
        return response;
        // 验证码错误放行,以便刷新验证码
    } else if(response.status === 404) {
        Message.error(response.statusText);
    } else {
    } else if (response.data.code === 500) {
      Message.error(response.statusText);
      return Promise.reject(response.statusText);
    } else {
        return response;
    }
  }, function (error) {
    console.log("错误响应结果",error)
src/api/session.js
New file
@@ -0,0 +1,39 @@
import axios from "./request";
// 获取左侧会话列表
export const sessionList = (params) => {
    return axios({
        url: "/session/client/list",
        method: "GET",
        params: params
    })
}
// 新增会话
export const addSession = (data) => {
    return axios({
        url: "/session/",
        method: "POST",
        data: data
    })
}
// 编辑会话
export const editSession = (data) => {
    return axios({
        url: "/session/",
        method: "PUT",
        data: data
    })
}
// id查找会话
export const getSessionDetail = (id) => {
    return axios({
        url: "/session/" + id,
        method: "GET"
    })
}
src/components/AiChat.vue
@@ -77,7 +77,6 @@
<script>
import ClipboardJS from 'clipboard';
// import {sendKbMsg} from "@/api/chat";
import {Message} from "element-ui";
const markdownIt = require('markdown-it')();
@@ -85,24 +84,25 @@
  name: 'AiChat',
  data() {
    return {
      chatId: null,
      messages: [], // 用于页面展示的对话列表
      netSearchEnable: false,
      inputMessage: '',
      sendMsgForm: {
        query: "",
        mode: "local_kb",
        kb_name: "SouthWest_Neclear_Develepment_KB",
        top_k: 3,
        score_threshold: 2,
        kbName: "SouthWest_Neclear_Develepment_KB",
        topK: 3,
        scoreThreshold: 2,
        history: [
        ],
        stream: true,
        model: "Qwen25-32B-Instruct",
        temperature: 1.15,
        max_tokens: 512,
        prompt_name: "default",
        return_direct: false
        maxTokens: 512,
        promptName: "default",
        returnDirect: false
      },
      msgIndex: null,
      msgRole: null,
@@ -179,7 +179,7 @@
      this.inputMessage = '';
      const assistantIndex = this.messages.length - 1;
      try {
        const response = await fetch('/api/chat/kb_chat', {
        const response = await fetch('/api/chat/send/msg', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
src/components/SessionConfig.vue
@@ -1,7 +1,7 @@
<template>
  <div>
    <el-dialog
        :title="sessionName + '——对话设置'"
        :title="form.sessionName + '——对话设置'"
        :visible.sync="show"
        :close-on-click-modal="false"
        :destroy-on-close="true"
@@ -10,7 +10,7 @@
        <div class="config-item">
          <div class="title">请选择对话模式:</div>
          <div>
            <el-select v-model="config.chatType" @change="saveConfig" size="small">
            <el-select v-model="form.mode" size="small" disabled>
              <el-option label="知识库问答" value="kb"/>
            </el-select>
          </div>
@@ -18,28 +18,28 @@
      <div class="config-item">
        <div class="title">请选择知识库:</div>
        <div>
          <el-select v-model="config.kb" @change="saveConfig" size="small">
            <el-option label="知识库A" value="kb1"/>
          <el-select v-model="form.kbName" size="small">
            <el-option label="SouthWest_Neclear_Develepment_KB" value="SouthWest_Neclear_Develepment_KB"/>
          </el-select>
        </div>
      </div>
      <div class="config-item">
        <div class="title">历史对话轮数:</div>
        <div>
          <el-input v-model="config.hisChatNum" @input="saveConfig" type="number" size="small"/>
          <el-input v-model="form.topK" type="number" size="small"/>
        </div>
      </div>
      <div class="config-item">
        <div class="title">匹配知识条数:</div>
        <div>
          <el-input v-model="config.matchKbNum" @input="saveConfig" type="number" size="small"/>
          <el-input v-model="form.scoreThreshold" type="number" size="small"/>
        </div>
      </div>
      <div class="config-item">
        <div class="title">匹配知识分数阈值:</div>
        <div>
          <el-slider
              v-model="config.matchKbScore"
              v-model="form.temperature"
              @change="saveConfig"
              :min="0.00"
              :max="2.00"
@@ -48,46 +48,66 @@
        </div>
      </div>
      <div>
        <el-checkbox v-model="config.returnMatchResult" @change="saveConfig">仅返回检索结果</el-checkbox>
        <el-checkbox v-model="form.returnDirect">仅返回检索结果</el-checkbox>
      </div>
        <span slot="footer" class="dialog-footer">
          <el-button @click="close" size="small">取 消</el-button>
          <el-button @click="saveConfig" type="primary" size="small">保 存</el-button>
        </span>
    </el-dialog>
  </div>
</template>
<script>
import {editSession, getSessionDetail} from "@/api/session";
export default {
  name: "SessionConfig",
  props: {
    sessionName: {
    id: {
      type: String
    }
  },
  watch: {
    id: {
      handler(newV) {
       if (newV) {
         getSessionDetail(newV).then(res => {
           this.form = res.data.data
         })
       }
      }
    }
  },
  data() {
    return {
      show: false,
      config: {
        chatType: 'kb',
        kb: 'kb1',
        hisChatNum: 3,
        matchKbNum: 3,
        matchKbScore: 1,
        returnMatchResult: false
      form: {
        id: '',
        sessionName: '',
        mode: "",
        kbName: "",
        topK: null,
        scoreThreshold: null,
        stream: true,
        model: "",
        temperature: 0,
        maxTokens: 0,
        promptName: "",
        returnDirect: false
      }
    };
  },
  methods: {
    saveConfig() {
      console.log("触发保存了")
      this.$emit('saveConfig', this.config)
      editSession(this.form).then(res => {
        if (res.data.code == 200) {
          this.$message.success("保存成功")
        }
      })
    },
    setShow(value) {
      this.show = value
    },
    setConfig(config) {
      this.config = config
    },
    close() {
      this.show = false
src/views/Index.vue
@@ -15,7 +15,7 @@
        <img style="width: 60px;height: 60px" src="@/assets/img/logo.png"/>
      </div>
      <div class="menu">
        <div class="add-chat">
        <div class="add-chat" @click="addSession">
          <i class="el-icon-plus"/>新建对话
        </div>
        <div :class="{tab: true, activeTab: this.$router.currentRoute.path == '/knowledge'}" style="margin-top: 2px" @click="changeTab()">
@@ -27,78 +27,77 @@
        </div>
      </div>
      <div class="session-list">
        <div :class="{session: true, 'active-session': currentSession == index}" v-for="(session, index) in sessionList" :key="'session' + index">
          <div style="width: 100%"  @mouseenter="activeSession = index" @mouseleave="activeSession = null">
            <div @click="sessionChange(session, index)">{{session.name}}</div>
            <div v-show="activeSession != null && activeSession == index" class="session-more">
              <el-dropdown @command="(command) => handleCommand(session, command)" trigger="click">
        <div class="session-block">
          <div class="time">今天</div>
          <div :class="{session: true, 'active-session': currentSession == session.id}" v-for="(session) in sessionList.today" :key="'todaysession' + session.id">
            <div style="width: 100%"  @mouseenter="activeSession = session.id" @mouseleave="activeSession = null">
              <div @click="sessionChange(session, session.id)">{{session.sessionName}}</div>
              <div v-show="activeSession != null && activeSession == session.id" class="session-more">
                <el-dropdown @command="(command) => handleCommand(session, command)" trigger="click">
              <span class="el-dropdown-link">
                <svg t="1743058876802" class="icon more" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2795" width="12" height="12">
                  <path fill="gray" d="M480 320C533.184 320 576 277.184 576 224S533.184 128 480 128 384 170.816 384 224 426.816 320 480 320zM480 448C426.816 448 384 490.816 384 544S426.816 640 480 640 576 597.184 576 544 533.184 448 480 448zM480 768c-53.184 0-96 42.816-96 96S426.816 960 480 960 576 917.184 576 864c0-52.672-42.816-96-96-96z" p-id="2796"></path></svg>
              </span>
                <el-dropdown-menu slot="dropdown">
                  <el-dropdown-item command="config">会话设置</el-dropdown-item>
                  <el-dropdown-item command="rename">重命名</el-dropdown-item>
                  <el-dropdown-item command="del">删除</el-dropdown-item>
                </el-dropdown-menu>
              </el-dropdown>
                  <el-dropdown-menu slot="dropdown">
                    <el-dropdown-item command="config">会话设置</el-dropdown-item>
                    <el-dropdown-item command="rename">重命名</el-dropdown-item>
                    <el-dropdown-item command="del">删除</el-dropdown-item>
                  </el-dropdown-menu>
                </el-dropdown>
              </div>
            </div>
          </div>
        </div>
        <div class="session-block" v-show="sessionList.yesterday && sessionList.yesterday.length > 0">
          <div class="time">昨天</div>
          <div :class="{session: true, 'active-session': currentSession == session.id}" v-for="(session) in sessionList.yesterday" :key="'yesterdaysession' + session.id">
            <div style="width: 100%"  @mouseenter="activeSession = session.id" @mouseleave="activeSession = null">
              <div @click="sessionChange(session, session.id)">{{session.sessionName}}</div>
              <div v-show="activeSession != null && activeSession == session.id" class="session-more">
                <el-dropdown @command="(command) => handleCommand(session, command)" trigger="click">
              <span class="el-dropdown-link">
                <svg t="1743058876802" class="icon more" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2795" width="12" height="12">
                  <path fill="gray" d="M480 320C533.184 320 576 277.184 576 224S533.184 128 480 128 384 170.816 384 224 426.816 320 480 320zM480 448C426.816 448 384 490.816 384 544S426.816 640 480 640 576 597.184 576 544 533.184 448 480 448zM480 768c-53.184 0-96 42.816-96 96S426.816 960 480 960 576 917.184 576 864c0-52.672-42.816-96-96-96z" p-id="2796"></path></svg>
              </span>
                  <el-dropdown-menu slot="dropdown">
                    <el-dropdown-item command="config">会话设置</el-dropdown-item>
                    <el-dropdown-item command="rename">重命名</el-dropdown-item>
                    <el-dropdown-item command="del">删除</el-dropdown-item>
                  </el-dropdown-menu>
                </el-dropdown>
              </div>
            </div>
          </div>
        </div>
        <div class="session-block" v-show="sessionList.old && sessionList.old.length > 0">
          <div class="time">更早</div>
          <div :class="{session: true, 'active-session': currentSession == session.id}" v-for="(session) in sessionList.old" :key="'oldsession' + session.id">
            <div style="width: 100%"  @mouseenter="activeSession = session.id" @mouseleave="activeSession = null">
              <div @click="sessionChange(session, session.id)">{{session.sessionName}}</div>
              <div v-show="activeSession != null && activeSession == session.id" class="session-more">
                <el-dropdown @command="(command) => handleCommand(session, command)" trigger="click">
              <span class="el-dropdown-link">
                <svg t="1743058876802" class="icon more" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2795" width="12" height="12">
                  <path fill="gray" d="M480 320C533.184 320 576 277.184 576 224S533.184 128 480 128 384 170.816 384 224 426.816 320 480 320zM480 448C426.816 448 384 490.816 384 544S426.816 640 480 640 576 597.184 576 544 533.184 448 480 448zM480 768c-53.184 0-96 42.816-96 96S426.816 960 480 960 576 917.184 576 864c0-52.672-42.816-96-96-96z" p-id="2796"></path></svg>
              </span>
                  <el-dropdown-menu slot="dropdown">
                    <el-dropdown-item command="config">会话设置</el-dropdown-item>
                    <el-dropdown-item command="rename">重命名</el-dropdown-item>
                    <el-dropdown-item command="del">删除</el-dropdown-item>
                  </el-dropdown-menu>
                </el-dropdown>
              </div>
            </div>
          </div>
        </div>
      </div>
<!--      <div class="setting">-->
<!--        <el-tabs v-model="activeSetting" @tab-click="tabSelect">-->
<!--          <el-tab-pane label="工具设置" name="util">-->
<!--            <div style="display: flex; align-items: center">-->
<!--              <el-checkbox v-model="enableAgent">启用Agent</el-checkbox>-->
<!--              <el-tooltip style="margin-left: 10px" content="Top center" placement="right" effect="light">-->
<!--                <svg t="1742971974478" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2969" width="16" height="16"><path d="M464 784.352c0 26.51 21.49 48 48 48s48-21.49 48-48-21.49-48-48-48-48 21.49-48 48z" p-id="2970" fill="#515151"></path><path d="M512 960C264.96 960 64 759.04 64 512S264.96 64 512 64s448 200.96 448 448-200.96 448-448 448z m0-831.713c-211.584 0-383.713 172.129-383.713 383.713 0 211.552 172.129 383.713 383.713 383.713 211.552 0 383.713-172.16 383.713-383.713 0-211.584-172.161-383.713-383.713-383.713z" p-id="2971" fill="#515151"></path><path d="M512 673.695c-17.665 0-32-14.336-32-31.999v-54.112c0-52.353 39.999-92.352 75.327-127.648 25.887-25.92 52.672-52.672 52.672-74.016 0-53.344-43.072-96.736-95.999-96.736-53.823 0-96 41.536-96 94.56 0 17.664-14.335 31.999-32 31.999s-32-14.336-32-32c0-87.423 71.774-158.559 160-158.559S672 297.28 672 385.92c0 47.904-36.32 84.191-71.424 119.296-27.84 27.776-56.575 56.512-56.575 82.335v54.112c0 17.665-14.336 32.032-32.001 32.032z" p-id="2972" fill="#515151"></path></svg>-->
<!--              </el-tooltip>-->
<!--            </div>-->
<!--            <div style="margin-top: 15px">-->
<!--              <div class="normal-text">选择工具</div>-->
<!--              <div style="margin-top: 5px">-->
<!--                <el-select v-model="selectUtil" size="mini" placeholder="未选择" style="width: 100%">-->
<!--                  <el-option-->
<!--                      v-for="item in options"-->
<!--                      :key="item.value"-->
<!--                      :label="item.label"-->
<!--                      :value="item.value">-->
<!--                  </el-option>-->
<!--                </el-select>-->
<!--              </div>-->
<!--            </div>-->
<!--            <div style="margin-top: 15px">-->
<!--              <div class="normal-text" style="display: flex;align-items: center">-->
<!--                <div>上传附件</div>-->
<!--                <el-tooltip style="margin-left: 10px" content="单个文件不超过200M" placement="right" effect="light">-->
<!--                  <svg t="1742971974478" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2969" width="16" height="16"><path d="M464 784.352c0 26.51 21.49 48 48 48s48-21.49 48-48-21.49-48-48-48-48 21.49-48 48z" p-id="2970" fill="#515151"></path><path d="M512 960C264.96 960 64 759.04 64 512S264.96 64 512 64s448 200.96 448 448-200.96 448-448 448z m0-831.713c-211.584 0-383.713 172.129-383.713 383.713 0 211.552 172.129 383.713 383.713 383.713 211.552 0 383.713-172.16 383.713-383.713 0-211.584-172.161-383.713-383.713-383.713z" p-id="2971" fill="#515151"></path><path d="M512 673.695c-17.665 0-32-14.336-32-31.999v-54.112c0-52.353 39.999-92.352 75.327-127.648 25.887-25.92 52.672-52.672 52.672-74.016 0-53.344-43.072-96.736-95.999-96.736-53.823 0-96 41.536-96 94.56 0 17.664-14.335 31.999-32 31.999s-32-14.336-32-32c0-87.423 71.774-158.559 160-158.559S672 297.28 672 385.92c0 47.904-36.32 84.191-71.424 119.296-27.84 27.776-56.575 56.512-56.575 82.335v54.112c0 17.665-14.336 32.032-32.001 32.032z" p-id="2972" fill="#515151"></path></svg>-->
<!--                </el-tooltip>-->
<!--              </div>-->
<!--              <div style="margin-top: 5px">-->
<!--                <el-upload-->
<!--                    class="upload"-->
<!--                    drag-->
<!--                    :on-change="handleChange"-->
<!--                    :before-upload="handleUpload"-->
<!--                    :file-list="fileList"-->
<!--                    multiple>-->
<!--                  <i class="el-icon-upload"></i>-->
<!--                  <div class="el-upload__text">-->
<!--                    <div>将文件拖到此处,或<em>点击上传</em></div>-->
<!--                  </div>-->
<!--                </el-upload>-->
<!--              </div>-->
<!--            </div>-->
<!--          </el-tab-pane>-->
<!--          <el-tab-pane label="会话设置" name="session"></el-tab-pane>-->
<!--        </el-tabs>-->
<!--      </div>-->
    </div>
    <div class="right">
      <router-view></router-view>
      <ai-chat v-if="showWhich == 'chat'"/>
      <knowledge-base v-else-if="showWhich == 'kb'"/>
    </div>
    <session-config :session-name="sessionName" @saveConfig="saveConfig" ref="sessionConfig" />
    <session-config :id="editSessionId" ref="sessionConfig" />
    <el-dialog
        :title="'重命名对话'"
@@ -118,18 +117,23 @@
<script>
import SessionConfig from "@/components/SessionConfig";
import AiChat from "@/components/AiChat";
import KnowledgeBase from "@/components/KnowledgeBase";
import Login from "@/components/Login";
import {sessionList} from "@/api/session";
export default {
  name: "IndexView",
  components: {
    SessionConfig, Login
    SessionConfig, Login, AiChat, KnowledgeBase
  },
  data() {
    return {
      editSessionId: '',
      showWhich: '',
      renameShow: false,
      rename: '',
      sessionName: '',
      editSession: '',
      config: {
        chatType: 'kb',
        kb: 'kb1',
@@ -138,17 +142,11 @@
        matchKbScore: 1,
        returnMatchResult: false
      },
      sessionList: [
        {
          name: '会话1'
        },
        {
          name: '会话2'
        },
        {
          name: '会话3'
        },
      ],
      sessionList: {
        today: [],
        yesterday: [],
        old: []
      },
      fileList: [],
      activeSession: null,
      currentSession: null,
@@ -174,14 +172,22 @@
    }
  },
  mounted() {
    this.getSessionList()
  },
  methods: {
    sessionChange(session, index) {
      this.currentSession = index
      if (this.$router.currentRoute.path !== "/chat") {
        this.$router.push("/chat")
      }
    addSession() {
      this.currentSession = null
      this.showWhich = 'chat'
    },
    // 获取会话列表
    getSessionList() {
      sessionList().then(res => {
        this.sessionList = res.data.data
      })
    },
    sessionChange(session, id) {
      this.currentSession = id
      this.showWhich = 'chat'
    },
    renameSubmit() {
      // TODO 保存到对话中
@@ -191,15 +197,10 @@
        message: '对话名称已修改'
      });
    },
    saveConfig(config) {
      // TODO 保存到对话中
      console.log(config, "父组件获取到配置了")
    },
    handleCommand(session, command) {
      console.log(session, command)
      if (command === 'config') {
        this.sessionName = session.name
        this.$refs.sessionConfig.setConfig(this.config)
        this.editSessionId = session.id
        this.$refs.sessionConfig.setShow(true)
      } else if (command === 'rename') {
        this.rename = session.name
@@ -233,13 +234,8 @@
    handleChange(file, fileList) {
      this.fileList = fileList;
    },
    tabSelect(tab, event) {
      console.log(tab, event)
    },
    changeTab() {
      if (this.$router.currentRoute.path !== "/knowledge") {
        this.$router.push("/knowledge")
      }
      this.showWhich = 'kb'
    },
  }
}
@@ -373,4 +369,16 @@
::v-deep(.el-dialog) {
  border-radius: 16px!important;
}
.session-block {
  width: 100%;
  margin-bottom: 20px;
}
.time {
  padding-left: 10px;
  margin-bottom: 10px;
  font-style: italic;
  font-weight: bold;
}
</style>
vue.config.js
@@ -5,7 +5,7 @@
    compress: false,
    proxy: {
      "/api": {
        target: 'http://i-1.gpushare.com:52574/',//代理地址 凡是使用/api
        target: 'http://127.0.0.1:9897/',//代理地址 凡是使用/api
        changeOrigin: true,//允许跨域请求
        secure: false,
        pathRewrite: { //重写路径 替换请求地址中的指定路径