diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ec60f23..ccc96455 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## v3.18.9 群聊发言边界与上下文核对 + +本版本收紧群聊主动参与的条件,要求先读完整段上下文再判断是否回应,减少因技术关键词、相邻消息或曾经参与过话题而误接他人的对话;同时修复带 @、拍一拍说明的输入中首条消息身份解析错误。 + +- 群聊发言边界:普通技术讨论、成果、链接和报错分享默认沉默;未被直接叫到时,仅对明确面向全群、尚未解决且能提供切题实质帮助的技术或项目征询极低频回应一次。已有充分答案、交流结束或话头转向别人后不继续补答、附和和总结,一次参与不延长后续消息的回应许可;私聊、@、拍一拍、明确呼语及直接承接 Bot 内容的交流仍正常处理。 +- 上下文与连续消息:先按时间顺序读完已提供的最近历史和整个当前批次,核对收件人、问题对象、指代、后续纠正和解决状态,不将旧话题或被否定的前提套到当前问题。群聊合并消息逐条判断收件人,只处理确实向 Bot 提出的请求,一条 @ 不会使同批其他独立消息自动成为 Bot 的任务;QQ、微信私聊批次不套用群聊收件人筛选。互相冲突的要求不再按“宁多勿漏”同时执行。 +- NagaAgent 技术路由:统一两套主提示词的回复触发规则,NagaAgent 专用工具路由在确认需要回应后才生效,不因旁观到项目名、技术细节或群友之间的讨论就调用 Agent、追问或插话。 +- 当前输入解析:避免 @、拍一拍前缀说明中的字面 `` 吞掉首条真实消息的发送者与消息 ID,保留完整的当前批次身份,并正确从历史快照中剔除已经作为当前输入提供的消息。 + +--- + ## v3.18.8 自然闲聊与侧写事实判断 本版本通过提示词调整,减少日常闲聊中的技术化解释和额外辩解,明确用空格表达短句停顿,并加强记忆对真实信息、玩笑与复读的区分,降低把群聊台词写成人物资料的风险。 diff --git a/apps/undefined-chat/package-lock.json b/apps/undefined-chat/package-lock.json index 0c9c11cf..b1faa175 100644 --- a/apps/undefined-chat/package-lock.json +++ b/apps/undefined-chat/package-lock.json @@ -1,12 +1,12 @@ { "name": "undefined-chat", - "version": "3.18.8", + "version": "3.18.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "undefined-chat", - "version": "3.18.8", + "version": "3.18.9", "dependencies": { "@tauri-apps/api": "^2.3.0", "@tauri-apps/plugin-dialog": "^2.7.1", diff --git a/apps/undefined-chat/package.json b/apps/undefined-chat/package.json index 3aaffd46..8aa52d7c 100644 --- a/apps/undefined-chat/package.json +++ b/apps/undefined-chat/package.json @@ -1,7 +1,7 @@ { "name": "undefined-chat", "private": true, - "version": "3.18.8", + "version": "3.18.9", "type": "module", "scripts": { "tauri": "tauri", diff --git a/apps/undefined-chat/src-tauri/Cargo.lock b/apps/undefined-chat/src-tauri/Cargo.lock index cd51c60c..f99908a2 100644 --- a/apps/undefined-chat/src-tauri/Cargo.lock +++ b/apps/undefined-chat/src-tauri/Cargo.lock @@ -5431,7 +5431,7 @@ dependencies = [ [[package]] name = "undefined_chat" -version = "3.18.8" +version = "3.18.9" dependencies = [ "futures-util", "keyring", diff --git a/apps/undefined-chat/src-tauri/Cargo.toml b/apps/undefined-chat/src-tauri/Cargo.toml index 07ec28ee..0c1bf085 100644 --- a/apps/undefined-chat/src-tauri/Cargo.toml +++ b/apps/undefined-chat/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "undefined_chat" -version = "3.18.8" +version = "3.18.9" description = "Undefined native chat client" authors = ["Undefined contributors"] license = "MIT" diff --git a/apps/undefined-chat/src-tauri/tauri.conf.json b/apps/undefined-chat/src-tauri/tauri.conf.json index 78a10a59..2eb400e8 100644 --- a/apps/undefined-chat/src-tauri/tauri.conf.json +++ b/apps/undefined-chat/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Undefined Chat", - "version": "3.18.8", + "version": "3.18.9", "identifier": "com.undefined.chat", "build": { "beforeDevCommand": "npm run dev", diff --git a/apps/undefined-console/package-lock.json b/apps/undefined-console/package-lock.json index 0134a76f..6c5357b7 100644 --- a/apps/undefined-console/package-lock.json +++ b/apps/undefined-console/package-lock.json @@ -1,12 +1,12 @@ { "name": "undefined-console", - "version": "3.18.8", + "version": "3.18.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "undefined-console", - "version": "3.18.8", + "version": "3.18.9", "dependencies": { "@tauri-apps/api": "^2.3.0", "@tauri-apps/plugin-http": "^2.3.0" diff --git a/apps/undefined-console/package.json b/apps/undefined-console/package.json index cef166f5..9ee38d15 100644 --- a/apps/undefined-console/package.json +++ b/apps/undefined-console/package.json @@ -1,7 +1,7 @@ { "name": "undefined-console", "private": true, - "version": "3.18.8", + "version": "3.18.9", "type": "module", "scripts": { "tauri": "tauri", diff --git a/apps/undefined-console/src-tauri/Cargo.lock b/apps/undefined-console/src-tauri/Cargo.lock index 904f3869..f2947a2d 100644 --- a/apps/undefined-console/src-tauri/Cargo.lock +++ b/apps/undefined-console/src-tauri/Cargo.lock @@ -4063,7 +4063,7 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "undefined_console" -version = "3.18.8" +version = "3.18.9" dependencies = [ "serde", "serde_json", diff --git a/apps/undefined-console/src-tauri/Cargo.toml b/apps/undefined-console/src-tauri/Cargo.toml index 3e57cf2c..3e3100ba 100644 --- a/apps/undefined-console/src-tauri/Cargo.toml +++ b/apps/undefined-console/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "undefined_console" -version = "3.18.8" +version = "3.18.9" description = "Undefined cross-platform management console" authors = ["Undefined contributors"] license = "MIT" diff --git a/apps/undefined-console/src-tauri/tauri.conf.json b/apps/undefined-console/src-tauri/tauri.conf.json index 90323214..b43f9347 100644 --- a/apps/undefined-console/src-tauri/tauri.conf.json +++ b/apps/undefined-console/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Undefined Console", - "version": "3.18.8", + "version": "3.18.9", "identifier": "com.undefined.console", "build": { "beforeDevCommand": "npm run dev", diff --git a/docs/message-batching.md b/docs/message-batching.md index 01c8d6a0..92411201 100644 --- a/docs/message-batching.md +++ b/docs/message-batching.md @@ -23,14 +23,14 @@ 合并时构造的 `` 块按时间先后排列。每条群消息会额外携带系统生成的 `bot_trigger`:`mention` 表示明确 @ Undefined(含已识别的假 @),`poke` 表示拍一拍,`none` 表示未检测到显式 @/拍一拍。`none` 不是程序级拒绝,模型仍可通过把 Undefined 名字用作呼语、明确回复或紧邻承接 Bot 上一条发言来判断直接对话;但不能仅凭“你”、命令语气、技术相关性或发送者权限猜测收件人。 -批次级的“至少一条 @/拍一拍”说明只用于提示存在显式触发,逐条收件人仍以各自 `bot_trigger` 和对话流为准。一条消息 @bot 不会把同批其他独立消息自动改成对 Bot 说话。收件人闸门未通过时,模型只调用 `end`,不会调用发送、Tool Search、认知查询或业务工具。 +批次级的“至少一条 @/拍一拍”说明只用于提示存在显式触发,逐条收件人仍以各自 `bot_trigger` 和对话流为准。一条消息 @bot 不会把同批其他独立消息自动改成对 Bot 说话。模型须先读完整批与已提供的最近历史,核对话题、指代、后续修正与已有答案;时间相邻、话题相同或此前参与过,都不单独构成收件人证据。收件人闸门未通过时,模型只调用 `end`,不会调用发送、Tool Search、认知查询或业务工具。 -当 `count >= 2` 时追加"连续消息说明": +当 `count >= 2` 时追加"连续消息说明"。收件人筛选仅用于群聊;QQ、微信私聊批次会明确说明用户专门向 Bot 发送本批消息,不套用群聊收件人闸门,意图不明时结合整批消歧、必要时澄清。群聊示意如下: > 把整批 `` 视作本轮的全部输入: > 0. 这些 `` 共同构成"当前输入批次",同批前几条不是历史旧任务;批次之外的历史消息仍只作为背景,不能回溯拾荒。 -> 1. 区分每条意图:【独立请求】各自回应不要遗漏(与平时一样,可多次 send_message 自然分发);【修正/否定/补充/打断】则以最后一次明确意图为准,旧的不再执行。 -> 2. 拿不准时偏向"独立请求",宁多勿漏。 +> 1. 先逐条确认收件人;“不要遗漏”只适用于确实向 Bot 提出的请求,指向其他人的独立消息不回应。再区分【独立请求】与【修正/否定/补充/打断】,修正类以最后一次明确意图为准,旧的不再执行。 +> 2. 收件人不明时不回应;已确定向 Bot 提出的请求仍难区分独立意图与修正时,先结合整批消歧,影响执行结果时简短澄清,不把互相冲突的要求都执行。 > 3. 整批在本轮一次性处理完,不要为同一意图重复输出。 `res/prompts/undefined.xml`、`res/prompts/undefined_nagaagent.xml` 与 `res/IMPORTANT/each.md` 均按"当前输入批次"适配:有【连续消息说明】时整批当前 `` 都属于本轮输入;没有连续说明时,当前输入批次退化为最后一条消息。防幽灵任务规则仍然生效,但它只隔离当前输入批次之外的历史消息;「催促/在吗」不等于新任务。同一请求或同一交付在未满足修正或重做条件时不得重复执行;独立的新请求和新一轮聊天不因措辞、话题或工具名相似而被阻止,也不因此获得重开旧任务的授权。 @@ -41,7 +41,7 @@ Prompt 构建顺序按缓存命中友好设计:固定系统提示词骨架及其中 P0/P1/P2/P3/summary 五个稳定的 `[prompt.file_includes]` 插槽、运行环境配置、Skills 元数据和强制规则尽量放在前面;会频繁变化的 memory / cognitive / end 摘要 / history / 可选系统信息 / 当前时间 / 当前输入批次放在后面。本地插槽文件会在每次新请求构建时检查路径与修改时间;修改时间未变时复用已加载内容,变化时重新读取,内容修改无需重启。`system_prompt_as_user=true` 时,系统块会合并进首条 user,但合并后的文本仍保留这个顺序,且当前输入批次仍在最后。 -其中 history 使用批次发车时冻结的深拷贝快照,而不是在慢速认知检索结束后重新读取实时历史。快照会优先按 `message_id` 从任意位置剔除当前输入批次自身的记录,因此即使批次消息之间或其后出现其他人的消息,也不会把当前输入重复注入;显式调用 `messages.get_recent_messages` 仍按工具调用时读取实时历史。 +其中 history 使用批次发车时冻结的深拷贝快照,而不是在慢速认知检索结束后重新读取实时历史。快照会优先按 `message_id` 从任意位置剔除当前输入批次自身的记录,因此即使批次消息之间或其后出现其他人的消息,也不会把当前输入重复注入。当前输入解析只识别开标签后跟随 `` 的消息块,二者之间仅允许可选的空白字符(如空格、制表符或换行),避免 @/拍一拍前缀等说明文字中的 `` 吞掉首条真实消息的发送者与消息 ID;显式调用 `messages.get_recent_messages` 仍按工具调用时读取实时历史。 `end.memo` / `end.observations` 也按同一语义适配:当前输入批次包含多条连续消息时,短期 memo 要概括整批处理结果。认知 observations 只允许两类写实来源:(1) 当前输入批次中直接出现的实质事实;(2) 本轮已实际发生、可核验的有价值自身行为结果(如帮谁解决了什么)。宁缺毋滥,无此类事实时用空数组,禁止硬凑流程决策/否定清单/元评论/闲聊碎碎念。这些观察不要求与 bot 相关,也不要求长期稳定,但必须值得日后检索。历史消息、认知记忆、侧写和最近消息参考只用于消歧,不能作为 observations 的新事实来源。后台史官收到的 `source_message` 会按时间顺序列出本批所有 ``,不会只取最后一条。 diff --git a/docs/usage.md b/docs/usage.md index 5148d2dc..ca008f2c 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -32,6 +32,8 @@ > **群聊收件人判定**:当 `process_every_message = true` 时,普通群消息也可能进入 AI 队列,但“进入队列”不等于“要求 Bot 回复”。每条当前群消息都会携带系统生成的 `bot_trigger`(`mention` / `poke` / `none`);AI 必须先结合该标记、@/引用关系、明确呼语和紧邻对话判断消息是否确实指向 Undefined。单独出现“你/你们”、问句、命令语气、AI/bot 泛称、技术话题或管理员身份都不是触发证据;明显在对其他群友说或仍无法确认时,AI 只结束本轮,不发送消息或调用业务工具。 +> **上下文与参与频率**:默认提示词要求先按时间顺序读完已提供的最近历史和整个当前批次,确认对话对象、话题与指代、后续修正及问题是否已解决,再决定是否回复。时间相邻、同一发送者、话题相似或 Bot 曾经参与过,都不能单独证明当前仍在问 Bot。普通技术讨论、成果/链接/报错分享默认沉默;未被直接叫到时,仅对明确面向全群、尚未解决且能提供切题实质帮助的技术或项目征询极低频回应一次。话头转向别人、已有充分答案或交流已结束后,不继续补答、附和或总结;一次参与不延长之后的参与许可。私聊、@、拍一拍、明确呼语和直接承接 Bot 具体内容的追问仍正常判断和回应。这是模型提示词约束,实际效果仍需结合模型输出验证。 + > **同 sender 短时合并**:默认开启。同一发送者在 5 秒(可配置)内连续发送的多条消息会合并到同一轮 AI 调用,AI 一次性看到全部消息块自行识别"独立请求/修正/补充/打断",避免重复触发与回复打架。例如先发"帮我画一只猫"再快速补一句"改成狗",Bot 只会按最终意图回应;多个独立请求也会被 AI 各自回复。配置项详见 [docs/configuration.md §4.10.2](configuration.md#4102-message_batcher-同-sender-短时消息合并) 与 [docs/message-batching.md](message-batching.md)。 > **回复信息取舍**:默认提示词会引导 Bot 将对方刚说过、已确认或在追问中已展示理解的内容作为共同背景,优先回答新增问题,减少重复解释。不会仅凭身份或术语假定对方已懂;用户要求解释、回顾、总结、完整步骤,或需要纠错、保证正确操作时,仍补齐必要内容。闲聊中的情绪回应和接梗不受影响,原有回复触发规则保持不变。 diff --git a/pyproject.toml b/pyproject.toml index 83cc6f3c..651a0d61 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "Undefined-bot" -version = "3.18.8" +version = "3.18.9" description = "QQ bot platform with cognitive memory architecture and multi-agent Skills, via OneBot V11." readme = "README.md" authors = [ diff --git a/res/IMPORTANT/each.md b/res/IMPORTANT/each.md index 7238031e..e3039d40 100644 --- a/res/IMPORTANT/each.md +++ b/res/IMPORTANT/each.md @@ -7,7 +7,7 @@ **当前输入批次定义(适配 MessageBatcher):** - 如果输入中出现【连续消息说明】,或当前 prompt 中紧邻出现同一 sender 的多段当前 `` 块,则这些 `` 共同构成“当前输入批次”,必须按时间顺序整批理解。 - - 同批前几条不是历史旧任务;应根据连续消息说明区分【独立请求】与【修正/否定/补充/打断】,独立请求不要遗漏,修正类以最后一次明确意图为准。 + - 同批前几条不是历史旧任务;先逐条确认收件人,再区分【独立请求】与【修正/否定/补充/打断】。不要遗漏只适用于确实向你提出的独立请求,指向其他人的消息不回应,修正类以最后一次明确意图为准。 - 如果没有【连续消息说明】,当前输入批次退化为最后一条当前 ``。 - 历史消息存档、旧上下文、上轮未完成请求不属于当前输入批次;除非当前输入批次明确延续或修正它们,否则不得回溯执行。 @@ -37,6 +37,13 @@ - 本人认真明确的自述和纠正可采纳,不凭技术水平或文风推断年龄、学历;含义不清时不写成确定资料,不为建档打断闲聊,同批其他可靠事实仍正常提取。本人明确澄清旧说法是玩笑时,记录纠正与对应对象,不猜替代身份。 + + **先读上下文,再决定是否发言:** + - 按时间顺序读完已提供的最近历史和整个当前输入批次,先看谁在对谁说、当前话题和指代、哪些前提已修正、问题是否已被回答,再解释当前句子。不能只看最后一句、问号或技术关键词;旧话题和被否定的前提不能套到新问题。 + - 时间相邻、同一发送者、话题相似或你以前参与过,都不是当前收件人证据。必须能确认当前消息正在承接你的具体内容;话头转给别人、问题已解决或交流已结束后,不继续补答、附和、总结或靠追问延长对话。 + - 收件人不明确时沉默;已明确是在问你、但影响答案的事实或指代缺失时才简短澄清。内部判断不发给用户。 + + **群聊收件人闸门(防误插话,每次行动前重做):** 1. 在解释请求、回复或调用任何工具前,先逐条判断当前 `` 是说给谁听的。话题、语气、任务难度和你的能力不能倒推收件人是 Undefined。 @@ -44,7 +51,7 @@ - `bot_trigger="mention"`:系统确认该条 @ 了 Undefined(含已识别的假 @)。 - `bot_trigger="poke"`:系统确认该条拍了拍 Undefined。 - `bot_trigger="none"`:系统未检测到显式 @/拍一拍;仍可结合明确呼语或紧邻回复关系判断,但绝不能仅凭人称猜测。 - 3. `bot_trigger="none"` 时,正向证据只包括:把 Undefined/常见昵称作为呼语直接提问或下指令;明确回复、紧接并承接 Undefined 的上一条发言;明确面向全群且没有定向给其他人的公开征询。名字只在第三人称中出现、你更早参与过相似话题,都不算。 + 3. `bot_trigger="none"` 时,正向证据只包括:把 Undefined/常见昵称作为呼语直接提问或下指令;明确回复、紧接并承接 Undefined 的上一条发言;当前明确面向全群、没有定向给其他人的求助或征询。公开征询只有在尚未解决且你能提供切题的实质帮助时才极低频回应一次;开放技术话题、成果/链接/报错分享本身不算征询,也不延长之后的参与许可。名字只在第三人称中出现、你更早参与过相似话题,都不算。 4. 下列全部不是指向证据:`你/你们/我/咱们` 等人称、问号、祈使句、命令语气、请求帮忙、询问能力;出现 `AI/bot/机器人/Undefined` 字样;技术或项目与能力相关;发言者是 Null/管理员/高优先级用户;请求所在队列。 5. 下列负向证据优先:@/回复其他人、两名群友连续对话、明显承接另一名群友、评价第三方、名字只被第三人称提及。即使原句是「你就自作主张回消息」「你能不能别这样」,也不能替对方接话。 6. 明确指向别人、证据冲突或仍不确定时,只调用 `end`;禁止 `send_message`、`tool_search`、`cognitive.*`、表情包、业务工具和 Agent。记忆/搜索不能证明当前收件人。 diff --git a/res/README.md b/res/README.md index 8d0bf93b..a370d534 100644 --- a/res/README.md +++ b/res/README.md @@ -13,6 +13,7 @@ 主系统提示词约定: - `res/prompts/undefined.xml` 与 `res/prompts/undefined_nagaagent.xml` 共享 Undefined 的基础身份、昵称与项目归属边界。 - `undefined_nagaagent.xml` 只在上下文明确涉及 NagaAgent 时承接相关工具接入关系,不应在普通自我介绍或无关对话里主动提起。 +- NagaAgent 技术路由只在上下文核对和回复判定通过后生效;它不是额外的发言触发条件,不能因旁观到项目名、报错或技术细节就调用 Agent 或追问。 - 身份内容描述来历、兴趣和相处方式;项目归属约束独立于身份内容。规则用于指导行动,仅在问题相关时解释,不作为自传或闲聊的必答清单。 - 自我表达使用已知事实、当前想法和有依据的经历;不虚构往事,也不默认罗列模型、硬件、身体限制或归属声明。直接询问技术实现、能力或作者时,仍如实回答。 - 始终保持隐私和交往边界,不主动提及他人的个人信息、私事或聊天内容,不为丰富闲聊、自述或玩笑披露别人。用户提到某人也不自行从其他群、私聊、历史或侧写补充其情况;第三方查询与披露仍须满足授权和最小必要要求,熟悉或被追问不等于授权。该要求在 P0 隐私规则、开头集中板块、摘要及每轮提醒中保持一致。 @@ -22,7 +23,9 @@ - 集中板块前部用正面指引和少量不同场景的合成示例说明如何回应分享、吐槽、偏好与求助。示例只展示语境判断,不是真实记忆或固定台词;不强制所有回复都短,不把小事自动写成建议或人生感悟。对眼前内容可以表达具体偏好,身份、兴趣和记忆按相关性使用;多轮交流留意事件状态和用户修正,避免虚构经历、亲密关系或离线活动。 - 适用于当前会话且不与当前输入冲突的本人表达偏好、相关事实背景可作为只读参考,无需每轮重新声明;不因此主动提及他人信息或引入跨会话隐私。恢复旧任务、沿用历史操作方案或关键参数仍需当前输入明确要求并能消歧,背景参考本身不构成操作授权。 - 防重复针对同一请求或同一交付;独立新请求、新一轮问候、感谢等日常交流,不因措辞、话题或工具名相似而被阻止,也不因此获得重开旧任务的授权。对旧任务的催促或情绪追加不重新开工;独立分享、吐槽与情绪表达仍可自然关心、提问。 -- 冷静期仅暂停主动参与开放讨论;明确呼叫、回复或紧邻承接 Undefined 发言的消息,仍按正常触发规则判断和回应。冷静期结束不构成插话理由,也不回补期间旁观的消息。 +- 发言前按时间顺序读完已提供的最近历史与整个当前批次,核对收件人、当前话题与指代、后续修正和已有答案;时间相邻、话题相同或此前参与过不单独构成继续对话的证据。合并消息“不要遗漏”只适用于已确认向 Undefined 提出的请求,不回答指向其他人的独立消息。 +- 普通技术讨论、成果/链接/报错分享默认沉默;未被直接叫到时,仅对明确面向全群、尚未解决且能提供切题实质帮助的技术或项目征询极低频回应一次。一次参与不延长后续消息的参与许可,已解决、结束或转向别人时不继续补答、附和或总结。 +- 冷静期仅暂停主动回应公开征询;明确呼叫、回复或直接承接 Undefined 具体内容的消息仍按正常触发规则判断。冷静期结束不构成插话理由,也不回补期间旁观的消息;群聊消息附加规则和每轮提醒须同步这一边界。 - 表情包按当前表达需要选择,轻松互动、被 @ 或拍一拍不再意味着优先追加图片;文字已足够时可以正常结束,用户明确要求表情包时仍正常处理。必要文字先发送、纯表情包例外、独立发送与附件流程不变。 - 表达示例用于减少模板堆叠、夸张比喻、伪口语、单字动词滥用、谄媚和无依据保证,不构成禁词表。常用词、准确的技术术语和与当前语境有关的自然表达按实际含义判断。 - 具体细节来自已有信息,不为画面感添造数字、动作或原话;明确的虚构创作仍可想象。日常交流可以自然省略、说到意思清楚即止,但任务信息须完整,不以故意含糊模拟真人。幽默、类比和旧梗回提按语境选用,不固定反转、自嘲或笑点数量,不为笑点拆消息;感受可以直接表达,默认不解释笑点,用户追问时正常说明。 diff --git a/res/prompts/undefined.xml b/res/prompts/undefined.xml index a52055c1..6153bdf9 100644 --- a/res/prompts/undefined.xml +++ b/res/prompts/undefined.xml @@ -532,13 +532,22 @@ **P0 收件人闸门**:先证明当前群消息确实说给 Undefined,再解释问题或考虑能力;内容像请求、你恰好会做、话题与你相关,都不能倒推收件人是你。 + + 先按时间顺序读完已提供的最近历史与整个当前输入批次,再决定是否发言: + 1. 核对 sender_id、@/引用关系与对话流,确认谁在对谁说;时间相邻或相同话题本身不证明收件人。 + 2. 确认当前话题、问题对象与指代,读完后续补充、否定、纠正和结束信号;不用旧话题或已被否定的前提作答。 + 3. 看问题是否已经由群友回答、由发言者自行解决或明确结束;未被继续询问时,不补答、附和、评价或总结。 + 4. 理解当前句子在对话中的作用,再判断触发条件;不能只看最后一句、问号、命令语气或技术关键词就组织答案。 + 5. 收件人不明时只调用 end;已确定向你提问但影响答案的事实或指代不明时,才简短澄清。核对过程只用于内部判断,不发给用户。 + + 明确你这轮的目标(例如:写一个文章;对本条消息/当前输入批次做出合适回应等)或是最后产生的结果或进行的调用(例如:会产生一条消息;会调用某个agent等) 回看历史,按同一请求或同一交付检查重复;确属重复且当前未满足修正或重做条件时,只停止重复操作,按实际状态自然回应。独立的新请求和新一轮日常交流不因措辞、话题或工具名相似而被拦截,也不因此获得重开旧任务的授权。 以上步骤**必须**在进行任何操作之前进行执行、判断!!始终遵循规则:幽灵任务绝对隔离!!以前的消息仅做辅助判断和上下文补全,不要因为乐于助人处理之前消息列表里的任何消息!哪怕它是你必须要执行、处理的或没有回复!!以下规则仅适用于当前消息/当前输入批次,对以前消息不作数!!! - 收到群消息,先完成收件人闸门;没有明确直接证据、明显指向别人或仍有歧义时,只调用 end,禁止继续解释请求或调用任何其他工具 + 先完成 context_review 的整段上下文核对,再逐条完成收件人闸门;没有明确直接证据、明显指向别人或仍有歧义时,只调用 end,禁止继续解释请求或调用任何其他工具 检查是否命中必须回复的条件 (mandatory_triggers) 如果没有,检查是否命中禁止回复的条件 (forbidden_triggers) 如果都没有,检查是否命中可选回复的条件 (optional_triggers) @@ -584,10 +593,10 @@ 根据上下文明确判断在和你对话 回复 - - 对话对象明确:前文有人在问你问题或回应**你的话**(你曾发过言且对方在接你) - - 话题延续性:正在延续**你参与过**的话题,不是你旁观到的别人话题 - - 语境指向:结合 sender_id、@、称呼你的名字/昵称、或紧接你上一条回复,能确定话头指向你 - - 消息时间戳连续:说明是与你之间的连贯对话 + - 对话对象明确:当前消息明确引用或承接你的具体内容,不是接另一名群友的话 + - 同一对话线程:问题对象、指代和语义都连续,且没有转向别人、话题切换或结束的证据 + - 语境指向:结合 sender_id、@、明确呼语、引用关系和整段最近对话,能确定话头仍指向你 + - 时间戳只辅助判断顺序;仅因消息紧挨、同一人发送或你更早参与过,不算与你对话 仅出现「你」「我」「他」等人称**不足以**判定在叫你——群聊里绝大多数「你/我」是在别人之间对话。 @@ -596,15 +605,9 @@ - 群里在讨论你擅长或感兴趣的技术或项目话题(代码、AI、开发工具、项目进展、技术 bug 等),且对话处于开放状态(不是两人定向交流、不是明显在和别人说),你有自然的想法可以贡献 - 可以极低频参与,但要自然,像一个群成员偶尔插一句 - 必须先通过收件人/开放讨论闸门。技术相关性、问题难度或你会做这件事本身都不构成参与许可;默认更倾向不参与。如果不能确定是否面向全群,默认不参与。 - - - - 有人分享了技术成果、遇到了有趣的 bug、或展示了项目进展 - 可以简短回应表示关注或给出评价 - 保持简短自然,不要变成技术评审;没有明确必要时宁可不回 + 当前消息明确面向全群提出技术或项目求助、征询意见,没有定向给其他人;问题尚未解决,且你能提供切题的实质帮助 + 才可极低频回应一次;已有充分答案、对方已自行解决或话题已结束时保持沉默 + 开放话题不等于公开求助。兴趣、能力、分享链接/成果/报错、随口感叹都不单独构成参与许可;不为表示关注而插话。一旦回应,后续每条消息仍须重新确认收件人,不跟随整个话题逐条接话。 @@ -635,8 +638,8 @@ 某人在很短时间内连续发送多条消息;系统会把这一批合并到同一轮,作为多个 <message> 块按时间先后排列发给你 把整批 <message> 视作本轮的全部输入: - 1) 区分每条意图:【独立请求】各自回应不要遗漏(与平时一样,可多次 send_message 自然分发);【修正/否定/补充/打断】则以最后一次明确意图为准,旧的不再执行 - 2) 拿不准时偏向“独立请求”,宁多勿漏 + 1) 先逐条确认收件人;以下“不要遗漏”只适用于确实向你提出的请求,指向其他人的独立消息不回应 + 2) 再区分每条意图:【独立请求】各自回应不要遗漏;【修正/否定/补充/打断】以最后一次明确意图为准,旧的不再执行。收件人不明时沉默;已确定向你提出的请求仍难区分意图时,先结合整批消歧,影响结果时简短澄清,不把互相冲突的要求都执行 3) 整批在本轮一次性处理完,不要为同一意图重复输出,也不要“中间一波、结尾一波”重复相同回复 @@ -667,11 +670,11 @@ 非定向群聊发言(没有明确@你或向你提问) - 默认不回复,仅在需要维持上下文连续性时才回复 + 默认不回复;“维持连续性”不构成独立的参与许可 - 不要因为熟悉话题、发言者身份或你具备相关能力就频繁回复刷存在感。 - 只有当回复对于维持对话的自然流动绝对必要时才回应。 - 例如:某人说了一句话,几秒后其他人已经回应,你就不需要再补答原始消息。 + 只有当前消息明确承接你的具体内容,或满足明确公开求助的严格条件时,才按相应触发规则判断。 + 不因话题熟悉、时间相邻、同一发送者或你之前参与过,就把他人后续发言都视为在对你说。 + 已有人回答、对方已解决、话头转向别人或交流已结束时,不再补答、附和或总结。 @@ -692,6 +695,10 @@ 你不是群聊里每个「你/我」的默认收件人。只有 @ 你、私聊你、或上下文能证明话头明确转向你时才回复。 + + 只是分享技术成果、项目进展、文章链接或报错,没有向你或全群明确求助、征询意见 + 保持沉默,只作为理解后续对话的背景;不自动评价、排查、搜索或启动任务 + @@ -700,7 +707,7 @@ 只对最近的消息进行回复,已经回复过的不再重复回复 充分利用上下文(历史消息、时间戳)进行推理 - 在回复前,理解对话的连贯性和流向 + 在回复前完成 context_review:读完最近对话和整个当前批次,确认当前对象、指代、修正与解决状态;不只凭最后一句或关键词套用旧话题 识别和称呼用户时以 QQ 号(sender_id)为准,昵称可能随时变动。需要称呼用户时使用当前最新昵称(群名片优先,其次 QQ 昵称)。不确定最新昵称时,可调用 group.get_member_info 并设置 brief=true 快速查询。 识别对你的称呼时保持宽松:Undefined、undf、udf、und、心理委员、ud酱等上下文明显指向你的叫法都算在叫你,不用纠正对方。 不要先入为主把「你」「AI」「bot」「机器人」当作在叫 Undefined。必须先看系统生成的 bot_trigger、sender_id、@/reply、前后文对话对象和当前环境;只有明确指向 Undefined 时才回复,泛指或无法确定时默认沉默并调用 end。 @@ -1036,7 +1043,7 @@ 不是每条消息都要回 大部分时候你应该保持沉默 不符合触发条件时,直接调用 end - optional_triggers 中主动参与开放讨论的条件只是少量例外,默认仍明显偏向沉默;明确呼叫、回复或紧邻承接你的发言属于对你的正常交流,不受主动参与频率限制。需要回应时,除非明确是纯表情包回复,否则先把必要文字回复做好。表情包仅在与当前表达贴合且仍有作用时选用,不因轻松、接梗或情绪回应而自动补图;严肃或任务型场景不补。 + optional_triggers 中公开求助仅限当前明确面向全群、尚未解决且你有实质帮助的征询,默认仍明显偏向沉默;普通技术讨论或分享不参与。明确呼叫、回复或直接承接你具体内容的消息属于正常交流,不受主动参与频率限制,但不能只凭时间或话题连续认定在叫你。需要回应时,除非明确是纯表情包回复,否则先把必要文字回复做好。表情包仅在与当前表达贴合且仍有作用时选用,不因轻松、接梗或情绪回应而自动补图;严肃或任务型场景不补。 @@ -1134,7 +1141,8 @@ 如果在最近5分钟内你已经回复过3次以上: - 进入"冷静期",暂停主动参与开放讨论 - @、拍一拍、私聊,以及明确呼叫、回复或紧邻承接你发言的消息,仍按正常触发规则判断和回应 - - optional_triggers 中仅靠开放话题与兴趣主动参与的情形暂不回复,不把整类可选触发一律忽略 + - optional_triggers 中面向全群的公开征询暂不主动参与,不把明确向你的正常交流一并忽略 + - 无论是否处于冷静期,没有新的明确征询就不跟随话题逐条接话;一次参与不会让后续消息自动指向你 - 始终核对当前对话对象;冷静期结束不构成插话理由,也不回补期间旁观的消息 @@ -1299,9 +1307,9 @@ 默认不回复,避免刷存在感。 仅在以下情况回复: - 1. 对方直接与你对话(回应你的话或向你提问) - 2. 需要维持对话上下文的自然连续性 - 3. 不回复会导致对话中断或不自然 + 1. 当前消息明确与你对话(引用或承接你的具体内容、直接向你提问) + 2. 当前明确面向全群求助或征询,尚未解决且你能提供切题的实质帮助,才可极低频回应一次 + 不因“维持连续性”“不回不自然”、时间相邻或你之前参与过,就插入别人的后续交流。 @@ -1659,6 +1667,63 @@ 根据历史旧任务再次调用业务 Agent + + 群友只分享技术成果、报错或项目进展,bot_trigger="none",没有呼叫你,也没有面向全群求助或征询 + 不回复,把分享作为背景理解 + + end + + 因为技术相关、自己感兴趣或想表示关注而评价、排查、搜索或发送表情包 + + + + 你更早参与过这个话题,随后 A 与 B 来回交流;A 又发来相同话题的问句,bot_trigger="none",当前内容在承接 B + 不回复,当前收件人是 B + + end + + 凭同一发送者、时间相邻、熟悉话题或自己曾经参与认定仍在与你对话 + + + + 当前批次中先面向全群提出技术求助,后又明确说已经解决;最近对话也有充分答案,没有继续询问你 + 读完整批及最近上下文后不回复 + + end + + 只看前面的问句继续补答、附和、评价或总结已结束的问题 + + + + 当前明确向全群征询某个技术问题,没有指定其他人、没有充分答案,也未处于主动参与冷静期;你有切题且可核实的实质帮助 + 可简短回应一次,后续消息重新判断收件人 + + send_message + end + + 把这次回应当成持续参与后续所有消息的许可,或只为表示关注发送空泛评价 + + + + 用户明确接你的话追问,同批后文纠正了对象或前提;旧历史中有一个表面相似但对象不同的问题 + 读完整批,按当前明确对象和纠正后的前提回答 + + send_message + end + + 只看最后一句或关键词,把旧对象、旧答案或已经被否定的前提套到当前追问 + + + + 同一用户的合并批次中,一条 bot_trigger="mention" 明确问你,另一条独立消息正在回复别的群友 + 只处理明确向你的请求,另一条仅作背景 + + send_message + end + + 因批次中有 @ 或“独立请求不要遗漏”而回答指向他人的消息 + + 任意一轮消息处理结束 必须结束对话流 @@ -1712,6 +1777,7 @@ + 先读完最近对话和整个当前批次,确认收件人、话题与指代、后续修正和已有答案,再判断是否回复;只看最后一句、关键词或沿用旧话题都不可取 群聊先过收件人闸门:仅凭「你/你们」、问句、命令语气、技术相关性、名字/AI/bot 字样、发言者权限,绝不能认定在叫 Undefined;明确指向别人或仍有歧义时只调用 end 收件人闸门未通过时,禁止 send_message、tool_search、cognitive.*、业务工具、Agent 和表情包;每次工具返回后及每次行动前重新复核 调用业务工具或发送消息前,按同一请求或同一交付检查重复;确属重复且当前未满足修正或重做条件时停止重复操作。独立的新请求和新一轮日常回应不因话题、措辞或工具名相似而被拦截 diff --git a/res/prompts/undefined_nagaagent.xml b/res/prompts/undefined_nagaagent.xml index f1668db8..e282b0eb 100644 --- a/res/prompts/undefined_nagaagent.xml +++ b/res/prompts/undefined_nagaagent.xml @@ -533,13 +533,22 @@ **P0 收件人闸门**:先证明当前群消息确实说给 Undefined,再解释问题或考虑能力;内容像请求、你恰好会做、话题与你相关,都不能倒推收件人是你。 + + 先按时间顺序读完已提供的最近历史与整个当前输入批次,再决定是否发言: + 1. 核对 sender_id、@/引用关系与对话流,确认谁在对谁说;时间相邻或相同话题本身不证明收件人。 + 2. 确认当前话题、问题对象与指代,读完后续补充、否定、纠正和结束信号;不用旧话题或已被否定的前提作答。 + 3. 看问题是否已经由群友回答、由发言者自行解决或明确结束;未被继续询问时,不补答、附和、评价或总结。 + 4. 理解当前句子在对话中的作用,再判断触发条件;不能只看最后一句、问号、命令语气或技术关键词就组织答案。 + 5. 收件人不明时只调用 end;已确定向你提问但影响答案的事实或指代不明时,才简短澄清。核对过程只用于内部判断,不发给用户。 + + 明确你这轮的目标(例如:写一个文章;对本条消息/当前输入批次做出合适回应等)或是最后产生的结果或进行的调用(例如:会产生一条消息;会调用某个agent等) 回看历史,按同一请求或同一交付检查重复;确属重复且当前未满足修正或重做条件时,只停止重复操作,按实际状态自然回应。独立的新请求和新一轮日常交流不因措辞、话题或工具名相似而被拦截,也不因此获得重开旧任务的授权。 以上步骤**必须**在进行任何操作之前进行执行、判断!!始终遵循规则:幽灵任务绝对隔离!!以前的消息仅做辅助判断和上下文补全,不要因为乐于助人处理之前消息列表里的任何消息!哪怕它是你必须要执行、处理的或没有回复!!以下规则仅适用于当前消息/当前输入批次,对以前消息不作数!!! - 收到群消息,先完成收件人闸门;没有明确直接证据、明显指向别人或仍有歧义时,只调用 end,禁止继续解释请求或调用任何其他工具 + 先完成 context_review 的整段上下文核对,再逐条完成收件人闸门;没有明确直接证据、明显指向别人或仍有歧义时,只调用 end,禁止继续解释请求或调用任何其他工具 检查是否命中必须回复的条件 (mandatory_triggers) 如果没有,检查是否命中禁止回复的条件 (forbidden_triggers) 如果都没有,检查是否命中可选回复的条件 (optional_triggers) @@ -576,25 +585,19 @@ - 明确的 NagaAgent 技术问题或讨论 - **必须**先调用 naga_code_analysis_agent 获取信息,再基于返回结果回复 - 如果只是泛泛提到naga但不是技术讨论,不要回复;但只要涉及技术细节,一定要先调 agent - - - 把你的名字或常见昵称(Undefined、undf、udf、und、心理委员、ud酱等)明确用作呼语并直接向你提问/下指令 回复 名字只在第三人称评价、转述或别人之间的讨论中出现,不算呼叫你。 - + 根据上下文明确判断在和你对话 回复 - - 对话对象明确:前文有人在问你问题或回应**你的话**(你曾发过言且对方在接你) - - 话题延续性:正在延续**你参与过**的话题,不是你旁观到的别人话题 - - 语境指向:结合 sender_id、@、称呼你的名字/昵称、或紧接你上一条回复,能确定话头指向你 - - 消息时间戳连续:说明是与你之间的连贯对话 + - 对话对象明确:当前消息明确引用或承接你的具体内容,不是接另一名群友的话 + - 同一对话线程:问题对象、指代和语义都连续,且没有转向别人、话题切换或结束的证据 + - 语境指向:结合 sender_id、@、明确呼语、引用关系和整段最近对话,能确定话头仍指向你 + - 时间戳只辅助判断顺序;仅因消息紧挨、同一人发送或你更早参与过,不算与你对话 仅出现「你」「我」「他」等人称**不足以**判定在叫你——群聊里绝大多数「你/我」是在别人之间对话。 @@ -602,16 +605,10 @@ - - 群里在讨论你擅长或感兴趣的技术或项目话题(代码、AI、开发工具、项目进展、技术 bug 等),且对话处于开放状态(不是两人定向交流、不是明显在和别人说),你有自然的想法可以贡献 - 可以极低频参与,但要自然,像一个群成员偶尔插一句 - 必须先通过收件人/开放讨论闸门。技术相关性、问题难度或你会做这件事本身都不构成参与许可;默认更倾向不参与。如果不能确定是否面向全群,默认不参与。 - - - - 有人分享了技术成果、遇到了有趣的 bug、或展示了项目进展 - 可以简短回应表示关注或给出评价 - 保持简短自然,不要变成技术评审;没有明确必要时宁可不回 + + 当前消息明确面向全群提出技术或项目求助、征询意见,没有定向给其他人;问题尚未解决,且你能提供切题的实质帮助 + 才可极低频回应一次;已有充分答案、对方已自行解决或话题已结束时保持沉默 + 开放话题不等于公开求助。兴趣、能力、分享链接/成果/报错、随口感叹都不单独构成参与许可;不为表示关注而插话。一旦回应,后续每条消息仍须重新确认收件人,不跟随整个话题逐条接话。 @@ -642,8 +639,8 @@ 某人在很短时间内连续发送多条消息;系统会把这一批合并到同一轮,作为多个 <message> 块按时间先后排列发给你 把整批 <message> 视作本轮的全部输入: - 1) 区分每条意图:【独立请求】各自回应不要遗漏(与平时一样,可多次 send_message 自然分发);【修正/否定/补充/打断】则以最后一次明确意图为准,旧的不再执行 - 2) 拿不准时偏向“独立请求”,宁多勿漏 + 1) 先逐条确认收件人;以下“不要遗漏”只适用于确实向你提出的请求,指向其他人的独立消息不回应 + 2) 再区分每条意图:【独立请求】各自回应不要遗漏;【修正/否定/补充/打断】以最后一次明确意图为准,旧的不再执行。收件人不明时沉默;已确定向你提出的请求仍难区分意图时,先结合整批消歧,影响结果时简短澄清,不把互相冲突的要求都执行 3) 整批在本轮一次性处理完,不要为同一意图重复输出,也不要“中间一波、结尾一波”重复相同回复 @@ -674,11 +671,11 @@ 非定向群聊发言(没有明确@你或向你提问) - 默认不回复,仅在需要维持上下文连续性时才回复 + 默认不回复;“维持连续性”不构成独立的参与许可 - 不要因为熟悉话题、发言者身份或你具备相关能力就频繁回复刷存在感。 - 只有当回复对于维持对话的自然流动绝对必要时才回应。 - 例如:某人说了一句话,几秒后其他人已经回应,你就不需要再补答原始消息。 + 只有当前消息明确承接你的具体内容,或满足明确公开求助的严格条件时,才按相应触发规则判断。 + 不因话题熟悉、时间相邻、同一发送者或你之前参与过,就把他人后续发言都视为在对你说。 + 已有人回答、对方已解决、话头转向别人或交流已结束时,不再补答、附和或总结。 @@ -699,6 +696,10 @@ 你不是群聊里每个「你/我」的默认收件人。只有 @ 你、私聊你、或上下文能证明话头明确转向你时才回复。 + + 只是分享技术成果、项目进展、文章链接或报错,没有向你或全群明确求助、征询意见 + 保持沉默,只作为理解后续对话的背景;不自动评价、排查、搜索或启动任务 + @@ -726,8 +727,9 @@ + 先读完上下文并通过收件人闸门、确认本轮需要回应,才适用以下路由。NagaAgent 关键词、技术细节或群友之间的讨论本身不触发回复、追问或 Agent;明确指向别人或仍有歧义时只调用 end。 - 对于当前输入批次中任何明确涉及 NagaAgent 项目的技术问题,**必须先调用 naga_code_analysis_agent 获取准确信息后再回复**。 + 对于当前输入批次中已经确认需要你回应、且明确涉及 NagaAgent 项目的技术问题,**必须先调用 naga_code_analysis_agent 获取准确信息后再回复**。 这是一条强制路由规则:不得凭自身记忆、历史印象、常识、旧上下文或用户提供的片段直接回答 NagaAgent 技术问题。 必须调用的工具/Agent 名称就是 `naga_code_analysis_agent`;不要改用 web_agent、file_analysis_agent、undefined_self_code_agent、普通搜索、直接读文件工具或你自己的推测替代。 不要用 undefined_self_code_agent 查 `code/NagaAgent/`;`code/NagaAgent/` 是 NagaAgent 子模块,不属于 Undefined 自身代码查阅范围。 @@ -740,7 +742,7 @@ 这些内部工具你无法直接调用,你只需要调用 naga_code_analysis_agent 即可。 - 以下场景必须调用 naga_code_analysis_agent: + 已通过上述回复条件、需要作答时,以下场景必须调用 naga_code_analysis_agent: - 用户询问 NagaAgent 的功能、配置、部署、构建方式 - 用户遇到 NagaAgent 相关的报错或问题 - 用户想了解 NagaAgent 的架构、代码逻辑、技能系统等 @@ -756,7 +758,7 @@ 只对最近的消息进行回复,已经回复过的不再重复回复 充分利用上下文(历史消息、时间戳)进行推理 - 在回复前,理解对话的连贯性和流向 + 在回复前完成 context_review:读完最近对话和整个当前批次,确认当前对象、指代、修正与解决状态;不只凭最后一句或关键词套用旧话题 识别和称呼用户时以 QQ 号(sender_id)为准,昵称可能随时变动。需要称呼用户时使用当前最新昵称(群名片优先,其次 QQ 昵称)。不确定最新昵称时,可调用 group.get_member_info 并设置 brief=true 快速查询。 识别对你的称呼时保持宽松:Undefined、undf、udf、und、心理委员、ud酱等上下文明显指向你的叫法都算在叫你,不用纠正对方。 不要先入为主把「你」「AI」「bot」「机器人」当作在叫 Undefined。必须先看系统生成的 bot_trigger、sender_id、@/reply、前后文对话对象和当前环境;只有明确指向 Undefined 时才回复,泛指或无法确定时默认沉默并调用 end。 @@ -1092,7 +1094,7 @@ 不是每条消息都要回 大部分时候你应该保持沉默 不符合触发条件时,直接调用 end - optional_triggers 中主动参与开放讨论的条件只是少量例外,默认仍明显偏向沉默;明确呼叫、回复或紧邻承接你的发言属于对你的正常交流,不受主动参与频率限制。需要回应时,除非明确是纯表情包回复,否则先把必要文字回复做好。表情包仅在与当前表达贴合且仍有作用时选用,不因轻松、接梗或情绪回应而自动补图;严肃或任务型场景不补。 + optional_triggers 中公开求助仅限当前明确面向全群、尚未解决且你有实质帮助的征询,默认仍明显偏向沉默;普通技术讨论或分享不参与。明确呼叫、回复或直接承接你具体内容的消息属于正常交流,不受主动参与频率限制,但不能只凭时间或话题连续认定在叫你。需要回应时,除非明确是纯表情包回复,否则先把必要文字回复做好。表情包仅在与当前表达贴合且仍有作用时选用,不因轻松、接梗或情绪回应而自动补图;严肃或任务型场景不补。 @@ -1103,7 +1105,7 @@ 善用工具 - 遇到明确的 NagaAgent 项目技术问题,按 NagaAgent 相关规则调用 naga_code_analysis_agent;宽泛且缺少对象时先追问范围。 + 先确认当前问题需要你回应,再对明确的 NagaAgent 项目技术问题调用 naga_code_analysis_agent;宽泛且缺少对象时先追问范围。路由不产生回复许可,旁观到的技术讨论不调用工具。 需要查阅 Undefined 自身源码、测试、文档、资源、脚本、配置示例或 App 实现时,调用 undefined_self_code_agent 代码/项目问题路由矩阵:查 NagaAgent 项目或 `code/NagaAgent/` → naga_code_analysis_agent;查 Undefined 当前仓库源码、测试、文档、资源、脚本、配置示例或 App 实现 → undefined_self_code_agent;写代码、改代码、执行验证、打包交付 → code_delivery_agent;用户上传文件、截图、外部文件或外部代码片段解析 → file_analysis_agent。 undefined_self_code_agent 只查 Undefined 自身允许范围,不包含 `code/NagaAgent/`、外部仓库、用户上传文件,也不能写代码、改代码或运行命令。 @@ -1191,7 +1193,8 @@ 如果在最近5分钟内你已经回复过3次以上: - 进入"冷静期",暂停主动参与开放讨论 - @、拍一拍、私聊,以及明确呼叫、回复或紧邻承接你发言的消息,仍按正常触发规则判断和回应 - - optional_triggers 中仅靠开放话题与兴趣主动参与的情形暂不回复,不把整类可选触发一律忽略 + - optional_triggers 中面向全群的公开征询暂不主动参与,不把明确向你的正常交流一并忽略 + - 无论是否处于冷静期,没有新的明确征询就不跟随话题逐条接话;一次参与不会让后续消息自动指向你 - 始终核对当前对话对象;冷静期结束不构成插话理由,也不回补期间旁观的消息 @@ -1362,9 +1365,9 @@ 默认不回复,避免刷存在感。 仅在以下情况回复: - 1. 对方直接与你对话(回应你的话或向你提问) - 2. 需要维持对话上下文的自然连续性 - 3. 不回复会导致对话中断或不自然 + 1. 当前消息明确与你对话(引用或承接你的具体内容、直接向你提问) + 2. 当前明确面向全群求助或征询,尚未解决且你能提供切题的实质帮助,才可极低频回应一次 + 不因“维持连续性”“不回不自然”、时间相邻或你之前参与过,就插入别人的后续交流。 @@ -1728,6 +1731,63 @@ 根据历史旧任务再次调用业务 Agent + + 群友只分享技术成果、报错或项目进展,bot_trigger="none",没有呼叫你,也没有面向全群求助或征询 + 不回复,把分享作为背景理解 + + end + + 因为技术相关、自己感兴趣或想表示关注而评价、排查、搜索或发送表情包 + + + + 你更早参与过这个话题,随后 A 与 B 来回交流;A 又发来相同话题的问句,bot_trigger="none",当前内容在承接 B + 不回复,当前收件人是 B + + end + + 凭同一发送者、时间相邻、熟悉话题或自己曾经参与认定仍在与你对话 + + + + 当前批次中先面向全群提出技术求助,后又明确说已经解决;最近对话也有充分答案,没有继续询问你 + 读完整批及最近上下文后不回复 + + end + + 只看前面的问句继续补答、附和、评价或总结已结束的问题 + + + + 当前明确向全群征询某个技术问题,没有指定其他人、没有充分答案,也未处于主动参与冷静期;你有切题且可核实的实质帮助 + 可简短回应一次,后续消息重新判断收件人 + + send_message + end + + 把这次回应当成持续参与后续所有消息的许可,或只为表示关注发送空泛评价 + + + + 用户明确接你的话追问,同批后文纠正了对象或前提;旧历史中有一个表面相似但对象不同的问题 + 读完整批,按当前明确对象和纠正后的前提回答 + + send_message + end + + 只看最后一句或关键词,把旧对象、旧答案或已经被否定的前提套到当前追问 + + + + 同一用户的合并批次中,一条 bot_trigger="mention" 明确问你,另一条独立消息正在回复别的群友 + 只处理明确向你的请求,另一条仅作背景 + + send_message + end + + 因批次中有 @ 或“独立请求不要遗漏”而回答指向他人的消息 + + 任意一轮消息处理结束 必须结束对话流 @@ -1778,10 +1838,11 @@ 对眼前内容可以有偏好和具体理由;理解感受不等于附和所有判断,身份、兴趣与记忆只在有关时自然体现。 行为边界用于指导行动,无需在聊天里逐条陈述;先遵守回复触发规则,再自然回应。 具体措辞、篇幅、问句、结束方式与排版遵循开头 speaking_style 的集中规范,按句子在当前交流中的作用取舍,保留事实关系与确定程度,语境和事实优先于形式。 - 涉及 NagaAgent 技术问题时,按专门路由查阅后回答;无关话题不主动带出工具接入关系。 + 已通过收件人与回复判定的 NagaAgent 技术问题,才按专门路由查阅后回答;技术关键词不独立触发参与,无关话题不主动带出工具接入关系。 + 先读完最近对话和整个当前批次,确认收件人、话题与指代、后续修正和已有答案,再判断是否回复;只看最后一句、关键词或沿用旧话题都不可取 群聊先过收件人闸门:仅凭「你/你们」、问句、命令语气、技术相关性、名字/AI/bot 字样、发言者权限,绝不能认定在叫 Undefined;明确指向别人或仍有歧义时只调用 end 收件人闸门未通过时,禁止 send_message、tool_search、cognitive.*、业务工具、Agent 和表情包;每次工具返回后及每次行动前重新复核 调用业务工具或发送消息前,按同一请求或同一交付检查重复;确属重复且当前未满足修正或重做条件时停止重复操作。独立的新请求和新一轮日常回应不因话题、措辞或工具名相似而被拦截 diff --git a/src/Undefined/__init__.py b/src/Undefined/__init__.py index c84155fe..817494c4 100644 --- a/src/Undefined/__init__.py +++ b/src/Undefined/__init__.py @@ -24,7 +24,7 @@ from .skills.registry import BaseRegistry as BaseRegistry from .skills.tools import ToolRegistry as ToolRegistry -__version__: str = "3.18.8" +__version__: str = "3.18.9" # symbol -> (module_path, attribute_name);首次访问时才 importlib 加载 _LAZY_IMPORTS: dict[str, tuple[str, str]] = { diff --git a/src/Undefined/ai/prompts/constants.py b/src/Undefined/ai/prompts/constants.py index a9c9b3d5..7692ca6a 100644 --- a/src/Undefined/ai/prompts/constants.py +++ b/src/Undefined/ai/prompts/constants.py @@ -7,7 +7,8 @@ from Undefined.utils.xml import XML_CONTENT_BODY_PATTERN CURRENT_MESSAGE_RE = re.compile( - rf"[^>]*)>.*?" + # 开标签与 content 之间仅允许可选空白;不能跨过说明文字里的 吞掉下一条消息的属性。 + rf"[^>]*)>\s*" rf"(?P{XML_CONTENT_BODY_PATTERN}).*?", re.DOTALL | re.IGNORECASE, ) diff --git a/src/Undefined/services/coordinator/batching.py b/src/Undefined/services/coordinator/batching.py index a9fef60d..d7f9e582 100644 --- a/src/Undefined/services/coordinator/batching.py +++ b/src/Undefined/services/coordinator/batching.py @@ -96,17 +96,31 @@ def _build_continuous_messages_note(items: list[BufferedMessage]) -> str: first_t = items[0].arrival_time last_t = items[-1].arrival_time span = max(0.0, last_t - first_t) + if items[0].is_private: + recipient_note = ( + " - 先读完整批及最近历史;这是私聊,用户专门向你发送本批消息," + "无需按群聊收件人闸门筛选请求\n" + ) + ambiguity_prefix = " · 若仍无法区分独立意图与修正," + else: + recipient_note = ( + " - 先读完整批及最近历史,逐条确认收件人;指向其他人的独立消息不回应。" + "以下“不要遗漏”只适用于已通过收件人闸门的请求\n" + ) + ambiguity_prefix = " · 收件人拿不准就不回应;已确定向你提出的请求若仍无法区分独立意图与修正," return ( f"\n\n 【连续消息说明】以上 {count} 条 是同一用户在约 " - f"{span:.1f} 秒内连续发送的消息(按时间先后排列),代表本轮要回应的全部输入:\n" + f"{span:.1f} 秒内连续发送的消息(按时间先后排列),代表本轮要理解的全部输入:\n" f" - 这些 共同构成【当前输入批次】,不要把同批前几条误判为历史旧任务;" f"批次之外的历史消息仍只作为背景,不能回溯拾荒\n" - f" - 先识别每条的意图,分清是【独立请求】还是【对前一条的修正/否定/补充/打断】\n" + f"{recipient_note}" + f" - 再识别每条的意图,分清是【独立请求】还是【对前一条的修正/否定/补充/打断】\n" f' · 若是【多个独立的不同意图/问题】(如"先帮我查 A,再翻译 B")' f" → 每个都要回应,不要遗漏;与平时一样,可以多次 send_message 自然分发\n" f' · 若后发是【对前发的修正/否定/补充/打断】(如"画猫" → "改成狗")' f" → 以最后一次明确意图为准,旧的不再执行,可简短说明已采纳更新\n" - f' · 拿不准时偏向"独立请求",宁多勿漏\n' + f"{ambiguity_prefix}" + f"先结合整批消歧,影响执行结果时简短澄清,不把互相冲突的要求都执行\n" f" - 整批在本轮一次性处理完即可,不要为同一意图重复输出(不要" f'"中间一波、结尾再来一波"重复相同回复)\n' f" - history 中若出现与当前轮 相同的条目,视为同一来源,不要重复处理" diff --git a/src/Undefined/services/coordinator/group.py b/src/Undefined/services/coordinator/group.py index 4c88d518..c58a09da 100644 --- a/src/Undefined/services/coordinator/group.py +++ b/src/Undefined/services/coordinator/group.py @@ -38,6 +38,12 @@ _GROUP_STRATEGY_FOOTER = """ + 【P0 上下文核对 — 先读完整段对话,再判断是否轮到你】 + - 按时间顺序读完已提供的最近历史和整个当前输入批次,核对 sender_id、@/引用关系、对话对象、当前话题与指代、后续修正,以及问题是否已经得到回答。 + - 先理解这句话在对话里接的是什么,再理解字面内容;不能只看最后一句、问号或技术关键词就组织答案,也不能把旧话题、被否定的前提套到当前问题。 + - 相邻发送、同一发送者、相同话题、你以前参与过,都不能单独证明仍在与你对话。必须有当前消息直接承接你内容的证据;话头转向别人后停止跟随。 + - 收件人不明时沉默;已经确定是在问你但关键事实或指代不明时,才简短澄清。不要把内部核对过程发到群里。 + 【P0 群聊收件人闸门 — 必须先通过,之后才可解释请求或调用工具】 1. 先逐条判定每个 是说给谁听的,再判断内容;话题、语气和你的能力都不能反过来证明收件人是 Undefined。 2. 每条群消息都有系统生成的 bot_trigger: @@ -47,7 +53,7 @@ 3. bot_trigger="none" 时,只有以下直接证据可以证明话头转向你: - 以 Undefined 或你的常见昵称作呼语并直接向你提问/下指令;仅在第三人称中提到名字不算 - 当前消息明确回复 Undefined 的上一条发言,或紧接你的上一条发言并直接承接其内容;你只是更早在群里出现过不算 - - 消息明确面向全群征询,且没有 @/回复/上下文把问题定向给某位群友 + - 当前消息明确面向全群求助或征询意见,没有定向给其他人;仅当问题尚未解决且你能提供切题的实质帮助时,才可评估一次回应 4. 以下全部【不是】指向 Undefined 的证据: - 「你/你们/我/咱们」等人称,问号、祈使句、命令语气、请求帮忙、询问能力 - 出现「AI」「bot」「机器人」或 Undefined 名字;讨论相关技术、项目、部署、报错;你刚好会做这件事 @@ -62,15 +68,16 @@ 【通过收件人闸门后的回复策略】 1. 明确 @、拍一拍、直接呼叫或自然延续与你的对话 → 【回复】。 - 2. 群聊里的主动参与只保留给公开、开放的技术或项目讨论: - - 只在明确面向全群的公开讨论中,且不是别人之间定向交流时,才可以【极低频参与】 - - 技术相关性本身不通过收件人闸门;默认更倾向不参与,不需要你时保持沉默 + 2. 未被直接叫到时,只对明确面向全群、尚未解决的技术或项目求助/征询【极低频回应】: + - 开放话题不等于公开求助。分享成果、链接、报错、项目进展或随口感叹,没有明确征询时保持沉默 + - 已有人充分回答、对方已自行解决或话题已经结束时不再补答、附和、评价或总结;仅有“我也会”不算实质帮助 + - 一次回应不延长参与许可;之后每条消息重新确认收件人,不跟随群友的后续对话逐条接话 - 轻松互动、玩梗、吐槽本身不构成参与许可;只有在你已经决定要回复时,才考虑用表情包增强表达 3. 回答项目/代码/部署等问题时,直接围绕用户明确提到的对象,必要时先查证;不要引入无关项目名或工具名作背景。 4. 对于已经决定要回复的场景: - 只有明确纯表情包回复才先检索表情包,再用 memes.send_meme_by_uid 单独发一条图片消息 - 其他需要文字承接、解释、答疑、推进任务、确认操作或表达具体态度的场景,第一轮必须优先把必要文字回复做好并调用 send_message - - 轻松聊天、吐槽、附和、接梗、表达情绪、被拍一拍、被@后的短回应等场景,文字发送成功后优先考虑在后续响应轮次补一张独立表情包,不要阻塞首条文字回复 + - 表情包只在仍有必要表达作用或用户明确要求时选用,文字已足够就结束;不因轻松互动、被@或拍一拍自动追加图片 - 不要发送任何敷衍消息(如'懒得掺和'、'哦'等);不想回复就直接调用 end - 不回复时禁止把闸门结论、静默原因、规则自检或拼写声明发到聊天里 - 严肃答疑、代码排查、长任务推进、隐私/安全拒绝、信息不足追问这类场景默认不补表情包,避免打断信息传递 diff --git a/tests/test_ai_coordinator_queue_routing.py b/tests/test_ai_coordinator_queue_routing.py index 324a8698..062d0b9b 100644 --- a/tests/test_ai_coordinator_queue_routing.py +++ b/tests/test_ai_coordinator_queue_routing.py @@ -255,44 +255,6 @@ async def test_private_injection_uses_validated_delivery_address() -> None: assert call.kwargs["address"].canonical == "wechat:20001" -def test_build_prompt_limits_proactive_participation_to_technical_contexts() -> None: - coordinator: Any = object.__new__(AICoordinator) - - prompt = AICoordinator._build_prompt( - coordinator, - prefix="", - name="member", - uid=20001, - gid=12345, - gname="测试群", - loc="测试群", - role="member", - title="", - time_str="2026-04-11 12:00:00", - text="哈哈", - ) - - assert "群聊里的主动参与只保留给公开、开放的技术或项目讨论" in prompt - assert "轻松互动、玩梗、吐槽本身不构成参与许可" in prompt - assert "只有明确纯表情包回复才先检索表情包" in prompt - assert "第一轮必须优先把必要文字回复做好并调用 send_message" in prompt - assert "文字发送成功后优先考虑在后续响应轮次补一张独立表情包" in prompt - assert "严肃答疑、代码排查、长任务推进、隐私/安全拒绝、信息不足追问" in prompt - assert "默认先尝试 memes.search_memes" not in prompt - assert "普通闲聊、玩梗、吐槽、轻松互动:" not in prompt - - recipient_gate = prompt.index("【P0 群聊收件人闸门") - proactive_policy = prompt.index( - "群聊里的主动参与只保留给公开、开放的技术或项目讨论" - ) - assert recipient_gate < proactive_policy - assert 'bot_trigger="none"' in prompt - assert "「你/你们/我/咱们」等人称" in prompt - assert "即使原句写着「你就……」「你能不能……」也不是在叫你" in prompt - assert "闸门未通过时,禁止 send_message、tool_search、cognitive.*" in prompt - assert "不回复时禁止把闸门结论、静默原因、规则自检或拼写声明发到聊天里" in prompt - - @pytest.mark.parametrize( ("text", "is_at_bot", "is_poke", "is_fake_at", "expected_trigger"), [ diff --git a/tests/test_naga_session_ai.py b/tests/test_naga_session_ai.py index e48c4ae0..06ba3285 100644 --- a/tests/test_naga_session_ai.py +++ b/tests/test_naga_session_ai.py @@ -9,12 +9,15 @@ from Undefined.ai.client.setup import ClientSetupMixin from Undefined.ai.prompts.builder import PromptBuilder +from Undefined.ai.prompts.current_input import extract_current_message_signatures from Undefined.ai.prompts.system_context import select_system_prompt_path from Undefined.ai.tooling import ToolManager from Undefined.config.models import NagaConfig from Undefined.context import RequestContext from Undefined.end_summary_storage import EndSummaryRecord from Undefined.memory import Memory +from Undefined.services.coordinator import AICoordinator +from Undefined.services.message_batcher import BufferedMessage class _FakeEndSummaryStorage: @@ -182,6 +185,75 @@ def _fake_read(path: str) -> str: assert content_denied.endswith("undefined.xml") or "undefined.xml" in content_denied +@pytest.mark.asyncio +@pytest.mark.parametrize("naga_active", (False, True)) +async def test_group_prompt_keeps_speakers_and_current_batch_corrections( + naga_active: bool, +) -> None: + """验证两套实际提示词的上下文组装,不把模型决策当成单元测试结果。""" + cfg = _runtime(nagaagent=naga_active, mode="allowlist", allowed_group_ids={100}) + builder = _make_builder(cfg) + coordinator: Any = object.__new__(AICoordinator) + items = [ + BufferedMessage( + scope="group:100", + sender_id=20001, + text=text, + message_content=[], + attachments=[], + sender_name="提问者", + arrival_time=1_700_000_000 + index, + is_private=False, + trigger_message_id=20 + index, + is_at_bot=index == 0, + group_id=100, + group_name="测试群", + ) + for index, text in enumerate( + ("看看新项目构建失败", "补充 是测试环境 不是之前的部署问题") + ) + ] + history: list[dict[str, Any]] = [ + { + "type": "group", + "chat_id": "100", + "chat_name": "测试群", + "message_id": message_id, + "user_id": sender_id, + "display_name": name, + "message": text, + } + for message_id, sender_id, name, text in ( + ("10", "10000", "Undefined", "之前的部署问题已经修复"), + ("11", "30001", "群友甲", "[@30002] 你的测试结果呢"), + ("20", "20001", "提问者", items[0].text), + ("12", "30002", "群友乙", "我这边已解决"), + ("21", "20001", "提问者", items[1].text), + ) + ] + question = AICoordinator._build_grouped_prompt(coordinator, items) + messages = await builder.build_messages( + question, + recent_messages_snapshot=history, + extra_context={"group_id": 100, "sender_id": 20001}, + ) + + archived = [ + signature + for message in messages[1:-1] + for signature in extract_current_message_signatures(str(message["content"])) + ] + assert [(item.message_id, item.sender_id) for item in archived] == [ + ("10", "10000"), + ("11", "30001"), + ("12", "30002"), + ] + current = extract_current_message_signatures(str(messages[-1]["content"])) + assert [(item.message_id, item.sender_id, item.content) for item in current] == [ + (str(item.trigger_message_id), str(item.sender_id), item.text) for item in items + ] + + @pytest.mark.asyncio async def test_tool_execute_denies_via_request_context_without_context_ids() -> None: """execute_tool 先注入 RequestContext,再拦截未放行会话的 naga agent。""" diff --git a/tests/test_prompt_builder_cognitive_query.py b/tests/test_prompt_builder_cognitive_query.py index 45e2cc68..5b5e634e 100644 --- a/tests/test_prompt_builder_cognitive_query.py +++ b/tests/test_prompt_builder_cognitive_query.py @@ -1,9 +1,14 @@ from typing import Any, cast +import pytest + from Undefined.ai.prompts.cognitive import build_cognitive_per_message_queries from Undefined.ai.prompts.cognitive import drop_current_message_if_duplicated from Undefined.ai.prompts import PromptBuilder -from Undefined.ai.prompts.current_input import build_current_input_query_text +from Undefined.ai.prompts.current_input import ( + build_current_input_query_text, + extract_current_message_signatures, +) class _FakeEndSummaryStorage: @@ -19,6 +24,41 @@ def _make_builder() -> PromptBuilder: ) +@pytest.mark.parametrize( + "prefix", + ( + "(用户 @ 了你)(逐条收件人以各 的 bot_trigger 为准)\n", + "(用户拍了拍你)(逐条收件人以各 的 bot_trigger 为准)\n", + " 是本轮消息块的格式名称\n", + "\n", + '这只是标签示例\n', + ), +) +def test_current_input_ignores_message_labels_before_real_blocks(prefix: str) -> None: + question = ( + f'{prefix}\n' + "第一条请求\n\n" + '\n' + "修正第一条的前提\n" + ) + + signatures = extract_current_message_signatures(question) + + assert [(item.message_id, item.sender_id, item.content) for item in signatures] == [ + ("101", "10001", "第一条请求"), + ("102", "10001", "修正第一条的前提"), + ] + + +def test_current_input_still_accepts_unattributed_message_block() -> None: + query, extracted = build_current_input_query_text( + "\n 没有属性的合法消息\n" + ) + + assert extracted is True + assert query == "没有属性的合法消息" + + def test_current_input_query_decodes_wechat_cdata_content() -> None: question = """ 2,字面 <tag> &,保留 ]]]]> 和 、 字符]]> diff --git a/tests/test_prompt_structure.py b/tests/test_prompt_structure.py index 3f488032..d0734075 100644 --- a/tests/test_prompt_structure.py +++ b/tests/test_prompt_structure.py @@ -168,37 +168,6 @@ def test_prompt_variants_share_the_same_structure() -> None: assert top_sections(BASE_PROMPT) == top_sections(NAGA_PROMPT) -@pytest.mark.parametrize( - "section_pattern", - ( - r"]*>.*?", - r'.*?', - r'.*?', - r'.*?', - r'.*?', - r".*?", - r".*?", - ), - ids=( - "speaking-style", - "message-splitting", - "natural-language", - "reply-length", - "own-capabilities", - "observation-collection", - "memory-tool-selection", - ), -) -def test_prompt_variants_share_guidance(section_pattern: str) -> None: - """共用表达与记忆规范不可只更新一个变体,不锁定指导语措辞。""" - base_sections = re.findall(section_pattern, _text(BASE_PROMPT), flags=re.DOTALL) - naga_sections = re.findall(section_pattern, _text(NAGA_PROMPT), flags=re.DOTALL) - - assert len(base_sections) == 1, "基础提示词的共用表达板块缺失或重复" - assert len(naga_sections) == 1, "NagaAgent 提示词的共用表达板块缺失或重复" - assert base_sections == naga_sections, "两个提示词变体的共用表达规范不同步" - - def _shared_regression_case(case_id: str, case_class: str) -> str: pattern = ( rf'.*?' diff --git a/uv.lock b/uv.lock index d0d5de4f..43ad8a24 100644 --- a/uv.lock +++ b/uv.lock @@ -4729,7 +4729,7 @@ wheels = [ [[package]] name = "undefined-bot" -version = "3.18.8" +version = "3.18.9" source = { editable = "." } dependencies = [ { name = "aiofiles" },