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,335 @@
import asyncio
from unittest.mock import AsyncMock, patch
import pytest
import demo
from browser import RecoverableFillError
from bus import MessageBus
from models import DecisionRecord, FieldSpec
from orchestration import (
ComputerAgent,
initiate_phone_call_agent,
run_parallel,
timing_evidence,
)
from voice import ScriptedPhoneChannel
class FakeBrowser:
def __init__(self):
self.values = {}
self.submit_enabled = False
async def fill(self, field, value):
await asyncio.sleep(0.05)
self.values[field.name] = value
async def submit(self):
return False
class SubmittingFakeBrowser(FakeBrowser):
def __init__(self):
super().__init__()
self.submit_enabled = True
self.submit_calls = 0
async def submit(self):
self.submit_calls += 1
return True
class FailingFillBrowser(FakeBrowser):
def __init__(self):
super().__init__()
self.submit_calls = 0
async def fill(self, field, value):
raise RecoverableFillError(f"cannot fill {field.name}")
async def submit(self):
self.submit_calls += 1
return True
@pytest.mark.asyncio
async def test_ask_one_fill_one_runs_concurrently_and_reasks_invalid_format():
fields = [
FieldSpec("email", "邮箱", "email", format_hint="name@example.com"),
FieldSpec("birth", "出生日期", "date", format_hint="YYYY-MM-DD"),
]
decision = DecisionRecord(
page_url="https://example.test/register",
page_title="Register",
known_fields=[],
discovered_fields=fields,
tool_called="initiate_phone_call_agent",
purpose="协助填写注册表单",
required_info=fields,
rationale_summary="tool call",
model="test",
monotonic_seconds=0,
)
# First email answer is invalid, forcing format feedback and a real re-ask.
channel = ScriptedPhoneChannel(["bad", "me@example.com", "2020-01-02"])
bus = MessageBus()
browser = FakeBrowser()
extracted = AsyncMock(side_effect=["bad", "me@example.com", "2020-01-02"])
agents = initiate_phone_call_agent(
decision=decision,
bus=bus,
channel=channel,
browser=browser,
known_values={},
)
with patch("orchestration._extract_value", extracted):
result = await run_parallel(agents, bus)
assert result["errors"] == []
assert browser.values == {"email": "me@example.com", "birth": "2020-01-02"}
assert any(m.type == "format_invalid" for m in bus.history)
evidence = timing_evidence(bus)
assert any(c["next_question_before_fill_completed"] for c in evidence["overlap_checks"])
def test_field_validation_handles_email_phone_date_and_page_pattern():
assert not FieldSpec("e", "email", "email").validate("bad")[0]
assert FieldSpec("e", "email", "email").validate("a@b.com")[0]
assert not FieldSpec("d", "date", "date").validate("01/02/2020")[0]
assert FieldSpec("p", "code", pattern=r"[A-Z]{2}\d{4}").validate("AB1234")[0]
@pytest.mark.asyncio
async def test_live_transport_without_consent_refuses_before_browser_or_audio_creation():
args = demo.parser().parse_args(["--headless", "--phone-transport", "local"])
with patch("demo.RegistrationBrowser") as browser_type, patch(
"demo.LiveMicrophoneChannel"
) as audio_type:
with pytest.raises(SystemExit, match="confirm-consent"):
await demo.main(args)
browser_type.assert_not_called()
audio_type.assert_not_called()
@pytest.mark.asyncio
async def test_unexpected_phone_failure_cancels_peer_and_closes_channel():
class ExplodingChannel:
def __init__(self):
self.closed = False
async def say(self, _text):
raise RuntimeError("synthetic transport failure")
async def listen(self, *, timeout=30): # pragma: no cover - say fails first
raise AssertionError(timeout)
async def close(self):
self.closed = True
fields = [FieldSpec("email", "邮箱", "email")]
decision = DecisionRecord(
page_url="https://example.test/register",
page_title="Register",
known_fields=[],
discovered_fields=fields,
tool_called="initiate_phone_call_agent",
purpose="协助填写注册表单",
required_info=fields,
rationale_summary="tool call",
model="test",
monotonic_seconds=0,
)
bus = MessageBus()
channel = ExplodingChannel()
agents = initiate_phone_call_agent(
decision=decision,
bus=bus,
channel=channel,
browser=FakeBrowser(),
known_values={},
)
with pytest.raises(RuntimeError, match="synthetic transport failure"):
await asyncio.wait_for(run_parallel(agents, bus), timeout=0.5)
assert channel.closed is True
assert not any(
task.get_name() in {"phone-agent-react-loop", "computer-agent-react-loop"}
and not task.done()
for task in asyncio.all_tasks()
)
@pytest.mark.asyncio
async def test_task_completed_triggers_submission_when_browser_is_opted_in():
fields = [FieldSpec("email", "邮箱", "email")]
decision = DecisionRecord(
page_url="https://example.test/register",
page_title="Register",
known_fields=[],
discovered_fields=fields,
tool_called="initiate_phone_call_agent",
purpose="协助填写注册表单",
required_info=fields,
rationale_summary="tool call",
model="test",
monotonic_seconds=0,
)
browser = SubmittingFakeBrowser()
bus = MessageBus()
agents = initiate_phone_call_agent(
decision=decision,
bus=bus,
channel=ScriptedPhoneChannel(["me@example.com"]),
browser=browser,
known_values={},
)
with patch("orchestration._extract_value", AsyncMock(return_value="me@example.com")):
result = await run_parallel(agents, bus)
assert result["submitted"] is True
assert browser.submit_calls == 1
@pytest.mark.asyncio
async def test_computer_fill_error_flows_back_to_phone_and_blocks_submission():
fields = [FieldSpec("email", "邮箱", "email")]
decision = DecisionRecord(
page_url="https://example.test/register",
page_title="Register",
known_fields=[],
discovered_fields=fields,
tool_called="initiate_phone_call_agent",
purpose="协助填写注册表单",
required_info=fields,
rationale_summary="tool call",
model="test",
monotonic_seconds=0,
)
browser = FailingFillBrowser()
channel = ScriptedPhoneChannel(["me@example.com"])
bus = MessageBus()
agents = initiate_phone_call_agent(
decision=decision,
bus=bus,
channel=channel,
browser=browser,
known_values={},
)
with patch("orchestration._extract_value", AsyncMock(return_value="me@example.com")):
result = await run_parallel(agents, bus)
assert result["submitted"] is False
assert browser.submit_calls == 0
assert agents.phone.browser_feedback == [
{"field": "email", "error": "cannot fill email"}
]
fill_error = next(message for message in bus.history if message.type == "fill_error")
assert getattr(fill_error, "_sensitive_keys") == ("error",)
assert "填写遇到问题" in channel.prompts[-1]
@pytest.mark.asyncio
async def test_malformed_info_collected_fails_before_fill_error_handling():
bus = MessageBus()
computer = ComputerAgent(bus, FakeBrowser(), [], {})
task = asyncio.create_task(computer.run())
await bus.send(
"phone_agent",
"computer_agent",
"info_collected",
sensitive_keys=("value",),
value="secret",
)
with pytest.raises(ValueError, match="non-empty field"):
await task
assert not any(message.type == "fill_error" for message in bus.history)
@pytest.mark.asyncio
async def test_optional_blank_is_audited_skip_and_never_written_to_browser():
fields = [
FieldSpec("email", "邮箱", "email"),
FieldSpec("birthday", "生日", "text", required=False),
FieldSpec("address", "地址", "textarea", required=False),
]
decision = DecisionRecord(
page_url="https://example.test/register",
page_title="Register",
known_fields=[],
discovered_fields=fields,
tool_called="initiate_phone_call_agent",
purpose="协助填写注册表单",
required_info=fields,
rationale_summary="tool call",
model="test",
monotonic_seconds=0,
)
browser = FakeBrowser()
bus = MessageBus()
agents = initiate_phone_call_agent(
decision=decision,
bus=bus,
channel=ScriptedPhoneChannel(["me@example.com", "", ""]),
browser=browser,
known_values={},
)
with patch("orchestration._extract_value", AsyncMock(return_value="me@example.com")) as extract:
result = await run_parallel(agents, bus)
assert result["errors"] == []
assert browser.values == {"email": "me@example.com"}
assert extract.await_count == 1
skipped = [message for message in bus.history if message.type == "info_skipped"]
assert [message.payload["field"] for message in skipped] == ["birthday", "address"]
evidence = timing_evidence(bus)
assert evidence["expected_overlap_count"] == 1
assert len(evidence["overlap_checks"]) == 1
class CountryFailBrowser(SubmittingFakeBrowser):
"""Fails only the pre-filled known field, succeeds on the phone-collected one."""
async def fill(self, field, value):
if field.name == "country":
raise RecoverableFillError(f"cannot fill {field.name}")
await asyncio.sleep(0.05)
self.values[field.name] = value
@pytest.mark.asyncio
async def test_known_value_fill_error_flows_back_to_phone_and_blocks_submission():
# A known_values field that fails to pre-fill must surface a fill_error (like
# the in-dialogue path), so the phone agent reports the failure instead of
# telling the user registration completed.
email = FieldSpec("email", "邮箱", "email")
country = FieldSpec("country", "国家", "text")
decision = DecisionRecord(
page_url="https://example.test/register",
page_title="Register",
known_fields=[country.name],
discovered_fields=[email, country],
tool_called="initiate_phone_call_agent",
purpose="协助填写注册表单",
required_info=[email],
rationale_summary="tool call",
model="test",
monotonic_seconds=0,
)
browser = CountryFailBrowser()
channel = ScriptedPhoneChannel(["me@example.com"])
bus = MessageBus()
agents = initiate_phone_call_agent(
decision=decision,
bus=bus,
channel=channel,
browser=browser,
known_values={"country": "US"},
)
with patch("orchestration._extract_value", AsyncMock(return_value="me@example.com")):
result = await run_parallel(agents, bus)
assert result["submitted"] is False
assert browser.submit_calls == 0
assert {"field": "country", "error": "cannot fill country"} in agents.phone.browser_feedback
assert any(message.type == "fill_error" for message in bus.history)
assert "填写遇到问题" in channel.prompts[-1]