Этапы утверждения с участием человека
Шаблоны приостановки, запроса и утверждения для важных действий агента.
«Этапы утверждения с участием человека» — бесплатный урок AI Agents на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Agents, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Agents содержит 4 уроков всего.
Когда агенту требуется одобрение человека
При выполнении действий с серьезными последствиями — отправке договора, удалении данных в рабочей среде, расходовании бюджета, публикации объявления — агент не должен действовать автономно. Перед продолжением он должен приостановить выполнение и запросить одобрение человека.
Это шаблон взаимодействия человека и системы (HITL).
Определение действий с серьезными последствиями
Определите функцию классификации, которая выявляет действия, требующие одобрения человека. Порог можно настраивать для каждого развертывания с учетом допустимого уровня риска.
HIGH_STAKES_ACTIONS = {
'send_email',
'delete_records',
'publish_content',
'transfer_funds',
'modify_production_config',
'export_all_data',
'send_push_notification_to_all'
}
HIGH_STAKES_THRESHOLDS = {
'transfer_funds': 1000, # USD — require approval above this
'delete_records': 10, # rows
'send_email': 50, # recipients
'push_notification': 1000 # users
}
def requires_approval(action: str, parameters: dict) -> bool:
if action not in HIGH_STAKES_ACTIONS:
return False
threshold = HIGH_STAKES_THRESHOLDS.get(action)
if threshold is None:
return True # all instances require approval
# Check parameter against threshold
amount = parameters.get('amount') or parameters.get('count') or 0
return float(amount) >= threshold
if __name__ == '__main__':
print('Small transfer:', requires_approval('transfer_funds', {'amount': 200}))
print('Large transfer:', requires_approval('transfer_funds', {'amount': 5000}))
print('Publish content:', requires_approval('publish_content', {}))
Создание запроса на одобрение
Когда требуется одобрение, агент создает запись запроса на одобрение и приостанавливает выполнение. Запрос содержит описание предстоящего действия, его параметры и крайний срок.
import uuid, time
approval_requests: dict[str, dict] = {} # approval_id -> request
def create_approval_request(agent_id: str, user_id: str, action: str,
parameters: dict, timeout_minutes: int = 30) -> str:
approval_id = str(uuid.uuid4())
approval_requests[approval_id] = {
'approval_id': approval_id,
'agent_id': agent_id,
'user_id': user_id,
'action': action,
'parameters': parameters,
'status': 'pending', # pending / approved / rejected / timed_out
'created_at': time.time(),
'expires_at': time.time() + timeout_minutes * 60,
'decided_by': None,
'decided_at': None
}
return approval_id
if __name__ == '__main__':
approval_id = create_approval_request(
'agent-1', 'user-42', 'delete_records', {'count': 50}
)
print('Created approval request:', approval_id)
print('Status:', approval_requests[approval_id]['status'])
Уведомление через Slack
Отправьте запрос на одобрение утверждающему лицу через Slack. Включите краткое описание действия, которое хочет выполнить агент, ссылку для одобрения или отклонения и крайний срок, после которого истекает время ожидания.
import requests
SLACK_WEBHOOK = 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL'
APPROVAL_BASE_URL = 'https://your-agent-dashboard.com/approvals'
def notify_approver_slack(approval_id: str, approver_slack_id: str):
req = approval_requests[approval_id]
import time as t
from datetime import datetime
expires = datetime.fromtimestamp(req['expires_at']).strftime('%H:%M UTC')
payload = {
'text': f'<@{approver_slack_id}> Agent approval required',
'attachments': [{
'color': '#ff9900',
'fields': [
{'title': 'Action', 'value': req['action'], 'short': True},
{'title': 'Requested by','value': req['user_id'], 'short': True},
{'title': 'Parameters', 'value': str(req['parameters'])[:200]},
{'title': 'Expires', 'value': expires, 'short': True}
],
'actions': [
{'type': 'button', 'text': 'Approve',
'url': f'{APPROVAL_BASE_URL}/{approval_id}/approve'},
{'type': 'button', 'text': 'Reject',
'url': f'{APPROVAL_BASE_URL}/{approval_id}/reject'}
]
}]
}
requests.post(SLACK_WEBHOOK, json=payload)Уведомление по электронной почте
В качестве резервного канала (или основного для организаций без Slack) отправляйте запросы на одобрение по электронной почте с понятными ссылками для одобрения или отклонения.
import smtplib
from email.mime.text import MIMEText
SMTP_HOST = 'smtp.yourcompany.com'
SMTP_PORT = 587
SMTP_USER = 'agent-noreply@yourcompany.com'
SMTP_PASS = 'YOUR_SMTP_PASSWORD'
def notify_approver_email(approval_id: str, approver_email: str):
req = approval_requests[approval_id]
body = (
f'An AI agent is requesting approval for:\n\n'
f'Action: {req["action"]}\n'
f'Parameters: {req["parameters"]}\n\n'
f'Approve: {APPROVAL_BASE_URL}/{approval_id}/approve\n'
f'Reject: {APPROVAL_BASE_URL}/{approval_id}/reject\n\n'
f'This request expires in 30 minutes.'
)
msg = MIMEText(body)
msg['Subject'] = f'Agent Approval Required: {req["action"]}'
msg['From'] = SMTP_USER
msg['To'] = approver_email
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
server.starttls()
server.login(SMTP_USER, SMTP_PASS)
server.send_message(msg)Ожидание решения об одобрении
После отправки уведомления агент ожидает решения. Используйте цикл опроса с короткой паузой. Прекратите опрос, когда статус изменится с «ожидание» или истечет крайний срок.
import time
def wait_for_approval(approval_id: str, poll_interval: float = 5.0) -> dict:
while True:
req = approval_requests.get(approval_id)
if not req:
return {'decision': 'error', 'reason': 'Approval request not found'}
if req['status'] == 'approved':
return {'decision': 'approved', 'decided_by': req['decided_by']}
if req['status'] == 'rejected':
return {'decision': 'rejected', 'decided_by': req['decided_by']}
if time.time() > req['expires_at']:
req['status'] = 'timed_out'
return {'decision': 'timed_out', 'reason': 'No decision within deadline'}
time.sleep(poll_interval)Запись решения об одобрении
Когда утверждающее лицо нажимает «Одобрить» или «Отклонить», запишите, кто и когда принял решение. Так формируется полная цепочка аудита одобрения: запрос создан → уведомление отправлено → решение принято → действие выполнено (или отменено).
def record_decision(approval_id: str, decision: str,
decided_by: str) -> dict:
req = approval_requests.get(approval_id)
if not req:
return {'error': 'Approval request not found'}
if req['status'] != 'pending':
return {'error': f'Request already in state: {req["status"]}'}
if time.time() > req['expires_at']:
req['status'] = 'timed_out'
return {'error': 'Request has expired'}
req['status'] = decision # 'approved' or 'rejected'
req['decided_by'] = decided_by
req['decided_at'] = time.time()
return {'ok': True, 'decision': decision}Обработка истечения времени: автоматическая отмена
Если в течение отведенного времени решение не принято, агент автоматически отменяет действие и уведомляет исходного инициатора запроса. Это не позволяет действиям навсегда блокироваться из-за отсутствия ответа от утверждающих лиц.
def handle_timeout(approval_id: str, agent_session: dict) -> str:
req = approval_requests.get(approval_id, {})
action = req.get('action', 'unknown')
user_id = req.get('user_id', 'unknown')
# Log the timeout
import logging
logging.warning(
'Approval timeout: action=%s user=%s approval_id=%s',
action, user_id, approval_id
)
# Tell the user
timeout_message = (
f'The "{action}" action was automatically cancelled because '
f'no approver responded within the 30-minute window. '
f'Please request again or contact your administrator.'
)
return timeout_messageПолная оркестрация проверки одобрения
Объедините все шаги: проверьте, требуется ли одобрение, создайте запрос, отправьте уведомление, дождитесь решения, а затем продолжите выполнение или отмените действие в зависимости от решения.
def execute_with_approval_gate(agent_id: str, user_id: str, action: str,
parameters: dict, approver_email: str,
execute_fn) -> dict:
# Step 1: Check if approval needed
if not requires_approval(action, parameters):
result = execute_fn(action, parameters)
return {'approved': True, 'auto': True, 'result': result}
# Step 2: Create approval request
approval_id = create_approval_request(agent_id, user_id, action, parameters)
# Step 3: Notify approver
notify_approver_email(approval_id, approver_email)
print(f'Approval requested: {approval_id}. Waiting...')
# Step 4: Wait for decision
decision = wait_for_approval(approval_id)
# Step 5: Act on decision
if decision['decision'] == 'approved':
result = execute_fn(action, parameters)
return {'approved': True, 'decided_by': decision['decided_by'], 'result': result}
elif decision['decision'] == 'rejected':
return {'approved': False, 'reason': 'Rejected by approver'}
else:
msg = handle_timeout(approval_id, {})
return {'approved': False, 'reason': msg}Аудиторский след одобрения
Каждое событие жизненного цикла одобрения необходимо записывать в журнал аудита: запрос создан, уведомление отправлено, решение принято, действие выполнено или отменено. Такая цепочка необходима для соответствия требованиям SOC 2.
import logging, json, time
approval_logger = logging.getLogger('agent.approvals')
def audit_approval_event(event: str, approval_id: str, details: dict):
entry = {
'timestamp': time.time(),
'event': event,
'approval_id': approval_id,
**details
}
approval_logger.info(json.dumps(entry))
# Usage flow:
# audit_approval_event('request_created', approval_id, {'action': 'delete_records', 'user': 'u123'})
# audit_approval_event('notification_sent', approval_id, {'channel': 'email', 'approver': 'admin@co.com'})
# audit_approval_event('decision_received', approval_id, {'decision': 'approved', 'by': 'admin@co.com'})
# audit_approval_event('action_executed', approval_id, {'result': 'success'})
if __name__ == '__main__':
import sys
approval_logger.setLevel(logging.INFO)
approval_logger.addHandler(logging.StreamHandler(sys.stdout))
audit_approval_event('request_created', 'appr-1', {'action': 'delete_records', 'user': 'u123'})
Эскалация при недоступности основного утверждающего лица
Если основное утверждающее лицо не отвечает в течение половины отведенного времени, передайте запрос второму утверждающему лицу. Это предотвращает блокировку всех одобрений из-за отсутствия одного человека.
def escalate_if_needed(approval_id: str, secondary_email: str,
escalation_at_pct: float = 0.5):
req = approval_requests.get(approval_id)
if not req or req['status'] != 'pending':
return
total_window = req['expires_at'] - req['created_at']
elapsed = time.time() - req['created_at']
escalation_at = req['created_at'] + total_window * escalation_at_pct
if time.time() >= escalation_at and not req.get('escalated'):
req['escalated'] = True
notify_approver_email(approval_id, secondary_email)
print(f'Escalated approval {approval_id} to {secondary_email}')Что происходит с ожидающим запросом на одобрение после истечения времени ожидания
Поведение при истечении времени — важнейшая часть проектирования HITL. Неправильный выбор варианта по умолчанию по-разному влияет на безопасность и удобство использования.
Итоги: проверки одобрения с участием человека
Проверки одобрения HITL работают следующим образом: классификация действий с серьезными последствиями, создание приостановленного запроса на одобрение, уведомление утверждающих лиц через Slack или электронную почту, опрос для получения решений, автоматическая отмена при истечении времени и запись каждого события жизненного цикла в цепочку аудита.
Эскалация к вторым утверждающим лицам предотвращает блокировку одобрений из-за недоступности основных.
Часто задаваемые вопросы
Урок «Этапы утверждения с участием человека» бесплатный?
Да — полный текст урока «Этапы утверждения с участием человека» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Agents, подпишись на CoddyKit PRO. Курс AI Agents содержит 4 уроков всего.
Чему я научусь в уроке «Этапы утверждения с участием человека»?
Шаблоны приостановки, запроса и утверждения для важных действий агента. Ты практикуешь AI Agents с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Agents?
Предыдущий опыт не требуется. AI Agents на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Этапы утверждения с участием человека»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Agents?
Да. Каждый урок AI Agents включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Неизменяемая регистрация действий агентов
- Применение политик к действиям агента
- Соблюдение нормативных требований: GDPR и SOC2
- Этапы утверждения с участием человека