multi-agent-env / eval /hint_effect.py
Madhav189's picture
Sync project files from main repo
1ba65c9
Raw
History Blame Contribute Delete
11 kB
"""Compare the same commander before and after applying a hint pack."""
from __future__ import annotations
import argparse
import json
import os
from pathlib import Path
import sys
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from inference import LLMCommander, LocalRuntime, RemoteRuntime, build_prompt, scenario_sequence
from runtime.env import load_runtime_env
from training.dataset_builder import EpisodeSummary, StepRecord, write_episode_summaries, write_step_records
def _run_condition(
*,
name: str,
runtime_mode: str,
base_url: str,
episodes: int,
difficulty: str,
scenario_id: str | None,
seed: int,
observation_mode: str,
specialist_mode: str,
commander: LLMCommander,
hint_pack: dict[str, Any] | None,
) -> tuple[list[StepRecord], list[EpisodeSummary]]:
runtime = (
LocalRuntime(specialist_mode=specialist_mode, observation_mode=observation_mode)
if runtime_mode == "local"
else RemoteRuntime(base_url=base_url)
)
records: list[StepRecord] = []
summaries: list[EpisodeSummary] = []
try:
for episode_index, (episode_scenario_id, episode_difficulty) in enumerate(
scenario_sequence(scenario_id=scenario_id, difficulty=difficulty, episodes=episodes),
start=1,
):
episode_seed = seed + episode_index - 1
observation = runtime.reset(
difficulty=episode_difficulty,
scenario_id=episode_scenario_id,
seed=episode_seed,
observation_mode=observation_mode,
specialist_mode=specialist_mode,
)
prior_actions: list[dict[str, object]] = []
actions_rendered: list[str] = []
round_index = 1
while not observation.done:
prompt, hint_id = build_prompt(observation, hint_pack=hint_pack)
reply = commander.complete(
prompt=prompt,
observation_mode=observation_mode,
allowed_actions=observation.allowed_actions,
valid_action_example=observation.valid_action_example,
)
next_observation = runtime.step(reply.action)
reward_total = float(next_observation.reward_breakdown.get("total", 0.0))
records.append(
StepRecord(
prompt=prompt,
completion=reply.completion,
reference_action=reply.action.model_dump(),
reward=reward_total,
reward_breakdown=dict(next_observation.reward_breakdown),
runtime=runtime_mode,
commander_backend=f"{name}:{reply.backend}",
commander_model=reply.model,
observation_mode=observation_mode,
specialist_mode=specialist_mode,
episode_id=observation.metadata.get("episode_id", "") or f"{name}_episode_{episode_index}",
episode_index=episode_index,
step_index=round_index,
scenario_id=observation.scenario_id,
scenario_name=observation.scenario_name,
difficulty=observation.difficulty,
workflow_stage=observation.workflow_stage,
seed=episode_seed,
allowed_actions=list(observation.allowed_actions),
prior_actions=list(prior_actions),
report_targets={report.agent_id.value: report.top_hypothesis_service for report in observation.reports},
report_confidences={report.agent_id.value: report.confidence for report in observation.reports},
specialist_reports=[report.model_dump() for report in observation.reports],
specialist_executions=[execution.model_dump() for execution in observation.specialist_executions],
stage_goal=observation.stage_goal,
valid_action_example=dict(observation.valid_action_example),
commander_parse_status=reply.parse_status,
commander_repair_retry_used=reply.repair_retry_used,
commander_latency_ms=reply.latency_ms,
hint_used=hint_pack is not None,
hint_digest=hint_id,
environment_feedback=next_observation.last_action_result,
)
)
prior_actions.append(reply.action.model_dump())
actions_rendered.append(reply.action.rendered)
observation = next_observation
round_index += 1
summaries.append(
EpisodeSummary(
episode_id=records[-1].episode_id,
episode_index=episode_index,
scenario_id=observation.scenario_id,
scenario_name=observation.scenario_name,
difficulty=observation.difficulty,
seed=episode_seed,
runtime=runtime_mode,
commander_backend=f"{name}:{commander.provider}",
commander_model=commander.model_name,
observation_mode=observation_mode,
specialist_mode=specialist_mode,
steps=round_index - 1,
incident_resolved=observation.incident_resolved,
cumulative_reward=observation.cumulative_reward,
actions=actions_rendered,
)
)
finally:
runtime.close()
return records, summaries
def _summary_payload(summaries: list[EpisodeSummary]) -> dict[str, Any]:
if not summaries:
return {"episodes": 0, "success_rate": 0.0, "mean_reward": 0.0, "mean_steps": 0.0}
return {
"episodes": len(summaries),
"success_rate": round(sum(1 for item in summaries if item.incident_resolved) / len(summaries), 3),
"mean_reward": round(sum(item.cumulative_reward for item in summaries) / len(summaries), 3),
"mean_steps": round(sum(item.steps for item in summaries) / len(summaries), 3),
}
def main() -> None:
load_runtime_env()
parser = argparse.ArgumentParser()
parser.add_argument("--episodes", type=int, default=1)
parser.add_argument("--difficulty", default="easy", choices=["easy", "medium", "hard", "mixed"])
parser.add_argument("--scenario-id", default=None)
parser.add_argument("--seed", type=int, default=42)
parser.add_argument("--runtime", default="local", choices=["local", "remote"])
parser.add_argument("--base-url", default="http://127.0.0.1:8000")
parser.add_argument("--observation-mode", default="multi_agent", choices=["multi_agent", "single_agent"])
parser.add_argument("--specialist-mode", default="llm", choices=["deterministic", "hybrid", "llm"])
parser.add_argument("--hint-file", required=True)
parser.add_argument("--commander-model", default="Qwen/Qwen2.5-3B-Instruct")
parser.add_argument("--commander-provider", default="hf", choices=["openai", "hf"])
parser.add_argument("--commander-base-url", default="http://127.0.0.1:11434/v1")
parser.add_argument("--commander-api-key", default=None)
parser.add_argument("--commander-hf-provider", nargs="?", const="", default=None)
parser.add_argument("--output-dir", default="outputs/hint_effect")
args = parser.parse_args()
output_dir = Path(args.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
hint_pack = json.loads(Path(args.hint_file).read_text(encoding="utf-8"))
commander_api_key = args.commander_api_key
if commander_api_key is None:
commander_api_key = (
os.environ.get("HF_TOKEN")
if args.commander_provider == "hf"
else os.environ.get("OPENAI_API_KEY", "local")
)
blind_commander = LLMCommander(
model_name=args.commander_model,
provider=args.commander_provider,
base_url=args.commander_base_url,
api_key=commander_api_key,
hf_provider=args.commander_hf_provider,
)
hinted_commander = LLMCommander(
model_name=args.commander_model,
provider=args.commander_provider,
base_url=args.commander_base_url,
api_key=commander_api_key,
hf_provider=args.commander_hf_provider,
)
blind_records, blind_summaries = _run_condition(
name="blind",
runtime_mode=args.runtime,
base_url=args.base_url,
episodes=args.episodes,
difficulty=args.difficulty,
scenario_id=args.scenario_id,
seed=args.seed,
observation_mode=args.observation_mode,
specialist_mode=args.specialist_mode,
commander=blind_commander,
hint_pack=None,
)
hinted_records, hinted_summaries = _run_condition(
name="hinted",
runtime_mode=args.runtime,
base_url=args.base_url,
episodes=args.episodes,
difficulty=args.difficulty,
scenario_id=args.scenario_id,
seed=args.seed,
observation_mode=args.observation_mode,
specialist_mode=args.specialist_mode,
commander=hinted_commander,
hint_pack=hint_pack,
)
blind_dataset = output_dir / "blind.jsonl"
blind_summary = output_dir / "blind.summary.json"
hinted_dataset = output_dir / "hinted.jsonl"
hinted_summary = output_dir / "hinted.summary.json"
write_step_records(blind_dataset, blind_records)
write_episode_summaries(blind_summary, blind_summaries)
write_step_records(hinted_dataset, hinted_records)
write_episode_summaries(hinted_summary, hinted_summaries)
comparison = {
"blind": _summary_payload(blind_summaries),
"hinted": _summary_payload(hinted_summaries),
"delta_success_rate": round(
_summary_payload(hinted_summaries)["success_rate"] - _summary_payload(blind_summaries)["success_rate"],
3,
),
"delta_mean_reward": round(
_summary_payload(hinted_summaries)["mean_reward"] - _summary_payload(blind_summaries)["mean_reward"],
3,
),
"artifacts": {
"blind_dataset": str(blind_dataset),
"blind_summary": str(blind_summary),
"hinted_dataset": str(hinted_dataset),
"hinted_summary": str(hinted_summary),
},
}
comparison_path = output_dir / "comparison.json"
comparison_path.write_text(json.dumps(comparison, indent=2), encoding="utf-8")
print(json.dumps(comparison, indent=2))
if __name__ == "__main__":
main()