ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run and retain the exact real Experiment 8-2 comparison.
|
||||
|
||||
The manuscript compares 10,000 deterministic Q-learning episodes with Kimi K3's
|
||||
first attempt in the same treasure-hunt environment. A failed manuscript
|
||||
hypothesis is still a completed experiment; acceptance therefore verifies the
|
||||
protocol and evidence provenance separately from the observed outcome.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import random
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
from experiment import ExperimentRunner
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def _sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as handle:
|
||||
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def _git_revision() -> str | None:
|
||||
try:
|
||||
return subprocess.run(
|
||||
["git", "rev-parse", "HEAD"],
|
||||
cwd=ROOT,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stdout.strip()
|
||||
except (OSError, subprocess.CalledProcessError):
|
||||
return None
|
||||
|
||||
|
||||
def _write_json(path: Path, payload: Any) -> None:
|
||||
def json_default(value: Any) -> Any:
|
||||
if isinstance(value, np.generic):
|
||||
return value.item()
|
||||
if isinstance(value, Path):
|
||||
return str(value)
|
||||
raise TypeError(f"Object of type {type(value).__name__} is not JSON serializable")
|
||||
|
||||
path.write_text(
|
||||
json.dumps(
|
||||
payload, ensure_ascii=False, indent=2, default=json_default
|
||||
) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Real, evidence-retaining Chapter 7 Experiment 8-2 campaign"
|
||||
)
|
||||
parser.add_argument("--model", default="kimi-k3")
|
||||
parser.add_argument("--seed", type=int, default=42)
|
||||
parser.add_argument("--rl-episodes", type=int, default=10_000)
|
||||
parser.add_argument("--rl-eval-episodes", type=int, default=100)
|
||||
parser.add_argument(
|
||||
"--llm-eval-episodes",
|
||||
type=int,
|
||||
default=0,
|
||||
help="The manuscript's core observation is the first attempt; optional later evaluations are separate.",
|
||||
)
|
||||
parser.add_argument("--output-root", default=str(ROOT / "validation"))
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.rl_episodes != 10_000 or args.rl_eval_episodes != 100:
|
||||
parser.error("canonical Experiment 8-2 requires 10,000 RL training and 100 RL evaluation episodes")
|
||||
if args.llm_eval_episodes < 0:
|
||||
parser.error("--llm-eval-episodes must be non-negative")
|
||||
if not os.getenv("MOONSHOT_API_KEY"):
|
||||
parser.error(
|
||||
"MOONSHOT_API_KEY is required: an OpenRouter substitute is not exact Kimi K3 evidence"
|
||||
)
|
||||
|
||||
random.seed(args.seed)
|
||||
np.random.seed(args.seed)
|
||||
|
||||
output_root = Path(args.output_root).expanduser().resolve()
|
||||
runner = ExperimentRunner(results_dir=str(output_root))
|
||||
started_at = datetime.now(timezone.utc)
|
||||
|
||||
rl_results = runner.run_rl_experiment(
|
||||
num_training_episodes=args.rl_episodes,
|
||||
num_eval_episodes=args.rl_eval_episodes,
|
||||
verbose=False,
|
||||
stochastic=False,
|
||||
checkpoint_interval=1000,
|
||||
learning_rate=0.2,
|
||||
discount_factor=0.99,
|
||||
epsilon_decay=0.9995,
|
||||
epsilon_min=0.1,
|
||||
)
|
||||
llm_results = runner.run_llm_experiment(
|
||||
num_training_episodes=1,
|
||||
num_eval_episodes=args.llm_eval_episodes,
|
||||
verbose=False,
|
||||
stochastic=False,
|
||||
model=args.model,
|
||||
)
|
||||
runner.results = {"rl": rl_results, "llm": llm_results}
|
||||
_write_json(runner.experiment_dir / "experiment_results.json", runner.results)
|
||||
|
||||
raw_path = runner.experiment_dir / "llm_experiences.json"
|
||||
raw = json.loads(raw_path.read_text(encoding="utf-8"))
|
||||
training = [
|
||||
episode
|
||||
for episode in raw.get("episode_trajectories", [])
|
||||
if episode.get("phase") == "training"
|
||||
]
|
||||
first_attempt = training[0] if training else None
|
||||
api_records = raw.get("api_records", [])
|
||||
response_ids_present = all(
|
||||
bool((record.get("response") or {}).get("id")) for record in api_records
|
||||
)
|
||||
response_contents_present = all(
|
||||
bool((record.get("response") or {}).get("content")) for record in api_records
|
||||
)
|
||||
no_api_errors = all(not record.get("error") for record in api_records)
|
||||
no_fallbacks = all(not record.get("fallback_used") for record in api_records)
|
||||
direct_exact_kimi = (
|
||||
raw.get("backend", {}).get("provider") == "moonshot"
|
||||
and raw.get("backend", {}).get("model") == "kimi-k3"
|
||||
and raw.get("backend", {}).get("using_openrouter") is False
|
||||
)
|
||||
response_models = sorted(
|
||||
{
|
||||
(record.get("response") or {}).get("model")
|
||||
for record in api_records
|
||||
if (record.get("response") or {}).get("model")
|
||||
}
|
||||
)
|
||||
|
||||
protocol_gates = {
|
||||
"same_deterministic_environment": True,
|
||||
"q_learning_10000_training_episodes": rl_results["training_episodes"] == 10_000,
|
||||
"q_learning_100_evaluation_episodes": args.rl_eval_episodes == 100,
|
||||
"q_learning_reached_full_evaluation_success": rl_results["eval_victory_rate"] == 1.0,
|
||||
"one_kimi_first_attempt_recorded": len(training) == 1,
|
||||
"direct_official_moonshot_kimi_k3": direct_exact_kimi,
|
||||
"one_real_response_per_first_attempt_action": bool(first_attempt)
|
||||
and len(api_records) == first_attempt["steps"],
|
||||
"provider_response_ids_retained": bool(api_records) and response_ids_present,
|
||||
"provider_response_content_retained": bool(api_records) and response_contents_present,
|
||||
"zero_api_errors": no_api_errors,
|
||||
"zero_fallback_actions": no_fallbacks,
|
||||
}
|
||||
acceptance_complete = all(protocol_gates.values())
|
||||
first_attempt_victory = bool(first_attempt and first_attempt["victory"])
|
||||
first_attempt_steps = first_attempt["steps"] if first_attempt else None
|
||||
|
||||
evidence = {
|
||||
"schema_version": 1,
|
||||
"experiment_id": "8-2",
|
||||
"title": "Traditional RL versus Kimi K3 in the same treasure-hunt environment",
|
||||
"started_at": started_at.isoformat(),
|
||||
"finished_at": datetime.now(timezone.utc).isoformat(),
|
||||
"git_revision": _git_revision(),
|
||||
"command": {
|
||||
"argv": sys.argv,
|
||||
"seed": args.seed,
|
||||
"deterministic": True,
|
||||
},
|
||||
"runtime": {
|
||||
"python": sys.version,
|
||||
"platform": platform.platform(),
|
||||
},
|
||||
"backend": raw.get("backend"),
|
||||
"provider_response_models": response_models,
|
||||
"usage": {
|
||||
"successful_api_calls": raw.get("statistics", {}).get("api_calls"),
|
||||
"api_attempts": len(api_records),
|
||||
"total_tokens": raw.get("statistics", {}).get("total_tokens"),
|
||||
"provider_cost": None,
|
||||
"provider_cost_note": "The response exposed token usage but no authoritative billed cost; unknown is not zero.",
|
||||
},
|
||||
"q_learning": {
|
||||
"training_episodes": rl_results["training_episodes"],
|
||||
"training_time_seconds": rl_results["training_time"],
|
||||
"training_victory_rate": rl_results["training_victory_rate"],
|
||||
"evaluation_victory_rate": rl_results["eval_victory_rate"],
|
||||
"evaluation_average_steps": rl_results["eval_avg_steps"],
|
||||
"q_table_states": rl_results["q_table_size"],
|
||||
"learning_curve": rl_results["learning_curve"],
|
||||
},
|
||||
"k3_first_attempt": {
|
||||
"victory": first_attempt_victory,
|
||||
"steps": first_attempt_steps,
|
||||
"reward": first_attempt.get("total_reward") if first_attempt else None,
|
||||
"api_calls": len(api_records),
|
||||
},
|
||||
"protocol_gates": protocol_gates,
|
||||
"acceptance_complete": acceptance_complete,
|
||||
"manuscript_observation_matches": {
|
||||
"first_attempt_victory": first_attempt_victory,
|
||||
"exactly_18_steps": first_attempt_steps == 18,
|
||||
"q_learning_11_step_greedy_solution": rl_results["eval_avg_steps"] == 11.0,
|
||||
},
|
||||
"interpretation": (
|
||||
"The protocol is accepted independently of whether stochastic model behavior reproduces the manuscript's exact 18-step observation."
|
||||
),
|
||||
"artifacts": {
|
||||
"experiment_results": "experiment_results.json",
|
||||
"raw_llm_calls_and_trajectories": "llm_experiences.json",
|
||||
"q_learning_checkpoint": "rl_agent.pkl",
|
||||
},
|
||||
"source_sha256": {
|
||||
name: _sha256(ROOT / name)
|
||||
for name in (
|
||||
"game_environment.py",
|
||||
"rl_agent.py",
|
||||
"llm_agent.py",
|
||||
"experiment.py",
|
||||
"run_experiment_8_2.py",
|
||||
)
|
||||
},
|
||||
}
|
||||
_write_json(runner.experiment_dir / "evidence.json", evidence)
|
||||
_write_json(output_root / "latest.json", {
|
||||
"experiment_id": "8-2",
|
||||
"artifact": str((runner.experiment_dir / "evidence.json").relative_to(output_root)),
|
||||
"acceptance_complete": acceptance_complete,
|
||||
"finished_at": evidence["finished_at"],
|
||||
})
|
||||
|
||||
print(json.dumps({
|
||||
"evidence": str(runner.experiment_dir / "evidence.json"),
|
||||
"acceptance_complete": acceptance_complete,
|
||||
"first_attempt_victory": first_attempt_victory,
|
||||
"first_attempt_steps": first_attempt_steps,
|
||||
}, indent=2))
|
||||
return 0 if acceptance_complete else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user