mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-06-02 00:57:41 +08:00
fix: write too long file
This commit is contained in:
@@ -179,7 +179,7 @@ def _build_tooling_section(tools: List[Any], language: str) -> List[str]:
|
|||||||
tool_map = {}
|
tool_map = {}
|
||||||
tool_descriptions = {
|
tool_descriptions = {
|
||||||
"read": "读取文件内容",
|
"read": "读取文件内容",
|
||||||
"write": "创建新文件或完全覆盖现有文件(会删除原内容!追加内容请用 edit)",
|
"write": "创建新文件或完全覆盖现有文件(会删除原内容!追加内容请用 edit)。注意:单次 write 内容不要超过 10KB,超大文件请分步创建",
|
||||||
"edit": "精确编辑文件(追加、修改、删除部分内容)",
|
"edit": "精确编辑文件(追加、修改、删除部分内容)",
|
||||||
"ls": "列出目录内容",
|
"ls": "列出目录内容",
|
||||||
"grep": "在文件中搜索内容",
|
"grep": "在文件中搜索内容",
|
||||||
|
|||||||
@@ -78,12 +78,15 @@ class AgentStreamExecutor:
|
|||||||
args_str = json.dumps(args, sort_keys=True, ensure_ascii=False)
|
args_str = json.dumps(args, sort_keys=True, ensure_ascii=False)
|
||||||
return hashlib.md5(args_str.encode()).hexdigest()[:8]
|
return hashlib.md5(args_str.encode()).hexdigest()[:8]
|
||||||
|
|
||||||
def _check_consecutive_failures(self, tool_name: str, args: dict) -> tuple[bool, str]:
|
def _check_consecutive_failures(self, tool_name: str, args: dict) -> tuple[bool, str, bool]:
|
||||||
"""
|
"""
|
||||||
Check if tool has failed too many times consecutively
|
Check if tool has failed too many times consecutively
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(should_stop, reason)
|
(should_stop, reason, is_critical)
|
||||||
|
- should_stop: Whether to stop tool execution
|
||||||
|
- reason: Reason for stopping
|
||||||
|
- is_critical: Whether to abort entire conversation (True for 8+ failures)
|
||||||
"""
|
"""
|
||||||
args_hash = self._hash_args(args)
|
args_hash = self._hash_args(args)
|
||||||
|
|
||||||
@@ -99,7 +102,7 @@ class AgentStreamExecutor:
|
|||||||
break # Different tool or args, stop counting
|
break # Different tool or args, stop counting
|
||||||
|
|
||||||
if same_args_failures >= 3:
|
if same_args_failures >= 3:
|
||||||
return True, f"Tool '{tool_name}' with same arguments failed {same_args_failures} times consecutively. Stopping to prevent infinite loop."
|
return True, f"工具 '{tool_name}' 使用相同参数连续失败 {same_args_failures} 次,停止执行以防止无限循环", False
|
||||||
|
|
||||||
# Count consecutive failures for same tool (any args)
|
# Count consecutive failures for same tool (any args)
|
||||||
same_tool_failures = 0
|
same_tool_failures = 0
|
||||||
@@ -112,10 +115,15 @@ class AgentStreamExecutor:
|
|||||||
else:
|
else:
|
||||||
break # Different tool, stop counting
|
break # Different tool, stop counting
|
||||||
|
|
||||||
if same_tool_failures >= 6:
|
# Hard stop at 8 failures - abort with critical message
|
||||||
return True, f"Tool '{tool_name}' failed {same_tool_failures} times consecutively (with any arguments). Stopping to prevent infinite loop."
|
if same_tool_failures >= 8:
|
||||||
|
return True, f"抱歉,我没能完成这个任务。可能是我理解有误或者当前方法不太合适。\n\n建议你:\n• 换个方式描述需求试试\n• 把任务拆分成更小的步骤\n• 或者换个思路来解决", True
|
||||||
|
|
||||||
return False, ""
|
# Warning at 6 failures
|
||||||
|
if same_tool_failures >= 6:
|
||||||
|
return True, f"工具 '{tool_name}' 连续失败 {same_tool_failures} 次(使用不同参数),停止执行以防止无限循环", False
|
||||||
|
|
||||||
|
return False, "", False
|
||||||
|
|
||||||
def _record_tool_result(self, tool_name: str, args: dict, success: bool):
|
def _record_tool_result(self, tool_name: str, args: dict, success: bool):
|
||||||
"""Record tool execution result for failure tracking"""
|
"""Record tool execution result for failure tracking"""
|
||||||
@@ -227,6 +235,12 @@ class AgentStreamExecutor:
|
|||||||
result = self._execute_tool(tool_call)
|
result = self._execute_tool(tool_call)
|
||||||
tool_results.append(result)
|
tool_results.append(result)
|
||||||
|
|
||||||
|
# Check for critical error - abort entire conversation
|
||||||
|
if result.get("status") == "critical_error":
|
||||||
|
logger.error(f"💥 检测到严重错误,终止对话")
|
||||||
|
final_response = result.get('result', '任务执行失败')
|
||||||
|
return final_response
|
||||||
|
|
||||||
# Log tool result in compact format
|
# Log tool result in compact format
|
||||||
status_emoji = "✅" if result.get("status") == "success" else "❌"
|
status_emoji = "✅" if result.get("status") == "success" else "❌"
|
||||||
result_data = result.get('result', '')
|
result_data = result.get('result', '')
|
||||||
@@ -467,15 +481,19 @@ class AgentStreamExecutor:
|
|||||||
try:
|
try:
|
||||||
arguments = json.loads(tc["arguments"]) if tc["arguments"] else {}
|
arguments = json.loads(tc["arguments"]) if tc["arguments"] else {}
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
logger.error(f"Failed to parse tool arguments: {tc['arguments']}")
|
args_preview = tc['arguments'][:200] if len(tc['arguments']) > 200 else tc['arguments']
|
||||||
|
logger.error(f"Failed to parse tool arguments for {tc['name']}")
|
||||||
|
logger.error(f"Arguments length: {len(tc['arguments'])} chars")
|
||||||
|
logger.error(f"Arguments preview: {args_preview}...")
|
||||||
logger.error(f"JSON decode error: {e}")
|
logger.error(f"JSON decode error: {e}")
|
||||||
|
|
||||||
# Return a clear error message to the LLM instead of empty dict
|
# Return a clear error message to the LLM instead of empty dict
|
||||||
# This helps the LLM understand what went wrong
|
# This helps the LLM understand what went wrong
|
||||||
tool_calls.append({
|
tool_calls.append({
|
||||||
"id": tc["id"],
|
"id": tc["id"],
|
||||||
"name": tc["name"],
|
"name": tc["name"],
|
||||||
"arguments": {},
|
"arguments": {},
|
||||||
"_parse_error": f"Invalid JSON in tool arguments: {tc['arguments'][:100]}... Error: {str(e)}"
|
"_parse_error": f"Invalid JSON in tool arguments: {args_preview}... Error: {str(e)}. Tip: For large content, consider splitting into smaller chunks or using a different approach."
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -558,16 +576,25 @@ class AgentStreamExecutor:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
# Check for consecutive failures (retry protection)
|
# Check for consecutive failures (retry protection)
|
||||||
should_stop, stop_reason = self._check_consecutive_failures(tool_name, arguments)
|
should_stop, stop_reason, is_critical = self._check_consecutive_failures(tool_name, arguments)
|
||||||
if should_stop:
|
if should_stop:
|
||||||
logger.error(f"🛑 {stop_reason}")
|
logger.error(f"🛑 {stop_reason}")
|
||||||
self._record_tool_result(tool_name, arguments, False)
|
self._record_tool_result(tool_name, arguments, False)
|
||||||
# 返回错误给 LLM,让它尝试其他方法
|
|
||||||
result = {
|
if is_critical:
|
||||||
"status": "error",
|
# Critical failure - abort entire conversation
|
||||||
"result": f"{stop_reason}\n\nThis approach is not working. Please try a completely different method or ask the user for more information/clarification.",
|
result = {
|
||||||
"execution_time": 0
|
"status": "critical_error",
|
||||||
}
|
"result": stop_reason,
|
||||||
|
"execution_time": 0
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# Normal failure - let LLM try different approach
|
||||||
|
result = {
|
||||||
|
"status": "error",
|
||||||
|
"result": f"{stop_reason}\n\n当前方法行不通,请尝试完全不同的方法或向用户询问更多信息。",
|
||||||
|
"execution_time": 0
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
|
|
||||||
self._emit_event("tool_execution_start", {
|
self._emit_event("tool_execution_start", {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class Write(BaseTool):
|
|||||||
"""Tool for writing file content"""
|
"""Tool for writing file content"""
|
||||||
|
|
||||||
name: str = "write"
|
name: str = "write"
|
||||||
description: str = "Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories."
|
description: str = "Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories. IMPORTANT: Single write should not exceed 10KB. For large files, create a skeleton first, then use edit to add content in chunks."
|
||||||
|
|
||||||
params: dict = {
|
params: dict = {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|||||||
Reference in New Issue
Block a user