پی در نسخه‌ی 1.0.3 دیگر فقط یک کلاینت مدل نیست، بلکه یک هارنس است که حلقه‌ی ایجنت، رابط ترمینال و CLI کدنویسی را یک‌جا می‌دهد. در این پست با فرمان npm install -g --ignore-scripts @earendil-works/pi-coding-agent نصبش می‌کنیم، با یک فایل models.json به یک سرور محلی وصلش می‌کنیم، و یک حلقه‌ی ابزار کامل را بدون هزینه اجرا می‌کنیم: مدل یک بار bash را صدا می‌زند، پی فایل را می‌نویسد، و عدد واقعی 8 بایت را از خروجی ابزار پس می‌گیرد. دو نکته که در README نیست و در همین اجرا ثابت شد: بسته از @mariozechner به @earendil-works منتقل شده و هر دو فرمان pi را می‌سازند، پس نصب دوم با EEXIST می‌شکند. همه‌ی عددها لحظه‌ی خواندن: ۵ اکتبر ۲۰۲۶.

نصب: بسته عوض شده، نام فرمان نه

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

$ npm install -g --ignore-scripts @earendil-works/pi-coding-agent
added 121 packages in 14s
$ pi --version
1.0.3

پی با Node نسخه‌ی 22.19 یا بالاتر کار می‌کند. روش دوم، نصب‌کننده‌ی رسمی با curl -fsSL https://pi.dev/install.sh | sh است که وابستگی‌ها را pin می‌کند و با pi update به‌روز می‌ماند؛ روش npm این کار را نمی‌کند و به همین دلیل دو مسیر جدا هستند.

اینجا اولین تله‌ی واقعی این اجرا رخ می‌دهد. بسته‌ی قدیمی @mariozechner/pi-coding-agent هنوز روی نسخه‌ی 0.73.1 در npm منتشر شده، ولی از آن‌جا که هر دو بسته فرمان pi را در یک مسیر می‌سازند، نصب دوم روی نصب اول می‌شکند:

$ npm install -g @earendil-works/pi-coding-agent
npm error code EEXIST
npm error path .../bin/pi
npm error EEXIST: file already exists

# راه درست: اول بسته‌ی قدیمی را بردارید، بعد نصب کنید
$ npm uninstall -g @mariozechner/pi-coding-agent
removed 188 packages in 2s
$ npm install -g --ignore-scripts @earendil-works/pi-coding-agent
added 121 packages in 14s
$ pi --version
1.0.3

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

بدون کلید API: وصل کردن به یک سرور محلی

پی چهار API متفاوت را می‌فهمد و برای هر کدام مسیر جدا دارد: openai-completions، openai-responses، anthropic-messages و google-generative-ai. اگر مدل محلی دارید یا می‌خواهید بدون هزینه حلقه‌ی ابزار را تمرین کنید، همین چهار API سطح کاری است که لازم دارید. هر ارائه‌دهنده‌ی سازگار با OpenAI، از جمله Ollama و vLLM، از مسیر اول استفاده می‌کند.

پیکربندی در یک فایل به نام models.json در مسیر ~/.pi/agent/ می‌نشیند و این فایل در مستند مدل‌های سفارشی توضیح داده شده. کمترین پیکربندی، چهار فیلد در سطح ارائه‌دهنده و یک id در سطح مدل است:

mkdir -p ~/.pi/agent
cat > ~/.pi/agent/models.json <<'JSON'
{
  "providers": {
    "pi-lab": {
      "baseUrl": "http://127.0.0.1:8848/v1",
      "api": "openai-completions",
      "apiKey": "lab-key",
      "authHeader": true,
      "models": [
        { "id": "pi-lab-demo", "name": "pi-lab demo", "reasoning": false,
          "input": ["text"], "contextWindow": 128000, "maxTokens": 4096 }
      ]
    }
  }
}
JSON

# pi مدل را از همین فایل می‌خواند، بدون نیاز به ورود به حساب
$ pi --offline --list-models
provider  model        context  max-out  thinking  images
pi-lab    pi-lab-demo  128K     4.1K     no        no

دو نکته در همین خروجی دیده می‌شود. ستون context دقیقا از contextWindow می‌آید، و پی عدد را به شکل 128K گرد می‌کند نه 128000. ستون thinking هم از reasoning می‌آید، پس مدلی که استدلال را پشتیبانی نمی‌کند همان‌جا no می‌ماند.

مقدار apiKey برای Ollama و vLLM هم اجباری است، هرچند آن سرورها آن را نادیده می‌گیرند و هر رشته‌ی دلخواهی کافی است. اگر کلید واقعی دارید و نمی‌خواهید در فایل بنویسید، همان فیلد می‌تواند نام یک متغیر محیطی یا حتی یک فرمان پوسته باشد که خروجی‌اش به‌عنوان کلید خوانده می‌شود. ترتیب اولویت هم نام‌دار است: پرچم --api-key، بعد auth.json، بعد متغیر محیطی، و در آخر کلیدهای ارائه‌دهنده‌ی سفارشی از همین فایل.

