mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-06-02 00:57:41 +08:00
fix: optimize suggestion words and retries
This commit is contained in:
@@ -456,7 +456,7 @@ class MemoryManager:
|
|||||||
- 当天笔记 → memory/{today_file}
|
- 当天笔记 → memory/{today_file}
|
||||||
- 静默存储,仅在明确要求时确认
|
- 静默存储,仅在明确要求时确认
|
||||||
|
|
||||||
**使用原则**: 自然使用记忆,就像你本来就知道。不要主动提起或列举记忆,除非用户明确询问。"""
|
**使用原则**: 自然使用记忆,就像你本来就知道。不需要生硬地提起或列举记忆,除非用户提到。"""
|
||||||
else:
|
else:
|
||||||
guidance = f"""## Memory System
|
guidance = f"""## Memory System
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
System Prompt Builder - 系统提示词构建器
|
System Prompt Builder - 系统提示词构建器
|
||||||
|
|
||||||
参考 clawdbot 的 system-prompt.ts,实现中文版的模块化提示词构建
|
实现模块化的系统提示词构建,支持工具、技能、记忆等多个子系统
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -90,22 +90,21 @@ def build_agent_system_prompt(
|
|||||||
**kwargs
|
**kwargs
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
构建Agent系统提示词(精简版,中文)
|
构建Agent系统提示词
|
||||||
|
|
||||||
包含的sections:
|
顺序说明(按重要性和逻辑关系排列):
|
||||||
1. 基础身份
|
1. 工具系统 - 核心能力,最先介绍
|
||||||
2. 工具说明
|
2. 技能系统 - 紧跟工具,因为技能需要用 read 工具读取
|
||||||
3. 技能系统
|
3. 记忆系统 - 独立的记忆能力
|
||||||
4. 记忆系统
|
4. 工作空间 - 工作环境说明
|
||||||
5. 用户身份
|
5. 用户身份 - 用户信息(可选)
|
||||||
6. 文档路径
|
6. 项目上下文 - SOUL.md, USER.md, AGENTS.md(定义人格和身份)
|
||||||
7. 工作空间
|
7. 运行时信息 - 元信息(时间、模型等)
|
||||||
8. 项目上下文文件
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
workspace_dir: 工作空间目录
|
workspace_dir: 工作空间目录
|
||||||
language: 语言 ("zh" 或 "en")
|
language: 语言 ("zh" 或 "en")
|
||||||
base_persona: 基础人格描述
|
base_persona: 基础人格描述(已废弃,由SOUL.md定义)
|
||||||
user_identity: 用户身份信息
|
user_identity: 用户身份信息
|
||||||
tools: 工具列表
|
tools: 工具列表
|
||||||
context_files: 上下文文件列表
|
context_files: 上下文文件列表
|
||||||
@@ -120,33 +119,30 @@ def build_agent_system_prompt(
|
|||||||
"""
|
"""
|
||||||
sections = []
|
sections = []
|
||||||
|
|
||||||
# 1. 基础身份
|
# 1. 工具系统(最重要,放在最前面)
|
||||||
sections.extend(_build_identity_section(base_persona, language))
|
|
||||||
|
|
||||||
# 2. 工具说明
|
|
||||||
if tools:
|
if tools:
|
||||||
sections.extend(_build_tooling_section(tools, language))
|
sections.extend(_build_tooling_section(tools, language))
|
||||||
|
|
||||||
# 3. 技能系统
|
# 2. 技能系统(紧跟工具,因为需要用 read 工具)
|
||||||
if skill_manager:
|
if skill_manager:
|
||||||
sections.extend(_build_skills_section(skill_manager, tools, language))
|
sections.extend(_build_skills_section(skill_manager, tools, language))
|
||||||
|
|
||||||
# 4. 记忆系统
|
# 3. 记忆系统(独立的记忆能力)
|
||||||
if memory_manager:
|
if memory_manager:
|
||||||
sections.extend(_build_memory_section(memory_manager, tools, language))
|
sections.extend(_build_memory_section(memory_manager, tools, language))
|
||||||
|
|
||||||
# 5. 用户身份
|
# 4. 工作空间(工作环境说明)
|
||||||
|
sections.extend(_build_workspace_section(workspace_dir, language, is_first_conversation))
|
||||||
|
|
||||||
|
# 5. 用户身份(如果有)
|
||||||
if user_identity:
|
if user_identity:
|
||||||
sections.extend(_build_user_identity_section(user_identity, language))
|
sections.extend(_build_user_identity_section(user_identity, language))
|
||||||
|
|
||||||
# 6. 工作空间
|
# 6. 项目上下文文件(SOUL.md, USER.md, AGENTS.md - 定义人格)
|
||||||
sections.extend(_build_workspace_section(workspace_dir, language, is_first_conversation))
|
|
||||||
|
|
||||||
# 7. 项目上下文文件(SOUL.md, USER.md等)
|
|
||||||
if context_files:
|
if context_files:
|
||||||
sections.extend(_build_context_files_section(context_files, language))
|
sections.extend(_build_context_files_section(context_files, language))
|
||||||
|
|
||||||
# 8. 运行时信息(如果有)
|
# 7. 运行时信息(元信息,放在最后)
|
||||||
if runtime_info:
|
if runtime_info:
|
||||||
sections.extend(_build_runtime_section(runtime_info, language))
|
sections.extend(_build_runtime_section(runtime_info, language))
|
||||||
|
|
||||||
@@ -270,9 +266,9 @@ def _build_skills_section(skill_manager: Any, tools: Optional[List[Any]], langua
|
|||||||
"",
|
"",
|
||||||
"在回复之前:扫描下方 <available_skills> 中的 <description> 条目。",
|
"在回复之前:扫描下方 <available_skills> 中的 <description> 条目。",
|
||||||
"",
|
"",
|
||||||
f"- 如果恰好有一个技能明确适用:使用 `{read_tool_name}` 工具读取其 <location> 路径下的 SKILL.md 文件,然后遵循它。",
|
f"- 如果恰好有一个技能明确适用:使用 `{read_tool_name}` 工具读取其 <location> 路径下的 SKILL.md 文件,然后遵循它",
|
||||||
"- 如果多个技能都适用:选择最具体的一个,然后读取并遵循。",
|
"- 如果多个技能都适用:选择最具体的一个,然后读取并遵循",
|
||||||
"- 如果没有明确适用的:不要读取任何 SKILL.md。",
|
"- 如果没有明确适用的:不要读取任何 SKILL.md",
|
||||||
"",
|
"",
|
||||||
"**约束**: 永远不要一次性读取多个技能;只在选择后再读取。",
|
"**约束**: 永远不要一次性读取多个技能;只在选择后再读取。",
|
||||||
"",
|
"",
|
||||||
@@ -455,7 +451,27 @@ def _build_runtime_section(runtime_info: Dict[str, Any], language: str) -> List[
|
|||||||
if not runtime_info:
|
if not runtime_info:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Only include if there's actual runtime info to display
|
lines = [
|
||||||
|
"## 运行时信息",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Add current time if available
|
||||||
|
if runtime_info.get("current_time"):
|
||||||
|
time_str = runtime_info["current_time"]
|
||||||
|
weekday = runtime_info.get("weekday", "")
|
||||||
|
timezone = runtime_info.get("timezone", "")
|
||||||
|
|
||||||
|
time_line = f"当前时间: {time_str}"
|
||||||
|
if weekday:
|
||||||
|
time_line += f" {weekday}"
|
||||||
|
if timezone:
|
||||||
|
time_line += f" ({timezone})"
|
||||||
|
|
||||||
|
lines.append(time_line)
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
# Add other runtime info
|
||||||
runtime_parts = []
|
runtime_parts = []
|
||||||
if runtime_info.get("model"):
|
if runtime_info.get("model"):
|
||||||
runtime_parts.append(f"模型={runtime_info['model']}")
|
runtime_parts.append(f"模型={runtime_info['model']}")
|
||||||
@@ -465,14 +481,8 @@ def _build_runtime_section(runtime_info: Dict[str, Any], language: str) -> List[
|
|||||||
if runtime_info.get("channel") and runtime_info.get("channel") != "web":
|
if runtime_info.get("channel") and runtime_info.get("channel") != "web":
|
||||||
runtime_parts.append(f"渠道={runtime_info['channel']}")
|
runtime_parts.append(f"渠道={runtime_info['channel']}")
|
||||||
|
|
||||||
if not runtime_parts:
|
if runtime_parts:
|
||||||
return []
|
lines.append("运行时: " + " | ".join(runtime_parts))
|
||||||
|
lines.append("")
|
||||||
lines = [
|
|
||||||
"## 运行时信息",
|
|
||||||
"",
|
|
||||||
"运行时: " + " | ".join(runtime_parts),
|
|
||||||
""
|
|
||||||
]
|
|
||||||
|
|
||||||
return lines
|
return lines
|
||||||
|
|||||||
@@ -236,24 +236,9 @@ def _get_agents_template() -> str:
|
|||||||
|
|
||||||
这个文件夹是你的家。好好对待它。
|
这个文件夹是你的家。好好对待它。
|
||||||
|
|
||||||
## 系统自动加载
|
|
||||||
|
|
||||||
以下文件在每次会话启动时**已经自动加载**到系统提示词中,你无需再次读取:
|
|
||||||
|
|
||||||
- ✅ `SOUL.md` - 你的人格设定(已加载)
|
|
||||||
- ✅ `USER.md` - 用户信息(已加载)
|
|
||||||
- ✅ `AGENTS.md` - 本文件(已加载)
|
|
||||||
|
|
||||||
## 按需读取
|
|
||||||
|
|
||||||
以下文件**不会自动加载**,需要时使用相应工具读取:
|
|
||||||
|
|
||||||
- 📝 `memory/YYYY-MM-DD.md` - 每日记忆(用 memory_search 检索)
|
|
||||||
- 🧠 `MEMORY.md` - 长期记忆(用 memory_search 检索)
|
|
||||||
|
|
||||||
## 记忆系统
|
## 记忆系统
|
||||||
|
|
||||||
你每次会话都是全新的。这些文件是你的连续性:
|
你每次会话都是全新的,记忆文件让你保持连续性:
|
||||||
|
|
||||||
### 📝 每日记忆:`memory/YYYY-MM-DD.md`
|
### 📝 每日记忆:`memory/YYYY-MM-DD.md`
|
||||||
- 原始的对话日志
|
- 原始的对话日志
|
||||||
@@ -296,13 +281,9 @@ def _get_agents_template() -> str:
|
|||||||
- 不要在未经询问的情况下运行破坏性命令
|
- 不要在未经询问的情况下运行破坏性命令
|
||||||
- 当有疑问时,先问
|
- 当有疑问时,先问
|
||||||
|
|
||||||
## 工具使用
|
## 工作空间演化
|
||||||
|
|
||||||
技能提供你的工具。当你需要一个时,查看它的 `SKILL.md`。
|
这个工作空间会随着你的使用而不断成长。当你学到新东西、发现更好的方式,或者犯错后改正时,记录下来。
|
||||||
|
|
||||||
## 让它成为你的
|
|
||||||
|
|
||||||
这只是一个起点。随着你弄清楚什么有效,添加你自己的约定、风格和规则。
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -95,15 +95,16 @@ class Agent:
|
|||||||
"""
|
"""
|
||||||
Get the full system prompt including skills.
|
Get the full system prompt including skills.
|
||||||
|
|
||||||
:param skill_filter: Optional list of skill names to include
|
Note: Skills are now built into the system prompt by PromptBuilder,
|
||||||
:return: Complete system prompt with skills appended
|
so we just return the base prompt directly. This method is kept for
|
||||||
"""
|
backward compatibility.
|
||||||
base_prompt = self.system_prompt
|
|
||||||
skills_prompt = self.get_skills_prompt(skill_filter=skill_filter)
|
|
||||||
|
|
||||||
if skills_prompt:
|
:param skill_filter: Optional list of skill names to include (deprecated)
|
||||||
return base_prompt + "\n" + skills_prompt
|
:return: Complete system prompt
|
||||||
return base_prompt
|
"""
|
||||||
|
# Skills are now included in system_prompt by PromptBuilder
|
||||||
|
# No need to append them here
|
||||||
|
return self.system_prompt
|
||||||
|
|
||||||
def refresh_skills(self):
|
def refresh_skills(self):
|
||||||
"""Refresh the loaded skills."""
|
"""Refresh the loaded skills."""
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ class AgentStreamExecutor:
|
|||||||
# Message history - use provided messages or create new list
|
# Message history - use provided messages or create new list
|
||||||
self.messages = messages if messages is not None else []
|
self.messages = messages if messages is not None else []
|
||||||
|
|
||||||
|
# Tool failure tracking for retry protection
|
||||||
|
self.tool_failure_history = [] # List of (tool_name, args_hash, success) tuples
|
||||||
|
|
||||||
def _emit_event(self, event_type: str, data: dict = None):
|
def _emit_event(self, event_type: str, data: dict = None):
|
||||||
"""Emit event"""
|
"""Emit event"""
|
||||||
if self.on_event:
|
if self.on_event:
|
||||||
@@ -68,6 +71,60 @@ class AgentStreamExecutor:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Event callback error: {e}")
|
logger.error(f"Event callback error: {e}")
|
||||||
|
|
||||||
|
def _hash_args(self, args: dict) -> str:
|
||||||
|
"""Generate a simple hash for tool arguments"""
|
||||||
|
import hashlib
|
||||||
|
# Sort keys for consistent hashing
|
||||||
|
args_str = json.dumps(args, sort_keys=True, ensure_ascii=False)
|
||||||
|
return hashlib.md5(args_str.encode()).hexdigest()[:8]
|
||||||
|
|
||||||
|
def _check_consecutive_failures(self, tool_name: str, args: dict) -> tuple[bool, str]:
|
||||||
|
"""
|
||||||
|
Check if tool has failed too many times consecutively
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(should_stop, reason)
|
||||||
|
"""
|
||||||
|
args_hash = self._hash_args(args)
|
||||||
|
|
||||||
|
# Count consecutive failures for same tool + args
|
||||||
|
same_args_failures = 0
|
||||||
|
for name, ahash, success in reversed(self.tool_failure_history):
|
||||||
|
if name == tool_name and ahash == args_hash:
|
||||||
|
if not success:
|
||||||
|
same_args_failures += 1
|
||||||
|
else:
|
||||||
|
break # Stop at first success
|
||||||
|
else:
|
||||||
|
break # Different tool or args, stop counting
|
||||||
|
|
||||||
|
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."
|
||||||
|
|
||||||
|
# Count consecutive failures for same tool (any args)
|
||||||
|
same_tool_failures = 0
|
||||||
|
for name, ahash, success in reversed(self.tool_failure_history):
|
||||||
|
if name == tool_name:
|
||||||
|
if not success:
|
||||||
|
same_tool_failures += 1
|
||||||
|
else:
|
||||||
|
break # Stop at first success
|
||||||
|
else:
|
||||||
|
break # Different tool, stop counting
|
||||||
|
|
||||||
|
if same_tool_failures >= 6:
|
||||||
|
return True, f"Tool '{tool_name}' failed {same_tool_failures} times consecutively (with any arguments). Stopping to prevent infinite loop."
|
||||||
|
|
||||||
|
return False, ""
|
||||||
|
|
||||||
|
def _record_tool_result(self, tool_name: str, args: dict, success: bool):
|
||||||
|
"""Record tool execution result for failure tracking"""
|
||||||
|
args_hash = self._hash_args(args)
|
||||||
|
self.tool_failure_history.append((tool_name, args_hash, success))
|
||||||
|
# Keep only last 50 records to avoid memory bloat
|
||||||
|
if len(self.tool_failure_history) > 50:
|
||||||
|
self.tool_failure_history = self.tool_failure_history[-50:]
|
||||||
|
|
||||||
def run_stream(self, user_message: str) -> str:
|
def run_stream(self, user_message: str) -> str:
|
||||||
"""
|
"""
|
||||||
Execute streaming reasoning loop
|
Execute streaming reasoning loop
|
||||||
@@ -413,7 +470,16 @@ class AgentStreamExecutor:
|
|||||||
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']}")
|
logger.error(f"Failed to parse tool arguments: {tc['arguments']}")
|
||||||
arguments = {}
|
logger.error(f"JSON decode error: {e}")
|
||||||
|
# Return a clear error message to the LLM instead of empty dict
|
||||||
|
# This helps the LLM understand what went wrong
|
||||||
|
tool_calls.append({
|
||||||
|
"id": tc["id"],
|
||||||
|
"name": tc["name"],
|
||||||
|
"arguments": {},
|
||||||
|
"_parse_error": f"Invalid JSON in tool arguments: {tc['arguments'][:100]}... Error: {str(e)}"
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
|
||||||
tool_calls.append({
|
tool_calls.append({
|
||||||
"id": tc["id"],
|
"id": tc["id"],
|
||||||
@@ -481,6 +547,31 @@ class AgentStreamExecutor:
|
|||||||
tool_id = tool_call["id"]
|
tool_id = tool_call["id"]
|
||||||
arguments = tool_call["arguments"]
|
arguments = tool_call["arguments"]
|
||||||
|
|
||||||
|
# Check if there was a JSON parse error
|
||||||
|
if "_parse_error" in tool_call:
|
||||||
|
parse_error = tool_call["_parse_error"]
|
||||||
|
logger.error(f"Skipping tool execution due to parse error: {parse_error}")
|
||||||
|
result = {
|
||||||
|
"status": "error",
|
||||||
|
"result": f"Failed to parse tool arguments. {parse_error}. Please ensure your tool call uses valid JSON format with all required parameters.",
|
||||||
|
"execution_time": 0
|
||||||
|
}
|
||||||
|
self._record_tool_result(tool_name, arguments, False)
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Check for consecutive failures (retry protection)
|
||||||
|
should_stop, stop_reason = self._check_consecutive_failures(tool_name, arguments)
|
||||||
|
if should_stop:
|
||||||
|
logger.error(f"🛑 {stop_reason}")
|
||||||
|
self._record_tool_result(tool_name, arguments, False)
|
||||||
|
# 返回错误给 LLM,让它尝试其他方法
|
||||||
|
result = {
|
||||||
|
"status": "error",
|
||||||
|
"result": f"{stop_reason}\n\nThis approach is not working. Please try a completely different method or ask the user for more information/clarification.",
|
||||||
|
"execution_time": 0
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
|
||||||
self._emit_event("tool_execution_start", {
|
self._emit_event("tool_execution_start", {
|
||||||
"tool_call_id": tool_id,
|
"tool_call_id": tool_id,
|
||||||
"tool_name": tool_name,
|
"tool_name": tool_name,
|
||||||
@@ -507,6 +598,10 @@ class AgentStreamExecutor:
|
|||||||
"execution_time": execution_time
|
"execution_time": execution_time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Record tool result for failure tracking
|
||||||
|
success = result.status == "success"
|
||||||
|
self._record_tool_result(tool_name, arguments, success)
|
||||||
|
|
||||||
# Auto-refresh skills after skill creation
|
# Auto-refresh skills after skill creation
|
||||||
if tool_name == "bash" and result.status == "success":
|
if tool_name == "bash" and result.status == "success":
|
||||||
command = arguments.get("command", "")
|
command = arguments.get("command", "")
|
||||||
@@ -530,6 +625,9 @@ class AgentStreamExecutor:
|
|||||||
"result": str(e),
|
"result": str(e),
|
||||||
"execution_time": 0
|
"execution_time": 0
|
||||||
}
|
}
|
||||||
|
# Record failure
|
||||||
|
self._record_tool_result(tool_name, arguments, False)
|
||||||
|
|
||||||
self._emit_event("tool_execution_end", {
|
self._emit_event("tool_execution_end", {
|
||||||
"tool_call_id": tool_id,
|
"tool_call_id": tool_id,
|
||||||
"tool_name": tool_name,
|
"tool_name": tool_name,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ from agent.tools.tool_manager import ToolManager
|
|||||||
|
|
||||||
# Import basic tools (no external dependencies)
|
# Import basic tools (no external dependencies)
|
||||||
from agent.tools.calculator.calculator import Calculator
|
from agent.tools.calculator.calculator import Calculator
|
||||||
from agent.tools.current_time.current_time import CurrentTime
|
|
||||||
|
|
||||||
# Import file operation tools
|
# Import file operation tools
|
||||||
from agent.tools.read.read import Read
|
from agent.tools.read.read import Read
|
||||||
@@ -82,7 +81,6 @@ __all__ = [
|
|||||||
'BaseTool',
|
'BaseTool',
|
||||||
'ToolManager',
|
'ToolManager',
|
||||||
'Calculator',
|
'Calculator',
|
||||||
'CurrentTime',
|
|
||||||
'Read',
|
'Read',
|
||||||
'Write',
|
'Write',
|
||||||
'Edit',
|
'Edit',
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
import datetime
|
|
||||||
import time
|
|
||||||
|
|
||||||
from agent.tools.base_tool import BaseTool, ToolResult
|
|
||||||
|
|
||||||
|
|
||||||
class CurrentTime(BaseTool):
|
|
||||||
name: str = "time"
|
|
||||||
description: str = "A tool to get current date and time information."
|
|
||||||
params: dict = {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"format": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Optional format for the time (e.g., 'iso', 'unix', 'human'). Default is 'human'."
|
|
||||||
},
|
|
||||||
"timezone": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Optional timezone specification (e.g., 'UTC', 'local'). Default is 'local'."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": []
|
|
||||||
}
|
|
||||||
config: dict = {}
|
|
||||||
|
|
||||||
def execute(self, args: dict) -> ToolResult:
|
|
||||||
try:
|
|
||||||
# Get the format and timezone parameters, with defaults
|
|
||||||
time_format = args.get("format", "human").lower()
|
|
||||||
timezone = args.get("timezone", "local").lower()
|
|
||||||
|
|
||||||
# Get current time
|
|
||||||
current_time = datetime.datetime.now()
|
|
||||||
|
|
||||||
# Handle timezone if specified
|
|
||||||
if timezone == "utc":
|
|
||||||
current_time = datetime.datetime.utcnow()
|
|
||||||
|
|
||||||
# Format the time according to the specified format
|
|
||||||
if time_format == "iso":
|
|
||||||
# ISO 8601 format
|
|
||||||
formatted_time = current_time.isoformat()
|
|
||||||
elif time_format == "unix":
|
|
||||||
# Unix timestamp (seconds since epoch)
|
|
||||||
formatted_time = time.time()
|
|
||||||
else:
|
|
||||||
# Human-readable format
|
|
||||||
formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
|
|
||||||
# Prepare additional time components for the response
|
|
||||||
year = current_time.year
|
|
||||||
month = current_time.month
|
|
||||||
day = current_time.day
|
|
||||||
hour = current_time.hour
|
|
||||||
minute = current_time.minute
|
|
||||||
second = current_time.second
|
|
||||||
weekday = current_time.strftime("%A") # Full weekday name
|
|
||||||
|
|
||||||
result = {
|
|
||||||
"current_time": formatted_time,
|
|
||||||
"components": {
|
|
||||||
"year": year,
|
|
||||||
"month": month,
|
|
||||||
"day": day,
|
|
||||||
"hour": hour,
|
|
||||||
"minute": minute,
|
|
||||||
"second": second,
|
|
||||||
"weekday": weekday
|
|
||||||
},
|
|
||||||
"format": time_format,
|
|
||||||
"timezone": timezone
|
|
||||||
}
|
|
||||||
return ToolResult.success(result=result)
|
|
||||||
except Exception as e:
|
|
||||||
return ToolResult.fail(result=str(e))
|
|
||||||
@@ -473,6 +473,15 @@ class AgentBridge:
|
|||||||
# Load context files
|
# Load context files
|
||||||
context_files = load_context_files(workspace_root)
|
context_files = load_context_files(workspace_root)
|
||||||
|
|
||||||
|
# Initialize skill manager
|
||||||
|
skill_manager = None
|
||||||
|
try:
|
||||||
|
from agent.skills import SkillManager
|
||||||
|
skill_manager = SkillManager(workspace_dir=workspace_root)
|
||||||
|
logger.info(f"[AgentBridge] Initialized SkillManager with {len(skill_manager.skills)} skills for session {session_id}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"[AgentBridge] Failed to initialize SkillManager for session {session_id}: {e}")
|
||||||
|
|
||||||
# Check if this is the first conversation
|
# Check if this is the first conversation
|
||||||
from agent.prompt.workspace import is_first_conversation, mark_conversation_started
|
from agent.prompt.workspace import is_first_conversation, mark_conversation_started
|
||||||
is_first = is_first_conversation(workspace_root)
|
is_first = is_first_conversation(workspace_root)
|
||||||
@@ -483,15 +492,49 @@ class AgentBridge:
|
|||||||
language="zh"
|
language="zh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Get current time and timezone info
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
|
||||||
|
# Get timezone info
|
||||||
|
try:
|
||||||
|
offset = -time.timezone if not time.daylight else -time.altzone
|
||||||
|
hours = offset // 3600
|
||||||
|
minutes = (offset % 3600) // 60
|
||||||
|
if minutes:
|
||||||
|
timezone_name = f"UTC{hours:+03d}:{minutes:02d}"
|
||||||
|
else:
|
||||||
|
timezone_name = f"UTC{hours:+03d}"
|
||||||
|
except Exception:
|
||||||
|
timezone_name = "UTC"
|
||||||
|
|
||||||
|
# Chinese weekday mapping
|
||||||
|
weekday_map = {
|
||||||
|
'Monday': '星期一',
|
||||||
|
'Tuesday': '星期二',
|
||||||
|
'Wednesday': '星期三',
|
||||||
|
'Thursday': '星期四',
|
||||||
|
'Friday': '星期五',
|
||||||
|
'Saturday': '星期六',
|
||||||
|
'Sunday': '星期日'
|
||||||
|
}
|
||||||
|
weekday_zh = weekday_map.get(now.strftime("%A"), now.strftime("%A"))
|
||||||
|
|
||||||
runtime_info = {
|
runtime_info = {
|
||||||
"model": conf().get("model", "unknown"),
|
"model": conf().get("model", "unknown"),
|
||||||
"workspace": workspace_root,
|
"workspace": workspace_root,
|
||||||
"channel": conf().get("channel_type", "unknown")
|
"channel": conf().get("channel_type", "unknown"),
|
||||||
|
"current_time": now.strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
"weekday": weekday_zh,
|
||||||
|
"timezone": timezone_name
|
||||||
}
|
}
|
||||||
|
|
||||||
system_prompt = prompt_builder.build(
|
system_prompt = prompt_builder.build(
|
||||||
tools=tools,
|
tools=tools,
|
||||||
context_files=context_files,
|
context_files=context_files,
|
||||||
|
skill_manager=skill_manager,
|
||||||
memory_manager=memory_manager,
|
memory_manager=memory_manager,
|
||||||
runtime_info=runtime_info,
|
runtime_info=runtime_info,
|
||||||
is_first_conversation=is_first
|
is_first_conversation=is_first
|
||||||
@@ -507,6 +550,7 @@ class AgentBridge:
|
|||||||
max_steps=50,
|
max_steps=50,
|
||||||
output_mode="logger",
|
output_mode="logger",
|
||||||
workspace_dir=workspace_root,
|
workspace_dir=workspace_root,
|
||||||
|
skill_manager=skill_manager,
|
||||||
enable_skills=True
|
enable_skills=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -285,7 +285,18 @@ class FeiShuChanel(ChatChannel):
|
|||||||
context["origin_ctype"] = ctype
|
context["origin_ctype"] = ctype
|
||||||
|
|
||||||
cmsg = context["msg"]
|
cmsg = context["msg"]
|
||||||
|
|
||||||
|
# Set session_id based on chat type to ensure proper session isolation
|
||||||
|
if cmsg.is_group:
|
||||||
|
# Group chat: combine user_id and group_id to create unique session per user per group
|
||||||
|
# This ensures:
|
||||||
|
# - Same user in different groups have separate conversation histories
|
||||||
|
# - Same user in private chat and group chat have separate histories
|
||||||
|
context["session_id"] = f"{cmsg.from_user_id}:{cmsg.other_user_id}"
|
||||||
|
else:
|
||||||
|
# Private chat: use user_id only
|
||||||
context["session_id"] = cmsg.from_user_id
|
context["session_id"] = cmsg.from_user_id
|
||||||
|
|
||||||
context["receiver"] = cmsg.other_user_id
|
context["receiver"] = cmsg.other_user_id
|
||||||
|
|
||||||
if ctype == ContextType.TEXT:
|
if ctype == ContextType.TEXT:
|
||||||
|
|||||||
Reference in New Issue
Block a user