چیت‌شیت

مهندسی بستر آزمایشی ایجنت

Agent Harness Engineering

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

  • ۱۳ بخش
  • ۱۸ درس مرتبط
  • کد و دستورها عیناً از دوره‌ی اصلی
فهرست مطالب
  1. معماری بستر آزمایشی
  2. یک گام از حلقه
  3. حلقه‌ی اصلی: HarnessLoop._step
  4. هدایت حلقه
  5. ابزارها، گیت‌های کنترلی و ردگیری محاسبات
  6. زنجیره گیت، سپس ساندباکس
  7. اسکریپت‌های میز کار
  8. هفت سطح میز کار
  9. پیش‌فرض‌ها
  10. عیب‌یابی
  11. واژه‌نامه
  12. پرسش و پاسخ
  13. نکات

معماری بستر آزمایشی

معماری
اجراشلیکبرنامهفراخوانی ابزارALLOWاسپنفراخوانندهاجرا، ادامه‌دهیHarnessLoop6 وضعیتHookRegistry10 مبحثبودجهنوبت‌های گفت‌وگو، فراخوانی‌های ابزاربرنامه‌ریزگام‌های برنامهGateChainاولین رد، برنده‌ستتوزیع‌کننده‌ی وظایف + ساندباکسرجیستری، تلاش مجدد، ایزوله‌سازیرویدادها + اسپن‌هاtraces.jsonl

یک گام از حلقه

گام‌به‌گام
  1. IDLEدستور run(goal) رویداد session.start را صادر می‌کند. این تنها ورودی مجاز است.
  2. PLANNINGبرنامه‌ریز گام‌ها را پیش‌نویس می‌کند. حلقه plan.draft و plan.commit را صادر می‌کند.
  3. بررسی بودجههر گام متد _check_budget() را فراخوانی می‌کند. رسیدن به سقف، وضعیت را به IDLE متوقف می‌کند.
  4. EXECUTINGحلقه هوک before_step را فعال می‌کند. گام بدون ابزار به REFLECTING می‌رود.
  5. گام ابزارحلقه رویداد tool.call را صادر کرده و یک PullRequest به فراخواننده برمی‌گرداند.
  6. AWAITING_TOOLفراخواننده ابزار را اجرا کرده و resume(payload) را صدا می‌زند.
  7. REFLECTINGگام بعدی، یا وضعیت DONE همراه‌با goal_met پس از آخرین گام.

حلقه‌ی اصلی: HarnessLoop._step

کد
python
def _step(self) -> PullRequest | SessionResult:
    budget_hit = self._check_budget()                # در هر گام
    if budget_hit is not None:
        return budget_hit                            # توقف، بازگشت به IDLE
    if self.state == State.REFLECTING:
        self._cursor += 1                            # نوبت‌های گفت‌وگو هم 1 واحد اضافه می‌شوند
        if self._cursor >= len(self._plan):
            return self._complete("goal_met")
    step = self._plan[self._cursor]
    if step.requires_tool:
        self.hooks.fire("before_tool_call", {"step": step})
        self.budget.tool_calls += 1                  # شمارش پیش از تحویل کنترل
        self._transition(State.AWAITING_TOOL)
        return PullRequest(reason="tool_call", ...)  # نقطه‌ی واگذاری

هدایت حلقه

کار و کد
وظیفهکد
شروع یک نشستout = loop.run("<goal>")
عضویت در یک هوکloop.hooks.on("<topic>", <fn>)
مسدودسازی فراخوانی ابزارraise HookAbort("<reason>")
بازگرداندن نتیجه‌ی ابزارloop.resume({"result": <value>})
ادامه پس از توقف بودجهloop.resume()
تنظیم بودجهBudget(max_turns=<n>, max_tool_calls=<n>)

ابزارها، گیت‌های کنترلی و ردگیری محاسبات

کار و کد
وظیفهکد
ثبت یک ابزارregistry.register(<name>, <schema>, <fn>)
جایگزینی یک ابزارregistry.register(..., override=True)
توزیع فراخوانیawait disp.dispatch("<name>", <args>)
ادغام فراخوانی‌های تکراریdisp.dispatch(..., idempotency_key="<key>")
اعمال گیت روی فراخوانیchain.evaluate(<call>, <ctx>)
اجرا در ساندباکسsandbox.run([<exe>, "<arg>"])
ثبت اسپن برای فراخوانی ابزارbuilder.span("gen_ai.tool.execution")

زنجیره گیت، سپس ساندباکس

کد
python
chain = GateChain(gates=(
    WhitelistGate(allowed=frozenset({"read_file"})),  # مجموعه‌ی مجاز
    RegexGate.from_strings(patterns=(r"\bsudo\b",)),  # الگوی argv
    RecencyGate(window=3),                            # مشاهده‌ی بیات
    BudgetGate(max_tokens=200),                       # مجموع دفتر کل
))
outcome = chain.evaluate(call, ctx)                   # توقف با اولین رد
if not outcome.allow:
    refused += 1                                      # مشاهده‌ی outcome.deny_reason
