"""主线 /goal 命令解析:发给主线会话的一句话里自动建 goal。 背景:此前主线不会因为收到“请在这条主线开启一个持久 /goal” 这样的自然语言就自己变成 goal 任务——goal 只在建任务时由 调用方用 goal_mode=1 指定,或在 /continue 时显式勾 goal 模式。 本模块把“/goal”当成显式命令标记,让发给主线的消息本身就能 触发转换,调用方不用再知道任务当前是进行中还是已终态。 解析规则(刻意收紧,防误触发): - 触发词是 "/goal" 或裸 "goal"(斜杠不用,全角 / 也认, 大小写不敏感,单数 only——"goals" 不算)。 - 光出现还不够,必须是命令形状,二者居一: 1. 文本以触发词开头(最显式的命令),例如“goal 收xxx”; 2. 触发词后面紧跟分隔符(空格、,、:、、)或“目标”标记, 例如“/goal 收xxx”“goal,目标是收xxx”; 3. 触发词前面有开启/发送类动词(开启/建立/创建/添加/设为/ 设置/开一个/发/发送),例如“请开启一个持久/goal收xxx”、 “发goal 收xxx”。 只是在讨论里提到“/goal 的用法”“goal 的用法”“goal 是什么” 这种不算命令:触发词后面紧跟“的/是/吗/什么/怎么/如何/ 模式/用法/意思/功能”时一律不当命令。 - 目标取触发词之后的部分:先去分隔符,再去“目标是/目标为/ 目标:/目标:”或“持久目标是/为/:/:”这类前缀,最后去尾句号。 - 返回值:不是命令返回 None;是命令但没给目标返回 ""(调用方 据此回 400“目标不能为空”,而不是当成普通消息悄悄排队); 否则返回目标文本。 本模块只做纯文本解析,不碰数据库、不发请求,server 与 worker 共用同一份解析,避免两端对“是不是 /goal 命令”判断不一致。 """ import re _GOAL_RE = re.compile(r"/goal(?![A-Za-z])", re.IGNORECASE) _BARE_GOAL_RE = re.compile(r"(? 0 and s[bare.start() - 1] == "/": continue # 斜杠那处已在上面判过 before = s[:bare.start()] rest = s[bare.end():] at_start = not before.strip() before_ok = at_start or ( before.rstrip() and before.rstrip()[-1] in _SEPARATORS) or ( any(verb in before for verb in _OPEN_VERBS)) if before_ok and _rest_is_command(rest): return _strip_target(rest) return None