وش يبني هذا النظام
سير عمل قابل للاستكمال يقيّم فرصة البحث، ويتجنب تكرار النية، ويجمع أدلة قابلة للتتبع، ويصيغ محتوى عربي وإنجليزي، ويطبق بوابات تحقق حتمية ومدعومة بالنموذج، ويطلب الاعتماد، ويتحقق من النشر، ثم يراقب الصفحة. يستخدم سجلات مرجعية خيالية ويستبعد عمداً النشر التلقائي بدون اعتماد.
يشمل
- قرارات الفرصة والمحتوى الحالي
- تتبع الأدلة والادعاءات
- عقود الموجز والمسودة ثنائية اللغة
- الاعتماد البشري والتحقق من النشر
ما يشمل
- قواعد أو حدود SEO الداخلية لدى تينغ
- التعليمات الخاصة أو بيانات العملاء أو الأسرار
- أي ضمان للترتيب أو الزيارات
- النشر التلقائي بدون مراجعة
البنية وحالة سير العمل
تعامل مع السير كآلة حالات، مو كتعليمة طويلة. كل عقدة تقرأ وتكتب حالة محددة النوع، وكل بوابة تقدر توقف أو تعيد المحاولة أو ترسل العمل للمراجعة بدون ما تضيع مصادر القرار.