ledger.record(obs)                                    # BudgetGate آن را می‌خواند
config = SandboxConfig(project_root=root)
result = Sandbox(config=config).run(["rm", "-rf", "."])
result.denied                                         # درست: rm در لیست سیاه

اسکریپت‌های میز کار

دستورها
دستورکارکرد
bin/install.shبسته را درون مخزن کپی می‌کند.
bin/install.sh --forceفایل AGENTS.md موجود را بازنویسی می‌کند.
python3 scripts/init_agent.pyمحیط اجرا، وابستگی‌ها، متغیرهای محیطی و عمر وضعیت را ارزیابی می‌کند.
python3 scripts/run_with_feedback.py <cmd>یک رکورد در feedback_record.jsonl ثبت می‌کند.
python3 scripts/verify_agent.py <task_id>حکم ارزیابی گیت. در صورت مسدودسازی با کد 1 خارج می‌شود.
python3 scripts/generate_handoff.py <task_id>handoff.json و handoff.md را می‌نویسد.

هفت سطح میز کار

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

پیش‌فرض‌ها

پیش‌فرض‌ها
تنظیمپیش‌فرضنکته
max_turns8بودجه. نوبت‌های گفت‌وگو پیش از توقف
max_tool_calls16بودجه. فراخوانی‌های ابزار در هر نشست
max_wall_seconds30.0بودجه. ثانیه‌های زمان واقعی
timeout_ms30_000به ازای هر ابزار. لغو اختصاصی برای هر فراخوانی اولویت دارد
max_attempts3توزیع‌کننده‌ی وظایف. فقط خطای زمان و گذرا
concurrency8سمافور توزیع موازی توزیع‌کننده‌ی وظایف
DEFAULT_MAX_OUTPUT_BYTES64 * 1024سقف ساندباکس در هر استریم
observation_budget4000توکن‌ها. اجرای سرتاسری، درس 29

عیب‌یابی

عیب‌یابی
  • illegal transition

    علت
    انتقال در جدول مجازها نیست
    راه‌حل
    وضعیت را فقط در _transition تغییر دهید
  • requires a payload

    علت
    فراخوانی resume() در نقطه‌ی واگذاری ابزار
    راه‌حل
    یک نتیجه یا یک خطا ارسال کنید
  • budget_exceeded:turns

    علت
    سقف یک بودجه تکمیل شد
    راه‌حل
    بررسی و سپس فراخوانی resume()
  • kind="schema" هرگز تکرار نمی‌شود

    علت
    خطاهای اسکما قطعی هستند
    راه‌حل
    آرگومان‌ها را اصلاح کنید
  • یک عملیات نوشتن دو بار اجرا می‌شود

    علت
    تلاش مجدد با فراخوانی در حال اجرا تداخل کرد
    راه‌حل
    idempotency_key را ارسال کنید
  • مسدودسازی با feedback.null_exit

    علت
    هیچ کد خروجی ضبط نشد
    راه‌حل
    اجرای مجدد؛ جلو نروید

واژه‌نامه

اصطلاح‌ها
ReAct
استدلال + اقدام. فکر، اقدام و مشاهده در یک استریم واحد.
OTel
مخفف OpenTelemetry. استاندارد GenAI کلیدهای gen_ai.* را تعیین می‌کند.
JSON-RPC
کدهای خطای 32601-، 32602-، 32603-.
pass@k
فرمول 1 - (1 - p)^k روی k نمونه.
HITL
انسان در چرخه. کنش‌های با اطمینان پایین به اینجا می‌روند.

پرسش و پاسخ

پرسش و پاسخ
چرا بودجه با توقف موقت کنترل را واگذار می‌کند؟
فراخواننده تصمیم می‌گیرد تمدید کرده و ادامه دهد، یا ببندد.
چرا در وقفه زمانی غیر بی‌اثر در تکرار، تلاش مجدد نداریم؟
احتمالاً نوشتن commit شده است؛ تلاش مجدد آن را تکراری می‌کند.
چرا گیت‌های ارزان در اولویتند؟
یک رد زودهنگام، زنجیره را قبل از بررسی‌های پرهزینه متوقف می‌کند.
چرا قاضی LLM در گیت نداریم؟
دست‌سازه‌های یکسان باید حکم ارزیابی یکسانی تولید کنند.

نکات

نکته‌ها
  • وضعیت را فقط در یک تابع تغییر دهید؛ تا پایاها همگی در یک نقطه باقی بمانند.
  • کلید را در برنامه‌ریز استخراج کنید: f"{step_id}:{tool_name}:{hash(args)}".
  • خطاهای ابزار را به صورت رشته‌ی مشاهده برگردانید، نه با خرابی سیستم.
  • هوک‌ها جهش می‌دهند یا لغو می‌کنند؛ رویدادها فقط ثبت می‌کنند.
  • فایل AGENTS.md را زیر 50 سطر نگه دارید؛ مستندات موضوعی را بر حسب تقاضا بارگذاری کنید.
  • کلید forbidden_files را در تمام قراردادهای دامنه قرار دهید.