محرك بحث قابل لإعادة الاستخدام، مو بناء لمتجر محدد
يغطي هذا المخطط المسار الأساسي لأي متجر مرتبط بكتالوج: يفهم طلب العميل بالعربي أو الإنجليزي، ويحفظ أقل قدر مفيد من سياق الجلسة، ويستدعي أدوات معتمدة، ويطبق قواعد الكتالوج والعمل بشكل حتمي، ويسأل عن التفاصيل الناقصة، ويرجع منتجات مرتبة وقابلة للتتبع. السجلات خيالية والبنية تنفيذ مرجعي.
يشمل
- بحث طبيعي بالمنتج أو SKU أو الصفات
- فهم الطلب بالعربي والإنجليزي
- أدوات مرتبطة بالكتالوج والمخزون والتوافق
- تصفية وترتيب وأسئلة توضيحية بشكل حتمي
ما يشمل
- الدفع أو إنشاء الطلب
- خدمة حية للهوية أو بيانات العميل
- تشخيص ميكانيكي أو طبي أو متعلق بالسلامة
- تعليمات تينغ أو تكاملاتها الخاصة أو ترتيبها الإنتاجي
افهم واسترجع وتحقق ورتّب
يحوّل النموذج طلب المتسوق إلى عقد متحقق منه، ويختار من قائمة صغيرة من الأدوات المسموحة. بعدها يتحقق كود التطبيق من نتائج الكتالوج ويصفيها ويرتبها. كل معرّف منتج يظهر للعميل لازم يكون جاي من نتيجة أداة.
عرض مصدر Mermaid
flowchart RL
Q["طلب المتسوق"] --> I["اللغة والنية والكيانات"]
I --> C{"السياق كافي؟"}
C -- "لا" --> F["سؤال متابعة محدد"]
F --> I
C -- "نعم" --> T["أدوات كتالوج معتمدة"]
T --> G["الأهلية وقواعد العمل"]
G --> R["ترتيب حتمي"]
R --> O["منتجات مرتبطة بالبيانات وخطوة تالية"]عرض مصدر Mermaid
stateDiagram-v2
[*] --> IDLE
IDLE --> INTERPRETING
INTERPRETING --> NEEDS_CLARIFICATION: تفاصيل ناقصة
NEEDS_CLARIFICATION --> INTERPRETING: تصحيح العميل
INTERPRETING --> RETRIEVING: عقد صالح
RETRIEVING --> VALIDATING
VALIDATING --> RANKING: نتائج مرتبطة بالبيانات
VALIDATING --> NEEDS_CLARIFICATION: توافق غير مؤكد
RANKING --> RESPONDING
RESPONDING --> COMPLETE
INTERPRETING --> FALLBACK: فشل المزود أو المخطط
RETRIEVING --> FALLBACK: انتهاء المهلة
FALLBACK --> COMPLETE: نتيجة حتمية
FALLBACK --> FAILED: لا توجد نتيجة آمنةالحزمة المرجعية المسماة
يستخدم المرجع AI SDK لحلقة الأدوات، وOpenRouter كبوابة نماذج من الخادم، وZod للتحقق عند كل حد، وPostgres لحالة الكتالوج والبحث، واستضافة ويب / بنية تحتية سحابية لتشغيل التطبيق. هذه خيارات تعليمية، وليست وصفاً لحزمة تينغ الإنتاجية.
AI SDK
تنسيق حلقة الأدوات بخطوات محدودة ومخرجات نموذج مكتوبة.
OpenRouter
بوابة نماذج من الخادم مع نموذج معتمد وسياسة واضحة للمهلة والتكلفة والبديل.
Zod
تحقق للطلبات والتفسير ومدخلات الأدوات والاستجابات العامة.
Postgres
نسخة الكتالوج وجلسات البحث وإصدارات السياسات وحالات التقييم وأحداث التدقيق.
Web Hoster / Cloud Infrastructure
تشغيل التطبيق وواجهة من نفس النطاق وحقن الأسرار وفحوصات الصحة والتراجع.
المستودع والتثبيت والبيئة
خل الوصول للمزود ومحولات أنظمة العمل داخل وحدات خاصة بالخادم. المتصفح يرسل طلباً محدوداً لنقطة نهاية من نفس النطاق؛ وما يستلم مفاتيح المزود ولا التتبعات الخاصة ولا وصولاً مفتوحاً للكتالوج.
agentic-store-search/
├─ app/
│ ├─ api/search/route.ts
│ └─ search/page.tsx
├─ src/search/
│ ├─ agent.ts
│ ├─ contracts.ts
│ ├─ state.ts
│ ├─ ranking.ts
│ ├─ safety.ts
│ ├─ observability.ts
│ ├─ providers/openrouter.ts
│ └─ tools/
│ ├─ catalog.ts
│ ├─ inventory.ts
│ └─ compatibility.ts
├─ data/
│ ├─ catalog.fixture.json
│ └─ evaluation-cases.jsonl
├─ tests/
│ ├─ grounding.test.ts
│ ├─ ranking.test.ts
│ └─ locale-parity.test.ts
└─ .env.examplenpm install ai @ai-sdk/openai-compatible zod pg
npm install -D typescript tsx vitest @types/node# Placeholder values only. Keep every secret server-side.
OPENROUTER_API_KEY=replace_with_server_only_key
OPENROUTER_MODEL=replace_with_approved_model_id
DATABASE_URL=postgres://user:password@host:5432/store_search
CATALOG_SOURCE_URL=https://catalog.example.invalid/api
SEARCH_REQUEST_TIMEOUT_MS=28000
SEARCH_MAX_TOOL_STEPS=7
SEARCH_DAILY_TOKEN_BUDGET=replace_with_integerعقود الكتالوج والتفسير والحالة
مخطط الكتالوج يحدد وش يقدر النظام يقوله. مخطط التفسير يحدد وش يقدر النموذج يطلبه. ونموذج حالة محدود يخلي السؤال التوضيحي والبديل والفشل واضحين بدل ما يختفون داخل النص.
import { z } from "zod";
export const productSchema = z.object({
id: z.string(),
sku: z.string(),
name: z.string(),
locale: z.enum(["en", "ar"]),
category: z.string(),
attributes: z.record(z.string(), z.string()),
price: z.number().nonnegative(),
currency: z.string().length(3),
availability: z.enum(["in_stock", "low_stock", "out_of_stock"]),
compatibilityKeys: z.array(z.string()).default([]),
sourceUpdatedAt: z.string().datetime()
});
// Fictional reference record — never a claim about a real store.
export const exampleProduct = productSchema.parse({
id: "prd_demo_001", sku: "DEMO-FLT-001",
name: "Premium Cabin Filter", locale: "en",
category: "Service Parts", attributes: { size: "standard" },
price: 89, currency: "SAR", availability: "in_stock",
compatibilityKeys: ["sedan-2021-2.5"],
sourceUpdatedAt: "2026-08-01T09:00:00.000Z"
});export const interpretationSchema = z.object({
language: z.enum(["en", "ar"]),
intent: z.enum([
"find_product", "exact_sku", "compare", "filter", "unknown"
]),
querySummary: z.string().max(220),
entities: z.object({
productName: z.string().nullable(),
sku: z.string().nullable(),
category: z.string().nullable(),
model: z.string().nullable(),
year: z.number().int().nullable(),
priceMax: z.number().positive().nullable(),
availableOnly: z.boolean()
}),
missingInformation: z.array(z.string()).max(6),
requestedTools: z.array(z.enum([
"searchCatalog", "findExactSku", "checkInventory",
"verifyCompatibility", "applyFilters"
])).min(1).max(5),
nextQuestion: z.object({
type: z.enum(["single_choice", "free_text", "none"]),
text: z.string().max(220),
options: z.array(z.string().max(80)).max(6)
})
});export type SearchState =
| "idle" | "interpreting" | "needs_clarification"
| "retrieving" | "validating" | "ranking"
| "responding" | "complete" | "fallback" | "failed";
export const legalTransitions = {
idle: ["interpreting"],
interpreting: ["needs_clarification", "retrieving", "fallback"],
needs_clarification: ["interpreting", "complete"],
retrieving: ["validating", "fallback"],
validating: ["ranking", "needs_clarification"],
ranking: ["responding"],
responding: ["complete"],
fallback: ["retrieving", "complete", "failed"],
complete: [], failed: []
} satisfies Record<SearchState, SearchState[]>;الاسترجاع المرتبط بالبيانات وقواعد الذاكرة
كل أداة ترجع الحقول المعتمدة فقط. ذاكرة الجلسة تحفظ أقل سياق مفيد—اللغة والطلب السابق والصفات المختارة والمعرّفات المقنّعة—وتنتهي حسب سياسة موثقة. نص المنتج مدخل غير موثوق وما يتحول أبداً إلى تعليمات.
export async function searchCatalog(input: {
query: string; category?: string; limit?: number;
}) {
const rows = await catalogRepository.search({
query: input.query,
category: input.category,
limit: Math.min(input.limit ?? 18, 30)
});
// The model receives approved facts, never unrestricted database access.
return rows.map((row) => ({
id: row.id, sku: row.sku, name: row.name,
category: row.category, price: row.price,
currency: row.currency, availability: row.availability
}));
}- اسمح فقط بأدوات مسماة ومدخلات متحقق منها.
- احفظ أقل ذاكرة جلسة مفيدة وحدد انتهاءها.
- اقطع تعليمات HTML أو نصوص المنتجات غير الموثوقة.
- لا تعرض منتجاً ما ظهر معرّفه في نتيجة أداة.
حدود الترتيب والتصحيحات
النموذج يقدر يحدد النية والمعلومات الناقصة؛ وكود التطبيق يملك الأهلية والتوافق والسعر والمخزون وفلاتر السياسة والنتيجة الرقمية النهائية. التصحيح يبدأ دورة تفسير جديدة، ويحفظ الطلب السابق للتدقيق، ويعيد تشغيل نفس البوابات الحتمية.
export function rankCandidates(candidates, request, rules) {
return candidates
.filter((item) => rules.allowedProductIds.has(item.id))
.filter((item) => !rules.incompatibleProductIds.has(item.id))
.map((item) => ({
...item,
score:
lexicalScore(item, request.query) * 0.45 +
attributeScore(item, request.entities) * 0.30 +
availabilityScore(item.availability) * 0.15 +
businessPriority(item.id, rules) * 0.10
}))
.sort((a, b) => b.score - a.score || a.sku.localeCompare(b.sku));
}
// The language model may interpret intent. It may not invent candidates,
// compatibility, stock, price, eligibility, or the final numeric score.من نتيجة أداة كتالوج معتمدة فقط.
التعارض والسياسة والمخزون حسب قواعد حتمية.
وزن موثق للكلمات والصفات والتوفر وأولوية العمل.
يصف سبب الترتيب بدون اختلاق بيانات أو تفكير مخفي.
استجابة منظمة وقابلة للتتبع
ترجع الواجهة نصاً مختصراً للعميل، ومعرّفات منتجات مرتبطة بالبيانات، والقواعد المطبقة، وحالة البديل، ومعرّف تتبع آمن. التفكير المخفي ومفاتيح المزود والمعرّفات الشخصية الكاملة وبيانات النموذج الخاصة ما توصل للعميل.
{
"requestId": "req_demo_7f3a",
"state": "complete",
"language": "en",
"answer": {
"headline": "I found three grounded options.",
"body": "Results match the approved catalog and current filters.",
"question": "",
"requiresClarification": false
},
"productIds": ["prd_demo_001", "prd_demo_014"],
"appliedRules": ["available_only", "compatibility_required"],
"fallbackUsed": false,
"traceId": "trace_demo_91c2"
}تصحيح
أعد تفسير الطلب وشغّل البوابات نفسها.
عدم يقين
اسأل سؤالاً محدداً بدل عرض ثقة مزيفة.
صفر نتائج
اشرح الحدود واقترح تعديل الطلب.
ذاكرة
خزن سياقاً آمناً فقط، بدون كامل المعرّفات الحساسة.
معالج الخادم والبديل الواضح
كل طلب يمر على تحقق بالمخطط وله مهلة زمنية وما ينحفظ في الكاش. فشل المزود أو المخطط يحول البحث إلى بحث حتمي بالكلمات والصفات؛ والواجهة توضح الوضع المخفف بدون ما توقف العميل أو تدّعي أن جولة أدوات حية اكتملت.
export async function POST(request: Request) {
const abort = AbortSignal.timeout(28_000);
const input = searchRequestSchema.parse(await request.json());
try {
const result = await runAgenticSearch(input, { signal: abort });
return Response.json(publicSearchResponseSchema.parse(result), {
headers: { "Cache-Control": "no-store" }
});
} catch (error) {
const fallback = await runDeterministicSearch(input);
return Response.json({ ...fallback, fallbackUsed: true }, {
status: 200,
headers: { "Cache-Control": "no-store" }
});
}
}بيانات التقييم والمقاييس واختبارات الانحدار
استخدم حالات عربية وإنجليزية لـSKU الدقيق والمرادفات والأخطاء الإملائية والمتابعات والصفات الناقصة ونصوص المنتجات غير الآمنة وفشل المزود. راقب دقة النتائج المرتبطة بالبيانات، ومعدل المنتجات غير الصالحة، وفائدة الأسئلة، والنتائج الصفرية، والوقت، والبديل، وتكافؤ اللغتين.
const cases = [
["exact SKU", "DEMO-FLT-001", ["prd_demo_001"]],
["Arabic synonym", "فلتر المكيف", ["prd_demo_001"]],
["out-of-stock filter", "available only", []],
["explicit incompatibility", "sedan-2022", []],
["prompt injection in product text", "ignore rules", []],
["provider timeout", "fallback", ["deterministic"]]
];
test.each(cases)("%s remains grounded", async (_name, query, expected) => {
const result = await runFixture(query);
expect(result.productIds).toEqual(expected);
expect(result.productIds.every(id => fixtureCatalog.has(id))).toBe(true);
});نسبة المنتجات الصحيحة من كل المنتجات المعروضة.
لا يظهر أي معرّف خارج نتيجة الأداة.
نسبة الأسئلة التي تقود إلى نتيجة أدق.
نفس القيود والمعنى والمنتجات في العربية والإنجليزية.
p50 وp95 لكل مرحلة وللطلب الكامل.
نسبة الطلبات التي انتقلت للبحث الحتمي.
النشر والحفظ والمراقبة
احفظ حالة الطلب فقط إذا احتاجت الاستمرارية؛ واحقن الأسرار وقت التشغيل؛ وحدد ميزانيات للطلبات والتوكن والأدوات؛ ووفّر فحوصات صحة وجاهزية؛ واحجب البيانات الحساسة من السجلات؛ وحدد مالكاً لمحول الكتالوج وسياسة الترتيب والحوادث والتراجع.
# Web Hoster / Cloud Infrastructure
npm run build
npm run test
# Required runtime checks
curl -f https://store.example.invalid/api/health
curl -f https://store.example.invalid/api/search/readiness
# Rollback: redeploy the prior immutable release, then replay the
# evaluation set before restoring normal traffic.حفظ الحالة
Postgres لجلسات تحتاج استمرارية وإصدارات السياسات وأحداث التدقيق.
حدود
معدل الطلبات وحجم المدخلات والتوكن وعدد الأدوات والمهلة.
مراقبة
زمن المراحل والأخطاء والبديل وصفر النتائج وتغير ترتيب المنتجات.
أسرار
حقن وقت التشغيل مع تدوير وحجب كامل من السجلات والمتصفح.
ملكية
مالك للكتالوج والترتيب والحوادث والتراجع ومراجعة العربية.
قائمة الأمان والجاهزية
لا تطلق قبل وجود دليل مسمى على معرّفات المنتجات المرتبطة بالبيانات، وملكية قواعد العمل، وتكافؤ العربية، وضوابط الخصوصية، وإتاحة الاستخدام، والمراقبة، والتراجع. الفئات الحساسة للسلامة تحتاج تأكيداً أقوى وما تُعرض كتشخيص.
معرّفات مرتبطة
كل منتج معروض جاء من أداة.
قواعد خارج النموذج
السعر والمخزون والتوافق والسياسة حتمية.
خصوصية
أقل بيانات ممكنة مع إخفاء وانتهاء واضح.
مدخلات غير موثوقة
نص المنتج ما يصير تعليمات.
تراجع مجرّب
الإصدار السابق وبيانات التقييم جاهزة.
مراجعة بشرية
مالك العمل يعتمد القواعد والفئات الحساسة.
