xueyiming 2 дней назад
Родитель
Сommit
6cd7229e63
1 измененных файлов с 98 добавлено и 40 удалено
  1. 98 40
      examples/create/run.py

+ 98 - 40
examples/create/run.py

@@ -220,6 +220,82 @@ def _print_message_details(message: Message):
 
 # ===== 交互菜单 =====
 
+async def perform_reflection(
+    runner: AgentRunner,
+    trace_id: str,
+    store: FileSystemTraceStore,
+    focus: str = "",
+    intervention_message: str = "",
+) -> str:
+    """
+    执行经验总结(反思)
+    
+    Args:
+        runner: AgentRunner 实例
+        trace_id: Trace ID
+        store: TraceStore 实例
+        focus: 可选的反思重点
+        intervention_message: 可选的干预消息,如果提供则一并记录到经验文件中
+    
+    Returns:
+        反思文本内容,如果失败则返回空字符串
+    """
+    from agent.trace.compaction import build_reflect_prompt
+    from datetime import datetime
+    
+    # 保存当前 head_sequence
+    trace = await store.get_trace(trace_id)
+    if not trace:
+        print("未找到 Trace,无法执行反思")
+        return ""
+    
+    saved_head = trace.head_sequence
+    
+    prompt = build_reflect_prompt()
+    if focus:
+        prompt += f"\n\n请特别关注:{focus}"
+    
+    print("正在生成反思...")
+    reflect_cfg = RunConfig(trace_id=trace_id, max_iterations=1, tools=[])
+    
+    reflection_text = ""
+    try:
+        result = await runner.run_result(
+            messages=[{"role": "user", "content": prompt}],
+            config=reflect_cfg,
+        )
+        reflection_text = result.get("summary", "")
+    finally:
+        # 恢复 head_sequence(反思消息成为侧枝)
+        await store.update_trace(trace_id, head_sequence=saved_head)
+    
+    # 追加到 experiences 文件
+    if reflection_text:
+        experiences_path = runner.experiences_path or "./.cache/experiences.md"
+        os.makedirs(os.path.dirname(experiences_path), exist_ok=True)
+        header = f"\n\n---\n\n## {trace_id} ({datetime.now().strftime('%Y-%m-%d %H:%M')})\n\n"
+        
+        # 如果提供了干预消息,也一并记录
+        content_parts = []
+        if intervention_message:
+            content_parts.append(f"**干预消息:**\n{intervention_message}\n")
+        content_parts.append(f"**反思总结:**\n{reflection_text}")
+        
+        with open(experiences_path, "a", encoding="utf-8") as f:
+            f.write(header + "\n".join(content_parts) + "\n")
+        print(f"\n反思已保存到: {experiences_path}")
+        if intervention_message:
+            print("\n--- 干预消息 ---")
+            print(intervention_message)
+        print("\n--- 反思内容 ---")
+        print(reflection_text)
+        print("--- 结束 ---\n")
+    else:
+        print("未生成反思内容")
+    
+    return reflection_text
+
+
 def _read_multiline() -> str:
     """
     读取多行输入,以连续两次回车(空行)结束。
@@ -282,6 +358,13 @@ async def show_interactive_menu(
             # 从 store 读取实际的 last_sequence,避免本地 current_sequence 过时
             live_trace = await store.get_trace(trace_id)
             actual_sequence = live_trace.last_sequence if live_trace and live_trace.last_sequence else current_sequence
+            
+            # 触发干预后,自动执行一次经验总结(并将干预消息一并记录)
+            print("\n" + "=" * 60)
+            print("自动触发经验总结...")
+            print("=" * 60)
+            await perform_reflection(runner, trace_id, store, focus="", intervention_message=text)
+            
             return {
                 "action": "continue",
                 "messages": [{"role": "user", "content": text}],
@@ -292,46 +375,8 @@ async def show_interactive_menu(
             # 触发经验总结
             print("\n触发经验总结...")
             focus = input("请输入反思重点(可选,直接回车跳过): ").strip()
-
-            from agent.trace.compaction import build_reflect_prompt
-
-            # 保存当前 head_sequence
-            trace = await store.get_trace(trace_id)
-            saved_head = trace.head_sequence
-
-            prompt = build_reflect_prompt()
-            if focus:
-                prompt += f"\n\n请特别关注:{focus}"
-
-            print("正在生成反思...")
-            reflect_cfg = RunConfig(trace_id=trace_id, max_iterations=1, tools=[])
-
-            reflection_text = ""
-            try:
-                result = await runner.run_result(
-                    messages=[{"role": "user", "content": prompt}],
-                    config=reflect_cfg,
-                )
-                reflection_text = result.get("summary", "")
-            finally:
-                # 恢复 head_sequence(反思消息成为侧枝)
-                await store.update_trace(trace_id, head_sequence=saved_head)
-
-            # 追加到 experiences 文件
-            if reflection_text:
-                from datetime import datetime
-                experiences_path = runner.experiences_path or "./.cache/experiences.md"
-                os.makedirs(os.path.dirname(experiences_path), exist_ok=True)
-                header = f"\n\n---\n\n## {trace_id} ({datetime.now().strftime('%Y-%m-%d %H:%M')})\n\n"
-                with open(experiences_path, "a", encoding="utf-8") as f:
-                    f.write(header + reflection_text + "\n")
-                print(f"\n反思已保存到: {experiences_path}")
-                print("\n--- 反思内容 ---")
-                print(reflection_text)
-                print("--- 结束 ---\n")
-            else:
-                print("未生成反思内容")
-
+            
+            await perform_reflection(runner, trace_id, store, focus=focus)
             continue
 
         elif choice == "3":
@@ -724,6 +769,19 @@ async def main():
                 print(f"\n✓ Messages 可视化已保存: {html_path}")
             except Exception as e:
                 print(f"\n⚠ 生成 HTML 失败: {e}")
+            
+            # 流程执行完退出时,自动总结一次经验
+            try:
+                final_trace = await store.get_trace(current_trace_id)
+                if final_trace and final_trace.status in ("completed", "failed"):
+                    print("\n" + "=" * 60)
+                    print("流程执行完成,自动触发经验总结...")
+                    print("=" * 60)
+                    await perform_reflection(runner, current_trace_id, store, focus="")
+            except Exception as e:
+                print(f"\n⚠ 自动经验总结失败: {e}")
+                import traceback
+                traceback.print_exc()
 
     # 6. 输出结果
     if final_response: