چیتشیت
مهندسی بستر آزمایشی ایجنت
Agent Harness Engineering
کد احاطهکنندهی مدل. قرارداد حلقه، هوکها، بودجهها، ابزارها، گیتهای کنترلی، ساندباکس، ردگیری محاسبات و فایلهای میز کار.
فهرست مطالب
معماری بستر آزمایشی
معمارییک گام از حلقه
گامبهگام- IDLEدستور
run(goal)رویدادsession.startرا صادر میکند. این تنها ورودی مجاز است. - PLANNINGبرنامهریز گامها را پیشنویس میکند. حلقه
plan.draftوplan.commitرا صادر میکند. - بررسی بودجههر گام متد
_check_budget()را فراخوانی میکند. رسیدن به سقف، وضعیت را به IDLE متوقف میکند. - EXECUTINGحلقه هوک
before_stepرا فعال میکند. گام بدون ابزار به REFLECTING میرود. - گام ابزارحلقه رویداد
tool.callرا صادر کرده و یکPullRequestبه فراخواننده برمیگرداند. - AWAITING_TOOLفراخواننده ابزار را اجرا کرده و
resume(payload)را صدا میزند. - 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_turns | 8 | بودجه. نوبتهای گفتوگو پیش از توقف |
max_tool_calls | 16 | بودجه. فراخوانیهای ابزار در هر نشست |
max_wall_seconds | 30.0 | بودجه. ثانیههای زمان واقعی |
timeout_ms | 30_000 | به ازای هر ابزار. لغو اختصاصی برای هر فراخوانی اولویت دارد |
max_attempts | 3 | توزیعکنندهی وظایف. فقط خطای زمان و گذرا |
concurrency | 8 | سمافور توزیع موازی توزیعکنندهی وظایف |
DEFAULT_MAX_OUTPUT_BYTES | 64 * 1024 | سقف ساندباکس در هر استریم |
observation_budget | 4000 | توکنها. اجرای سرتاسری، درس 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را در تمام قراردادهای دامنه قرار دهید.