مجله ایرانیکاسرورآموزش سرور، هاست، وردپرس و شبکه
آموزشی

ساخت Human-in-the-Loop برای AI Agent؛ اجرای عملیات حساس فقط با تأیید انسان

تصور کنید یک Agent هوش مصنوعی به پنل مدیریت سرور متصل است و می‌تواند وضعیت سرویس‌ها را بررسی کند، گزارش تهیه کند یا تنظیمات آن‌ها را تغییر دهد. حالا اگر Agent تصمیم بگیرد یک…

ساخت Human-in-the-Loop برای AI Agent؛ اجرای عملیات حساس فقط با تأیید انسان
خلاصه سریع این آموزش

تصور کنید یک Agent هوش مصنوعی به پنل مدیریت سرور متصل است و می‌تواند وضعیت سرویس‌ها را بررسی کند، گزارش تهیه کند یا تنظیمات آن‌ها را تغییر دهد. حالا اگر…

۳۸ دقیقه مطالعه ۰ دیدگاه ۱۳ آبان ۱۴۰۵ آخرین به‌روزرسانی
تصور کنید یک Agent هوش مصنوعی به پنل مدیریت سرور متصل است و می‌تواند وضعیت سرویس‌ها را بررسی کند، گزارش تهیه کند یا تنظیمات آن‌ها را تغییر دهد. حالا اگر Agent تصمیم بگیرد یک سرور را Restart کند یا اطلاعاتی را حذف کند، چه چیزی مانع می‌شود که این عملیات بدون اطلاع مدیر انجام شود؟

راه‌حل، پیاده‌سازی Human-in-the-Loop یا HITL است؛ یعنی انسان در نقطه‌های حساس گردش کار Agent حضور داشته باشد و قبل از اجرای بعضی عملیات‌ها، تصمیم معتبر او دریافت شود.

با استفاده از OpenAI Agents SDK می‌توان اجرای یک Tool را پیش از انجام عملیات متوقف کرد، مشخصات درخواست را برای بازبینی ذخیره کرد و پس از تأیید یا رد، اجرای Agent را ادامه داد. اما ساخت یک سیستم امن فقط با نمایش دو دکمه «تأیید» و «رد» کامل نمی‌شود. باید بررسی کنیم چه کسی تأیید می‌کند، دقیقاً کدام عملیات تأیید شده، تأیید تا چه زمانی اعتبار دارد و در صورت Restart، خطا یا ارسال تکراری درخواست چه اتفاقی می‌افتد.

در این مقاله ساخت یک Human in the Loop AI Agent را به‌صورت عملی آموزش می‌دهیم. ابتدا مفاهیم Pause، Resume و Approval State را توضیح می‌دهیم و سپس یک برنامه Python با OpenAI Agents SDK و SQLite می‌سازیم که تغییر وضعیت یک سرویس آزمایشی را فقط پس از تأیید اپراتور انجام می‌دهد.

در ادامه، انقضای تأییدها، ثبت Audit Log، مقابله با اجرای تکراری Tool، بازیابی پس از Crash و طراحی عملیات جبرانی یا Rollback را بررسی می‌کنیم. تمرکز مقاله بر ساخت یک Approval Gate واقعی در لایه اجرا است، نه صرفاً درخواست مؤدبانه از مدل برای گرفتن اجازه.

Human-in-the-Loop در AI Agent چیست؟

Human-in-the-Loop الگویی است که در آن سیستم هوش مصنوعی می‌تواند بخشی از کار را به‌صورت خودکار انجام دهد، اما برای تصمیم‌ها یا عملیات‌های مشخص به مداخله انسان نیاز دارد.

برای مثال، Agent می‌تواند اطلاعات سرور را بخواند و پیشنهاد دهد سرویس Restart شود؛ اما دستور Restart تنها زمانی اجرا شود که اپراتور مجاز، جزئیات آن را بررسی و تأیید کرده باشد.

یک مثال ساده

فرض کنید Agent سه وظیفه دارد:

  1. بررسی وضعیت یک سرور بازی.
  2. تحلیل علت احتمالی مشکل.
  3. راه‌اندازی مجدد سرویس در صورت نیاز.

دو مرحله اول می‌توانند فقط‌خواندنی باشند؛ اما مرحله سوم روی کاربران متصل تأثیر می‌گذارد. در این نقطه اجرای Agent متوقف می‌شود و درخواست تأیید برای مدیر ارسال می‌شود.

اگر مدیر موافقت کند، همان عملیات با همان پارامترها اجرا خواهد شد. اگر درخواست رد شود یا اعتبار آن تمام شود، عملیات نباید انجام شود.

Pause و Resume چه معنایی دارند؟

Pause یعنی Runner در یک مرز مشخص متوقف شود و قبل از اجرای Tool حساس، وضعیت قابل ذخیره ایجاد کند.

Resume یعنی پس از دریافت تصمیم معتبر، اجرای متوقف‌شده با همان وضعیت ذخیره‌شده ادامه پیدا کند.

این دو قابلیت لزوماً به معنی توقف و ادامه یک فرآیند Python واحد نیستند. می‌توان وضعیت متوقف‌شده را ذخیره کرد، برنامه را بست و بعداً در یک فرآیند جدید ادامه داد.

آیا Human-in-the-Loop باعث کند شدن Agent می‌شود؟

برای کارهای کم‌خطر می‌توان اجازه اجرای خودکار تعریف کرد. HITL قرار نیست هر پاسخ ساده را به تأیید انسان وابسته کند؛ هدف، کنترل عملیات‌هایی است که پیامد امنیتی، مالی یا اجرایی مهم دارند.

بنابراین طراحی درست باید میان سرعت خودکارسازی و میزان خطر عملیات تعادل برقرار کند.

تفاوت Human-in-the-Loop با Guardrails و Permission چیست؟

این سه مفهوم مکمل یکدیگر هستند و نباید جایگزین هم در نظر گرفته شوند.

کنترل وظیفه مثال
Guardrail بررسی ورودی، خروجی یا فراخوانی Tool رد درخواست دارای الگوی محرمانه
Authentication شناسایی درخواست‌کننده تأیید هویت اپراتور
Authorization بررسی مجوز انجام عملیات اجازه Restart فقط برای سرور متعلق به کاربر
Human Approval دریافت تصمیم صریح برای یک عملیات مشخص تأیید Restart همین سرور
Idempotency جلوگیری از اثر تکراری عدم اجرای دوباره Restart منطقی
Audit ثبت وقایع برای پیگیری چه کسی، چه زمانی و چه چیزی را تأیید کرد

برای مثال، کاربری که مالک یک VPS است ممکن است مجوز Restart آن را داشته باشد؛ اما سیاست سامانه همچنان ایجاب کند قبل از Restart، تأیید صریح همان کاربر دریافت شود.

از طرف دیگر، اگر کاربر اصلاً مجوز دسترسی به VPS دیگری را ندارد، تأیید او نباید باعث مجاز شدن عملیات شود.

اصل مهم: تأیید انسانی، مجوز جدید و نامحدود ایجاد نمی‌کند؛ بلکه یک شرط اضافی برای عملیاتی است که از نظر سیاست دسترسی قابل انجام است.

کدام عملیات Agent باید تأیید انسانی داشته باشند؟

قبل از پیاده‌سازی HITL، بهتر است ابزارهای Agent را براساس میزان خطر دسته‌بندی کنید.

سطح خطر نمونه Tool سیاست پیشنهادی
کم خواندن راهنمای عمومی اجرای خودکار با کنترل دسترسی
متوسط ویرایش پیش‌نویس داخلی بررسی سیاست و در صورت نیاز تأیید
زیاد ارسال ایمیل، انتشار محتوا، Restart سرور تأیید صریح برای هر عملیات مهم
بحرانی پرداخت، حذف Backup، تغییر Firewall، حذف دیتابیس احراز هویت قوی‌تر، تأیید ویژه و کنترل اجرایی مستقل

چرا طبقه‌بندی ریسک مهم است؟

اگر همه ابزارها نیازمند تأیید باشند، سیستم برای کارهای ساده نیز کند و پرهزینه می‌شود. اگر هیچ ابزاری نیازمند تأیید نباشد، خطای مدل یا Prompt Injection ممکن است پیامد جدی ایجاد کند.

در معماری مناسب، ابزارهای خواندنیِ مجاز می‌توانند خودکار اجرا شوند؛ اما عملیات پرخطر قبل از انجام متوقف می‌شوند.

