DeepSeek Harness: محول LLM
آخر تحديث: 2026-08-31
نهج DSH "المستقل عن النموذج" ليس مجرد شعار — إنه تصميم معماري. محولات LLM، كـ Providers في نظام قدرات Cordis، تجعل تبديل النماذج سهلاً كتبديل بطاريات. هذا الدرس يتعمق في تسجيل محولات LLM وتنفيذها وإدارة النماذج المتعددة.
LLMCapability. الوكلاء والأدوات تعتمد فقط على هذه الواجهة، لا على ما إذا كان النموذج الأساسي DeepSeek أو GPT-4o.
📋 المتطلبات المسبقة: أكمل 22-capability.md، تفهم أدوار القدرات الثلاثة
1. ما ستتعلمه
- تسجيل محول ctx.llm
- محول نقطة نهاية متوافق مع OpenAI
- توجيه نماذج مخصص
- بروتوكول StreamChunk
- تصريحات قدرات النموذج
- موازنة تحميل النماذج المتعددة
2. تسجيل محول ctx.llm
(1) واجهة قدرة LLM
محول LLM في DSH ينفذ واجهة LLMCapability:
interface LLMCapability {
complete(request: LLMRequest): Promise<LLMResponse>
stream(request: LLMRequest): AsyncIterable<StreamChunk>
getModels(): ModelInfo[]
getModelCapabilities(model: string): ModelCapabilities
}
(2) ▶ مثال 2
import { Service, Context } from '@deepseek-ai/cordis'
export default class MyLLMAdapter extends Service {
constructor(ctx: Context) {
super(ctx, 'llm')
}
}
super(ctx, 'llm') يُسجّل المحول كخدمة llm.
(3) المحولات المدمجة
DSH يأتي مع محولي LLM:
| المحول | اسم الخدمة | النماذج المدعومة |
|---|---|---|
| DeepSeek | llm |
deepseek-chat, deepseek-reasoner |
| OpenAI Compatible | llm |
أي API متوافق مع OpenAI |
(4) ▶ مثال 4
# cordis.yml
plugins:
llm:
$replace: ./adapters/my-custom-llm
config:
apiKey: sk-xxx
endpoint: https://api.example.com/v1
3. محول نقطة نهاية متوافق مع OpenAI
(1) نظرة عامة على البروتوكول
OpenAI Chat Completions API هو المعيار الفعلي في الصناعة. المحول المدمج في DSH متوافق مع هذا البروتوكول:
interface OpenAIRequest {
model: string
messages: { role: string; content: string }[]
temperature?: number
max_tokens?: number
stream?: boolean
tools?: ToolDefinition[]
}
(2) ▶ مثال 2
# cordis.yml
plugins:
llm:
config:
provider: openai-compatible
apiKey: sk-xxx
endpoint: https://api.openai.com/v1
models:
- id: gpt-4o
capabilities: [chat, tool-use, vision]
- id: gpt-4o-mini
capabilities: [chat, tool-use]
(3) نقاط نهاية مخصصة
أي API متوافق مع OpenAI يمكن توصيله:
# الاتصال بـ Ollama المحلي
plugins:
llm:
config:
provider: openai-compatible
endpoint: http://localhost:11434/v1
apiKey: ollama
models:
- id: llama3
capabilities: [chat]
# الاتصال بـ Azure OpenAI
plugins:
llm:
config:
provider: openai-compatible
endpoint: https://my-resource.openai.azure.com/openai/deployments/my-deployment
apiKey: xxx
headers:
api-key: xxx
(4) كتابة محول مخصص
عندما لا يلبي المحول المدمج احتياجاتك، اكتب واحداً مخصصاً:
import { Service, Context } from '@deepseek-ai/cordis'
import { LLMCapability, LLMRequest, LLMResponse, StreamChunk } from '@deepseek-ai/dsh'
export const Config = Schema.object({
apiKey: Schema.string().required().description('API key'),
endpoint: Schema.string().required().description('API endpoint'),
defaultModel: Schema.string().default('custom-model').description('Default model ID')
})
export default class CustomLLMAdapter extends Service implements LLMCapability {
static inject = []
private apiKey: string
private endpoint: string
private defaultModel: string
constructor(ctx: Context) {
super(ctx, 'llm')
this.apiKey = ctx.config.apiKey
this.endpoint = ctx.config.endpoint
this.defaultModel = ctx.config.defaultModel
}
async complete(request: LLMRequest): Promise<LLMResponse> {
const response = await fetch(`${this.endpoint}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify({
model: request.model || this.defaultModel,
messages: request.messages,
temperature: request.temperature,
max_tokens: request.maxTokens,
stream: false
})
})
const data = await response.json()
return {
content: data.choices[0].message.content,
model: data.model,
usage: data.usage
}
}
async *stream(request: LLMRequest): AsyncIterable<StreamChunk> {
const response = await fetch(`${this.endpoint}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify({
model: request.model || this.defaultModel,
messages: request.messages,
temperature: request.temperature,
max_tokens: request.maxTokens,
stream: true
})
})
const reader = response.body!.getReader()
const decoder = new TextDecoder()
let buffer = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split('\n')
buffer = lines.pop() || ''
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6)
if (data === '[DONE]') return
try {
const parsed = JSON.parse(data)
const delta = parsed.choices[0].delta
if (delta.content) {
yield { type: 'text', content: delta.content }
}
} catch {}
}
}
}
}
getModels() {
return [{ id: this.defaultModel, capabilities: ['chat', 'tool-use'] }]
}
getModelCapabilities(model: string) {
return { chat: true, toolUse: true, vision: false }
}
}
4. توجيه النماذج المخصص
(1) مفهوم التوجيه
توجيه النماذج يختار محولات LLM مختلفة بناءً على خصائص الطلب:
graph TB
REQ[طلب LLM] --> ROUTER{مُوجّه النماذج}
ROUTER -->|مهمة كود| CODE[DeepSeek Coder]
ROUTER -->|استدلال| REASON[DeepSeek Reasoner]
ROUTER -->|محادثة بسيطة| CHAT[GPT-4o-mini]
(2) إعداد التوجيه
plugins:
llm:
config:
routing:
default: deepseek-chat
rules:
- match:
mode: ptc
model: deepseek-reasoner
- match:
mode: creative
model: deepseek-chat
- match:
tools: [shell]
model: deepseek-coder
(3) منطق توجيه مخصص
export default class RoutingLLMAdapter extends Service {
private adapters: Map<string, LLMCapability> = new Map()
constructor(ctx: Context) {
super(ctx, 'llm')
}
async complete(request: LLMRequest): Promise<LLMResponse> {
const model = this._selectModel(request)
const adapter = this._getAdapter(model)
return adapter.complete({ ...request, model })
}
private _selectModel(request: LLMRequest): string {
if (request.tools?.some(t => t.name === 'shell')) {
return 'deepseek-coder'
}
if (request.metadata?.mode === 'ptc') {
return 'deepseek-reasoner'
}
return 'deepseek-chat'
}
private _getAdapter(model: string): LLMCapability {
const prefix = model.split('-')[0]
return this.adapters.get(prefix) || this.adapters.get('default')!
}
}
5. بروتوكول StreamChunk
(1) تعريف البروتوكول
StreamChunk هو بروتوكول DSH الموحد للمخرجات المتدفقة:
type StreamChunk =
| { type: 'text'; content: string }
| { type: 'tool_call'; id: string; name: string; arguments: string }
| { type: 'tool_result'; id: string; result: any }
| { type: 'error'; error: Error }
| { type: 'done'; reason: 'stop' | 'tool_use' | 'length' }
(2) شرح أنواع القطع
| النوع | الوصف | المصدر |
|---|---|---|
text |
جزء محتوى نصي | مخرجات LLM المتدفقة |
tool_call |
طلب استدعاء أداة | LLM يقرر استدعاء أداة |
tool_result |
نتيجة تنفيذ الأداة | بعد تنفيذ الأداة |
error |
معلومات خطأ | أخطاء في أي مرحلة |
done |
نهاية التدفق | انتهت مخرجات LLM |
(3) استهلاك StreamChunks
for await (const chunk of ctx.llm.stream(request)) {
switch (chunk.type) {
case 'text':
process.stdout.write(chunk.content)
break
case 'tool_call':
console.log(`\nTool call: ${chunk.name}`)
break
case 'tool_result':
console.log(`Tool result:`, chunk.result)
break
case 'error':
console.error(`Error:`, chunk.error)
break
case 'done':
console.log(`\nDone (${chunk.reason})`)
break
}
}
6. تصريحات قدرات النموذج
(1) واجهة ModelCapabilities
interface ModelCapabilities {
chat: boolean
toolUse: boolean
vision: boolean
maxTokens: number
supportedModes: string[]
}
(2) التصريح بقدرات النموذج
getModels(): ModelInfo[] {
return [
{
id: 'deepseek-chat',
capabilities: {
chat: true,
toolUse: true,
vision: false,
maxTokens: 65536,
supportedModes: ['standard', 'ptc', 'minimal', 'creative']
}
},
{
id: 'deepseek-reasoner',
capabilities: {
chat: true,
toolUse: true,
vision: false,
maxTokens: 65536,
supportedModes: ['ptc']
}
}
]
}
(3) غرض تصريحات القدرات
الإطار يستخدم تصريحات القدرات ليقرر:
- هل يسمح باستخدام الأدوات لهذا النموذج (toolUse)
- هل يسمح بإرسال الصور (vision)
- الحد الأقصى للرموز (maxTokens)
- أوضاع التشغيل المدعومة (supportedModes)
7. موازنة تحميل النماذج المتعددة
(1) الإعدادات
plugins:
llm:
config:
loadBalancing:
strategy: round-robin
endpoints:
- model: deepseek-chat
endpoint: https://api.deepseek.com/v1
apiKey: sk-key1
weight: 3
- model: gpt-4o
endpoint: https://api.openai.com/v1
apiKey: sk-key2
weight: 1
(2) استراتيجيات الموازنة
| الاستراتيجية | الوصف |
|---|---|
round-robin |
تناوب دائري |
random |
اختيار عشوائي |
weighted |
توزيع مرجح |
least-latency |
اختيار أقل زمن استجابة |
(3) موازنة تحميل مخصصة
export default class LoadBalancedLLM extends Service {
private endpoints: Endpoint[]
private currentIndex = 0
async complete(request: LLMRequest): Promise<LLMResponse> {
const endpoint = this._nextEndpoint()
return endpoint.complete(request)
}
private _nextEndpoint(): Endpoint {
const endpoint = this.endpoints[this.currentIndex]
this.currentIndex = (this.currentIndex + 1) % this.endpoints.length
return endpoint
}
}
❓ أسئلة شائعة
complete، stream، getModels، getModelCapabilities. غياب أي دالة يُسبب خطأ ترجمة TypeScript.llm في اختبارات الوحدات.error. المستهلكون يجب أن يعالجوا قطع الأخطاء، يسجلوها، ويقرروا إن كان يجب إعادة المحاولة..env؛ لا تُرمّز بثبات في الإعدادات. Schemastery يدعم .hidden() لتمييز الحقول الحساسة.📖 ملخص
- محولات LLM هي Providers في نظام قدرات Cordis، تنفذ واجهة
LLMCapability - المحول المدمج المتوافق مع OpenAI يدعم أي API متوافق (DeepSeek، GPT-4o، Ollama إلخ)
- المحولات المخصصة تنفذ أربع دوال: complete/stream/getModels/getModelCapabilities
- توجيه النماذج يختار نماذج مختلفة بناءً على خصائص الطلب؛ موازنة التحميل توزع عبر نقاط نهاية متعددة
- بروتوكول StreamChunk يوحّد المخرجات المتدفقة: text/tool_call/tool_result/error/done
- تصريحات قدرات النموذج تُخبر الإطار بما يمكن للنموذج فعله؛ الإطار يقيّد الميزات وفقاً لذلك
📝 تمارين
1. ⭐ أساسي: هيّئ DSH لاستخدام نقطة نهاية متوافقة مع OpenAI لـ GPT-4o، عيّن API Key ومُعرّف النموذج، واجعل الوكيل يُكمل محادثة بنجاح.
2. ⭐⭐ متوسط: اكتب محول LLM مخصص يُوجّه الطلبات لخدمة Ollama محلية (http://localhost:11434/v1). نفّذ دوال complete و stream، اختبر المخرجات المتدفقة.
3. ⭐⭐⭐ تحدٍ: نفّذ محول توجيه يختار النماذج بناءً على وضع تشغيل الوكيل — وضع PTC يستخدم reasoner، الأوضاع الأخرى تستخدم chat. أنشئ جلستين تستخدمان أوضاعاً مختلفة وتحقق أنهما تستخدمان نماذج مختلفة.