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
+373
View File
@@ -0,0 +1,373 @@
"""
Demo: Email sending with learning capability
This demo shows how the agent learns to send emails and reuses the learned workflow.
Uses Ethereal Email (ethereal.email) as a test email service.
"""
import argparse
import asyncio
import json
import logging
import time
from dotenv import load_dotenv
from browser_use import ChatOpenAI, ChatGoogle
from learning_agent import LearningAgent
from llm_factory import make_llm, DEFAULT_MODEL
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
load_dotenv()
# Default two-phase tasks. Phase 1 teaches the workflow from scratch (expensive,
# multimodal LLM loop); Phase 2 replays it with different parameters (cheap, no LLM).
DEFAULT_LEARNING_TASK = """
Go to https://ethereal.email and send a test email with:
- To: test@example.com
- Subject: Hello from Learning Agent
- Message: This is a test email sent by the learning agent. The workflow will be captured for future reuse.
"""
DEFAULT_REPLAY_TASK = """
Send an email to another@example.com with subject "Workflow Test"
and message "This email is sent using a learned workflow. No LLM calls needed!"
"""
class EmailDemo:
"""Demo for email sending with learning capability."""
def __init__(self, llm_model=DEFAULT_MODEL, knowledge_base_path="./email_knowledge",
headless=False, max_steps=20, learning_task=None, replay_task=None,
output_path=None):
self.llm_model = llm_model
self.knowledge_base_path = knowledge_base_path
self.headless = headless
self.max_steps = max_steps
self.learning_task = learning_task or DEFAULT_LEARNING_TASK
self.replay_task = replay_task or DEFAULT_REPLAY_TASK
self.output_path = output_path
async def run_full_demo(self):
"""Run complete email demo with learning and replay phases."""
print("=" * 80)
print("EMAIL SENDING DEMO - LEARNING AGENT")
print("=" * 80)
print("\nThis demo uses Ethereal Email (ethereal.email) for testing.")
print("Note: This is a test service - emails won't actually be delivered.")
print("=" * 80)
# Phase 1: Learning
await self.phase1_learning()
# Wait before Phase 2
print("\n⏳ Waiting 5 seconds before replay phase...")
await asyncio.sleep(5)
# Phase 2: Replay
await self.phase2_replay()
# Phase 3: Statistics
self.show_statistics()
# Optionally persist the before/after comparison for later analysis
if self.output_path:
self.save_results()
async def phase1_learning(self):
"""Phase 1: Learn how to send an email."""
print("\n" + "📚 PHASE 1: LEARNING - First Email Task ".ljust(70, "="))
print("\nThe agent will learn how to send an email from scratch.")
print("This requires multiple LLM calls to explore and understand the interface.")
print("-" * 70)
# Task with specific details
task = self.learning_task
print(f"Task: {task.strip()}")
print("Expected behavior:")
print(" 1. Navigate to Ethereal Email")
print(" 2. Create a test account (if needed)")
print(" 3. Compose and send the email")
print(" 4. Capture workflow for future reuse")
# Create learning agent
agent = LearningAgent(
task=task,
llm=self._get_llm(),
knowledge_base_path=self.knowledge_base_path,
headless=self.headless
)
print("\n🚀 Starting learning phase...")
start_time = time.time()
try:
result = await agent.run(max_steps=self.max_steps)
elapsed = time.time() - start_time
print("\n✅ Learning phase completed!")
print(f" 📊 Results:")
print(f" - Success: {'' if result['success'] else ''}")
print(f" - Execution time: {elapsed:.2f} seconds")
print(f" - LLM calls made: {result['llm_calls']}")
print(f" - Workflow captured: {'Yes' if result['success'] else 'No'}")
if result['success']:
print("\n 💡 Workflow successfully learned and saved!")
print(" The agent can now repeat similar tasks without LLM calls.")
# Store metrics for comparison
self.learning_metrics = {
'time': elapsed,
'llm_calls': result['llm_calls'],
'success': result['success']
}
except Exception as e:
print(f"\n❌ Learning phase failed: {e}")
self.learning_metrics = {'time': 0, 'llm_calls': 0, 'success': False}
async def phase2_replay(self):
"""Phase 2: Replay learned workflow with different parameters."""
print("\n" + "🚀 PHASE 2: REPLAY - Second Email Task ".ljust(70, "="))
print("\nThe agent will reuse the learned workflow with different parameters.")
print("This should be much faster and require NO LLM calls.")
print("-" * 70)
# Different email parameters
task = self.replay_task
print(f"Task: {task.strip()}")
print("Expected behavior:")
print(" 1. Match task to learned workflow")
print(" 2. Extract new parameters (recipient, subject, message)")
print(" 3. Replay workflow with new parameters")
print(" 4. Complete task without any LLM calls")
# Create agent for replay
agent = LearningAgent(
task=task,
llm=self._get_llm(),
knowledge_base_path=self.knowledge_base_path,
headless=self.headless
)
print("\n🔄 Starting replay phase...")
start_time = time.time()
try:
result = await agent.run(max_steps=self.max_steps)
elapsed = time.time() - start_time
print("\n✅ Replay phase completed!")
print(f" 📊 Results:")
print(f" - Success: {'' if result['success'] else ''}")
print(f" - Execution time: {elapsed:.2f} seconds")
print(f" - Workflow reused: {'Yes' if result['replay_used'] else 'No'}")
if result['replay_used']:
# Calculate improvements
if hasattr(self, 'learning_metrics') and self.learning_metrics['success']:
speedup = self.learning_metrics['time'] / elapsed
calls_saved = self.learning_metrics['llm_calls']
print(f"\n 🎯 Performance Improvements:")
print(f" - Speed: {speedup:.1f}x faster")
print(f" - LLM calls saved: {calls_saved}")
print(f" - Time saved: {self.learning_metrics['time'] - elapsed:.1f} seconds")
else:
print(f" - LLM calls made: {result.get('llm_calls', 0)}")
print("\n ⚠️ Workflow was not reused. Task may be too different.")
# Store replay metrics
self.replay_metrics = {
'time': elapsed,
'replay_used': result['replay_used'],
'success': result['success']
}
except Exception as e:
print(f"\n❌ Replay phase failed: {e}")
self.replay_metrics = {'time': 0, 'replay_used': False, 'success': False}
def show_statistics(self):
"""Show knowledge base statistics."""
print("\n" + "📊 KNOWLEDGE BASE STATISTICS ".ljust(70, "="))
from learning_agent import KnowledgeBase
kb = KnowledgeBase(self.knowledge_base_path)
stats = kb.get_statistics()
print("\n Current Knowledge Base Status:")
for key, value in stats.items():
formatted_key = key.replace('_', ' ').title()
print(f" - {formatted_key}: {value}")
# Show comparison if both phases completed
if hasattr(self, 'learning_metrics') and hasattr(self, 'replay_metrics'):
if self.learning_metrics['success'] and self.replay_metrics['replay_used']:
print("\n 📈 Performance Comparison:")
print(f" Phase 1 (Learning):")
print(f" - Time: {self.learning_metrics['time']:.2f}s")
print(f" - LLM Calls: {self.learning_metrics['llm_calls']}")
print(f" Phase 2 (Replay):")
print(f" - Time: {self.replay_metrics['time']:.2f}s")
print(f" - LLM Calls: 0")
improvement = (1 - self.replay_metrics['time'] / self.learning_metrics['time']) * 100
print(f"\n 🚀 Overall Improvement: {improvement:.0f}% faster with replay!")
print("\n" + "=" * 70)
print("DEMO COMPLETED SUCCESSFULLY")
print("=" * 70)
def save_results(self):
"""Persist learning/replay metrics and KB stats to a JSON file."""
from learning_agent import KnowledgeBase
kb = KnowledgeBase(self.knowledge_base_path)
payload = {
'model': self.llm_model,
'knowledge_base_path': self.knowledge_base_path,
'headless': self.headless,
'max_steps': self.max_steps,
'learning_task': self.learning_task.strip(),
'replay_task': self.replay_task.strip(),
'learning_metrics': getattr(self, 'learning_metrics', None),
'replay_metrics': getattr(self, 'replay_metrics', None),
'knowledge_base_stats': kb.get_statistics(),
}
with open(self.output_path, 'w', encoding='utf-8') as f:
json.dump(payload, f, ensure_ascii=False, indent=2)
print(f"\n💾 Results saved to: {self.output_path}")
def _get_llm(self):
"""Get LLM instance based on configuration (OpenAI 直连,缺 Key 时 OpenRouter 兜底)。"""
return make_llm(self.llm_model)
async def quick_test(model=DEFAULT_MODEL, headless=False, max_steps=15,
knowledge_base_path="./test_knowledge", task=None):
"""Quick test with a single simple task (no replay comparison)."""
print("\n🧪 QUICK TEST - Simple Email Task")
print("-" * 40)
task = task or "Go to ethereal.email and send a test email to demo@test.com"
llm = make_llm(model)
agent = LearningAgent(
task=task,
llm=llm,
knowledge_base_path=knowledge_base_path,
headless=headless
)
print(f"Task: {task}")
result = await agent.run(max_steps=max_steps)
print(f"\nResult: {'Success' if result['success'] else 'Failed'}")
print(f"Time: {result['execution_time']:.2f}s")
print(f"LLM calls: {result.get('llm_calls', 0)}")
def build_parser() -> argparse.ArgumentParser:
"""构建命令行参数解析器(RPA 邮件学习/回放演示)。"""
parser = argparse.ArgumentParser(
prog="demo_email.py",
description=(
"browser-use RPA 演示:学习一次「发送邮件」工作流,之后用不同参数高速回放。\n"
"第一阶段(学习)通过多模态大模型逐步探索并录制工作流;\n"
"第二阶段(回放)直接复用工作流、无需再调用大模型,对比耗时与调用次数。"
),
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=(
"示例:\n"
" python demo_email.py # 运行完整的「学习→回放」对比演示\n"
" python demo_email.py --quick # 只跑一次简单任务,快速冒烟测试\n"
" python demo_email.py --model gemini-2.0-flash-exp --headless\n"
" python demo_email.py --task '给 a@b.com 发主题为\"报告\"的邮件' \\\n"
" --replay-task '给 c@d.com 发主题为\"周报\"的邮件' \\\n"
" --output results.json\n"
),
)
parser.add_argument(
"--task", default=None,
help="学习阶段的任务描述(默认:向 test@example.com 发送测试邮件)",
)
parser.add_argument(
"--replay-task", default=None,
help="回放阶段的任务描述,参数不同但流程相同(默认:向 another@example.com 发送邮件)",
)
parser.add_argument(
"--model", default=DEFAULT_MODEL,
help="使用的大模型,gpt-* 走 OpenAI(缺 Key 时走 OpenRouter 兜底),"
"gemini-* 走 Google(默认:gpt-5.6-luna",
)
parser.add_argument(
"--headless", action="store_true",
help="以无界面(headless)模式运行浏览器(默认:显示浏览器窗口)",
)
parser.add_argument(
"--knowledge-base", default="./email_knowledge", metavar="PATH",
help="工作流知识库的存储目录(默认:./email_knowledge",
)
parser.add_argument(
"--max-steps", type=int, default=20, metavar="N",
help="学习阶段允许的最大操作步数(默认:20)",
)
parser.add_argument(
"--output", default=None, metavar="PATH",
help="将学习/回放的指标对比与知识库统计写入指定 JSON 文件",
)
parser.add_argument(
"--quick", action="store_true",
help="快速冒烟测试:只运行一次简单任务,不做学习/回放对比",
)
return parser
def main():
"""Main entry point."""
args = build_parser().parse_args()
if args.quick:
# Run quick test (single task, no replay comparison)
asyncio.run(quick_test(
model=args.model,
headless=args.headless,
max_steps=args.max_steps,
knowledge_base_path=args.knowledge_base,
task=args.task,
))
else:
# Run full learning + replay demo
demo = EmailDemo(
llm_model=args.model,
knowledge_base_path=args.knowledge_base,
headless=args.headless,
max_steps=args.max_steps,
learning_task=args.task,
replay_task=args.replay_task,
output_path=args.output,
)
asyncio.run(demo.run_full_demo())
if __name__ == "__main__":
main()