9.7 KiB
Phase 3 — Unified Tool Discovery API
وضعیت: در کد پیاده شده است.
تاریخ: ۱۹ اوت ۲۰۲۶
Source of Truth کد: hesabixAPI/app/services/ai/ai_tool_discovery.py
مرتبط: tool-discovery-architecture.md · phase-2-security-first-discovery.md
Discovery میگوید کدام Tool مناسب است. Schema Loader میگوید JSON کامل آن چیست. این دو یکی نیستند.
1. Problem
پس از Phase 2 امنیت قبل از Ranking است، اما انتخاب Tool هنوز در چند مسیر پخش بود:
AIService.get_available_functionsشاخهٔ analyzer / catalog را خودش میساخت- Intent (
select_tool_names/select_catalog_tool_names) هم Security داشت هم Ranking - CRM، تیکت، Workflow، Subagent هر کدام
get_available_functionsیا Registry را جدا صدا میزدند - MCP
tools/listکاتالوگ permissionخوردهٔ کامل را بدون Intent میداد
بدون API واحد، Phase بعد (hybrid / Top-K تنگ) باید همهٔ Consumerها را دوباره عوض میکرد.
2. Existing Discovery Paths
استخراج از کد واقعی (قبل از این Phase):
| مسیر | Input | Toolهایی که میبیند | Permission | Security | Intent / Rank | Top-K |
|---|---|---|---|---|---|---|
Chat get_available_functions |
query, mode, session, force, history | خروجی Registry برای همان user/tenant | Registry | filter_security_candidates |
select_tool_names یا catalog |
۴۸ / ۱۲۸ |
| Subagent | goal، mode=analyzer | همان Chat سپس filter_subagent_tools |
Registry از طریق Chat | Chat + write strip | Chat | ۴۸ سپس allowlist فرزند |
| CRM | query، analyzer | Chat سپس CHANNEL_CRM_READ_TOOLS |
Registry | Chat + channel allowlist | Chat | allowlist کوچک |
| Ticket | عنوان تیکت، analyzer | Chat سپس ticket allowlist | Registry | همان | Chat | allowlist کوچک |
| Workflow agent | category / بدون query | همهٔ authorized (بدون rank) سپس denylist | Registry | Security بدون rank | هیچ | تا hard max |
MCP tools/list |
business_id | همهٔ permissioned | Registry | نه mutation | نه | بدون سقف Intent |
MCP tools/call |
name + args | یک Tool | Registry + write guard | اجرا | — | — |
| Gold / unit tests | catalog ساختگی | نامهای دادهشده | شبیهسازیشده | داخل select_* | keyword | ۴۸ / ۱۲۸ |
Permission در Discovery تکرار نمیشود. Tenant همان business_id نشست است.
3. Unified API
discover_tools(
query,
permissioned_names=...,
execution_mode=...,
limit=...,
history_messages=...,
forced_names=...,
prefer_names=...,
protected_names=...,
channel="chat",
capability=None,
rank=None,
) -> DiscoveryOffer
خروجی:
DiscoveryOffer
├── candidates: ToolCandidate[] # بدون Schema
│ name, score, match_reason, capability, namespace, domains, side_effect
├── strategy, channel, execution_mode, mutation
├── requested_limit, effective_limit
├── authorized_count, latency_ms, ranked
get_available_functions حالا:
Registry.get_function_definitions
↓
discover_tools() # names + lightweight metadata
↓
filter_function_definitions # Schema loading (هنوز همینجا)
4. Security Boundary
Registry (tenant / role / permission)
↓
discover_tools
↓
filter_security_candidates # موجود؛ Duplicate نیست
↓
KeywordIntentStrategy
↓
Ranked authorized names
صدای مستقیم discover_tools هم Security را اجرا میکند. Permission را دوباره پیاده نمیکند؛ اگر caller نام غیرمجاز بدهد، آن نامها permission-check نمیشوند — قرارداد این است که permissioned_names از Registry آمده باشد.
UNKNOWN همچنان fail-closed است (unknown_policy=deny در production).
5. Discovery Contract
Input: مجموعهٔ permissioned + query/mode/limit/channel — نه db و نه PII اضافه.
Output: فقط نام و metadata سبک. هیچ parameters / JSON Schema.
Empty: permissioned_names=[] یا هیچ Tool مجاز → DiscoveryOffer خالی، نه None و نه کاتالوگ کامل.
Cache: Interface برای کش بعدی آماده است (DiscoveryEngine.discover). در این Phase کش اضافه نشد.
6. Strategy Architecture
DiscoveryEngine
├── KeywordIntentStrategy ← فعال (Phase 3)
├── SemanticStrategy ← NotImplemented
└── HybridStrategy ← NotImplemented
جایگزینی Phase بعد بدون تغییر Chat/Subagent: strategy دیگر به Engine داده میشود. Consumer همان discover_tools است.
Capability: پارامتر اختیاری؛ فعلاً prefer است نه hard filter. مقدارهای امروز درشتاند (accounting, inventory, …) نه reports.sales.
7. Chat Integration
AIService.get_available_functions → discover_tools(..., channel="chat").
سقف پیشفرض همان ۴۸ analyzer / ۱۲۸ supervised-autonomous است. force_tool_names و مهارت/plan/subagent بهصورت forced/prefer عبور میکنند.
Schema همچنان بعد از Discovery از Registry لود میشود تا حلقهٔ LLM نشکند.
8. Subagent Integration
Subagent Discovery مستقل ندارد.
child.get_available_functions(..., channel="subagent")
↓
discover_tools()
↓
filter_subagent_tools (read-only + allowlist فرزند)
9. MCP Integration
در این Phase tools/list عوض نشد. قرارداد MCP: کلاینت خارجی کاتالوگ permissionخورده را میبیند و خودش انتخاب میکند. Execution Guard در tools/call باقی است.
Phase بعد (یا ۹ در roadmap):
tools/list + query → discover_tools(channel="mcp", limit=…)
tools/list بدون query → permissioned catalog با DISCOVERY_HARD_MAX
tools/call → بدون تغییر Guard
تا کلاینت MCP به listing کامل وابسته است، dynamize کردن list بدون هماهنگی پروتکل خطر سازگاری دارد.
10. Limit Handling
| مقدار | معنی |
|---|---|
requested_limit |
آنچه Consumer خواست |
default |
۴۸ analyzer / ۱۲۸ catalog / ۲۵۶ اگر rank خاموش |
DISCOVERY_HARD_MAX |
۲۵۶ — بزرگتر از کاتالوگ ۱۸۰؛ limit=100000 به این سقف میرسد |
effective_limit |
min(requested, hard_max) پس از default |
۴۸ و ۱۲۸ بهعنوان پیشفرض Chat حفظ شدند تا Accuracy عوض نشود.
11. Observability
log_ai_event("tool_discovery"):
- channel, mode, mutation, strategy, ranked
- candidate_count (authorized), selected_count, selected_tools (نام Tool)
- requested_limit, limit, discovery_latency_ms
- capability
لاگ نمیشود: متن query، نام مشتری، مبلغ، موجودی، ایمیل، شناسهٔ سند.
12. Tests
tests/test_ai_tool_discovery.py:
- Chat source →
discover_tools - Subagent source →
get_available_functions+channel=subagent - Unauthorized هرگز برنمیگردد
limit=100000bounded- Empty permissioned → offer خالی
- Gold recall از طریق
discover_tools≥ ۹۵٪ - Candidate بدون Schema
- Semantic/Hybrid هنوز NotImplemented
رگرسیون Phase 1–2 باید سبز بماند. تعداد Tool = ۱۸۰.
13. Migration
- هیچ Tool حذف یا rename نشد
select_tool_namesbackend استراتژی است؛ Gold مستقیم هم هنوز آن را صدا میزند- CRM/تیکت/Workflow از طریق
get_available_functionsبه API واحد رسیدند؛ allowlist کانال سر جایش است - MCP list عمداً مهاجرت نشد
14. Phase 4 Design
دو گزینه روی میز بود: Hybrid/Semantic فوری، یا پاکسازی Permission/Metadata.
توصیهٔ این پروژه: ابتدا یک Phase مستقل برای کیفیت Metadata و بستن Permission Gap، سپس Hybrid.
دلیل از وضعیت واقعی کد:
- Phase 0 حدود ۲۲ Tool بدون
required_permissionsثبت کرده؛ Registry برای permission خالی fail-open است. Semantic match این Toolها را بیشتر به مدل نزدیک میکند. upsert_memory_entryهنوزside_effect=noneاست و در Discovery شبیه Read بهنظر میرسد.- Capability امروز درشت است (
accounting) نهreports.sales؛ Hybrid بدون متن جستجوی غنی (aliases/examples ناقص برای خیلی از Toolها) نویز دامنه میسازد. - سقف ۴۸/۱۲۸ هنوز برای بنچمارک ۵–۲۰ بسته نشده؛ Progressive Schema هم نیست.
پس Phase 4 پیشنهادی این مخزن: Tool Metadata & Permission Hardening (بستن gap، side_effect حافظه، aliases برای delete/export). Phase 5: Tight Top-K + Hybrid روی همان discover_tools / HybridStrategy.
وضعیت اجرا: Phase 4 طبق همین توصیه در کد فرود آمده است. جزئیات: phase-4-permission-metadata-hardening.md. Retrieval هنوز عوض نشده.