آیا نام Tool معیار خوبی برای تشخیص خطر است؟

خیر. نام‌هایی مانند safe_action یا read_only تضمین نمی‌کنند که ابزار واقعاً بی‌خطر است.

رفتار واقعی ابزار، نوع داده‌های قابل دسترسی، پیامد عملیات و مجوزهای Backend باید مبنای طبقه‌بندی باشند.

معماری Human-in-the-Loop روی VPS چگونه است؟

برای ساخت یک سیستم قابل اعتماد، تصمیم مدل و اجرای عملیات حساس را از یکدیگر جدا می‌کنیم.

User Request
     |
OpenAI Agent
     |
Sensitive Tool Call
     |
Approval Gate
     |
Pause + Serialize RunState
     |
Trusted Approval Store
     |
Human Reviewer
   /       \
Approve    Reject / Expire
   |           |
Authorization  Resume with rejection
   |
Resume RunState
   |
Backend Revalidates Approval
   |
Idempotent Tool Execution
   |
Audit + Result

بخش اول: Agent

Agent وظیفه تحلیل درخواست و پیشنهاد فراخوانی ابزار را بر عهده دارد. مدل نباید به‌تنهایی تصمیم بگیرد که تأیید کاربر انجام شده است.

بخش دوم: Approval Store

درخواست‌های تأیید، پارامترهای عملیات، زمان انقضا و تصمیم اپراتور در یک محل پایدار مانند دیتابیس ذخیره می‌شوند.

بخش سوم: رابط تأییدکننده

اپراتور فقط جزئیات مجاز و لازم برای تصمیم‌گیری را مشاهده می‌کند. شناسه Run یا یک لینک تصادفی به‌تنهایی اثبات‌کننده مجوز تأیید نیست.

بخش چهارم: Backend یا Tool Executor

در این بخش، حتی پس از Resume شدن SDK، مجوز و تأیید همان عملیات دوباره بررسی می‌شود.

اگر تأیید منقضی شده باشد یا پارامترهای Tool تغییر کرده باشند، اجرای عملیات رد خواهد شد.

بخش پنجم: Audit و Recovery

سیستم ثبت می‌کند درخواست چه زمانی ایجاد شد، چه کسی آن را تأیید یا رد کرد و آیا عملیات واقعاً انجام شد یا خیر.

همچنین اگر فرآیند هنگام اجرا متوقف شود، وضعیت نامطمئن باید برای بررسی باقی بماند؛ نه اینکه هر عملیات حساسی به‌صورت کورکورانه تکرار شود.

Human-in-the-Loop در OpenAI Agents SDK چگونه پیاده می‌شود؟

OpenAI Agents SDK دارای امکانات داخلی برای توقف اجرای Tool پیش از انجام عملیات است.

در نسخه پایدار مورد استفاده مقاله، مهم‌ترین اجزای این قابلیت عبارت‌اند از:

  • needs_approval: مشخص می‌کند یک فراخوانی Tool به تأیید نیاز دارد.
  • RunResult.interruptions: درخواست‌های تأیید معلق را در اختیار برنامه قرار می‌دهد.
  • RunState: وضعیت اجرای متوقف‌شده را برای ذخیره و بازیابی فراهم می‌کند.
  • state.approve: تأیید یک فراخوانی مشخص را ثبت می‌کند.
  • state.reject: همان فراخوانی را رد می‌کند.
  • Runner.run(agent, state): اجرای Agent را از وضعیت ذخیره‌شده ادامه می‌دهد.

تعریف ساده Tool نیازمند تأیید

from agents import Agent, Runner
from agents.decorators import tool

@tool(needs_approval=True)
def change_demo_setting(setting: str) -> str:
    """Demonstration only; not a real system operation."""
    return f"Demo setting changed: {setting}"

agent = Agent(
    name="Approval Demo",
    model="gpt-6-luna",
    instructions="Use the tool only for the requested demo change.",
    tools=[change_demo_setting],
)

با این تنظیم، اگر مدل ابزار change_demo_setting را فراخوانی کند، Runner پیش از اجرای تابع متوقف می‌شود و یک درخواست تأیید ایجاد می‌کند.

دریافت درخواست تأیید

result = await Runner.run(
    agent,
    "تنظیم آزمایشی را تغییر بده."
)

if result.interruptions:
    state = result.to_state()
    saved_state = state.to_json()

مقدار saved_state باید در محل ذخیره‌سازی مورد اعتماد نگهداری شود. این Snapshot ممکن است شامل وضعیت اجرای Agent، پارامتر Tool و تصمیم‌های تأیید باشد.

تأیید یا رد و ادامه اجرا

from agents import RunState

state = await RunState.from_json(
    agent,
    saved_state,
)

pending = state.get_interruptions()

# Only after a trusted reviewer authorizes this exact call:
state.approve(
    pending[0],
    always_approve=False,
)

result = await Runner.run(
    agent,
    state,
)

این قطعه فقط جریان SDK را نمایش می‌دهد. نباید به‌عنوان Endpoint عمومی برای تأیید مستقیم استفاده شود. ابتدا باید هویت تأییدکننده، مجوز، زمان انقضا و پارامترهای دقیق عملیات بررسی شوند.

تفاوت تأیید همان فراخوانی و تأیید دائمی ابزار

در حالت معمول بهتر است از تأیید همان Tool Call استفاده کنید. گزینه always_approve=True می‌تواند باعث شود تصمیم تأیید در وضعیت اجرایی برای فراخوانی‌های بعدی همان هویت ابزار نیز معتبر بماند.

برای ابزارهای حساس، استفاده از تأیید دائمی ممکن است خطرناک باشد؛ به‌خصوص اگر چند Agent یا Executor با مجوزهای متفاوت از ابزارهای مشابه استفاده کنند.

در نمونه عملی این مقاله، تأیید هر فراخوانی مستقل انجام می‌شود و always_approve روی False باقی می‌ماند.

آیا تأیید در Handoff نیز کار می‌کند؟

بله. طبق مستندات SDK، اگر درخواست تأیید در یک Agent متخصص یا یک Agent تو در تو ایجاد شود، می‌تواند در جریان اجرای اصلی ظاهر شود.

در این حالت تصمیم‌ها باید روی RunState همان گردش کار اصلی اعمال شوند و اجرای اصلی Resume شود.

پیش‌نیازها و منابع VPS برای اجرای Approval Agent

برای پروژه آموزشی از Ubuntu 24.04 LTS، Python و SQLite استفاده می‌کنیم. پردازش مدل زبانی از طریق OpenAI API انجام می‌شود؛ بنابراین برای این نمونه GPU لازم نیست.

نوع استفاده RAM پیشنهادی CPU پیشنهادی
نمونه محلی و آموزشی ۱ تا ۲ گیگابایت ۱ vCPU
Agent دارای API و دیتابیس ۲ تا ۴ گیگابایت ۲ vCPU
چند Worker و ابزارهای محلی براساس تست بار براساس مصرف واقعی

این مقادیر پیشنهاد اولیه‌اند و حداقل رسمی OpenAI نیستند. ظرفیت موردنیاز با تعداد کاربران، حجم داده‌های ذخیره‌شده و نوع Toolها تغییر می‌کند.

ابزارهای پروژه

  • Python 3.12 در Ubuntu 24.04.
  • OpenAI Agents SDK نسخه 0.24.0.
  • SQLite برای نگهداری Runها و تأییدها.
  • یک رابط CLI مورد اعتماد برای اپراتور.
  • systemd برای مدیریت زمان‌بندی و اجرای سرویس‌های موردنیاز.

در این آموزش از یک رابط CLI استفاده می‌کنیم تا کلیدهای تأیید و Snapshotهای Agent در معرض یک وب‌سایت عمومی قرار نگیرند. برای ساخت رابط وب چندکاربره باید احراز هویت، RBAC، CSRF Protection و کنترل‌های اضافی اضافه شوند.

مرحله اول: نصب پروژه Human-in-the-Loop روی Ubuntu VPS

ابتدا با SSH به سرور متصل شوید و بسته‌های لازم را نصب کنید:

sudo apt update
sudo apt install -y python3 python3-venv python3-pip

ساخت کاربر مستقل برای سرویس

بهتر است Agent با حساب root اجرا نشود. برای نصب جدید می‌توان یک حساب اختصاصی ایجاد کرد:

