feat: 知识库设置页面增加 retrieval_model 检索配置功能
1. 召回测试页面增加 Score 阈值参数配置 2. 知识库设置页面新增检索模型配置: - 检索方式 (向量/全文/混合/关键字检索) - Reranking 模型 (默认开启,不可关闭) - Top K 返回数量 - Score 阈值 (默认开启,可调节数值) 3. 修复 Dify API 字段名问题 (retrieval_model_dict) 4. 优化数据加载流程,使用详情接口获取完整配置 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,18 @@
|
||||
import { Form, Input, Button, Card, Spin } from 'antd';
|
||||
import { SaveOutlined } from '@ant-design/icons';
|
||||
import { useDatasetSettings } from '~/hooks/dify-dataset-manager/dataset-settings';
|
||||
import { Form, Input, Button, Card, Spin, Divider, Select, Slider, InputNumber, Tooltip, Checkbox } from 'antd';
|
||||
import { SaveOutlined, QuestionCircleOutlined, CheckCircleFilled } from '@ant-design/icons';
|
||||
import { useDatasetSettings, type SearchMethod } from '~/hooks/dify-dataset-manager/dataset-settings';
|
||||
import type { DatasetSettingsProps } from '~/types/dify-dataset-manager/dataset-settings';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
// 检索方式选项
|
||||
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: '精确关键字匹配' },
|
||||
];
|
||||
|
||||
/**
|
||||
* 知识库设置组件
|
||||
* 用于修改知识库名称和描述
|
||||
@@ -14,15 +22,23 @@ export default function DatasetSettings({
|
||||
onDatasetUpdated,
|
||||
}: DatasetSettingsProps) {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
|
||||
const {
|
||||
saving,
|
||||
hasChanges,
|
||||
retrievalSettings,
|
||||
handleValuesChange,
|
||||
handleSave,
|
||||
handleReset,
|
||||
updateRetrievalSettings,
|
||||
} = useDatasetSettings(dataset, form, onDatasetUpdated);
|
||||
|
||||
// 是否需要显示 Reranking 提示(语义检索和混合检索需要,且强制开启)
|
||||
const showRerankingInfo = retrievalSettings.searchMethod === 'semantic_search' || retrievalSettings.searchMethod === 'hybrid_search';
|
||||
// 权重设置:由于 Reranking 强制开启,混合检索时由 Reranking 模型决定排序,不需要手动设置权重
|
||||
// 所以这里始终不显示权重设置
|
||||
const showWeightsOption = false;
|
||||
|
||||
if (!dataset) {
|
||||
return (
|
||||
<div className="settings-loading">
|
||||
@@ -81,12 +97,6 @@ export default function DatasetSettings({
|
||||
{dataset.indexing_technique === 'high_quality' ? '高质量' : '经济'}
|
||||
</span>
|
||||
</div>
|
||||
{/* <div className="info-item">
|
||||
<span className="info-label">Embedding 模型:</span>
|
||||
<span className="info-value">
|
||||
{dataset.embedding_model || '默认模型'}
|
||||
</span>
|
||||
</div> */}
|
||||
<div className="info-item">
|
||||
<span className="info-label">文档数量:</span>
|
||||
<span className="info-value">{dataset.document_count}</span>
|
||||
@@ -102,24 +112,167 @@ export default function DatasetSettings({
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div className="form-actions">
|
||||
<Button onClick={handleReset} disabled={!hasChanges}>
|
||||
重置
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SaveOutlined />}
|
||||
onClick={handleSave}
|
||||
loading={saving}
|
||||
disabled={!hasChanges}
|
||||
>
|
||||
保存
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</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}>
|
||||
重置
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SaveOutlined />}
|
||||
onClick={handleSave}
|
||||
loading={saving}
|
||||
disabled={!hasChanges}
|
||||
>
|
||||
保存
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SearchOutlined, FileSearchOutlined } from '@ant-design/icons';
|
||||
import { Button, Tag, Input, Slider, Spin, Select, Flex } from 'antd';
|
||||
import { Button, Tag, Input, Slider, Spin, Select, Flex, Switch, InputNumber, Tooltip } from 'antd';
|
||||
import type { RetrieveRecord } from '~/api/dify-dataset/type';
|
||||
import { useRetrieveTest } from '~/hooks/dify-dataset-manager/retrieve-test';
|
||||
import type { RetrieveTestProps } from '~/types/dify-dataset-manager/retrieve-test';
|
||||
@@ -97,6 +97,10 @@ export default function RetrieveTest({ datasetId }: RetrieveTestProps) {
|
||||
setSearchMethod,
|
||||
topK,
|
||||
setTopK,
|
||||
scoreThresholdEnabled,
|
||||
setScoreThresholdEnabled,
|
||||
scoreThreshold,
|
||||
setScoreThreshold,
|
||||
handleRetrieve,
|
||||
} = useRetrieveTest(datasetId);
|
||||
|
||||
@@ -229,6 +233,46 @@ export default function RetrieveTest({ datasetId }: RetrieveTestProps) {
|
||||
{topK}
|
||||
</span>
|
||||
</Flex>
|
||||
|
||||
{/* Score 阈值设置 */}
|
||||
<Flex align="center" gap={12}>
|
||||
<Tooltip title="开启后,只返回相似度分数高于阈值的结果">
|
||||
<span style={{
|
||||
fontSize: 13,
|
||||
color: colors.textSecondary,
|
||||
whiteSpace: 'nowrap',
|
||||
cursor: 'help',
|
||||
}}>
|
||||
Score 阈值:
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Switch
|
||||
size="small"
|
||||
checked={scoreThresholdEnabled}
|
||||
onChange={setScoreThresholdEnabled}
|
||||
/>
|
||||
{scoreThresholdEnabled && (
|
||||
<>
|
||||
<Slider
|
||||
value={scoreThreshold}
|
||||
onChange={setScoreThreshold}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.01}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<InputNumber
|
||||
value={scoreThreshold}
|
||||
onChange={(value) => setScoreThreshold(value ?? 0.5)}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.01}
|
||||
size="small"
|
||||
style={{ width: 70 }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user