"""Evolution Engine — 从历史轨迹中学习, 反哺各智能体 prompt, 实现自进化。 闭环(自进化): 轨迹采集 → 奖励打分 → 归纳成功/失败模式 → 生成进化建议 → 反哺/追加到各 agent 的系统提示词(可选启用) Level 1 实现: - analyze: 从已保存轨迹中总结高分/低分模式(常见工具序列、步骤分布) - generate_insights: 生成可读的进化洞察(markdown) - evolve_prompt: 将洞察追加到对应 agent 的 system prompt(可选) 轻量自进化的核心: 不编造"强化学习训练", 而是做"基于经验的 prompt 增强" ——把历史中高奖励轨迹用过的工具调用序列、成功策略, 有选择地注入 未来 agent 的提示词, 让系统越用越聪明。 """ from __future__ import annotations import json from collections import Counter from pathlib import Path from typing import Any from .reward import compute_reward_v1 from .trajectory import TrajectoryRecorder class EvolutionEngine: """自进化引擎(Level 1 轻量版)。""" def __init__( self, base_dir: Path, recorder: TrajectoryRecorder, enabled: bool = True, ): self._base_dir = Path(base_dir) self._recorder = recorder self.enabled = enabled self._insights_dir = self._base_dir / "xingbang_evolution" / "insights" self._insights_dir.mkdir(parents=True, exist_ok=True) # ── 分析历史轨迹 ──────────────────────────────────── def _load_all_trajectories(self) -> list[dict[str, Any]]: trajs = [] for p in self._recorder.list_trajectories(): try: trajs.append(json.loads(p.read_text(encoding="utf-8"))) except Exception: continue return trajs def analyze(self) -> dict[str, Any]: """分析全部历史轨迹, 返回统计与模式。""" trajectories = self._load_all_trajectories() if not trajectories: return {"count": 0, "message": "尚无轨迹样本,先积累数据"} # 计算每条奖励 scored = [] for t in trajectories: r = compute_reward_v1(t) t["_reward"] = r scored.append(t) scored.sort(key=lambda x: x["_reward"], reverse=True) high = scored[: max(1, len(scored) // 4)] # 前 25% low = scored[-max(1, len(scored) // 4):] # 后 25% # 归纳高频工具序列 def agent_tool_sequences(trajs): counter = Counter() for t in trajs: for s in t.get("steps", []): seq = tuple(s.get("tools", [])) if seq: counter[seq] += 1 return counter.most_common(5) high_patterns = { "seqs": agent_tool_sequences(high), "avg_steps": sum(len(t["steps"]) for t in high) / max(1, len(high)), "success_rate": sum(1 for t in high if t["final_result"].get("success")) / max(1, len(high)), } return { "count": len(trajectories), "avg_reward": round(sum(t["_reward"] for t in scored) / len(scored), 2), "high": {"samples": len(high), "patterns": high_patterns}, "low": { "samples": len(low), "avg_steps": sum(len(t["steps"]) for t in low) / max(1, len(low)), }, "top_agents_used": self._agent_usage(scored), } def _agent_usage(self, trajs) -> dict: counter = Counter() for t in trajs: for s in t.get("steps", []): counter[s.get("agent", "?")] += 1 return dict(counter.most_common()) # ── 生成进化洞察 ──────────────────────────────────── def generate_insights(self) -> str: """生成可读的 markdown 进化洞察报告。""" analysis = self.analyze() if analysis.get("count", 0) == 0: return "# 自进化洞察\n\n尚无轨迹样本。运行审批流水线积累数据后,系统将自动进化。" lines = [ f"# 兴邦 AI 评审·自进化洞察", f"\n**样本数**: {analysis['count']} 条轨迹 | **平均奖励**: {analysis['avg_reward']}", "", "## ✅ 高分轨迹模式 (前 25%)", ] hp = analysis["high"] lines.append(f"- 平均步骤数: {hp['patterns']['avg_steps']:.1f}") lines.append(f"- 成功率: {hp['patterns']['success_rate']*100:.0f}%") lines.append("- 高频工具序列:") for seq, n in hp["patterns"]["seqs"]: lines.append(f" - {' → '.join(seq)} (出现 {n} 次)") lines += ["", "## ⚠️ 低分轨迹 (后 25%)", f"- 平均步骤数: {analysis['low']['avg_steps']:.1f}"] lines += ["", "## 🧠 智能体使用分布", ""] for agent, count in analysis.get("top_agents_used", {}).items(): lines.append(f"- {agent}: {count} 次") lines += [ "", "## 进化建议", "1. 高分轨迹的工具序列可在各 agent 的 system prompt 中作为'推荐工作流'注入", "2. 步骤数偏多(>5)的 agent 提示词应强调'一次到位,减少反复读取'", "3. 持续积累轨迹 → 达到一定量后启动更高阶优化(LoRA 微调)", ] return "\n".join(lines) def save_insights(self) -> Path: """保存洞察报告到 insights 目录。""" report = self.generate_insights() path = self._insights_dir / "evolution_insights.md" path.write_text(report, encoding="utf-8") return path # ── 反哺 prompt (自进化闭环) ──────────────────────── def evolve_prompt_suggestion(self, agent_key: str) -> str | None: """为指定 agent 生成可注入的 prompt 增强建议(基于历史高分轨迹)。 返回一段可追加到该 agent system_prompt 的文本, 若无历史则 None。 """ analysis = self.analyze() if analysis.get("count", 0) == 0: return None hp = analysis["high"] seqs = hp["patterns"]["seqs"] if not seqs: return None top_seq_text = " → ".join(seqs[0][0]) if seqs else "" suggestion = ( f"\n\n[自进化推荐 · 基于 {analysis['count']} 条历史轨迹] " f"高分任务常用工具序列: {top_seq_text}。" f"请优先复用该模式,减少无效工具往返,一次到位完成任务。" ) return suggestion