sudo useradd --system --create-home \
--home-dir /opt/iranica-hitl \
--shell /usr/sbin/nologin hitlagent

sudo install -d -o hitlagent -g hitlagent -m 750 \
/var/lib/iranica-hitl

اگر کاربر یا مسیر مربوطه از قبل وجود دارد، این مرحله را بدون بررسی تکرار نکنید.

آماده‌سازی فایل‌های پروژه

بسته سورس همراه مقاله شامل سه فایل است:

فایل کاربرد
hitl_demo.py تعریف Agent، ذخیره تأییدها، اجرای Tool، Audit و Rollback
requirements.txt وابستگی و نسخه SDK
README.md دستورات نصب و تست سناریوهای عملی

فایل‌های پروژه را در مسیر /opt/iranica-hitl قرار دهید و مالکیت آن‌ها را برای کاربر hitlagent تنظیم کنید.

ساخت محیط مجازی

sudo -u hitlagent python3 -m venv \
/opt/iranica-hitl/.venv

sudo -u hitlagent /opt/iranica-hitl/.venv/bin/python \
-m pip install -r /opt/iranica-hitl/requirements.txt

نسخه SDK در فایل requirements.txt مشخص شده است. برای محیط عملیاتی بهتر است بعد از تست، نسخه وابستگی‌های غیرمستقیم را نیز قفل کنید.

تنظیم اطلاعات محیطی

برای اجرای آزمایشی، برنامه به OpenAI API Key معتبر نیاز دارد. بهتر است اطلاعات محرمانه در فایل محافظت‌شده نگهداری شوند.

مقادیر محیطی اصلی عبارت‌اند از:

OPENAI_API_KEY=YOUR_OPENAI_API_KEY
AGENT_MODEL=gpt-6-luna
HITL_DB=/var/lib/iranica-hitl/hitl.sqlite3
APPROVAL_TTL_SECONDS=600
HITL_REVIEWER=local-operator

مقدار HITL_REVIEWER در این نمونه فقط برچسب اپراتور محلی است و به‌تنهایی یک سیستم احراز هویت محسوب نمی‌شود.

مرحله دوم: ساخت Tool حساس با دو لایه تأیید

سناریوی عملی ما تغییر وضعیت یک سرویس کاملاً آزمایشی با شناسه demo-vps-01 است.

سرویس دو وضعیت دارد: active و maintenance. عملیات هیچ سرور واقعی را تغییر نمی‌دهد و فقط یک رکورد SQLite را به‌روزرسانی می‌کند.

لایه اول: توقف Tool در Agents SDK

در سورس پروژه از دکوراتور زیر استفاده کرده‌ایم:

@tool(needs_approval=True)
def set_demo_mode(
    ctx: RunContextWrapper[dict],
    service_id: str,
    new_mode: Literal["active", "maintenance"],
) -> str:
    """Change a demo service only after approval."""
    ...

این تنظیم باعث می‌شود Runner پیش از اجرای تابع، درخواست تأیید ایجاد کند.

لایه دوم: بررسی تأیید در خود Tool

علاوه بر توقف SDK، تابع اجرایی باید رکورد تأیید معتبر را از دیتابیس بخواند.

در پروژه، شرایط زیر بررسی می‌شوند:

  • شناسه Run از Context مورد اعتماد استخراج شده باشد.
  • درخواست تأیید با همان Run مرتبط باشد.
  • وضعیت درخواست approved باشد.
  • زمان انقضای تأیید هنوز نگذشته باشد.
  • نام ابزار دقیقاً با ابزار تأییدشده برابر باشد.
  • Hash پارامترهای فعلی با پارامترهای تأییدشده مطابقت داشته باشد.
  • سرویس هدف در Allowlist آزمایشی قرار داشته باشد.
  • همان عملیات قبلاً اثر تکراری ایجاد نکرده باشد.

نمونه بخش اصلی کنترل مستقل:

approval = db.execute(
    "SELECT * FROM approvals WHERE run_id=?",
    (run_id,),
).fetchone()

if not approval or approval["status"] != "approved":
    raise PermissionError("Approval missing")

if approval["expires_at"] <= int(time.time()):
    raise PermissionError("Approval expired")

if approval["args_hash"] != digest(requested):
    raise PermissionError("Action parameters changed")

این قطعه بخشی از تابع کامل داخل سورس است. در نسخه کامل، بررسی نام Tool، محدودیت سرویس و ثبت اثر عملیات نیز وجود دارد.

چرا دو لایه کنترل لازم است؟

لایه SDK به مدیریت تعامل Agent و توقف گردش کار کمک می‌کند. لایه Backend مرز امنیتی مستقل‌تری ایجاد می‌کند تا حتی در صورت خطای منطق Resume یا پیکربندی، عملیات بدون وضعیت تأیید معتبر اجرا نشود.

در سیستم‌های مالی و مدیریتی، مجوز واقعی منبع هدف نیز باید در Backend کنترل شود.

مرحله سوم: ذخیره Approval State و RunState در دیتابیس

اگر درخواست تأیید فقط در RAM نگهداری شود، با Restart برنامه از بین می‌رود.

برای همین دو نوع اطلاعات را به‌صورت جداگانه ذخیره می‌کنیم: Snapshot اجرای Agent و وضعیت تصمیم تأییدکننده.

ساختار منطقی دیتابیس

جدول وظیفه
runs وضعیت Run و Snapshot ذخیره‌شده
approvals جزئیات و نتیجه تأیید
demo_services وضعیت سرویس آزمایشی
effects ثبت اثر واقعی عملیات و امکان جبران
audit تاریخچه رخدادها و تصمیم‌ها

نمونه جدول Approval

CREATE TABLE IF NOT EXISTS approvals (
    run_id TEXT PRIMARY KEY,
    tool_name TEXT NOT NULL,
    args_hash TEXT NOT NULL,
    args_json TEXT NOT NULL,
    status TEXT NOT NULL,
    expires_at INTEGER NOT NULL,
    reviewed_by TEXT,
    decided_at INTEGER
);

کلید run_id در این نمونه یکتا است؛ زیرا پروژه آموزشی عمداً هر Run را به حداکثر یک درخواست تأیید محدود می‌کند.

در سیستم واقعی، ممکن است هر Run شامل چند Tool Call باشد. در آن صورت باید برای هر Call شناسه مستقل، وضعیت و تصمیم جداگانه داشته باشید.

وضعیت‌های Approval

وضعیت معنا
pending هنوز تصمیمی گرفته نشده است
approved اپراتور تأیید کرده، ولی اثر Tool هنوز لزوماً اجرا نشده است
rejected اپراتور عملیات را رد کرده است
expired مهلت تصمیم تمام شده است
consumed تأیید در اجرای عملیات مصرف شده است

تفاوت approved و consumed مهم است. ممکن است اپراتور عملیات را تأیید کرده باشد، اما اجرای Tool به دلیل خطا یا توقف فرآیند هنوز انجام نشده باشد.

RunState چه زمانی ذخیره می‌شود؟

در لحظه‌ای که Runner درخواست تأیید تولید می‌کند:

state_json = json.dumps(
    result.to_state().to_json(),
    ensure_ascii=False,
)

این داده در جدول runs نگهداری می‌شود و به مرورگر یا کاربر غیرمجاز ارسال نمی‌شود.

RunState ممکن است شامل اطلاعات حساس اجرایی، پارامترهای ابزار و تصمیم‌های تأیید باشد. بنابراین باید مانند داده داخلی مورد اعتماد محافظت شود.

مرحله چهارم: ایجاد درخواست تأیید و Pause کردن Agent

پس از آماده شدن پروژه و تنظیم متغیرهای محیطی، ابتدا دیتابیس آزمایشی را ایجاد می‌کنیم.

cd /opt/iranica-hitl

.venv/bin/python hitl_demo.py init

این دستور جداول لازم و سرویس آزمایشی demo-vps-01 را ایجاد می‌کند.

درخواست تغییر وضعیت سرویس

.venv/bin/python hitl_demo.py start \
'برای demo-vps-01 حالت maintenance را فعال کن.'

برنامه یک Run ID ایجاد می‌کند و درخواست را به Agent می‌فرستد.

اگر مدل ابزار set_demo_mode را انتخاب کند، Runner پیش از اجرای تابع متوقف می‌شود. در این حالت وضعیت Run برابر waiting_approval خواهد بود.

