DeepSeek Harness: التصريح بالاعتماديات: inject

آخر تحديث: 2026-08-31

الإضافات ليست جزراً — معظم الإضافات تحتاج خدمات توفرها إضافات أخرى. مصفوفة inject هي آلية التصريح بالاعتماديات في Cordis، تضمن تحميل الاعتماديات قبل مستهلكيها وتقضي على أخطاء وقت التشغيل "الخدمة غير موجودة".

💡 نصيحة: inject هو اعتماد تصريحي — فقط أخبر الإطار "ما أحتاجه"، وهو يتولى التحميل بالترتيب الصحيح. لا تتحكم بترتيب التحميل يدوياً أبداً.

📋 المتطلبات المسبقة: أكمل 11-first-plugin.md، تفهم apply و Context

1. ما ستتعلمه

آلية Inject الجاهزة


2. مصفوفة inject للتصريح بالاعتماديات

(1) ▶ مثال 1

TYPESCRIPT
import { Context } from '@deepseek-ai/cordis'

export const name = 'my-tool'
export const inject = ['tools', 'llm']

export function apply(ctx: Context) {
  // ctx.tools و ctx.llm مضمونان الجاهزية عند استدعاء apply
  ctx.logger.info('tools ready:', !!ctx.tools)
  ctx.logger.info('llm ready:', !!ctx.llm)
}

مصفوفة inject تسرد جميع أسماء الخدمات التي تحتاجها الإضافة. الإطار يضمن تسجيل هذه الخدمات قبل استدعاء apply.

(2) ▶ مثال 2

TYPESCRIPT
export default {
  name: 'my-tool',
  inject: ['tools', 'llm'],
  apply(ctx: Context) {
    // ...
  }
}

(3) ▶ مثال 3

TYPESCRIPT
export default class MyPlugin {
  static name = 'my-tool'
  static inject = ['tools', 'llm']
  
  constructor(private ctx: Context) {
    // ...
  }
}

(4) عواقب عدم التصريح بـ inject

TYPESCRIPT
// ❌ عدم التصريح بـ inject، استخدام الخدمات مباشرة
export function apply(ctx: Context) {
  ctx.tools.register(...)  // خطأ وقت التشغيل: ctx.tools قد لا يكون موجوداً
}

بدون التصريح بـ inject، قد تُحمّل الإضافة قبل تسجيل الخدمات المعتمدة، مما يُسبب ctx.tools غير معرّف.


3. قائمة الخدمات المدمجة

(1) الخدمات الأساسية

DSH يوفر الخدمات التالية عبر إضافات مدمجة:

اسم الخدمة المُوفّر الوظيفة
tools dsh-core تسجيل وتنفيذ الأدوات
llm dsh-plugin-llm محول LLM
sessions dsh-core إدارة الجلسات
fs dsh-plugin-fs عمليات نظام الملفات
shell dsh-plugin-shell تنفيذ أوامر Shell
sandbox dsh-plugin-sandbox بيئة Sandbox
search dsh-plugin-search بحث في الكود
trajectory dsh-core تسجيل السجلات

(2) طريقة الوصول للخدمات

بعد التصريح بـ inject، ادخل الخدمات عبر ctx.serviceName:

TYPESCRIPT
export const inject = ['tools', 'llm']

export function apply(ctx: Context) {
  // ctx.tools — خدمة الأدوات
  ctx.tools.register({
    name: 'my_tool',
    // ...
  })

  // ctx.llm — خدمة LLM
  const response = await ctx.llm.complete({
    messages: [{ role: 'user', content: 'hello' }]
  })
}

(3) استنتاج نوع الخدمة

TypeScript يستنتج أنواع الخدمات على ctx تلقائياً بناءً على inject:

TYPESCRIPT
// inject = ['tools'] → ctx.tools: ToolsService
// inject = ['llm']   → ctx.llm: LLMService
// inject = ['tools', 'llm'] → كلتا ctx.tools + ctx.llم مكتوبة الأنواع

4. ضمان ترتيب تحميل الاعتماديات

(1) الفرز الطوبولوجي

Cordis يبني رسم اعتماديات من تصريحات inject لجميع الإضافات، ثم يحمّل بترتيب طوبولوجي:

100%
graph LR
    A[plugin-a<br/>inject: []] --> B[plugin-b<br/>inject: ['a']]
    B --> C[plugin-c<br/>inject: ['a', 'b']]

ترتيب التحميل: A → B → C

(2) الترتيب التلقائي

