diff --git a/src/adapter.py b/src/adapter.py index 85042c1..7502fd1 100644 --- a/src/adapter.py +++ b/src/adapter.py @@ -168,9 +168,9 @@ class DeepSeekWebAdapter(BaseAdapter): await self._reply(task_id, sender_id, "DeepSeek 响应超时", scope, private_to_id) return - action = self._parse_action(response) + actions = self._parse_actions(response) - if action is None: + if not actions: if "<<>>" in response: logger.warning("ACTION 解析失败,返回错误给 DeepSeek") error_msg = ( @@ -185,94 +185,103 @@ class DeepSeekWebAdapter(BaseAdapter): await self._reply(task_id, sender_id, clean_msg, scope, private_to_id) return - action_type = action.get("type", "") + has_executable = False + result_parts = [] - if action_type == "reply": - reply_content = action.get("content", "") - reply_status = action.get("status", "received") - logger.info(f"[reply] status={reply_status}, content={reply_content[:80]}...") + for action in actions: + action_type = action.get("type", "") - await self._reply(task_id, sender_id, reply_content, scope, private_to_id) + if action_type == "reply": + reply_content = action.get("content", "") + reply_status = action.get("status", "received") + logger.info(f"[reply] status={reply_status}, content={reply_content[:80]}...") + await self._reply(task_id, sender_id, reply_content, scope, private_to_id) + if reply_status == "completed": + logger.info(f"[任务完成] task={task_id}") + return - if reply_status == "completed": + elif action_type == "complete": + explanation = action.get("explanation", "任务已完成") logger.info(f"[任务完成] task={task_id}") + await self._reply(task_id, sender_id, explanation, scope, private_to_id) return - await self.session.send_message("已转发回复给用户,请继续执行下一步操作。") - continue + else: + has_executable = True + cmd = action.get("cmd", "") + path = action.get("path", "") + action_content = action.get("content", "") + explanation = action.get("explanation", "") - if action_type == "complete": - explanation = action.get("explanation", "任务已完成") - logger.info(f"[任务完成] task={task_id}") - await self._reply(task_id, sender_id, explanation, scope, private_to_id) + if action_type == "write_file" and path: + cmd = path + + logger.info(f"[执行] [{action_type}] {cmd or path} {explanation}") + + result = await self.session.execute_action(action_type, cmd, action_content) + + status = "[OK]" if result["success"] else "[ERR]" + output_preview = result["output"][:200].replace("\n", " ") + logger.info(f" {status} {output_preview}...") + + display_cmd = path if action_type == "write_file" and path else cmd + result_parts.append( + f"操作: {action_type}\n" + f"命令: {display_cmd}\n" + f"成功: {result['success']}\n\n" + f"输出:\n```\n{result['output']}\n```" + ) + + if has_executable and result_parts: + result_msg = ( + f"[执行结果]\n" + + "\n---\n".join(result_parts) + + "\n\n请继续下一步或完成任务。" + ) + await self.session.send_message(result_msg) + elif not has_executable: return - cmd = action.get("cmd", "") - path = action.get("path", "") - action_content = action.get("content", "") - explanation = action.get("explanation", "") - - if action_type == "write_file" and path: - cmd = path - - logger.info(f"[执行] [{action_type}] {cmd or path} {explanation}") - - result = await self.session.execute_action(action_type, cmd, action_content) - - status = "[OK]" if result["success"] else "[ERR]" - output_preview = result["output"][:200].replace("\n", " ") - logger.info(f" {status} {output_preview}...") - - display_cmd = path if action_type == "write_file" and path else cmd - result_msg = ( - f"[执行结果]\n" - f"操作: {action_type}\n" - f"命令: {display_cmd}\n" - f"成功: {result['success']}\n\n" - f"输出:\n```\n{result['output']}\n```\n\n" - f"请继续下一步或完成任务。" - ) - - await self.session.send_message(result_msg) - await self._reply(task_id, sender_id, "已达到最大交互轮数,任务终止", scope, private_to_id) - def _parse_action(self, text: str) -> Optional[dict]: + def _parse_actions(self, text: str) -> list: if not text: - return None + return [] action_pattern = r'<<>>' - action_match = re.search(action_pattern, text, re.DOTALL) + action_matches = re.findall(action_pattern, text, re.DOTALL) - if not action_match: + if not action_matches: if "<<>>" in text: try: start = text.find("<<>>") json_content = text[start:end].strip() if json_content.startswith("{") and json_content.endswith("}"): - action_match = type('obj', (object,), {'group': lambda x, s=json_content: s})() + action_matches = [json_content] except Exception: - return None + return [] else: - return None + return [] - try: - json_str = action_match.group(1).strip() - action = json.loads(json_str) + actions = [] + for json_str in action_matches: + try: + action = json.loads(json_str.strip()) - if action.get("type") == "write_file": - content_pattern = r'<<>>' - content_match = re.search(content_pattern, text, re.DOTALL) - if content_match: - action["content"] = content_match.group(1) - else: - action["content"] = "" + if action.get("type") == "write_file": + content_pattern = r'<<>>' + content_match = re.search(content_pattern, text, re.DOTALL) + if content_match: + action["content"] = content_match.group(1) + else: + action["content"] = "" - return action - except json.JSONDecodeError as e: - logger.warning(f"JSON 解析失败: {e}") - return None + actions.append(action) + except json.JSONDecodeError as e: + logger.warning(f"JSON 解析失败: {e}") + + return actions def _clean_action_markers(self, text: str) -> str: clean = re.sub(r'<<>>', '', text, flags=re.DOTALL) diff --git a/src/deepseek_agent_adapter.py b/src/deepseek_agent_adapter.py index 78ea8b8..054feed 100644 --- a/src/deepseek_agent_adapter.py +++ b/src/deepseek_agent_adapter.py @@ -70,11 +70,7 @@ ADAPTER_SYSTEM_PROMPT = """【系统指令】你是一个智能助手,同时 例:用户说"在吗" → reply completed "在的,有什么可以帮你的?" 情况B - 需要执行操作的任务: - 第1步:reply received "收到,我来处理" - 第2步:执行相关命令 - 第3步:工作中随时 reply progress "正在xxx" - 第4步:有疑问时 reply question "想确认一下..." - 第5步:完成时 reply completed "任务结果..." + 在一条消息里同时 reply + 执行命令,尽量减少来回轮次 ═══════════════════════════════════════════════════════ 【执行流程】 @@ -94,66 +90,22 @@ ADAPTER_SYSTEM_PROMPT = """【系统指令】你是一个智能助手,同时 ═══════════════════════════════════════════════════════ 【响应格式 - 必须严格遵守】 ═══════════════════════════════════════════════════════ -每次响应必须包含两部分: +每条消息可以包含多个 ACTION 块,系统会按顺序依次执行: -1. ACTION 块(如需执行命令): - <<>> - - reply 命令格式: - <<>> - -2. 结束标记(必须!): - <<>> - -═══════════════════════════════════════════════════════ -【重要规则】 -═══════════════════════════════════════════════════════ -1. 【必须先回复】收到任务后,必须先执行 reply received,再开始工作 -2. 【必须加结束标记】每次回复的最后一行必须是 <<>> -3. 【非阻塞执行】command/powershell 不等待命令完成,立即返回 -4. 【必须查询输出】执行命令后,必须用 get_terminal_output 查看结果 -5. 【交互式程序】如果程序等待输入,用 send_input 发送输入,不要假设程序已结束 -6. 【文件编码】write_file 配合 <<>> +<<>> <<>> -你(第2轮 - 执行命令): +reply 命令格式: <<>> -<<>> -你(第3轮 - 查看输出): -<<>> -<<>> - -你(第4轮 - 汇报完成): -<<>> -<<>> - -═══════════════════════════════════════════════════════ -【示例 - 写入文件(必须用代码围栏)】 -═══════════════════════════════════════════════════════ +write_file 格式(CONTENT 块紧跟 ACTION 块): <<>> @@ -161,9 +113,65 @@ ACTION>>> ```python def hello(): print("Hello") - return 42 ``` CONTENT>>> + +结束标记(每条消息最后一行必须!): +<<>> + +═══════════════════════════════════════════════════════ +【重要规则】 +═══════════════════════════════════════════════════════ +1. 【多指令并行】一条消息里可以包含多个 ACTION,系统会按顺序执行。尽量在一条消息里同时 reply + 执行命令,减少来回轮次 +2. 【必须加结束标记】每次回复的最后一行必须是 <<>> +3. 【非阻塞执行】command/powershell 不等待命令完成,立即返回 +4. 【必须查询输出】执行命令后,必须用 get_terminal_output 查看结果 +5. 【交互式程序】如果程序等待输入,用 send_input 发送输入,不要假设程序已结束 +6. 【文件编码】write_file 配合 <<>> +<<>> + +═══════════════════════════════════════════════════════ +【示例 - 需要执行操作的任务(一条消息同时回复+执行)】 +═══════════════════════════════════════════════════════ +用户:帮我看看桌面上有什么文件 + +<<>> +<<>> +<<>> + +(系统返回命令输出后) + +<<>> +<<>> + +═══════════════════════════════════════════════════════ +【示例 - 查看目录(一条消息同时回复+执行)】 +═══════════════════════════════════════════════════════ +用户:看看本地目录 + +<<>> +<<>> <<>> ═══════════════════════════════════════════════════════