feat:知识库精细化管理 (#96)

* feat(知识库管理): 新增知识库创建人选择功能 (#96)

* "feat(知识库管理): 新增知识库权限修改功能并扩展操作列宽度 (#97)"

* feat(文档解析): 添加embedding向量维度校验,确保维度为1024并提示使用bge-m3模型
This commit is contained in:
zstar 2025-05-15 16:01:39 +08:00 committed by GitHub
parent 98f3547521
commit d0d7a24297
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 193 additions and 36 deletions

View File

@ -576,6 +576,12 @@ def perform_parse(doc_id, doc_info, file_info, embedding_config):
embedding_data = embedding_resp.json()
q_1024_vec = embedding_data["data"][0]["embedding"]
print(f"[Parser-INFO] 获取embedding成功长度: {len(q_1024_vec)}")
# 检查向量维度是否为1024
if len(q_1024_vec) != 1024:
error_msg = f"[Parser-ERROR] Embedding向量维度不是1024实际维度: {len(q_1024_vec)}, 建议使用bge-m3模型"
print(error_msg)
update_progress(-1, error_msg)
raise ValueError(error_msg)
except Exception as e:
print(f"[Parser-ERROR] 获取embedding失败: {e}")
raise Exception(f"[Parser-ERROR] 获取embedding失败: {e}")

View File

@ -154,6 +154,7 @@ class KnowledgebaseService:
@classmethod
def create_knowledgebase(cls, **data):
"""创建知识库"""
try:
# 检查知识库名称是否已存在
exists = cls._check_name_exists(data['name'])
@ -163,36 +164,42 @@ class KnowledgebaseService:
conn = cls._get_db_connection()
cursor = conn.cursor(dictionary=True)
# 获取最早的用户ID作为tenant_id和created_by
tenant_id = None
created_by = None
try:
query_earliest_user = """
SELECT id FROM user
WHERE create_time = (SELECT MIN(create_time) FROM user)
LIMIT 1
"""
cursor.execute(query_earliest_user)
earliest_user = cursor.fetchone()
if earliest_user:
tenant_id = earliest_user['id']
created_by = earliest_user['id'] # 使用最早用户ID作为created_by
print(f"使用创建时间最早的用户ID作为tenant_id和created_by: {tenant_id}")
else:
# 如果找不到用户,使用默认值
# 使用传入的 creator_id 作为 tenant_id 和 created_by
tenant_id = data.get('creator_id')
created_by = data.get('creator_id')
if not tenant_id:
# 如果没有提供 creator_id则使用默认值
print("未提供 creator_id尝试获取最早用户 ID")
try:
query_earliest_user = """
SELECT id FROM user
WHERE create_time = (SELECT MIN(create_time) FROM user)
LIMIT 1
"""
cursor.execute(query_earliest_user)
earliest_user = cursor.fetchone()
if earliest_user:
tenant_id = earliest_user['id']
created_by = earliest_user['id']
print(f"使用创建时间最早的用户ID作为tenant_id和created_by: {tenant_id}")
else:
# 如果找不到用户,使用默认值
tenant_id = "system"
created_by = "system"
print(f"未找到用户, 使用默认值作为tenant_id和created_by: {tenant_id}")
except Exception as e:
print(f"获取用户ID失败: {str(e)},使用默认值")
tenant_id = "system"
created_by = "system"
print(f"未找到用户, 使用默认值作为tenant_id和created_by: {tenant_id}")
except Exception as e:
print(f"获取用户ID失败: {str(e)},使用默认值")
tenant_id = "system"
created_by = "system"
else:
print(f"使用传入的 creator_id 作为 tenant_id 和 created_by: {tenant_id}")
# --- 获取动态 embd_id ---
dynamic_embd_id = None
default_embd_id = 'bge-m3___VLLM@VLLM' # Fallback default
default_embd_id = 'bge-m3' # Fallback default
try:
query_embedding_model = """
SELECT llm_name
@ -316,6 +323,10 @@ class KnowledgebaseService:
if 'description' in data:
update_fields.append("description = %s")
params.append(data['description'])
if 'permission' in data:
update_fields.append("permission = %s")
params.append(data['permission'])
# 更新时间
current_time = datetime.now()

View File

@ -29,6 +29,7 @@ export function createKnowledgeBaseApi(data: {
description?: string
language?: string
permission?: string
creator_id: string
}) {
return request({
url: "/api/v1/knowledgebases",

View File

@ -1,6 +1,7 @@
<script lang="ts" setup>
import type { FormInstance, UploadUserFile } from "element-plus"
import { batchDeleteFilesApi, deleteFileApi, getFileListApi, uploadFileApi } from "@@/apis/files"
import { getTableDataApi } from "@@/apis/tables"
import { usePagination } from "@@/composables/usePagination"
import { Delete, Download, Refresh, Search, Upload } from "@element-plus/icons-vue"
import { ElLoading, ElMessage, ElMessageBox } from "element-plus"
@ -393,7 +394,7 @@ onActivated(() => {
{{ (paginationData.currentPage - 1) * paginationData.pageSize + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="name" label="文档名" align="center" sortable="custom"/>
<el-table-column prop="name" label="文档名" align="center" sortable="custom" />
<el-table-column label="大小" align="center" width="120" sortable="custom">
<template #default="scope">
{{ formatFileSize(scope.row.size) }}

View File

@ -18,13 +18,16 @@ import {
getSystemEmbeddingConfigApi,
setSystemEmbeddingConfigApi
} from "@@/apis/kbs/knowledgebase"
import { getTableDataApi } from "@@/apis/tables"
import { usePagination } from "@@/composables/usePagination"
import { CaretRight, Delete, Loading, Plus, Refresh, Search, Setting, View } from "@element-plus/icons-vue"
import { CaretRight, Delete, Edit, Loading, Plus, Refresh, Search, Setting, View } from "@element-plus/icons-vue"
import axios from "axios"
import { ElMessage, ElMessageBox } from "element-plus"
import { nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, reactive, ref, watch } from "vue"
import "element-plus/dist/index.css"
import "element-plus/theme-chalk/el-message-box.css"
import "element-plus/theme-chalk/el-message.css"
defineOptions({
@ -88,7 +91,8 @@ const knowledgeBaseForm = reactive({
name: "",
description: "",
language: "Chinese",
permission: "me"
permission: "me",
creator_id: ""
})
// API
@ -116,6 +120,9 @@ const knowledgeBaseFormRules = {
],
description: [
{ max: 200, message: "描述不能超过200个字符", trigger: "blur" }
],
creator_id: [
{ required: true, message: "请选择创建人", trigger: "change" }
]
}
@ -146,6 +153,43 @@ const fileSortData = reactive({
sortOrder: "desc" //
})
const editDialogVisible = ref(false)
const editForm = reactive({
id: "",
name: "",
permission: "me"
})
const editLoading = ref(false)
//
function handleEdit(row: KnowledgeBaseData) {
editDialogVisible.value = true
editForm.id = row.id
editForm.name = row.name
editForm.permission = row.permission
}
//
function submitEdit() {
editLoading.value = true
// API
axios.put(`/api/v1/knowledgebases/${editForm.id}`, {
permission: editForm.permission
})
.then(() => {
ElMessage.success("知识库权限修改成功")
editDialogVisible.value = false
//
getTableData()
})
.catch((error) => {
ElMessage.error(`修改知识库权限失败: ${error?.message || "未知错误"}`)
})
.finally(() => {
editLoading.value = false
})
}
//
const multipleSelection = ref<KnowledgeBaseData[]>([])
@ -186,6 +230,31 @@ function resetSearch() {
//
function handleCreate() {
createDialogVisible.value = true
getUserList() //
}
//
function getUserList() {
userLoading.value = true
// API
getTableDataApi({
currentPage: 1,
size: 1000, //
username: "",
email: "",
sort_by: "create_date",
sort_order: "desc"
}).then(({ data }) => {
userList.value = data.list.map((user: any) => ({
id: user.id,
username: user.username
}))
}).catch(() => {
userList.value = []
ElMessage.error("获取用户列表失败")
}).finally(() => {
userLoading.value = false
})
}
//
@ -982,6 +1051,10 @@ function isLoadingStatus(status: string) {
function shouldShowProgressCount(status: string) {
return !["starting", "not_found"].includes(status)
}
//
const userList = ref<{ id: number, username: string }[]>([])
const userLoading = ref(false)
</script>
<template>
@ -1037,8 +1110,8 @@ function shouldShowProgressCount(status: string) {
{{ (paginationData.currentPage - 1) * paginationData.pageSize + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="name" label="知识库名称" align="center" min-width="120" sortable="custom"/>
<el-table-column prop="description" label="描述" align="center" min-width="180" show-overflow-tooltip />
<el-table-column prop="name" label="知识库名称" align="center" min-width="120" sortable="custom" />
<el-table-column prop="description" label="描述" align="center" min-width="180" show-overflow-tooltip />
<el-table-column prop="doc_num" label="文档数量" align="center" width="120" />
<el-table-column label="语言" align="center" width="100">
<template #default="scope">
@ -1060,7 +1133,7 @@ function shouldShowProgressCount(status: string) {
{{ scope.row.create_date }}
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="180" align="center">
<el-table-column fixed="right" label="操作" width="300" align="center">
<template #default="scope">
<el-button
type="primary"
@ -1072,6 +1145,16 @@ function shouldShowProgressCount(status: string) {
>
查看
</el-button>
<el-button
type="warning"
text
bg
size="small"
:icon="Edit"
@click="handleEdit(scope.row)"
>
修改
</el-button>
<el-button
type="danger"
text
@ -1268,6 +1351,22 @@ function shouldShowProgressCount(status: string) {
<el-option label="英文" value="English" />
</el-select>
</el-form-item>
<el-form-item label="创建人" prop="creator_id">
<el-select
v-model="knowledgeBaseForm.creator_id"
placeholder="请选择创建人"
style="width: 100%"
filterable
:loading="userLoading"
>
<el-option
v-for="user in userList"
:key="user.id"
:label="user.username"
:value="user.id"
/>
</el-select>
</el-form-item>
<el-form-item label="权限" prop="permission">
<el-select v-model="knowledgeBaseForm.permission" placeholder="请选择权限">
<el-option label="个人" value="me" />
@ -1289,6 +1388,45 @@ function shouldShowProgressCount(status: string) {
</template>
</el-dialog>
<!-- 修改知识库对话框 -->
<el-dialog
v-model="editDialogVisible"
title="修改知识库权限"
width="40%"
>
<el-form
label-width="120px"
>
<el-form-item label="知识库名称">
<span>{{ editForm.name }}</span>
</el-form-item>
<el-form-item label="权限设置">
<el-select v-model="editForm.permission" placeholder="请选择权限">
<el-option label="个人" value="me" />
<el-option label="团队" value="team" />
</el-select>
</el-form-item>
<el-form-item>
<div style="color: #909399; font-size: 12px; line-height: 1.5;">
个人权限仅自己可见和使用<br>
团队权限团队成员可见和使用
</div>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="editDialogVisible = false">
取消
</el-button>
<el-button
type="primary"
:loading="editLoading"
@click="submitEdit"
>
确认修改
</el-button>
</template>
</el-dialog>
<!-- 文档对话框 -->
<el-dialog
v-model="addDocumentDialogVisible"
@ -1314,11 +1452,11 @@ function shouldShowProgressCount(status: string) {
{{ formatFileType(scope.row.type) }}
</template>
</el-table-column>
<el-table-column prop="create_date" label="创建时间" align="center" width="180" sortable="custom">
<template #default="scope">
{{ scope.row.create_date }}
</template>
</el-table-column>
<el-table-column prop="create_date" label="创建时间" align="center" width="180" sortable="custom">
<template #default="scope">
{{ scope.row.create_date }}
</template>
</el-table-column>
</el-table>
<!-- 分页控件 -->
@ -1554,4 +1692,4 @@ function shouldShowProgressCount(status: string) {
vertical-align: middle;
animation: rotating 2s linear infinite;
}
</style>
</style>