نمایش درخواست‌های منتظر تأیید

.venv/bin/python hitl_demo.py pending

در نتیجه، نام Tool، پارامترهای عملیاتی، وضعیت درخواست و زمان انقضا قابل مشاهده خواهند بود.

نمونه درخواست

{
  "tool_name": "set_demo_mode",
  "args_json": {
    "service_id": "demo-vps-01",
    "new_mode": "maintenance"
  },
  "status": "pending"
}

در نسخه کامل، مقدار expires_at نیز به‌صورت Unix Timestamp ذخیره می‌شود.

تا این مرحله نباید وضعیت سرویس تغییر کرده باشد. این همان رفتار مورد انتظار از یک Approval Gate صحیح است.

مرحله پنجم: تأیید، رد و Resume کردن Agent

اکنون اپراتور باید جزئیات درخواست را بررسی کند و تصمیم بگیرد.

در این نمونه، فرمان‌های تأیید فقط از طریق CLI محلی در اختیار اپراتور مورد اعتماد هستند. این طراحی را نباید بدون احراز هویت اضافی به یک Endpoint عمومی تبدیل کرد.

تأیید عملیات

.venv/bin/python hitl_demo.py decide RUN_ID approve

به‌جای RUN_ID، شناسه واقعی ایجادشده توسط برنامه را وارد کنید.

این دستور فقط تصمیم تأیید را در پایگاه داده ثبت می‌کند. هنوز لازم است اجرای Agent از وضعیت ذخیره‌شده ادامه داده شود.

ادامه اجرای Agent

.venv/bin/python hitl_demo.py resume RUN_ID

در این مرحله RunState از دیتابیس بازیابی می‌شود و تصمیم تأیید به همان Tool Call اعمال خواهد شد.

اگر کنترل‌های Backend نیز موفق باشند، وضعیت سرویس آزمایشی تغییر می‌کند و اثر آن در دیتابیس ثبت می‌شود.

رد عملیات

اگر اپراتور درخواست را مناسب نداند:

.venv/bin/python hitl_demo.py decide RUN_ID reject

.venv/bin/python hitl_demo.py resume RUN_ID

در این حالت تصمیم رد به RunState اعمال می‌شود و Agent می‌تواند به کاربر توضیح دهد که عملیات تأیید نشده است.

هیچ تغییر جدیدی نباید روی سرویس آزمایشی انجام شود.

اگر مدل بعد از Resume دوباره درخواست ابزار حساس بدهد چه می‌شود؟

در جریان‌های پیچیده، ممکن است پس از Resume یک درخواست تأیید جدید ایجاد شود. در این صورت، تصمیم قبلی نباید به‌طور خودکار جایگزین بررسی درخواست جدید شود.

نمونه آموزشی ما برای جلوگیری از پیچیدگی، بیش از یک درخواست تأیید در هر Run را پشتیبانی نمی‌کند و در صورت مشاهده جریان غیرمنتظره متوقف می‌شود.

در محصول واقعی، هر Tool Call باید شناسه مستقل و وضعیت مخصوص خود را داشته باشد.

مرحله ششم: تنظیم Timeout و انقضای درخواست تأیید

یک درخواست تأیید نباید برای همیشه معتبر بماند. ممکن است وضعیت سرور، مالکیت منبع یا شرایط عملیات بعد از چند ساعت تغییر کند.

برای همین باید مهلت مشخصی برای پاسخ اپراتور تعیین شود.

تنظیم زمان اعتبار

در پروژه، مدت اعتبار پیش‌فرض ۶۰۰ ثانیه است؛ یعنی ۱۰ دقیقه.

APPROVAL_TTL_SECONDS=600

این مقدار برای نمونه آموزشی انتخاب شده و سیاست رسمی ثابت برای تمام Agentها نیست.

اگر اپراتور دیر تأیید کند چه می‌شود؟

اگر زمان انقضا گذشته باشد، درخواست دیگر نباید تأیید مؤثر دریافت کند.

در این نمونه، تصمیم دیرهنگام به expired تبدیل می‌شود و اجرای حساس مجاز نخواهد شد.

پردازش درخواست‌های منقضی

.venv/bin/python hitl_demo.py expire

این دستور درخواست‌های pending منقضی‌شده را مشخص می‌کند. سپس می‌توان Run مربوطه را با تصمیم رد ادامه داد تا وضعیت منطقی آن بسته شود.

تفاوت Approval Timeout با Tool Timeout

Approval Timeout مهلت پاسخ انسان است. Tool Timeout حداکثر زمانی است که اجرای خود ابزار اجازه دارد مصرف کند.

ممکن است تأیید تا ۱۰ دقیقه اعتبار داشته باشد، اما اجرای یک درخواست HTTP فقط چند ثانیه مهلت داشته باشد.

این دو محدودیت را باید جداگانه طراحی کنید.

آیا تأیید معتبر در زمان شروع، تا پایان عملیات معتبر است؟

این موضوع به سیاست برنامه بستگی دارد. در نمونه ما، اعتبار تأیید درست پیش از اجرای اثر جانبی دوباره بررسی می‌شود.

برای عملیات طولانی یا چندمرحله‌ای، ممکن است لازم باشد تأیید به یک Operation ID مشخص متصل شود و اجرای ادامه مراحل تحت یک قرارداد اجرایی معتبر قرار بگیرد.

مرحله هفتم: جلوگیری از اجرای تکراری Tool پس از تأیید

فرض کنید اپراتور یک عملیات را تأیید کرده و Tool آن را با موفقیت اجرا کرده است. درست پس از انجام عملیات، فرآیند Agent متوقف می‌شود و پاسخ نهایی ثبت نمی‌شود.

اگر برنامه همان RunState قبلی را دوباره Resume کند، ممکن است Tool دوباره فراخوانی شود.

در عملیات‌هایی مانند Restart سرور، ارسال ایمیل، ثبت سفارش یا پرداخت، تکرار ناخواسته می‌تواند مشکل‌ساز باشد.

استفاده از شناسه یکتای عملیات

در پروژه عملی، برای هر Run فقط یک رکورد اثر در جدول effects ذخیره می‌شود.

CREATE TABLE IF NOT EXISTS effects (
    run_id TEXT PRIMARY KEY,
    service_id TEXT NOT NULL,
    old_mode TEXT NOT NULL,
    new_mode TEXT NOT NULL,
    applied_version INTEGER NOT NULL,
    compensated_at INTEGER
);

کلید یکتای run_id از ثبت اثر مستقل دوم برای همان Run جلوگیری می‌کند.

بررسی اجرای قبلی

قبل از ایجاد اثر جدید، Tool بررسی می‌کند آیا اثر مربوط به همان Run قبلاً ثبت شده است یا خیر.

previous = db.execute(
    "SELECT * FROM effects WHERE run_id=?",
    (run_id,),
).fetchone()

if previous:
    if (
        previous["service_id"] == service_id
        and previous["new_mode"] == new_mode
    ):
        return "Already processed; no extra change made."

    raise RuntimeError("Conflicting replay")

اگر اثر قبلی با همین پارامترها وجود داشته باشد، برنامه تغییر جدیدی اعمال نمی‌کند. اگر پارامترهای آن متفاوت باشند، اجرای دوباره رد می‌شود.

آیا این روش برای پرداخت واقعی کافی است؟

خیر. در این مثال، تغییر سرویس و ثبت اثر در یک دیتابیس SQLite انجام می‌شوند و می‌توان آن‌ها را در یک تراکنش اتمیک کنترل کرد.

اگر عملیات به یک API خارجی ارسال شود، ممکن است آن API اثر را ثبت کند ولی برنامه محلی قبل از ذخیره نتیجه Crash کند.

در این شرایط باید API مقصد از Idempotency Key معتبر پشتیبانی کند یا نتیجه از طریق فرآیند Reconciliation بررسی شود.

بنابراین تأیید SDK، رکورد محلی و یک Header دلخواه به‌تنهایی تضمین Exactly Once در چند سیستم مستقل ایجاد نمی‌کنند.

مرحله هشتم: ثبت Audit Log برای تمام تصمیم‌های انسانی

وقتی یک Agent می‌تواند عملیات مهم انجام دهد، لازم است مشخص باشد چه کسی درخواست را ایجاد کرده، چه تصمیمی گرفته شده و نتیجه اجرا چیست.

برای این کار از Audit Log استفاده می‌کنیم.

