65 KiB
Dynamic Tool Discovery — Phase 0 Analysis & Architecture
وضعیت سند: Phase 0 — فقط بررسی و طراحی؛ هیچ کد منبعی در این مرحله تغییر نکرده است.
تاریخ بررسی: ۱۹ اوت ۲۰۲۶ (۱۴۰۵/۰۵/۲۸)
دامنه کد: hesabixAPI/app/services/ai/، hesabixAPI/adapters/api/v1/ai/، مدلهای ai_*، تستهای tests/test_ai_tool_*.py
اسناد مرتبط: ../AI_AGENT_SYSTEM_AUDIT.md، ../AI_EXECUTION_PHASES.md، ../AI_AGENT_RUNTIME_CAPABILITIES_SCENARIO.md، معماری هدف: tool-discovery-architecture.md
قانون این سند: هر ادعای «وضعیت فعلی» باید از کد آمده باشد. اگر چیزی از روی کد قابل اثبات نبود، با برچسب Unknown / نیازمند بررسی آمده است.
1. Executive Summary
سیستم فعلی یک Accounting Agent چندنوبتی است که ابزارها را در یک Registry مرکزی Singleton ثبت میکند، قبل از ارسال به LLM با نقش + Permission + Intent کلیدواژهای فیلتر میکند، و اجرا را از مسیر Write Guard + Execution Mode رد میکند.
عدد «حدود ۱۲۰۰ Tool» با کد فعلی مطابقت ندارد.
| ادعا | واقعیت از کد |
|---|---|
| ~۱۲۰۰ ابزار AI | ۱۸۰ AIFunction یکتا با name= در Registry |
| همه Toolها در هر درخواست به LLM میروند | خیر. پس از Permission، Intent و سقف: حداکثر ۴۸ (حالت تحلیلگر) یا ۱۲۸ (حالت با تأیید / خودکار) |
| Schema همه ابزارها همیشه در Context است | خیر. فقط زیرمجموعهٔ فیلترشده. اما همان زیرمجموعه در هر نوبت حلقهٔ Agent دوباره به API مدل فرستاده میشود |
| MCP برای بارگذاری پویای Tool به داخل Agent | خیر. MCP فعلی یک سرور خروجی است (POST /api/v1/ai/mcp) که همان Registry را به کلاینت خارجی میدهد |
| وابستگی به یک Provider | خیر از نظر Abstraction. کلاینتها: OpenAIProvider، AnthropicProvider، LocalProvider. مدل زنده از دیتابیس (ai_configs / ai_models) خوانده میشود |
مشکل اصلی مقیاس Tool، تعداد ۱۲۰۰ ثبتشده نیست؛ چند منبع حقیقت پراکنده + سقف سخت ۴۸/۱۲۸ + ranking واژهای بدون embedding است. اگر کاتالوگ به چند هزار Tool برسد، همین معماری بدون لایهٔ Discovery معنایی و بدون Progressive Disclosure میشکند.
پیشنهاد سطح بالا: Registry فعلی را دور نریزید. آن را به یک Tool Catalog با Metadata واحد ارتقا دهید، فیلتر امنیتی را قبل از جستجو نگه دارید، Top-K را برای حالت تحلیلگر کاهش دهید، و برای مقیاس هزاران Tool یک meta-tool کشف اضافه کنید — نه اینکه هر Endpoint بکاند را یک Tool جدا کنید.
2. Current Architecture
پروژه یک بکاند FastAPI (hesabixAPI) و کلاینت Flutter (hesabixUI) است. Agent داخل بکاند اجرا میشود؛ UI فقط SSE/REST مصرف میکند.
flowchart TB
subgraph clients [Clients]
UI[Flutter AIChatDialog]
TG[Telegram AI]
CRM[CRM AI]
TKT[Support Ticket AI]
WF[Workflow AIAgentAction]
MCPC[MCP Client خارجی]
end
subgraph api [hesabixAPI FastAPI]
ChatAPI["POST /api/v1/ai/chat/sessions/{id}/messages"]
MCPAPI["POST /api/v1/ai/mcp"]
OtherAI["CRM / Ticket / Voice / Skills"]
end
subgraph agent [AI Runtime]
AIS[AIService]
Loop[chat_completion_stream Agent Loop]
Reg[AIFunctionRegistry singleton]
Prov[AIProviderBase]
end
subgraph data [Persistence]
PG[(PostgreSQL)]
MemCache[In-process tool cache]
end
UI --> ChatAPI
TG --> AIS
CRM --> OtherAI
TKT --> OtherAI
WF --> AIS
MCPC --> MCPAPI
ChatAPI --> AIS
OtherAI --> AIS
MCPAPI --> Reg
AIS --> Loop
Loop --> Prov
Loop --> Reg
Reg --> PG
Loop --> MemCache
2.1 Entry points
| سطح | مسیر | نقش |
|---|---|---|
| چت اصلی | adapters/api/v1/ai/chat.py → send_message |
حلقهٔ کامل + SSE |
| MCP | adapters/api/v1/ai/mcp.py |
JSON-RPC tools/list و tools/call |
| CRM | adapters/api/v1/ai/crm_ai.py |
Allowlist فقطخواندنی CRM، سقف ۴ نوبت |
| تیکت | مسیر support AI (سیاست در ai_channel_policy.py) |
Allowlist مالی فقطخواندنی، سقف ۴ نوبت |
| ورکفلو | اکشن Agent در workflow | سیاست جدا در ai_workflow_agent_policy.py |
| صوت | voice_ws.py / voice_http.py |
کانال جدا؛ جزئیات حلقهٔ Tool در این Phase بهصورت کامل trace نشد (نیازمند بررسی اگر صوت همان get_available_functions را صدا میزند) |
| تلگرام | سرویس تلگرام (اشارهشده در ممیزی) | جزئیات فیلتر Tool در این بررسی بهطور کامل باز نشد (Unknown / نیازمند بررسی) |
2.2 Backend stack مشهود از کد
| جزء | وضعیت در کد |
|---|---|
| API | FastAPI — app/main.py حدود ۱۰۰ include_router |
| DB | docker-compose.yml سرویس postgres:16. مهاجرت 20260626_000002_ai_pgvector_optional.py برای دانشنامه |
| Redis | در زیرسیستم AI استفاده نمیشود. کش Tool درونپردازهای است (ai_tool_cache.py). Redis در سایر بخشهای محصول (ادمین/باسلام) مستند شده |
| Auth | AuthContext + X-Business-ID + membership/owner checks |
| Tenant | business_id از session چت inject میشود؛ مدل نمیتواند tenant را عوض کند |
2.3 فهرست قابلیتهای Agent که در کد وجود دارند
| قابلیت | فایل اصلی | وضعیت |
|---|---|---|
| Agent Loop | ai_service.py chat_completion_stream |
حلقه while True با بودجه |
| LLM Client | ai_provider.py |
OpenAI / Anthropic / Local |
| Model Config | ai_config.py, ai_model.py, ai_model_service.py |
از DB؛ پیشفرض کلاس gpt-4 |
| Tool Registry | function_registry.py |
Singleton، ثبت در __init__ |
| Tool Definitions | ai_function_extensions*.py |
Static Python |
| Tool Schemas | JSON Schema داخل AIFunction.parameters_schema + ai_tool_payloads.py |
Static |
| Tool Execution | call_function / run_ai_registry_function |
Session DB جدا per call |
| Tool Dispatcher | handle_function_calls_async + ai_tool_parallel.py |
Read موازی تا ۴، Write سریال |
| MCP Server | ai_mcp_service.py |
هست |
| MCP Client (ورود Tool خارجی به Agent) | — | وجود ندارد |
| Memory | ai_memory_service.py, ai_memory_item_service.py, toolهای memory |
هست |
| RAG / Knowledge | ai_knowledge_service.py, ai_embedding_service.py |
Hybrid: کلمه + embedding اختیاری pgvector |
| Prompt Management | prompt_service.py, جدول ai_prompts |
هست |
| Context Management | ai_context_budget.py, ai_system_prompt.py |
بودجه ۲۸٬۰۰۰ توکن برای پیامها |
| Streaming | SSE در chat.py |
هست |
| Logging / Metrics | ai_ops_metrics.py (AI_METRIC) |
هست؛ Langfuse/Phoenix نیست |
| Error Handling | ai_tool_error.py, ApiError |
هست |
| Retry | ai_retry_policy.py حداکثر ۳ بار |
هست |
| Caching | Prompt cache Provider + کش نتیجه Tool ۶۰ثانیه | هست |
| Conversation | ai_chat_session / ai_chat_message |
هست |
| Subagent | ai_subagent.py |
موقت، سقف ۲×۴ نوبت، بدون write |
3. Agent Architecture
قلب سیستم AIService.chat_completion_stream است (ai_service.py).
3.1 حلقهٔ فعلی
flowchart TD
A[User message + history] --> B[Quota / model / execution_mode]
B --> C[build structured system prompt]
C --> D[trim context budget 28k message tokens]
D --> E{resolve_round_tool_offer}
E -->|none| F[LLM بدون tools]
E -->|load| G[get_available_functions]
G --> H[Permission + Role]
H --> I{execution_mode}
I -->|analyzer| J[select_tool_names max 48]
I -->|supervised/autonomous| K[select_catalog_tool_names max 128 write protected]
J --> L[Full JSON schemas subset]
K --> L
F --> M[Agent loop while True]
L --> M
M --> N[LLM stream]
N --> O{tool_calls?}
O -->|no + text| P[Final answer / synthesis]
O -->|yes| Q[Write Guard]
Q --> R[Execute partitioned]
R --> S[Append tool results to messages]
S --> T{budget remaining?}
T -->|yes| M
T -->|no| P
رفتارهای مهم حلقه که از کد خوانده شد:
- بودجه چندبعدی: نوبت (۳/۶/۱۲ بر اساس complexity، سقف مطلق ۱۵)، توکن کل، wall-clock، ۲ نوبت بیحاصل، حداکثر ۲ تمدید (
ai_constants.py,ai_budget.py). - Planning: برای سوال
complexممکن استtool_choiceرویcreate_session_planقفل شود (OpenAI/Anthropic). - tool_choice=required: اگر سوال دادهمحور باشد و provider در
PROVIDERS_WITH_FORCED_TOOLSباشد. - Forced synthesis: اگر evidenc هست ولی متن نهایی نیامد، یک نوبت بدون Tool.
- Run persist: جدول
ai_agent_runsو continue API در ممیزی بهعنوان انجامشده آمده؛ حلقه همچنان داخل همان درخواست HTTP است (AGT-02 هنوز باز). - Subagent:
spawn_subagent/await_subagent/cancel_subagent؛ فرزند analyzer و بدون write.
3.2 LLM Client و مدل
Abstraction: AIProviderBase با chat_completion و chat_completion_stream.
| Provider type | کلاس | سازگاری Tool |
|---|---|---|
openai |
OpenAIProvider |
tools به سبک OpenAI Chat Completions |
anthropic |
AnthropicProvider |
تبدیل _openai_tools_to_anthropic |
local |
LocalProvider |
همان قرارداد OpenAI-compatible |
مدل hard-code نیست. منبع حقیقت:
AIConfig.model_nameپیشفرض کلاس:"gpt-4"- کاتالوگ
ai_modelsباcode,provider,model_id,supports_tools - Routing:
ai_model_router.py+ai_model_service.py(شامل مدل مجازیauto) - BYOK:
business_ai_provider_service.py
Unknown / نیازمند بررسی: مدل و Provider واقعی production (مقدار ردیفهای ai_configs / ai_models در این مرحله از روی کد قابل خواندن نیست).
estimate_tokens در هر سه Provider برابر است با len(text) // 4. این تقریب است نه tokenizer رسمی.
4. Current Tool Architecture
4.1 تعداد واقعی Toolها
شمارش استاتیک روی AIFunction(name="...") در فایلهای Registry:
| منبع ثبت | تعداد |
|---|---|
function_registry.py |
32 |
ai_function_extensions.py |
18 |
ai_function_extensions_phase2.py |
15 |
ai_function_extensions_phase3.py |
17 |
ai_function_extensions_phase4.py |
10 |
ai_function_extensions_phase5.py |
6 |
ai_function_extensions_phase6.py |
1 |
ai_function_extensions_phase7.py |
5 |
ai_function_extensions_phase8.py |
7 |
ai_function_extensions_phase10.py |
2 |
ai_function_extensions_phase11.py |
38 |
ai_function_extensions_workflow.py |
13 |
ai_function_extensions_hscript.py |
7 |
ai_function_extensions_memory.py |
3 |
ai_function_extensions_session_todos.py |
3 |
ai_function_extensions_subagent.py |
3 |
| جمع یکتا | ۱۸۰ |
نام تکراری در ثبت پیدا نشد (۱۸۰ register() و ۱۸۰ نام یکتا).
اسناد قدیمیتر (AI_CAPABILITY_GAP_SCENARIO_V2.md: ~۱۱۲، ممیزی: ~۱۰۰+) از کد امروز عقبترند.
4.2 عدد ۱۲۰۰ از کجا میتواند آمده باشد؟
در کد AI، ۱۲۰۰ Tool وجود ندارد. فرضهای محتمل که باید با محصول تأیید شوند (حدس بهعنوان واقعیت گزارش نمیشود):
| فرض | شواهد |
|---|---|
| هدف مقیاس آینده | مأموریت همین سند |
| اشتباه با Endpointهای REST | app/main.py حدود ۱۰۰ روتر؛ تعداد دقیق Route شمارش نشد (نیازمند بررسی اگر عدد ۱۲۰۰ از OpenAPI آمده) |
| گسترش درونی یک Tool | get_report حدود ۳۸ report_type؛ query_business_data حدود ۳۰ entity (۱۳ فاز۱ + ۱۰ فاز۲ + ۳ فاز۳ + ۴ فاز۴) |
| جمع Tool + entity + report بهصورت «قابلیت» | اگر کسی ۱۸۰ + ۳۸ + ۳۰ + نودهای workflow را با هم جمع کند باز به ۱۲۰۰ نمیرسد |
نتیجه قابل اتکا: سطح Tool قابل فراخوانی توسط LLM امروز ۱۸۰ function است، نه ۱۲۰۰.
4.3 مدل دادهٔ فعلی Tool
# function_registry.py — AIFunction
name: str
description: str
parameters_schema: Dict[str, Any]
handler: Callable
allowed_roles: Set[AIRole]
required_permissions: Optional[List[str]] = None
business_context_required: bool = True
category: Optional[str] = None
requires_approval: bool = False
risk_level: str = "safe" # safe / medium / high
is_readonly: bool = True
is_agent_internal: bool = False
آنچه در dataclass نیست و امروز در فایلهای جدا پخش شده:
| نیاز | محل فعلی |
|---|---|
| Intent category / keywords | ai_tool_intent.py _CATEGORY_TOOLS, _KEYWORD_CATEGORIES |
| Alias برای ranking | ai_tool_rank.py TOOL_ALIASES |
| Write list موازی | ai_tool_intent._WRITE_TOOLS و ai_write_guard.WRITE_FUNCTIONS |
| High-risk always-approve | ai_execution_policy.HIGH_RISK_ALWAYS_APPROVE |
| i18n | ai_tool_keys.py |
| Skill allowlist | جدول ai_skills / allowed_tool_names |
| Embedding | وجود ندارد برای Tool |
| Examples / few-shot | در Schemaها مشاهده نشد (example در توصیف/schema استاتیک صفر مورد) |
Namespace فراتر از category |
وجود ندارد |
Destructive جدا از risk_level و delete_* |
فیلد جدا نیست |
4.4 توزیع Metadata (شمارش AST روی ۱۸۰ ثبت)
| فیلد | مشاهده |
|---|---|
requires_approval=True |
۳۸ |
is_readonly=False |
۴۱ |
is_readonly پیشفرض True (صریح نشده) |
۱۰۱ |
is_agent_internal=True |
۶ |
بدون required_permissions |
۲۲ |
risk_level=high |
۲۳ |
risk_level=medium |
۱۷ |
risk_level پیشفرض safe |
۱۳۲ |
category خالی |
۰ در ثبتهای دیدهشده؛ مقادیر متنوع (business, invoices, financial, …) |
Schema با properties: {} یا خالی |
۵۹ |
| Examples در schema/description | ۰ |
category در Registry با کلیدهای Intent یکی نیست. مثلاً Registry invoices / persons دارد؛ Intent از financial / people استفاده میکند. دو طبقهبندی موازی است.
4.5 Static در برابر Dynamic
| نوع | وضعیت |
|---|---|
| ثبت Tool | Static — import-time در Singleton |
| Connector HTTP | یک Tool ثابت invoke_business_connector؛ لیست کانکتورها به prompt تزریق میشود نه به صورت Tool جدا |
| Skill | فیلتر/اتحاد نام Toolهای موجود؛ Tool جدید نمیسازد |
| MCP inbound | نیست |
| تولید خودکار از OpenAPI | نیست |
4.6 همپوشانی عملیاتی (Duplicate capability)
نام تکراری نیست، اما چند مسیر برای یک سؤال وجود دارد:
search_invoicesدر کنارquery_business_data(entity=invoice)get_sales_report/get_debtors_report/ … در کنارget_report(report_type=...)search_personsدر کنارquery_business_data(entity=person)
این همپوشانی عمدی (fan-in + dedicated) است، ولی انتخاب مدل را سختتر میکند.
4.7 Toolهایی که در نقشهٔ Intent نیستند
۲۱ نام ثبتشده در هیچ دستهٔ _CATEGORY_TOOLS نیستند (ممکن است فقط از مسیر write-protected catalog یا core نیایند):
create_account, create_check, create_invoice, create_product_attribute, create_transfer, delete_account, delete_check, delete_product_attribute, delete_transfer, get_wallet_metrics, list_announcements, list_business_notification_logs, list_business_notification_templates, list_business_users, list_frequent_descriptions, list_my_businesses, list_user_notifications, update_account, update_check, update_product_attribute, update_transfer
برخی از اینها در _WRITE_TOOLS هستند (create_invoice, create_check, …) و با کلیدواژهٔ نوشتن اضافه میشوند. بقیه در حالت تحلیلگر بهراحتی از دست میروند.
عملیات نوشتنی ثبتشده که در _WRITE_TOOLS نیستند:
create_product_attribute, create_session_plan, delete_check, delete_expense_income, delete_memory_entry, delete_product_attribute, delete_receipt_payment, delete_transfer, update_check, update_product_attribute, update_session_todo, update_transfer
create_session_plan / update_session_todo داخلی Agent هستند (is_agent_internal) و عمداً write کسبوکار نیستند. بقیه شکاف امنیتی/intent هستند.
5. Tool Registration Flow
sequenceDiagram
participant Imp as Python import
participant R as AIFunctionRegistry
participant Ext as register_*_functions
participant Mod as AIFunction._functions
Imp->>R: registry = AIFunctionRegistry()
R->>R: _register_all_functions once
R->>R: business/invoice/product/person/financial/crm
R->>Ext: phase2..phase11, workflow, hscript, memory, todos, subagent
R->>R: _register_connector_functions
Note over R,Mod: _register_operator/admin/owner = pass (خالی)
Ext->>Mod: registry.register(AIFunction)
نکات:
- Lazy/singleton: بار اول import، همه Toolها در حافظهٔ فرایند میمانند.
- Operator/Admin/Business-owner اختصاصی ثبت نشده (
pass). نقشها در enum هستند ولی کاتالوگ جدا ندارند (TOOL-02 ممیزی، وضعیت باز). - Schema یا از dict لغوی ساخته میشود یا از ثابتهای
ai_tool_payloads.py(فاکتور/شخص/کالا و …). - Description دستی و فارسی است؛ تولید خودکار از OpenAPI نیست.
6. Tool Execution Flow
sequenceDiagram
participant LLM
participant Loop as Agent Loop
participant Guard as Write Guard / Execution Policy
participant Cache as ai_tool_cache
participant DB as run_ai_registry_function
participant Reg as registry.call_function
LLM-->>Loop: tool_calls
Loop->>Guard: is_write? analyzer? high-risk? approved?
alt blocked
Guard-->>Loop: APPROVAL_REQUIRED / read-only mode
else allowed
Loop->>Cache: readonly hit?
Cache-->>Loop: maybe
Loop->>DB: new DB session
DB->>Reg: role + permission + inject business_id
Reg-->>Loop: result envelope
Loop->>Cache: set / invalidate on write
end
Loop->>LLM: role=tool JSON truncated
جزئیات اجرا:
- Unknown tool:
unknown_tool_result— مدل نام جعلی میسازد. - Analyzer:
should_block_write_in_analyzerاگرis_write_function(منبع:requires_approvalدر Registry؛ نام ناشناخته fail-closed = write فرض میشود). - Supervised / high-risk autonomous:
build_approval_required_resultتا UI تأیید بدهد؛ اجرای بعدی باapprove_writes+approved_write_callsو تطبیق آرگومان. - Session cache: فقط
is_readonly، TTL ۶۰ ثانیه، per(business_id, session_id)، درونپردازهای. - Parallelism:
MAX_PARALLEL_READ_TOOLS = 4؛ write همیشه سریال. - Tenant:
business_idاز session؛ اگر مدلbusiness_idدیگری بدهد فقط در صورتcan_access_businessلاگ میشود و handler باز هم session را استفاده میکند. - نتیجه: سقف حدود ۶۰۰۰ کاراکتر JSON و ۱۵ رکورد (
MAX_TOOL_RESULT_JSON_CHARS,MAX_TOOL_RESULT_RECORDS).
7. LLM Request Flow
flowchart LR
subgraph perUserTurn [یک پیام کاربر]
P[System prompt layered] --> H[History trimmed]
H --> T[tools JSON subset]
T --> API[Provider chat.completions / messages]
end
subgraph perIteration [هر نوبت Agent]
API --> CALLS[tool_calls]
CALLS --> EXEC[execute]
EXEC --> H
end
get_available_functions یکبار قبل از حلقه صدا زده میشود (ai_service.py حدود ۲۱۱۶–۲۱۴۲). سپس همان لیست tools در هر iteration به provider.chat_completion_stream(..., tools=tools) میرود (حدود ۲۲۹۵–۲۳۰۰).
یعنی:
- Schema Tool در هر نوبت LLM تکرار میشود.
- Intent در وسط حلقه بر اساس نتایج Tool دوباره محاسبه نمیشود (بازمحاسبهٔ پویا داخل loop وجود ندارد).
- اگر
resolve_round_tool_offerبگویدnone(سوال غیر دادهمحور و کالر لیست نداده)، اصلاً Tool نمیرود.
کانالهای جانبی اگر خودشان tools= بدهند، حلقه آن را keep میکند و Intent چت را جایگزین نمیکند (ai_channel_policy.resolve_round_tool_offer).
MCP tools/list Intent و سقف ۴۸/۱۲۸ را اعمال نمیکند؛ فقط Role + Permission + business_context. در مقیاس بزرگ این مسیر همهٔ Toolهای مجاز کاربر را یکجا برمیگرداند.
8. Current Token Consumption
8.1 چه چیزی وارد Context میشود؟
| لایه | محتوا | Cache |
|---|---|---|
| Static system | نقش از DB + business anchor | Prompt cache (Anthropic cache_control / OpenAI prompt_cache_key فقط api.openai.com) |
| Semi-static | حافظه + insights + connectors | لایهٔ نیمهپایدار |
| Dynamic system | execution_mode، datetime، knowledge، skills، attachments، todos | نه |
| Messages | تاریخچه تا ۴۰ پیام، trim در ۲۸k توکن تخمینی | conversation cache در صورت فعال بودن policy |
| tools parameter | JSON تعریف Functionها | جدا از system prompt؛ در بودجه ۲۸k پیام حساب نمیشود |
یافتهٔ مهم: ai_context_budget.estimate_messages_tokens محتوای tools را جمع نمیزند. هزینهٔ Schema Tool روی Context Window مدل هست، اما روی CONTEXT_INPUT_TOKEN_BUDGET دیده نمیشود.
8.2 اندازهگیری Schema (استاتیک)
روش: AST روی ۱۸۰ AIFunction، json.dumps معادل OpenAI tools array، تقریب توکن همان chars/4 که خود Provider استفاده میکند.
| سناریو | تقریب توکن Schema |
|---|---|
۱۴۵ Tool با schema لغوی قابل parse + بقیه {} |
~۱۶٬۴۰۸ برای هر ۱۸۰ |
| میانگین Toolهای parseشده | ~۹۱ توکن / Tool |
| ۴۸ Tool (سقف تحلیلگر) × میانگین | ~۴٬۴۰۰ |
| ۱۲۸ Tool (سقف autonomous) × میانگین | ~۱۱٬۷۰۰ |
| ۱۸۰ Tool کامل | ≥ ۱۶٬۴۰۰ |
| اگر کاتالوگ به ۱۲۰۰ Tool با همین میانگین برسد | ~۱۰۹٬۰۰۰ فقط تعریف Tool |
کمبرآوردی قطعی: ۱۹ schema تو در تو و چند ثابت CREATE_*_PARAMETERS_SCHEMA (فاکتور، شخص، کالا، دریافت/پرداخت) بهخاطر dict(...) / Call در AST بهصورت {} شمرده شدند. اینها معمولاً بزرگترین Schemaها هستند. عدد واقعی ۱۸۰ Tool بالاتر از ۱۶.۴k است.
اندازهٔ چند schema نامدار که parse شدند:
| ثابت | حدود توکن |
|---|---|
CREATE_WAREHOUSE_DOCUMENT_PARAMETERS_SCHEMA |
~۳۰۶ |
_INVOICE_LINE_ITEM_SCHEMA |
~۳۲۶ |
_CASH_SIDE_LINE_SCHEMA |
~۳۸۷ |
CREATE_CHECK_PARAMETERS_SCHEMA |
~۲۵۱ |
Unknown بدون اجرای runtime: توکن واقعی یک request زنده (system + history + tools + output) چون به مدل/تاریخچه/insights وابسته است.
8.3 روش اندازهگیری پیشنهادی برای Phase بعد (بدون حدس عدد زنده)
- تست واحد: instantiate
AIFunctionRegistryدر محیط pytest،get_function_definitionsبا یکAuthContextجعلی superadmin. json.dumps(definitions)وlen//4بهعلاوه در صورت وجودtiktokenبرای مدل واقعی.- سه snapshot: analyzer+query نمونه، supervised write، MCP
tools/list. - لاگ موجود
context_usageرا گسترش دهید تاtools_json_chars/tools_countهم برود (log_ai_event("context_usage", ...)امروزstatic_tokensوestimated_tokensپیام را دارد، نه tools). - از
usage.cached_tokensProvider برای اثر Prompt Cache استفاده کنید؛ Cache رویtoolsبسته به Provider است و در کد جداگانه تضمین نشده.
8.4 پاسخ به سؤالهای Token
| سؤال | پاسخ از کد |
|---|---|
| آیا Schema هر ۱۸۰/۱۲۰۰ به مدل میرود؟ | ۱۸۰ وجود دارد؛ همه نمیروند. سقف ۴۸ یا ۱۲۸ پس از فیلتر |
| چند Tool در هر Request؟ | ۰ اگر routing tools نخواهد؛ وگرنه ≤۴۸ یا ≤۱۲۸ |
| آیا هر Turn دوباره میرود؟ | بله، هر iteration حلقه |
| Prompt Caching؟ | بله برای system prefix؛ نه بهعنوان کش اختصاصی Tool definition |
| Tool Definitions Cache؟ | نه در سطح LLM. فقط نتیجهٔ اجرای read-only |
| Context Window تحت تأثیر Tool؟ | بله در API مدل؛ نه در بودجه داخلی ۲۸k |
| Tool نامرتبط هزینه را زیاد میکند؟ | بله در حد سقف ۴۸/۱۲۸ و ranking واژهای |
برای Context Window فرضی ۱۲۸k، ۴۸ Tool × ~۹۱ توکن ≈ ۳٪ فقط میانگین Schema است؛ ۱۲۸ Tool ≈ ۹٪. با Schemaهای نوشتن بزرگ این درصد بالاتر است. بودجهٔ داخلی ۲۸k پیام جدا است؛ اگر Toolها روی همان window بنشینند، جا برای history کمتر میشود.
9. Current Limitations
9.1 محدودیت تعداد Tool مدل
کد صریحاً سقف ایمنی میگذارد:
MAX_TOOLS_PER_REQUEST = 48 # analyzer پس از intent
MAX_TOOLS_AUTONOMOUS = 128 # supervised/autonomous؛ write حذف نمیشود
این سقفها در خود پروژه تعریف شدهاند، نه با خواندن limit زندهٔ API.
| موضوع | وضعیت کد |
|---|---|
| Tool Search / Deferred Tool Loading | نیست |
OpenAI tool_choice required / function |
هست |
| Anthropic tools + cache_control | هست |
| MCP به عنوان discovery داخل حلقه | نیست (فقط expose بیرونی) |
| وابستگی به API خاص | قرارداد داخلی OpenAI-shaped است؛ Anthropic تبدیل میشود. Local باید OpenAI-compatible باشد |
Unknown: حد دقیق مدل production (بعضی gatewayها مثل vLLM با function_calling_enabled=False اصلاً tools نمیگیرند — این فلگ در AIConfig هست).
محدودیت عملی امروز بیشتر Request size / Context است تا «فقط ۱۲۸ function»، چون حتی ۴۸ Schema سنگین نوشتن میتواند گران باشد. کد با ۱۲۸ بهعنوان سقف ایمنی Provider طراحی شده؛ اگر کاتالوگ از ۱۲۸ Tool مجاز کاربر بیشتر شود، rank_and_cap_tool_names بقیه را حذف میکند مگر protected (writeها). در autonomous با تعداد write زیاد، مجموعهٔ protected میتواند از ۱۲۸ هم بزرگتر شود (تابع صریحاً protected را هرگز بهخاطر سقف حذف نمیکند).
9.2 محدودیتهای انتخاب
- Intent = regex فارسی/انگلیسی + دسته. سوال مبهم → fallback
financial, warehouse, crm, misc. - Ranking = overlap توکن نام + alias محدود (
TOOL_ALIASESحدود ۵۰ Tool، نه ۱۸۰). - Eval طلایی: ۱۶+ کیس در
test_ai_tool_intent_gold.pyبا recall ≥ ۹۵٪ روی کاتالوگ ساختگی intent، نه لزوماً هر ۱۸۰ Tool واقعی. - Skills با Intent اتحاد میشوند نه جایگزینی؛ progressive disclosure کامل skill-first نیست.
10. Identified Problems
- چند منبع حقیقت برای یک Tool: Registry،
_CATEGORY_TOOLS،_WRITE_TOOLS،WRITE_FUNCTIONS،HIGH_RISK_ALWAYS_APPROVE،TOOL_ALIASES،TOOL_L10N_KEYS، permission aliases. اضافه کردن Tool جدید بهراحتی ناقص میماند. - سقف ۴۸/۱۲۸ بدون Discovery دومرحلهای: مدل یا Tool را نمیبیند یا کاتالوگ شلوغ میبیند.
- Intent کلیدواژهای شکننده است برای زبان محاوره، غلط املایی، و دامنههای کمکلیدواژه (اعلانها، کاربران کسبوکار، ویژگی کالا).
- ۲۱ Tool خارج از نقشهٔ دسته و چند delete/update خارج از
_WRITE_TOOLS. - Schema در بودجه Context داخلی نیست → overflow واقعی مدل دیرتر از trim داخلی دیده میشود.
- تکرار tools در هر iteration بدون cache تضمینی سمت Provider برای خود آرایهٔ tools.
- همپوشانی query_business_data / search_ / get_report* انتخاب را پخش میکند.
- MCP tools/list بدون سقف Intent — مسیر مقیاسنشده.
- کش Tool درونپردازهای — با چند worker و ۱۰k کاربر به اشتراک گذاشته نمیشود.
- God Object:
ai_service.pyوfunction_registry.pyبزرگ؛ ثبت Tool پراکنده در ۱۵ فایل. - Examples/few-shot per tool وجود ندارد.
- Operator/Admin catalog خالی است.
- Destructive policy ناقص:
HIGH_RISK_ALWAYS_APPROVEشاملdelete_invoiceوdelete_personهست، اماdelete_check/delete_transfer/delete_expense_income/delete_receipt_payment/delete_accountدر آن مجموعه نیستند (ممکن است باrisk_level=highدر Registry پوشش داده شوند — برای هر نام باید در Phase 1 جدول تطبیق زده شود). - عدد ذهنی ۱۲۰۰ با معماری فعلی ناسازگار است؛ اگر هدف محصول هزاران Tool جدا باشد، ثبت 1:1 با Endpoint اشتباه معماری است.
11. Root Cause Analysis
علائم
├─ Token بالا وقتی سوال ساده است ولی ۴۸ Tool میرود
├─ مدل Tool اشتباه / hallucinate نام
├─ Tool جدید در چت دیده نمیشود
└─ نگرانی از سقف Provider
علت نزدیک
├─ فیلتر Intent همهٔ دستهٔ financial را یکجا باز میکند نه Top-K تنگ
├─ Metadata جستجوپذیر واحد نیست
└─ Full schema همان لحظهٔ اول به LLM داده میشود
علت ریشه
├─ طراحی «لیست Function برای Function Calling» بهجای «کاتالوگ قابل جستجو + افشای تدریجی»
├─ رشد تدریجی فاز ۰–۱۱ بدون لایهٔ Discovery
└─ امنیت و Intent بهصورت لیستهای موازی بهجای یک record
این مشکل با «اضافه کردن pgvector و تمام» حل نمیشود. اول باید یک رکورد Tool و سیاست امنیتی واحد باشد؛ بعد جستجو.
12. Proposed Architecture
معماری پیشنهادی امتداد سیستم فعلی است، نه جایگزینی کورکورانه با pipeline نمونه.
اصول:
- Security-first: Tenant → Role → Permission → Execution mode → سپس جستجو.
- Fan-in را حفظ کنید:
query_business_dataوget_reportبهتر از ۱۲۰۰ Tool ریز هستند. - دو سطح افشا: (الف) نام + یک خط توضیح برای جستجو؛ (ب) Schema کامل فقط برای Top-K.
- Keyword فعلی را دور نریزید؛ آن را لایهٔ ارزان اول کنید.
- Embedding را روی زیرساخت دانشنامهٔ موجود بگذارید نه Vector DB جدید در Phase اول.
- Provider-agnostic بماند: کشف داخل Hesabix؛ به LLM فقط OpenAI-shaped subset.
flowchart TB
U[User query + session] --> SEC[Tenant / Role / Permission / execution_mode]
SEC --> CAND[Candidate catalog از Registry]
CAND --> INT[Intent keywords + history + skills موجود]
INT --> HYB[Hybrid rank: keyword score + optional embedding]
HYB --> POL[Policy: destructive فقط با intent صریح]
POL --> K[Top-K schemas]
K --> LLM[LLM]
LLM --> META{نیاز به Tool خارج از K?}
META -->|search_tools meta| HYB
META -->|tool_calls| EX[Write guard + execute]
چرا این بهتر از «فقط Semantic Search روی ۱۲۰۰ Tool» است:
- Permission حسابداری را نمیتوان با cosine similarity تضمین کرد.
- Regex فعلی برای دامنهٔ فارسی حسابداری ارزش دارد و تست طلایی دارد.
- ۱۸۰ Tool هنوز در محدودهٔ ranking هیبرید سبک است؛ ۱۲۰۰+ نیاز به meta-tool دارد.
12.1 مقایسهٔ جایگزینها
| گزینه | شرح | مناسب این کد؟ |
|---|---|---|
| A. ارسال همهٔ Toolها | ساده | خیر؛ همین حالا سقف ۱۲۸ گذاشتهاند |
| B. Intent-only فعلی | ارزان | تا ~۲۰۰ Tool قابل تحمل؛ برای هزاران خیر |
| C. فقط Vector Search | معنایی | بدون فیلتر permission خطرناک؛ cold-start فارسی ضعیف |
| D. Router مدل دوم | LLM کوچک برای انتخاب دسته | هزینه و تأخیر اضافه؛ فعلاً لازم نیست |
| E. MCP Tool Search داخل حلقه | استاندارد نوظهور | MCP فعلی سرور خروجی است؛ وابستگی Provider |
| F. Hybrid + Top-K + meta-tool (پیشنهادی) | امتداد Registry + intent + embedding اختیاری | بیشترین سازگاری با کد موجود |
| G. یک Tool بهازای هر REST endpoint | انفجار کاتالوگ | ضد هدف token و امنیت |
13. Dynamic Tool Discovery Design
13.1 جریان پیشنهادی Request
sequenceDiagram
participant User
participant API as chat.py
participant Disc as ToolDiscovery
participant Reg as Registry
participant LLM as Provider
participant Ex as Executor
User->>API: message
API->>Reg: all functions for role+permission
API->>Disc: query, history, mode, skills
Disc->>Disc: keyword categories + BM25/alias
opt embeddings enabled
Disc->>Disc: vector rerank on remaining
end
Disc->>API: top K names + maybe search_tools
API->>LLM: K full schemas
LLM->>API: tool_call
alt name in K or search_tools
API->>Ex: guard + execute
else name not offered
API->>LLM: unknown / not in session catalog
end
13.2 سطوح کاتالوگ vis-à-vis LLM
| سطح | چه میبیند | اندازه هدف |
|---|---|---|
| 0 Core always | همان _CORE_TOOL_NAMES (~۱۲) + در صورت نیاز search_tools |
۱۲–۱۵ |
| 1 Domain Top-K | Schema کامل | تحلیلگر ۱۲–۲۰ (بهجای ۴۸)؛ نوشتن core + write هدف + companions |
| 2 On-demand | search_tools(query) → نام و توضیح کوتاه؛ سپس load_tool_schema(name) یا دور بعد با schema |
۲–۸ اضافه |
| 3 Never offered | Tool بدون permission، analyzer+write، destructive بدون intent صریح | ۰ |
13.3 meta-toolها (فقط وقتی کاتالوگ رشد کرد)
پیشنهاد نامها (پیادهسازی در Phaseهای بعدی، نه الان):
search_tools: ورودی query/domain؛ خروجی{name, description, category, risk}بدون schema کامل.- اختیاری
get_tool_schema: یک نام → schema کامل (اگر نخواهیم دور LLM اضافه کنیم، دور بعد discovery همان نام را در K میگذارد).
تا وقتی تعداد Tool مجاز کاربر پس از permission زیر ~۸۰ است، meta-tool اجباری نیست؛ ranking هیبرید کافی است.
13.4 رفتار داخل Agent Loop
امروز K یکبار قبل از حلقه ثابت است. پیشنهاد تدریجی:
- Phaseهای اولیه: همان رفتار (سادهتر برای cache و تست).
- Phase بعدی: اگر مدل
search_toolsزد، K همان turn یا turn بعد گسترش یابد — با سقف مطلق (مثلاً هرگز بیش از ۳۲ schema کامل در یک call).
14. Tool Registry Design
Registry Singleton را حذف نکنید؛ metadata را در همان AIFunction غنی کنید تا فایلهای موازی حذف شوند.
پیشنهاد فیلدهای واقعاً لازم بر اساس شکاف کد — نه یک schema تزئینی:
AIFunction (موجود + پیشنهادی)
├── name # موجود؛ شناسه پایدار
├── description # موجود؛ کوتاه برای جستجو، جدا از schema
├── parameters_schema # موجود
├── handler # موجود
├── allowed_roles # موجود
├── required_permissions # موجود؛ منبع واحد permission
├── business_context_required
├── category # موجود؛ باید با Intent یکی شود
├── requires_approval # موجود؛ منبع واحد write
├── risk_level # موجود؛ منبع واحد high-risk
├── is_readonly # موجود
├── is_agent_internal # موجود
├── keywords[] # جدید — جایگزین پراکندگی regex تا حد ممکن
├── aliases[] # جدید — ادغام TOOL_ALIASES
├── side_effect # جدید: none | write | delete | execute | export
├── domains[] # جدید: financial, warehouse, … همتراز Intent
├── companion_tools[] # جدید — ادغام _WRITE_TOOL_COMPANIONS
├── channel_allow # جدید: chat | crm | ticket | workflow | mcp
├── search_text # derived: name+description+aliases+keywords
└── embedding # derived/optional؛ نه در هر request
آنچه لازم نیست در Phase 1:
- Vector DB جدا
- نسخهبندی Schema پیچیده
- Marketplace Tool از افزونههای ثالث (بعداً از روی connector/skill)
منبع حقیقت پیشنهادی: خود AIFunction. لیستهای _WRITE_TOOLS و WRITE_FUNCTIONS باید به wrapper روی Registry تبدیل شوند (الان دو منبعاند).
15. Search Architecture
| روش | لازم؟ | چرا |
|---|---|---|
| Metadata / Permission filter | الزامی | حسابداری؛ الان در get_function_definitions هست |
| Execution mode filter | الزامی | الان هست |
| Namespace / domain filter | الزامی | همان Intent categories؛ باید با category یکی شود |
| Keyword / alias (وضعیت فعلی) | الزامی در کوتاهمدت | ارزان، فارسی، تست طلایی دارد |
| BM25 روی description | مفید Phase 4 | بهتر از regex برای توضیح طولانی؛ میتوان در PostgreSQL tsvector یا درونحافظه روی ۱۸۰–۲۰۰۰ سند |
| Vector Search | مفید نه فوری | ابهام معنایی («بدهکارهای معوق» ↔ aging)؛ زیرساخت embedding دانشنامه موجود است |
| Hybrid (keyword + vector) | هدف میانمدت | keyword برای exact domain، vector برای paraphrase |
| Reranker مدل دوم | غیرلازم تا eval نشان دهد keyword+vector کافی نیست | تأخیر و هزینه |
| Intent classifier جدا (LLM) | غیرلازم فعلاً | detect_categories همین کار را ارزان میکند؛ ارتقا به embedding classifier بعداً |
| pgvector جدا برای Tool | نه در شروع | ۱۸۰ بردار در حافظه کافی است؛ در هزاران Tool میتوان جدول شبیه ai_knowledge_chunks یا ستون روی کاتالوگ |
15.1 PostgreSQL و pgvector
در کد:
- مهاجرت اختیاری pgvector برای
ai_knowledge_chunks.embedding_vector(۱۵۳۶ بعد، HNSW cosine). - اگر extension نباشد، embedding JSON و جستجوی غیرpgvector.
توصیه: برای Tool Discovery تا چند هزار بردار، همان PostgreSQL کافی است. Vector DB جدا (Qdrant/Milvus) فقط وقتی (الف) pgvector در production در دسترس نیست و (ب) تعداد بردار و QPS از ظرفیت PG خارج شد — که امروز شاهد عددی برای آن نیست.
Unknown: آیا pgvector روی دیتابیس production نصب است؟ کد هر دو مسیر را پشتیبانی میکند.
Redis برای این جستجو لازم نیست؛ برای کش نتیجه و اشتراک بین workerها بعداً مفید است.
16. Permission Architecture
جریان فعلی و پیشنهادی practically یکسان است؛ تفاوت در ترتیب اجباری و completeness است.
همه Toolهای فرایند (۱۸۰)
→ Tenant / business_context_required
→ Role intersect allowed_roles
→ has_any_ai_tool_permission (alias به UI section.action)
→ execution_mode (حذف write در analyzer مگر agent_internal)
→ channel allowlist (CRM/ticket/…)
→ Discovery / Top-K
این ترتیب باید در Discovery جدید حفظ شود. جستجو هرگز روی کاتالوگ بدون Permission اجرا نشود.
نقشها: USER, OPERATOR, ADMIN, BUSINESS_OWNER. Superadmin همه نقشها را میگیرد.
شکاف: ۲۲ Tool بدون required_permissions — باید در Phase 2 تعیین تکلیف شوند (deny یا permission صریح).
17. Security Considerations
17.1 Toolهای حساس
باید در Discovery رفتار متفاوت داشته باشند. پیشنهاد سیاست (طراحی، نه پیادهسازی):
| کلاس | مثالهای موجود در Registry | Discovery | اجرا |
|---|---|---|---|
| Read | search_*, get_*, list_* |
قابل جستجو و Top-K | آزاد با permission |
| Write معمولی | create_invoice, create_person |
فقط اگر execution_mode ≠ analyzer و intent نوشتن/companion | تأیید در supervised؛ در autonomous طبق Registry |
| Destructive | delete_invoice, delete_person, delete_workflow, و سایر delete_* |
فقط با کلیدواژه/intent حذف صریح؛ هرگز در fallback عمومی | همیشه تأیید، حتی autonomous اگر risk_level=high |
| Execute / side effect خارجی | execute_workflow, invoke_business_connector |
محدود | تأیید |
| Export | export_business_data |
محدود | الان در HIGH_RISK |
| Agent internal | session plan, subagent, memory | سیاست جدا؛ memory write نباید با حذف فاکتور یکی فرض شود |
توصیه صریح: بله — حذف فاکتور/سند/مشتری/کالا، ثبت سند، انتقال وجه، بستن دوره (اگر Tool داشته باشد)، انتقال وجه (create_transfer) نباید صرفاً بهخاطر «دسته financial باز شد» در ۴۸ Tool بیایند.
create_transfer امروز در _WRITE_TOOLS است و با کلیدواژهٔ نوشتن میآید؛ در autonomous در protected set میماند و حذف بهخاطر سقف نمیشود — یعنی در حالت خودکار ممکن است تا ۱۲۸ Tool شامل همهٔ writeها به مدل نشان داده شود. این برای مدل وسوسهٔ فراخوانی اشتباه میسازد. Discovery پیشنهادی: writeها را به مدل نشان بده فقط وقتی intent نوشتن یا تأیید در جریان است.
17.2 MCP
Write پیشفرض APPROVAL_REQUIRED مگر params.approve_writes: true. خوب است. tools/list بدون Intent خطر افشای کاتالوگ کامل به کلاینت MCP است؛ برای MCP هم Top-K یا حداقل فیلتر دامنه پیشنهاد میشود.
17.3 Fail-closed
is_write_function(..., registry) اگر نام در Registry نباشد write فرض میکند. این رفتار را در Discovery هم نگه دارید: Tool ناشناخته اجرا نشود.
18. Scalability Considerations
تحلیل فقط روی معماری واقعی، نه سیستم خیالی.
| مقیاس کاربران همزمان Agent | Bottleneck محتمل | شواهد کد |
|---|---|---|
| 1 | LLM latency | حلقه چند نوبتی تا ۳۰۰ثانیه complex |
| 100 | LLM + DB per tool (run_ai_registry_function session جدید) |
هر Tool یک session |
| 1,000 | LLM cost/rate limit؛ اتصال SSE طولانی (AGT-02)؛ pool دیتابیس | راهنمای مقیاس DB: pool 20+30 per worker |
| 10,000 | همان + کش درونپردازهای بیفایده بین worker؛ insights cache ۵ دقیقه in-process | ai_tool_cache dict سراسری فرایند |
| 100,000 | بدون worker جدا برای Agent و بدون Redis برای cache، افق عملی نیست | حلقه داخل HTTP |
| جزء | نقش در مقیاس Tool Discovery |
|---|---|
| LLM | گرانترین؛ کاهش Tool و iteration بیشترین اثر را دارد |
| Tool Discovery keyword | O(tools) در حافظه؛ تا چند هزار ناچیز است |
| Vector search | تا چند هزار روی PG/حافظه کافی؛ بعد pgvector |
| PostgreSQL | اجرای خود Toolها (جستجوی فاکتور و …) سنگینتر از Discovery است |
| Redis | امروز در مسیر AI Tool نیست؛ برای cache چند-worker بعدی |
| Network | SSE طولانی |
| Tool Execution | موازی فقط ۴ read؛ write سریال درست است |
| Agent Loop | سقف ۱۵ نوبت × هر نوبت tools JSON |
| FastAPI | مناسب؛ مشکل معماری run داخل request است |
| MCP Server | tools/list کامل با ۱۸۰ Schema در آینده ۱۲۰۰ یعنی payload بزرگ |
19. Migration Strategy
هیچ مهاجرت دیتابیسی در Phase 0 لازم نیست.
اصل مهاجرت بعدی: رفتار فعلی پیشفرض بماند (۴۸/۱۲۸ + intent) تا eval طلایی سبز بماند؛ سپس flag برای Top-K تنگتر.
- غنیسازی
AIFunctionبدون حذف فایلهای phase. - تولید
_CATEGORY_TOOLSاز روی فیلدdomains(یکبار dual-run). - حذف تدریجی لیستهای تکراری وقتی تستها معادل شدند.
- نباید ۱۸۰ Tool را به ۱۲۰۰ خرد کنید. برعکس: Tool جدید فقط وقتی dedicated value دارد؛ وگرنه entity در
query_business_data/report_typeدرget_report.
20. Implementation Phases
این فازها جایگزین فازهای محصولی موجود در AI_EXECUTION_PHASES.md نیستند؛ لایهٔ Tool Discovery هستند.
Phase 0 — Analysis & Documentation
- هدف: همین سند.
- فایلها: فقط
docs/ai-agent/dynamic-tool-discovery.md. - تغییر معماری / dependency / migration: هیچ.
- ریسک: سند با رشد کد کهنه شود.
- موفقیت: تطابق ادعاها با ۱۸۰ Tool و جریان کد.
- وابستگی: —
Phase 1 — Unified Tool Metadata (بدون تغییر رفتار LLM)
- هدف: یک رکورد برای permission/write/risk/category/aliases؛ بستن شکاف لیستها.
- فایلهای درگیر:
function_registry.py,ai_function_extensions*.py,ai_tool_intent.py,ai_write_guard.py,ai_execution_policy.py,ai_tool_rank.py,ai_tool_keys.py, تستهای طلایی. - معماری: فیلدهای جدید با پیشفرض سازگار (
is_readonly=True,risk_level=safe). - Dependency: هیچ اجباری.
- Migration: اختیاری نیست مگر جدول کاتالوگ؛ ترجیح همچنان کد Python.
- ریسک: ناسازگاری موقت دو لیست.
- تست: طلایی intent بدون افت recall؛ تست که هر
delete_*یاrequires_approvalدر سیاست write باشد. - موفقیت: صفر Tool ثبتشده خارج از domain map؛ صفر write بدون flag.
- وابستگی: Phase 0.
Phase 2 — Security alignment برای Toolهای حساس
- هدف: تطبیق
HIGH_RISKبا همهٔ delete/execute/export؛ بستن Tool بدون permission؛ سیاست «write در autonomous به مدل نشان داده نشود مگر intent». - فایلها:
ai_execution_policy.py,ai_service.get_available_functions, extensions phase5/11. - ریسک: کاهش recall نوشتن اگر intent ضعیف باشد — با companionها جبران شود.
- تست: کیس «گزارش فروش» نباید
delete_invoiceرا ببیند؛ «این فاکتور را حذف کن» باید ببیند. - موفقیت: ماتریس کلاسهای بخش ۱۷ روی همهٔ ۱۸۰ نام.
- وابستگی: Phase 1.
Phase 3 — Benchmark Token و Catalog snapshot
- هدف: عدد واقعی توکن Schema در pytest؛ لاگ
tools_count/tools_tokens. - فایلها:
tests/test_ai_tool_catalog.pyیا تست جدید،ai_ops_metrics.py,ai_context_budget.py(فقط افزودن تخمین tools — اگر در این فاز کد مجاز شد). - Dependency: اختیاری
tiktokenفقط در تست. - موفقیت: جدول زنده: count، chars، tokens برای analyzer/autonomous/MCP.
- وابستگی: Phase 0 (میتواند موازی Phase 1).
شمارهگذاری اجرا در برابر این roadmap: Phase 4 واقعی کد Permission & Tool Metadata Hardening است. Phase 5 اجرا Tight Top-K Evaluation است (
phase-5-tight-topk-evaluation.md) و سقف ۴۸ را پایین نیاورد. Hybrid هنوز پیاده نشده.
Phase 4 (roadmap اولیه) — Tighter Top-K + Hybrid rank روی زیرساخت موجود
- هدف: analyzer پیشفرض Top-K ≈ ۱۶–۲۰ با حفظ gold recall؛ امتیاز فعلی + (اختیاری) embedding با
ai_embedding_service. - فایلها:
ai_tool_intent.py,ai_tool_rank.py,ai_embedding_service.py,ai_constants.py. - Dependency جدید: هیچ اگر از embedder فعلی دانشنامه استفاده شود. pgvector فقط اگر persist بخواهید.
- ریسک: افت recall؛ باید gold set را به ۱۸۰ نام واقعی گسترش داد.
- موفقیت: recall طلایی ≥ ۹۵٪ با K≤۲۰ برای analyzer؛ کاهش اندازهگیریشدهٔ tools_tokens.
- وابستگی: Phase 1 و 3.
Phase 5 — Progressive disclosure / search_tools
- هدف: وقتی کاندیداها > سقف Schema، مدل
search_toolsببیند نه ۵۰ Schema. - فایلها: extension جدید،
get_available_functions, prompt routing (chat.tool_routing). - ریسک: یک نوبت LLM اضافه؛ hallucination نام — خروجی باید فقط از کاتالوگ permissionخورده باشد.
- موفقیت: هیچ Schema کامل بیش از سقف مطلق (پیشنهاد ۳۲) در یک call؛ قابلیت رسیدن به Tool دمبلند.
- وابستگی: Phase 4.
Phase 6 — Permission-aware discovery بهعنوان API داخلی واحد
- هدف: تابع واحد
discover_tools(ctx, query, mode, channel) -> ToolOfferبرای چت، MCP، CRM، subagent. - فایلها: ماژول جدید مثلاً
ai_tool_discovery.py؛ حذف شاخههای تکراری درai_channel_policyبهتدریج. - موفقیت: MCP هم سقف و فیلتر دامنه دارد.
- وابستگی: Phase 2 و 5.
Phase 7 — Caching & performance
- هدف: کش تعریف Tool (پایدار تا deploy)، اشتراک کش نتیجهٔ read بین worker در صورت وجود Redis محصول، لحاظ کردن tools در context budget.
- فایلها:
ai_tool_cache.py,ai_prompt_cache.py,ai_context_budget.py. - Dependency: Redis فقط اگر محصول قبلاً برای AI روشن شود؛ اجباری نکنید.
- ریسک: stale permission اگر کش تعریف بدون user scope باشد — کش schema سراسری است، فیلتر permission باید per-request بماند.
- وابستگی: Phase 3.
Phase 8 — Evaluation & benchmarking
- هدف: gold set روی Registry واقعی ۱۸۰ تایی؛ کیسهای destructive منفی؛ اندازهگیری latency discovery (باید << latency LLM).
- فایلها:
tests/test_ai_tool_intent_gold.py,ai_eval_*. - موفقیت: CI روی MIN_OFFLINE_EVAL_PASS_RATE موجود.
- وابستگی: Phase 4+.
Phase 9 — رشد کاتالوگ بدون انفجار Tool
- هدف: پوشش دامنهٔ بیشتر از طریق entity/report/skill، نه ۱۲۰۰ function.
- فایلها:
ai_query_service.py,ai_reports_catalog.py, skill packages. - Migration: ندارد به معنای DB Tool table مگر تصمیم محصول به کاتالوگ DB-driven.
- موفقیت: افزایش قابلیت کاربر با رشد کم
len(registry._functions). - وابستگی: پوشش محصول در
AI_EXECUTION_PHASES.mdفاز ۹.
21. Testing Strategy
موجود و باید حفظ شود:
tests/test_ai_tool_intent.py/test_ai_tool_intent_gold.pytests/test_ai_tool_catalog.pytests/test_ai_tool_parallel.py,test_ai_tool_error.py,test_ai_mcp_write_guard.py
اضافهٔ پیشنهادی (فازهای بعد):
- Snapshot تعداد و نام Toolها (
len == 180تا وقتی عمداً عوض شود). - هر Tool: یکی از readonly/write/internal؛ write ⇒ permission و approval مشخص.
- تست منفی: query گزارش، مجموعهٔ پیشنهادی فاقد
delete_*. - Discovery هرگز Tool بدون permission برنمیگرداند (ctx جعلی محدود).
- MCP list ⊆ permission set و پس از Phase 6 ⊆ سقف.
22. Benchmark Strategy
اگر دادهٔ زنده نداریم، در Phase 3 بسازید:
| متریک | نحوهٔ اندازهگیری | فعلی (از کد/استاتیک) | هدف پیشنهادی |
|---|---|---|---|
| Tool count در Registry | شمارش AIFunction |
۱۸۰ | رشد کنترلشده؛ نه ۱۲۰۰ ریز |
| Tool visible به LLM | len(definitions) در get_available_functions | ≤۴۸ / ≤۱۲۸ | Analyzer ≤۲۰؛ write session ≤۳۲ |
| Tool schema tokens | json tools chars/4 یا tiktoken | ≥۱۶.۴k برای ۱۸۰؛ ~۴.۴k برای ۴۸ میانگین | Analyzer schema ≤۲.۵k |
| Context size داخلی | context_usage فعلی |
بودجه ۲۸k بدون tools | همان + tools در فرمول |
| Tool selection accuracy | gold set | ≥۹۵٪ روی کاتالوگ intent مصنوعی | ≥۹۵٪ روی ۱۸۰ واقعی + کیس منفی destructive |
| Latency discovery | time.monotonic دور get_available_functions | Unknown | < ۲۰ms p95 بدون embed؛ < ۱۰۰ms با embed محلی |
| Latency end-to-end | SSE done | Unknown (وابسته به مدل) | کاهش از محل کمتر بودن tools/iteration |
| Cost per request | usage meter موجود | Unknown per-tool-share | کاهش input tokens متناسب با کاهش tools × iterations |
هزینه: هر iteration همان tools را میفرستد؛ کاهش K و کاهش نوبت هر دو ضریب میشوند.
23. Risks
| ریسک | شدت | کاهش |
|---|---|---|
| Tight Top-K باعث از دست رفتن Tool دمبلند | بالا | search_tools + gold set گسترده |
| Embedding فارسی ضعیف | متوسط | hybrid با keyword موجود |
| دو رفتار موازی در مهاجرت | متوسط | flag + تست |
| نمایش write در autonomous | امنیتی | Phase 2 قبل از کاهش K |
| فرض ۱۲۰۰ Tool جدا | محصولی | fan-in؛ مخالفت با wrapping همهٔ REST |
| pgvector نبودن در production | کم | مسیر JSON/حافظه مثل دانشنامه |
| MCP کاتالوگ کامل | متوسط | Phase 6 |
24. Open Questions
مواردی که از کد نمیتوان با اطمینان گفت:
- مدل و Provider واقعی production و اینکه gateway آروان/vLLM است یا api.openai.com (روی Prompt Cache اثر دارد).
- آیا pgvector روی سرور production فعال است.
- آیا صوت و تلگرام دقیقاً
get_available_functionsچت را صدا میزنند. - تعداد دقیق Routeهای REST (اگر کسی ۱۲۰۰ را از Swagger شمرده).
- توزیع واقعی
risk_levelبرای تکتکdelete_*پس از instantiate Registry (AST نشان میدهد برخی high هستند؛ جدول کامل runtime در Phase 1). - نرخ cache hit واقعی Prompt Cache (
cached_tokens). - QPS و تعداد worker تولید.
25. Final Recommendation
- وضعیت فعلی را ۱۸۰ Tool با فیلتر Intent و سقف ۴۸/۱۲۸ بدانید نه معماری ۱۲۰۰ Tool بدون فیلتر.
- Registry را نگه دارید و به کاتالوگ Metadata واحد تبدیل کنید.
- اول امنیت و یکپارچگی metadata (Phase 1–2)، بعد اندازهگیری توکن (Phase 3)، بعد Top-K تنگتر و hybrid search (Phase 4)، بعد meta-tool برای مقیاس (Phase 5).
- به ۱۲۰۰ Function جدا مهاجرت نکنید. قابلیت را با entity/report/skill زیاد کنید.
- Vector DB جدید نصب نکنید تا ارزیابی Phase 4 روی embedding موجود/pgvector اختیاری انجام شود.
- کشف Tool را از LLM جدا نگه دارید تا تعویض مدل (OpenAI / Anthropic / Local) باقی بماند.
اگر فقط یک حرکت بعدی مجاز باشد: Phase 1 + بستن شکاف write/delete در لیستهای موازی، چون هم ایمنی حسابداری است و هم پیشنیاز هر Discovery بعدی.
پیوست A — فهرست ۱۸۰ نام ثبتشده
adjust_customer_club_points, await_subagent, batch_query_business_data, cancel_subagent,
create_account, create_check, create_expense_income, create_invoice, create_lead,
create_person, create_product, create_product_attribute, create_receipt_payment,
create_session_plan, create_transfer, create_warehouse_document, create_workflow,
delete_account, delete_check, delete_expense_income, delete_invoice, delete_memory_entry,
delete_person, delete_product_attribute, delete_receipt_payment, delete_transfer,
delete_workflow, execute_workflow, export_business_data, get_account, get_basalam_overview,
get_bom_details, get_business_credit_settings, get_business_dashboard, get_business_info,
get_cash_flow, get_check_details, get_creditors_report, get_crm_summary,
get_current_fiscal_year, get_customer_club_rfm_summary, get_customer_club_settings,
get_customer_info, get_deal_details, get_debtors_report, get_document_details,
get_document_numbering_settings, get_financial_summary, get_inventory_status,
get_inventory_valuation, get_invoice_details, get_invoices_count, get_lead_details,
get_lead_funnel_report, get_loan_facility, get_opening_balance, get_person_balance,
get_person_credit, get_person_transactions, get_pipeline_report, get_product_attribute,
get_product_info, get_product_kardex, get_project_summary, get_purchase_report,
get_quick_sales_settings, get_repair_order_details, get_report, get_report_template,
get_report_template_scope_catalog, get_sales_report, get_tax_data_quality, get_tax_settings,
get_wallet_metrics, get_wallet_overview, get_warehouse_document_details, get_warehouse_report,
get_warehouse_stock_summary, get_workflow, get_workflow_component_schema,
get_workflow_design_rules, get_workflow_execution_debug, hscript_fix_script,
hscript_language_guide, hscript_read_doc, hscript_retrieve_docs, hscript_run_preview,
hscript_search_docs, hscript_validate_script, invoke_business_connector, list_accounts,
list_announcements, list_available_reports, list_bank_accounts, list_basalam_dead_letter,
list_basalam_product_conflicts, list_basalam_synced_invoices, list_boms,
list_business_notification_logs, list_business_notification_templates, list_business_plugins,
list_business_users, list_cash_registers, list_credit_installment_plans, list_currencies,
list_currency_rates, list_customer_club_ledger, list_customer_club_tiers,
list_distribution_routes, list_fiscal_years, list_frequent_descriptions, list_loan_facilities,
list_marketplace_plugins, list_my_businesses, list_payment_gateways, list_person_groups,
list_petty_cash, list_price_list_items, list_price_lists, list_product_attributes,
list_queryable_fields, list_report_templates, list_session_todos, list_user_notifications,
list_wallet_transactions, list_warehouse_locations, list_warehouse_placements,
list_warehouses, list_woocommerce_orders, list_woocommerce_products,
list_workflow_action_catalog, list_workflow_builtin_nodes, list_workflow_executions,
list_workflow_trigger_catalog, list_workflows, poll_workflow_execution,
publish_report_template, query_business_data, read_memory, recalculate_customer_club_rfm,
resolve_currency_rate, resolve_date_range, search_activities, search_activity_logs,
search_categories, search_checks, search_customer_club_rfm_persons, search_deals,
search_documents, search_expense_income, search_invoices, search_leads, search_persons,
search_product_instances, search_production_documents, search_products, search_projects,
search_receipts_payments, search_repair_orders, search_tax_workspace, search_transfers,
search_warehouse_documents, search_warranty_codes, set_default_report_template,
spawn_subagent, test_workflow, update_account, update_check, update_customer_club_settings,
update_expense_income, update_invoice, update_person, update_product,
update_product_attribute, update_receipt_payment, update_session_todo, update_transfer,
update_workflow, upsert_memory_entry, validate_workflow_draft
پیوست B — فایلهای کلیدی بررسیشده
hesabixAPI/app/services/ai/function_registry.pyhesabixAPI/app/services/ai/ai_service.pyhesabixAPI/app/services/ai/ai_tool_intent.pyhesabixAPI/app/services/ai/ai_tool_rank.pyhesabixAPI/app/services/ai/ai_tool_catalog.pyhesabixAPI/app/services/ai/ai_write_guard.pyhesabixAPI/app/services/ai/ai_execution_policy.pyhesabixAPI/app/services/ai/ai_permission_map.pyhesabixAPI/app/services/ai/ai_provider.pyhesabixAPI/app/services/ai/ai_prompt_cache.pyhesabixAPI/app/services/ai/ai_context_budget.pyhesabixAPI/app/services/ai/ai_constants.pyhesabixAPI/app/services/ai/ai_mcp_service.pyhesabixAPI/app/services/ai/ai_channel_policy.pyhesabixAPI/app/services/ai/ai_embedding_service.pyhesabixAPI/adapters/api/v1/ai/chat.pyhesabixAPI/adapters/api/v1/ai/mcp.pydocs/AI_AGENT_SYSTEM_AUDIT.md