لا تحتاج للتحكم بترتيب التحميل يدوياً. حتى لو ظهر C قبل A في cordis.yml:

YAML
plugins:
  plugin-c: ...
  plugin-a: ...
  plugin-b: ...

الإطار يحمّل بترتيب A → B → C بأي حال.

(3) التحميل المتوازي

الإضافات بدون علاقات اعتماد يمكن تحميلها بالتوازي:

100%
graph TB
    A[plugin-a] --> C[plugin-c<br/>inject: a, b]
    B[plugin-b] --> C

A و B يمكن تحميلهما في نفس الوقت؛ C يُحمّل فقط بعد اكتمالهما معاً.

(4) مراحل التحميل

TEXT 📖 للعرض فقط
المرحلة 1: تحميل الإضافات بدون اعتماديات → [core, logger]
المرحلة 2: تحميل الإضافات المعتمدة على المرحلة 1 → [tools, sessions]
المرحلة 3: تحميل الإضافات المعتمدة على المرحلة 2 → [my-plugin, other-plugin]
...

5. الاعتماديات الاختيارية

(1) الصيغة

ألحق ? باسم الاعتمادية لجعلها اختيارية:

TYPESCRIPT
export const inject = ['tools', 'llm?']

المعنى: tools اعتمادية مطلوبة (فقدها يُسبب فشل التحميل)، llm اختيارية (فقدها يُحمّل بشكل طبيعي).

(2) الوصول للاعتماديات الاختيارية

TYPESCRIPT
export const inject = ['tools', 'llm?']

export function apply(ctx: Context) {
  // tools موجود دائماً
  ctx.tools.register(...)

  // llm قد لا يكون موجوداً
  if (ctx.llm) {
    ctx.llm.complete(...)
  } else {
    ctx.logger.warn('llm not available, skipping LLM features')
  }
}

(3) حالات استخدام الاعتماديات الاختيارية

السيناريو مطلوبة/اختيارية السبب
تسجيل الأدوات يجب استخدام tools مطلوبة وظيفة أساسية
تحسين قدرات LLM اختيارية يعمل بدونها
وظائف Sandbox اختيارية ليست كل البيئات لديها sandbox
خدمة السجلات مطلوبة بنية تحتية

(4) الكشف وقت التشغيل

TYPESCRIPT
export const inject = ['tools', 'search?']

export function apply(ctx: Context) {
  ctx.tools.register({
    name: 'smart_search',
    async execute(params) {
      if (ctx.search) {
        return ctx.search.query(params.query)
      }
      return 'search service not available'
    }
  })
}

6. كشف الاعتماديات الدائرية

(1) ما هي الاعتمادية الدائرية

A تعتمد على B، و B تعتمد على A:

TEXT 📖 للعرض فقط
A inject: ['B']
B inject: ['A']

هذا يُنشئ طريق مسدود: A تنتظر B، B تنتظر A — لا واحدة يمكنها التحميل.

(2) آلية الكشف في Cordis

الإطار يفحص رسم الاعتماديات عند البدء ويُبلغ عن الاعتماديات الدائرية فوراً:

TEXT 📖 للعرض فقط
Error: Circular dependency detected:
  plugin-a → plugin-b → plugin-a
  
  Please review your inject declarations.

(3) حل الاعتماديات الدائرية

الحل 1: استخراج الاعتماديات المشتركة

TEXT 📖 للعرض فقط
قبل:  A → B → A
بعد:   A → C, B → C

استخرج المنطق الذي يحتاجه كل من A و B إلى C.

الحل 2: فك الارتباط بالأحداث

TYPESCRIPT
// A لا تعتمد مباشرة على B، بل تستمع للأحداث
export const inject = []

export function apply(ctx: Context) {
  ctx.on('b/ready', (bService) => {
    // A تستخدم قدرات B بدون التصريح باعتمادية
  })
}

الحل 3: استخدام الاعتماديات الاختيارية

TYPESCRIPT
// A تعتمد اختيارياً على B
export const inject = ['B?']

export function apply(ctx: Context) {
  if (ctx.B) {
    // استخدم B
  }
}

(4) دورات ثلاث عقد

TEXT 📖 للعرض فقط
A → B → C → A

Cordis يمكنه أيضاً كشف دورات العقد المتعددة. رسالة الخطأ تُظهر السلسلة الكاملة.


7. آلية حقن الاعتماديات الأساسية

(1) تسجيل واكتشاف الخدمات

TYPESCRIPT
// إضافة المُوفّر تُسجّل الخدمة
ctx.provide('tools', toolsInstance)