رویدادهایی که پروژه ثبت می‌کند

رویداد معنا
run_started گردش کار جدید ایجاد شده است
approval_requested Agent پیش از اجرای Tool متوقف شده است
approval_approved اپراتور درخواست را تأیید کرده است
approval_rejected اپراتور درخواست را رد کرده است
approval_expired مهلت تصمیم تمام شده است
resume_claimed ادامه اجرا توسط فرآیند دریافت شده است
tool_applied اثر آزمایشی Tool ثبت شده است
run_completed اجرای Agent پایان یافته است
resume_needs_reconciliation بازیابی به بررسی نتیجه نیاز دارد
compensation_applied عملیات جبرانی انجام شده است

نمایش تاریخچه یک Run

.venv/bin/python hitl_demo.py audit RUN_ID

این خروجی به اپراتور نشان می‌دهد درخواست چه مراحلی را طی کرده است.

آیا باید تمام Promptها و پاسخ‌های مدل را ثبت کنیم؟

خیر. ممکن است محتوای گفتگو یا خروجی Tool شامل اطلاعات خصوصی یا محرمانه باشد.

بهتر است Audit به اطلاعات ضروری مانند شناسه Run، نام Tool، هویت واقعی تأییدکننده، زمان، تصمیم و نتیجه محدود شود.

برای محیط‌های حساس، حفاظت از لاگ‌ها در برابر دستکاری و ارسال آن‌ها به یک مقصد مستقل نیز اهمیت دارد.

آیا Audit Log همان Tracing است؟

خیر. Tracing برای تحلیل مسیر اجرای Agent و رفتار Toolها مفید است، اما Audit باید به پرسش‌های امنیتی و عملیاتی پاسخ دهد: چه کسی اجازه داده و چه اثری واقعاً ایجاد شده است؟

برای فعالیت‌های حساس بهتر است Audit مستقل از Tracing مدل طراحی شود.

مرحله نهم: Rollback و جبران عملیات انجام‌شده

حتی اگر یک عملیات با تأیید معتبر اجرا شده باشد، ممکن است بعداً مشخص شود باید به وضعیت قبلی بازگردد.

در سیستم‌های پایدار باید بین Rollback واقعی تراکنش و Compensating Action تفاوت قائل شویم.

Rollback در یک تراکنش دیتابیس

اگر تغییر هنوز Commit نشده باشد، می‌توان تراکنش را Rollback کرد و از ثبت آن جلوگیری کرد.

Compensation پس از Commit

اگر تغییر انجام و ثبت شده باشد، بازگرداندن آن معمولاً یک عملیات جدید است.

برای مثال، اگر حالت سرویس از active به maintenance تغییر کرده باشد، می‌توان یک عملیات جبرانی انجام داد تا حالت قبلی بازگردد.

اجرای عملیات جبرانی در پروژه

.venv/bin/python hitl_demo.py rollback RUN_ID

این فرمان برای اپراتور محلی مورد اعتماد طراحی شده است. در یک محصول واقعی، خود عملیات Rollback نیز ممکن است به مجوز و تأیید ویژه نیاز داشته باشد.

کنترل تغییرات بعدی

فرض کنید Agent وضعیت سرویس را تغییر داده، اما بعد از آن یک مدیر دیگر تنظیمات همان سرویس را تغییر داده است.

در چنین شرایطی نباید بدون بررسی، مقدار قدیمی را روی تنظیمات جدید بازنویسی کرد.

در کد پروژه، هر تغییر یک شماره نسخه دارد. Compensation فقط در صورتی مجاز است که وضعیت و نسخه فعلی با نسخه ثبت‌شده پس از عملیات موردنظر مطابقت داشته باشند.

چه عملیات‌هایی Rollback واقعی ندارند؟

عملیات امکان بازگردانی
تغییر یک مقدار محلی قابل نسخه‌بندی معمولاً با عملیات جبرانی
Restart یک سرویس نمی‌توان قطع ارتباط گذشته را حذف کرد
ارسال ایمیل ایمیل ارسال‌شده لزوماً قابل پس‌گرفتن نیست
انتشار عمومی محتوا امکان حذف وجود دارد، ولی انتشار اولیه رخ داده است
انتقال وجه ممکن است نیازمند تراکنش بازگشت مستقل باشد
حذف Backup بدون نسخه جایگزین ممکن است غیرقابل بازگشت باشد

بنابراین Rollback نباید به‌عنوان تضمین حذف کامل پیامد یک عملیات معرفی شود.

الگوی Saga برای گردش کار چندمرحله‌ای

در گردش کارهای دارای چند سرویس مستقل، می‌توان از الگوی Saga استفاده کرد. در این روش، برای مرحله‌هایی که امکان جبران دارند، یک Compensating Action تعریف می‌شود.

اگر مرحله بعدی شکست بخورد، سیستم تلاش می‌کند اثر مرحله‌های قبلی را به‌شکل کنترل‌شده جبران کند.

اما عملیات جبرانی نیز ممکن است شکست بخورد؛ بنابراین باید وضعیت‌های نیازمند رسیدگی انسانی و Reconciliation وجود داشته باشند.

چگونه مطمئن شویم تأییدکننده واقعاً مجاز است؟

یکی از اشتباهات رایج، اعتماد به شناسه کاربر یا نام اپراتوری است که از سمت مدل یا درخواست HTTP ارسال شده است.

برای مثال، پارامتر زیر هیچ اثباتی درباره هویت واقعی ندارد:

{
  "reviewer": "admin",
  "approved": true
}

اگر Client بتواند این مقادیر را دلخواه وارد کند، ممکن است هر کسی خودش را مدیر معرفی کند.

اصول احراز هویت در Approval API

  • هویت تأییدکننده باید از Session یا Token معتبر سمت سرور استخراج شود.
  • مجوز تأیید باید برای همان منبع و همان نوع عملیات بررسی شود.
  • برای عملیات بحرانی می‌توان Step-up Authentication یا MFA لازم دانست.
  • پارامترهای عملیات باید از Snapshot ذخیره‌شده سمت سرور خوانده شوند.
  • Client نباید بتواند RunState یا نام Tool تأییدشده را جایگزین کند.
  • تأیید باید یک‌بارمصرف و دارای مهلت مشخص باشد.
  • درخواست تأیید مجدد نباید باعث اجرای هم‌زمان یک Snapshot شود.

جلوگیری از Replay تأیید

اگر همان درخواست Approve دو بار به Backend ارسال شود، فقط اولین تصمیم معتبر باید موفق باشد.

در پروژه آموزشی، تغییر وضعیت Approval داخل یک تراکنش SQLite انجام می‌شود و تصمیم دوم دیگر روی درخواست pending اعمال نمی‌شود.

تأیید در پنل تحت وب

اگر برای اپراتورها رابط وب طراحی می‌کنید، بهتر است جزئیات عملیات را به‌صورت واضح نمایش دهید و دو گزینه تأیید و رد مستقل داشته باشید.

در این رابط باید علاوه بر احراز هویت و RBAC، حفاظت CSRF، محدودیت نرخ درخواست و تأیید دوباره عملیات‌های بحرانی را نیز در نظر بگیرید.

هرگز Snapshot کامل RunState را به مرورگر نفرستید تا بعداً از سمت Client به سرور بازگردانده شود.

تأیید Human-in-the-Loop در چند Agent و MCP چگونه است؟

در پروژه‌های بزرگ، Agent اصلی ممکن است درخواست را به یک Agent متخصص منتقل کند یا از MCP Server برای اجرای ابزار استفاده کند.

تأیید در Agent متخصص

اگر Tool حساس داخل یک Agent متخصص اجرا شود، SDK می‌تواند درخواست تأیید را در سطح اجرای اصلی گزارش کند.

پس از دریافت تصمیم، باید RunState همان اجرای اصلی Resume شود؛ نه اینکه یک گردش کار جدید بدون اطلاعات قبلی ایجاد کنید.

تأیید Agent as Tool

وقتی یک Agent به‌عنوان Tool در اختیار Agent دیگر قرار می‌گیرد، ممکن است تأیید در سطح فراخوانی Agent متخصص یا در سطح Toolهای داخلی آن لازم شود.

برای جلوگیری از مجوزهای ناخواسته، باید مشخص باشد تأیید دقیقاً به کدام فراخوانی و کدام سطح از گردش کار مربوط است.

تأیید Toolهای MCP

