فاز 14 · درس 37

حلقه‌های بازخورد محیط اجرا: ثبت پایدار دستورات در ایجنت‌ها

نوع
ساختنی
زمان
حدود 50 دقیقه
زبان‌ها
Python (stdlib)
پیش‌نیاز
فاز 14 · 32 (میز کار مینیمال)، فاز 14 · 35 (اسکریپت مقداردهی اولیه)
یک ترمینال و حسگر فلزی که خروجی دستورات شل را دریافت کرده و به یک نوار متنی ثبت سوابق ساختاریافته متصل می‌کند
فهرست مطالب
  1. اهداف یادگیری
  2. صورت مسئله
  3. مفهوم اصلی
  4. چه مواردی داخل رکورد بازخورد قرار می‌گیرد
  5. هرس کردن قطعی است
  6. بازخورد در برابر تله‌متری
  7. خودداری از پیشروی در نبود بازخورد
  8. پیاده‌سازی از صفر
  9. الگوهای محیط عملیاتی در دنیای واقعی
  10. استفاده از ابزارهای واقعی
  11. خروجی این درس
  12. تمرین‌ها
  13. اصطلاحات کلیدی
  14. برای مطالعه‌ی بیشتر

یکی از باگ‌های رایج در توسعه‌ی سیستم‌های مبتنی بر ایجنت (Agentic Systems) این است که مدل‌ها گاهی بدون اجرای واقعی دستور یا بدون خواندن دقیق خروجی، ادعا می‌کنند کار انجام شده است. وقتی ایجنت به خروجی واقعی شل دسترسی نداشته باشد، حدس می‌زند و توهم نتایج تست‌ها یا عملیات‌ها را به عنوان فکت تحویل می‌دهد.

در این درس یاد می‌گیرید چطور یک اجراکننده‌ی بازخورد (Feedback Runner) بسازید که دستورات شل را مهار کند، مقادیر stdout، stderr، کد خروج (Exit code) و مدت‌زمان اجرا را در رکوردهای ساختاریافته‌ی JSONL ذخیره کند و مانع از توهم ایجنت در ارزیابی موفقیت دستورات شود.

این ابزار به مدل اجازه می‌دهد در نوبت گفت‌وگوی (Turn) بعدی دقیقاً بداند چه اتفاقی افتاده و سیستم نیز در صورت خالی بودن بازخورد، جلوی پیشروی بی‌مورد حلقه را می‌گیرد.

ایجنت‌هایی که خروجی واقعی دستورات را نمی‌بینند، دست به حدس زدن می‌زنند. یک اجراکننده‌ی بازخورد (Feedback runner) خروجی استاندارد (stdout)، خطای استاندارد (stderr)، کد خروج (exit code) و زمان اجرا را در قالب یک رکورد ساختاریافته ذخیره می‌کند تا نوبت گفت‌وگوی (Turn) بعدی بتواند آن را بخواند. به این ترتیب ایجنت به جای پیش‌بینی‌های خودش از واقعیت، بر اساس فکت‌ها عمل می‌کند.

اهداف یادگیری

  • تشخیص تفاوت بازخورد محیط اجرا (Runtime feedback) از تله‌متری پایش‌پذیری (Observability telemetry).
  • ساخت یک اجراکننده‌ی بازخورد که دستورات شل را در بر می‌گیرد و رکوردهای ساختاریافته را ذخیره می‌کند.
  • هرس (Truncation) خروجی‌های حجیم به صورت قطعی (Deterministic) تا حلقه‌ی ایجنت درون سهمیه‌ی توکن (Token budget) بماند.
  • جلوگیری از ادامه‌ی حلقه‌ی ایجنت در صورت در دسترس نبودن بازخورد.

صورت مسئله

ایجنت می‌گوید: «در حال اجرای تست‌ها هستم.» پیام بعدی می‌گوید: «همه‌ی تست‌ها پاس شدند.» اما در واقعیت هیچ تستی اجرا نشده است. ایجنت خروجی را از خودش درآورده، یا دستور را اجرا کرده ولی نتیجه را نخوانده، یا نتیجه را خوانده اما خط مربوط به شکست را در سکوت هرس کرده است.

