diff --git a/src/interfaces/request/base.ts b/src/interfaces/request/base.ts
index 789be81..96f0911 100644
--- a/src/interfaces/request/base.ts
+++ b/src/interfaces/request/base.ts
@@ -5,3 +5,11 @@ export interface IPaginationRequestBody {
orderby?: string;
desc?: string;
}
+
+/**
+ * 分页请求参数
+ */
+export interface IPaginationBody {
+ page?: number;
+ size?: number;
+}
diff --git a/src/interfaces/request/knowledge.ts b/src/interfaces/request/knowledge.ts
index de1b00b..5e3d651 100644
--- a/src/interfaces/request/knowledge.ts
+++ b/src/interfaces/request/knowledge.ts
@@ -1,4 +1,18 @@
-export interface ITestRetrievalRequestBody {
+/**
+{"similarity_threshold":0.2,
+"vector_similarity_weight":0.3,
+"top_k":1024,"use_kg":true,
+"cross_languages":["English","Chinese","Spanish","French","German",
+"Japanese","Korean","Vietnamese"],"question":"123","kb_id":"dcc2871aa4cd11f08d4116ac85b1de0a",
+"page":1,"size":10,"doc_ids":["92b37a3aa7de11f084d336b0b158b556"]}
+ */
+
+import type { IPaginationBody } from "./base";
+
+/**
+ * 检索测试请求体
+ */
+export interface ITestRetrievalRequestBody extends IPaginationBody {
question: string;
similarity_threshold: number;
vector_similarity_weight: number;
@@ -6,13 +20,21 @@ export interface ITestRetrievalRequestBody {
top_k?: number;
use_kg?: boolean;
highlight?: boolean;
- kb_id?: string[];
+ kb_id?: string;
+ doc_ids?: string[];
+ cross_languages?: string[];
}
+/**
+ * 获取知识库列表请求体
+ */
export interface IFetchKnowledgeListRequestBody {
owner_ids?: string[];
}
+/**
+ * 获取知识库列表请求参数
+ */
export interface IFetchKnowledgeListRequestParams {
kb_id?: string;
keywords?: string;
@@ -20,6 +42,9 @@ export interface IFetchKnowledgeListRequestParams {
page_size?: number;
}
+/**
+ * 获取文档列表请求体
+ */
export interface IFetchDocumentListRequestBody {
suffix?: string[];
run_status?: string[];
diff --git a/src/pages/knowledge/components/TestChunkResult.tsx b/src/pages/knowledge/components/TestChunkResult.tsx
new file mode 100644
index 0000000..1cde4e1
--- /dev/null
+++ b/src/pages/knowledge/components/TestChunkResult.tsx
@@ -0,0 +1,249 @@
+import React from 'react';
+import {
+ Box,
+ Paper,
+ Typography,
+ FormControl,
+ InputLabel,
+ Select,
+ MenuItem,
+ Grid,
+ Card,
+ CardContent,
+ Chip,
+ Stack,
+ Checkbox,
+ ListItemText,
+ OutlinedInput,
+} from '@mui/material';
+import type { SelectChangeEvent } from '@mui/material/Select';
+import { FilterList as FilterListIcon } from '@mui/icons-material';
+import type { INextTestingResult, ITestingDocument } from '@/interfaces/database/knowledge';
+
+interface TestChunkResultProps {
+ result: INextTestingResult | null;
+ loading: boolean;
+ page: number;
+ pageSize: number;
+ onDocumentFilter: (docIds: string[]) => void;
+ selectedDocIds: string[];
+}
+
+function TestChunkResult({ result, page, pageSize, onDocumentFilter, selectedDocIds }: TestChunkResultProps) {
+ if (!result) {
+ return (
+
+
+ 请输入问题并点击"开始测试"来查看检索结果
+
+
+ );
+ }
+
+ // 计算分页数据 - 使用服务端分页,直接显示当前页数据
+ const chunks = result.chunks || [];
+ const totalPages = Math.ceil(result.total / pageSize);
+
+ const handleDocumentFilterChange = (event: SelectChangeEvent) => {
+ const value = event.target.value as string[];
+ onDocumentFilter(value);
+ };
+
+ return (
+
+ {/* 测试结果概览 */}
+
+
+ 测试结果概览
+
+
+
+
+
+
+ {result.total}
+
+
+ 匹配的文档块
+
+
+
+
+
+
+
+
+ {result.doc_aggs?.length || 0}
+
+
+ 相关文档
+
+
+
+
+
+
+
+
+ {result.chunks?.length || 0}
+
+
+ 返回的块数
+
+
+
+
+
+
+
+ {/* 文档过滤器 */}
+ {result.doc_aggs && result.doc_aggs.length > 0 && (
+
+
+
+ 文档过滤
+
+
+ 选择要显示的文档
+ }
+ renderValue={(selected) => (
+
+ {selected.map((value) => {
+ const doc = result.doc_aggs?.find(d => d.doc_id === value);
+ return (
+
+ );
+ })}
+
+ )}
+ >
+ {result.doc_aggs.map((doc) => (
+
+ ))}
+
+
+
+ )}
+
+ {/* 匹配的文档块 */}
+ {chunks && chunks.length > 0 && (
+
+
+
+ 匹配的文档块 (第 {page} 页,共 {totalPages} 页)
+
+
+ 共找到 {result.total} 个匹配块
+
+
+
+
+ {chunks.map((chunk, index) => (
+
+
+
+
+
+ {chunk.doc_name}
+
+
+
+ {chunk.vector_similarity !== undefined && (
+
+ )}
+ {chunk.term_similarity !== undefined && (
+
+ )}
+
+
+
+
+
+ {chunk.important_kwd && chunk.important_kwd.length > 0 && (
+
+
+ 关键词:
+
+
+ {chunk.important_kwd.map((keyword, kwdIndex) => (
+
+ ))}
+
+
+ )}
+
+
+
+ ))}
+
+
+ )}
+
+ {/* 相关文档统计 */}
+ {result.doc_aggs && result.doc_aggs.length > 0 && (
+
+
+ 相关文档统计
+
+
+ {result.doc_aggs.map((doc: ITestingDocument, index: number) => (
+
+
+ {doc.doc_name}
+
+
+
+ ))}
+
+
+ )}
+
+ );
+}
+
+export default TestChunkResult;
\ No newline at end of file
diff --git a/src/pages/knowledge/testing.tsx b/src/pages/knowledge/testing.tsx
index 114e4d5..e36b7df 100644
--- a/src/pages/knowledge/testing.tsx
+++ b/src/pages/knowledge/testing.tsx
@@ -1,6 +1,6 @@
-import React, { useState } from 'react';
+import React, { useState, useMemo } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
-import { useForm } from 'react-hook-form';
+import { useForm, Controller } from 'react-hook-form';
import {
Box,
Container,
@@ -15,270 +15,206 @@ import {
MenuItem,
FormControlLabel,
Switch,
- Fab,
- Snackbar,
- Alert,
Grid,
- Card,
- CardContent,
+ Pagination,
+ Checkbox,
+ ListItemText,
+ OutlinedInput,
+ ListSubheader,
Chip,
- Divider,
- Stack,
- CircularProgress,
} from '@mui/material';
-import {
- ArrowBack as ArrowBackIcon,
- Search as SearchIcon,
- Psychology as PsychologyIcon,
-} from '@mui/icons-material';
import { useKnowledgeDetail } from '@/hooks/knowledge-hooks';
+import { useRerankModelOptions } from '@/hooks/llm-hooks';
import knowledgeService from '@/services/knowledge_service';
import type { ITestRetrievalRequestBody } from '@/interfaces/request/knowledge';
-import type { ITestingResult, ITestingChunk, ITestingDocument } from '@/interfaces/database/knowledge';
+import type { INextTestingResult } from '@/interfaces/database/knowledge';
import KnowledgeBreadcrumbs from './components/KnowledgeBreadcrumbs';
+import TestChunkResult from './components/TestChunkResult';
+import { useSnackbar } from '@/components/Provider/SnackbarProvider';
+import { useTranslation } from 'react-i18next';
+import { toLower } from 'lodash';
+import { t } from 'i18next';
+// 语言选项常量
+const Languages = [
+ 'English',
+ 'Chinese',
+ 'Spanish',
+ 'French',
+ 'German',
+ 'Japanese',
+ 'Korean',
+ 'Vietnamese',
+];
+
+const options = Languages.map((x) => ({
+ label: t('language.' + toLower(x)),
+ value: x,
+}));
+
+// 表单数据接口
interface TestFormData {
question: string;
similarity_threshold: number;
vector_similarity_weight: number;
rerank_id?: string;
- top_k: number;
- use_kg: boolean;
- highlight: boolean;
-}
-
-interface ResultViewProps {
- result: ITestingResult | null;
- loading: boolean;
-}
-
-function ResultView({ result, loading }: ResultViewProps) {
- if (loading) {
- return (
-
-
-
- 正在检索测试...
-
-
- );
- }
-
- if (!result) {
- return (
-
-
-
- 请输入测试问题并点击测试按钮
-
-
- );
- }
-
- return (
-
- {/* 测试结果概览 */}
-
-
- 测试结果概览
-
-
-
-
-
-
- {result.total}
-
-
- 匹配的文档块
-
-
-
-
-
-
-
-
- {result.documents?.length || 0}
-
-
- 相关文档
-
-
-
-
-
-
-
-
- {result.chunks?.length || 0}
-
-
- 返回的块数
-
-
-
-
-
-
-
- {/* 匹配的文档块 */}
- {result.chunks && result.chunks.length > 0 && (
-
-
- 匹配的文档块
-
-
- {result.chunks.map((chunk: ITestingChunk, index: number) => (
-
-
-
-
- 文档: {chunk.document_name}
-
-
-
- {chunk.vector_similarity && (
-
- )}
- {chunk.term_similarity && (
-
- )}
-
-
-
- {chunk.content || '内容不可用'}
-
-
-
- ))}
-
-
- )}
-
- {/* 相关文档统计 */}
- {result.documents && result.documents.length > 0 && (
-
-
- 相关文档统计
-
-
- {result.documents.map((doc: ITestingDocument, index: number) => (
-
-
- {doc.doc_name}
-
-
-
- ))}
-
-
- )}
-
- );
+ top_k?: number;
+ use_kg?: boolean;
+ cross_languages?: string[];
+ doc_ids?: string[];
}
function KnowledgeBaseTesting() {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
- const [testResult, setTestResult] = useState(null);
+ const { t } = useTranslation();
+
+ // 状态管理
+ const [testResult, setTestResult] = useState(null);
const [testing, setTesting] = useState(false);
- const [snackbar, setSnackbar] = useState<{
- open: boolean;
- message: string;
- severity: 'success' | 'error';
- }>({
- open: false,
- message: '',
- severity: 'success',
- });
+ const [page, setPage] = useState(1);
+ const [pageSize] = useState(10);
+ const [selectedDocIds, setSelectedDocIds] = useState([]);
+
+ const { showMessage } = useSnackbar();
// 获取知识库详情
- const { knowledge, loading: detailLoading } = useKnowledgeDetail(id || '');
+ const { knowledge: knowledgeDetail, loading: detailLoading } = useKnowledgeDetail(id || '');
- // 测试表单
- const form = useForm({
+ // 获取重排序模型选项
+ const { options: rerankOptions, loading: rerankLoading } = useRerankModelOptions();
+
+ // 表单配置
+ const { control, handleSubmit, watch, register, setValue, getValues, formState: { errors } } = useForm({
defaultValues: {
question: '',
similarity_threshold: 0.2,
vector_similarity_weight: 0.3,
rerank_id: '',
- top_k: 6,
+ top_k: 1024,
use_kg: false,
- highlight: true,
+ cross_languages: [],
+ doc_ids: [],
},
});
- const { register, handleSubmit, watch, setValue, formState: { errors } } = form;
-
+ // 处理测试提交
const handleTestSubmit = async (data: TestFormData) => {
if (!id) return;
+ setTesting(true);
try {
- setTesting(true);
-
const requestBody: ITestRetrievalRequestBody = {
question: data.question,
similarity_threshold: data.similarity_threshold,
vector_similarity_weight: data.vector_similarity_weight,
- top_k: data.top_k,
- use_kg: data.use_kg,
- highlight: data.highlight,
- kb_id: [id],
+ kb_id: id,
+ page: page,
+ size: pageSize,
};
- if (data.rerank_id && data.rerank_id.trim()) {
+ // 只有当字段有值时才添加到请求体中
+ if (data.rerank_id) {
requestBody.rerank_id = data.rerank_id;
}
+ if (data.top_k) {
+ requestBody.top_k = data.top_k;
+ }
+
+ if (data.use_kg !== undefined) {
+ requestBody.use_kg = data.use_kg;
+ }
+
+ // 如果有选择的文档,添加到请求中
+ if (data.doc_ids && data.doc_ids.length > 0) {
+ requestBody.doc_ids = data.doc_ids;
+ } else {
+ if (selectedDocIds.length > 0) {
+ requestBody.doc_ids = selectedDocIds;
+ }
+ }
+
+ if (data.cross_languages && data.cross_languages.length > 0) {
+ requestBody.cross_languages = data.cross_languages;
+ }
+
const response = await knowledgeService.retrievalTest(requestBody);
if (response.data.code === 0) {
setTestResult(response.data.data);
- setSnackbar({
- open: true,
- message: '检索测试完成',
- severity: 'success',
- });
+ setPage(1); // 重置到第一页
+ showMessage.success('检索测试完成');
} else {
throw new Error(response.data.message || '检索测试失败');
}
} catch (error: any) {
- setSnackbar({
- open: true,
- message: error.message || '检索测试失败',
- severity: 'error',
- });
+ showMessage.error(error.message || '检索测试失败');
} finally {
setTesting(false);
}
};
- const handleBackToDetail = () => {
- navigate(`/knowledge/${id}`);
+ // 处理分页变化
+ const handlePageChange = async (event: React.ChangeEvent, value: number) => {
+ if (!id) return;
+
+ setPage(value);
+ setTesting(true);
+
+ try {
+ const formData = getValues();
+ const requestBody: ITestRetrievalRequestBody = {
+ question: formData.question,
+ similarity_threshold: formData.similarity_threshold,
+ vector_similarity_weight: formData.vector_similarity_weight,
+ kb_id: id,
+ page: value,
+ size: pageSize,
+ highlight: true,
+ };
+
+ // 只有当字段有值时才添加到请求体中
+ if (formData.rerank_id) {
+ requestBody.rerank_id = formData.rerank_id;
+ }
+ if (formData.top_k) {
+ requestBody.top_k = formData.top_k;
+ }
+ if (formData.use_kg !== undefined) {
+ requestBody.use_kg = formData.use_kg;
+ }
+ if (selectedDocIds.length > 0) {
+ requestBody.doc_ids = selectedDocIds;
+ }
+
+ if (formData.cross_languages && formData.cross_languages.length > 0) {
+ requestBody.cross_languages = formData.cross_languages;
+ }
+
+ const response = await knowledgeService.retrievalTest(requestBody);
+ if (response.data.code === 0) {
+ setTestResult(response.data.data);
+ } else {
+ throw new Error(response.data.message || '分页请求失败');
+ }
+ } catch (error: any) {
+ showMessage.error(error.message || '分页请求失败');
+ } finally {
+ setTesting(false);
+ }
};
- const handleCloseSnackbar = () => {
- setSnackbar(prev => ({ ...prev, open: false }));
+ // 处理文档过滤
+ const handleDocumentFilter = (docIds: string[]) => {
+ setSelectedDocIds(docIds);
+ setValue('doc_ids', docIds);
+ handleTestSubmit(getValues());
+ };
+
+ // 返回详情页
+ const handleBackToDetail = () => {
+ navigate(`/knowledge/${id}`);
};
if (detailLoading) {
@@ -291,26 +227,27 @@ function KnowledgeBaseTesting() {
return (
+
{/* 面包屑导航 */}
-
-
+
+
知识库测试
- {knowledge?.name}
+ {knowledgeDetail?.name}
-
+
{/* 测试表单 */}
-
+
测试配置
-
+
-
+
+ 重排序模型 (可选)
+ (
+
+ )}
+ />
+
-
+ {/* Top-K 字段 - 只有选择了rerank_id时才显示 */}
+ {watch('rerank_id') && (
+
+ )}
+
+
+ 跨语言搜索
+ (
+ }
+ renderValue={(selected) => (
+
+ {selected.map((value) => {
+ const option = options.find(opt => opt.value === value);
+ return (
+
+ );
+ })}
+
+ )}
+ >
+ {options.map((option) => (
+
+ ))}
+
+ )}
+ />
+
setValue('use_kg', e.target.checked)}
/>
}
label="使用知识图谱"
- sx={{ mt: 2 }}
- />
-
- setValue('highlight', e.target.checked)}
- />
- }
- label="高亮显示"
- sx={{ mt: 1 }}
/>
: }
- sx={{ mt: 3 }}
+ sx={{ mt: 2 }}
>
{testing ? '测试中...' : '开始测试'}
+
+ {testResult && (
+
+ )}
- {/* 测试结果 */}
-
-
+ {/* 分页组件 */}
+ {testResult && testResult.total > 10 && (
+
+
+
+ )}
-
- {/* 返回按钮 */}
-
-
-
-
- {/* 消息提示 */}
-
-
- {snackbar.message}
-
-
);
-}
+};
export default KnowledgeBaseTesting;
\ No newline at end of file