در Python Agents SDK، برای MCP Serverهای محلی می‌توان سیاست require_approval تعریف کرد. در Hosted MCP نیز قابلیت درخواست تأیید وجود دارد.

این قابلیت‌ها به مدیریت جریان تأیید کمک می‌کنند؛ اما MCP Server یا Backend مقصد باید همچنان هویت، Scope و مجوز واقعی عملیات را بررسی کند.

آیا Toolهای Shell و Computer Use نیز همین رفتار را دارند؟

برای ShellTool و ApplyPatchTool، تأیید در SDK قابل تنظیم است؛ اما در حالت محلی به‌صورت پیش‌فرض همیشه اجباری نیست. باید سیاست موردنظر را صریحاً فعال کنید.

برای ComputerTool، بررسی‌های ایمنی دارای Callback متفاوتی هستند و نباید فرض کرد تمام درخواست‌های Computer Use دقیقاً از همان مسیر RunState مربوط به Function Tool عبور می‌کنند.

به همین دلیل در طراحی یک سیستم حساس، سیاست اجرایی هر نوع ابزار باید جداگانه بررسی شود.

اگر Agent پس از تأیید و پیش از ثبت نتیجه Crash کند چه کنیم؟

این یکی از مهم‌ترین سناریوهای طراحی HITL است.

فرض کنید اپراتور عملیات را تأیید کرده، RunState بازیابی شده و Tool شروع به اجرا کرده است. حالا فرآیند Python یا VPS متوقف می‌شود.

آیا عملیات انجام شده است؟ ممکن است بله، ممکن است خیر.

چرا Resume خودکار همیشه امن نیست؟

اگر نتیجه Tool در Snapshot ثبت نشده باشد، اجرای دوباره از Snapshot قبلی ممکن است همان عملیات را تکرار کند.

در این وضعیت، تأیید معتبر قبلی به معنی امکان اجرای نامحدود همان اثر خارجی نیست.

رفتار نمونه آموزشی

در پروژه عملی، اگر ادامه اجرا با خطای غیرمنتظره مواجه شود، Run به وضعیت needs_review منتقل می‌شود.

در این وضعیت، برنامه عمداً از Retry خودکار عملیات حساس خودداری می‌کند.

اپراتور باید ابتدا جدول effects و وضعیت واقعی سرویس آزمایشی را بررسی کند.

رفتار پیشنهادی در Production

  1. شناسه یکتای عملیات را از دیتابیس بازیابی کنید.
  2. بررسی کنید آیا Backend مقصد اثر را ثبت کرده است.
  3. اگر نتیجه قطعی است، وضعیت محلی را با آن تطبیق دهید.
  4. اگر مقصد Idempotency معتبر دارد، از همان کلید برای بازیابی استفاده کنید.
  5. اگر نتیجه نامعلوم است، عملیات پرخطر را خودکار تکرار نکنید.
  6. وضعیت نیازمند رسیدگی انسانی را در Audit ثبت کنید.

این روش با مقاله حفظ State و بازیابی Agent بعد از Restart ارتباط دارد؛ اما در اینجا تمرکز روی حفظ اعتبار تأیید و جلوگیری از اجرای ناخواسته عملیات حساس است.

اجرای پایدار و مدیریت درخواست‌های منقضی با systemd

برای یک Agent که باید ساعت‌ها یا روزها منتظر تأیید بماند، لازم نیست فرآیند مدل در تمام این مدت فعال بماند.

درخواست تأیید و RunState در دیتابیس ذخیره می‌شوند و بعداً توسط اپراتور یا سرویس مورد اعتماد پردازش خواهند شد.

مدیریت انقضای درخواست‌ها

می‌توانید فرمان expire را با یک systemd Timer اجرا کنید تا درخواست‌های منقضی به‌صورت دوره‌ای بررسی شوند.

فایل سرویس نمونه:

[Unit]
Description=Expire pending Agent approvals

[Service]
Type=oneshot
User=hitlagent
Group=hitlagent
WorkingDirectory=/opt/iranica-hitl
EnvironmentFile=/etc/iranica-hitl/agent.env

ExecStart=/opt/iranica-hitl/.venv/bin/python /opt/iranica-hitl/hitl_demo.py expire

NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/var/lib/iranica-hitl

این محتوا را می‌توانید در فایل /etc/systemd/system/iranica-hitl-expire.service قرار دهید؛ البته مسیرها، کاربر و EnvironmentFile باید از قبل آماده باشند.

ساخت Timer

[Unit]
Description=Check expired Agent approvals periodically

[Timer]
OnBootSec=1min
OnUnitActiveSec=1min
Unit=iranica-hitl-expire.service

[Install]
WantedBy=timers.target

این محتوا در فایل /etc/systemd/system/iranica-hitl-expire.timer ذخیره می‌شود.

فعال‌سازی Timer

sudo systemctl daemon-reload
sudo systemctl enable --now iranica-hitl-expire.timer

sudo systemctl list-timers --all \
| grep iranica-hitl

با این روش لازم نیست مدیر مرتباً فرمان expire را اجرا کند.

با این حال، اجرای Timer فقط وضعیت درخواست‌های منقضی را به‌روزرسانی می‌کند. برای بستن Runهای متوقف‌شده، اجرای Resume با تصمیم رد نیز باید در گردش کار شما برنامه‌ریزی شود.

آیا این Timer تضمین می‌کند Tool منقضی اجرا نشود؟

خیر؛ Timer ممکن است با تأخیر اجرا شود. کنترل اصلی باید هنگام اجرای Tool و با بررسی زمان انقضای ذخیره‌شده انجام شود.

در پروژه ما این بررسی داخل خود Tool وجود دارد، بنابراین حتی اگر Timer هنوز اجرا نشده باشد، تأیید منقضی اجازه ایجاد اثر جدید نمی‌دهد.

سناریوهای تست Human-in-the-Loop قبل از انتشار

برای اینکه سیستم واقعاً از عملیات ناخواسته جلوگیری کند، باید فقط مسیر موفق را آزمایش نکنید؛ بلکه وضعیت‌های خطا، درخواست تکراری و تغییر پارامترها را نیز بررسی کنید.

تست نتیجه مورد انتظار
درخواست Tool حساس توقف قبل از اجرا
مشاهده Pending نمایش جزئیات دقیق عملیات
تأیید معتبر امکان Resume همان فراخوانی
رد درخواست عدم ایجاد اثر جانبی
تأیید بعد از انقضا رد عملیات
تغییر پارامتر پس از تأیید عدم تطابق Hash و رد اجرا
تأیید دوباره درخواست رد تصمیم تکراری
اجرای مجدد همان Tool عدم ایجاد اثر دوم
Crash هنگام Resume ثبت وضعیت نیازمند بررسی
Rollback بلافاصله پس از اجرا اجرای جبران کنترل‌شده
Rollback بعد از تغییر جدید رد جبران خودکار
دسترسی غیرمجاز به Snapshot عدم امکان دریافت یا جایگزینی Snapshot
نبود Audit یا خطای دیتابیس عدم اجرای عملیات حساس بدون ثبت معتبر

تست تغییر پارامترها

در محیط آزمایشی بررسی کنید که تأیید تغییر حالت demo-vps-01 به maintenance نتواند برای تغییر یک سرویس دیگر یا یک مقدار متفاوت استفاده شود.

برای این کار، نام Tool و Hash پارامترهای نرمال‌شده باید جزئی از قرارداد تأیید باشند.

تست انقضا

می‌توانید برای محیط آزمایش مقدار TTL را کوتاه تنظیم کنید و پس از انقضا تلاش کنید همان درخواست را تأیید و اجرا کنید.

نتیجه مورد انتظار، رد عملیات بدون تغییر وضعیت سرویس است.

تست اجرای هم‌زمان

در محیط تست، دو درخواست برای تأیید یا Resume همان Run ارسال کنید.

فقط یک درخواست باید بتواند وضعیت معتبر را مصرف کند. از این موضوع در سیستم چند Worker با تراکنش و قفل‌گذاری مناسب اطمینان پیدا کنید.

تست بعد از تغییر کد Agent

اگر RunStateها برای مدت طولانی ذخیره می‌شوند، ممکن است در زمان Resume نسخه SDK یا تعریف Toolها تغییر کرده باشد.

