Add matcher mode selection to RuleManagement component
This commit is contained in:
@@ -28,6 +28,16 @@ import { generateEventId } from '../rule-id';
|
||||
import LlmConfiguration from './LlmConfiguration';
|
||||
|
||||
const MODEL_EVENT_ID_STORAGE_KEY = 'asr-monitor:model-event-ids';
|
||||
const MATCHER_MODE_STORAGE_KEY = 'asr-monitor:matcher-mode';
|
||||
|
||||
type MatcherMode = 'keyword' | 'llm';
|
||||
|
||||
function loadMatcherMode(): MatcherMode {
|
||||
if (typeof window === 'undefined') return 'keyword';
|
||||
return window.localStorage.getItem(MATCHER_MODE_STORAGE_KEY) === 'llm'
|
||||
? 'llm'
|
||||
: 'keyword';
|
||||
}
|
||||
|
||||
function loadModelEventIds(): Record<string, string> {
|
||||
if (typeof window === 'undefined') return {};
|
||||
@@ -81,11 +91,23 @@ export default function RuleManagement({
|
||||
const [formErrors, setFormErrors] = useState<Record<string, string>>({});
|
||||
const [excelAction, setExcelAction] = useState<'template' | 'import' | 'export' | null>(null);
|
||||
const [modelEventIds, setModelEventIds] = useState<Record<string, string>>(loadModelEventIds);
|
||||
const [matcherMode, setMatcherMode] = useState<MatcherMode>(loadMatcherMode);
|
||||
|
||||
// Tag editor input ref
|
||||
const tagInputRef = useRef<HTMLInputElement>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleMatcherModeChange = (mode: MatcherMode) => {
|
||||
setMatcherMode(mode);
|
||||
window.localStorage.setItem(MATCHER_MODE_STORAGE_KEY, mode);
|
||||
addToast(
|
||||
mode === 'keyword'
|
||||
? '已切换为关键词匹配,大模型配置已隐藏'
|
||||
: '已切换为大模型事件匹配',
|
||||
'info',
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setModelEventIds(current => {
|
||||
const next = { ...current };
|
||||
@@ -412,10 +434,57 @@ export default function RuleManagement({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 2. LLM matcher configuration */}
|
||||
<LlmConfiguration enabledRuleCount={activeRulesCount} addToast={addToast} />
|
||||
{/* 2. Matcher selection */}
|
||||
<section className="flex flex-col gap-3 rounded-md border border-slate-200 bg-white px-5 py-4 shadow-3xs sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-xs font-bold text-slate-900">匹配方式</h2>
|
||||
<span className={`rounded px-2 py-0.5 text-[9px] font-bold uppercase tracking-widest ${
|
||||
matcherMode === 'keyword'
|
||||
? 'bg-blue-50 text-blue-700'
|
||||
: 'bg-violet-50 text-violet-700'
|
||||
}`}>
|
||||
{matcherMode === 'keyword' ? 'Keyword' : 'LLM'}
|
||||
</span>
|
||||
</div>
|
||||
<p className="mt-1 text-[11px] leading-5 text-slate-400">
|
||||
{matcherMode === 'keyword'
|
||||
? '按照规则库中的监控关键词进行确定性匹配,不加载模型和提示词配置。'
|
||||
: '将启用规则与通话上下文注入提示词,由大模型返回命中的短事件 ID。'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 3. Searching & Filter Options Bar */}
|
||||
<label className="flex shrink-0 items-center gap-2">
|
||||
<span className="text-[10px] font-bold uppercase tracking-widest text-slate-400">
|
||||
当前算法
|
||||
</span>
|
||||
<select
|
||||
value={matcherMode}
|
||||
onChange={(event) => handleMatcherModeChange(event.target.value as MatcherMode)}
|
||||
className="min-w-44 rounded-md border border-slate-200 bg-white px-3 py-2 text-xs font-bold text-slate-800 outline-none transition focus:border-slate-900"
|
||||
>
|
||||
<option value="keyword">关键词匹配</option>
|
||||
<option value="llm">大模型事件匹配</option>
|
||||
</select>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
{/* 3. LLM matcher configuration */}
|
||||
<AnimatePresence initial={false}>
|
||||
{matcherMode === 'llm' && (
|
||||
<motion.div
|
||||
key="llm-configuration"
|
||||
initial={{ opacity: 0, y: -8 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -8 }}
|
||||
transition={{ duration: 0.16, ease: 'easeOut' }}
|
||||
>
|
||||
<LlmConfiguration enabledRuleCount={activeRulesCount} addToast={addToast} />
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
{/* 4. Searching & Filter Options Bar */}
|
||||
<div className="flex flex-col space-y-3 rounded-md border border-slate-200 bg-white p-4 shadow-3xs sm:flex-row sm:items-center sm:space-y-0 sm:space-x-4">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute top-2.5 left-3 h-4 w-4 text-slate-400" />
|
||||
@@ -439,7 +508,7 @@ export default function RuleManagement({
|
||||
|
||||
</div>
|
||||
|
||||
{/* 4. High Density Data Table */}
|
||||
{/* 5. High Density Data Table */}
|
||||
<div className="overflow-hidden rounded-md border border-slate-200 bg-white shadow-3xs">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full border-collapse text-left text-xs text-slate-505">
|
||||
@@ -571,7 +640,7 @@ export default function RuleManagement({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 5. Sliding Right Drawer for Creating/Editing Rules */}
|
||||
{/* 6. Sliding Right Drawer for Creating/Editing Rules */}
|
||||
<AnimatePresence>
|
||||
{isDrawerOpen && (
|
||||
<>
|
||||
@@ -599,7 +668,11 @@ export default function RuleManagement({
|
||||
{editingRule ? '编辑特征规则' : '新增特征规则'}
|
||||
</h2>
|
||||
<p className="text-[11px] text-slate-400 mt-0.5">
|
||||
{editingRule ? `UUID: ${formId}` : 'UUID 负责系统关联,事件 ID 供大模型输出。'}
|
||||
{editingRule
|
||||
? `UUID: ${formId}`
|
||||
: matcherMode === 'llm'
|
||||
? 'UUID 负责系统关联,事件 ID 供大模型输出。'
|
||||
: 'UUID 负责系统关联,事件 ID 作为接口侧业务标识。'}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
@@ -663,7 +736,9 @@ export default function RuleManagement({
|
||||
</p>
|
||||
) : (
|
||||
<p className="mt-1 text-[10px] leading-4 text-slate-400">
|
||||
提示词会把它注入规则清单,大模型只需返回该短 ID;后端再通过 UUID 还原事件名称。建议简短、稳定且创建后不修改。
|
||||
{matcherMode === 'llm'
|
||||
? '提示词会把它注入规则清单,大模型只需返回该短 ID;后端再通过 UUID 还原事件名称。建议简短、稳定且创建后不修改。'
|
||||
: '关键词命中后接口使用该短 ID 标识事件,UUID 继续负责内部关联。建议简短、稳定且创建后不修改。'}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -744,9 +819,20 @@ export default function RuleManagement({
|
||||
<div className="rounded border border-slate-200 bg-slate-50 p-3 text-xs text-slate-500 flex items-start space-x-2">
|
||||
<Info className="h-4 w-4 text-slate-600 shrink-0 mt-0.5" />
|
||||
<div className="space-y-1 font-sans">
|
||||
<p className="font-bold text-slate-700">过滤提取逻辑须知</p>
|
||||
<p>1. 只要 ASR 语句文本中「完整包含」此处设定的任一触发词,即瞬时抛出预警卡片。</p>
|
||||
<p>2. 特征词需精简准确,避免设置过短或通用的词(例如“有”、“去”),以免大面积噪点干扰。</p>
|
||||
<p className="font-bold text-slate-700">
|
||||
{matcherMode === 'llm' ? '大模型规则说明' : '关键词匹配逻辑须知'}
|
||||
</p>
|
||||
{matcherMode === 'llm' ? (
|
||||
<>
|
||||
<p>1. 关键词作为事件参考表达注入提示词,模型无需逐字命中。</p>
|
||||
<p>2. 规则名称和关键词应能清楚表达事件边界,模型最终只返回上方短事件 ID。</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p>1. 只要 ASR 语句文本中「完整包含」此处设定的任一触发词,即瞬时抛出预警卡片。</p>
|
||||
<p>2. 特征词需精简准确,避免设置过短或通用的词(例如“有”、“去”),以免大面积噪点干扰。</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user