feat: add online evolution loop and 5% gray risk-policy rollout

This commit is contained in:
2026-02-28 22:03:30 +08:00
parent fec5aaf8f3
commit d497e8d42a
9 changed files with 948 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Self-evolution MVP cycle runner.
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from pathlib import Path
from dotenv import load_dotenv
PROJECT_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(PROJECT_ROOT))
load_dotenv(dotenv_path=PROJECT_ROOT / ".env")
from evolution.mvp import ChatSourceConfig, DEFAULT_CANDIDATE_PATH, DEFAULT_POLICY_PATH, run_cycle
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Run self-evolution MVP cycle")
parser.add_argument(
"--source",
type=str,
default="mysql",
choices=["auto", "sqlite", "mysql"],
help="Chat data source, default mysql (online)",
)
parser.add_argument("--hours", type=int, default=24, help="Lookback window for chat samples")
parser.add_argument("--max-customers", type=int, default=200, help="Max customers sampled")
parser.add_argument(
"--max-messages-per-customer",
type=int,
default=80,
help="Max messages loaded per customer",
)
parser.add_argument("--runtime-hours", type=int, default=24, help="Runtime metric window")
parser.add_argument(
"--publish",
action="store_true",
help="Write config/evolution_candidate.json when gate passes",
)
parser.add_argument(
"--policy-path",
type=str,
default=str(DEFAULT_POLICY_PATH),
help="Path to evolution gate policy file",
)
parser.add_argument(
"--candidate-path",
type=str,
default=str(DEFAULT_CANDIDATE_PATH),
help="Path to candidate output file",
)
parser.add_argument("--db-path", type=str, default="", help="SQLite path when --source sqlite")
parser.add_argument("--mysql-host", type=str, default=os.getenv("MYSQL_HOST", "127.0.0.1"))
parser.add_argument("--mysql-port", type=int, default=int(os.getenv("MYSQL_PORT", "3306")))
parser.add_argument("--mysql-user", type=str, default=os.getenv("MYSQL_USER", "root"))
parser.add_argument("--mysql-password", type=str, default=os.getenv("MYSQL_PASSWORD", ""))
parser.add_argument("--mysql-database", type=str, default=os.getenv("MYSQL_DATABASE", "ai_cs"))
return parser.parse_args()
def main() -> int:
args = parse_args()
os.environ.setdefault("PYTHONUTF8", "1")
chat_source = ChatSourceConfig(
source=args.source,
sqlite_path=args.db_path or str(PROJECT_ROOT / "db" / "chat_log_db" / "chats.db"),
mysql_host=args.mysql_host,
mysql_port=args.mysql_port,
mysql_user=args.mysql_user,
mysql_password=args.mysql_password,
mysql_database=args.mysql_database,
)
result = run_cycle(
hours=args.hours,
max_customers=args.max_customers,
max_messages_per_customer=args.max_messages_per_customer,
runtime_hours=args.runtime_hours,
publish=args.publish,
chat_source=chat_source,
policy_path=Path(args.policy_path),
candidate_path=Path(args.candidate_path),
)
print(json.dumps(result, ensure_ascii=False, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())