یک اجراکننده‌ی بازخورد این شکاف را برطرف می‌کند. تمام دستورات از این اجراکننده می‌گذرند. هر رکورد شامل دستور، stdout و stderr ثبت‌شده، کد خروج، مدت‌زمان واقعی اجرا و یک یادداشت تک‌خطی از طرف ایجنت است. ایجنت در نوبت گفت‌وگوی بعدی این رکورد را می‌خواند. گیت اعتبارسنجی (Verification gate) نیز در پایان تسک همین رکوردها را بررسی می‌کند.

مفهوم اصلی

حلقه‌ی ایجنتrun_with_feedback.pysubprocessstdout / stderr / خروج / مدت زمانfeedback_record.jsonlدروازه اعتبارسنجی

چه مواردی داخل رکورد بازخورد قرار می‌گیرد

فیلد چرا اهمیت دارد
command مقدار دقیق argv؛ بدون رفتارهای غیرمنتظره‌ی ناشی از بسط شل (Shell expansion)
stdout_tail N خط آخر؛ هرس قطعی
stderr_tail N خط آخر؛ تفکیک‌شده از stdout
exit_code نشانه‌ی صریح و بدون ابهام موفقیت
duration_ms شناسایی پروب‌های کند و فرایندهای مهارنشدنی
started_at برچسب زمانی برای بازپخش (Replay)
agent_note یادداشت یک‌خطی ایجنت درباره‌ی انتظاری که از خروجی داشت

هرس کردن قطعی است

یک لاگ 50 مگابایتی حلقه‌ی ایجنت را از پا درمی‌آورد. اجراکننده سر (Head) و ته لاگ را با نشانگر ...truncated N lines... هرس می‌کند؛ این کار قطعی است تا همان خروجی همیشه همان رکورد را تولید کند. اینجا خبری از نمونه‌برداری تصادفی نیست؛ بخش‌هایی که ایجنت باید ببیند (خطای نهایی، خلاصه‌ی پایانی) همیشه در انتهای خروجی جا دارند.

بازخورد در برابر تله‌متری

تله‌متری (فاز 14 · 23، قراردادهای معنایی OTel GenAI) برای اپراتورهای انسانی است که می‌خواهند اجراها را در طول زمان بررسی کنند. بازخورد برای نوبت گفت‌وگوی بعدی همین اجرا است. این دو فیلدهای مشترکی دارند، اما در فایل‌های مجزایی با دوره‌های نگهداری (Retention) متفاوت ذخیره می‌شوند.

خودداری از پیشروی در نبود بازخورد

اگر اجراکننده قبل از ثبت کد خروج خطا دهد، رکورد با مقادیر exit_code: null و error: <reason> ذخیره می‌شود. حلقه‌ی ایجنت به‌هیچ‌وجه نباید با خروج null ادعای موفقیت کند. وقتی خروجی در کار نیست، پیشروی هم متوقف می‌شود.

حلقه‌ی بازخوردکدهای خروج به نوبت گفت‌وگو بازمی‌گردند
FEEDBACK LOOPEvery command goes through the runner, which captures stdout, stderr, exit code, and duration into a structured record. The first run fails with exit 1 and that record, not the agent's imagination of the output, routes back into the next turn; the retry then earns its exit 0 on the same evidence trail the gate will read.نوبت ایجنتrunnerپوشش‌دهنده‌ی cmdshellfeedback_record.jsonlexit 1 · stderr ثبت شدexit 0 · 412msخطا بازگردانده می‌شود؛ تلاش مجدد بر اساس واقعیات عمل می‌کند
هر دستور از طریق رانر اجرا می‌شود که stdout، stderr، کد خروج و مدت‌زمان را در یک رکورد ساختاریافته ثبت می‌کند. اجرای اول با کد خروج 1 شکست می‌خورد و همان رکورد—نه تصور ذهنی ایجنت از خروجی—به نوبت بعدی هدایت می‌شود؛ سپس تلاش مجدد بر پایه همان ردپای شواهدی که گیت خواهد خواند، کد خروج 0 را کسب می‌کند.