اجرای حلقه‌ی ایجنت و گرفتن خروجی واقعی

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

$ pi --provider pi-lab --model pi-lab-demo -p \
    'Write marker.txt with the text pi-demo and report its size in bytes.'
The bash tool wrote the file and reported 8 bytes. DONE

$ cat marker.txt | wc -c
8

عدد ۸ از کجا آمد؟ از خود ابزار، نه از حدس مدل. پی فرمان printf 'pi-demo\n' > marker.txt && wc -c < marker.txt را در پوسته اجرا کرد، خروجی عدد ۸ را گرفت و آن را در نقش tool به مدل برگرداند. اگر فقط حرف مدل بود، عدد باید همان چیزی می‌بود که مدل حدس می‌زد.

# آنچه واقعا روی دیسک نوشته شد
$ ls -l marker.txt
-rw-r--r-- 1 root root 8 ... marker.txt

# و ترافیکی که پی به سرور محلی فرستاد
$ # نوبت ۱:  tools=['bash','edit','read','write']  roles=['system','user']
$ # نوبت ۲:  roles=['system','user','assistant','tool']

تفاوت دو نوبت، خود حلقه است. در نوبت اول فقط پیام‌های system و user وجود دارد. در نوبت دوم یک پیام assistant با فراخوانی ابزار و یک پیام tool با نتیجه اضافه شده است. یعنی پی خودش مدیریت کرد که نتیجه‌ی ابزار به مدل برگردد و مدل دوباره تصمیم بگیرد.

پی دقیقا چه چیزی می‌فرستد

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

