mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-21 06:07:13 +08:00
refactor: 改用动态函数实现运行时信息更新(更健壮的方案)
改进点: 1. builder.py: _build_runtime_section() 支持 callable 动态时间函数 2. agent_initializer.py: 传入 get_current_time 函数而非静态时间值 3. agent.py: _rebuild_runtime_section() 动态调用时间函数并重建该部分 优势: - 解耦模板:不依赖具体的提示词格式 - 健壮性:提示词模板改变不会导致功能失效 - 向后兼容:保留对静态时间的支持 - 性能优化:只在需要时才计算时间 相比之前的正则匹配方案,这个方案更加优雅和可维护。
This commit is contained in:
@@ -461,7 +461,7 @@ def _build_context_files_section(context_files: List[ContextFile], language: str
|
|||||||
|
|
||||||
|
|
||||||
def _build_runtime_section(runtime_info: Dict[str, Any], language: str) -> List[str]:
|
def _build_runtime_section(runtime_info: Dict[str, Any], language: str) -> List[str]:
|
||||||
"""构建运行时信息section"""
|
"""构建运行时信息section - 支持动态时间"""
|
||||||
if not runtime_info:
|
if not runtime_info:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -471,7 +471,17 @@ def _build_runtime_section(runtime_info: Dict[str, Any], language: str) -> List[
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Add current time if available
|
# Add current time if available
|
||||||
if runtime_info.get("current_time"):
|
# Support dynamic time via callable function
|
||||||
|
if callable(runtime_info.get("_get_current_time")):
|
||||||
|
try:
|
||||||
|
time_info = runtime_info["_get_current_time"]()
|
||||||
|
time_line = f"当前时间: {time_info['time']} {time_info['weekday']} ({time_info['timezone']})"
|
||||||
|
lines.append(time_line)
|
||||||
|
lines.append("")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"[PromptBuilder] Failed to get dynamic time: {e}")
|
||||||
|
elif runtime_info.get("current_time"):
|
||||||
|
# Fallback to static time for backward compatibility
|
||||||
time_str = runtime_info["current_time"]
|
time_str = runtime_info["current_time"]
|
||||||
weekday = runtime_info.get("weekday", "")
|
weekday = runtime_info.get("weekday", "")
|
||||||
timezone = runtime_info.get("timezone", "")
|
timezone = runtime_info.get("timezone", "")
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
import datetime
|
|
||||||
import re
|
|
||||||
|
|
||||||
from common.log import logger
|
from common.log import logger
|
||||||
from agent.protocol.models import LLMRequest, LLMModel
|
from agent.protocol.models import LLMRequest, LLMModel
|
||||||
@@ -15,7 +13,8 @@ class Agent:
|
|||||||
def __init__(self, system_prompt: str, description: str = "AI Agent", model: LLMModel = None,
|
def __init__(self, system_prompt: str, description: str = "AI Agent", model: LLMModel = None,
|
||||||
tools=None, output_mode="print", max_steps=100, max_context_tokens=None,
|
tools=None, output_mode="print", max_steps=100, max_context_tokens=None,
|
||||||
context_reserve_tokens=None, memory_manager=None, name: str = None,
|
context_reserve_tokens=None, memory_manager=None, name: str = None,
|
||||||
workspace_dir: str = None, skill_manager=None, enable_skills: bool = True):
|
workspace_dir: str = None, skill_manager=None, enable_skills: bool = True,
|
||||||
|
runtime_info: dict = None):
|
||||||
"""
|
"""
|
||||||
Initialize the Agent with system prompt, model, description.
|
Initialize the Agent with system prompt, model, description.
|
||||||
|
|
||||||
@@ -33,6 +32,7 @@ class Agent:
|
|||||||
:param workspace_dir: Optional workspace directory for workspace-specific skills
|
:param workspace_dir: Optional workspace directory for workspace-specific skills
|
||||||
:param skill_manager: Optional SkillManager instance (will be created if None and enable_skills=True)
|
:param skill_manager: Optional SkillManager instance (will be created if None and enable_skills=True)
|
||||||
:param enable_skills: Whether to enable skills support (default: True)
|
:param enable_skills: Whether to enable skills support (default: True)
|
||||||
|
:param runtime_info: Optional runtime info dict (with _get_current_time callable for dynamic time)
|
||||||
"""
|
"""
|
||||||
self.name = name or "Agent"
|
self.name = name or "Agent"
|
||||||
self.system_prompt = system_prompt
|
self.system_prompt = system_prompt
|
||||||
@@ -50,6 +50,7 @@ class Agent:
|
|||||||
self.memory_manager = memory_manager # Memory manager for auto memory flush
|
self.memory_manager = memory_manager # Memory manager for auto memory flush
|
||||||
self.workspace_dir = workspace_dir # Workspace directory
|
self.workspace_dir = workspace_dir # Workspace directory
|
||||||
self.enable_skills = enable_skills # Skills enabled flag
|
self.enable_skills = enable_skills # Skills enabled flag
|
||||||
|
self.runtime_info = runtime_info # Runtime info for dynamic time update
|
||||||
|
|
||||||
# Initialize skill manager
|
# Initialize skill manager
|
||||||
self.skill_manager = None
|
self.skill_manager = None
|
||||||
@@ -107,51 +108,57 @@ class Agent:
|
|||||||
:return: Complete system prompt
|
:return: Complete system prompt
|
||||||
"""
|
"""
|
||||||
# Skills are now included in system_prompt by PromptBuilder
|
# Skills are now included in system_prompt by PromptBuilder
|
||||||
# Update runtime info (timestamp) dynamically before returning
|
# If runtime_info contains dynamic time function, rebuild runtime section
|
||||||
return self._update_runtime_info(self.system_prompt)
|
if self.runtime_info and callable(self.runtime_info.get('_get_current_time')):
|
||||||
|
return self._rebuild_runtime_section(self.system_prompt)
|
||||||
|
return self.system_prompt
|
||||||
|
|
||||||
def _update_runtime_info(self, prompt: str) -> str:
|
def _rebuild_runtime_section(self, prompt: str) -> str:
|
||||||
"""
|
"""
|
||||||
Update runtime information (timestamp) in the system prompt.
|
Rebuild runtime info section with current time.
|
||||||
|
|
||||||
This ensures the model always has the current time, even if the
|
This method dynamically updates the runtime info section by calling
|
||||||
agent was initialized hours ago.
|
the _get_current_time function from runtime_info.
|
||||||
|
|
||||||
:param prompt: Original system prompt
|
:param prompt: Original system prompt
|
||||||
:return: Updated system prompt with current time
|
:return: Updated system prompt with current runtime info
|
||||||
"""
|
"""
|
||||||
# Find the runtime info section
|
|
||||||
runtime_section_pattern = r'(## 运行时信息\s*\n\s*\n)(当前时间: )([^\n]+)(\s+星期[一二三四五六日])(\s+\([^)]+\))?'
|
|
||||||
|
|
||||||
# Get current time info
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
|
|
||||||
# Get timezone
|
|
||||||
try:
|
try:
|
||||||
offset = -time.timezone if not time.daylight else -time.altzone
|
# Get current time dynamically
|
||||||
hours = offset // 3600
|
time_info = self.runtime_info['_get_current_time']()
|
||||||
minutes = (offset % 3600) // 60
|
|
||||||
timezone_name = f"UTC{hours:+03d}:{minutes:02d}" if minutes else f"UTC{hours:+03d}"
|
# Build new runtime section
|
||||||
except Exception:
|
runtime_lines = [
|
||||||
timezone_name = "UTC"
|
"\n## 运行时信息\n",
|
||||||
|
"\n",
|
||||||
# Chinese weekday mapping
|
f"当前时间: {time_info['time']} {time_info['weekday']} ({time_info['timezone']})\n",
|
||||||
weekday_map = {
|
"\n"
|
||||||
'Monday': '星期一', 'Tuesday': '星期二', 'Wednesday': '星期三',
|
]
|
||||||
'Thursday': '星期四', 'Friday': '星期五', 'Saturday': '星期六', 'Sunday': '星期日'
|
|
||||||
}
|
# Add other runtime info
|
||||||
weekday_zh = weekday_map.get(now.strftime("%A"), now.strftime("%A"))
|
runtime_parts = []
|
||||||
|
if self.runtime_info.get("model"):
|
||||||
# Build new time string
|
runtime_parts.append(f"模型={self.runtime_info['model']}")
|
||||||
new_time = now.strftime("%Y-%m-%d %H:%M:%S")
|
if self.runtime_info.get("workspace"):
|
||||||
|
runtime_parts.append(f"工作空间={self.runtime_info['workspace']}")
|
||||||
# Replace the time in the prompt
|
if self.runtime_info.get("channel") and self.runtime_info.get("channel") != "web":
|
||||||
def replace_time(match):
|
runtime_parts.append(f"渠道={self.runtime_info['channel']}")
|
||||||
return f"{match.group(1)}{match.group(2)}{new_time} {weekday_zh} ({timezone_name})"
|
|
||||||
|
if runtime_parts:
|
||||||
updated_prompt = re.sub(runtime_section_pattern, replace_time, prompt)
|
runtime_lines.append("运行时: " + " | ".join(runtime_parts) + "\n")
|
||||||
|
runtime_lines.append("\n")
|
||||||
return updated_prompt
|
|
||||||
|
new_runtime_section = "".join(runtime_lines)
|
||||||
|
|
||||||
|
# Find and replace the runtime section
|
||||||
|
import re
|
||||||
|
pattern = r'\n## 运行时信息\s*\n.*?(?=\n##|\Z)'
|
||||||
|
updated_prompt = re.sub(pattern, new_runtime_section.rstrip('\n'), prompt, flags=re.DOTALL)
|
||||||
|
|
||||||
|
return updated_prompt
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to rebuild runtime section: {e}")
|
||||||
|
return prompt
|
||||||
|
|
||||||
def refresh_skills(self):
|
def refresh_skills(self):
|
||||||
"""Refresh the loaded skills."""
|
"""Refresh the loaded skills."""
|
||||||
|
|||||||
@@ -110,7 +110,8 @@ class AgentInitializer:
|
|||||||
workspace_dir=workspace_root,
|
workspace_dir=workspace_root,
|
||||||
skill_manager=skill_manager,
|
skill_manager=skill_manager,
|
||||||
enable_skills=True,
|
enable_skills=True,
|
||||||
max_context_tokens=max_context_tokens
|
max_context_tokens=max_context_tokens,
|
||||||
|
runtime_info=runtime_info # Pass runtime_info for dynamic time updates
|
||||||
)
|
)
|
||||||
|
|
||||||
# Attach memory manager
|
# Attach memory manager
|
||||||
@@ -289,34 +290,40 @@ class AgentInitializer:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_runtime_info(self, workspace_root: str):
|
def _get_runtime_info(self, workspace_root: str):
|
||||||
"""Get runtime information"""
|
"""Get runtime information with dynamic time support"""
|
||||||
from config import conf
|
from config import conf
|
||||||
|
|
||||||
now = datetime.datetime.now()
|
def get_current_time():
|
||||||
|
"""Get current time dynamically - called each time system prompt is accessed"""
|
||||||
# Get timezone info
|
now = datetime.datetime.now()
|
||||||
try:
|
|
||||||
offset = -time.timezone if not time.daylight else -time.altzone
|
# Get timezone info
|
||||||
hours = offset // 3600
|
try:
|
||||||
minutes = (offset % 3600) // 60
|
offset = -time.timezone if not time.daylight else -time.altzone
|
||||||
timezone_name = f"UTC{hours:+03d}:{minutes:02d}" if minutes else f"UTC{hours:+03d}"
|
hours = offset // 3600
|
||||||
except Exception:
|
minutes = (offset % 3600) // 60
|
||||||
timezone_name = "UTC"
|
timezone_name = f"UTC{hours:+03d}:{minutes:02d}" if minutes else f"UTC{hours:+03d}"
|
||||||
|
except Exception:
|
||||||
# Chinese weekday mapping
|
timezone_name = "UTC"
|
||||||
weekday_map = {
|
|
||||||
'Monday': '星期一', 'Tuesday': '星期二', 'Wednesday': '星期三',
|
# Chinese weekday mapping
|
||||||
'Thursday': '星期四', 'Friday': '星期五', 'Saturday': '星期六', 'Sunday': '星期日'
|
weekday_map = {
|
||||||
}
|
'Monday': '星期一', 'Tuesday': '星期二', 'Wednesday': '星期三',
|
||||||
weekday_zh = weekday_map.get(now.strftime("%A"), now.strftime("%A"))
|
'Thursday': '星期四', 'Friday': '星期五', 'Saturday': '星期六', 'Sunday': '星期日'
|
||||||
|
}
|
||||||
|
weekday_zh = weekday_map.get(now.strftime("%A"), now.strftime("%A"))
|
||||||
|
|
||||||
|
return {
|
||||||
|
'time': now.strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
'weekday': weekday_zh,
|
||||||
|
'timezone': timezone_name
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"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"),
|
"_get_current_time": get_current_time # Dynamic time function
|
||||||
"weekday": weekday_zh,
|
|
||||||
"timezone": timezone_name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def _migrate_config_to_env(self, workspace_root: str):
|
def _migrate_config_to_env(self, workspace_root: str):
|
||||||
|
|||||||
Reference in New Issue
Block a user