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

This commit is contained in:
2026-08-20 13:12:50 +00:00
commit b119135836
10275 changed files with 3284984 additions and 0 deletions
@@ -0,0 +1,6 @@
.env
artifacts/
__pycache__/
*.pyc
.venv/
venv/
+92
View File
@@ -0,0 +1,92 @@
# Experiment 10-4 · Parallel research with real browser sessions
This implementation uses no simulated sources, canned content, or artificial source latency. The Manager dynamically launches one homogeneous worker per real university URL. Every worker owns an isolated Playwright Chromium browser context, navigates the live page, reads rendered text, and uses a real configured LLM endpoint for evidence-constrained profile extraction.
Implemented requirements:
- Dynamic N-way launch with target URL, teacher name, and routed task ID.
- Push status updates over a timestamped asynchronous message bus.
- Per-site timeout/error isolation; an inaccessible or structurally different site does not stop peers.
- First `target_found` is settled under an `asyncio.Lock`; exactly one terminate broadcast is allowed and late hits are recorded.
- Navigation and LLM extraction race against the terminate event. Losing workers cancel at a safe point, acknowledge, and close their browser context.
- Context creation/closure counters make leaked browser sessions an explicit failing audit.
- Serial and parallel paths visit the same live sites and use the same extraction function; wall-clock time and speedup are measured, not estimated.
## Code map
- **Run first:** python demo.py --target "Professor Name" --sites-json sites.example.json --agents 3.
- **Start here:** agents.py::search_one and the Manager run path in run_official_experiment.py.
- **Core behavior:** worker navigation/extraction, async message bus, first-target settlement and cancellation.
- **State / protocol:** task IDs, status/result/terminate events, worker registry and manifest.
- **Verifier:** evidence-constrained extraction, acceptance gates, lock-protected single winner, acknowledgement count and browser-context closure.
- **Experiment variable:** site count, serial versus parallel scheduling and cascade timing.
- **Skip on first pass:** provider request serialization, HTML fixtures and report formatting.
## Run
```bash
# From the repository root: use the shared Chapter 10 environment
uv sync --locked --python 3.12 --extra ch10
# Activate it before changing directories:
# macOS/Linux:
source .venv/bin/activate
# Windows PowerShell: .\.venv\Scripts\Activate.ps1
# Windows cmd: .venv\Scripts\activate.bat
# pip fallback when uv is not installed:
# python -m pip install -e ".[ch10]"
cd chapter10/parallel-web-research
# Single-project compatibility path, still supported during migration:
# python -m pip install -r requirements.txt
playwright install chromium
cp env.example .env # configure one real text-model endpoint
python demo.py # 10 Stanford pages + real serial comparison
```
For the provenance-complete acceptance campaign (the default comparison plus
the four-worker live cascade in one run):
```bash
python run_official_experiment.py --run-id exp10-4-real-receipts-YYYYMMDD-vN
```
This runner stores full rendered browser observations, credential-free raw SDK
request/response bodies with provider response IDs and usage, the message-bus
event stream, exact runtime source hashes, artifact hashes, and acceptance gates.
Use your own university school/directory list:
```bash
python demo.py --target 'Professor Name' --sites-json sites.example.json --agents 3
```
`cascade-stress.example.json` repeats a real target-bearing Stanford profile under distinct query URLs solely to make near-simultaneous live hits and cancellation observable. It is a real-browser stress supplement, not the multi-school research dataset.
## Recorded real integration evidence
On 2026-07-29, the default ten-page Stanford run found Andrew Ng on the live Stanford HAI page using ARK extraction. Parallel wall time was 18.542 s; serial time was 58.264 s, a measured 3.142× speedup. All 10 parallel and 10 serial browser contexts closed. The live cascade stress run produced one winner, one terminate broadcast, three losing-worker acknowledgements, and 4/4 closed contexts.
The current provenance-complete campaign is
[`validation/runs/exp10-4-real-receipts-20260730-v2/manifest.json`](validation/runs/exp10-4-real-receipts-20260730-v2/manifest.json).
All 12 acceptance gates passed: the ten-site parallel and serial paths both
found the target and closed all 20 contexts; the measured speedup was 1.872×;
the cascade produced one broadcast, three loser acknowledgements, and 4/4
closed contexts. The run retains 24 full browser observations, three raw ARK
responses with unique response IDs and usage, and 114 bus events. Seven runtime
source/input hashes and all four artifact hashes recompute exactly, and the
credential scan found zero hits.
The earlier sanitized summary-only records remain at
[`validation/real_parallel_serial_2026-07-29.json`](validation/real_parallel_serial_2026-07-29.json)
and [`validation/real_cascade_2026-07-29.json`](validation/real_cascade_2026-07-29.json)
for historical comparison; they are not the current provenance anchor.
---
## 中文说明
本实现不再使用“可控字符串 + 模拟延迟”。每个同构子 Agent 都拥有独立 Playwright Chromium context,访问真实大学网站、读取实际渲染内容,再由真实 LLM 做证据约束抽取。Manager 维护状态表、错误隔离、超时、加锁单次结算、级联终止、ack 与资源关闭审计;默认还会在同一批网站上实跑串行基线。
+396
View File
@@ -0,0 +1,396 @@
"""Real-browser workers and central coordinator for Experiment 10-4."""
from __future__ import annotations
import asyncio
import time
from dataclasses import dataclass, field
from enum import Enum
from typing import Callable, Dict, List, Optional
from llm import extract_profile
from message_bus import BROADCAST, MessageBus
from sources import Website
class TaskState(str, Enum):
SUBMITTED = "已提交"
RUNNING = "执行中"
SUCCEEDED = "已完成"
FAILED = "失败"
TERMINATED = "已终止"
@dataclass
class TaskRecord:
worker_id: str
source_name: str
state: TaskState = TaskState.SUBMITTED
note: str = ""
updated: float = field(default_factory=time.monotonic)
class BrowserPool:
"""One Chromium process, one fully isolated browser context per worker."""
def __init__(self, headless: bool = True):
self.headless = headless
self._pw = None
self.browser = None
self.contexts_created = 0
self.contexts_closed = 0
async def start(self):
from playwright.async_api import async_playwright
self._pw = await async_playwright().start()
self.browser = await self._pw.chromium.launch(headless=self.headless)
async def new_context(self):
if not self.browser:
raise RuntimeError("BrowserPool not started")
context = await self.browser.new_context()
self.contexts_created += 1
return context
async def mark_closed(self):
self.contexts_closed += 1
async def close(self):
if self.browser:
await self.browser.close()
if self._pw:
await self._pw.stop()
class WorkerAgent:
def __init__(self, worker_id: str, site: Website, bus: MessageBus, target: str,
browsers: BrowserPool, timeout: float = 120,
browser_receipt_sink: Optional[Callable[[dict], None]] = None,
llm_receipt_sink: Optional[Callable[[dict], None]] = None,
run_phase: str = "parallel"):
self.id, self.site, self.bus, self.target = worker_id, site, bus, target
self.browsers, self.timeout = browsers, timeout
self.sub = bus.subscribe(worker_id, types=["task_assigned", "terminate"])
self.terminate = asyncio.Event()
self._termination_reason = ""
self.context = None
self.browser_receipt_sink = browser_receipt_sink
self.llm_receipt_sink = llm_receipt_sink
self.run_phase = run_phase
async def report(self, state: TaskState, note: str):
await self.bus.send(self.id, "coordinator", "status_update", {
"state": state.value, "note": note, "source": self.site.name,
})
async def _signals(self):
while True:
message = await self.sub.get()
if message.type == "terminate":
self._termination_reason = message.payload.get("reason", "cascade")
self.terminate.set()
return
async def _await_interruptibly(self, awaitable):
operation = asyncio.create_task(awaitable)
stopping = asyncio.create_task(self.terminate.wait())
try:
done, _ = await asyncio.wait(
{operation, stopping}, return_when=asyncio.FIRST_COMPLETED
)
except BaseException:
operation.cancel()
stopping.cancel()
await asyncio.gather(operation, stopping, return_exceptions=True)
raise
if stopping in done and self.terminate.is_set():
operation.cancel()
await asyncio.gather(operation, return_exceptions=True)
raise asyncio.CancelledError
stopping.cancel()
await asyncio.gather(stopping, return_exceptions=True)
return await operation
async def _navigate_interruptibly(self, page):
return await self._await_interruptibly(page.goto(
self.site.url, wait_until="domcontentloaded", timeout=int(self.timeout * 1000)
))
async def run(self):
assigned = await self.sub.get()
while assigned.type != "task_assigned":
if assigned.type == "terminate":
self._termination_reason = assigned.payload.get("reason", "cascade")
self.terminate.set()
await self.report(TaskState.TERMINATED, f"安全点响应终止:{self._termination_reason}")
await self.bus.send(self.id, "coordinator", "ack", {
"acked": "terminate", "source": self.site.name,
})
return
assigned = await self.sub.get()
signal_task = asyncio.create_task(self._signals())
try:
await self.report(TaskState.RUNNING, "创建独立 Chromium context")
self.context = await self.browsers.new_context()
page = await self.context.new_page()
await self.report(TaskState.RUNNING, f"正在加载 {self.site.url}")
navigation = await self._navigate_interruptibly(page)
if self.terminate.is_set():
raise asyncio.CancelledError
await self.report(TaskState.RUNNING, "正在读取渲染后的教师页面")
text = await self._await_interruptibly(
page.locator("body").inner_text(timeout=20_000)
)
if self.browser_receipt_sink:
self.browser_receipt_sink({
"kind": "rendered_browser_observation",
"phase": self.run_phase,
"worker_id": self.id,
"site": self.site.name,
"college": self.site.college,
"requested_url": self.site.url,
"final_url": page.url,
"http_status": navigation.status if navigation else None,
"rendered_body_text": text,
})
if self.terminate.is_set():
raise asyncio.CancelledError
await self.report(TaskState.RUNNING, "正在做证据约束的教师信息抽取")
profile = await self._await_interruptibly(
extract_profile(
self.target,
self.site.college,
self.site.url,
text,
receipt_sink=self.llm_receipt_sink,
call_context={
"phase": self.run_phase,
"worker_id": self.id,
"site": self.site.name,
},
)
)
if profile.get("found"):
await self.bus.send(self.id, "coordinator", "target_found", {
"data": profile, "source": self.site.name,
})
await self.report(TaskState.SUCCEEDED, "找到目标教师")
else:
await self.bus.send(self.id, "coordinator", "not_found", {
"reason": profile.get("reason", "not found"), "source": self.site.name,
})
await self.report(TaskState.SUCCEEDED, "页面中未找到目标")
except asyncio.CancelledError:
if self.terminate.is_set():
await self.report(TaskState.TERMINATED, f"安全点响应终止:{self._termination_reason}")
await self.bus.send(self.id, "coordinator", "ack", {
"acked": "terminate", "source": self.site.name,
})
else:
await self.bus.send(self.id, "coordinator", "worker_error", {
"error": f"TimeoutError: exceeded {self.timeout + 15:.0f}s worker deadline",
"source": self.site.name,
})
await self.report(TaskState.FAILED, "任务超时,已关闭独立浏览器会话")
except Exception as exc:
await self.bus.send(self.id, "coordinator", "worker_error", {
"error": f"{type(exc).__name__}: {exc}", "source": self.site.name,
})
await self.report(TaskState.FAILED, f"{type(exc).__name__}: {exc}")
finally:
signal_task.cancel()
await asyncio.gather(signal_task, return_exceptions=True)
context_closed = self.context is None
if self.context:
try:
await self.context.close()
await self.browsers.mark_closed()
context_closed = True
except Exception as exc:
await self.bus.send(self.id, "coordinator", "worker_error", {
"error": f"ContextCloseError: {exc}", "source": self.site.name,
})
await self.bus.send(self.id, "coordinator", "resource_closed", {
"browser_context_closed": context_closed, "source": self.site.name,
})
class Coordinator:
def __init__(self, bus: MessageBus, target: str):
self.bus, self.target = bus, target
self.sub = bus.subscribe("coordinator", types=None)
self.workers: List[WorkerAgent] = []
self.table: Dict[str, TaskRecord] = {}
self._lock = asyncio.Lock()
self._settled = False
self.winner: Optional[str] = None
self.profile: Optional[dict] = None
self.expected_loser_acks: Optional[set[str]] = None
self.duplicate_hits: List[str] = []
self.acks: set[str] = set()
self.errors: Dict[str, str] = {}
self.not_found: Dict[str, str] = {}
self.closed: set[str] = set()
self.resource_failures: Dict[str, str] = {}
def add_worker(self, worker: WorkerAgent):
self.workers.append(worker)
self.table[worker.id] = TaskRecord(worker.id, worker.site.name)
async def _settle(self, worker_id: str, profile: dict):
async with self._lock:
if self._settled:
self.duplicate_hits.append(worker_id)
return
self._settled, self.winner, self.profile = True, worker_id, profile
# Only workers still running when the winner settles receive the
# terminate broadcast and therefore owe an acknowledgement.
self.expected_loser_acks = {
worker.id
for worker in self.workers
if worker.id != worker_id
and worker.id not in self.not_found
and worker.id not in self.errors
}
await self.bus.send("coordinator", BROADCAST, "terminate", {
"reason": f"target_found_by_{worker_id}", "winner": worker_id,
})
async def run(self) -> dict:
started = time.monotonic()
for w in self.workers:
await self.bus.send("coordinator", w.id, "task_assigned", {
"target": self.target, "url": w.site.url, "task_id": w.id,
})
tasks = [asyncio.create_task(asyncio.wait_for(w.run(), timeout=w.timeout + 15)) for w in self.workers]
while len(self.closed) < len(self.workers):
try:
env = await asyncio.wait_for(self.sub.get(), timeout=0.5)
except asyncio.TimeoutError:
if all(t.done() for t in tasks):
break
continue
rec = self.table.get(env.sender_id)
if env.type == "status_update" and rec:
rec.state = TaskState(env.payload["state"])
rec.note = env.payload.get("note", "")
rec.updated = time.monotonic()
elif env.type == "target_found":
await self._settle(env.sender_id, env.payload["data"])
elif env.type == "ack":
self.acks.add(env.sender_id)
elif env.type == "worker_error":
self.errors[env.sender_id] = env.payload["error"]
elif env.type == "not_found":
self.not_found[env.sender_id] = env.payload.get("reason", "not found")
elif env.type == "resource_closed":
self.closed.add(env.sender_id)
if not env.payload.get("browser_context_closed", False):
self.resource_failures[env.sender_id] = "browser context did not close"
await asyncio.gather(*tasks, return_exceptions=True)
failure_types: Dict[str, int] = {}
for error in self.errors.values():
kind = error.split(":", 1)[0]
failure_types[kind] = failure_types.get(kind, 0) + 1
expected_acks = self.expected_loser_acks or set()
missing_acks = expected_acks - self.acks
return {
"outcome": "found" if self.winner else "not_found",
"winner": self.winner,
"profile": self.profile,
"duplicate_hits": self.duplicate_hits,
"acks": sorted(self.acks),
"expected_loser_acks": sorted(expected_acks),
"missing_loser_acks": sorted(missing_acks),
"errors": self.errors,
"failure_summary": {
"count": len(self.errors),
"by_type": failure_types,
},
"not_found_reasons": self.not_found,
"status_table": {
worker_id: {
"source": record.source_name,
"state": record.state.value,
"note": record.note,
}
for worker_id, record in self.table.items()
},
"terminate_broadcasts": sum(1 for e in self.bus.history if e.type == "terminate"),
"parallel_seconds": round(time.monotonic() - started, 3),
"contexts_closed": len(self.closed),
"resource_failures": self.resource_failures,
}
async def search_one(
site: Website,
target: str,
browsers: BrowserPool,
timeout: float,
browser_receipt_sink: Optional[Callable[[dict], None]] = None,
llm_receipt_sink: Optional[Callable[[dict], None]] = None,
worker_id: str = "serial",
run_phase: str = "serial",
) -> dict:
context = await browsers.new_context()
started = time.monotonic()
try:
page = await context.new_page()
navigation = await page.goto(site.url, wait_until="domcontentloaded", timeout=int(timeout * 1000))
text = await page.locator("body").inner_text(timeout=20_000)
if browser_receipt_sink:
browser_receipt_sink({
"kind": "rendered_browser_observation",
"phase": run_phase,
"worker_id": worker_id,
"site": site.name,
"college": site.college,
"requested_url": site.url,
"final_url": page.url,
"http_status": navigation.status if navigation else None,
"rendered_body_text": text,
})
profile = await extract_profile(
target,
site.college,
site.url,
text,
receipt_sink=llm_receipt_sink,
call_context={"phase": run_phase, "worker_id": worker_id, "site": site.name},
)
return {"site": site.name, "profile": profile, "seconds": time.monotonic() - started}
finally:
await context.close()
await browsers.mark_closed()
async def run_sequential(
sites: List[Website],
target: str,
browsers: BrowserPool,
timeout: float,
browser_receipt_sink: Optional[Callable[[dict], None]] = None,
llm_receipt_sink: Optional[Callable[[dict], None]] = None,
run_phase: str = "serial",
) -> dict:
started = time.monotonic()
results = []
for site in sites:
try:
item = await search_one(
site,
target,
browsers,
timeout,
browser_receipt_sink=browser_receipt_sink,
llm_receipt_sink=llm_receipt_sink,
worker_id=f"serial-{len(results):02d}",
run_phase=run_phase,
)
results.append(item)
if item["profile"].get("found"):
break
except Exception as exc:
results.append({"site": site.name, "error": f"{type(exc).__name__}: {exc}"})
return {"seconds": round(time.monotonic() - started, 3), "visited": len(results), "results": results}
@@ -0,0 +1,6 @@
[
{"name": "hai-a", "college": "Stanford HAI", "url": "https://hai.stanford.edu/people/andrew-ng?worker=a"},
{"name": "hai-b", "college": "Stanford HAI", "url": "https://hai.stanford.edu/people/andrew-ng?worker=b"},
{"name": "hai-c", "college": "Stanford HAI", "url": "https://hai.stanford.edu/people/andrew-ng?worker=c"},
{"name": "hai-d", "college": "Stanford HAI", "url": "https://hai.stanford.edu/people/andrew-ng?worker=d"}
]
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""Experiment 10-4: parallel real-browser faculty search."""
from __future__ import annotations
import argparse
import asyncio
import json
import time
from pathlib import Path
try:
from dotenv import load_dotenv
load_dotenv()
except Exception:
pass
from agents import BrowserPool, Coordinator, WorkerAgent, run_sequential
from message_bus import MessageBus
from sources import DEFAULT_SITES, TARGET, load_sites
def parse_args():
p = argparse.ArgumentParser(description="实验 10-4:N 个独立真实浏览器会话并行搜索教师")
p.add_argument("--target", default=TARGET, help="要查找的教师姓名")
p.add_argument("--sites-json", help="网站数组 JSON,每项包含 name/college/url")
p.add_argument("--agents", type=int, default=len(DEFAULT_SITES), help="使用前 N 个网站/Agent")
p.add_argument("--timeout", type=float, default=120, help="每网站超时秒数")
p.add_argument("--headed", action="store_true", help="显示每个真实浏览器页面")
p.add_argument("--quiet", action="store_true", help="不打印逐条总线消息")
p.add_argument("--no-compare", action="store_true", help="跳过串行基线(默认真实运行并对比)")
p.add_argument("--output", default="artifacts/latest.json", help="保存实测结果 JSON")
return p.parse_args()
async def main(args) -> int:
sites = load_sites(args.sites_json, args.agents)
print(f"真实目标:{args.target}; 真实网站/独立 browser context 数:{len(sites)}")
parallel_pool = BrowserPool(headless=not args.headed)
await parallel_pool.start()
try:
bus = MessageBus(verbose=not args.quiet)
coordinator = Coordinator(bus, args.target)
for i, site in enumerate(sites):
coordinator.add_worker(WorkerAgent(
f"agent-{i:02d}", site, bus, args.target, parallel_pool, args.timeout
))
parallel = await coordinator.run()
finally:
await parallel_pool.close()
serial = None
serial_pool = None
if not args.no_compare:
serial_pool = BrowserPool(headless=not args.headed)
await serial_pool.start()
try:
serial = await run_sequential(sites, args.target, serial_pool, args.timeout)
finally:
await serial_pool.close()
evidence = {
"target": args.target,
"sites": [site.__dict__ for site in sites],
"parallel": parallel,
"serial": serial,
"resource_audit": {
"parallel_contexts_created": parallel_pool.contexts_created,
"parallel_contexts_closed": parallel_pool.contexts_closed,
"serial_contexts_created": serial_pool.contexts_created if serial_pool else 0,
"serial_contexts_closed": serial_pool.contexts_closed if serial_pool else 0,
},
"measured_at": time.strftime("%Y-%m-%dT%H:%M:%S%z"),
}
if serial and parallel["parallel_seconds"]:
evidence["measured_speedup"] = round(serial["seconds"] / parallel["parallel_seconds"], 3)
output = Path(args.output)
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(evidence, ensure_ascii=False, indent=2), encoding="utf-8")
print(json.dumps(evidence, ensure_ascii=False, indent=2))
resources_ok = parallel_pool.contexts_created == parallel_pool.contexts_closed
single_cascade = parallel["winner"] is None or parallel["terminate_broadcasts"] == 1
acknowledgements_ok = not parallel["missing_loser_acks"]
print(f"资源清理:{'PASS' if resources_ok else 'FAIL'}; 单次级联广播:{'PASS' if single_cascade else 'FAIL'}")
if parallel["winner"] is None:
print(f"搜索完成:未找到目标教师;失败统计={parallel['failure_summary']}")
return 0 if resources_ok and single_cascade and acknowledgements_ok else 1
if __name__ == "__main__":
raise SystemExit(asyncio.run(main(parse_args())))
@@ -0,0 +1,10 @@
# Set at least one real text-model provider. The first successful endpoint performs
# evidence-constrained profile extraction from text rendered by Chromium.
# ARK_API_KEY=
# ARK_MODEL=doubao-seed-1-6-250615
# MOONSHOT_API_KEY=
# MOONSHOT_MODEL=kimi-k3
# OPENAI_API_KEY=
# OPENAI_MODEL=gpt-4.1-mini
# OPENAI_BASE_URL=
# OPENROUTER_API_KEY=
+103
View File
@@ -0,0 +1,103 @@
"""Evidence-grounded profile extraction with real configured LLM APIs."""
from __future__ import annotations
import json
import os
import time
from typing import Callable, Dict, Optional
ReceiptSink = Optional[Callable[[Dict[str, object]], None]]
def _backends():
from openai import AsyncOpenAI
out = []
if os.getenv("ARK_API_KEY"):
out.append((AsyncOpenAI(api_key=os.environ["ARK_API_KEY"], base_url="https://ark.cn-beijing.volces.com/api/v3"), os.getenv("ARK_MODEL", "doubao-seed-1-6-250615"), "ark"))
if os.getenv("MOONSHOT_API_KEY"):
out.append((AsyncOpenAI(api_key=os.environ["MOONSHOT_API_KEY"], base_url="https://api.moonshot.cn/v1"), os.getenv("MOONSHOT_MODEL", "kimi-k3"), "moonshot"))
if os.getenv("OPENAI_API_KEY"):
out.append((AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"], base_url=os.getenv("OPENAI_BASE_URL") or None), os.getenv("OPENAI_MODEL", "gpt-4.1-mini"), "openai"))
if os.getenv("OPENROUTER_API_KEY"):
raw = os.getenv("OPENAI_MODEL", "gpt-4.1-mini")
out.append((AsyncOpenAI(api_key=os.environ["OPENROUTER_API_KEY"], base_url="https://openrouter.ai/api/v1"), raw if "/" in raw else f"openai/{raw}", "openrouter"))
if not out:
raise RuntimeError("真实网页内容抽取需要 ARK/MOONSHOT/OPENAI/OPENROUTER 任一 API Key")
return out
async def extract_profile(
target: str,
college: str,
url: str,
text: str,
receipt_sink: ReceiptSink = None,
call_context: Optional[Dict[str, object]] = None,
) -> Dict[str, object]:
"""Extract only facts visible in the browser observation.
The deterministic name-presence gate prevents a model from supplying a profile
from parametric memory when the page did not actually contain the target.
"""
if target.casefold() not in text.casefold():
return {"found": False, "reason": "target name absent from rendered page"}
clipped = text[:45_000]
prompt = {
"target": target,
"site_college": college,
"url": url,
"rendered_page_text": clipped,
"instruction": (
"Use only rendered_page_text. Decide whether it contains this exact person's faculty profile. "
"Return JSON keys found, name, college, position, research, evidence. If the name is only a link/listing, "
"found may be true but leave unsupported fields empty. evidence must be a short verbatim excerpt."
),
}
last = None
for client, model, provider in _backends():
started = time.monotonic()
try:
kwargs = dict(
model=model,
messages=[{"role": "user", "content": json.dumps(prompt, ensure_ascii=False)}],
response_format={"type": "json_object"},
)
if "kimi-k3" in model:
kwargs.update(temperature=1, max_tokens=2048)
response = await client.chat.completions.create(**kwargs)
raw_response = response.model_dump(mode="json")
if receipt_sink:
receipt_sink({
"kind": "llm_chat_completion",
"context": dict(call_context or {}),
"provider": provider,
"request": kwargs,
"response": raw_response,
"response_id": response.id,
"response_model": response.model,
"usage": response.usage.model_dump(mode="json") if response.usage else None,
"duration_seconds": round(time.monotonic() - started, 3),
})
content = response.choices[0].message.content or ""
if not content.strip():
raise ValueError("empty model response")
result = json.loads(content)
result["provider"] = provider
result["url"] = url
return result
except Exception as exc:
last = exc
if receipt_sink:
receipt_sink({
"kind": "llm_chat_completion_error",
"context": dict(call_context or {}),
"provider": provider,
"model": model,
"error_type": type(exc).__name__,
"duration_seconds": round(time.monotonic() - started, 3),
})
print(f" [extract] {provider} failed: {type(exc).__name__}; trying next endpoint")
raise RuntimeError("all configured LLM extraction endpoints failed") from last
@@ -0,0 +1,140 @@
"""
进程内异步消息总线(Message Bus)
================================
模仿 Redis Pub/Sub 的语义,但完全跑在单进程的 asyncio 事件循环里,
无需真正部署 Redis。它承担实验 10-4 里"中心协调"的通信底座:
- 每条消息都封装在 ``Envelope`` 信封里,带上 sender_id / target / type / payload
- Agent 通过 ``subscribe()`` 拿到一个订阅句柄,按消息类型接收;
- Agent 通过 ``publish()`` 把消息投递给指定目标或广播给所有人;
- 总线本身不做任何业务判断,只负责"可靠地把信封送达订阅者"
设计要点:
- 使用 ``asyncio.Queue`` 做每个订阅者的收件箱,天然线程/协程安全;
- ``target`` 为 ``BROADCAST`` 时投递给所有订阅了该类型的人(发送者除外);
- 打印带时间戳的事件日志,方便在演示里"看见"发布/订阅的消息流。
"""
from __future__ import annotations
import asyncio
import itertools
import json
import time
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional
# 广播目标常量:发给所有订阅者
BROADCAST = "*"
# 全局单调递增的消息序号,方便在日志里追踪顺序
_seq_counter = itertools.count(1)
# 演示启动时间,用于打印相对时间戳(更易读)
_START_TIME = time.monotonic()
def _now() -> float:
"""返回自演示启动以来的秒数(相对时间戳)。"""
return time.monotonic() - _START_TIME
@dataclass
class Envelope:
"""消息信封:总线里流动的最小单元。"""
sender_id: str # 发送者 ID
target: str # 目标 Agent ID,或 BROADCAST 表示广播
type: str # 消息类型:task_assigned / status_update / result / terminate / ack ...
payload: Dict[str, Any] = field(default_factory=dict) # JSON 负载
seq: int = field(default_factory=lambda: next(_seq_counter)) # 全局序号
ts: float = field(default_factory=_now) # 相对时间戳
def short(self) -> str:
"""给日志用的紧凑单行表示。"""
tgt = "ALL" if self.target == BROADCAST else self.target
try:
body = json.dumps(self.payload, ensure_ascii=False, default=str)
except Exception:
body = str(self.payload)
if len(body) > 80:
body = body[:77] + "..."
return (
f"[t={self.ts:6.2f}s #{self.seq:<3}] "
f"{self.sender_id:>11} -> {tgt:<11} | {self.type:<14} | {body}"
)
class Subscription:
"""订阅句柄:内部就是一个收件箱队列 + 关心的消息类型集合。"""
def __init__(self, owner_id: str, types: Optional[List[str]]):
self.owner_id = owner_id
# types 为 None 表示订阅所有类型
self.types = set(types) if types is not None else None
self.inbox: "asyncio.Queue[Envelope]" = asyncio.Queue()
def accepts(self, env: Envelope) -> bool:
return self.types is None or env.type in self.types
async def get(self) -> Envelope:
return await self.inbox.get()
async def get_nowait_or_wait(self, timeout: float) -> Optional[Envelope]:
"""带超时地取一条消息;超时返回 None(便于子 Agent 在循环里轮询终止信号)。"""
try:
return await asyncio.wait_for(self.inbox.get(), timeout=timeout)
except asyncio.TimeoutError:
return None
class MessageBus:
"""异步消息总线:注册订阅者、投递信封、打印消息流日志。"""
def __init__(self, verbose: bool = True):
# owner_id -> 该 owner 的订阅列表
self._subs: Dict[str, List[Subscription]] = {}
self.verbose = verbose
# 记录全部流过总线的信封,便于事后统计/断言
self.history: List[Envelope] = []
def subscribe(self, owner_id: str, types: Optional[List[str]] = None) -> Subscription:
"""注册一个订阅者,返回订阅句柄。types=None 表示接收所有类型。"""
sub = Subscription(owner_id, types)
self._subs.setdefault(owner_id, []).append(sub)
return sub
async def publish(self, env: Envelope) -> None:
"""把信封投递到总线:广播或点对点。"""
self.history.append(env)
if self.verbose:
print(" BUS " + env.short())
delivered = 0
for owner_id, sub_list in self._subs.items():
# 点对点:只投递给指定目标
if env.target != BROADCAST and owner_id != env.target:
continue
# 广播时不回投给发送者自己
if env.target == BROADCAST and owner_id == env.sender_id:
continue
for sub in sub_list:
if sub.accepts(env):
await sub.inbox.put(env)
delivered += 1
# 让出事件循环,保证消息尽快被对端取走(更接近真实推送时序)
await asyncio.sleep(0)
# —— 便捷构造并发布 ——
async def send(
self,
sender_id: str,
target: str,
type: str,
payload: Optional[Dict[str, Any]] = None,
) -> Envelope:
env = Envelope(sender_id=sender_id, target=target, type=type, payload=payload or {})
await self.publish(env)
return env
@@ -0,0 +1,5 @@
openai>=1.30.0
playwright>=1.44.0
python-dotenv>=1.0.0
pytest>=8.0.0
pytest-asyncio>=0.23.0
@@ -0,0 +1,423 @@
#!/usr/bin/env python3
"""Run Experiment 10-4 with provenance-complete real-provider receipts."""
from __future__ import annotations
import argparse
import asyncio
import hashlib
import importlib.metadata
import json
import os
import platform
import re
import subprocess
import sys
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, Iterable, List
try:
from dotenv import load_dotenv
load_dotenv()
except Exception:
pass
from agents import BrowserPool, Coordinator, WorkerAgent, run_sequential
from message_bus import MessageBus
from sources import TARGET, Website, load_sites
ROOT = Path(__file__).resolve().parent
SOURCE_FILES = [
"run_official_experiment.py",
"demo.py",
"agents.py",
"llm.py",
"message_bus.py",
"sources.py",
"cascade-stress.example.json",
]
SECRET_ENV_NAMES = (
"ARK_API_KEY",
"MOONSHOT_API_KEY",
"OPENAI_API_KEY",
"OPENROUTER_API_KEY",
)
def utc_now() -> str:
return datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
def canonical_bytes(value: Any) -> bytes:
return json.dumps(
value, ensure_ascii=False, sort_keys=True, separators=(",", ":")
).encode("utf-8")
def sha256_bytes(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def sha256_file(path: Path) -> str:
return sha256_bytes(path.read_bytes())
def write_json(path: Path, value: Any) -> None:
path.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
def git_commit() -> 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
class ReceiptRecorder:
def __init__(self) -> None:
self.browser: List[Dict[str, Any]] = []
self.llm: List[Dict[str, Any]] = []
self.bus: List[Dict[str, Any]] = []
def record_browser(self, receipt: dict) -> None:
item = dict(receipt)
body = item.get("rendered_body_text", "")
item["rendered_body_bytes"] = len(body.encode("utf-8"))
item["rendered_body_sha256"] = sha256_bytes(body.encode("utf-8"))
item["captured_at"] = utc_now()
self.browser.append(item)
def record_llm(self, receipt: dict) -> None:
item = dict(receipt)
request = item.get("request")
response = item.get("response")
if request is not None:
item["request_sha256"] = sha256_bytes(canonical_bytes(request))
if response is not None:
item["response_sha256"] = sha256_bytes(canonical_bytes(response))
item["captured_at"] = utc_now()
self.llm.append(item)
def record_bus(self, phase: str, bus: MessageBus) -> None:
for env in bus.history:
self.bus.append({
"phase": phase,
"sender_id": env.sender_id,
"target": env.target,
"type": env.type,
"payload": env.payload,
"sequence": env.seq,
"relative_seconds": round(env.ts, 6),
})
async def run_parallel_phase(
sites: List[Website],
target: str,
timeout: float,
phase: str,
recorder: ReceiptRecorder,
) -> Dict[str, Any]:
pool = BrowserPool(headless=True)
await pool.start()
browser_version = pool.browser.version if pool.browser else None
try:
bus = MessageBus(verbose=False)
coordinator = Coordinator(bus, target)
for index, site in enumerate(sites):
coordinator.add_worker(WorkerAgent(
f"agent-{index:02d}",
site,
bus,
target,
pool,
timeout,
browser_receipt_sink=recorder.record_browser,
llm_receipt_sink=recorder.record_llm,
run_phase=phase,
))
result = await coordinator.run()
recorder.record_bus(phase, bus)
finally:
await pool.close()
return {
"result": result,
"contexts_created": pool.contexts_created,
"contexts_closed": pool.contexts_closed,
"chromium_version": browser_version,
}
async def run_serial_phase(
sites: List[Website],
target: str,
timeout: float,
recorder: ReceiptRecorder,
) -> Dict[str, Any]:
pool = BrowserPool(headless=True)
await pool.start()
browser_version = pool.browser.version if pool.browser else None
try:
result = await run_sequential(
sites,
target,
pool,
timeout,
browser_receipt_sink=recorder.record_browser,
llm_receipt_sink=recorder.record_llm,
run_phase="default_serial",
)
finally:
await pool.close()
return {
"result": result,
"contexts_created": pool.contexts_created,
"contexts_closed": pool.contexts_closed,
"chromium_version": browser_version,
}
def gate(status: bool, **details: Any) -> Dict[str, Any]:
return {"status": "pass" if status else "fail", **details}
def find_credential_hits(payloads: Iterable[bytes]) -> Dict[str, int]:
blobs = list(payloads)
actual_secret_hits = 0
for name in SECRET_ENV_NAMES:
secret = os.getenv(name, "").encode("utf-8")
if len(secret) >= 8:
actual_secret_hits += sum(blob.count(secret) for blob in blobs)
generic_patterns = (
re.compile(rb'(?i)"(?:api[_-]?key|authorization)"\s*:\s*"(?!<redacted>|null|")[^"]+"'),
re.compile(rb'(?i)bearer\s+[a-z0-9._~+/=-]{16,}'),
)
pattern_hits = sum(len(pattern.findall(blob)) for pattern in generic_patterns for blob in blobs)
return {"actual_secret_hits": actual_secret_hits, "credential_pattern_hits": pattern_hits}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--target", default=TARGET)
parser.add_argument("--timeout", type=float, default=120.0)
parser.add_argument("--run-id", help="immutable validation/runs directory name")
parser.add_argument("--output-root", default=str(ROOT / "validation" / "runs"))
return parser.parse_args()
async def main(args: argparse.Namespace) -> int:
run_id = args.run_id or f"exp10-4-real-receipts-{datetime.now(timezone.utc):%Y%m%dT%H%M%SZ}"
run_dir = Path(args.output_root).resolve() / run_id
run_dir.mkdir(parents=True, exist_ok=False)
started_at = utc_now()
started_monotonic = time.monotonic()
source_hashes = {
path: sha256_file(ROOT / path)
for path in SOURCE_FILES
}
default_sites = load_sites(None)
cascade_sites = load_sites(str(ROOT / "cascade-stress.example.json"))
recorder = ReceiptRecorder()
default_parallel = await run_parallel_phase(
default_sites, args.target, args.timeout, "default_parallel", recorder
)
default_serial = await run_serial_phase(
default_sites, args.target, args.timeout, recorder
)
cascade = await run_parallel_phase(
cascade_sites, args.target, args.timeout, "cascade_stress", recorder
)
browser_path = run_dir / "browser_receipts.json"
llm_path = run_dir / "llm_receipts.json"
bus_path = run_dir / "message_bus_receipts.json"
write_json(browser_path, {"schema_version": 1, "receipts": recorder.browser})
write_json(llm_path, {"schema_version": 1, "receipts": recorder.llm})
write_json(bus_path, {"schema_version": 1, "receipts": recorder.bus})
parallel_result = default_parallel["result"]
serial_result = default_serial["result"]
cascade_result = cascade["result"]
speedup = (
round(serial_result["seconds"] / parallel_result["parallel_seconds"], 3)
if parallel_result["parallel_seconds"]
else None
)
successful_llm = [r for r in recorder.llm if r["kind"] == "llm_chat_completion"]
phases_with_browser_receipts = sorted({r["phase"] for r in recorder.browser})
phases_with_llm_receipts = sorted({r["context"]["phase"] for r in successful_llm})
cascade_expected_acks = set(cascade_result["expected_loser_acks"])
cascade_actual_acks = set(cascade_result["acks"])
credential_scan = find_credential_hits(
[browser_path.read_bytes(), llm_path.read_bytes(), bus_path.read_bytes()]
)
gates = {
"ten_real_default_sites": gate(
len(default_sites) == 10 and all(s.url.startswith("https://") for s in default_sites),
count=len(default_sites),
),
"same_sites_parallel_and_serial": gate(
serial_result["visited"] == len(default_sites),
configured_count=len(default_sites),
serial_visited=serial_result["visited"],
),
"default_target_found_both_modes": gate(
parallel_result["outcome"] == "found"
and any(item.get("profile", {}).get("found") for item in serial_result["results"]),
parallel_winner=parallel_result["winner"],
),
"default_resources_closed": gate(
default_parallel["contexts_created"] == default_parallel["contexts_closed"] == len(default_sites)
and default_serial["contexts_created"] == default_serial["contexts_closed"] == len(default_sites),
parallel_created=default_parallel["contexts_created"],
parallel_closed=default_parallel["contexts_closed"],
serial_created=default_serial["contexts_created"],
serial_closed=default_serial["contexts_closed"],
),
"measured_parallel_speedup": gate(speedup is not None and speedup > 1, speedup=speedup),
"raw_browser_receipts": gate(
len(recorder.browser) >= len(default_sites)
and {"default_parallel", "default_serial", "cascade_stress"}.issubset(phases_with_browser_receipts),
count=len(recorder.browser),
phases=phases_with_browser_receipts,
),
"raw_llm_provider_receipts": gate(
len(successful_llm) >= 3
and all(r.get("response_id") and r.get("response") for r in successful_llm)
and {"default_parallel", "default_serial", "cascade_stress"}.issubset(phases_with_llm_receipts),
successful_count=len(successful_llm),
response_ids=[r.get("response_id") for r in successful_llm],
phases=phases_with_llm_receipts,
),
"single_cascade_settlement": gate(
cascade_result["winner"] is not None
and cascade_result["terminate_broadcasts"] == 1
and not cascade_result["duplicate_hits"],
winner=cascade_result["winner"],
terminate_broadcasts=cascade_result["terminate_broadcasts"],
duplicate_hits=cascade_result["duplicate_hits"],
),
"cascade_loser_acknowledgements": gate(
cascade_expected_acks == cascade_actual_acks
and not cascade_result["missing_loser_acks"],
expected=sorted(cascade_expected_acks),
actual=sorted(cascade_actual_acks),
),
"cascade_resources_closed": gate(
cascade["contexts_created"] == cascade["contexts_closed"] == len(cascade_sites),
created=cascade["contexts_created"],
closed=cascade["contexts_closed"],
),
"runtime_source_hashes": gate(
len(source_hashes) == len(SOURCE_FILES)
and all(len(value) == 64 for value in source_hashes.values()),
count=len(source_hashes),
),
"credential_free_artifacts": gate(
credential_scan["actual_secret_hits"] == 0
and credential_scan["credential_pattern_hits"] == 0,
**credential_scan,
),
}
overall_status = "pass" if all(item["status"] == "pass" for item in gates.values()) else "incomplete"
evidence = {
"schema_version": 2,
"experiment": "10-4",
"run_id": run_id,
"run_type": "real_parallel_serial_and_cascade_with_raw_receipts",
"started_at": started_at,
"completed_at": utc_now(),
"duration_seconds": round(time.monotonic() - started_monotonic, 3),
"target": args.target,
"git_commit": git_commit(),
"environment": {
"python": sys.version.split()[0],
"platform": platform.platform(),
"playwright": importlib.metadata.version("playwright"),
"parallel_chromium": default_parallel["chromium_version"],
"serial_chromium": default_serial["chromium_version"],
"cascade_chromium": cascade["chromium_version"],
},
"inputs": {
"default_sites": [site.__dict__ for site in default_sites],
"cascade_sites": [site.__dict__ for site in cascade_sites],
"timeout_seconds": args.timeout,
},
"default_parallel": default_parallel,
"default_serial": default_serial,
"measured_speedup": speedup,
"cascade_stress": cascade,
"receipt_counts": {
"browser": len(recorder.browser),
"llm_all_attempts": len(recorder.llm),
"llm_successful": len(successful_llm),
"message_bus": len(recorder.bus),
},
"gates": gates,
"overall_status": overall_status,
}
evidence_path = run_dir / "evidence.json"
write_json(evidence_path, evidence)
artifact_paths = [evidence_path, browser_path, llm_path, bus_path]
manifest = {
"schema_version": 1,
"experiment": "10-4",
"run_id": run_id,
"generated_at": utc_now(),
"git_commit": evidence["git_commit"],
"runtime_source_sha256": source_hashes,
"input_sha256": {
"default_sites_canonical_json": sha256_bytes(canonical_bytes(evidence["inputs"]["default_sites"])),
"cascade_sites_canonical_json": sha256_bytes(canonical_bytes(evidence["inputs"]["cascade_sites"])),
},
"artifact_sha256": {
path.name: sha256_file(path)
for path in artifact_paths
},
"acceptance": {
"overall_status": overall_status,
"passed_gates": sum(item["status"] == "pass" for item in gates.values()),
"total_gates": len(gates),
},
}
manifest_path = run_dir / "manifest.json"
write_json(manifest_path, manifest)
latest = ROOT / "validation" / "latest.json"
write_json(latest, {
"schema_version": 1,
"run_id": run_id,
"run_directory": str(run_dir.relative_to(ROOT)),
"manifest_sha256": sha256_file(manifest_path),
"overall_status": overall_status,
})
print(json.dumps({
"run_id": run_id,
"run_directory": str(run_dir),
"overall_status": overall_status,
"passed_gates": manifest["acceptance"]["passed_gates"],
"total_gates": manifest["acceptance"]["total_gates"],
"measured_speedup": speedup,
"receipt_counts": evidence["receipt_counts"],
}, ensure_ascii=False, indent=2))
return 0 if overall_status == "pass" else 1
if __name__ == "__main__":
raise SystemExit(asyncio.run(main(parse_args())))
@@ -0,0 +1,5 @@
[
{"name": "law", "college": "Law School", "url": "https://law.stanford.edu/directory/?tax_and_terms=1067"},
{"name": "education", "college": "Graduate School of Education", "url": "https://ed.stanford.edu/faculty"},
{"name": "profile", "college": "Stanford Profiles", "url": "https://profiles.stanford.edu/andrew-ng"}
]
@@ -0,0 +1,50 @@
"""Real university website inputs for Experiment 10-4."""
from __future__ import annotations
import json
from dataclasses import dataclass
from pathlib import Path
from typing import List
@dataclass(frozen=True)
class Website:
name: str
college: str
url: str
TARGET = "Andrew Ng"
# All are real Stanford-owned web pages. They intentionally mix directories and
# school/center profiles, as a user-supplied experiment should. URLs are data, not
# mocked content; every worker launches an isolated Playwright browser context.
DEFAULT_SITES: List[Website] = [
Website("medicine-profiles", "School of Medicine", "https://med.stanford.edu/profiles/browse"),
Website("law-faculty", "Stanford Law School", "https://law.stanford.edu/directory/?tax_and_terms=1067"),
Website("education-faculty", "Graduate School of Education", "https://ed.stanford.edu/faculty"),
Website("business-faculty", "Graduate School of Business", "https://www.gsb.stanford.edu/faculty-research/faculty"),
Website("sustainability-faculty", "Doerr School of Sustainability", "https://sustainability.stanford.edu/people/faculty"),
Website("humanities-faculty", "School of Humanities and Sciences", "https://humsci.stanford.edu/about/leadership-and-administration/deans-office"),
Website("engineering-faculty", "School of Engineering", "https://engineering.stanford.edu/faculty-research/faculty"),
Website("computer-science", "School of Engineering / Computer Science", "https://www.cs.stanford.edu/people/faculty"),
Website("stanford-profiles", "Stanford Profiles", "https://profiles.stanford.edu/andrew-ng"),
Website("human-ai", "Stanford HAI", "https://hai.stanford.edu/people/andrew-ng"),
]
def load_sites(path: str | None, limit: int | None = None) -> List[Website]:
if path:
raw = json.loads(Path(path).read_text(encoding="utf-8"))
sites = [Website(**item) for item in raw]
else:
sites = list(DEFAULT_SITES)
if limit is not None:
sites = sites[:limit]
if not sites:
raise ValueError("网站列表不能为空")
for site in sites:
if not site.url.startswith(("http://", "https://")):
raise ValueError(f"{site.name} 不是 HTTP(S) URL")
return sites
@@ -0,0 +1,82 @@
import asyncio
import pytest
from agents import Coordinator, TaskRecord
from message_bus import MessageBus
from sources import Website
@pytest.mark.asyncio
async def test_worker_completing_before_winner_is_not_reported_as_missing_ack():
"""Contract proved: Coordinator.run excludes workers that completed with not_found or errors prior to target settlement from expected_loser_acks.
Bug locked out: falsely reporting self-completed workers as missing loser ACKs when a winner settles."""
bus = MessageBus(verbose=False)
coordinator = Coordinator(bus, "target")
site1 = Website("Site 1", "https://site1.edu", "College 1")
site2 = Website("Site 2", "https://site2.edu", "College 2")
site3 = Website("Site 3", "https://site3.edu", "College 3")
# Create fake worker objects for coordinator.workers
class FakeWorker:
def __init__(self, wid, site):
self.id = wid
self.site = site
self.timeout = 10
async def run(self):
pass
workers = [
FakeWorker("worker-1", site1),
FakeWorker("worker-2", site2),
FakeWorker("worker-3", site3),
]
for w in workers:
coordinator.add_worker(w)
# Worker 1 completed with not_found BEFORE settlement
await bus.send("worker-1", "coordinator", "not_found", {"reason": "not found", "source": "Site 1"})
await bus.send("worker-1", "coordinator", "resource_closed", {"browser_context_closed": True, "source": "Site 1"})
# Worker 2 found target (winner)
await bus.send("worker-2", "coordinator", "target_found", {"data": {"found": True}, "source": "Site 2"})
await bus.send("worker-2", "coordinator", "resource_closed", {"browser_context_closed": True, "source": "Site 2"})
# Worker 3 received terminate and acknowledged it
await bus.send("worker-3", "coordinator", "ack", {"acked": "terminate", "source": "Site 3"})
await bus.send("worker-3", "coordinator", "resource_closed", {"browser_context_closed": True, "source": "Site 3"})
result = await coordinator.run()
assert result["winner"] == "worker-2"
assert result["expected_loser_acks"] == ["worker-3"]
assert result["missing_loser_acks"] == []
@pytest.mark.asyncio
async def test_worker_completing_after_winner_still_owes_ack():
"""The expected ACK set is a snapshot taken when the winner settles."""
bus = MessageBus(verbose=False)
coordinator = Coordinator(bus, "target")
class FakeWorker:
timeout = 10
def __init__(self, wid):
self.id = wid
self.site = Website(wid, f"https://{wid}.edu", wid)
async def run(self):
pass
for worker_id in ("worker-1", "worker-2"):
coordinator.add_worker(FakeWorker(worker_id))
await bus.send("worker-2", "coordinator", "target_found", {"data": {"found": True}})
await bus.send("worker-1", "coordinator", "not_found", {"reason": "finished after settlement"})
for worker_id in ("worker-1", "worker-2"):
await bus.send(worker_id, "coordinator", "resource_closed", {"browser_context_closed": True})
result = await coordinator.run()
assert result["expected_loser_acks"] == ["worker-1"]
assert result["missing_loser_acks"] == ["worker-1"]
@@ -0,0 +1,151 @@
import asyncio
from types import SimpleNamespace
import pytest
from agents import Coordinator
from agents import WorkerAgent
from message_bus import MessageBus
from sources import DEFAULT_SITES, load_sites
@pytest.mark.asyncio
async def test_near_simultaneous_hits_settle_and_broadcast_once():
bus = MessageBus(verbose=False)
coordinator = Coordinator(bus, "target")
await asyncio.gather(
coordinator._settle("agent-a", {"name": "target"}),
coordinator._settle("agent-b", {"name": "target"}),
)
assert coordinator.winner in {"agent-a", "agent-b"}
assert len(coordinator.duplicate_hits) == 1
assert sum(m.type == "terminate" for m in bus.history) == 1
def test_default_dataset_is_ten_real_http_university_pages():
sites = load_sites(None)
assert len(sites) == 10
assert all(s.url.startswith("https://") for s in sites)
assert all(not hasattr(s, "content") and not hasattr(s, "latency") for s in sites)
class StubWorker:
def __init__(self, worker_id, bus, events):
self.id = worker_id
self.site = SimpleNamespace(
name=f"source-{worker_id}", url=f"https://example.test/{worker_id}"
)
self.bus = bus
self.events = events
self.timeout = 0.1
self.sub = bus.subscribe(worker_id, types=["task_assigned", "terminate"])
async def run(self):
assigned = await self.sub.get()
assert assigned.type == "task_assigned"
for event_type, payload in self.events:
if event_type == "status_update":
payload = {"source": self.site.name, **payload}
else:
payload = {**payload, "source": self.site.name}
await self.bus.send(self.id, "coordinator", event_type, payload)
@pytest.mark.asyncio
async def test_all_not_found_has_no_cascade_and_returns_reason_and_status_aggregation():
bus = MessageBus(verbose=False)
coordinator = Coordinator(bus, "missing")
for worker_id in ("agent-a", "agent-b"):
coordinator.add_worker(StubWorker(worker_id, bus, [
("status_update", {"state": "执行中", "note": "reading"}),
("not_found", {"reason": "target absent"}),
("status_update", {"state": "已完成", "note": "未找到目标"}),
("resource_closed", {"browser_context_closed": True}),
]))
result = await coordinator.run()
assert result["outcome"] == "not_found"
assert result["winner"] is None
assert result["terminate_broadcasts"] == 0
assert result["not_found_reasons"] == {
"agent-a": "target absent", "agent-b": "target absent",
}
assert all(row["state"] == "已完成" for row in result["status_table"].values())
assert result["failure_summary"] == {"count": 0, "by_type": {}}
@pytest.mark.asyncio
async def test_worker_failure_is_isolated_and_summarized_while_peer_completes():
bus = MessageBus(verbose=False)
coordinator = Coordinator(bus, "missing")
coordinator.add_worker(StubWorker("bad", bus, [
("worker_error", {"error": "TimeoutError: deadline"}),
("status_update", {"state": "失败", "note": "timeout"}),
("resource_closed", {"browser_context_closed": True}),
]))
coordinator.add_worker(StubWorker("good", bus, [
("not_found", {"reason": "target absent"}),
("status_update", {"state": "已完成", "note": "peer completed"}),
("resource_closed", {"browser_context_closed": True}),
]))
result = await coordinator.run()
assert result["outcome"] == "not_found"
assert result["errors"] == {"bad": "TimeoutError: deadline"}
assert result["not_found_reasons"] == {"good": "target absent"}
assert result["failure_summary"] == {"count": 1, "by_type": {"TimeoutError": 1}}
assert result["status_table"]["good"]["state"] == "已完成"
@pytest.mark.asyncio
async def test_timeout_cancellation_closes_real_worker_context():
class BlockingPage:
async def goto(self, *args, **kwargs):
return None
def locator(self, _selector):
return self
async def inner_text(self, **kwargs):
await asyncio.Future()
class Context:
def __init__(self):
self.closed = False
async def new_page(self):
return BlockingPage()
async def close(self):
self.closed = True
class Pool:
def __init__(self):
self.context = Context()
self.closed = 0
async def new_context(self):
return self.context
async def mark_closed(self):
self.closed += 1
bus = MessageBus(verbose=False)
pool = Pool()
site = SimpleNamespace(name="blocking", url="https://example.test")
worker = WorkerAgent("agent-timeout", site, bus, "target", pool, timeout=0.01)
coordinator_sub = bus.subscribe("coordinator", types=None)
await bus.send("coordinator", worker.id, "task_assigned", {})
# The outer Manager deadline cancels the worker while body text is pending.
await asyncio.wait_for(worker.run(), timeout=0.05)
assert pool.context.closed is True
assert pool.closed == 1
messages = []
while not coordinator_sub.inbox.empty():
messages.append(coordinator_sub.inbox.get_nowait())
assert any(m.type == "worker_error" and "TimeoutError" in m.payload["error"] for m in messages)
assert any(m.type == "resource_closed" and m.payload["browser_context_closed"] for m in messages)
@@ -0,0 +1,22 @@
import json
from pathlib import Path
ROOT = Path(__file__).parent / "validation"
def test_real_parallel_serial_evidence_closes_every_context_and_measures_speedup():
data = json.loads((ROOT / "real_parallel_serial_2026-07-29.json").read_text())
assert data["overall_status"] == "pass"
assert data["parallel"]["contexts_created"] == data["parallel"]["contexts_closed"] == 10
assert data["serial"]["contexts_created"] == data["serial"]["contexts_closed"] == 10
assert data["parallel"]["errors"] == {}
assert data["measured_speedup"] > 1
def test_real_cascade_evidence_has_one_broadcast_all_acks_and_no_leaks():
data = json.loads((ROOT / "real_cascade_2026-07-29.json").read_text())
assert data["terminate_broadcasts"] == 1
assert len(data["loser_acknowledgements"]) == data["workers"] - 1
assert data["contexts_created"] == data["contexts_closed"]
assert data["errors"] == {}
@@ -0,0 +1,71 @@
import hashlib
import json
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parent
RUN = ROOT / "validation" / "runs" / "exp10-4-real-receipts-20260730-v2"
def sha256_bytes(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def canonical_bytes(value) -> bytes:
return json.dumps(
value, ensure_ascii=False, sort_keys=True, separators=(",", ":")
).encode("utf-8")
def test_official_manifest_binds_artifacts_and_runtime_sources():
manifest = json.loads((RUN / "manifest.json").read_text(encoding="utf-8"))
assert manifest["acceptance"] == {
"overall_status": "pass",
"passed_gates": 12,
"total_gates": 12,
}
for name, expected in manifest["artifact_sha256"].items():
assert sha256_bytes((RUN / name).read_bytes()) == expected
for name, expected in manifest["runtime_source_sha256"].items():
assert sha256_bytes((ROOT / name).read_bytes()) == expected
def test_official_receipts_are_raw_hashed_and_cover_all_three_phases():
browser = json.loads((RUN / "browser_receipts.json").read_text(encoding="utf-8"))["receipts"]
llm = json.loads((RUN / "llm_receipts.json").read_text(encoding="utf-8"))["receipts"]
successful = [item for item in llm if item["kind"] == "llm_chat_completion"]
assert len(browser) == 24
assert {item["phase"] for item in browser} == {
"default_parallel", "default_serial", "cascade_stress",
}
for item in browser:
raw = item["rendered_body_text"].encode("utf-8")
assert len(raw) == item["rendered_body_bytes"]
assert sha256_bytes(raw) == item["rendered_body_sha256"]
assert len(successful) == 3
assert len({item["response_id"] for item in successful}) == 3
assert {item["context"]["phase"] for item in successful} == {
"default_parallel", "default_serial", "cascade_stress",
}
for item in successful:
assert item["response"]
assert item["usage"]["total_tokens"] > 0
assert sha256_bytes(canonical_bytes(item["request"])) == item["request_sha256"]
assert sha256_bytes(canonical_bytes(item["response"])) == item["response_sha256"]
def test_official_acceptance_and_latest_pointer_are_consistent_and_credential_free():
evidence = json.loads((RUN / "evidence.json").read_text(encoding="utf-8"))
latest = json.loads((ROOT / "validation" / "latest.json").read_text(encoding="utf-8"))
assert evidence["overall_status"] == "pass"
assert all(item["status"] == "pass" for item in evidence["gates"].values())
assert evidence["measured_speedup"] > 1
assert latest["run_id"] == evidence["run_id"]
assert latest["manifest_sha256"] == sha256_bytes((RUN / "manifest.json").read_bytes())
combined = b"\n".join(path.read_bytes() for path in RUN.iterdir() if path.is_file())
assert not re.search(rb'(?i)"(?:api[_-]?key|authorization)"\s*:\s*"(?!<redacted>|null|")[^"]+"', combined)
assert not re.search(rb'(?i)bearer\s+[a-z0-9._~+/=-]{16,}', combined)
@@ -0,0 +1,32 @@
import asyncio
import pytest
import sys
from pathlib import Path
# Ensure chapter10/parallel-web-research is in sys.path
sys.path.insert(0, str(Path(__file__).parent))
from agents import WorkerAgent
from message_bus import MessageBus, BROADCAST
from sources import Website
@pytest.mark.asyncio
async def test_worker_exits_early_when_terminate_arrives_before_task_assigned():
"""Contract proved: WorkerAgent handles terminate broadcast received before task_assigned without hanging or ignoring the signal.
Bug locked out: infinite loop awaiting task_assigned while ignoring terminate signal."""
bus = MessageBus(verbose=False)
site = Website("s1", "http://site1.edu", "College 1")
w = WorkerAgent("worker-1", site, bus, "target", None)
# Broadcast terminate to bus BEFORE worker-1 gets task_assigned
await bus.send("coordinator", BROADCAST, "terminate", {"reason": "target_found_by_other"})
# w.run() must exit promptly (not hang awaiting task_assigned), set terminate event, and send ACK
await asyncio.wait_for(w.run(), timeout=2.0)
assert w.terminate.is_set()
assert w._termination_reason == "target_found_by_other"
acks = [m for m in bus.history if m.type == "ack" and m.sender_id == "worker-1"]
assert len(acks) == 1
assert acks[0].payload.get("acked") == "terminate"
@@ -0,0 +1,7 @@
{
"schema_version": 1,
"run_id": "exp10-4-real-receipts-20260730-v2",
"run_directory": "validation/runs/exp10-4-real-receipts-20260730-v2",
"manifest_sha256": "da70764d5917f999a14d52f10cb147ce53c8c378853dd8dbc947b8d97bbfbc97",
"overall_status": "pass"
}
@@ -0,0 +1,28 @@
{
"schema_version": 1,
"experiment": "10-4",
"run_type": "real_browser_cascade_stress",
"measured_at": "2026-07-29T17:58:19+0800",
"target": "Andrew Ng",
"browser": "Playwright Chromium",
"llm_provider": "Volcengine ARK",
"source": "live Stanford HAI profile repeated with distinct query URLs to expose cancellation timing",
"workers": 4,
"winner": "agent-03",
"winner_url": "https://hai.stanford.edu/people/andrew-ng?worker=d",
"parallel_seconds": 16.313,
"terminate_broadcasts": 1,
"loser_acknowledgements": ["agent-00", "agent-01", "agent-02"],
"duplicate_hits": [],
"errors": {},
"contexts_created": 4,
"contexts_closed": 4,
"gates": {
"real_browser_sessions": {"status": "pass", "count": 4},
"single_locked_settlement": {"status": "pass"},
"single_terminate_broadcast": {"status": "pass", "count": 1},
"all_losing_workers_acknowledged": {"status": "pass", "count": 3},
"all_browser_resources_closed": {"status": "pass", "created": 4, "closed": 4}
},
"overall_status": "pass"
}
@@ -0,0 +1,55 @@
{
"schema_version": 1,
"experiment": "10-4",
"run_type": "real_parallel_and_serial_same_sites",
"measured_at": "2026-07-29T17:57:11+0800",
"target": "Andrew Ng",
"browser": "Playwright Chromium",
"llm_provider": "Volcengine ARK",
"llm_model": "doubao-seed-1-6-250615",
"sites": [
"https://med.stanford.edu/profiles/browse",
"https://law.stanford.edu/directory/?tax_and_terms=1067",
"https://ed.stanford.edu/faculty",
"https://www.gsb.stanford.edu/faculty-research/faculty",
"https://sustainability.stanford.edu/people/faculty",
"https://humsci.stanford.edu/about/leadership-and-administration/deans-office",
"https://engineering.stanford.edu/faculty-research/faculty",
"https://www.cs.stanford.edu/people/faculty",
"https://profiles.stanford.edu/andrew-ng",
"https://hai.stanford.edu/people/andrew-ng"
],
"parallel": {
"seconds": 18.542,
"winner": "agent-09",
"errors": {},
"contexts_created": 10,
"contexts_closed": 10,
"terminate_broadcasts": 1,
"result": {
"name": "Andrew Ng",
"college": "Stanford HAI",
"position": "Adjunct Professor at Stanford University",
"research": "machine learning with an emphasis on deep learning, applications to computer vision and speech, autonomous driving",
"evidence": "Founder of DeepLearning.AI and Adjunct Professor at Stanford University",
"url": "https://hai.stanford.edu/people/andrew-ng"
}
},
"serial": {
"seconds": 58.264,
"sites_visited": 10,
"contexts_created": 10,
"contexts_closed": 10,
"winner_site": "human-ai"
},
"measured_speedup": 3.142,
"gates": {
"real_websites": {"status": "pass", "count": 10},
"independent_browser_sessions": {"status": "pass", "count": 10},
"real_llm_grounded_extraction": {"status": "pass"},
"parallel_serial_same_site_comparison": {"status": "pass"},
"all_browser_resources_closed": {"status": "pass", "created": 20, "closed": 20},
"measured_parallel_improvement": {"status": "pass", "speedup": 3.142}
},
"overall_status": "pass"
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,461 @@
{
"schema_version": 2,
"experiment": "10-4",
"run_id": "exp10-4-real-receipts-20260730-v2",
"run_type": "real_parallel_serial_and_cascade_with_raw_receipts",
"started_at": "2026-07-30T05:08:35.893Z",
"completed_at": "2026-07-30T05:09:57.065Z",
"duration_seconds": 81.172,
"target": "Andrew Ng",
"git_commit": "f119cf510f3746cd5d071d0bce0051617f02f163",
"environment": {
"python": "3.11.4",
"platform": "macOS-26.3-arm64-arm-64bit",
"playwright": "1.54.0",
"parallel_chromium": "139.0.7258.5",
"serial_chromium": "139.0.7258.5",
"cascade_chromium": "139.0.7258.5"
},
"inputs": {
"default_sites": [
{
"name": "medicine-profiles",
"college": "School of Medicine",
"url": "https://med.stanford.edu/profiles/browse"
},
{
"name": "law-faculty",
"college": "Stanford Law School",
"url": "https://law.stanford.edu/directory/?tax_and_terms=1067"
},
{
"name": "education-faculty",
"college": "Graduate School of Education",
"url": "https://ed.stanford.edu/faculty"
},
{
"name": "business-faculty",
"college": "Graduate School of Business",
"url": "https://www.gsb.stanford.edu/faculty-research/faculty"
},
{
"name": "sustainability-faculty",
"college": "Doerr School of Sustainability",
"url": "https://sustainability.stanford.edu/people/faculty"
},
{
"name": "humanities-faculty",
"college": "School of Humanities and Sciences",
"url": "https://humsci.stanford.edu/about/leadership-and-administration/deans-office"
},
{
"name": "engineering-faculty",
"college": "School of Engineering",
"url": "https://engineering.stanford.edu/faculty-research/faculty"
},
{
"name": "computer-science",
"college": "School of Engineering / Computer Science",
"url": "https://www.cs.stanford.edu/people/faculty"
},
{
"name": "stanford-profiles",
"college": "Stanford Profiles",
"url": "https://profiles.stanford.edu/andrew-ng"
},
{
"name": "human-ai",
"college": "Stanford HAI",
"url": "https://hai.stanford.edu/people/andrew-ng"
}
],
"cascade_sites": [
{
"name": "hai-a",
"college": "Stanford HAI",
"url": "https://hai.stanford.edu/people/andrew-ng?worker=a"
},
{
"name": "hai-b",
"college": "Stanford HAI",
"url": "https://hai.stanford.edu/people/andrew-ng?worker=b"
},
{
"name": "hai-c",
"college": "Stanford HAI",
"url": "https://hai.stanford.edu/people/andrew-ng?worker=c"
},
{
"name": "hai-d",
"college": "Stanford HAI",
"url": "https://hai.stanford.edu/people/andrew-ng?worker=d"
}
],
"timeout_seconds": 120.0
},
"default_parallel": {
"result": {
"outcome": "found",
"winner": "agent-09",
"profile": {
"found": true,
"name": "Andrew Ng",
"college": "Stanford HAI",
"position": "Adjunct Professor at Stanford University",
"research": "machine learning with an emphasis on deep learning. He founded and led the “Google Brain” project which developed massive-scale deep learning algorithms... including such applications as autonomous driving",
"evidence": "PEOPLE\nFACULTY\nAndrew Ng\n\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University",
"provider": "ark",
"url": "https://hai.stanford.edu/people/andrew-ng"
},
"duplicate_hits": [],
"acks": [],
"expected_loser_acks": [
"agent-00",
"agent-01",
"agent-02",
"agent-03",
"agent-04",
"agent-05",
"agent-06",
"agent-07",
"agent-08"
],
"missing_loser_acks": [
"agent-00",
"agent-01",
"agent-02",
"agent-03",
"agent-04",
"agent-05",
"agent-06",
"agent-07",
"agent-08"
],
"errors": {},
"failure_summary": {
"count": 0,
"by_type": {}
},
"not_found_reasons": {
"agent-07": "target name absent from rendered page",
"agent-06": "target name absent from rendered page",
"agent-02": "target name absent from rendered page",
"agent-00": "target name absent from rendered page",
"agent-04": "target name absent from rendered page",
"agent-03": "target name absent from rendered page",
"agent-08": "target name absent from rendered page",
"agent-05": "target name absent from rendered page",
"agent-01": "target name absent from rendered page"
},
"status_table": {
"agent-00": {
"source": "medicine-profiles",
"state": "已完成",
"note": "页面中未找到目标"
},
"agent-01": {
"source": "law-faculty",
"state": "已完成",
"note": "页面中未找到目标"
},
"agent-02": {
"source": "education-faculty",
"state": "已完成",
"note": "页面中未找到目标"
},
"agent-03": {
"source": "business-faculty",
"state": "已完成",
"note": "页面中未找到目标"
},
"agent-04": {
"source": "sustainability-faculty",
"state": "已完成",
"note": "页面中未找到目标"
},
"agent-05": {
"source": "humanities-faculty",
"state": "已完成",
"note": "页面中未找到目标"
},
"agent-06": {
"source": "engineering-faculty",
"state": "已完成",
"note": "页面中未找到目标"
},
"agent-07": {
"source": "computer-science",
"state": "已完成",
"note": "页面中未找到目标"
},
"agent-08": {
"source": "stanford-profiles",
"state": "已完成",
"note": "页面中未找到目标"
},
"agent-09": {
"source": "human-ai",
"state": "已完成",
"note": "找到目标教师"
}
},
"terminate_broadcasts": 1,
"parallel_seconds": 24.022,
"contexts_closed": 10,
"resource_failures": {}
},
"contexts_created": 10,
"contexts_closed": 10,
"chromium_version": "139.0.7258.5"
},
"default_serial": {
"result": {
"seconds": 44.971,
"visited": 10,
"results": [
{
"site": "medicine-profiles",
"profile": {
"found": false,
"reason": "target name absent from rendered page"
},
"seconds": 3.14453391591087
},
{
"site": "law-faculty",
"profile": {
"found": false,
"reason": "target name absent from rendered page"
},
"seconds": 3.242926917038858
},
{
"site": "education-faculty",
"profile": {
"found": false,
"reason": "target name absent from rendered page"
},
"seconds": 2.353850208222866
},
{
"site": "business-faculty",
"profile": {
"found": false,
"reason": "target name absent from rendered page"
},
"seconds": 2.5877689169719815
},
{
"site": "sustainability-faculty",
"profile": {
"found": false,
"reason": "target name absent from rendered page"
},
"seconds": 2.4759700000286102
},
{
"site": "humanities-faculty",
"profile": {
"found": false,
"reason": "target name absent from rendered page"
},
"seconds": 2.9777685422450304
},
{
"site": "engineering-faculty",
"profile": {
"found": false,
"reason": "target name absent from rendered page"
},
"seconds": 2.225629042368382
},
{
"site": "computer-science",
"profile": {
"found": false,
"reason": "target name absent from rendered page"
},
"seconds": 2.5532497079111636
},
{
"site": "stanford-profiles",
"profile": {
"found": false,
"reason": "target name absent from rendered page"
},
"seconds": 2.9755149162374437
},
{
"site": "human-ai",
"profile": {
"found": true,
"name": "Andrew Ng",
"college": "Stanford HAI",
"position": "Adjunct Professor at Stanford University",
"research": "machine learning with an emphasis on deep learning, including applications to computer vision, speech, and autonomous driving",
"evidence": "Andrew Ng\n\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University",
"provider": "ark",
"url": "https://hai.stanford.edu/people/andrew-ng"
},
"seconds": 20.37298774998635
}
]
},
"contexts_created": 10,
"contexts_closed": 10,
"chromium_version": "139.0.7258.5"
},
"measured_speedup": 1.872,
"cascade_stress": {
"result": {
"outcome": "found",
"winner": "agent-02",
"profile": {
"found": true,
"name": "Andrew Ng",
"college": "Stanford HAI",
"position": "Adjunct Professor at Stanford University",
"research": "machine learning with an emphasis on deep learning, applications to computer vision and speech, including autonomous driving",
"evidence": "Andrew Ng\n\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University",
"provider": "ark",
"url": "https://hai.stanford.edu/people/andrew-ng?worker=c"
},
"duplicate_hits": [],
"acks": [
"agent-00",
"agent-01",
"agent-03"
],
"expected_loser_acks": [
"agent-00",
"agent-01",
"agent-03"
],
"missing_loser_acks": [],
"errors": {},
"failure_summary": {
"count": 0,
"by_type": {}
},
"not_found_reasons": {},
"status_table": {
"agent-00": {
"source": "hai-a",
"state": "已终止",
"note": "安全点响应终止:target_found_by_agent-02"
},
"agent-01": {
"source": "hai-b",
"state": "已终止",
"note": "安全点响应终止:target_found_by_agent-02"
},
"agent-02": {
"source": "hai-c",
"state": "已完成",
"note": "找到目标教师"
},
"agent-03": {
"source": "hai-d",
"state": "已终止",
"note": "安全点响应终止:target_found_by_agent-02"
}
},
"terminate_broadcasts": 1,
"parallel_seconds": 10.878,
"contexts_closed": 4,
"resource_failures": {}
},
"contexts_created": 4,
"contexts_closed": 4,
"chromium_version": "139.0.7258.5"
},
"receipt_counts": {
"browser": 24,
"llm_all_attempts": 3,
"llm_successful": 3,
"message_bus": 114
},
"gates": {
"ten_real_default_sites": {
"status": "pass",
"count": 10
},
"same_sites_parallel_and_serial": {
"status": "pass",
"configured_count": 10,
"serial_visited": 10
},
"default_target_found_both_modes": {
"status": "pass",
"parallel_winner": "agent-09"
},
"default_resources_closed": {
"status": "pass",
"parallel_created": 10,
"parallel_closed": 10,
"serial_created": 10,
"serial_closed": 10
},
"measured_parallel_speedup": {
"status": "pass",
"speedup": 1.872
},
"raw_browser_receipts": {
"status": "pass",
"count": 24,
"phases": [
"cascade_stress",
"default_parallel",
"default_serial"
]
},
"raw_llm_provider_receipts": {
"status": "pass",
"successful_count": 3,
"response_ids": [
"021785388119659a5cf16cfa3c4ebff37e7c43914719d1a235950",
"021785388168110fc13f77dd38da39bcdb33ad9bd57064028fde0",
"0217853881892313749365b55318faa75db47eb4778acb39034a4"
],
"phases": [
"cascade_stress",
"default_parallel",
"default_serial"
]
},
"single_cascade_settlement": {
"status": "pass",
"winner": "agent-02",
"terminate_broadcasts": 1,
"duplicate_hits": []
},
"cascade_loser_acknowledgements": {
"status": "pass",
"expected": [
"agent-00",
"agent-01",
"agent-03"
],
"actual": [
"agent-00",
"agent-01",
"agent-03"
]
},
"cascade_resources_closed": {
"status": "pass",
"created": 4,
"closed": 4
},
"runtime_source_hashes": {
"status": "pass",
"count": 7
},
"credential_free_artifacts": {
"status": "pass",
"actual_secret_hits": 0,
"credential_pattern_hits": 0
}
},
"overall_status": "pass"
}
@@ -0,0 +1,251 @@
{
"schema_version": 1,
"receipts": [
{
"kind": "llm_chat_completion",
"context": {
"phase": "default_parallel",
"worker_id": "agent-09",
"site": "human-ai"
},
"provider": "ark",
"request": {
"model": "doubao-seed-1-6-250615",
"messages": [
{
"role": "user",
"content": "{\"target\": \"Andrew Ng\", \"site_college\": \"Stanford HAI\", \"url\": \"https://hai.stanford.edu/people/andrew-ng\", \"rendered_page_text\": \"Skip to content\\nAbout\\nResearch\\nEducation\\nPolicy\\nAI Index\\nNews\\nEvents\\nIndustry\\nCenters & Labs\\nPEOPLE\\nFACULTY\\nAndrew Ng\\n\\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University\\n\\nAndrew Ng is Founder & CEO of Landing AI, Founder of deeplearning.ai, Co-Chairman and Co-Founder of Coursera, and is currently an Adjunct Professor at Stanford University. He was also Chief Scientist at Baidu Inc., and Founder & Lead for the Google Brain Project.\\n\\nIn 2011 he led the development of Stanford Universitys main MOOC (Massive Open Online Courses) platform and also taught an online Machine Learning class to over 100,000 students, leading to the founding of Coursera. Ngs goal is to give everyone in the world access to a great education, for free. Today, Coursera partners with some of the top universities in the world to offer high quality online courses, and is the largest MOOC platform in the world.\\n\\nNg also works on machine learning with an emphasis on deep learning. He founded and led the “Google Brain” project which developed massive-scale deep learning algorithms. This resulted in the famous “Google cat” result, in which a massive neural network with 1 billion parameters learned from unlabeled YouTube videos to detect cats. More recently, he continues to work on deep learning and its applications to computer vision and speech, including such applications as autonomous driving.\\n\\nSHARE\\nNAVIGATE\\nAbout\\nEvents\\nAI Glossary\\nCareers\\nSearch\\nPARTICIPATE\\nGet Involved\\nSupport HAI\\nContact Us\\nStay Up To Date\\n\\nGet the latest news, advances in research, policy work, and education program updates from HAI in your inbox weekly.\\n\\nSign Up For Latest News\\n\\nStanford\\nUniversity\\nStanford HomeMaps & DirectionsSearch StanfordEmergency Info\\nTerms of UsePrivacyCopyrightTrademarksNon-DiscriminationAccessibility\\n© Stanford University.  Stanford, California 94305.\\nPause Media\", \"instruction\": \"Use only rendered_page_text. Decide whether it contains this exact person's faculty profile. Return JSON keys found, name, college, position, research, evidence. If the name is only a link/listing, found may be true but leave unsupported fields empty. evidence must be a short verbatim excerpt.\"}"
}
],
"response_format": {
"type": "json_object"
}
},
"response": {
"id": "021785388119659a5cf16cfa3c4ebff37e7c43914719d1a235950",
"choices": [
{
"finish_reason": "stop",
"index": 0,
"logprobs": null,
"message": {
"content": "{\"found\": true, \"name\": \"Andrew Ng\", \"college\": \"Stanford HAI\", \"position\": \"Adjunct Professor at Stanford University\", \"research\": \"machine learning with an emphasis on deep learning. He founded and led the “Google Brain” project which developed massive-scale deep learning algorithms... including such applications as autonomous driving\", \"evidence\": \"PEOPLE\\nFACULTY\\nAndrew Ng\\n\\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University\"}",
"refusal": null,
"role": "assistant",
"annotations": null,
"audio": null,
"function_call": null,
"tool_calls": null,
"reasoning_content": "\nI now need to solve this problem. Let's start by carefully reading the user's instruction. The task is to use only the provided rendered_page_text to determine if it contains the exact person's faculty profile. Then, return a JSON with keys: found, name, college, position, research, evidence. \n\nFirst, the target person is Andrew Ng, and the site_college is Stanford HAI. Let's look at the rendered_page_text. \n\nLooking through the text, there's a section: \"PEOPLE\\nFACULTY\\nAndrew Ng\\n\\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University\\n\\nAndrew Ng is Founder & CEO of Landing AI, Founder of deeplearning.ai, Co-Chairman and Co-Founder of Coursera, and is currently an Adjunct Professor at Stanford University...\" \n\nSo, the name is clearly Andrew Ng. Now, check if it's a faculty profile. The text under \"PEOPLE\" and \"FACULTY\" lists Andrew Ng, so that's a faculty profile. \n\nFound: True, because it's a faculty profile for Andrew Ng.\n\nName: Andrew Ng (explicitly stated).\n\nCollege: The site_college is Stanford HAI, and the page is from Stanford HAI (url is https://hai.stanford.edu/people/andrew-ng), so the college should be Stanford HAI.\n\nPosition: The text says \"Adjunct Professor at Stanford University\".\n\nResearch: The text mentions \"Ng also works on machine learning with an emphasis on deep learning. He founded and led the “Google Brain” project which developed massive-scale deep learning algorithms... including such applications as autonomous driving.\" So research is machine learning with an emphasis on deep learning, including applications like autonomous driving.\n\nEvidence: Need a short verbatim excerpt. The line \"Andrew Ng\\n\\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University\" is a good excerpt as it directly links the name and position.\n\nNow, check if all fields are supported. The name is present, college is Stanford HAI (since the page is from Stanford HAI), position is Adjunct Professor at Stanford University, research is mentioned, evidence is available.\n\nSo putting it all together:\n\nfound: true\n\nname: \"Andrew Ng\"\n\ncollege: \"Stanford HAI\"\n\nposition: \"Adjunct Professor at Stanford University\"\n\nresearch: \"machine learning with an emphasis on deep learning, including applications such as autonomous driving\"\n\nevidence: \"Andrew Ng\\n\\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University\"\n\nWait, but need to make sure research is directly from the text. The exact research part is \"machine learning with an emphasis on deep learning. He founded and led the “Google Brain” project which developed massive-scale deep learning algorithms... including such applications as autonomous driving.\" So maybe more concise: \"machine learning with an emphasis on deep learning, including applications such as autonomous driving\" (verbatim parts: \"machine learning with an emphasis on deep learning\" and \"including such applications as autonomous driving\").\n\nEvidence should be verbatim. Let's pick the excerpt where it starts with \"Andrew Ng\" under FACULTY. The text has: \"PEOPLE\\nFACULTY\\nAndrew Ng\\n\\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University\" that's a verbatim excerpt.\n\nNow, check if any fields are unsupported. All fields have support from the text. So the JSON should have all these filled."
}
}
],
"created": 1785388140,
"model": "doubao-seed-1-6-250615",
"object": "chat.completion",
"service_tier": "default",
"system_fingerprint": null,
"usage": {
"completion_tokens": 839,
"prompt_tokens": 643,
"total_tokens": 1482,
"completion_tokens_details": {
"accepted_prediction_tokens": null,
"audio_tokens": null,
"reasoning_tokens": 739,
"rejected_prediction_tokens": null
},
"prompt_tokens_details": {
"audio_tokens": null,
"cached_tokens": 0
}
}
},
"response_id": "021785388119659a5cf16cfa3c4ebff37e7c43914719d1a235950",
"response_model": "doubao-seed-1-6-250615",
"usage": {
"completion_tokens": 839,
"prompt_tokens": 643,
"total_tokens": 1482,
"completion_tokens_details": {
"accepted_prediction_tokens": null,
"audio_tokens": null,
"reasoning_tokens": 739,
"rejected_prediction_tokens": null
},
"prompt_tokens_details": {
"audio_tokens": null,
"cached_tokens": 0
}
},
"duration_seconds": 21.299,
"request_sha256": "685ffe4d54aa05d0ba113de8d9768265eb9168d74198810e28121f9e9cd5f749",
"response_sha256": "399e874edaed8591ce68c7661f881afac2e097ad3d56e050cee3035c94c4df5f",
"captured_at": "2026-07-30T05:09:00.593Z"
},
{
"kind": "llm_chat_completion",
"context": {
"phase": "default_serial",
"worker_id": "serial-09",
"site": "human-ai"
},
"provider": "ark",
"request": {
"model": "doubao-seed-1-6-250615",
"messages": [
{
"role": "user",
"content": "{\"target\": \"Andrew Ng\", \"site_college\": \"Stanford HAI\", \"url\": \"https://hai.stanford.edu/people/andrew-ng\", \"rendered_page_text\": \"Skip to content\\nAbout\\nResearch\\nEducation\\nPolicy\\nAI Index\\nNews\\nEvents\\nIndustry\\nCenters & Labs\\nPEOPLE\\nFACULTY\\nAndrew Ng\\n\\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University\\n\\nAndrew Ng is Founder & CEO of Landing AI, Founder of deeplearning.ai, Co-Chairman and Co-Founder of Coursera, and is currently an Adjunct Professor at Stanford University. He was also Chief Scientist at Baidu Inc., and Founder & Lead for the Google Brain Project.\\n\\nIn 2011 he led the development of Stanford Universitys main MOOC (Massive Open Online Courses) platform and also taught an online Machine Learning class to over 100,000 students, leading to the founding of Coursera. Ngs goal is to give everyone in the world access to a great education, for free. Today, Coursera partners with some of the top universities in the world to offer high quality online courses, and is the largest MOOC platform in the world.\\n\\nNg also works on machine learning with an emphasis on deep learning. He founded and led the “Google Brain” project which developed massive-scale deep learning algorithms. This resulted in the famous “Google cat” result, in which a massive neural network with 1 billion parameters learned from unlabeled YouTube videos to detect cats. More recently, he continues to work on deep learning and its applications to computer vision and speech, including such applications as autonomous driving.\\n\\nSHARE\\nNAVIGATE\\nAbout\\nEvents\\nAI Glossary\\nCareers\\nSearch\\nPARTICIPATE\\nGet Involved\\nSupport HAI\\nContact Us\\nStay Up To Date\\n\\nGet the latest news, advances in research, policy work, and education program updates from HAI in your inbox weekly.\\n\\nSign Up For Latest News\\n\\nStanford\\nUniversity\\nStanford HomeMaps & DirectionsSearch StanfordEmergency Info\\nTerms of UsePrivacyCopyrightTrademarksNon-DiscriminationAccessibility\\n© Stanford University.  Stanford, California 94305.\\nPause Media\", \"instruction\": \"Use only rendered_page_text. Decide whether it contains this exact person's faculty profile. Return JSON keys found, name, college, position, research, evidence. If the name is only a link/listing, found may be true but leave unsupported fields empty. evidence must be a short verbatim excerpt.\"}"
}
],
"response_format": {
"type": "json_object"
}
},
"response": {
"id": "021785388168110fc13f77dd38da39bcdb33ad9bd57064028fde0",
"choices": [
{
"finish_reason": "stop",
"index": 0,
"logprobs": null,
"message": {
"content": "{\"found\": true, \"name\": \"Andrew Ng\", \"college\": \"Stanford HAI\", \"position\": \"Adjunct Professor at Stanford University\", \"research\": \"machine learning with an emphasis on deep learning, including applications to computer vision, speech, and autonomous driving\", \"evidence\": \"Andrew Ng\\n\\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University\"}",
"refusal": null,
"role": "assistant",
"annotations": null,
"audio": null,
"function_call": null,
"tool_calls": null,
"reasoning_content": "\nI now need to solve this problem. The user provided a rendered_page_text, and the task is to determine if it contains the exact faculty profile of Andrew Ng. Then, return JSON with keys found, name, college, position, research, evidence. Let's start step by step.\n\nFirst, check if the rendered_page_text has Andrew Ng's faculty profile. The target is Andrew Ng, site_college is Stanford HAI. Looking at the rendered_page_text, there's a section under \"PEOPLE\" and \"FACULTY\" with \"Andrew Ng\" as a heading. Then it says \"Founder of DeepLearning.AI and Adjunct Professor at Stanford University\". So that's a faculty profile.\n\nNow, the keys: found should be true. Name is Andrew Ng. College: the site_college is Stanford HAI, and the page is from Stanford HAI, so college is Stanford HAI. Position: \"Adjunct Professor at Stanford University\" is mentioned, so position is \"Adjunct Professor at Stanford University\". Research: The text says \"Ng also works on machine learning with an emphasis on deep learning. He founded and led the 'Google Brain' project which developed massive-scale deep learning algorithms... continues to work on deep learning and its applications to computer vision and speech, including such applications as autonomous driving.\" So research is \"machine learning with an emphasis on deep learning, including applications to computer vision, speech, and autonomous driving\". Evidence needs to be a short verbatim excerpt. The relevant excerpt could be \"Andrew Ng\\n\\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University\" as that's the start of his profile.\n\nLet me verify each key:\n\nfound: true, because there's a faculty profile for Andrew Ng.\n\nname: Andrew Ng (exact name mentioned).\n\ncollege: Stanford HAI (since the site is Stanford HAI, and the profile is on Stanford HAI's page).\n\nposition: \"Adjunct Professor at Stanford University\" (verbatim from the text: \"Adjunct Professor at Stanford University\").\n\nresearch: The research section mentions machine learning with emphasis on deep learning, Google Brain project, applications to computer vision, speech, autonomous driving. So research is \"machine learning with an emphasis on deep learning, including applications to computer vision, speech, and autonomous driving\".\n\nevidence: verbatim excerpt. The text starts with \"Andrew Ng\\n\\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University\", which is a verbatim part.\n\nUnsupported fields: none, since all required fields are supported.\n\nSo putting it all together, the JSON should have these values."
}
}
],
"created": 1785388186,
"model": "doubao-seed-1-6-250615",
"object": "chat.completion",
"service_tier": "default",
"system_fingerprint": null,
"usage": {
"completion_tokens": 627,
"prompt_tokens": 643,
"total_tokens": 1270,
"completion_tokens_details": {
"accepted_prediction_tokens": null,
"audio_tokens": null,
"reasoning_tokens": 547,
"rejected_prediction_tokens": null
},
"prompt_tokens_details": {
"audio_tokens": null,
"cached_tokens": 0
}
}
},
"response_id": "021785388168110fc13f77dd38da39bcdb33ad9bd57064028fde0",
"response_model": "doubao-seed-1-6-250615",
"usage": {
"completion_tokens": 627,
"prompt_tokens": 643,
"total_tokens": 1270,
"completion_tokens_details": {
"accepted_prediction_tokens": null,
"audio_tokens": null,
"reasoning_tokens": 547,
"rejected_prediction_tokens": null
},
"prompt_tokens_details": {
"audio_tokens": null,
"cached_tokens": 0
}
},
"duration_seconds": 18.008,
"request_sha256": "685ffe4d54aa05d0ba113de8d9768265eb9168d74198810e28121f9e9cd5f749",
"response_sha256": "3936add6e391127252cb2f79371ab5ffd114d16fa21e97d8ca4127eb5846de03",
"captured_at": "2026-07-30T05:09:45.877Z"
},
{
"kind": "llm_chat_completion",
"context": {
"phase": "cascade_stress",
"worker_id": "agent-02",
"site": "hai-c"
},
"provider": "ark",
"request": {
"model": "doubao-seed-1-6-250615",
"messages": [
{
"role": "user",
"content": "{\"target\": \"Andrew Ng\", \"site_college\": \"Stanford HAI\", \"url\": \"https://hai.stanford.edu/people/andrew-ng?worker=c\", \"rendered_page_text\": \"Skip to content\\nAbout\\nResearch\\nEducation\\nPolicy\\nAI Index\\nNews\\nEvents\\nIndustry\\nCenters & Labs\\nPEOPLE\\nFACULTY\\nAndrew Ng\\n\\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University\\n\\nAndrew Ng is Founder & CEO of Landing AI, Founder of deeplearning.ai, Co-Chairman and Co-Founder of Coursera, and is currently an Adjunct Professor at Stanford University. He was also Chief Scientist at Baidu Inc., and Founder & Lead for the Google Brain Project.\\n\\nIn 2011 he led the development of Stanford Universitys main MOOC (Massive Open Online Courses) platform and also taught an online Machine Learning class to over 100,000 students, leading to the founding of Coursera. Ngs goal is to give everyone in the world access to a great education, for free. Today, Coursera partners with some of the top universities in the world to offer high quality online courses, and is the largest MOOC platform in the world.\\n\\nNg also works on machine learning with an emphasis on deep learning. He founded and led the “Google Brain” project which developed massive-scale deep learning algorithms. This resulted in the famous “Google cat” result, in which a massive neural network with 1 billion parameters learned from unlabeled YouTube videos to detect cats. More recently, he continues to work on deep learning and its applications to computer vision and speech, including such applications as autonomous driving.\\n\\nSHARE\\nNAVIGATE\\nAbout\\nEvents\\nAI Glossary\\nCareers\\nSearch\\nPARTICIPATE\\nGet Involved\\nSupport HAI\\nContact Us\\nStay Up To Date\\n\\nGet the latest news, advances in research, policy work, and education program updates from HAI in your inbox weekly.\\n\\nSign Up For Latest News\\n\\nStanford\\nUniversity\\nStanford HomeMaps & DirectionsSearch StanfordEmergency Info\\nTerms of UsePrivacyCopyrightTrademarksNon-DiscriminationAccessibility\\n© Stanford University.  Stanford, California 94305.\\nPause Media\", \"instruction\": \"Use only rendered_page_text. Decide whether it contains this exact person's faculty profile. Return JSON keys found, name, college, position, research, evidence. If the name is only a link/listing, found may be true but leave unsupported fields empty. evidence must be a short verbatim excerpt.\"}"
}
],
"response_format": {
"type": "json_object"
}
},
"response": {
"id": "0217853881892313749365b55318faa75db47eb4778acb39034a4",
"choices": [
{
"finish_reason": "stop",
"index": 0,
"logprobs": null,
"message": {
"content": "{\"found\": true, \"name\": \"Andrew Ng\", \"college\": \"Stanford HAI\", \"position\": \"Adjunct Professor at Stanford University\", \"research\": \"machine learning with an emphasis on deep learning, applications to computer vision and speech, including autonomous driving\", \"evidence\": \"Andrew Ng\\n\\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University\"}",
"refusal": null,
"role": "assistant",
"annotations": null,
"audio": null,
"function_call": null,
"tool_calls": null,
"reasoning_content": "\nI need to process the given query. Let's start by understanding the requirements. The user wants to use only the \"rendered_page_text\" to determine if it contains Andrew Ng's faculty profile. Then return JSON with keys: found, name, college, position, research, evidence.\n\nFirst, check if the name is present. The rendered_page_text has \"Andrew Ng\" under \"FACULTY\". So \"found\" should be true.\n\n\"name\" is clearly \"Andrew Ng\".\n\n\"college\": The \"site_college\" is \"Stanford HAI\", and in the text, it's part of Stanford University, so college would be \"Stanford HAI\".\n\n\"position\": The text states \"Adjunct Professor at Stanford University\".\n\n\"research\": It mentions \"machine learning with an emphasis on deep learning\" and applications to \"computer vision and speech, including such applications as autonomous driving\".\n\n\"evidence\" needs to be a short verbatim excerpt. A good excerpt is \"Andrew Ng\\n\\nFounder of DeepLearning.AI and Adjunct Professor at Stanford University\" as it directly identifies him and his position.\n\nNow, structure all these into the required JSON."
}
}
],
"created": 1785388197,
"model": "doubao-seed-1-6-250615",
"object": "chat.completion",
"service_tier": "default",
"system_fingerprint": null,
"usage": {
"completion_tokens": 325,
"prompt_tokens": 646,
"total_tokens": 971,
"completion_tokens_details": {
"accepted_prediction_tokens": null,
"audio_tokens": null,
"reasoning_tokens": 246,
"rejected_prediction_tokens": null
},
"prompt_tokens_details": {
"audio_tokens": null,
"cached_tokens": 0
}
}
},
"response_id": "0217853881892313749365b55318faa75db47eb4778acb39034a4",
"response_model": "doubao-seed-1-6-250615",
"usage": {
"completion_tokens": 325,
"prompt_tokens": 646,
"total_tokens": 971,
"completion_tokens_details": {
"accepted_prediction_tokens": null,
"audio_tokens": null,
"reasoning_tokens": 246,
"rejected_prediction_tokens": null
},
"prompt_tokens_details": {
"audio_tokens": null,
"cached_tokens": 0
}
},
"duration_seconds": 8.026,
"request_sha256": "5b59d5243bb3dd829d888e5aa2f9baa0906662ad520ac7b73c4e3aa63ea42629",
"response_sha256": "2b82f2e8eb37313b43e5d655d72bb7a4d37fbc9082a07b22995d495a1e4e9454",
"captured_at": "2026-07-30T05:09:57.026Z"
}
]
}
@@ -0,0 +1,31 @@
{
"schema_version": 1,
"experiment": "10-4",
"run_id": "exp10-4-real-receipts-20260730-v2",
"generated_at": "2026-07-30T05:09:57.077Z",
"git_commit": "f119cf510f3746cd5d071d0bce0051617f02f163",
"runtime_source_sha256": {
"run_official_experiment.py": "e09e215e433fbc96073bc57bb9ecfda611a967a0854ec89a0846755f8d4d276e",
"demo.py": "0e974987333715f931d6a9f9fa914a9efa7ead3608c21511c407480dc2bd5e86",
"agents.py": "1dc78ff96f765de2fee8ee96ac72ae64b706c924d2ae8d9caa084bd4a874eb73",
"llm.py": "b7d820398a3e5be6f3cf6fde833f20036ec8039cc9c62aa7560f091dc0ae60a0",
"message_bus.py": "06c25d988884830422a1f902d4a6b32c96566af6a180b6f710cd8fa48f6caa42",
"sources.py": "05c1115ea2538c32d906c9bb965fe23209078847053e018fe3190a8ff693e27f",
"cascade-stress.example.json": "94126527d495304a5f397f9d84de25e4c12f4a916067e38bed8bc8fab82fb146"
},
"input_sha256": {
"default_sites_canonical_json": "6b6c1ecf715b826c18bf553140c0d769e9c65c1c64b51310f6f54f6b6b035223",
"cascade_sites_canonical_json": "2fcb099b49d4402967f067a1cb98af51f955c0a809f5fdc4767e2460cccf2dc3"
},
"artifact_sha256": {
"evidence.json": "06dd7becf5602b7a3f98f012b5abf1a2000e0faa7198d7594f59a6cd36887b37",
"browser_receipts.json": "7706a362b3f1a09af9351ee1b97b1987f27ef8f442b81eb6d31df44004aaa882",
"llm_receipts.json": "6d42e1988095c14f496f3f25b6e47b450ace01d25bcbf8de88fa9c74a2ebdef7",
"message_bus_receipts.json": "cddd2ec2da6400c3b2efddb5e2f780bec70dd030d7a9d4d798a9252174cb48c1"
},
"acceptance": {
"overall_status": "pass",
"passed_gates": 12,
"total_gates": 12
}
}