DeepSeek Harness: التصريح بالاعتماديات: inject
آخر تحديث: 2026-08-31
الإضافات ليست جزراً — معظم الإضافات تحتاج خدمات توفرها إضافات أخرى. مصفوفة inject هي آلية التصريح بالاعتماديات في Cordis، تضمن تحميل الاعتماديات قبل مستهلكيها وتقضي على أخطاء وقت التشغيل "الخدمة غير موجودة".
📋 المتطلبات المسبقة: أكمل 11-first-plugin.md، تفهم apply و Context
1. ما ستتعلمه
- مصفوفة
injectللتصريح بالاعتماديات - قائمة الخدمات المدمجة: tools، llm، sessions، fs، shell إلخ
- ضمانات ترتيب تحميل الاعتماديات
- الاعتماديات الاختيارية
inject: ['tools', 'llm?'] - كشف الاعتماديات الدائرية
- آلية حقن الاعتماديات الأساسية
2. مصفوفة inject للتصريح بالاعتماديات
(1) ▶ مثال 1
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
export default {
name: 'my-tool',
inject: ['tools', 'llm'],
apply(ctx: Context) {
// ...
}
}
(3) ▶ مثال 3
export default class MyPlugin {
static name = 'my-tool'
static inject = ['tools', 'llm']
constructor(private ctx: Context) {
// ...
}
}
(4) عواقب عدم التصريح بـ inject
// ❌ عدم التصريح بـ 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:
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:
// inject = ['tools'] → ctx.tools: ToolsService
// inject = ['llm'] → ctx.llm: LLMService
// inject = ['tools', 'llm'] → كلتا ctx.tools + ctx.llم مكتوبة الأنواع
4. ضمان ترتيب تحميل الاعتماديات
(1) الفرز الطوبولوجي
Cordis يبني رسم اعتماديات من تصريحات inject لجميع الإضافات، ثم يحمّل بترتيب طوبولوجي:
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:
plugins:
plugin-c: ...
plugin-a: ...
plugin-b: ...
الإطار يحمّل بترتيب A → B → C بأي حال.
(3) التحميل المتوازي
الإضافات بدون علاقات اعتماد يمكن تحميلها بالتوازي:
graph TB
A[plugin-a] --> C[plugin-c<br/>inject: a, b]
B[plugin-b] --> C
A و B يمكن تحميلهما في نفس الوقت؛ C يُحمّل فقط بعد اكتمالهما معاً.
(4) مراحل التحميل
المرحلة 1: تحميل الإضافات بدون اعتماديات → [core, logger]
المرحلة 2: تحميل الإضافات المعتمدة على المرحلة 1 → [tools, sessions]
المرحلة 3: تحميل الإضافات المعتمدة على المرحلة 2 → [my-plugin, other-plugin]
...
5. الاعتماديات الاختيارية
(1) الصيغة
ألحق ? باسم الاعتمادية لجعلها اختيارية:
export const inject = ['tools', 'llm?']
المعنى: tools اعتمادية مطلوبة (فقدها يُسبب فشل التحميل)، llm اختيارية (فقدها يُحمّل بشكل طبيعي).
(2) الوصول للاعتماديات الاختيارية
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) الكشف وقت التشغيل
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:
A inject: ['B']
B inject: ['A']
هذا يُنشئ طريق مسدود: A تنتظر B، B تنتظر A — لا واحدة يمكنها التحميل.
(2) آلية الكشف في Cordis
الإطار يفحص رسم الاعتماديات عند البدء ويُبلغ عن الاعتماديات الدائرية فوراً:
Error: Circular dependency detected:
plugin-a → plugin-b → plugin-a
Please review your inject declarations.
(3) حل الاعتماديات الدائرية
الحل 1: استخراج الاعتماديات المشتركة
قبل: A → B → A
بعد: A → C, B → C
استخرج المنطق الذي يحتاجه كل من A و B إلى C.
الحل 2: فك الارتباط بالأحداث
// A لا تعتمد مباشرة على B، بل تستمع للأحداث
export const inject = []
export function apply(ctx: Context) {
ctx.on('b/ready', (bService) => {
// A تستخدم قدرات B بدون التصريح باعتمادية
})
}
الحل 3: استخدام الاعتماديات الاختيارية
// A تعتمد اختيارياً على B
export const inject = ['B?']
export function apply(ctx: Context) {
if (ctx.B) {
// استخدم B
}
}
(4) دورات ثلاث عقد
A → B → C → A
Cordis يمكنه أيضاً كشف دورات العقد المتعددة. رسالة الخطأ تُظهر السلسلة الكاملة.
7. آلية حقن الاعتماديات الأساسية
(1) تسجيل واكتشاف الخدمات
// إضافة المُوفّر تُسجّل الخدمة
ctx.provide('tools', toolsInstance)
// إضافة المستهلك تكتشف الخدمة
const tools = ctx.get('tools')
(2) توقيت inject و apply
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) عندما لا تكون الاعتماديات جاهزة
sequenceDiagram
participant F as الإطار
participant C as إضافة المستهلك
F->>F: فحص inject ['tools'] ❌ غير جاهز
F->>C: الإضافة تدخل حالة الانتظار
Note over F: انتظار تسجيل خدمة tools
F->>F: خدمة tools سُجّلت
F->>C: إعادة الفحص ✅ → استدعاء apply()
الإضافات لا تُهمَل عندما تكون الاعتماديات مفقودة — تدخل حالة انتظار وتُفعّل تلقائياً عندما تصبح الاعتماديات جاهزة.
(4) حقن اعتماديات آمن الأنواع
// تعيين الأنواع الداخلي للإطار
interface Context {
tools: ToolsService // عندما يتضمن inject 'tools'
llm: LLMService // عندما يتضمن inject 'llm'
sessions: SessionService // عندما يتضمن inject 'sessions'
// ...
}
آلية الأنواع الشرطية في TypeScript توسع تعريف نوع ctx تلقائياً بناءً على مصفوفة inject، مما يضمن أمان الأنواع وقت الترجمة.
❓ أسئلة شائعة
bash pnpm dsh web --patch --dump-config # أو وقت التشغيل: ctx.logger.info(Object.keys(ctx.services)) import هو مرجع وحدة ثابت في TypeScript، يُحدد وقت الترجمة. inject هو اعتمادية خدمة وقت التشغيل، يحلّها إطار Cordis أثناء تحميل الإضافة. كلاهما يُكمّل الآخر: import يجلب الأنواع والدوال المساعدة، inject يصرّح باعتماديات الخدمة وقت التشغيل.📖 ملخص
- مصفوفة
injectتصرّح باعتماديات وقت التشغيل للإضافة؛ الإطار يضمن تحميل الاعتماديات قبل apply - الخدمات المدمجة: tools، llm، sessions، fs، shell، sandbox، search، trajectory
- ألحق
?باسم الاعتمادية للاعتماديات الاختيارية؛ الإضافات تُحمّل بشكل طبيعي حتى لو كانت مفقودة - Cordis يكشف الاعتماديات الدائرية تلقائياً ويُبلغ عن الأخطاء؛ حلها بالاستخراج / فك الارتباط بالأحداث / الاعتماديات الاختيارية
- عند فقدان الاعتماديات، تدخل الإضافات حالة انتظار وتُفعّل تلقائياً عند الجاهزية
- inject هو اعتماد تصريحي — لا تتحكم بترتيب التحميل يدوياً أبداً
📝 تمارين
1. ⭐ أساسي: اكتب إضافة تصرّح بـ inject: ['tools']، سجّل أداة بسيطة باستخدام ctx.tools.register في apply. ابدأ وتحقق أن الأداة متاحة.
2. ⭐⭐ متوسط: اكتب إضافة تصرّح بـ inject: ['tools', 'llm?']. عندما يكون llm متاحاً، الأداة تستدعي LLM للتحسين؛ عندما يكون غير متاح، تُعيد نتائج مُتحللة. اختبر كلا السيناريوهين.
3. ⭐⭐⭐ تحدٍ: أنشئ عمداً إضافتين مُترابطتين A (inject: ['B']) و B (inject: ['A'])، لاحظ خطأ الاعتمادية الدائرية في Cordis. ثم أعد الكتابة باستخدام فك الارتباط بالأحداث لإلغاء الاعتمادية الدائرية، مع التحقق من تحميل كلتا الإضافتين بشكل طبيعي.