From 1c864f0de1d75074b4f2a90827cb40c5bb3c16a2 Mon Sep 17 00:00:00 2001 From: WangDL Date: Sun, 24 May 2026 13:42:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20M2-08=20admin=20=E2=80=94=20KnowledgeOp?= =?UTF-8?q?s=20page=20(candidates,=20chunks,=20RAG=20debug)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- src/App.tsx | 2 + src/config/menu.tsx | 1 + src/pages/KnowledgeOps.tsx | 90 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 src/pages/KnowledgeOps.tsx diff --git a/src/App.tsx b/src/App.tsx index cda76bc..eaf6202 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -30,6 +30,7 @@ const Dashboard = lazy(() => import('./pages/Dashboard')) const UserManagement = lazy(() => import('./pages/UserManagement')) const MemberManagement = lazy(() => import('./pages/MemberManagement')) const ImportMonitorPage = lazy(() => import('./pages/ImportMonitor')) +const KnowledgeOpsPage = lazy(() => import('./pages/KnowledgeOps')) const HermesSettings = lazy(() => import('./pages/HermesSettings')) const TaskAssistant = lazy(() => import('./pages/TaskAssistant')) const Placeholder = lazy(() => import('./pages/Placeholder')) @@ -100,6 +101,7 @@ function App() { /> }>} /> } /> + }>} /> }>} /> }>} /> , children: [ { path: '/knowledge/bases', name: '知识库列表' }, { path: '/knowledge/sources', name: '知识源列表' }, + { path: '/knowledge/ops', name: '知识运维' }, ]}, { path: '/imports', name: '文档导入', icon: }, { path: '/files', name: '文件与 COS', icon: }, diff --git a/src/pages/KnowledgeOps.tsx b/src/pages/KnowledgeOps.tsx new file mode 100644 index 0000000..5e8c5d2 --- /dev/null +++ b/src/pages/KnowledgeOps.tsx @@ -0,0 +1,90 @@ +import { useState } from 'react' +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { Table, Tag, Typography, Button, App, Tabs, Input, Card, Space } from 'antd' +import { ReloadOutlined, SearchOutlined, ExperimentOutlined } from '@ant-design/icons' +import { api } from '@/services/http-client' +import dayjs from 'dayjs' + +const { Title, Text } = Typography + +export default function KnowledgeOpsPage() { + const qc = useQueryClient() + const { message } = App.useApp() + const [sourceId, setSourceId] = useState('') + const [debugQuery, setDebugQuery] = useState('') + + const { data: candidates } = useQuery({ + queryKey: ['ops', 'candidates'], + queryFn: (): Promise => api.get('/admin-api/knowledge-bases/candidates?limit=50'), + }) + + const { data: chunks } = useQuery({ + queryKey: ['ops', 'chunks', sourceId], + queryFn: (): Promise => api.get(`/admin-api/knowledge-bases/chunks?sourceId=${sourceId}&limit=50`), + enabled: !!sourceId, + }) + + const handleDebugSearch = async () => { + if (!debugQuery) return + const res: any = await api.post('/admin-api/vector/debug-search', { query: debugQuery }) + message.info(JSON.stringify(res.data)) + } + + const candidateCols = [ + { title: '标题', dataIndex: 'title', width: 200, ellipsis: true }, + { title: '用户', dataIndex: 'userId', width: 120, ellipsis: true }, + { title: '状态', dataIndex: 'status', width: 80, render: (s: string) => {s} }, + { title: '置信度', dataIndex: 'confidence', width: 80, align: 'center' as const, render: (v: any) => v ? `${(Number(v)*100).toFixed(0)}%` : '-' }, + { title: '难度', dataIndex: 'difficulty', width: 70 }, + { title: '时间', dataIndex: 'createdAt', width: 130, render: (d: string) => dayjs(d).format('MM-DD HH:mm') }, + ] + + const chunkCols = [ + { title: '#', dataIndex: 'chunkIndex', width: 50 }, + { title: '内容', dataIndex: 'content', ellipsis: true, render: (v: string) => {v?.slice(0, 150)} }, + { title: '页', dataIndex: 'pageNumber', width: 50 }, + { title: '章节', dataIndex: 'sectionTitle', width: 150, ellipsis: true }, + { title: 'Tokens', dataIndex: 'tokenCount', width: 70, align: 'center' as const }, + { title: 'Embedding', dataIndex: 'embeddingStatus', width: 80, render: (s: string) => {s} }, + ] + + return ( +
+
+ <ExperimentOutlined /> 知识运维 + +
+ + , + }, + { + key: 'chunks', label: 'Chunk 管理', + children: ( + <> + + setSourceId(e.target.value)} onSearch={v => setSourceId(v)} enterButton={} style={{ width: 300 }} /> + + {sourceId && } + + ), + }, + { + key: 'rag-debug', label: 'RAG 调试', + children: ( + + + setDebugQuery(e.target.value)} onSearch={handleDebugSearch} enterButton="检索" style={{ width: 400 }} /> + +
+ 输入查询文本后,将显示检索参数和结果(完整 RAG 管道在 M3 阶段完善)。 +
+
+ ), + }, + ]} /> + + ) +}