mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-17 19:27:11 +08:00
Add a self-evolution subsystem that reviews idle conversations in an isolated agent and durably learns from them — patching/creating skills, finishing unfinished tasks, and backfilling missed memory. - Trigger: background idle scan, fires when a session is idle >= N min AND (>= N turns OR context usage > 80%). In-memory cursor reviews only new messages so a session never re-learns old content. - Isolated review agent: same model, restricted toolset, hard write-guard confining edits to the workspace (built-in skills are protected). - Safety: file-level backup before edits + evolution_undo tool; notify the user ONLY when a workspace file actually changed (no-nag rule); capped concurrency. - Records to memory/evolution/<date>.md, surfaced in the memory UI's renamed "Self-Evolution" tab (merged with dream diaries). - Hide internal [SCHEDULED]/[EVOLUTION]/backup_id markers from chat history display (also fixes scheduler marker leakage) while keeping them in stored content for undo. - Flat config: self_evolution_enabled (default off until release), self_evolution_idle_minutes (15), self_evolution_min_turns (6). - Tests: tests/test_evolution.py (stub + real model modes, 7 scenarios).
77 lines
2.2 KiB
Python
77 lines
2.2 KiB
Python
"""Configuration for the self-evolution subsystem.
|
|
|
|
Reads flat ``self_evolution_*`` keys from config.json. All fields have safe
|
|
defaults so the feature degrades gracefully when keys are absent.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
|
|
# Defaults — conservative (see executor module docstring). Disabled by default
|
|
# until release; enable via ``self_evolution_enabled``.
|
|
DEFAULT_ENABLED = False
|
|
DEFAULT_IDLE_MINUTES = 15
|
|
DEFAULT_MIN_TURNS = 6
|
|
# Max review steps for the isolated evolution agent. Kept small (not exposed as
|
|
# config): the review is meant to be cheap and focused, not a long autonomous run.
|
|
DEFAULT_MAX_STEPS = 12
|
|
|
|
|
|
@dataclass
|
|
class EvolutionConfig:
|
|
"""Resolved self-evolution settings."""
|
|
|
|
enabled: bool = DEFAULT_ENABLED
|
|
idle_minutes: int = DEFAULT_IDLE_MINUTES
|
|
min_turns: int = DEFAULT_MIN_TURNS
|
|
max_steps: int = DEFAULT_MAX_STEPS
|
|
|
|
@property
|
|
def idle_seconds(self) -> int:
|
|
return max(60, self.idle_minutes * 60)
|
|
|
|
|
|
def _as_bool(value: Any, fallback: bool) -> bool:
|
|
if isinstance(value, bool):
|
|
return value
|
|
if isinstance(value, str):
|
|
v = value.strip().lower()
|
|
if v in ("true", "1", "yes", "on"):
|
|
return True
|
|
if v in ("false", "0", "no", "off"):
|
|
return False
|
|
return fallback
|
|
|
|
|
|
def _as_pos_int(value: Any, fallback: int) -> int:
|
|
try:
|
|
n = int(value)
|
|
return n if n > 0 else fallback
|
|
except (TypeError, ValueError):
|
|
return fallback
|
|
|
|
|
|
def get_evolution_config() -> EvolutionConfig:
|
|
"""Build EvolutionConfig from the live config.json ``self_evolution_*`` keys."""
|
|
try:
|
|
from config import conf
|
|
c = conf()
|
|
except Exception:
|
|
c = {}
|
|
|
|
def _get(key, default):
|
|
try:
|
|
return c.get(key, default)
|
|
except Exception:
|
|
return default
|
|
|
|
return EvolutionConfig(
|
|
enabled=_as_bool(_get("self_evolution_enabled", None), DEFAULT_ENABLED),
|
|
idle_minutes=_as_pos_int(_get("self_evolution_idle_minutes", None), DEFAULT_IDLE_MINUTES),
|
|
min_turns=_as_pos_int(_get("self_evolution_min_turns", None), DEFAULT_MIN_TURNS),
|
|
max_steps=DEFAULT_MAX_STEPS,
|
|
)
|