Files
leaudit-platform-frontend/app/components/dify-dataset-manager/dataset-settings.tsx
T
TanWenyan d53742948d 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>
2025-12-05 22:07:16 +08:00

279 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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: '精确关键字匹配' },
];
/**
* 知识库设置组件
* 用于修改知识库名称和描述
*/
export default function DatasetSettings({
dataset,
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">
<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">
<Form
form={form}
layout="vertical"
onValuesChange={handleValuesChange}
>
<Form.Item
name="name"
label="知识库名称"
rules={[
{ required: true, message: '请输入知识库名称' },
{ max: 100, message: '名称不能超过100个字符' },
]}
>
<Input placeholder="请输入知识库名称" maxLength={100} />
</Form.Item>
<Form.Item
name="description"
label="知识库描述"
extra="描述知识库的用途和内容(仅展示,暂不支持修改)"
>
<TextArea
placeholder="请输入知识库描述"
rows={4}
maxLength={500}
showCount
disabled
/>
</Form.Item>
{/* 只读信息 */}
<div className="readonly-info">
<div className="info-item">
<span className="info-label"></span>
<span className="info-value">
{dataset.indexing_technique === 'high_quality' ? '高质量' : '经济'}
</span>
</div>
<div className="info-item">
<span className="info-label"></span>
<span className="info-value">{dataset.document_count}</span>
</div>
<div className="info-item">
<span className="info-label"></span>
<span className="info-value">{dataset.word_count?.toLocaleString()}</span>
</div>
<div className="info-item">
<span className="info-label"></span>
<span className="info-value">
{new Date(dataset.created_at * 1000).toLocaleString('zh-CN')}
</span>
</div>
</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>
);
}