عرض مصدر Mermaid
flowchart RL
A["فرصة البحث"] --> B["الأهلية والمحتوى الحالي"]
B --> C["بحث الأدلة"]
C --> D["موجز ومسودة ثنائية اللغة"]
D --> E["بوابات الادعاءات والجودة وSEO"]
E --> F["اعتماد بشري"]
F --> G["النشر والتحقق"]
G --> H["المراقبة وقرار دورة الحياة"]عرض مصدر Mermaid
stateDiagram-v2
[*] --> مكتشفة
مكتشفة --> مؤهلة
مؤهلة --> مبحوثة
مبحوثة --> مصاغة
مصاغة --> متحقق_منها
متحقق_منها --> معتمدة
معتمدة --> منشورة
منشورة --> مراقبة
متحقق_منها --> مصاغة: فشل_البوابة
معتمدة --> مصاغة: طلب_تعديل
مراقبة --> مبحوثة: تحديثالحزمة المرجعية
الأدوات المسماة هنا خيار تعليمي عملي، وليست تصريحاً عن حزمة تينغ الإنتاجية. خلك محافظ على وصول النموذج والتتبّع داخل الخادم، وخلي مستودع المحتوى هو مصدر الحقيقة.
OpenRouter
بوابة نماذج مرجعية داخل الخادم لمهام البحث والتصنيف والصياغة المنظمة.
LangChain
موصلات النماذج والأدوات والمخرجات المتحقق منها لكل عقدة في السير.
LangGraph
تنسيق آلة حالات قابلة للاستكمال وبوابات شرطية ومحاولات محدودة.
LangSmith
تتبّع ومجموعات تقييم واختبارات انحدار للعقد المدعومة بالنموذج.
استضافة ويب / بنية تحتية سحابية
يشغّل خادم التطبيق والعامل والطابور مع حالة دائمة وأسرار محقونة.
هيكل المشروع والإعداد
افصل التنسيق وتخزين المحتوى والموصلات وبيانات التقييم. كذا تقدر تختبر البوابات الأساسية بدون الاتصال بأنظمة خارجية.
src/
seo/
graph.ts
state.ts
nodes/
discover-opportunity.ts
inspect-existing-content.ts
research-evidence.ts
build-brief.ts
draft-locales.ts
validate-claims.ts
validate-seo.ts
request-approval.ts
publish.ts
monitor.ts
content/
repository.ts
schemas.ts
api/seo-jobs.ts
evals/seo-cases.json
tests/seo-workflow.test.tsnpm install @langchain/core @langchain/langgraph @openrouter/ai-sdk-provider ai langsmith zod
# Add your application server, CMS adapter and test runner.
# Keep every credential on the server.# .env.example — placeholders only
OPENROUTER_API_KEY=replace_with_server_side_secret
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
REFERENCE_MODEL_ID=choose_for_your_requirements
LANGSMITH_TRACING=true
LANGSMITH_API_KEY=replace_with_server_side_secret
LANGSMITH_PROJECT=seo-automation-reference
CONTENT_REPOSITORY_URL=replace_with_internal_endpoint
CONTENT_REPOSITORY_TOKEN=replace_with_server_side_secret
PUBLIC_SITE_ORIGIN=https://replace-with-your-domain.exampleالفرصة والأدلة وحالة سير العمل
احفظ القرارات ومعرّفات المصادر بدل الاعتماد على سجل محادثة. لازم تكون الحالة قابلة للاستكمال والتدقيق والتصحيح بأمان.
import { z } from "zod";
export const Opportunity = z.object({
id: z.string(),
query: z.string().min(2),
locale: z.enum(["en", "ar"]),
intent: z.enum(["informational", "commercial", "navigational"]),
existingUrls: z.array(z.string().url()),
decision: z.enum(["create", "refresh", "merge", "reject"]),
evidenceIds: z.array(z.string()),
status: z.enum(["discovered", "researched", "drafted", "validated", "approved", "published", "monitoring"])
});export const Evidence = z.object({
id: z.string(),
url: z.string().url(),
publisher: z.string(),
title: z.string(),
retrievedAt: z.string().datetime(),
excerpt: z.string(),
supportsClaims: z.array(z.string()),
authority: z.enum(["primary", "official", "secondary"]),
locale: z.enum(["en", "ar"])
});
export const Claim = z.object({
text: z.string(),
evidenceIds: z.array(z.string()).min(1),
status: z.enum(["supported", "needs_review", "rejected"])
});type SeoState = {
jobId: string;
opportunity: Opportunity;
evidence: Evidence[];
brief?: ContentBrief;
drafts: Partial<Record<"en" | "ar", Draft>>;
gates: GateResult[];
approval?: { by: string; at: string };
publication?: { urls: string[]; verifiedAt: string };
lifecycle?: "keep" | "refresh" | "merge" | "noindex";
errors: Array<{ node: string; safeCode: string }>;
};const workflow = new StateGraph(SeoState)
.addNode("eligibility", inspectExistingContent)
.addNode("research", researchEvidence)
.addNode("brief", buildBrief)
.addNode("draft", draftLocales)
.addNode("claims", validateClaims)
.addNode("quality", validateSeo)
.addNode("approval", requestHumanApproval)
.addNode("publish", publishAndVerify)
.addConditionalEdges("eligibility", routeEligibility)
.addEdge("research", "brief")
.addEdge("brief", "draft")
.addEdge("draft", "claims")
.addConditionalEdges("claims", routeGateResult)
.addConditionalEdges("quality", routeGateResult)
.addConditionalEdges("approval", routeApproval)
.addEdge("publish", END);
export const seoGraph = workflow.compile({ checkpointer });منطق المحتوى الحالي والتجميع
قبل الصياغة، قارن اللغة ونية البحث ومجموعة الموضوع والتغطية الحالية. القرار الافتراضي يكون إنشاء أو تحديث أو دمج أو رفض؛ ولا تنشئ إلا إذا كانت الفرصة تضيف قيمة مختلفة فعلاً.
createلا توجد صفحة قوية بنفس اللغة والنية.
refreshصفحة واحدة مناسبة موجودة لكن تغطيتها ناقصة أو قديمة.
mergeعدة صفحات تتنافس على نفس النية.
rejectالمحتوى الحالي قوي أو الفرصة غير مناسبة.
export function decideAction(input: {
opportunity: Opportunity;
candidates: ExistingPage[];
}) {
const sameIntent = input.candidates.filter((page) =>
page.locale === input.opportunity.locale &&
page.intent === input.opportunity.intent
);
if (sameIntent.some((page) => page.coverage === "strong")) return "reject";
if (sameIntent.some((page) => page.coverage === "partial")) return "refresh";
if (sameIntent.length > 1) return "merge";
return "create";
}البحث وربط الادعاءات
خزّن المصادر بشكل مستقل عن المسودة. كل ادعاء واقعي يحمل معرّفات الأدلة ووقت الاسترجاع وحالة الدعم. الادعاء غير المدعوم أو القديم يوقف النشر بدل ما يتحول إلى كلام محتمل.
export function validateClaims(draft: Draft, evidence: Evidence[]) {
const knownEvidence = new Map(evidence.map((item) => [item.id, item]));
return draft.claims.map((claim) => {
const sources = claim.evidenceIds.map((id) => knownEvidence.get(id)).filter(Boolean);
return {
...claim,
status: sources.length > 0 && sources.every(isFreshAndAllowed)
? "supported"
: "needs_review"
};
});
}- اسمح بالمصادر الرسمية والأولية حسب السياسة.
- احفظ عنوان المصدر ووقت الاسترجاع والمقتطف.
- افصل عدم توفر المصدر عن عدم دعم الادعاء.
- لا تسمح للنموذج باختراع رابط أو معرّف دليل.
الموجز والصياغة ثنائية اللغة
ابنِ موجزاً واحداً مرتبطاً بالأدلة، وبعدها صغ مسودة لكل لغة. العربية مستند أصلي للسوق السعودي، مو ترجمة حرفية للإنجليزية. النسختان لازم تحافظان على نفس الحقائق المدعومة والنية.
موجز مشترك
النية والجمهور والأدلة والنطاق والروابط المطلوبة.
مسودتان أصليتان
كل لغة تصاغ لقرّائها مع نفس حدود الادعاءات.
ربط اللغات
كل نسخة تحمل canonical وhreflang صحيحين.
مراجعة الانحراف
قارن الادعاءات والأرقام والوعود بين اللغتين.
بوابات الجودة وSEO والنشر
استخدم فحوصات حتمية للبيانات الوصفية والروابط والمخططات وربط اللغات والعناوين الأساسية وتكرار النية. استخدم فحص النموذج فقط للحكم النوعي، ثم اطلب اعتماداً واضحاً قبل النشر.
{
"jobId": "seo-ref-0042",
"status": "awaiting_approval",
"decision": "refresh",
"locales": ["en", "ar"],
"gates": {
"claims": "pass",
"duplicateIntent": "pass",
"internalLinks": "pass",
"metadata": "pass",
"localization": "pass"
},
"previewUrls": ["/preview/en", "/preview/ar"],
"publishable": false
}المراقبة والتحديث والدمج وnoindex
بعد النشر، تحقق من الاستجابة العامة والعنوان الأساسي وقابلية الفهرسة وبدائل اللغة. راقب إشارات البحث والتفاعل المهمة، وبعدها افتح مراجعة تحديث أو دمج أو noindex بدل إنتاج صفحات جديدة باستمرار.
keep
المحتوى ما زال مفيداً وصحيحاً.
refresh
الأدلة أو التغطية أو الأداء تحتاج تحديثاً.
merge
صفحات متعددة تتنافس ونحتاج توحيدها.
noindex
الصفحة ما عادت تقدم قيمة مستقلة.
واجهة التطبيق وسلوك التعطل
ارجع معرّف مهمة مقبولة واحفظ كل انتقال واستخدم رموز خطأ آمنة. إذا تعطل البحث أو النموذج أو النشر، أوقف المهمة. لا تعتبر الصفحة منشورة حتى ينجح فحص الرابط العام.
export async function POST(request: Request) {
const input = StartJobInput.parse(await request.json());
const job = await repository.create(input);
try {
await seoGraph.invoke({ jobId: job.id, opportunity: input });
return Response.json({ jobId: job.id, status: "started" }, { status: 202 });
} catch (error) {
await repository.failSafely(job.id, "WORKFLOW_UNAVAILABLE");
return Response.json(
{ jobId: job.id, status: "paused", retryable: true },
{ status: 503 }
);
}
}مصفوفة الاختبار والتقييم
اختبر دوال العقد وانتقالات الحالة وحالات سير كاملة. احتفظ بحالات اختبار منفصلة للتكرار والادعاءات غير المدعومة وانحراف الترجمة وفشل التحقق العام وقرارات دورة الحياة.
const cases = [
["strong existing page", "reject"],
["partial same-intent page", "refresh"],
["unsupported claim", "block_publish"],
["Arabic draft changes the claim", "block_publish"],
["CMS accepts but public URL is 404", "fail_verification"],
["traffic decays after publication", "refresh_review"]
];
test.each(cases)("%s", async (scenario, expected) => {
expect(await runFixture(scenario)).toEqual(expected);
});create / refresh / merge / reject يطابق الحالة المتوقعة.
نسبة الادعاءات الواقعية المرتبطة بدليل صالح.
لا تغيّر في الأرقام أو الوعود أو المعنى بين النسختين.
صفر حالات نشر بدون اعتماد وفحص عام ناجح.
النشر والمراقبة والتراجع
شغّل خادم التطبيق على استضافة ويب / بنية تحتية سحابية. استخدم حالة دائمة وأسراراً محقونة ومحاولات محدودة ورؤية واضحة للطابور وفحوصات صحة وسياسة احتفاظ بالتتبّع ومالكاً محدداً للاعتمادات والحوادث.
حالة دائمة
احفظ المهمة والانتقالات والأدلة والاعتماد خارج ذاكرة العملية.
محاولات محدودة
استخدم idempotency وbackoff وطابور أخطاء بدل التكرار المفتوح.
مراقبة
تتبّع زمن كل عقدة والتكلفة ومعدل فشل البوابات وعمر المهام.
تراجع
احتفظ بالإصدار السابق وخطة rollback واختبر الرابط بعد أي استعادة.
ملكية
حدد مالك اعتماد ومالك نشر ومسؤول حادث قبل التشغيل.
الأمان والاستثناءات المقصودة
طبّق أقل صلاحية على بيانات اعتماد النشر، ونظّف النصوص المستوردة، وقيّد الروابط، وحدد معدل إنشاء المهام، واحجب البيانات الحساسة من السجلات. يتعمد المخطط استبعاد حدود الترتيب الخاصة والتعليمات المملوكة وتفاصيل الإنتاج القابلة للاستغلال.
صلاحيات محدودة
افصل قراءة المحتوى عن اعتماد النشر.
مدخلات غير موثوقة
نظّف HTML وتعليمات المصادر قبل إرسالها للنموذج.
أسرار الخادم
لا ترسل المفاتيح أو سجلات التتبّع للمتصفح.
حدود الطلبات
حدد معدل وحجم ومدة المهام.
سجلات آمنة
احجب الأسرار والبيانات الشخصية والمحتوى الخاص.
مراجعة بشرية
لا تتحول بوابة الاعتماد إلى إجراء تلقائي.