$ # خلاصه‌ی درخواست اول
{ "model": "pi-lab-demo", "stream": true,
  "max_completion_tokens": 4096,
  "store": false,
  "stream_options": { "include_usage": true },
  "messages": [ { "role": "system", "content": "You are an expert coding
                  assistant operating inside pi, ..." },
                { "role": "user", "content": [{"type":"text","text":"Write
                  marker.txt ..."}] } ],
  "tools": [ { "name": "read",  "properties": ["path","offset","limit"] },
             { "name": "bash",  "properties": ["command","timeout"] },
             { "name": "edit",  "properties": ["path","edits"] },
             { "name": "write", "properties": ["path","content"] } ] }

سه جزئیات اینجا کار را می‌کنند. نخست، پی کلید را max_completion_tokens می‌فرستد نه max_tokens. اگر سرور شما فقط max_tokens را می‌فهمد، باید در پیکربندی compat.maxTokensField را روی max_tokens بگذارید. دوم، پی درخواست را به‌صورت جریانی می‌فرستد و include_usage را هم روشن می‌کند؛ سروری که این را پشتیبانی نکند باید compat.supportsUsageInStreaming را false کند. سوم، نام ابزارها دقیقا همان چیزی است که در فراخوانی برمی‌گردد، پس نام‌گذاری را دست نزنید.

متن سیستم هم قابل خواندن است و دلیل رفتار پی را توضیح می‌دهد. اولین سطر آن می‌گوید پی یک هارنس ایجنت است و ابزارهای read، bash، edit و write را به شکل فهرست توضیح می‌دهد. یعنی شما با --system-prompt می‌توانید قواعد پروژه را جلوتر بیاورید.

چه وقت پی را به چه چیزی وصل کنیم

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

سطحمسیرهزینهکاربرد
اشتراک/login با ChatGPT Plus، Claude Pro/Max یا Copilotشامل اشتراککار روزمره با همان اشتراک
کلید APIمتغیر محیطی ارائه‌دهندهمتغیراتوماسیون و اسکریپت
مدل محلیmodels.json با openai-completions۰توسعه‌ی حلقه‌ی ابزار و تست بدون اینترنت
پروکسیbaseUrl روی ارائه‌دهنده‌ی آمادهمتغیرمسیریابی، لاگ و کنترل هزینه

دو نکته‌ی عملی از همین جدول بیرون می‌آید. اول، اگر فقط می‌خواهید منطق ایجنت را تغییر دهید و نه مدل را، سطح سوم کم‌هزینه‌ترین راه است: همین آزمایش با یک سرور محلی در چند دقیقه اجرا شد و هیچ درخواستی به سرویس بیرونی نرفت. دوم، برای عبور یک ارائه‌دهنده‌ی آماده از پروکسی نیازی به بازنویسی فهرست مدل‌ها نیست؛ کافی است baseUrl را روی همان ارائه‌دهنده بگذارید.

محیط‌های واقعی این تنظیم روشن‌اند. Ollama مسیر /v1/chat/completions را ارائه می‌کند و چون نقش developer را نمی‌فهمد، باید compat.supportsDeveloperRole را false بگذارید تا پی پیام سیستم را به‌صورت system بفرستد. همین قاعده برای vLLM هم صدق می‌کند و اگر سرور reasoning_effort را نمی‌فهمد، compat.supportsReasoningEffort هم باید خاموش شود. هر دو الگو در مستندات رسمی همان پروژه‌ها آمده است.

قاعده‌ای که از این آزمایش بیرون آمد

پی ارزشش را از تعداد مدل‌هایش نمی‌گیرد، بلکه از این می‌گیرد که حلقه‌ی ایجنت را از ارائه‌دهنده جدا کرده است. اگر ارائه‌دهنده یک لایه‌ی نازک JSON باشد، همان حلقه‌ای که برای یک مدل ابری می‌نویسید بدون تغییر روی Ollama، vLLM یا یک پروکسی داخلی هم کار می‌کند.

# سه فرمان برای بستن حلقه روی هر ماشینی
$ pi --version
1.0.3
$ pi --offline --list-models
provider  model        context  max-out  thinking  images
pi-lab    pi-lab-demo  128K     4.1K     no        no
$ pi --provider pi-lab --model pi-lab-demo -p 'list the files in this folder and stop'
# بدون کلید API، بدون درخواست بیرونی، یک حلقه‌ی ابزار کامل

اگر می‌خواهید ایجنت خودتان را روی همین حلقه بسازید، پیش از اینکه سراغ مدل بروید، models.json را به یک سرور محلی وصل کنید و هزینه‌ی اشتراک را کنار بگذارید. متن سیستم را هم از همان ابتدا با --system-prompt بنویسید، چون پی قواعد پروژه را پیش از هر فراخوانی ابزار به مدل می‌دهد و تغییر دادن آن در حلقه سخت‌تر است.

جزئیات بیشتر در ریپوی پی در گیت‌هاب، مستند ارائه‌دهنده‌ها و ترتیب حل کلید، راهنمای افزونه‌ها و مرجع تنظیمات است. اگر می‌خواهید بدانید یک حلقه‌ی ابزار چطور در یک هارنس دیگر ساخته می‌شود، پست چطور ایجنت را بدون کلید API با مدل ساختگی Mastra تست کنیم همان مسیر را در Mastra نشان می‌دهد، و چرا ایجنت بعد از یک فراخوانی ابزار خالی برمی‌گردد توضیح می‌دهد چرا همان حلقه در Vercel AI SDK زودتر متوقف می‌شود.

منابع

  1. ریپوی earendil-works/pi در گیت‌هاب؛ هارنس ایجنت با حلقه‌ی مشترک، رابط ترمینال و CLI کدنویسی، بازبینی: ۵ اکتبر ۲۰۲۶
  2. خروجی API گیت‌هاب برای همان ریپو؛ ستاره، فورک، مجوز و تاریخ آخرین push در لحظه‌ی خواندن، بازبینی: ۵ اکتبر ۲۰۲۶
  3. صفحه‌ی بسته‌ی @earendil-works/pi-coding-agent در npm؛ نسخه‌ی 1.0.3 و برچسب legacy-node20 روی 0.74.2، بازبینی: ۵ اکتبر ۲۰۲۶
  4. صفحه‌ی بسته‌ی قدیمی @mariozechner/pi-coding-agent؛ هشدار deprecation با ارجاع به بسته‌ی جدید، بازبینی: ۵ اکتبر ۲۰۲۶
  5. مستند شروع سریع پی؛ خط نصب رسمی با --ignore-scripts و حداقل نسخه‌ی Node، بازبینی: ۵ اکتبر ۲۰۲۶
  6. مستند مدل‌های سفارشی پی؛ ساختار فایل models.json، چهار API پشتیبانی‌شده و جدول کامل کلیدهای compat، بازبینی: ۵ اکتبر ۲۰۲۶
  7. مستند ارائه‌دهنده‌های پی؛ ترتیب حل کلید و مسیر ارائه‌دهنده‌های سفارشی، بازبینی: ۵ اکتبر ۲۰۲۶
  8. راهنمای افزونه‌های پی؛ تعریف ابزار و فراخوانی افزونه، بازبینی: ۵ اکتبر ۲۰۲۶
  9. مرجع تنظیمات پی؛ کلیدهای پیکربندی و مسیر پرونده‌ها، بازبینی: ۵ اکتبر ۲۰۲۶
  10. سازگاری OpenAI در Ollama؛ مسیر /v1/chat/completions و محدودیت نقش developer، بازبینی: ۵ اکتبر ۲۰۲۶
  11. سرور سازگار با OpenAI در vLLM؛ مسیر سرو و محدودیت پارامترهای استدلال، بازبینی: ۵ اکتبر ۲۰۲۶
  12. مرجع ساخت Chat Completion در OpenAI؛ نام فیلد max_completion_tokens و شکل فراخوانی ابزار، بازبینی: ۵ اکتبر ۲۰۲۶