بهتر است نسخه برنامه، SDK و تعریف Agent مرتبط با هر Snapshot ثبت شود و قبل از انتشار نسخه جدید، بازیابی درخواست‌های معلق آزمایش شود.

خطاهای رایج هنگام ساخت Human-in-the-Loop Agent

۱. Tool بدون درخواست تأیید اجرا می‌شود

بررسی کنید needs_approval واقعاً برای Tool فعال شده است. برای بعضی نوع ابزارها، مانند ShellTool، تأیید محلی به‌صورت پیش‌فرض اجباری نیست.

همچنین مطمئن شوید مسیر اجرایی دیگری وجود ندارد که مستقیم Backend را بدون Approval Gate فراخوانی کند.

۲. result.interruptions خالی است

ممکن است مدل اصلاً Tool حساس را انتخاب نکرده باشد یا Run به پاسخ نهایی رسیده باشد.

در یک تست عملی باید بررسی کنید آیا Tool Call تولید شده است، نه اینکه همیشه فرض کنید Agent حتماً درخواست تأیید خواهد ساخت.

۳. RunState پس از Restart بازیابی نمی‌شود

ممکن است فایل یا رکورد Snapshot ذخیره نشده باشد، مجوز دسترسی وجود نداشته باشد یا نسخه کد با وضعیت ذخیره‌شده ناسازگار شده باشد.

Snapshot باید در محل پایدار و تحت کنترل سرور نگهداری شود.

۴. درخواست تأیید منقضی شده است

این رفتار ممکن است کاملاً طبیعی باشد. زمان انقضا را بررسی کنید و در صورت نیاز برای عملیات جدید، درخواست تأیید تازه ایجاد کنید.

تغییر دستی تاریخ انقضا برای دور زدن فرآیند بازبینی توصیه نمی‌شود.

۵. خطای Approval missing

Tool نتوانسته رکورد تأیید معتبر را برای Run فعلی پیدا کند.

شناسه Run، وضعیت Approval و نحوه بازیابی Context را بررسی کنید.

۶. خطای Action parameters changed

پارامترهای Tool با درخواست تأییدشده مطابقت ندارند.

این خطا باید به‌عنوان رد امنیتی در نظر گرفته شود؛ نه مشکلی که با غیرفعال کردن اعتبارسنجی Hash برطرف شود.

۷. درخواست تأیید دو بار ثبت می‌شود

ممکن است چند مسیر اجرایی یا چند Worker هم‌زمان تلاش کرده باشند وضعیت مشابهی ایجاد کنند.

در دیتابیس باید محدودیت یکتایی و تغییر وضعیت اتمیک وجود داشته باشد.

۸. Agent بعد از Reject دوباره درخواست Tool می‌دهد

ممکن است مدل بعد از دریافت پیام رد، راه دیگری برای انجام همان هدف پیشنهاد کند.

در سیستم حساس، رد عملیات باید در سیاست اجرایی و محدودیت گردش کار نیز منعکس شود. صرفاً پاسخ مدل نباید اجازه ایجاد مسیر جایگزین برای دور زدن رد را بدهد.

۹. خطای resume_needs_reconciliation

این وضعیت نشان می‌دهد ادامه اجرا با عدم قطعیت یا خطا مواجه شده است.

قبل از تلاش مجدد، اثر ثبت‌شده در Backend را بررسی کنید. انجام Retry خودکار برای عملیات حساس می‌تواند باعث اثر تکراری شود.

۱۰. Rollback انجام نمی‌شود

ممکن است وضعیت سرویس پس از عملیات اصلی تغییر کرده باشد و نسخه فعلی دیگر با نسخه ثبت‌شده مطابقت نداشته باشد.

در این حالت باید بررسی دستی انجام شود. بازنویسی اجباری وضعیت جدید می‌تواند تغییرات معتبر دیگر را از بین ببرد.

۱۱. چند درخواست تأیید هم‌زمان ایجاد می‌شود

پروژه آموزشی این مقاله فقط یک درخواست تأیید برای هر Run دارد.

برای سیستم چندابزاری باید شناسه مستقل هر Tool Call، نگهداری تصمیم‌ها و مدیریت Resume ناقص یا چندمرحله‌ای را پیاده‌سازی کنید.

۱۲. هزینه API بعد از Resume بیشتر از انتظار است

بعد از تأیید یا رد، Agent ممکن است برای تولید پاسخ نهایی دوباره مدل را فراخوانی کند. همچنین خطاها و تلاش‌های مجدد می‌توانند به مصرف بیشتر منجر شوند.

تعداد مراحل، مدل انتخابی و رخدادهای اجرای Agent را بررسی کنید.

چک‌لیست Production برای Human-in-the-Loop AI Agent

قبل از اتصال HITL به عملیات واقعی، موارد زیر را بررسی کنید:

  • ابزارهای حساس به‌صورت صریح شناسایی شده‌اند.
  • سیاست تأیید براساس ریسک واقعی عملیات است.
  • مدل نمی‌تواند خودش تأیید ایجاد کند.
  • تأیید در لایه Backend نیز کنترل می‌شود.
  • هویت تأییدکننده از منبع معتبر استخراج می‌شود.
  • مجوز تأیید همان منبع بررسی می‌شود.
  • جزئیات عملیات پیش از تأیید نمایش داده می‌شوند.
  • تأیید به نام Tool و پارامترهای دقیق متصل است.
  • تأییدها دارای زمان انقضا هستند.
  • تأیید یک‌بارمصرف است.
  • Resume هم‌زمان یک Snapshot مسدود شده است.
  • RunState فقط در محل مورد اعتماد ذخیره می‌شود.
  • Snapshot و Credentialها در Log عمومی قرار نمی‌گیرند.
  • برای عملیات خارجی Idempotency یا Reconciliation وجود دارد.
  • Crash در حین Tool به Retry کورکورانه منجر نمی‌شود.
  • Audit تمام تصمیم‌ها و نتایج مهم را ثبت می‌کند.
  • Rollback یا Compensation مناسب طراحی شده است.
  • برای عملیات غیرقابل بازگشت، کنترل شدیدتری وجود دارد.
  • تست‌های تأیید، رد، انقضا، Crash و Replay انجام شده‌اند.
  • نسخه SDK و قالب Snapshotهای ذخیره‌شده مدیریت می‌شوند.
  • منابع VPS، محدودیت درخواست‌ها و سیاست Backup مشخص هستند.

سؤالات متداول درباره Human-in-the-Loop AI Agent

Human in the Loop AI Agent چیست؟

روشی برای ساخت Agent است که اجرای بعضی تصمیم‌ها یا عملیات‌های حساس را به بررسی و تأیید انسان وابسته می‌کند.

آیا می‌توان Agent را قبل از اجرای Tool متوقف کرد؟

بله. در OpenAI Agents SDK می‌توانید برای Toolهای پشتیبانی‌شده از needs_approval استفاده کنید تا Runner پیش از اجرای ابزار متوقف شود.

Pause و Resume در Agents SDK چگونه کار می‌کنند؟

درخواست‌های تأیید در interruptions ظاهر می‌شوند، وضعیت با RunState ذخیره می‌شود و پس از تصمیم معتبر با Runner.run ادامه پیدا می‌کند.

آیا Agent پس از Restart همچنان می‌تواند منتظر تأیید بماند؟

بله. اگر RunState و درخواست تأیید در یک محل پایدار ذخیره شده باشند، می‌توان بعداً آن‌ها را بازیابی کرد.

آیا برای HITL باید مدل همیشه فعال باشد؟

خیر. وضعیت در انتظار تأیید می‌تواند در دیتابیس باقی بماند و اجرای مدل تنها در زمان نیاز ادامه پیدا کند.

آیا needs_approval=True امنیت کامل ایجاد می‌کند؟

خیر. این گزینه توقف در سطح SDK را فراهم می‌کند، اما مجوز واقعی، هویت تأییدکننده و کنترل عملیات در Backend همچنان ضروری هستند.

آیا مدل می‌تواند خودش عملیات را تأیید کند؟

در طراحی امن نباید چنین امکانی داشته باشد. تأیید باید از یک منبع مورد اعتماد و مستقل از خروجی مدل دریافت شود.

آیا هر Tool باید تأیید انسانی داشته باشد؟

خیر. ابزارهای کم‌خطر و فقط‌خواندنی می‌توانند با سیاست دسترسی مناسب خودکار اجرا شوند. ابزارهای پرخطر به کنترل‌های قوی‌تر نیاز دارند.

