feat(intercept): 未命中规则的命令接入 LLM 兜底审批

在正则/字符串规则之外新增一套独立的模型兜底层:规则命中直接按规则走;
一条规则都没命中时,才由模型做语义判定(ALLOW/ASK/DENY),人工审批兜底。

- intercept: DefaultJudgePrompt(三态判定提示词)+ ParseVerdict;Reviewer 回调注入;
  Judge() 读全局 llm_judge_* 配置、超时/失败回退、ask 填审批超时、模型判定加 [模型] 前缀
- guard: applyIntercept 未命中分支调 Judge;模型 allow 不落库,deny/ask 带 reason 入库
- db: intercept_pending 加 reason 列(带 ALTER IF NOT EXISTS 补旧库);
  create 函数支持 reason 与 ruleID 0→NULL;历史查询带出 reason
- server: 用 providerForProfile + 单次 Stream 收集注入裁判;GET/PUT /api/intercept/judge
- web: 拦截页拆「拦截规则 / 模型配置」两 tab;拦截范围提为共用信息条;
  模型配置双栏布局(提示词直接展开编辑);审批/任务拦截页标注「模型判定」+ 理由

配置默认值:失败回退 allow、审批超时动作 deny、模型 allow 不落库、
提示词默认即内置模板(清空回退模板)。无需 DB 结构性迁移(judge 走 settings KV)。

Claude-Session: https://claude.ai/code/session_019QXSCDmzimwnjntBNCgUD7
This commit is contained in:
Autumn-27 committed 2026-08-23 10:02:43 -04:00
1 parent f63a451b87
commit 95d2309935
14 files changed
+860 -66

No files matched your search

+3
View File
@@ -264,6 +264,7 @@ func New(ctx context.Context, m *Manager, skillDir string, dataDir string, keyDi
}
}
go s.reconcileConcurrency()
s.wireInterceptReviewer() // LLM 兜底审批:未命中拦截规则的命令交给模型判定
return s
}
@@ -831,6 +832,8 @@ func (s *Server) Handler() http.Handler {
mux.HandleFunc("GET /api/intercept/task/{taskID}", s.interceptListTaskItems)
mux.HandleFunc("GET /api/intercept/tool-config", s.interceptGetToolConfig)
mux.HandleFunc("PUT /api/intercept/tool-config", s.interceptSetToolConfig)
mux.HandleFunc("GET /api/intercept/judge", s.interceptGetJudgeConfig)
mux.HandleFunc("PUT /api/intercept/judge", s.interceptSetJudgeConfig)
// /api/* goes through CORS + JWT; everything else is served by the embedded
// frontend (public — auth is enforced client-side and on the API). With the