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,264 @@
"""
Workflow data structures for capturing and storing browser action sequences.
This module defines the structures used to represent learned workflows,
including individual steps and complete action sequences.
"""
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any, Dict, List, Optional
from enum import Enum
import json
class ActionType(Enum):
"""Types of actions that can be recorded in a workflow"""
NAVIGATE = "navigate"
CLICK = "click"
INPUT_TEXT = "input_text"
SELECT_OPTION = "select_option"
SCROLL = "scroll"
WAIT = "wait"
SWITCH_TAB = "switch_tab"
CLOSE_TAB = "close_tab"
UPLOAD_FILE = "upload_file"
class PredicateType(Enum):
"""Machine-checkable browser state predicates."""
URL_CONTAINS = "url_contains"
ELEMENT_VISIBLE = "element_visible"
ELEMENT_TEXT_CONTAINS = "element_text_contains"
ELEMENT_VALUE_EQUALS = "element_value_equals"
PAGE_STATE_EQUALS = "page_state_equals"
class WorkflowStatus(Enum):
CANDIDATE = "candidate"
VALIDATED = "validated"
INVALID = "invalid"
@dataclass
class StatePredicate:
"""A precondition, postcondition or final-state assertion."""
predicate_type: PredicateType
expected: Any = True
selector: Optional[str] = None
state_key: Optional[str] = None
description: str = ""
def to_dict(self) -> Dict[str, Any]:
return {
"predicate_type": self.predicate_type.value,
"expected": self.expected,
"selector": self.selector,
"state_key": self.state_key,
"description": self.description,
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'StatePredicate':
values = dict(data)
values["predicate_type"] = PredicateType(values["predicate_type"])
return cls(**values)
@dataclass
class WorkflowStep:
"""Represents a single step in a workflow"""
action_type: ActionType
# Stable selectors for element identification
xpath: Optional[str] = None
css_selector: Optional[str] = None
# Action parameters
parameters: Dict[str, Any] = field(default_factory=dict)
# Additional context
element_attributes: Dict[str, str] = field(default_factory=dict)
description: str = ""
# Timing information
wait_before: float = 0.0 # Seconds to wait before executing this step
timeout: float = 15.0 # Maximum time to wait for element to be ready
# Validation
expected_outcome: Optional[str] = None
preconditions: List[StatePredicate] = field(default_factory=list)
postconditions: List[StatePredicate] = field(default_factory=list)
def to_dict(self) -> Dict[str, Any]:
"""Convert step to dictionary for serialization"""
return {
"action_type": self.action_type.value,
"xpath": self.xpath,
"css_selector": self.css_selector,
"parameters": self.parameters,
"element_attributes": self.element_attributes,
"description": self.description,
"wait_before": self.wait_before,
"timeout": self.timeout,
"expected_outcome": self.expected_outcome,
"preconditions": [item.to_dict() for item in self.preconditions],
"postconditions": [item.to_dict() for item in self.postconditions],
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'WorkflowStep':
"""Create step from dictionary"""
data = data.copy()
data['action_type'] = ActionType(data['action_type'])
data['preconditions'] = [StatePredicate.from_dict(item) for item in data.get('preconditions', [])]
data['postconditions'] = [StatePredicate.from_dict(item) for item in data.get('postconditions', [])]
return cls(**data)
@classmethod
def from_browser_action(cls, action_type: str, element: Optional[Any] = None, **params) -> 'WorkflowStep':
"""Create a workflow step from browser-use action and element info"""
step = cls(
action_type=ActionType(action_type.lower()),
parameters=params
)
if element:
# Extract stable selectors from DOMInteractedElement
if hasattr(element, 'x_path'):
step.xpath = element.x_path
# Store relevant attributes for fallback identification
if hasattr(element, 'attributes') and element.attributes:
step.element_attributes = {
k: v for k, v in element.attributes.items()
if k in ['id', 'name', 'class', 'type', 'role', 'aria-label', 'data-testid']
}
return step
@dataclass
class Workflow:
"""Represents a complete workflow that can be learned and replayed"""
# Identification
workflow_id: str
intent: str # The task intent this workflow accomplishes
# Steps
steps: List[WorkflowStep] = field(default_factory=list)
# Metadata
created_at: datetime = field(default_factory=datetime.now)
last_used_at: Optional[datetime] = None
success_count: int = 0
failure_count: int = 0
# Learning context
initial_url: Optional[str] = None
example_parameters: Dict[str, Any] = field(default_factory=dict)
description: str = ""
# Performance metrics
average_execution_time: float = 0.0
model_calls_saved: int = 0
# Validation lifecycle. New workflows are candidates until a complete
# replay succeeds in a reset environment.
validation_status: WorkflowStatus = WorkflowStatus.CANDIDATE
final_predicates: List[StatePredicate] = field(default_factory=list)
validated_at: Optional[datetime] = None
invalid_reason: Optional[str] = None
def add_step(self, step: WorkflowStep) -> None:
"""Add a step to the workflow"""
self.steps.append(step)
def to_dict(self) -> Dict[str, Any]:
"""Convert workflow to dictionary for serialization"""
return {
"workflow_id": self.workflow_id,
"intent": self.intent,
"steps": [step.to_dict() for step in self.steps],
"created_at": self.created_at.isoformat(),
"last_used_at": self.last_used_at.isoformat() if self.last_used_at else None,
"success_count": self.success_count,
"failure_count": self.failure_count,
"initial_url": self.initial_url,
"example_parameters": self.example_parameters,
"description": self.description,
"average_execution_time": self.average_execution_time,
"model_calls_saved": self.model_calls_saved,
"validation_status": self.validation_status.value,
"final_predicates": [item.to_dict() for item in self.final_predicates],
"validated_at": self.validated_at.isoformat() if self.validated_at else None,
"invalid_reason": self.invalid_reason,
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'Workflow':
"""Create workflow from dictionary"""
data = data.copy()
data['steps'] = [WorkflowStep.from_dict(s) for s in data.get('steps', [])]
data['created_at'] = datetime.fromisoformat(data['created_at'])
if data.get('last_used_at'):
data['last_used_at'] = datetime.fromisoformat(data['last_used_at'])
# Old files had no lifecycle field. Treat them as candidates so they
# cannot silently bypass the new validation protocol.
data['validation_status'] = WorkflowStatus(data.get('validation_status', 'candidate'))
data['final_predicates'] = [StatePredicate.from_dict(item) for item in data.get('final_predicates', [])]
if data.get('validated_at'):
data['validated_at'] = datetime.fromisoformat(data['validated_at'])
return cls(**data)
def to_json(self) -> str:
"""Serialize workflow to JSON string"""
return json.dumps(self.to_dict(), indent=2, default=str)
@classmethod
def from_json(cls, json_str: str) -> 'Workflow':
"""Deserialize workflow from JSON string"""
data = json.loads(json_str)
return cls.from_dict(data)
def parameterize(self, parameters: Dict[str, Any]) -> 'Workflow':
"""
Create a parameterized copy of this workflow with specific values.
Args:
parameters: Dictionary mapping parameter names to values
Returns:
A new Workflow instance with parameters applied
"""
import copy
parameterized = copy.deepcopy(self)
# Apply parameters to each step
for step in parameterized.steps:
for param_key, param_value in parameters.items():
# Replace placeholders in step parameters
for key, value in step.parameters.items():
if isinstance(value, str) and f"{{{param_key}}}" in value:
step.parameters[key] = value.replace(f"{{{param_key}}}", str(param_value))
for predicate in (*step.preconditions, *step.postconditions):
if isinstance(predicate.expected, str) and f"{{{param_key}}}" in predicate.expected:
predicate.expected = predicate.expected.replace(f"{{{param_key}}}", str(param_value))
for predicate in parameterized.final_predicates:
for param_key, param_value in parameters.items():
if isinstance(predicate.expected, str) and f"{{{param_key}}}" in predicate.expected:
predicate.expected = predicate.expected.replace(f"{{{param_key}}}", str(param_value))
return parameterized
def mark_validated(self) -> None:
self.validation_status = WorkflowStatus.VALIDATED
self.validated_at = datetime.now()
self.invalid_reason = None
def mark_invalid(self, reason: str) -> None:
self.validation_status = WorkflowStatus.INVALID
self.invalid_reason = reason