// إضافة المستهلك تكتشف الخدمة
const tools = ctx.get('tools')

(2) توقيت inject و apply

100%
sequenceDiagram
    participant F as الإطار
    participant P as إضافة المُوفّر
    participant C as إضافة المستهلك
    
    F->>P: تحميل المُوفّر
    P->>F: apply() → تسجيل خدمة 'tools'
    F->>C: فحص inject ['tools'] ✅ جاهز
    F->>C: استدعاء apply()
    C->>F: ctx.tools متاح

(3) عندما لا تكون الاعتماديات جاهزة

100%
sequenceDiagram
    participant F as الإطار
    participant C as إضافة المستهلك
    
    F->>F: فحص inject ['tools'] ❌ غير جاهز
    F->>C: الإضافة تدخل حالة الانتظار
    Note over F: انتظار تسجيل خدمة tools
    F->>F: خدمة tools سُجّلت
    F->>C: إعادة الفحص ✅ → استدعاء apply()

الإضافات لا تُهمَل عندما تكون الاعتماديات مفقودة — تدخل حالة انتظار وتُفعّل تلقائياً عندما تصبح الاعتماديات جاهزة.

(4) حقن اعتماديات آمن الأنواع

TYPESCRIPT
// تعيين الأنواع الداخلي للإطار
interface Context {
  tools: ToolsService      // عندما يتضمن inject 'tools'
  llm: LLMService          // عندما يتضمن inject 'llm'
  sessions: SessionService // عندما يتضمن inject 'sessions'
  // ...
}

آلية الأنواع الشرطية في TypeScript توسع تعريف نوع ctx تلقائياً بناءً على مصفوفة inject، مما يضمن أمان الأنواع وقت الترجمة.


❓ أسئلة شائعة

س هل ترتيب مصفوفة inject مهم؟
ج لا. inject يصرّح فقط "أحتاج هذه الخدمات"؛ الإطار يُحدد ترتيب التحميل بناءً على رسم الاعتماديات العام.
س ماذا يحدث إذا نسيت التصريح بـ inject لكن استخدمت خدمة؟
ج لا خطأ وقت الترجمة (TypeScript قد يحذّر)، لكن وقت التشغيل الخاصية المقابلة على ctx قد تكون undefined، مُسببةً TypeError. صرّح دائماً بالخدمات التي تستخدمها.
س كم عدد الخدمات التي يمكن أن تعتمد عليها إضافة؟
ج لا حد أقصى. لكن اعتماديات كثيرة عادة تعني أن مسؤوليات الإضافة غير واضحة — فكّر في تقسيمها.
س إذا سُجّلت خدمة اعتمادية اختيارية لاحقاً، هل تُفعّل الإضافة المنتظرة تلقائياً؟
ج نعم. Cordis يراقب أحداث تسجيل الخدمات ويُحوّل الإضافات المنتظرة للنشاط تلقائياً عندما تكون الاعتماديات جاهزة.
س كيف أرى جميع الخدمات المُسجّلة حالياً؟
ج bash pnpm dsh web --patch --dump-config # أو وقت التشغيل: ctx.logger.info(Object.keys(ctx.services))
س ما الفرق بين inject و import؟
ج import هو مرجع وحدة ثابت في TypeScript، يُحدد وقت الترجمة. inject هو اعتمادية خدمة وقت التشغيل، يحلّها إطار Cordis أثناء تحميل الإضافة. كلاهما يُكمّل الآخر: import يجلب الأنواع والدوال المساعدة، inject يصرّح باعتماديات الخدمة وقت التشغيل.

📖 ملخص


📝 تمارين

1. ⭐ أساسي: اكتب إضافة تصرّح بـ inject: ['tools']، سجّل أداة بسيطة باستخدام ctx.tools.register في apply. ابدأ وتحقق أن الأداة متاحة.

2. ⭐⭐ متوسط: اكتب إضافة تصرّح بـ inject: ['tools', 'llm?']. عندما يكون llm متاحاً، الأداة تستدعي LLM للتحسين؛ عندما يكون غير متاح، تُعيد نتائج مُتحللة. اختبر كلا السيناريوهين.

3. ⭐⭐⭐ تحدٍ: أنشئ عمداً إضافتين مُترابطتين A (inject: ['B']) و B (inject: ['A'])، لاحظ خطأ الاعتمادية الدائرية في Cordis. ثم أعد الكتابة باستخدام فك الارتباط بالأحداث لإلغاء الاعتمادية الدائرية، مع التحقق من تحميل كلتا الإضافتين بشكل طبيعي.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%