arc/docs/AI_AGENT_RUNTIME_CAPABILITIES_SCENARIO.md

162 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# سناریوی قابلیت‌های runtime ایجنت — subagent، ابزار موازی، context پایدار
**تاریخ:** ۱۴۰۵/۰۵/۲۸ (۱۸ اوت ۲۰۲۶)
**وضعیت:** فازهای PRM-04 ۰–۱، TOOL-06 A–B+D، AGT-06 ۰–۲ پیاده شد — UI موازی و دانش-به‌ابزار موکول
**مرجع ممیزی:** [`AI_AGENT_SYSTEM_AUDIT.md`](AI_AGENT_SYSTEM_AUDIT.md) (`AGT-06`، `TOOL-06`، `PRM-04`)
**رقیب مرجع:** Cursor Task/subagent، Claude Code، ChatGPT agent teams، OpenAI Agents SDK
این سند پاسخ خط‌به‌خط به سه سوال محصول است و برای هر شکاف فاز اجرا با معیار پذیرش می‌دهد. تا وقتی آیتم ممیزی `انجام‌شده` نشده، پیاده‌سازی شروع نشود مگر مالک صریح بخواهد.
---
## ۱. جمع‌بندی سریع
| قابلیت | در سیستم امروز | کجا | شکاف با سطح جهانی |
|--------|-----------------|-----|-------------------|
| ساخت و هدایت subagent | **هست (فاز ۰–۲)** | `spawn_subagent` / `await_subagent` / `cancel_subagent` روی حلقهٔ تو در تو analyzer | UI تایم‌لاین و persist SQL هنوز نیست |
| چند ابزار در یک نوبت، موازی | **هست (read)** | مدل می‌تواند چند `tool_calls` بدهد؛ بک‌اند read را تا ۴تایی موازی و write را سریال اجرا می‌کند | UI موازی‌بودن را نشان نمی‌دهد؛ نوبت اول سوال `complex` با `tool_choice` روی یک ابزار plan قفل می‌شود |
| تکرار بینش/حافظه/دانش در هر سوال | **لایهٔ semi_static cacheپذیر** | `static` + `semi_static` (حافظه/بینش/کانکتور) + `dynamic` (datetime/دانش/…) | دانش سوال‌وابسته هنوز در system است اگر RAG لازم باشد |
**نتیجه برای فرض سوم:** فرض «این اطلاعات داخل تاریخچهٔ گفت‌وگو تکرار می‌شود» دقیق نیست. پیام‌های ذخیره‌شدهٔ جلسه فقط user/assistant/tool هستند. آنچه تکرار می‌شود **بلاک runtime سیستم** است که بدون آن مدل در درخواست بعدی آن را به یاد ندارد. راه‌حل درست حذف از تاریخچه نیست (آنجا نیست)؛ راه‌حل **لایهٔ cache نیمه‌پایدار** و ارسال مجدد فقط وقتی محتوا عوض شده است.
---
## ۲. یافتهٔ ۱ — Subagent
### ۲.۱ آنچه در کد هست
- یک حلقهٔ ایجنت در `chat_completion_stream` با بودجه، todo، continuation، write guard.
- Run persist در `ai_agent_runs` و ادامه با `POST .../runs/{run_id}/continue` (AGT-01). هنوز همان درخواست/همان حلقه است (AGT-02).
- هیچ ابزار `spawn_subagent` / `delegate_task` در registry نیست.
- `GAP-07` (چند شخصیت حسابدار/انباردار) با این قابلیت یکی نیست: آن نقش ثابت است؛ subagent اجرای موقت با هدف و allowlist است.
### ۲.۲ رفتار مطلوب (Cursor / Claude)
1. والد تشخیص می‌دهد کار چنددامنه‌ای است (مثلاً فروش + موجودی + بدهکاران).
2. یک یا چند subagent با `goal`، `tool_allowlist`، `max_iterations`، `execution_mode=analyzer` ساخته می‌شود.
3. والد می‌تواند صبر کند، ادامه بدهد، یا subagent را قطع کند.
4. خروجی subagent یک envelope است (یافته‌ها + citations + خطا)؛ والد آن را نقد می‌کند و پاسخ نهایی را می‌سازد.
5. subagent حق write ندارد مگر والد صریحاً بسپارد (پیش‌فرض ممنوع).
### ۲.۳ فاز اجرا
| فاز | کار | معیار پذیرش | ریسک |
|-----|-----|-------------|------|
| ۰ — قرارداد ✓ | ابزار `spawn_subagent` + store درون‌پردازه‌ای `ai_subagent_runs` | schema در `function_registry` + intent + l10n | بدون UI هنوز |
| ۱ — حلقهٔ تو در تو ✓ | `AIService` با `operation=subagent`، سقف نوبت ۴، allowlist، `approve_writes=False` | تست واحد: دو read در فرزند؛ write در فرزند fail-closed | انفجار هزینه اگر سقف نباشد |
| ۲ — کنترل والد ✓ | tool result شامل `subagent_id`؛ ابزار `await_subagent` / `cancel_subagent` | والد بعد از cancel دیگر token فرزند را مصرف نکند | race با SSE والد |
| ۳ — ادغام | envelope استاندارد + citation از فرزند به والد | پاسخ نهایی بدون عدد بدون منبع از فرزند | نشت PII بین دامنه |
| ۴ — UI | گام `trace_step` با kind=`subagent` در تایم‌لاین | کاربر ببیند کدام زیر-اجرا زنده/تمام/قطع است | شلوغی God Widget |
سقف پیشنهادی محصول: حداکثر **۲** subagent همزمان، حداکثر **۴** نوبت ابزار برای هر کدام، فقط کانال چت درون‌برنامه (تلگرام/CRM/ورک‌فلو نه در فاز ۱).
**سناریوی پذیرش دستی**
1. «گزارش فروش این ماه، موجودی کالاهای کم، و سه بدهکار برتر را یکجا بده.»
2. انتظار: والد plan می‌سازد؛ حداقل یک spawn؛ نتایج در پاسخ نهایی با منبع.
3. کاربر Stop روی والد: فرزند هم cancel شود.
4. سوال ساده «موجودی کالای X؟» نباید spawn کند.
---
## ۳. یافتهٔ ۲ — اجرای موازی ابزار
### ۳.۱ آنچه در کد هست (انجام‌شده)
- اگر مدل در **یک** پیام assistant چند `tool_calls` بدهد، `handle_function_calls_async` آن‌ها را به `run_tool_calls_partitioned` می‌دهد.
- Read حداکثر `MAX_PARALLEL_READ_TOOLS = 4` با semaphore؛ هر write سد سریال است؛ ترتیب مدل حفظ می‌شود.
- Prompt مسیریابی صریحاً می‌گوید: «ابزارهای read-only مستقل را در یک نوبت parallel صدا بزن» (`ai_tool_routing_prompt.py`).
- `parallel_tool_calls=false` در OpenAI و `disable_parallel_tool_use` در Anthropic **ست نشده** — یعنی ارائه‌دهنده موازی را اجازه می‌دهد.
- تست: `tests/test_ai_tool_parallel.py`.
### ۳.۲ آنچه نیست
- UI: تایم‌لاین ابزارها را پشت‌سرهم نشان می‌دهد حتی اگر موازی اجرا شده باشند.
- نوبت صفر سوال `complex`: `tool_choice` روی `create_session_plan` است → در آن نوبت موازی عملاً قفل است (عمدی).
- مدل گاهی هنوز سریالی کار می‌کند؛ این محدودیت مدل است نه بک‌اند.
### ۳.۳ فاز اجرا (تکمیل، نه ساخت از صفر)
| فاز | کار | معیار پذیرش |
|-----|-----|-------------|
| A — مشاهده ✓ | لاگ `AI_METRIC` با `tool_calls_in_round` و `parallel_read_count` | رویداد `tool_round_parallel` |
| B — eval ✓ | کیس طلایی «فروش+موجودی+بدهکار» assertion که حداقل دو tool در یک round باشد | `ai_eval_assertions` + `DEFAULT_EVAL_CASES` |
| C — UI | در trace، ابزارهای هم‌نوبت با یک bundleId نمایش موازی | کاربر زمان انتظار را کوتاه‌تر حس کند |
| D — سقف ✓ | اگر مدل >۴ read بدهد، همان semaphore فعلی کافی است؛ در prompt سقف را بنویس | stampede DB رخ ندهد |
**سناریوی پذیرش دستی**
1. همان سوال چنددامنه‌ای پس از ساخته‌شدن plan.
2. در لاگ سرور یک round با ≥۲ function هم‌زمان (زمان شروع هم‌پوشان).
3. Write (مثلاً ایجاد فاکتور) هرگز با write دیگر موازی نشود.
---
## ۴. یافتهٔ ۳ — بینش و context در هر سوال جدید
### ۴.۱ مسیر واقعی هر پیام کاربر
```
POST .../messages?stream=true
→ persist پیام user
→ build_system_prompt_stream
static_core = نقش + سیاست untrusted ← cacheپذیر
business_anchor = شناسه کسب‌وکار + تقویم ← cacheپذیر
semi_static = حافظه + بینش + کانکتور ← cacheپذیر جدا
execution/plan = حالت اجرا + todos
runtime = datetime، دانش، مهارت، پیوست، todo
→ تاریخچه جلسه (حداکثر ۴۰ پیام؛ در صورت تنگی بودجه خلاصه)
→ حلقهٔ مدل + ابزار
```
بینش (`format_insights_for_prompt`) حدود چند خط KPI است (فروش امروز/هفته، بدهکاران، کم‌موجود) و با TTL **۳۰۰ ثانیه** در حافظهٔ فرایند کش می‌شود تا SQL تکرار نشود. **متن همان KPI هر نوبت دوباره در system می‌آید.**
دانش فقط اگر `query_needs_knowledge(user_query)` باشد لود می‌شود — یعنی وابسته به سوال است و نباید cache بلندمدت شود.
تاریخچهٔ DB شامل بینش نیست. پس «تکرار در تاریخچه» رخ نمی‌دهد؛ «تکرار در ورودی مدل» رخ می‌دهد و برای HTTP stateless طبیعی است.
Prompt cache (`ai_prompt_cache.py`) دو breakpoint دارد: `static_core + business_anchor` و لایهٔ `semi_static` (حافظه/بینش/کانکتور). datetime و دانش در `dynamic_system` می‌مانند.
### ۴.۲ راه‌حل پیشنهادی (ترتیب امن)
نباید بینش را به‌کلی حذف کرد: مدل بدون آن در سوال «وضعیت امروز چطوره؟» مجبور به چند tool اضافه می‌شود. هدف کاهش **توکن تکراری** است نه حذف قابلیت.
| فاز | کار | معیار پذیرش |
|-----|-----|-------------|
| ۰ — اندازه ✓ | در `context_usage` فیلدهای `runtime_tokens` / `insights_tokens` / `static_tokens` / `semi_static_tokens` | عدد واقعی در لاگ `AI_METRIC context_usage` |
| ۱ — لایهٔ نیمه‌پایدار ✓ | `semi_static` = بینش + حافظهٔ دستورات + کانکتور؛ hash محتوا در cache_key | برای Anthropic، `cache_read_tokens` در نوبت ۲+ همان جلسه > ۰ وقتی KPI عوض نشده |
| ۲ — دانش فقط با ابزار | دانش را از system درآور؛ `search_knowledge` اجباری وقتی RAG لازم است | سوال غیر دانش‌محور runtime بدون بلوک دانش |
| ۳ — datetime جدا ✓ | ساعت همیشه dynamic بماند (نباید کل cache را بشکند) | breakpoint cache قبل از datetime |
| ۴ — اختیاری | اگر hash بینش عوض شد (فروش جدید)، یک خط «بینش به‌روز شد» در runtime | مدل عدد کهنه ندهد |
**آنچه نباید کرد**
- چسباندن بینش به پیام assistant در تاریخچه (تاریخچه را آلوده می‌کند و با خلاصه قاطی می‌شود).
- حذف کامل بینش از نوبت‌های بعدی بدون cache لایهٔ سیستم (مدل کور می‌شود).
- گذاشتن دانش بازیابی‌شدهٔ هر سوال داخل prefix ثابت (cache غلط و پاسخ کهنه).
**سناریوی پذیرش دستی**
1. دو سوال پشت‌سرهم در یک جلسه: «سلام» سپس «فروش امروز چقدر بود؟»
2. انتظار: نوبت دوم SQL بینش نزند اگر TTL نگذشته (کش ۳۰۰ثانیه).
3. پس از فاز ۱: usage نوبت دوم `cache_read` داشته باشد؛ متن بینش در پیام‌های DB نباشد.
---
## ۵. ترتیب پیشنهادی اجرا
1. **PRM-04 فاز ۰–۱** ✓
2. **TOOL-06 تکمیل A–B** ✓ (+ سقف prompt فاز D)
3. **AGT-06 فاز ۰–۲** ✓ — فاز ۳–۴ (ادغام citation در UI / تایم‌لاین) بعدی است.
وابستگی: subagent موازیِ چند فرزند روی همان `run_tool_calls_partitioned` سوار می‌شود؛ سقف ۲ همزمان و ۴ نوبت اعمال شده.
---
## ۶. چک‌لیست PR وقتی یکی از فازها پیاده شد
- [x] ابزار جدید: registry + permission + intent + l10n + write guard
- [x] تست واحد مسیر اصلی + مسیر شکست (cancel subagent، write در فرزند، cache miss)
- [x] کیس eval یا سناریوی دستی بالا
- [x] به‌روز کردن وضعیت آیتم در ممیزی و یک سطر تاریخچه
- [x] متریک `AI_METRIC` برای spawn / parallel_round / cache_hit