پیاده‌سازی از صفر

فایل code/main.py این موارد را پیاده‌سازی می‌کند:

  • تابع run_with_feedback(command, agent_note) که subprocess.run را در بر می‌گیرد، مقادیر stdout/stderr/exit/duration را ضبط می‌کند، خروجی را به‌صورت قطعی هرس می‌کند و به feedback_record.jsonl می‌افزاید.
  • یک بارگذار کوچک که فایل JSONL را به‌صورت استریم به یک لیست پایتون تبدیل می‌کند.
  • یک دمو که سه دستور (موفق، ناموفق، کند) را اجرا کرده و آخرین رکورد هر دستور را چاپ می‌کند.

آن را اجرا کنید:

python3 code/main.py

خروجی: سه رکورد بازخورد به feedback_record.jsonl اضافه می‌شود و آخرین رکورد هر کدام در همان خروجی چاپ می‌شود. فایل را در حین اجراهای مجدد پایش (Tail) کنید تا جمع شدن گام‌به‌گام داده‌های حلقه را ببینید.

الگوهای محیط عملیاتی در دنیای واقعی

سه الگو اجراکننده را آن‌قدر مقاوم می‌کنند که برای محیط عملیاتی (Production) آماده باشد.

پاک‌سازی هنگام نوشتن، نه هنگام خواندن. هر رکوردی که به stdout یا stderr دسترسی دارد ممکن است اطلاعات محرمانه (Secrets) را نشت دهد. اجراکننده قبل از نوشتن داده در JSONL، یک مرحله پاک‌سازی داده‌های حساس (Redaction) اجرا می‌کند: حذف خطوط منطبق با ^Bearer ، password=، api[_-]?key=، AKIA[0-9A-Z]{16} (مربوط به AWS) و xox[baprs]- (مربوط به Slack). پاک‌سازی هنگام خواندن خطایی مرگبار است؛ چون مهاجم به همان فایل روی دیسک دسترسی پیدا می‌کند. الگوهای پاک‌سازی را هر سه ماه یک‌بار با قالب‌های واقعی کلیدها در محیط اجرای عملیاتی ارزیابی و تطبیق دهید.

سیاست چرخش فایل، نه یک تک‌فایل. حجم feedback_record.jsonl را به 1 مگابایت برای هر فایل محدود کنید؛ در صورت پر شدن، آن را به .1 و .2 بچرخانید و فایل .5 را حذف کنید. حلقه‌ی ایجنت فقط فایل فعلی را می‌خواند؛ بنابراین هزینه‌ی محیط اجرا مهار می‌شود. مخزن ذخیره‌ی دست‌سازه‌های (CI artifact storage) تمام مجموعه‌ی فایل‌های چرخیده‌شده را دریافت می‌کند. بدون این چرخش، فایل لاگ با هر بار فراخوانی بارگذار به گلوگاه تبدیل می‌شود.

شناسه‌ی دستور والد برای زنجیره‌های تلاش مجدد. هر رکورد یک command_id می‌گیرد؛ تلاش‌های مجدد شامل parent_command_id هستند که به تلاش قبلی اشاره می‌کند. لیست «تلاش‌های ناموفق» ارزیاب (فاز 14 · 40) و ممیزی گیت اعتبارسنجی، هر دو این زنجیره را دنبال می‌کنند. بدون این پیوند، تلاش‌های مجدد مثل موفقیت‌های مستقل به نظر می‌رسند و تاریخچه‌ی شکست‌ها از چشم ممیزی پنهان می‌ماند.

استفاده از ابزارهای واقعی

الگوهای محیط عملیاتی:

  • ابزار Claude Code Bash. این ابزار همین حالا هم مقادیر stdout، stderr، exit و duration را ثبت می‌کند. اجراکننده‌ی این درس، معادل مستقل از فریم‌ورک آن برای هر محصول ایجنتی است.
  • گره‌های LangGraph. هر گره‌ی شل را در این اجراکننده بپیچید تا رکورد خارج از وضعیت گراف (Graph state) ماندگار شود.
  • لاگ‌های CI. فایل JSONL را به مخزن ذخیره‌ی دست‌سازه‌های CI بفرستید؛ ارزیاب‌ها می‌توانند بدون نیاز به اجرای مجدد نشست، هر دستوری را بازپخش کنند.

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

