diff --git a/core/main_terminal.py b/core/main_terminal.py index d178018..4479611 100644 --- a/core/main_terminal.py +++ b/core/main_terminal.py @@ -142,6 +142,8 @@ class MainTerminal: self._refresh_disabled_tools() self.thinking_fast_interval = THINKING_FAST_INTERVAL self.default_disabled_tool_categories: List[str] = [] + # 个性化工具意图开关 + self.tool_intent_enabled: bool = False self.apply_personalization_preferences() # 新增:自动开始新对话 @@ -337,6 +339,9 @@ class MainTerminal: except Exception: effective_config = {} + # 工具意图开关 + self.tool_intent_enabled = bool(effective_config.get("tool_intent_enabled")) + interval = effective_config.get("thinking_interval") if isinstance(interval, int) and interval > 0: self.thinking_fast_interval = interval @@ -1053,6 +1058,57 @@ class MainTerminal: # ===== 保持原有的其他方法不变,只需要小修改 ===== + def _inject_intent(self, properties: Dict[str, Any]) -> Dict[str, Any]: + """在工具参数中注入 intent(简短意图说明),仅当开关启用时。 + + 字段含义:要求模型用不超过15个中文字符对即将执行的动作做简要说明,供前端展示。 + """ + if not self.tool_intent_enabled: + return properties + if not isinstance(properties, dict): + return properties + intent_field = { + "intent": { + "type": "string", + "description": "用不超过15个字向用户说明你要做什么,例如:等待下载完成/创建日志文件" + } + } + # 将 intent 放在最前面以提高模型关注度 + return {**intent_field, **properties} + + def _apply_intent_to_tools(self, tools: List[Dict]) -> List[Dict]: + """遍历工具列表,为缺少 intent 的工具补充字段(开关启用时生效)。""" + if not self.tool_intent_enabled: + return tools + intent_field = { + "intent": { + "type": "string", + "description": "用不超过15个字向用户说明你要做什么,例如:等待下载完成/创建日志文件/搜索最新新闻" + } + } + for tool in tools: + func = tool.get("function") or {} + params = func.get("parameters") or {} + if not isinstance(params, dict): + continue + if params.get("type") != "object": + continue + props = params.get("properties") + if not isinstance(props, dict): + continue + # 补充 intent 属性 + if "intent" not in props: + params["properties"] = {**intent_field, **props} + # 将 intent 加入必填 + required_list = params.get("required") + if isinstance(required_list, list): + if "intent" not in required_list: + required_list.insert(0, "intent") + params["required"] = required_list + else: + params["required"] = ["intent"] + return tools + def define_tools(self) -> List[Dict]: """定义可用工具(添加确认工具)""" current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") @@ -1065,7 +1121,7 @@ class MainTerminal: "description": "等待指定的秒数。用于等待长时间操作完成,如安装包、编译、服务启动等。当终端或进程需要时间完成操作时使用。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "seconds": { "type": "number", "description": "等待的秒数,可以是小数(如2.5秒)。建议范围:0.5-30秒" @@ -1074,7 +1130,7 @@ class MainTerminal: "type": "string", "description": "等待的原因说明(可选)" } - }, + }), "required": ["seconds"] } } @@ -1086,11 +1142,11 @@ class MainTerminal: "description": "创建新文件(仅创建空文件,正文请使用 write_file_diff 提交补丁)", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "path": {"type": "string", "description": "文件路径"}, "file_type": {"type": "string", "enum": ["txt", "py", "md"], "description": "文件类型"}, "annotation": {"type": "string", "description": "文件备注"} - }, + }), "required": ["path", "file_type", "annotation"] } } @@ -1102,7 +1158,7 @@ class MainTerminal: "description": "读取/搜索/抽取 UTF-8 文本文件内容。通过 type 参数选择 read(阅读)、search(搜索)、extract(具体行段),支持限制返回字符数。若文件非 UTF-8 或过大,请改用 run_python。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "path": {"type": "string", "description": "文件路径"}, "type": { "type": "string", @@ -1164,7 +1220,7 @@ class MainTerminal: }, "minItems": 1 } - }, + }), "required": ["path", "type"] } } @@ -1176,10 +1232,10 @@ class MainTerminal: "description": "使用 Qwen3-VL模型 读取图片中的文字或根据提示生成描述,仅支持本地图片路径。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "path": {"type": "string", "description": "项目内的图片路径"}, "prompt": {"type": "string", "description": "传递给 OCR 模型的提示词,如“请识别图片中的文字”,“图中的手机是什么颜色的”必须使用中文提示词。"} - }, + }), "required": ["path", "prompt"] } } @@ -1191,9 +1247,9 @@ class MainTerminal: "description": "删除文件", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "path": {"type": "string", "description": "文件路径"} - }, + }), "required": ["path"] } } @@ -1205,10 +1261,10 @@ class MainTerminal: "description": "重命名文件", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "old_path": {"type": "string", "description": "原文件路径"}, "new_path": {"type": "string", "description": "新文件路径"} - }, + }), "required": ["old_path", "new_path"] } } @@ -1220,13 +1276,13 @@ class MainTerminal: "description": "使用统一 diff(`@@` 块、`-`/`+`/空格行)对单个文件做精确编辑:追加、插入、替换、删除都可以在一次调用里完成。\\n硬性规则:\\n\\n1) 补丁必须被 `*** Begin Patch` 与 `*** End Patch` 包裹。\\n2) 每个修改块必须以 `@@ [id:数字]` 开头。\\n3) 块内每一行只能是三类之一:\\n - 上下文行:以空格开头(` ␠`),表示“文件里必须原样存在”的锚点;\\n - 删除行:以 `-` 开头,表示要从文件中移除的原文;\\n - 新增行:以 `+` 开头,表示要写入的新内容。\\n4) 任何“想新增/想删除/想替换”的内容都必须逐行写 `+` 或 `-`;空行也必须写成单独一行的 `+`(这一行只有 `+` 和换行);如果你把多行新内容直接贴上去却不加 `+`,它会被当成上下文锚点去匹配原文件,极易导致“未找到匹配的原文”。\\n5) 重要语义:一个块里如果完全没有上下文行(空格开头)也没有删除行(`-`),那么它会被视为“仅追加(append-only)”,也就是把所有 `+` 行追加到文件末尾——这对“给空文件写正文”很合适,但对“插入到中间”是错误的。\\n\\n正面案例(至少 5 个,且都包含多行原文/多处修改)\\n\\n1) 给空文件写完整正文(追加到末尾;空文件=正确)\\n目标:新建 README.md 后一次性写入标题、安装、用法、FAQ(多段落、多行)。\\n要点:没有上下文/删除行 → 追加模式;空文件时最常用。\\n\\n*** Begin Patch\\n@@ [id:1]\\n+# 项目名称\\n+\\n+一个简短说明:这个项目用于……\\n+\\n+## 安装\\n+\\n+```bash\\n+pip install -r requirements.txt\\n+```\\n+\\n+## 快速开始\\n+\\n+```bash\\n+python main.py\\n+```\\n+\\n+## 常见问题\\n+\\n+- Q: 为什么会报 xxx?\\n+ A: 先检查 yyy,再确认 zzz。\\n+\\n*** End Patch\\n\\n2) “不删除,直接插入内容”到函数内部(必须用上下文锚定插入位置)\\n目标:在 def build_prompt(...): 里插入日志与参数归一化,但不改动其它行。\\n要点:插入发生在“两个上下文行之间”,上下文必须精确(包含缩进)。\\n\\n*** Begin Patch\\n@@ [id:1]\\n def build_prompt(user_text: str, system_text: str, tags: list):\\n prompt_parts = []\\n+ # 参数归一化:去掉首尾空白,避免模型误判\\n+ user_text = (user_text or \\\"\\\").strip()\\n+ system_text = (system_text or \\\"\\\").strip()\\n+\\n+ logger.debug(\\\"build_prompt: tags=%s, user_len=%d\\\", tags, len(user_text))\\n prompt_parts.append(system_text)\\n prompt_parts.append(user_text)\\n if tags:\\n prompt_parts.append(\\\"TAGS: \\\" + \\\",\\\".join(tags))\\n*** End Patch\\n\\n3) 复杂替换:整段函数重构(多行 old/new + 保留稳定上下文)\\n目标:把旧的 apply_patch()(弱校验)替换成新实现(多分支、异常信息更清晰)。\\n要点:替换不是“改一行”,而是“删一段、加一段”,并用函数签名/相邻代码作锚点。\\n\\n*** Begin Patch\\n@@ [id:1]\\n class FilePatcher:\\n def __init__(self, root: Path):\\n self.root = root\\n\\n def apply_patch(self, path: str, patch_text: str) -> dict:\\n- # old: naive replace\\n- content = (self.root / path).read_text(encoding=\\\"utf-8\\\")\\n- content = content.replace(\\\"foo\\\", \\\"bar\\\")\\n- (self.root / path).write_text(content, encoding=\\\"utf-8\\\")\\n- return {\\\"success\\\": True}\\n+ full_path = (self.root / path).resolve()\\n+ if self.root not in full_path.parents and full_path != self.root:\\n+ return {\\\"success\\\": False, \\\"error\\\": \\\"非法路径:越界访问\\\"}\\n+\\n+ if \\\"*** Begin Patch\\\" not in patch_text or \\\"*** End Patch\\\" not in patch_text:\\n+ return {\\\"success\\\": False, \\\"error\\\": \\\"补丁格式错误:缺少 Begin/End 标记\\\"}\\n+\\n+ try:\\n+ original = full_path.read_text(encoding=\\\"utf-8\\\")\\n+ except Exception as e:\\n+ return {\\\"success\\\": False, \\\"error\\\": f\\\"读取失败: {e}\\\"}\\n+\\n+ # 这里省略:解析 blocks、逐块应用、失败回滚等\\n+ updated = original\\n+ try:\\n+ full_path.write_text(updated, encoding=\\\"utf-8\\\")\\n+ except Exception as e:\\n+ return {\\\"success\\\": False, \\\"error\\\": f\\\"写入失败: {e}\\\"}\\n+\\n+ return {\\\"success\\\": True, \\\"message\\\": \\\"已应用补丁\\\"}\\n*** End Patch\\n\\n4) 复杂多块:同一文件里同时“加 import + 替换逻辑 + 插入新 helper + 删除旧函数”\\n目标:一次调用完成 4 种操作,且每块都有足够上下文,避免误匹配。\\n要点:不同区域用不同 @@ [id:n] 分块,互不干扰。\\n\\n*** Begin Patch\\n@@ [id:1]\\n-import json\\n+import json\\n+import re\\n from pathlib import Path\\n\\n@@ [id:2]\\n def normalize_user_input(text: str) -> str:\\n- return text\\n+ text = (text or \\\"\\\").strip()\\n+ # 压缩多余空白,减少提示词抖动\\n+ text = re.sub(r\\\"\\\\\\\\s+\\\", \\\" \\\", text)\\n+ return text\\n\\n@@ [id:3]\\n def load_config(path: str) -> dict:\\n cfg_path = Path(path)\\n if not cfg_path.exists():\\n return {}\\n data = cfg_path.read_text(encoding=\\\"utf-8\\\")\\n return json.loads(data)\\n+\\n+def safe_get(cfg: dict, key: str, default=None):\\n+ if not isinstance(cfg, dict):\\n+ return default\\n+ return cfg.get(key, default)\\n\\n@@ [id:4]\\n-def legacy_parse_flags(argv):\\n- # deprecated, kept for compatibility\\n- flags = {}\\n- for item in argv:\\n- if item.startswith(\\\"--\\\"):\\n- k, _, v = item[2:].partition(\\\"=\\\")\\n- flags[k] = v or True\\n- return flags\\n-\\n def main():\\n cfg = load_config(\\\"config.json\\\")\\n # ...\\n*** End Patch\\n\\n5) 删除示例:删除一整段“废弃配置块”,并顺手修正周围空行(多行删除 + 上下文)\\n目标:删掉 DEPRECATED_* 配置和旧注释,确保删除位置精确。\\n要点:删除行必须逐行 `-`;保留上下文行确保定位。\\n\\n*** Begin Patch\\n@@ [id:1]\\n # ==============================\\n # Runtime Config\\n # ==============================\\n-DEPRECATED_TIMEOUT = 5\\n-DEPRECATED_RETRIES = 1\\n-# 注意:这些字段将在下个版本移除\\n-# 请迁移到 NEW_TIMEOUT / NEW_RETRIES\\n NEW_TIMEOUT = 30\\n NEW_RETRIES = 3\\n*** End Patch\\n\\n如何写“带上下文”的正确姿势(要点)\\n\\n- 上下文要选“稳定锚点”:函数签名、类名、关键注释、紧邻的两三行缩进代码。\\n- 不要用“容易变的行”当唯一锚点:时间戳、日志序号、随机 id、生成内容片段。\\n- 上下文必须字节级一致(空格/Tab/大小写/标点都算),否则会匹配失败。\\n\\n反面案例(至少 3 个,且都是“真实会踩坑”的类型)\\n\\n反例 A(来自一次常见错误):空文件时只有第一行加了 `+`,后面直接贴正文\\n这会让后面的正文变成“上下文锚点”,工具会去空文件里找这些原文,必然失败(常见报错:未找到匹配的原文)。\\n\\n*** Begin Patch\\n@@ [id:1]\\n+\\n仰望U9X·电驭苍穹\\n银箭破空电光闪\\n三千马力云中藏\\n*** End Patch\\n\\n正确做法:正文每一行都要写 `+`(包括空行也写 `+`),空行写法是一行单独的 `+`。\\n\\n(反例:空行没加 `+`,会被当成上下文,空文件/定位修改时容易失败)\\n\\n*** Begin Patch\\n@@ [id:1]\\n+title = \\\"示例\\\"\\n\\n+[db]\\n+enabled = true\\n*** End Patch\\n\\n(正确:空行也要用 `+` 表示)\\n\\n*** Begin Patch\\n@@ [id:1]\\n+title = \\\"示例\\\"\\n+\\n+[db]\\n+enabled = true\\n*** End Patch\\n\\n(对应的正确 patch 示例:向空文件追加多行)\\n\\n*** Begin Patch\\n@@ [id:1]\\n+\\n+仰望U9X·电驭苍穹\\n+银箭破空电光闪\\n+三千马力云中藏\\n*** End Patch\\n\\n反例 B:想“插入到中间”,却只写 `+`(没有任何上下文/删除行)\\n这种块会被当成“追加到文件末尾”,结果内容跑到文件最后,不会插入到你以为的位置。\\n\\n*** Begin Patch\\n@@ [id:1]\\n+# 我以为会插到某个函数上面\\n+print(\\\"hello\\\")\\n*** End Patch\\n\\n正确做法:用上下文锚定插入点(见正面案例 2)。\\n\\n(对应的正确 patch 示例:用上下文把内容插入到函数内部,而不是追加到文件末尾)\\n\\n*** Begin Patch\\n@@ [id:1]\\n def main():\\n config = load_config(\\\"config.json\\\")\\n+ # 这里插入:启动提示(不会移动到文件末尾)\\n+ print(\\\"hello\\\")\\n run(config)\\n*** End Patch\\n\\n反例 C:补丁在第一个 `@@` 之前出现内容 / 或漏掉 Begin/End 标记\\n解析会直接报格式错误(例如:“在检测到第一个 @@ 块之前出现内容”、“缺少 Begin/End 标记”)。\\n\\n(错误形态示意)\\n这里先写了一段说明文字(没有 @@)\\n@@ [id:1]\\n+...\\n\\n正确做法:确保第一段非空内容必须从 `@@ [id:n]` 开始,并且整体有 Begin/End。\\n\\n(对应的正确 patch 示例:完整结构、第一段内容从 @@ 块开始)\\n\\n*** Begin Patch\\n@@ [id:1]\\n # ==============================\\n # Runtime Config\\n # ==============================\\n+# 说明:此处新增一行注释作为示例\\n NEW_TIMEOUT = 30\\n*** End Patch\\n", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "path": {"type": "string", "description": "目标文件路径(相对项目根目录)。"}, "patch": { "type": "string", "description": "完整补丁文本,格式类似 unified diff,必须用 *** Begin Patch / *** End Patch 包裹,并用 @@ [id:数字] 划分多个修改块。示例:*** Begin Patch\\n@@ [id:1]\\n def main():\\n- def greet(self):\\n- return \"hi\"\\n+ def greet(self, name: str) -> str:\\n+ message = f\"Hello, {name}!\"\\n+ return message\\n@@ [id:2]\\n+\\n+if __name__ == \"__main__\":\\n+ print(\"hello world\")\\n*** End Patch" } - }, + }), "required": ["path", "patch"] } } @@ -1238,9 +1294,9 @@ class MainTerminal: "description": "创建文件夹", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "path": {"type": "string", "description": "文件夹路径"} - }, + }), "required": ["path"] } } @@ -1252,9 +1308,9 @@ class MainTerminal: "description": "聚焦 UTF-8 文本文件,将完整内容持续注入上下文。适合频繁查看/修改的核心文件;超过字符限制或非 UTF-8 时会拒绝。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "path": {"type": "string", "description": "文件路径"} - }, + }), "required": ["path"] } } @@ -1266,9 +1322,9 @@ class MainTerminal: "description": "取消聚焦文件,从上下文中移除", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "path": {"type": "string", "description": "文件路径"} - }, + }), "required": ["path"] } } @@ -1280,7 +1336,7 @@ class MainTerminal: "description": "管理持久化终端会话,可打开、关闭、列出或切换终端。请在授权工作区内执行命令,禁止启动需要完整 TTY 的程序(python REPL、vim、top 等)。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "action": { "type": "string", "enum": ["open", "close", "list", "switch"], @@ -1294,7 +1350,7 @@ class MainTerminal: "type": "string", "description": "工作目录,相对于项目路径(open时可选)" } - }, + }), "required": ["action"] } } @@ -1306,7 +1362,7 @@ class MainTerminal: "description": "向活动终端发送命令或输入。禁止启动会占用终端界面的程序(python/node/nano/vim 等);如遇卡死请结合 terminal_snapshot 并使用 terminal_reset 恢复。必须提供 timeout,超时仅终止当前命令,终端保持可用。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "command": { "type": "string", "description": "要执行的命令或发送的输入" @@ -1319,7 +1375,7 @@ class MainTerminal: "type": "number", "description": "等待输出的最长秒数,必填,最大300" } - }, + }), "required": ["command", "timeout"] } } @@ -1331,7 +1387,7 @@ class MainTerminal: "description": "获取指定终端最近的输出快照,用于判断当前状态。默认返回末尾的50行,可通过参数调整。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "session_name": { "type": "string", "description": "目标终端会话名称(可选,默认活动终端)" @@ -1344,7 +1400,7 @@ class MainTerminal: "type": "integer", "description": "返回的最大字符数(可选)" } - } + }) } } }, @@ -1355,12 +1411,12 @@ class MainTerminal: "description": "重置指定终端:关闭当前进程并重新创建同名会话,用于从卡死或非法状态中恢复。请在总结中说明重置原因。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "session_name": { "type": "string", "description": "目标终端会话名称(可选,默认活动终端)" } - } + }) } } }, @@ -1371,7 +1427,7 @@ class MainTerminal: "description": f"当现有资料不足时搜索外部信息(当前时间 {current_time})。调用前说明目的,精准撰写 query,并合理设置时间/主题参数;避免重复或无意义的搜索。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "query": { "type": "string", "description": "搜索查询内容(不要包含日期或时间范围)" @@ -1404,7 +1460,7 @@ class MainTerminal: "type": "string", "description": "国家过滤,仅 topic=general 可用,使用英文小写国名" } - }, + }), "required": ["query"] } } @@ -1416,9 +1472,9 @@ class MainTerminal: "description": "在 web_search 结果不够详细时提取网页正文。调用前说明用途,注意提取内容会消耗大量 token,超过80000字符将被拒绝。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "url": {"type": "string", "description": "要提取内容的网页URL"} - }, + }), "required": ["url"] } } @@ -1430,10 +1486,10 @@ class MainTerminal: "description": "提取网页内容并保存为纯文本文件,适合需要长期留存的长文档。请提供网址与目标路径(含 .txt 后缀),落地后请通过终端命令查看。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "url": {"type": "string", "description": "要保存的网页URL"}, "target_path": {"type": "string", "description": "保存位置,包含文件名,相对于项目根目录"} - }, + }), "required": ["url", "target_path"] } } @@ -1445,13 +1501,13 @@ class MainTerminal: "description": "执行一次性 Python 脚本,可用于处理二进制或非 UTF-8 文件(如 Excel、Word、PDF、图片),或进行数据分析与验证。必须提供 timeout(最长60秒);超时会尝试中断并返回已捕获输出。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "code": {"type": "string", "description": "Python代码"}, "timeout": { "type": "number", "description": "超时时长(秒),必填,最大60" } - }, + }), "required": ["code", "timeout"] } } @@ -1463,13 +1519,13 @@ class MainTerminal: "description": "执行一次性终端命令,适合查看文件信息(file/ls/stat/iconv 等)、转换编码或调用 CLI 工具。禁止启动交互式程序;对已聚焦文件仅允许使用 grep -n 等定位命令。必须提供 timeout(最长30秒),超时会尝试中断并返回已捕获输出;输出超过10000字符将被截断或拒绝。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "command": {"type": "string", "description": "终端命令"}, "timeout": { "type": "number", "description": "超时时长(秒),必填,最大30" } - }, + }), "required": ["command", "timeout"] } } @@ -1481,12 +1537,12 @@ class MainTerminal: "description": "按条目更新记忆列表(自动编号)。append 追加新条目;replace 用序号替换;delete 用序号删除。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "memory_type": {"type": "string", "enum": ["main", "task"], "description": "记忆类型"}, "content": {"type": "string", "description": "条目内容。append/replace 时必填"}, "operation": {"type": "string", "enum": ["append", "replace", "delete"], "description": "操作类型"}, "index": {"type": "integer", "description": "要替换/删除的序号(从1开始)"} - }, + }), "required": ["memory_type", "operation"] } } @@ -1498,7 +1554,7 @@ class MainTerminal: "description": "创建待办列表,将多步骤任务拆解为最多 8 条可执行项。概述请控制在 50 字以内,直接说明清单目标;任务列表只写 2~4 条明确步骤。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "overview": {"type": "string", "description": "一句话概述待办清单要完成的目标,50 字以内。"}, "tasks": { "type": "array", @@ -1513,7 +1569,7 @@ class MainTerminal: "minItems": 1, "maxItems": 8 } - }, + }), "required": ["overview", "tasks"] } } @@ -1525,10 +1581,10 @@ class MainTerminal: "description": "勾选或取消指定任务。在调整任务顺序或内容前,请先向用户说明最新理解或变更。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "task_index": {"type": "integer", "description": "任务序号(1-8)"}, "completed": {"type": "boolean", "description": "true=打勾,false=取消"} - }, + }), "required": ["task_index", "completed"] } } @@ -1540,9 +1596,9 @@ class MainTerminal: "description": "尝试结束待办列表,需同步汇报每项任务结果。若仍有未完事项,请注明原因与后续建议。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "reason": {"type": "string", "description": "可选说明"} - } + }) } } }, @@ -1553,10 +1609,10 @@ class MainTerminal: "description": "在任务未完成时确认是否提前结束。若确认结束,请说明后续交付建议或遗留风险。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "confirm": {"type": "boolean", "description": "true=确认结束,false=继续执行"}, "reason": {"type": "string", "description": "确认结束时的说明"} - }, + }), "required": ["confirm"] } } @@ -1568,10 +1624,10 @@ class MainTerminal: "description": "强制关闭指定子智能体,适用于长时间无响应、超时或卡死的任务。使用前请确认必要的日志/文件已保留,操作会立即终止该任务。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "task_id": {"type": "string", "description": "子智能体任务ID"}, "agent_id": {"type": "integer", "description": "子智能体编号(1~5),若缺少 task_id 可用"} - } + }) } } }, @@ -1582,7 +1638,7 @@ class MainTerminal: "description": "创建新的子智能体任务。适合大规模信息搜集、网页提取与多文档总结等会占用大量上下文的工作,需要提供任务摘要、详细要求、交付目录以及参考文件。注意:同一时间最多运行5个子智能体。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "agent_id": {"type": "integer", "description": "子智能体代号(1~5)"}, "summary": {"type": "string", "description": "任务摘要,简要说明目标"}, "task": {"type": "string", "description": "任务详细要求"}, @@ -1594,7 +1650,7 @@ class MainTerminal: "maxItems": 10 }, "timeout_seconds": {"type": "integer", "description": "子智能体最大运行秒数:单/双次搜索建议180秒,多轮搜索整理建议300秒,深度调研或长篇分析可设600秒"} - }, + }), "required": ["agent_id", "summary", "task", "target_dir"] } } @@ -1606,11 +1662,11 @@ class MainTerminal: "description": "等待指定子智能体任务结束(或超时)。任务完成后会返回交付目录,并将结果复制到指定的项目文件夹。调用时 `timeout_seconds` 应不少于对应子智能体的 `timeout_seconds`,否则可能提前终止等待。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "task_id": {"type": "string", "description": "子智能体任务ID"}, "agent_id": {"type": "integer", "description": "子智能体代号(可选,用于缺省 task_id 的情况)"}, "timeout_seconds": {"type": "integer", "description": "本次等待的超时时长(秒)"} - }, + }), "required": [] } } @@ -1622,12 +1678,12 @@ class MainTerminal: "description": "触发隐藏彩蛋,用于展示非功能性特效。需指定 effect 参数,例如 flood(灌水)或 snake(贪吃蛇)。", "parameters": { "type": "object", - "properties": { + "properties": self._inject_intent({ "effect": { "type": "string", "description": "彩蛋标识,目前支持 flood(灌水)与 snake(贪吃蛇)。" } - }, + }), "required": ["effect"] } } @@ -1638,7 +1694,7 @@ class MainTerminal: tool for tool in tools if tool.get("function", {}).get("name") not in self.disabled_tools ] - return tools + return self._apply_intent_to_tools(tools) async def handle_tool_call(self, tool_name: str, arguments: Dict) -> str: """处理工具调用(添加参数预检查和改进错误处理)""" diff --git a/static/src/components/personalization/PersonalizationDrawer.vue b/static/src/components/personalization/PersonalizationDrawer.vue index 01090ae..9a0ddd2 100644 --- a/static/src/components/personalization/PersonalizationDrawer.vue +++ b/static/src/components/personalization/PersonalizationDrawer.vue @@ -253,6 +253,29 @@ 使用快速模型为新对话生成含 emoji 的简短标题 +
开启后,调用工具时会先用约15字告诉你要做什么,替代“正在/完成”文案。
+