5bee9288b9
1、修复了若干无权限时的失败提示语 2、新增了一个生成后续建议问题的功能 3、重构了知识问答部分的权限管理模块 4、修复了若干渲染不恰当的样式渲染
270 lines
13 KiB
TypeScript
270 lines
13 KiB
TypeScript
import { CheckCircleFilled, QuestionCircleOutlined, SaveOutlined } from '@ant-design/icons';
|
|
import { Button, Card, Checkbox, Descriptions, Divider, InputNumber, Select, Slider, Spin, Tag, Tooltip } from 'antd';
|
|
import { useDatasetSettings, type SearchMethod } from '~/hooks/dify-dataset-manager/dataset-settings';
|
|
import type { DatasetSettingsProps } from '~/types/dify-dataset-manager/dataset-settings';
|
|
import { usePermission } from '~/hooks/usePermission';
|
|
|
|
// 检索方式选项
|
|
const SEARCH_METHOD_OPTIONS: { label: string; value: SearchMethod; description: string }[] = [
|
|
{ label: '向量检索', value: 'semantic_search', description: '基于语义理解的智能检索,适合需要理解上下文的场景' },
|
|
{ label: '全文检索', value: 'full_text_search', description: '基于关键词匹配的传统检索方式' },
|
|
{ label: '混合检索', value: 'hybrid_search', description: '结合向量和全文检索,综合效果最佳' },
|
|
{ label: '关键字检索', value: 'keyword_search', description: '精确关键字匹配' },
|
|
];
|
|
|
|
/**
|
|
* 知识库设置组件
|
|
* 使用 Descriptions 展示只读的知识库基本信息,提供可编辑的检索设置
|
|
* 注:Dify API 不支持修改知识库名称和描述,故这些字段仅作只读展示
|
|
*/
|
|
export default function DatasetSettings({
|
|
dataset,
|
|
onDatasetUpdated,
|
|
canEditDataset = true,
|
|
}: DatasetSettingsProps) {
|
|
const {
|
|
saving,
|
|
hasChanges,
|
|
retrievalSettings,
|
|
handleSave,
|
|
handleReset,
|
|
updateRetrievalSettings,
|
|
} = useDatasetSettings(dataset, onDatasetUpdated);
|
|
|
|
const { hasPermission } = usePermission();
|
|
const canWrite = hasPermission('dify:settings:write') && canEditDataset;
|
|
|
|
// 是否需要显示 Reranking 提示(语义检索和混合检索需要,且强制开启)
|
|
const showRerankingInfo = retrievalSettings.searchMethod === 'semantic_search' || retrievalSettings.searchMethod === 'hybrid_search';
|
|
// 权重设置:由于 Reranking 强制开启,混合检索时由 Reranking 模型决定排序,不需要手动设置权重
|
|
// 所以这里始终不显示权重设置
|
|
const showWeightsOption = false;
|
|
|
|
if (!dataset) {
|
|
return (
|
|
<div className="settings-loading">
|
|
<Spin size="large" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="dataset-settings-page">
|
|
{/* 页面标题 */}
|
|
<div className="page-header">
|
|
<h1>设置</h1>
|
|
<p className="page-description">
|
|
管理知识库的基本信息和检索配置
|
|
</p>
|
|
</div>
|
|
|
|
{/* 知识库基本信息 */}
|
|
<Card
|
|
className="settings-card"
|
|
title={
|
|
<span style={{ fontSize: 16, fontWeight: 500 }}>
|
|
知识库信息
|
|
</span>
|
|
}
|
|
>
|
|
<Descriptions
|
|
column={{ xs: 1, sm: 2, md: 2, lg: 3 }}
|
|
labelStyle={{
|
|
color: '#8c8c8c',
|
|
fontWeight: 400,
|
|
}}
|
|
contentStyle={{
|
|
color: '#262626',
|
|
fontWeight: 500,
|
|
}}
|
|
>
|
|
<Descriptions.Item label="知识库名称" span={3}>
|
|
<span style={{ fontSize: 15 }}>{dataset.name}</span>
|
|
</Descriptions.Item>
|
|
{dataset.description && (
|
|
<Descriptions.Item label="描述" span={3}>
|
|
<span style={{ color: '#595959' }}>{dataset.description}</span>
|
|
</Descriptions.Item>
|
|
)}
|
|
<Descriptions.Item label="索引方式">
|
|
<Tag color={dataset.indexing_technique === 'high_quality' ? 'green' : 'blue'}>
|
|
{dataset.indexing_technique === 'high_quality' ? '高质量' : '经济'}
|
|
</Tag>
|
|
</Descriptions.Item>
|
|
<Descriptions.Item label="文档数量">
|
|
<span style={{ fontFamily: 'monospace' }}>{dataset.document_count}</span>
|
|
</Descriptions.Item>
|
|
<Descriptions.Item label="总字符数">
|
|
<span style={{ fontFamily: 'monospace' }}>{dataset.word_count?.toLocaleString() || 0}</span>
|
|
</Descriptions.Item>
|
|
<Descriptions.Item label="创建时间" span={2}>
|
|
{new Date(dataset.created_at * 1000).toLocaleString('zh-CN')}
|
|
</Descriptions.Item>
|
|
<Descriptions.Item label="最后更新">
|
|
{new Date(dataset.updated_at * 1000).toLocaleString('zh-CN')}
|
|
</Descriptions.Item>
|
|
</Descriptions>
|
|
</Card>
|
|
|
|
{/* 检索设置卡片 */}
|
|
<Card className="settings-card" style={{ marginTop: 16 }}>
|
|
<h3 style={{ marginBottom: 16, fontSize: 16, fontWeight: 500 }}>
|
|
检索设置
|
|
<Tooltip title="配置知识库的默认检索参数,影响召回效果">
|
|
<QuestionCircleOutlined style={{ marginLeft: 8, color: '#8c8c8c', fontSize: 14 }} />
|
|
</Tooltip>
|
|
</h3>
|
|
|
|
{/* 检索方式 */}
|
|
<div className="setting-item" style={{ marginBottom: 16 }}>
|
|
<label style={{ display: 'block', marginBottom: 8, fontWeight: 500 }}>
|
|
检索方式
|
|
</label>
|
|
<Select
|
|
value={retrievalSettings.searchMethod}
|
|
onChange={(value) => updateRetrievalSettings('searchMethod', value)}
|
|
style={{ width: '100%' }}
|
|
options={SEARCH_METHOD_OPTIONS.map(opt => ({
|
|
value: opt.value,
|
|
label: (
|
|
<div>
|
|
<span>{opt.label}</span>
|
|
<span style={{ color: '#8c8c8c', fontSize: 12, marginLeft: 8 }}>
|
|
{opt.description}
|
|
</span>
|
|
</div>
|
|
),
|
|
}))}
|
|
/>
|
|
</div>
|
|
|
|
{/* Reranking 设置(语义检索和混合检索时显示,默认开启不可关闭) */}
|
|
{showRerankingInfo && (
|
|
<div className="setting-item" style={{ marginBottom: 16 }}>
|
|
<Checkbox
|
|
checked={true}
|
|
disabled={true}
|
|
>
|
|
<span style={{ color: '#262626' }}>
|
|
启用 Reranking 模型
|
|
<CheckCircleFilled style={{ marginLeft: 6, color: '#52c41a', fontSize: 12 }} />
|
|
</span>
|
|
<Tooltip title="Reranking 模型已默认开启,用于对检索结果进行重新排序,提高相关性">
|
|
<QuestionCircleOutlined style={{ marginLeft: 4, color: '#8c8c8c' }} />
|
|
</Tooltip>
|
|
</Checkbox>
|
|
</div>
|
|
)}
|
|
|
|
{/* 混合检索权重设置 */}
|
|
{showWeightsOption && (
|
|
<div className="setting-item" style={{ marginBottom: 16 }}>
|
|
<label style={{ display: 'block', marginBottom: 8, fontWeight: 500 }}>
|
|
语义权重
|
|
<Tooltip title="混合检索中语义检索的权重,值越大语义检索占比越高">
|
|
<QuestionCircleOutlined style={{ marginLeft: 4, color: '#8c8c8c' }} />
|
|
</Tooltip>
|
|
</label>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
<span style={{ fontSize: 12, color: '#8c8c8c' }}>关键词</span>
|
|
<Slider
|
|
value={retrievalSettings.weights}
|
|
onChange={(value) => updateRetrievalSettings('weights', value)}
|
|
min={0}
|
|
max={1}
|
|
step={0.1}
|
|
style={{ flex: 1 }}
|
|
/>
|
|
<span style={{ fontSize: 12, color: '#8c8c8c' }}>语义</span>
|
|
<InputNumber
|
|
value={retrievalSettings.weights}
|
|
onChange={(value) => updateRetrievalSettings('weights', value ?? 0.7)}
|
|
min={0}
|
|
max={1}
|
|
step={0.1}
|
|
size="small"
|
|
style={{ width: 70 }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<Divider />
|
|
|
|
{/* Top K 设置 */}
|
|
<div className="setting-item" style={{ marginBottom: 16 }}>
|
|
<label style={{ display: 'block', marginBottom: 8, fontWeight: 500 }}>
|
|
返回数量 (Top K)
|
|
<Tooltip title="每次检索返回的最大结果数量">
|
|
<QuestionCircleOutlined style={{ marginLeft: 4, color: '#8c8c8c' }} />
|
|
</Tooltip>
|
|
</label>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
<Slider
|
|
value={retrievalSettings.topK}
|
|
onChange={(value) => updateRetrievalSettings('topK', value)}
|
|
min={1}
|
|
max={20}
|
|
style={{ flex: 1 }}
|
|
/>
|
|
<InputNumber
|
|
value={retrievalSettings.topK}
|
|
onChange={(value) => updateRetrievalSettings('topK', value ?? 3)}
|
|
min={1}
|
|
max={20}
|
|
size="small"
|
|
style={{ width: 70 }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Score 阈值设置(默认开启不可关闭,但可调节数值) */}
|
|
<div className="setting-item" style={{ marginBottom: 16 }}>
|
|
<label style={{ display: 'block', marginBottom: 8, fontWeight: 500 }}>
|
|
Score 阈值
|
|
<CheckCircleFilled style={{ marginLeft: 6, color: '#52c41a', fontSize: 12 }} />
|
|
<Tooltip title="Score 阈值已默认开启,只返回相似度分数高于阈值的结果,过滤低质量匹配">
|
|
<QuestionCircleOutlined style={{ marginLeft: 4, color: '#8c8c8c' }} />
|
|
</Tooltip>
|
|
</label>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
<Slider
|
|
value={retrievalSettings.scoreThreshold}
|
|
onChange={(value) => updateRetrievalSettings('scoreThreshold', value)}
|
|
min={0}
|
|
max={1}
|
|
step={0.01}
|
|
style={{ flex: 1 }}
|
|
/>
|
|
<InputNumber
|
|
value={retrievalSettings.scoreThreshold}
|
|
onChange={(value) => updateRetrievalSettings('scoreThreshold', value ?? 0.5)}
|
|
min={0}
|
|
max={1}
|
|
step={0.01}
|
|
size="small"
|
|
style={{ width: 70 }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 操作按钮 */}
|
|
<div className="form-actions" style={{ marginTop: 24, display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
|
|
<Button onClick={handleReset} disabled={!hasChanges || !canWrite}>
|
|
重置
|
|
</Button>
|
|
<Button
|
|
type="primary"
|
|
icon={<SaveOutlined />}
|
|
onClick={handleSave}
|
|
loading={saving}
|
|
disabled={!hasChanges || !canWrite}
|
|
>
|
|
保存
|
|
</Button>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|