خروجی این درس

فایل outputs/skill-feedback-runner.md یک اسکریپت run_with_feedback.py مختص پروژه با سهمیه‌ی هرس مناسب، یک نویسنده‌ی JSONL متصل به محیط کاری و بارگذاری که ایجنت در هر نوبت گفت‌وگو می‌خواند، تولید می‌کند.

تمرین‌ها

  1. فیلد cwd را به هر رکورد اضافه کنید تا اجرای یک دستور یکسان از دایرکتوری‌های مختلف قابل تشخیص باشد.
  2. یک مرحله‌ی redaction اضافه کنید که خطوط منطبق با ^Bearer یا password= را حذف کند. آن را روی یک رکورد آزمایشی بسنجید.
  3. با چرخش لاگ به فایل‌های .1 و .2، حجم کلی feedback_record.jsonl را روی 1 مگابایت محدود کنید. از سیاست چرخشی که چیده‌اید دفاع کنید.
  4. فیلد parent_command_id را اضافه کنید تا زنجیره‌های تلاش مجدد مشخص شوند: کدام دستور ورودیِ مصرف‌شده توسط دستور بعدی را تولید کرده است.
  5. فایل JSONL را به یک رابط متنی (TUI) کوچک وصل کنید که آخرین کد خروج غیرصفر را متمایز نشان دهد. هشت قابلیت کلیدی را فهرست کنید که این TUI باید نمایش دهد تا در فرایند بررسی واقعاً به کار بیاید.

اصطلاحات کلیدی

اصطلاح آنچه معمولاً می‌گویند معنای دقیق فنی
Feedback record «لاگ اجرا» مدخل ساختاریافته‌ی JSONL شامل دستور، خروجی، خروج و مدت‌زمان
Tail truncation «کوتاه کردن لاگ» ثبت قطعی سر و ته خروجی تا رکوردها در سهمیه‌ی توکن جا شوند
Refuse-on-null «توقف به خاطر داده‌های ناقص» حلقه در صورت خالی (null) بودن exit_code نباید جلو برود
Agent note «برچسب انتظار» پیش‌بینی تک‌خطی که ایجنت قبل از خواندن نتیجه می‌نویسد
Telemetry split «دو فایل لاگ» بازخورد برای نوبت گفت‌وگوی بعدی، تله‌متری برای اپراتور

برای مطالعه‌ی بیشتر

خودآزمایی

به سؤال‌ها جواب بدهید تا ببینید چقدر از درس در ذهنتان مانده است.

  1. پیش از درسرانرِ بازخورد، ایجنت را مجبور به انجام چه کاری می‌کند؟
  2. پیش از درسبازخورد چه تفاوتی با تله‌متری دارد؟
  3. حین درسکدام فیلد حتماً باید در هر رکورد بازخورد وجود داشته باشد؟
  4. حین درسرانر چگونه خروجی‌های حجیم را هرس (کوتاه) می‌کند؟
  5. حین درسچرا پاک‌سازی داده‌های حساس باید هنگام نوشتن انجام شود و نه هنگام خواندن؟
  6. پس از درسوجود parent_command_id چه مزیتی به محیط کار می‌دهد؟
  7. پس از درسچرا feedback_record.jsonl با چرخش فایل‌ها روی 1 مگابایت محدود می‌شود؟

سؤالات متداول

تفاوت تله‌متری پایش‌پذیری با بازخورد محیط اجرا چیست؟

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

چرا هرس خروجی لاگ باید قطعی (Deterministic) باشد؟

چون لاگ‌های بزرگ سهمیه‌ی توکن را پر می‌کنند؛ هرس قطعی از سر و ته لاگ تضمین می‌کند که خروجی یکسان همواره لاگ یکسانی تولید کند و پیام‌های خطای پایانی از دست نروند.

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

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

ما رو توی گوگل بیشتر ببین

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