Files
kwcode/kaiwu/core/execution_trace.py
Val-sss 8fd5275b2e feat: complete MoE framework — token budget, guardrails, observability, session continuity
P1: Token budget tracking (llm/llama_backend.py)
  - Auto-count input/output tokens per LLM call
  - BudgetExceededError when over limit
  - OpenAI API uses real usage data, Ollama estimates

P2: Guardrails (tools/executor.py)
  - Block dangerous commands (rm -rf, git push --force, drop database, etc.)
  - Protect sensitive files (.env, credentials.json, id_rsa)
  - Confine writes to project_root

P3: Execution observability (core/execution_trace.py)
  - Structured trace per task (steps, timing, tokens, success)
  - Human-readable summary() output

P4: Session continuity (memory/session_md.py)
  - Auto-save SESSION.md on exit (recent task summaries)
  - Auto-load on next startup into Gate memory_context
  - Based on Claude Code 4-Layer Memory + Augment "Session-End Spec Update"

Also fixed: apply_patch method accidentally dropped during executor.py rewrite.

311 tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 20:52:13 +08:00

92 lines
2.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Execution Trace: 结构化执行可观测性。
记录每个任务的完整执行链路每步耗时、token、结果任务完成后输出摘要。
"""
import logging
import time
from dataclasses import dataclass, field
from typing import Optional
logger = logging.getLogger(__name__)
@dataclass
class TraceStep:
"""单步执行记录。"""
name: str
started_at: float = 0.0
ended_at: float = 0.0
success: bool = True
detail: str = ""
@property
def elapsed_ms(self) -> float:
return (self.ended_at - self.started_at) * 1000
@dataclass
class ExecutionTrace:
"""一次任务的完整执行轨迹。"""
task_input: str = ""
steps: list[TraceStep] = field(default_factory=list)
total_input_tokens: int = 0
total_output_tokens: int = 0
llm_calls: int = 0
retries: int = 0
success: bool = False
started_at: float = 0.0
ended_at: float = 0.0
@property
def elapsed_s(self) -> float:
return self.ended_at - self.started_at
def begin(self, task_input: str):
"""开始追踪。"""
self.task_input = task_input[:100]
self.started_at = time.time()
def step_start(self, name: str) -> TraceStep:
"""记录一步开始。"""
step = TraceStep(name=name, started_at=time.time())
self.steps.append(step)
return step
def step_end(self, step: TraceStep, success: bool = True, detail: str = ""):
"""记录一步结束。"""
step.ended_at = time.time()
step.success = success
step.detail = detail[:100]
def finish(self, success: bool, llm_usage: Optional[dict] = None):
"""结束追踪,记录最终状态。"""
self.ended_at = time.time()
self.success = success
if llm_usage:
self.total_input_tokens = llm_usage.get("input_tokens", 0)
self.total_output_tokens = llm_usage.get("output_tokens", 0)
self.llm_calls = llm_usage.get("call_count", 0)
def summary(self) -> str:
"""生成人类可读的执行摘要。"""
lines = []
status = "成功" if self.success else "失败"
lines.append(f"[{status}] {self.task_input} ({self.elapsed_s:.1f}s)")
if self.llm_calls > 0:
total_tokens = self.total_input_tokens + self.total_output_tokens
lines.append(f" LLM: {self.llm_calls}次调用, {total_tokens} tokens")
if self.retries > 0:
lines.append(f" 重试: {self.retries}")
# 每步耗时
for step in self.steps:
icon = "+" if step.success else "x"
lines.append(f" {icon} {step.name}: {step.elapsed_ms:.0f}ms")
if step.detail and not step.success:
lines.append(f" {step.detail}")
return "\n".join(lines)