چگونه Approval را به پارامترهای دقیق متصل کنیم؟

با ذخیره نام Tool، شناسه منبع، پارامترهای نرمال‌شده و یک Hash معتبر در رکورد تأیید.

اگر تأیید منقضی شود چه اتفاقی می‌افتد؟

عملیات نباید اجرا شود. برنامه می‌تواند درخواست را رد کند و در صورت نیاز، درخواست جدیدی برای تصمیم‌گیری ایجاد کند.

آیا Reject باعث توقف کامل Agent می‌شود؟

بسته به گردش کار، Agent ممکن است پس از رد Tool پاسخ نهایی تولید کند یا مسیر مجاز دیگری پیشنهاد دهد. سیاست برنامه باید مانع دور زدن رد شود.

آیا می‌توان تأیید را برای تمام فراخوانی‌های بعدی حفظ کرد؟

SDK قابلیت تصمیم‌های ماندگار دارد، اما برای عملیات حساس معمولاً تأیید جداگانه هر فراخوانی امن‌تر است.

آیا HITL در Agentهای چندمرحله‌ای کار می‌کند؟

بله. درخواست‌های تأیید می‌توانند از Toolهای Agent اصلی، Agent متخصص یا Agent تو در تو ظاهر شوند.

آیا MCP Toolها نیز قابلیت تأیید دارند؟

بله. SDK برای بعضی انواع اتصال MCP امکان تعریف سیاست نیاز به تأیید را ارائه می‌کند.

آیا Computer Use از همین روش استفاده می‌کند؟

تمام مسیرهای Computer Use دقیقاً مانند Function Tool نیستند. برای بررسی‌های ایمنی ComputerTool، Callback اختصاصی وجود دارد.

چرا یک Approval ID به‌تنهایی کافی نیست؟

زیرا داشتن یک شناسه، مالکیت یا مجوز کاربر را اثبات نمی‌کند. هویت و سطح دسترسی باید در سمت سرور بررسی شوند.

چگونه جلوی اجرای دوباره عملیات پس از Crash را بگیریم؟

با Idempotency معتبر، ثبت نتیجه واقعی، Reconciliation و جلوگیری از Retry کورکورانه عملیات حساس.

آیا ذخیره RunState اجرای Exactly Once را تضمین می‌کند؟

خیر. RunState وضعیت Agent را نگهداری می‌کند، اما ممکن است عملیات خارجی پیش از ذخیره Snapshot جدید انجام شده باشد.

تفاوت Rollback و Compensation چیست؟

Rollback معمولاً به بازگردانی یک تراکنش ثبت‌نشده مربوط است؛ Compensation یک عملیات جدید برای جبران اثری است که قبلاً ثبت شده است.

آیا می‌توان پرداخت انجام‌شده را Rollback کرد؟

پرداخت واقعی ممکن است نیازمند تراکنش بازگشت جداگانه و مجوزهای مشخص باشد. نمی‌توان فرض کرد تمام پیامدها قابل حذف هستند.

Audit Log چه اطلاعاتی باید ثبت کند؟

شناسه Run، هویت تأییدکننده، نام عملیات، زمان، نتیجه مجوز، تصمیم و وضعیت اجرا، بدون ثبت غیرضروری Secrets.

آیا SQLite برای ذخیره Approval مناسب است؟

برای یک نمونه کوچک و تک‌سروری مناسب است. برای چند Worker و سرویس توزیع‌شده معمولاً به دیتابیس مشترک و کنترل هم‌زمانی قوی‌تر نیاز دارید.

آیا HITL به GPU روی VPS نیاز دارد؟

برای برنامه مبتنی بر OpenAI API معمولاً خیر. منابع بیشتر VPS عمدتاً به ظرفیت برنامه، دیتابیس و Workerهای محلی کمک می‌کنند.

چگونه امنیت HITL را تست کنیم؟

سناریوهای رد، انقضا، تأیید تکراری، تغییر پارامتر، Crash، Resume هم‌زمان، اجرای تکراری و Rollback را در محیط آزمایشی بررسی کنید.

جمع‌بندی

ساخت Human-in-the-Loop برای AI Agent یکی از مهم‌ترین روش‌ها برای کنترل عملیات حساس در سیستم‌های هوش مصنوعی است. با این الگو، Agent می‌تواند درخواست و پیشنهاد انجام کار را ایجاد کند، اما اجرای عملیات پرخطر تا دریافت تصمیم معتبر انسان متوقف می‌شود.

در این آموزش از قابلیت‌های داخلی OpenAI Agents SDK مانند needs_approval، interruptions و RunState استفاده کردیم و یک نمونه عملی ساختیم که درخواست تأیید را در SQLite ذخیره می‌کند، امکان Approve و Reject دارد، درخواست‌های منقضی را رد می‌کند و پس از Resume، مجوز همان عملیات را دوباره بررسی می‌کند.

همچنین یاد گرفتیم که تأیید انسانی به‌تنهایی از اجرای تکراری Tool جلوگیری نمی‌کند. برای جلوگیری از اثرهای ناخواسته باید Idempotency، ثبت نتیجه، مدیریت Crash و در صورت نیاز Reconciliation در سیستم وجود داشته باشد.

برای عملیات انجام‌شده نیز Rollback همیشه به معنی حذف پیامد گذشته نیست؛ در بسیاری از سناریوها باید از Compensating Action استفاده کرد.

اصل نهایی این است: مدل می‌تواند پیشنهاد اجرای عملیات را بدهد، اما اجازه واقعی اجرا باید توسط کد قابل اعتماد، سیاست دسترسی و تصمیم معتبر انسان تعیین شود.

اگر هنوز ساختار اولیه Agent را پیاده‌سازی نکرده‌اید، راهنمای ساخت AI Agent با OpenAI Agents SDK روی VPS نقطه شروع مناسبی است. برای نگهداری State و بازیابی گردش کار پس از Restart نیز مقاله AI Agent Persistence و Crash Recovery را مطالعه کنید.

منابع و مستندات رسمی

نمونه‌ها و توصیه‌های این راهنما با استفاده از مستندات رسمی OpenAI Agents SDK و منابع معتبر امنیت برنامه بررسی شده‌اند.

۱. OpenAI Agents SDK — Human-in-the-loop

راهنمای رسمی توقف اجرای Tool، مدیریت درخواست‌های تأیید و ادامه Agent از RunState.

مستندات Human-in-the-loop

۲. OpenAI Agents SDK — RunState Reference

مرجع ذخیره و بازیابی وضعیت، approve، reject، to_json و from_json.

مستندات RunState

۳. OpenAI Agents SDK — Tools

توضیح needs_approval، ابزارهای Python، ShellTool و ApplyPatchTool.

مستندات Tools

۴. OpenAI Agents SDK — نمونه تأیید سمت سرور

نمونه رسمی نگهداری Snapshot در سمت سرور، بررسی مالکیت و مصرف یک‌بارمصرف تصمیم.

مشاهده نمونه رسمی Python

۵. OpenAI Agents SDK — Releases

مرجع رسمی نسخه‌های پایدار و تغییرات مرتبط با تأیید ابزارها.

مشاهده نسخه‌های SDK

۶. OWASP — AI Agent Security Cheat Sheet

توصیه‌های امنیتی درباره تأیید عملیات حساس، مرزهای دسترسی و کنترل تصمیم‌های Agent.

راهنمای امنیت AI Agent در OWASP

۷. OWASP — Logging Cheat Sheet

راهنمای ثبت رخدادهای امنیتی، محافظت از Audit و جلوگیری از نشت داده‌های محرمانه در Log.

مستندات ثبت رویدادهای امنیتی

۸. OpenAI API — Models

فهرست مدل‌های API و بررسی مدل مناسب برای اجرای Agent.

مدل‌های رسمی OpenAI API

۹. SQLite — Transactions

مرجع تراکنش‌های اتمیک برای ثبت تصمیم‌های Approval و جلوگیری از اثرهای تکراری.

مستندات SQLite Transactions

۱۰. systemd — Timers

راهنمای رسمی اجرای دوره‌ای فرمان‌های مدیریت انقضا روی Linux.

مستندات systemd.timer

اشتراک‌گذاری مقاله
گفت‌وگو درباره آموزش

دیدگاه‌ها (۰)

هنوز دیدگاهی ثبت نشده است.

دیدگاه یا سؤال خود را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *