Files
Agent-System/internal/intent/recognizer_helpers.go
T
WeiCHEN a5ea8197ab fix(coordinator): force Tavily search for weather/realtime dialogue answers
Anchor weather and other realtime questions on runner /v1/search results (or an explicit refuse constraint) so the model cannot invent dates or temperatures from training memory.
2026-09-23 14:51:09 +08:00

204 lines
6.6 KiB
Go
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.
package intent
import (
"regexp"
"strings"
"unicode/utf8"
)
// isSecurityViolation 检查是否命中敏感安全与权限红线
func isSecurityViolation(input string) bool {
lower := strings.ToLower(input)
patterns := []string{
"修改身份", "身份修改", "修改职位", "职位修改", "修改岗位", "岗位修改",
"修改姓名", "姓名修改", "修改名字", "名字修改",
"修改部门", "部门修改",
"修改资质", "资质修改",
"改成批准人", "改成审核人", "改成编制人",
"工号改成", "修改工号", "工号修改",
"修改设置", "修改配置", "系统设置", "系统配置",
"修改网关", "网关配置", "网关密钥", "大模型网关", "模型网关",
"修改密码", "重置密码", "提升权限", "越权",
"修改向量库", "向量库连接", "向量库配置", "修改向量库连接",
"修改知识库连接", "知识库连接", "知识库配置",
}
for _, p := range patterns {
if strings.Contains(lower, p) {
return true
}
}
secRegexes := []string{
`把.*(?:身份|职位|角色|工号|权限|姓名|名字|部门|资质).*(?:修改|改|换|重置|设为|变为)`,
`(?:修改|改|换|重置|设为).*(?:我的)?(?:身份|职位|角色|工号|权限|姓名|名字|部门|资质|网关|向量库|知识库连接|密钥|apikey|api_key)`,
`.*(?:向量库连接|修改向量库|知识库连接|大模型网关密钥|网关密钥).*`,
}
for _, r := range secRegexes {
if matched, _ := regexp.MatchString(r, input); matched {
return true
}
}
return false
}
// isPureUICommand 检查是否为纯前端UI视窗布局与主题控制指令
func isPureUICommand(input string) bool {
patterns := []string{
"打开工具栏", "关闭工具栏",
"折叠侧边栏", "展开侧边栏", "隐藏侧边栏", "显示侧边栏",
"折叠左侧栏", "展开左侧栏", "折叠右侧栏", "展开右侧栏",
"折叠工作台", "展开工作台",
"切换全屏", "全屏显示", "退出全屏",
"打开控制台", "关闭控制台",
"切换主题", "换成深色", "换成浅色",
"打开抽屉", "展开抽屉", "关闭抽屉",
"深色模式", "浅色模式", "深色主题", "浅色主题",
}
for _, p := range patterns {
if strings.Contains(input, p) {
return true
}
}
return false
}
// resolveTargetDoc 优先从输入中提取显式受控文档名称,提取不到时回退至上下文的 ActiveDocID
func resolveTargetDoc(input string, userCtx *ContextSnapshot) string {
if doc := extractDocName(input); doc != "" {
return doc
}
if userCtx != nil && userCtx.ActiveDocID != "" {
return userCtx.ActiveDocID
}
return ""
}
// isRejectReasonQuery 判定是否为询问驳回/退回原因(而非发起驳回执行)
func isRejectReasonQuery(input string) bool {
markers := []string{
"原因", "为何", "为什么", "怎么回事", "什么情况", "什么意见",
"哪点", "哪些问题", "整改", "意见是什么", "理由", "怎么改", "如何修改",
"查一下驳回", "看看驳回",
}
for _, m := range markers {
if strings.Contains(input, m) {
return true
}
}
return strings.Contains(input, "被驳回")
}
// extractFactSlots 从文本中提取结构化事实槽位 (支持补丁合并与补充信息)
func extractFactSlots(input string) map[string]string {
slots := make(map[string]string)
marketPrefixes := []string{
"目标市场不仅是", "目标市场不仅有", "目标市场还包括", "目标市场确定为", "目标市场确定是",
"目标市场改成", "目标市场改为", "目标市场是", "目标市场为",
"市场改成", "市场改为", "市场是", "市场为", "目标市场除了国内还想考虑",
}
for _, p := range marketPrefixes {
if idx := strings.Index(input, p); idx != -1 {
val := strings.TrimSpace(input[idx+len(p):])
val = strings.Trim(val, "::,,。 ")
if val != "" {
slots["target_market"] = val
slots[SlotFactKey] = "target_market"
slots[SlotFactValue] = val
break
}
}
}
budgetPrefixes := []string{"预算改成", "预算改为", "预算是", "预算为", "预算只有", "预算大概", "预算"}
for _, p := range budgetPrefixes {
if idx := strings.Index(input, p); idx != -1 && slots["budget"] == "" {
val := strings.TrimSpace(input[idx+len(p):])
val = strings.Trim(val, "::,,。 ")
if val != "" {
slots["budget"] = val
slots[SlotFactKey] = "budget"
slots[SlotFactValue] = val
break
}
}
}
usePrefixes := []string{"用途是", "用途为", "预期用途是", "场景是", "场景为"}
for _, p := range usePrefixes {
if idx := strings.Index(input, p); idx != -1 && slots["intended_use"] == "" {
val := strings.TrimSpace(input[idx+len(p):])
val = strings.Trim(val, "::,,。 ")
if val != "" {
slots["intended_use"] = val
slots[SlotFactKey] = "intended_use"
slots[SlotFactValue] = val
break
}
}
}
return slots
}
func isGreetingOrHelp(input string) bool {
greetings := []string{"你好", "您好", "早上好", "下午好", "晚上好", "早啊", "在吗", "hi", "hello", "你是谁"}
for _, g := range greetings {
if strings.Contains(strings.ToLower(input), g) {
return true
}
}
return false
}
func sortCandidates(c []CandidateIntent) {
for i := 0; i < len(c)-1; i++ {
for j := i + 1; j < len(c); j++ {
if c[j].Confidence > c[i].Confidence {
c[i], c[j] = c[j], c[i]
}
}
}
}
// IsSmallTalkOrGeneralQuestion 判定是否为通用非业务问句(闲聊/常识提问)。
func IsSmallTalkOrGeneralQuestion(input string) bool {
trimmed := strings.TrimSpace(input)
if trimmed == "" || utf8.RuneCountInString(trimmed) < 3 {
return false
}
if hasStrongBusinessLexicon(trimmed) {
return false
}
questionMarkers := []string{
"怎么样", "好吗", "什么是", "为什么", "怎么", "哪些", "多少",
"吗", "呢", "?", "?",
}
for _, m := range questionMarkers {
if strings.Contains(trimmed, m) {
return true
}
}
return false
}
// HasStrongBusinessLexicon 是否含强业务词典(项目/法规/文档治理等),供闲聊与实时检索判定复用。
func HasStrongBusinessLexicon(input string) bool {
return hasStrongBusinessLexicon(input)
}
func hasStrongBusinessLexicon(input string) bool {
markers := []string{
"项目", "阶段", "推进", "法规", "注册", "文档", "审核", "批准", "驳回",
"报价", "需求", "型检", "成熟度", "工作流", "交付", "立项", "送审", "认领",
"方案成型", "V0.1", "V0.5", "V1.0", "v0.1", "v0.5", "v1.0",
"进度", "风险", "预算", "目标市场", "受控",
}
for _, m := range markers {
if strings.Contains(input, m) {
return true
}
}
return false
}