پرش به محتوای اصلی
OpenAI

۳ بهمن ۱۴۰۴

مهندسی

باز کردن حلقه عامل Codex

نوشته Michael Bolin، عضو کادر فنی

در حال بارگذاری…

Codex CLI(در یک پنجره جدید باز می‌شود) عامل نرم‌افزاری محلی چندپلتفرمی ما است که برای ایجاد تغییرات نرم‌افزاری باکیفیت و قابل‌اعتماد طراحی شده و به‌صورت ایمن و کارآمد روی دستگاه شما عمل می‌کند. ما مقدار زیادی درباره چگونگی ساخت یک عامل نرم‌افزاری در سطح جهانی از زمان راه‌اندازی اولیه CLI در ماه آوریل آموخته‌ایم. برای بررسی این بینش‌ها، این نخستین پست از یک رشته مستمر است که در آن جنبه‌های مختلف نحوهٔ کار Codex و درس‌های سخت‌آموخته را بررسی خواهیم کرد. (برای مشاهده‌ای حتی جزئی‌تر از نحوه ساخت Codex CLI، محل نگهداری متن‌باز ما را در https://github.com/openai/codex(در یک پنجره جدید باز می‌شود) بررسی بفرمایید. اگر مایلید بیشتر بدانید، بسیاری از جزئیات دقیق‌تر تصمیم‌های طراحی ما در مسائل و درخواست‌های نظرسنجی GitHub ثبت شده‌اند.)

برای شروع، بر روی حلقه عامل تمرکز خواهیم کرد که منطق اصلی در Codex CLI است و وظیفه‌ی هماهنگ‌سازی تعامل میان کاربر، مدل، و ابزارهایی را دارد که مدل برای انجام کارهای معنادار نرم‌افزاری از آن‌ها استفاده می‌کند. امیدواریم این پست درباره نقشی که عامل ما (یا «زیرساخت اجرایی») در بهره‌گیری از الگوهای زبانی بزرگ (LLM) ایفا می‌کند، دید خوبی به شما بدهد.

پیش از اینکه وارد جزئیات شویم، یک نکته کوتاه درباره اصطلاحات: در OpenAI، «Codex» شامل مجموعه‌ای از ارائه‌های عامل نرم‌افزاری، از جمله Codex CLI، Codex Cloud و افزونه Codex VS Code است. این پست بر زیرساخت اجرایی Codex تمرکز دارد که حلقهٔ اصلی عامل و منطق اجرا را فراهم می‌کند و زیربنای همهٔ تجربه‌های Codex است و از طریق Codex CLI ارائه می‌شود. برای سهولت در اینجا، از اصطلاحات «Codex» و «Codex CLI» به‌جای یکدیگر استفاده خواهیم کرد.

حلقه عامل

در قلب هر عامل AI چیزی به نام «حلقه عامل» وجود دارد. تصویری ساده از حلقه عامل به این صورت است:

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

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

گام بعدی، ارسال دستورالعمل‌ها به مدل و درخواست تولید پاسخ از آن است — فرآیندی که با عنوان استنتاج شناخته می‌شود. در طول استنتاج، دستور متنی ابتدا به یک دنباله از توکن‌های(در یک پنجره جدید باز می‌شود) ورودی—اعداد صحیحی که به واژگان مدل اشاره می‌کنند—ترجمه می‌شود. سپس از این توکن‌ها برای نمونه‌گیری از مدل استفاده می‌شود و در نتیجه، یک دنباله جدید از توکن‌های خروجی تولید می‌شود.

توکن‌های خروجی به متن بازگردانده می‌شوند و به پاسخ مدل تبدیل می‌گردند. از آنجا که توکن‌ها به‌صورت تدریجی تولید می‌شوند، این ترجمه می‌تواند همزمان با اجرای مدل انجام شود و به همین دلیل بسیاری از برنامه‌های مبتنی بر الگوهای زبانی بزرگ (LLM) خروجی جریانی را نمایش می‌دهند. در عمل، استنتاج معمولاً پشت یک API کپسوله می‌شود که بر روی متن عمل می‌کند و جزئیات توکنیزه سازی را انتزاع می‌کند.

در نتیجهٔ گام استنتاج، مدل یا (1) یک پاسخ نهایی برای ورودی اصلی کاربر تولید می‌کند، یا (2) یک فراخوانی ابزار درخواست می‌کند که انتظار می‌رود عامل آن را انجام دهد (مثلاً «ls را اجرا کنید و خروجی را گزارش دهید»). در مورد (۲)، عامل فراخوانی ابزار را اجرا می‌کند و خروجی آن را به دستور اصلی اضافه می‌کند. این خروجی برای تولید یک ورودی جدید استفاده می‌شود که برای پرس‌وجوی مجدد از مدل به کار می‌رود؛ سپس عامل می‌تواند این اطلاعات جدید را در نظر بگیرد و دوباره تلاش کند.

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

از آنجا که عامل می‌تواند فراخوانی‌های ابزار را اجرا کند که محیط محلی را تغییر دهند، «خروجی» آن به پیام دستیار محدود نیست. در بسیاری از موارد، خروجی اصلی یک عامل نرم‌افزاری کدی است که روی دستگاه شما می‌نویسد یا ویرایش می‌کند. با این حال، هر نوبت همیشه با یک پیام دستیار پایان می‌یابد—مانند «من architecture.md را که درخواست کرده‌اید اضافه کردم»—که نشان‌دهندهٔ یک وضعیت پایان در حلقه عامل است. از دیدگاه عامل، کار آن به پایان رسیده است و کنترل به کاربر بازمی‌گردد.

مسیر از ورودی کاربر تا پاسخ عامل که در نمودار نشان داده شده است، به عنوان یک نوبت از یک مکالمه (یک رشته در Codex) شناخته می‌شود. اگرچه این نوبت مکالمه می‌تواند شامل تکرارهای زیادی بین استنتاج مدل و فراخوانی‌های ابزار باشد. هر بار که یک پیام جدید به یک مکالمه موجود ارسال می‌کنید، تاریخچه مکالمه به‌عنوان بخشی از دستور برای نوبت جدید گنجانده می‌شود، که شامل پیام‌ها و فراخوانی‌های ابزار از نوبت‌های قبلی است:

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

این بدان معناست که با گسترش مکالمه، طول دستور مورد استفاده برای نمونه‌گیری از مدل نیز افزایش می‌یابد. این طول اهمیت دارد زیرا هر مدل دارای یک پنجره زمینه است که حداکثر تعداد توکن‌هایی است که می‌تواند برای یک فراخوان استنتاج، استفاده کند. توجه داشته باشید که این پنجره شامل توکن‌های ورودی و خروجی است. همان‌طور که ممکن است تصور کنید، یک عامل می‌تواند در یک نوبت صدها فراخوانی ابزار انجام دهد که این کار ممکن است پنجره‌ی زمینه را به‌طور کامل مصرف کند. به همین دلیل، مدیریت پنجره زمینه یکی از مسئولیت‌های متعدد عامل است. اکنون، اجازه دهید عمیق‌تر بررسی کنیم تا ببینیم Codex چگونه حلقه عامل را اجرا می‌کند.

استنتاج مدل

Codex CLI درخواست‌های HTTP را به Responses API(در یک پنجره جدید باز می‌شود) ارسال می‌کند تا استنتاج مدل را اجرا کند. ما بررسی خواهیم کرد که اطلاعات چگونه از طریق Codex جریان می‌یابد، که از Responses API برای هدایت حلقه عامل استفاده می‌کند.

نقطه پایانی Responses API که Codex CLI از آن استفاده می‌کند قابل پیکربندی(در یک پنجره جدید باز می‌شود) است، بنابراین می‌توان از آن با هر نقطه پایانی که Responses API را اجرا می‌کند(در یک پنجره جدید باز می‌شود) استفاده کرد:

بیایید بررسی کنیم که Codex چگونه دستور را برای اولین فراخوانی استنتاج در یک مکالمه ایجاد می‌کند.

ایجاد دستور اولیه

به‌عنوان یک کاربر نهایی، زمانی که از Responses API استعلام می‌گیرید، دستور استفاده‌شده برای نمونه‌گیری از مدل را به‌صورت کلمه‌به‌کلمه مشخص نمی‌کنید. در عوض، شما انواع مختلف ورودی را به‌عنوان بخشی از استعلام خود مشخص می‌کنید و سرور Responses API تصمیم می‌گیرد که چگونه این اطلاعات را به یک دستور ساختاربندی کند که مدل برای مصرف آن طراحی شده است. می‌توانید دستور را به عنوان یک «فهرست اقلام» در نظر بگیرید؛ این بخش توضیح می‌دهد که چگونه استعلام شما به آن فهرست تبدیل می‌شود.

در دستور اولیه، هر مورد در فهرست با یک نقش مرتبط است. نقش نشان می‌دهد محتوای مرتبط باید چه میزان اهمیت داشته باشد و یکی از مقادیر زیر است (به ترتیب نزولیِ اولویت): سیستم، توسعه‌دهنده، کاربر، دستیار.

Responses API(در یک پنجره جدید باز می‌شود) یک بار داده از نوع JSON را با پارامترهای متعدد دریافت می‌کند. روی این سه مورد تمرکز خواهیم کرد:

در Codex، کادر دستورالعمل‌ها از model_instructions_file(در یک پنجره جدید باز می‌شود) در ~/.codex/config.toml خوانده می‌شود، اگر مشخص شده باشد؛ در غیر این صورت، base_instructions مرتبط با یک مدل(در یک پنجره جدید باز می‌شود) استفاده می‌شود. دستورالعمل‌های خاص مدل در مخزن Codex موجود هستند و در CLI گنجانده شده‌اند (مثلاً gpt-5.2-codex_prompt.md(در یک پنجره جدید باز می‌شود)).

کادر ابزار فهرستی از تعاریف ابزار است که با الگویی که توسط Responses API تعریف شده است، مطابقت دارد. برای Codex، این شامل ابزارهایی است که توسط Codex CLI ارائه می‌شوند، ابزارهایی که توسط Responses API ارائه می‌شوند و باید در اختیار Codex قرار گیرند، و همچنین ابزارهایی که توسط کاربر ارائه می‌شوند، معمولاً از طریق سرورهای MCP:

JavaScript

1
[
2
// Codex's default shell tool for spawning new processes locally.
3
{
4
"type": "function",
5
"name": "shell",
6
"description": "Runs a shell command and returns its output...",
7
"strict": false,
8
"parameters": {
9
"type": "object",
10
"properties": {
11
"command": {"type": "array", "description": "The command to execute", ...},
12
"workdir": {"description": "The working directory...", ...},
13
"timeout_ms": {"description": "The timeout for the command...", ...},
14
...
15
},
16
"required": ["command"],
17
}
18
}
19

20
// Codex's built-in plan tool.
21
{
22
"type": "function",
23
"name": "update_plan",
24
"description": "Updates the task plan...",
25
"strict": false,
26
"parameters": {
27
"type": "object",
28
"properties": {"plan":..., "explanation":...},
29
"required": ["plan"]
30
}
31
},
32

33
// Web search tool provided by the Responses API.
34
{
35
"type": "web_search",
36
"external_web_access": false
37
},
38

39
// MCP server for getting weather as configured in the
40
// user's ~/.codex/config.toml.
41
{
42
"type": "function",
43
"name": "mcp__weather__get-forecast",
44
"description": "Get weather alerts for a US state",
45
"strict": false,
46
"parameters": {
47
"type": "object",
48
"properties": {"latitude": {...}, "longitude": {...}},
49
"required": ["latitude", "longitude"]
50
}
51
}
52
]

در نهایت، کادر ورودی در بار داده JSON یک فهرستی از اقلام است. Codex قبل از افزودن پیام کاربر، اقلام زیر را(در یک پنجره جدید باز می‌شود) در ورودی درج می‌کند:

1. پیامی با role=developer که محیط سندباکس را توصیف می‌کند که فقط برای ابزارِ shell ارائه‌شده توسط Codex که در بخش tools تعریف شده است، اعمال می‌شود. یعنی سایر ابزارها مانند ابزارهایی که از سرورهای MCP ارائه می‌شوند تحت سندباکس Codex قرار ندارند و خودشان مسئول اعمال محدودیت‌ها و محافظت‌های لازم هستند.

پیام از یک قالب ساخته می‌شود که در آن بخش‌های کلیدی محتوا از قطعه‌های Markdown که در Codex CLI بسته‌بندی شده‌اند، مانند workspace_write.md(در یک پنجره جدید باز می‌شود) و on_request.md(در یک پنجره جدید باز می‌شود)، می‌آیند:

متن ساده

1
<permissions instructions>
2
- description of the sandbox explaining file permissions and network access
3
- instructions for when to ask the user for permissions to run a shell command
4
- list of folders writable by Codex, if any
5
</permissions instructions>

2. (اختیاری) پیامی با role=developer که محتوای آن مقدار developer_instructions است که از فایل config.toml کاربر خوانده می‌شود.

۳. (اختیاری) پیامی با role=user که محتوای آن «دستورالعمل‌های کاربر» است و این دستورالعمل‌ها از یک فایل واحد گرفته نشده‌اند، بلکه از چندین منبع جمع‌آوری شده‌اند(در یک پنجره جدید باز می‌شود). به‌طور کلی، دستورالعمل‌های مشخص‌تر در مراحل بعدی ظاهر می‌شوند:

۴. پیامی با role=user که محیط محلی را توصیف می‌کند که عامل در حال حاضر در آن فعالیت می‌کند. این دایرکتوری کاری فعلی و پوسته کاربر را مشخص می‌کند(در یک پنجره جدید باز می‌شود):

متن ساده

1
<environment_context>
2
<cwd>/Users/mbolin/code/codex5</cwd>
3
<shell>zsh</shell>
4
</environment_context>

زمانی که Codex تمام محاسبات فوق را برای راه‌اندازی ورودی انجام داد، پیام کاربر را اضافه می‌کند تا مکالمه را آغاز کند.

مثال‌های قبلی بر محتوای هر پیام تمرکز داشتند، اما توجه داشته باشید که هر عنصر از ورودی یک شیء JSON با نوع، نقش(در یک پنجره جدید باز می‌شود) و محتوا به صورت زیر است:

JSON

1
{
2
"type": "message",
3
"role": "user",
4
"content": [
5
{
6
"type": "input_text",
7
"text": "Add an architecture diagram to the README.md"
8
}
9
]
10
}

زمانی که Codex محموله کامل JSON را برای ارسال به Responses API آماده می‌کند، سپس بسته به نحوه پیکربندی نقطه پایان Responses API در ~/.codex/config.toml ، درخواست HTTP POST را با یک سرآیند مجوز ارسال می‌کند (در صورت مشخص شدن، سرآیندهای HTTP اضافی و پارامترهای کوئری نیز اضافه می‌شوند).

هنگامی که یک سرور OpenAI Responses API درخواست را دریافت می‌کند، از JSON برای استخراج دستور برای مدل به شکل زیر استفاده می‌کند (برای اطمینان، یک اجرا سفارشی از Responses API می‌تواند انتخاب متفاوتی داشته باشد):

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

همان‌طور که مشاهده می‌کنید، ترتیب سه مورد اول در دستور توسط سرور تعیین می‌شود، نه مشتری. با این حال، از میان آن سه مورد، تنها محتوای پیام سیستم نیز توسط سرور کنترل می‌شود، زیرا ابزار و دستورالعمل‌ها توسط مشتری تعیین می‌شوند. پس از آن ورودی از بار داده JSON دنبال می‌شوند تا دستور تکمیل شود.

اکنون که دستور خود را داریم، آماده‌ایم از مدل نمونه‌گیری کنیم.

اولین نوبت

این درخواست HTTP به Responses API نخستین «نوبت» از یک مکالمه در Codex را آغاز می‌کند. سرور با یک جریان رویدادهای ارسال‌شده از سرور (SSE(در یک پنجره جدید باز می‌شود)) پاسخ می‌دهد. data هر رویداد یک بار داده از نوع JSON است که دارای یک «نوع» است که با «پاسخ» شروع می‌شود و می‌تواند چیزی شبیه به این باشد (فهرست کامل رویدادها را می‌توانید در مستندات API(در یک پنجره جدید باز می‌شود) ما پیدا کنید):

متن ساده

1
data: {"type":"response.reasoning_summary_text.delta","delta":"ah ", ...}
2
data: {"type":"response.reasoning_summary_text.delta","delta":"ha!", ...}
3
data: {"type":"response.reasoning_summary_text.done", "item_id":...}
4
data: {"type":"response.output_item.added", "item":{...}}
5
data: {"type":"response.output_text.delta", "delta":"forty-", ...}
6
data: {"type":"response.output_text.delta", "delta":"two!", ...}
7
data: {"type":"response.completed","response":{...}}

Codex جریان رویدادها را مصرف می‌کند(در یک پنجره جدید باز می‌شود) و آن‌ها را به‌عنوان اشیای رویداد داخلی بازنشر می‌کند که می‌تواند توسط یک مشتری استفاده شود. رویدادهایی مانند response.output_text.delta برای پشتیبانی از پخش جریانی در UI استفاده می‌شوند، در حالی که رویدادهای دیگری مانند response.output_item.added به اشیایی تبدیل می‌شوند که به ورودی برای فراخوانی‌های بعدی Responses API پیوست می‌شوند.

فرض کنید اولین درخواست به API Responses شامل دو رویداد response.output_item.done است: یکی با type=reasoning و یکی با type=function_call. این رویدادها باید در کادر ورودی از JSON نمایش داده شوند وقتی دوباره مدل را با پاسخ به فراخوانی ابزار کوئری می‌کنیم: 

JavaScript

1
[
2
/* ... original 5 items from the input array ... */
3
{
4
"type": "reasoning",
5
"summary": [
6
"type": "summary_text",
7
"text": "**Adding an architecture diagram for README.md**\n\nI need to..."
8
],
9
"encrypted_content": "gAAAAABpaDWNMxMeLw..."
10
},
11
{
12
"type": "function_call",
13
"name": "shell",
14
"arguments": "{\"command\":\"cat README.md\",\"workdir\":\"/Users/mbolin/code/codex5\"}",
15
"call_id": "call_8675309..."
16
},
17
{
18
"type": "function_call_output",
19
"call_id": "call_8675309...",
20
"output": "<p align=\"center\"><code>npm i -g @openai/codex</code>..."
21
}
22
]

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

نموداری با برچسب “Snapshot 2” که یک عامل AI را پس از یک فراخوانی ابزار نشان می‌دهد. مدل یک مشاهده ابزار را دریافت می‌کند و یک فکر و عمل جدید تولید می‌کند. پیکان‌ها ورودی‌ها، مشاهدات و خروجی‌ها را به هم متصل می‌کنند تا نشان دهند که عامل چگونه حلقه استدلال خود را تکرار می‌کند.

به‌ویژه، توجه کنید که دستور قدیمی یک پیشوند دقیق از دستور جدید است. این عمدی است، زیرا این کار درخواست‌های بعدی را بسیار کارآمدتر می‌کند چون به ما امکان می‌دهد از کش دستور بهره ببریم (که در بخش بعدی درباره عملکرداش صحبت خواهیم کرد).

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

متن ساده

1
data: {"type":"response.output_text.done","text": "I added a diagram to explain...", ...}
2
data: {"type":"response.completed","response":{...}}

در Codex CLI، پیام دستیار را به کاربر نمایش می‌دهیم و نگارنده را متمرکز می‌کنیم تا به کاربر نشان دهیم که «نوبت» اوست تا گفتگو را ادامه دهد. اگر کاربر پاسخ دهد، هم پیام دستیار از نوبت قبلی و هم پیام جدید کاربر باید به ورودی در درخواست Responses API اضافه شوند تا نوبت جدید آغاز گردد:

JavaScript

1
[
2
/* ... all items from the last Responses API request ... */
3
{
4
"type": "message",
5
"role": "assistant",
6
"content": [
7
{
8
"type": "output_text",
9
"text": "I added a diagram to explain the client/server architecture."
10
}
11
]
12
},
13
{
14
"type": "message",
15
"role": "user",
16
"content": [
17
{
18
"type": "input_text",
19
"text": "That's not bad, but the diagram is missing the bike shed."
20
}
21
]
22
}
23
]

یک بار دیگر، چون داریم یک مکالمه را ادامه می‌دهیم، طول ورودی که به Responses API ارسال می‌کنیم، مدام افزایش می‌یابد:

نموداری با برچسب “Snapshot 3” که مرحله نهایی یک حلقه عامل AI را نشان می‌دهد. پس از دریافت نتایج ابزار، مدل یک فکر نهایی و یک پاسخ نهایی تولید می‌کند که به کاربر بازگردانده می‌شود. پیکان‌ها گذار از خروجی ابزار به پاسخ کامل‌شده را نشان می‌دهند.

بیایید بررسی کنیم این دستور همیشه در حال رشد چه معنایی برای عملکرد دارد.

ملاحظات مربوط به عملکرد

ممکن است از خود بپرسید: «صبر کنید، آیا حلقه عامل از نظر مقدار JSON ارسال‌شده به Responses API در طول مکالمه درجه‌دو نیست؟» و حق با شما بود. در حالی که Responses API از یک پارامتر اختیاری previous_response_id(در یک پنجره جدید باز می‌شود) برای کاهش این مشکل پشتیبانی می‌کند، Codex در حال حاضر از آن استفاده نمی‌کند، عمدتاً برای اینکه درخواست‌ها کاملاً بدون حالت باقی بمانند و از پیکربندی‌های عدم ذخیره داده (ZDR) پشتیبانی شود.

اجتناب از previous_response_id کار را برای ارائه‌دهنده‌ی Responses API ساده‌تر می‌کند، زیرا تضمین می‌کند که هر درخواست بدون حالت باشد. این همچنین پشتیبانی از مشتریانی را که در عدم ذخیره داده (ZDR)(در یک پنجره جدید باز می‌شود) ثبت‌نام کرده‌اند، ساده می‌کند، زیرا ذخیره‌سازی داده‌های لازم برای پشتیبانی از previous_response_id با ZDR در تضاد خواهد بود. توجه داشته باشید که مشتریان ZDR توانایی بهره‌مندی از پیام‌های استدلال اختصاصی از نوبت‌های قبلی را از دست نمی‌دهند، زیرا encrypted_content مرتبط می‌تواند روی سرور رمزگشایی شود. (OpenAI کلید رمزگشاییِ مشتری ZDR را نگه می‌دارد، اما داده‌های او را نه.) برای مشاهده تغییرات مرتبط در Codex جهت پشتیبانی از ZDR، به درخواست های نظرسنجی #642(در یک پنجره جدید باز می‌شود) و #1641(در یک پنجره جدید باز می‌شود) مراجعه کنید.

به‌طور کلی، هزینه نمونه‌گیری از مدل بر هزینه ترافیک شبکه غالب است و نمونه‌گیری را به هدف اصلی تلاش‌های ما برای بهبود کارایی تبدیل می‌کند. به همین دلیل کش‌کردن دستور بسیار مهم است، زیرا به ما امکان می‌دهد محاسبات را از یک فراخوانی استنتاج قبلی مجدداً استفاده کنیم. هنگامی که بازیابی کش داریم، نمونه‌گیری از مدل به صورت خطی انجام می‌شود نه درجه‌دوم. مستندات ذخیره‌شه ما دستور(در یک پنجره جدید باز می‌شود)ما این موضوع را با جزئیات بیشتری توضیح می‌دهد:

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

با این نکته در ذهن، بیایید در نظر بگیریم چه نوع عملیات‌هایی می‌توانند باعث یک «cache miss» در Codex شوند:

  • تغییر ابزارها ی در دسترس مدل در میانهٔ مکالمه.
  • تغییر مدل که هدف درخواست API Responses است (در عمل، این تغییر مورد سوم در اعلان اصلی را تغییر می‌دهد، زیرا شامل دستورالعمل‌های خاص مدل است).
  • تغییر پیکربندی سندباکس، حالت تأیید یا دایرکتوری کاری کنونی.

تیم Codex باید هنگام معرفی ویژگی‌های جدید در Codex CLI که ممکن است کش کردن اعلان را به خطر بیندازد، کوشا باشد. به‌عنوان مثال، پشتیبانی اولیهٔ ما از ابزارهای MCP یک اشکالی را معرفی کرد که در آن نتوانستیم ابزارها را به ترتیبی سازگار فهرست کنیم(در یک پنجره جدید باز می‌شود)، که باعث عدم اصابت به کش می‌شد. توجه داشته باشید که ابزارهای MCP می‌توانند به‌ویژه پیچیده باشند، زیرا سرورهای MCP می‌توانند فهرست ابزارهایی را که ارائه می‌دهند، به‌صورت آنی از طریق اعلان notifications/tools/list_changed(در یک پنجره جدید باز می‌شود) تغییر دهند. پذیرفتن این اعلان در میانهٔ یک مکالمهٔ طولانی می‌تواند باعث یک عدم برخورد کشِ پرهزینه شود.

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

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

راهبرد کلی ما برای جلوگیری از تمام شدن پنجره زمینه این است که وقتی تعداد توکن‌ها از یک آستانه مشخص فراتر می‌رود، مکالمه را فشرده نماییم. به‌طور مشخص، ما ورودی را با یک فهرست جدید و کوچک‌تر از مواردی که نمایندهٔ گفتگو هستند جایگزین می‌کنیم و این به عامل امکان می‌دهد با درکی از آنچه تا اینجا رخ داده است، ادامه دهد. یک پیاده‌سازی اولیهٔ فشرده‌سازی(در یک پنجره جدید باز می‌شود) از کاربر می‌خواست که دستور /فشرده را به‌صورت دستی اجرا کند، که از طریق Responses API با استفاده از مکالمهٔ موجود به‌همراه دستورالعمل‌های سفارشی برای خلاصه‌سازی(در یک پنجره جدید باز می‌شود) پرس‌وجو می‌کرد. Codex از پیام دستیارِ حاصل که شامل خلاصه به‌عنوان ورودی جدید برای نوبت‌های بعدی مکالمه(در یک پنجره جدید باز می‌شود) استفاده کرد.

از آن زمان، API Responses تکامل یافته است تا از یک /پاسخ‌ها/فشرده نقطه پایانی(در یک پنجره جدید باز می‌شود) ویژه پشتیبانی کند که فشرده‌سازی را به‌صورت کارآمدتری انجام می‌دهد. این فهرستی از آیتم‌ها(در یک پنجره جدید باز می‌شود) را برمی‌گرداند که می‌توان از آن به‌جای ورودی قبلی برای ادامهٔ گفتگو استفاده کرد، در حالی که پنجره زمینه را آزاد می‌کند. این فهرست شامل یک مورد ویژه type=compaction با یک مورد مبهم encrypted_content است که درک پنهان مدل از مکالمه اصلی را حفظ می‌کند. اکنون، Codex به‌طور خودکار از این نقطه پایانی برای فشرده‌سازی مکالمه استفاده می‌کند زمانی که auto_compact_limit(در یک پنجره جدید باز می‌شود) از حد مجاز فراتر رود.

بعدی

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

در حالی که حلقه عامل پایه Codex را فراهم می‌کند، این تنها آغاز کار است. در پست‌های آینده، به معماری CLI عمیق‌تر خواهیم پرداخت، بررسی خواهیم کرد که استفاده از ابزارها چگونه پیاده‌سازی شده است، و نگاهی دقیق‌تر به مدل سندباکسینگ Codex خواهیم انداخت.

نویسنده

Michael Bolin

تقدیر و تشکر

تشکر ویژه از کل تیمی که Codex CLI را ساخت.