mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-18 20:17:09 +08:00
fix(evolution): skip idle review while a turn is running
This commit is contained in:
@@ -55,6 +55,10 @@ class ChatService:
|
|||||||
context = self._build_context(query, session_id, channel_type)
|
context = self._build_context(query, session_id, channel_type)
|
||||||
self._attach_context_aware_tools(agent, context)
|
self._attach_context_aware_tools(agent, context)
|
||||||
|
|
||||||
|
# Mark this session as mid-run so the self-evolution idle scan does not
|
||||||
|
# fire concurrently when a single turn runs longer than idle_minutes.
|
||||||
|
self._mark_run_active(agent, True)
|
||||||
|
|
||||||
# State shared between the event callback and this method
|
# State shared between the event callback and this method
|
||||||
state = _StreamState()
|
state = _StreamState()
|
||||||
|
|
||||||
@@ -205,6 +209,8 @@ class ChatService:
|
|||||||
logger.info("[ChatService] Cleared agent message history after executor recovery")
|
logger.info("[ChatService] Cleared agent message history after executor recovery")
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
|
# Clear the mid-run flag so idle scans can review this session again.
|
||||||
|
self._mark_run_active(agent, False)
|
||||||
# Release cancel token to keep the registry bounded.
|
# Release cancel token to keep the registry bounded.
|
||||||
if session_id:
|
if session_id:
|
||||||
try:
|
try:
|
||||||
@@ -314,6 +320,15 @@ class ChatService:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"[ChatService] Failed to attach context to scheduler: {e}")
|
logger.warning(f"[ChatService] Failed to attach context to scheduler: {e}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _mark_run_active(agent, active):
|
||||||
|
"""Toggle the self-evolution mid-run flag for this session's agent."""
|
||||||
|
try:
|
||||||
|
from agent.evolution.trigger import mark_run_active
|
||||||
|
mark_run_active(agent, active)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _note_evolution_turn(agent, context):
|
def _note_evolution_turn(agent, context):
|
||||||
"""Record a user turn so the self-evolution idle trigger has signal."""
|
"""Record a user turn so the self-evolution idle trigger has signal."""
|
||||||
|
|||||||
@@ -164,7 +164,24 @@ def build_review_user_message(transcript: str, protected_skills: list = None) ->
|
|||||||
"rare last resorts — stay [SILENT] unless there is a clear, durable signal "
|
"rare last resorts — stay [SILENT] unless there is a clear, durable signal "
|
||||||
"not already covered."
|
"not already covered."
|
||||||
f"{protected_note}\n"
|
f"{protected_note}\n"
|
||||||
|
f"{_language_instruction()}\n"
|
||||||
"<transcript>\n"
|
"<transcript>\n"
|
||||||
f"{transcript}\n"
|
f"{transcript}\n"
|
||||||
"</transcript>"
|
"</transcript>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _language_instruction() -> str:
|
||||||
|
"""Force the user-facing summary into the configured UI language.
|
||||||
|
|
||||||
|
The summary must match what the user reads, so resolve the language from
|
||||||
|
i18n rather than letting the model guess from the (English-heavy) prompt.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from common import i18n
|
||||||
|
lang_name = "中文" if i18n.is_zh() else "English"
|
||||||
|
except Exception:
|
||||||
|
lang_name = "the user's language"
|
||||||
|
return (
|
||||||
|
f"\nIMPORTANT: if you produce a summary, write it ENTIRELY in {lang_name}. "
|
||||||
|
)
|
||||||
|
|||||||
@@ -73,6 +73,20 @@ def note_user_turn(agent, channel_type: str = "", receiver: str = "") -> None:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def mark_run_active(agent, active: bool) -> None:
|
||||||
|
"""Flag whether the agent is mid-run, so idle scans skip a busy session.
|
||||||
|
|
||||||
|
Without this, a single run that lasts longer than idle_minutes would let
|
||||||
|
the scanner fire an evolution pass concurrently with the live turn.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
agent._evo_run_active = bool(active)
|
||||||
|
if active:
|
||||||
|
agent._evo_last_active = time.time()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def start_evolution_trigger(agent_bridge) -> None:
|
def start_evolution_trigger(agent_bridge) -> None:
|
||||||
"""Start the idle-scan thread once per process (idempotent)."""
|
"""Start the idle-scan thread once per process (idempotent)."""
|
||||||
if getattr(agent_bridge, "_evolution_trigger_started", False):
|
if getattr(agent_bridge, "_evolution_trigger_started", False):
|
||||||
@@ -105,6 +119,10 @@ def _scan_once(agent_bridge, cfg) -> None:
|
|||||||
sessions = list(getattr(agent_bridge, "agents", {}).items())
|
sessions = list(getattr(agent_bridge, "agents", {}).items())
|
||||||
for session_id, agent in sessions:
|
for session_id, agent in sessions:
|
||||||
try:
|
try:
|
||||||
|
# Skip sessions whose agent is mid-run: a long turn must not be
|
||||||
|
# reviewed while it is still producing the answer.
|
||||||
|
if getattr(agent, "_evo_run_active", False):
|
||||||
|
continue
|
||||||
last_active = getattr(agent, "_evo_last_active", 0)
|
last_active = getattr(agent, "_evo_last_active", 0)
|
||||||
turns = int(getattr(agent, "_evo_turns", 0))
|
turns = int(getattr(agent, "_evo_turns", 0))
|
||||||
# Enough signal = enough turns OR enough context pressure.
|
# Enough signal = enough turns OR enough context pressure.
|
||||||
|
|||||||
@@ -524,6 +524,15 @@ class AgentBridge:
|
|||||||
session_id, query, context, clear_history
|
session_id, query, context, clear_history
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Mark this session as mid-run so the self-evolution idle scan does
|
||||||
|
# not fire concurrently when a single turn runs longer than
|
||||||
|
# idle_minutes.
|
||||||
|
try:
|
||||||
|
from agent.evolution.trigger import mark_run_active
|
||||||
|
mark_run_active(agent, True)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Use agent's run_stream method with event handler
|
# Use agent's run_stream method with event handler
|
||||||
response = agent.run_stream(
|
response = agent.run_stream(
|
||||||
@@ -533,6 +542,13 @@ class AgentBridge:
|
|||||||
cancel_event=cancel_event,
|
cancel_event=cancel_event,
|
||||||
)
|
)
|
||||||
finally:
|
finally:
|
||||||
|
# Clear the mid-run flag so idle scans can review this session.
|
||||||
|
try:
|
||||||
|
from agent.evolution.trigger import mark_run_active
|
||||||
|
mark_run_active(agent, False)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# Restore original tools
|
# Restore original tools
|
||||||
if context and context.get("is_scheduled_task"):
|
if context and context.get("is_scheduled_task"):
|
||||||
agent.tools = original_tools
|
agent.tools = original_tools
|
||||||
|
|||||||
Reference in New Issue
Block a user