Files
liqiang b119135836
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
ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
2026-08-20 13:12:50 +00:00

248 lines
11 KiB
Python

"""Scenario A: Hiring Pipeline.
Objects: organizations, positions, candidates, interviews, evaluations, audit_logs
Constraints:
- State machine on candidate status: applied -> screened -> interviewed -> offered -> hired/rejected
- Referential integrity between candidates and positions
- Cross-object validation: position must be open to accept new candidates
- Role-based permissions: recruiters modify candidates, hiring managers approve offers,
candidates view only their own records
"""
from pedo.core.models import (
AccessContext, DataObject, ObjectType, Operation,
PermissionRule, PrivilegeType, Relationship, RelationshipAction,
ReactionDeclaration,
)
from pedo.core.store import ObjectStore
# Valid state transitions for candidates
VALID_TRANSITIONS = {
None: ["applied"],
"applied": ["screened", "rejected"],
"screened": ["interviewed", "rejected"],
"interviewed": ["offered", "rejected"],
"offered": ["hired", "rejected"],
"hired": [],
"rejected": [],
}
def validate_candidate_status(proposed, existing, accessor, store):
"""Validate candidate status follows the state machine."""
new_status = proposed.content.get("status")
if existing is None:
# Creating a new candidate
if new_status and new_status != "applied":
return f"New candidates must start with status 'applied', got '{new_status}'"
return True
old_status = existing.content.get("status")
if new_status and new_status != old_status:
valid = VALID_TRANSITIONS.get(old_status, [])
if new_status not in valid:
return f"Invalid status transition: {old_status} -> {new_status}. Valid: {valid}"
return True
def validate_position_open(proposed, existing, accessor, store):
"""Validate that the referenced position is still open."""
position_id = proposed.content.get("position_id")
if not position_id:
return True
position = store.raw_read(position_id)
if position is None:
return f"Position {position_id} not found"
if position.content.get("status") != "open":
return f"Position {position_id} is not open (status: {position.content.get('status')})"
return True
def validate_salary_range(proposed, existing, accessor, store):
"""Validate salary expectation is within position range."""
salary = proposed.content.get("salary_expectation")
position_id = proposed.content.get("position_id")
if salary is None or position_id is None:
return True
position = store.raw_read(position_id)
if position is None:
return True # position validator will catch this
min_sal = position.content.get("salary_min", 0)
max_sal = position.content.get("salary_max", float("inf"))
if not (min_sal <= salary <= max_sal):
return f"Salary {salary} outside position range [{min_sal}, {max_sal}]"
return True
def validate_interview_candidate_exists(proposed, existing, accessor, store):
"""Validate that the candidate for an interview exists and is in correct status."""
candidate_id = proposed.content.get("candidate_id")
if not candidate_id:
return "Interview must reference a candidate"
candidate = store.raw_read(candidate_id)
if candidate is None:
return f"Candidate {candidate_id} not found"
if existing is None:
# Creating new interview: candidate must be in screened status
if candidate.content.get("status") not in ("screened", "interviewed"):
return f"Candidate must be screened/interviewed for interview, got {candidate.content.get('status')}"
return True
def validate_evaluation_interview_exists(proposed, existing, accessor, store):
"""Validate that the interview for an evaluation exists."""
interview_id = proposed.content.get("interview_id")
if not interview_id:
return "Evaluation must reference an interview"
interview = store.raw_read(interview_id)
if interview is None:
return f"Interview {interview_id} not found"
return True
# Reaction handlers
def create_audit_log(event, store):
"""Create an audit log entry after any candidate status change."""
system = AccessContext(user_id="system", role="system", org_id=event["object_org"])
log = DataObject(
type_name="audit_log",
content={
"action": event["event"],
"object_id": event["object_id"],
"object_type": event["object_type"],
"changed_fields": event.get("changed_fields", []),
"timestamp": event["timestamp"],
},
owner_id="system",
org_id=event["object_org"],
)
store.create(log, system, _reaction_depth=event["depth"])
def register_hiring_types(store: ObjectStore):
"""Register all hiring pipeline types with the store."""
# Organization (root of hierarchy)
store.register_type(ObjectType(
name="organization",
fields={"name": "str"},
permission_rules=[
PermissionRule(Operation.ACCEPT, PrivilegeType.READ, {"roles": ["recruiter", "hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.SELECT, {"roles": ["recruiter", "hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.INSERT, {"roles": ["admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.UPDATE, {"roles": ["admin"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.DELETE, {"roles": ["admin"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.MANAGE, {"roles": ["admin"]}),
],
default_policy=Operation.DENY,
))
# Position
store.register_type(ObjectType(
name="position",
fields={"title": "str", "department": "str", "status": "str",
"salary_min": "int", "salary_max": "int"},
permission_rules=[
PermissionRule(Operation.ACCEPT, PrivilegeType.READ, {"roles": ["recruiter", "hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.WRITE, {"roles": ["hiring_manager", "admin"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.INSERT, {"roles": ["hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.SELECT, {"roles": ["recruiter", "hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.UPDATE, {"roles": ["hiring_manager", "admin"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.DELETE, {"roles": ["admin"]}),
],
default_policy=Operation.DENY,
))
# Candidate
store.register_type(ObjectType(
name="candidate",
fields={"name": "str", "email": "str", "status": "str",
"position_id": "str", "salary_expectation": "int"},
permission_rules=[
PermissionRule(Operation.ACCEPT, PrivilegeType.READ, {"roles": ["recruiter", "hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.READ, {"is_owner": True}),
PermissionRule(Operation.ACCEPT, PrivilegeType.WRITE, {"roles": ["recruiter", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.INSERT, {"roles": ["recruiter", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.SELECT, {"roles": ["recruiter", "hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.UPDATE, {"roles": ["recruiter", "admin"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.DELETE, {"roles": ["admin"]}),
# Candidates can only read their own record (via is_owner above)
# Hiring managers can read but not modify (no WRITE rule for them)
PermissionRule(Operation.DENY, PrivilegeType.WRITE, {"role": "hiring_manager"}),
],
validators=[validate_candidate_status, validate_position_open, validate_salary_range],
reactions=[
ReactionDeclaration(event="after_update:status", handler="create_audit_log"),
ReactionDeclaration(event="after_create", handler="create_audit_log"),
],
relationships=[
Relationship(name="position", target_type="position",
on_delete=RelationshipAction.RESTRICT, required=True),
],
default_policy=Operation.DENY,
))
# Interview
store.register_type(ObjectType(
name="interview",
fields={"candidate_id": "str", "interviewer": "str",
"scheduled_at": "str", "notes": "str", "score": "int"},
permission_rules=[
PermissionRule(Operation.ACCEPT, PrivilegeType.READ, {"roles": ["recruiter", "hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.WRITE, {"roles": ["hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.INSERT, {"roles": ["recruiter", "hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.SELECT, {"roles": ["recruiter", "hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.UPDATE, {"roles": ["hiring_manager", "admin"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.DELETE, {"roles": ["admin"]}),
],
validators=[validate_interview_candidate_exists],
relationships=[
Relationship(name="candidate", target_type="candidate",
on_delete=RelationshipAction.CASCADE),
],
default_policy=Operation.DENY,
))
# Evaluation
store.register_type(ObjectType(
name="evaluation",
fields={"interview_id": "str", "decision": "str", "comments": "str"},
permission_rules=[
PermissionRule(Operation.ACCEPT, PrivilegeType.READ, {"roles": ["hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.WRITE, {"roles": ["hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.INSERT, {"roles": ["hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.SELECT, {"roles": ["hiring_manager", "admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.UPDATE, {"roles": ["hiring_manager", "admin"]}),
# Recruiters can NOT see evaluations
PermissionRule(Operation.DENY, PrivilegeType.READ, {"role": "recruiter"}),
],
validators=[validate_evaluation_interview_exists],
relationships=[
Relationship(name="interview", target_type="interview",
on_delete=RelationshipAction.CASCADE),
],
default_policy=Operation.DENY,
))
# Audit Log (system-only, immutable)
store.register_type(ObjectType(
name="audit_log",
fields={"action": "str", "object_id": "str", "object_type": "str",
"changed_fields": "list", "timestamp": "float"},
permission_rules=[
PermissionRule(Operation.ACCEPT, PrivilegeType.READ, {"roles": ["admin", "system"]}),
PermissionRule(Operation.ACCEPT, PrivilegeType.INSERT, {"role": "system"}),
PermissionRule(Operation.DENY, PrivilegeType.WRITE, {}), # immutable
PermissionRule(Operation.DENY, PrivilegeType.DELETE, {}), # immutable
],
default_policy=Operation.DENY,
))
# Register reaction handlers
store.register_reaction_handler("create_audit_log", create_audit_log)