الوثائق
إنشاء عميل مساعد التدفق
فتح منعطف وعرض أحداثه الحقيقية والإجابة على الطلبات المعلقة واستعادة المحادثة بعد انقطاع الاتصال.
مترجم تلقائيًا من الإنجليزية. عرض الأصل بالإنجليزية
الاتصال بالسايدكار قيد التشغيل
يتصل عميل مساعد البث بنفس السايدكار المحلي مثل سطح المكتب. احصل على عنوان URL للاسترجاع المخصص له والرمز المميز لحامل التطبيق الحالي من الاتصال الذي تتكامل معه. المنفذ المحفوظ منذ إطلاق الأمس ليس عنوانًا دائمًا: يطلب السايدكار منفذًا سريع الزوال في كل مرة يبدأ فيها. إن الرمز المميز لجلسة الحساب، والرمز المميز للاقتران المصاحب، والرمز المميز لجسر الإجراء المحدد له أغراض مختلفة ولا يمكن أن يحل محل حامل التطبيق على مسار الدردشة هذا.
قبل تقديم الدور، اقرأ GET /api/ai/status, /api/ai/providers، واختياريا /api/ai/models?configuredOnly=true. تعني بيانات الاعتماد التي تم تكوينها وجود مفتاح؛ ولا يثبت أن الموفر سيقبل الطلب أو أن نقطة النهاية تدعم النموذج المحدد. المضيف الافتراضي هو nikcli المضمن. يمكن للوضع المباشر دفق إجابة نموذجية ولكنه لا يوفر حلقة الأدوات المستمرة للمضيف المضمن أو المجدول المتخصص.
يستخدم هذا الدليل CADCAMFUN_URL و CADCAMFUN_TOKEN كمتغيرات shell العميل. قم بتوفيرها من خلال التعامل مع الاتصال الحالي لديك. انظر مرجع API لمنظمة العفو الدولية للمخططات الدقيقة و سايدكار لسلوك الإطلاق. لا تقوم الأمثلة بتثبيت بيانات الاعتماد أو اختيار نموذج نيابة عنك.
اختر هوية المشروع والرسم والجلسة
يمكن للعميل توريد أ sessionId عند المنعطف الأول وإعادة استخدامه في المنعطفات اللاحقة. تقوم الخدمة بتعيين معرف العميل هذا إلى جلسة المضيف الفعلية والتحقق من الربط قبل كل مطالبة. احتفظ بهوية الحدث/الجلسة كحالة محادثة، بدلاً من إنشاء معرف مختلف في كل مرة يتم فيها إعادة الاتصال بواجهة المستخدم.
حقيقي projectId يمنح الدور الوصول إلى الموارد المسموح بها لهذا المشروع. اختياري drawingId يجب أن تنتمي إلى هذا المشروع؛ فهو يربط أدوات الدور بهذا الرسم. لا يمكن تسمية الرسم بدون مشروعه. تم رفض الجلسة المملوكة بالفعل لحساب أو مشروع آخر. هذه هي عمليات التحقق من القبول قبل أن يتمكن النموذج من تنفيذ أدوات المشروع، وليست تلميحات في موجه النظام.
لإجراء محادثة توضيحية عامة، احذف المشروع والرسم. يستخدم التطبيق السياق الافتراضي الخاص به. بالنسبة للعمل الهندسي، احمل معرفات المشروع/الرسم المحددة الفعلية من خلال طلبات الدردشة واسترداد الحالة وقائمة الانتظار والإجهاض والتحكم. يجب ألا يعتمد تبديل الحساب بصمت الجلسة النشطة للحساب السابق.
أرسل منعطفًا متدفقًا
POST /api/ai/chat/stream إرجاع هيئة SSE. يتطلب رسالة غير فارغة على الرغم من أن مخطط النقل يجعل عدة حقول اختيارية حتى يتمكن المعالجون من إرجاع رسائل التحقق من الصحة القابلة للقراءة. يقوم السؤال العام التالي بتعطيل حقن السياق المخزن، مما يجعله مناسبًا للتحقق من النقل بشكل مستقل عن هندسة المشروع.
curl --no-buffer --fail-with-body \
-H "Authorization: Bearer $CADCAMFUN_TOKEN" \
-H "Content-Type: application/json" \
--data '{"message":"Explain the difference between G0 and G1.","sessionId":"inspection-demo","includeContext":false,"mode":"gcode"}' \
"$CADCAMFUN_URL/api/ai/chat/stream" يضيف projectId و drawingId للعمل على رسم معين. mode يقبل General أو CAD أو Cam أو Gcode أو مسار الأدوات. أ model يمكن أن يحتوي المحدد على الموفر والنموذج والطبقة والمتغير؛ المستويات سريعة أو متوازنة أو الأفضل. اكتشف معرفات الموفر/الطراز من الكتالوج الجاري تشغيله بدلاً من ترميز افتراضات حول البائع المتوفر.
permissionMode يقبل الموافقة أو السؤال أو التلقائي. تحتفظ مجموعات الأدوات التدميرية بالموافقة البشرية في كل وضع. ال agent يحدد الحقل دور مساعد تم تكوينه؛ يقرأ GET /api/ai/agents بدلاً من تخمين معرفات الأدوار.
قراءة إطارات بيانات SSE كاملة
يستخدم المسار وضع بيانات SSE الخاص بـ Effect: كل حمولة بيانات SSE عبارة عن سجل حدث JSON مع ملف type تمييز. قطعة الشبكة ليست حدًا للحدث. يمكن تقسيم أحرف JSON ونهايات الأسطر وأحرف UTF-8 بين القراءات. تجميع النص الذي تم فك تشفيره وتحليل الإطارات الكاملة فقط، وربط خطوط بيانات متعددة عند وجودها.
async function* events(response: Response): AsyncGenerator<Record<string, unknown>> {
if (!response.ok) throw new Error(await response.text())
if (!response.body) throw new Error("The response has no stream")
const reader = response.body.getReader()
const decoder = new TextDecoder()
let pending = ""
try {
while (true) {
const next = await reader.read()
pending += decoder.decode(next.value, { stream: !next.done })
let boundary: RegExpExecArray | null
while ((boundary = /\r?\n\r?\n/.exec(pending)) !== null) {
const frame = pending.slice(0, boundary.index)
pending = pending.slice(boundary.index + boundary[0].length)
const data = frame.split(/\r?\n/)
.filter((line) => line.startsWith("data:"))
.map((line) => line.slice(5).replace(/^ /, "")).join("\n")
if (!data) continue
const event: unknown = JSON.parse(data)
if (!event || typeof event !== "object" || !("type" in event)) {
throw new Error("Invalid assistant event")
}
yield event as Record<string, unknown>
}
if (next.done) break
}
} finally {
await reader.cancel().catch(() => {})
reader.releaseLock()
}
} يوضح هذا القارئ التأطير؛ يجب على عميل الإنتاج أيضًا التحقق من صحة كل حدث مقابل العقد المشترك أو استخدام عميل البث الذي تم إنشاؤه. إنه يتجاهل حقول التعليق/التحكم SSE عمدًا لأن حالة تطبيق نقطة النهاية هذه موجودة في سجلات بيانات JSON. يعد تحرير دفق العميل بمثابة تنظيف للنقل، وليس طلبًا صريحًا لإيقاف العمل من جانب الخادم.
تحقيق التقدم دون تكرار المحتوى
ابدأ عملية الجلب بنفس الحامل ونص JSON الموضح أعلاه، ثم كرر القارئ. إلحاق delta.text لنص الإجابة المرئية والطريق reasoning.text إلى وجهة نظرها الخاصة. أ done الحدث يحمل نص الرد النهائي؛ استبدل الإجابة المتراكمة أو قم بتسويتها بتلك القيمة النهائية بدلاً من إلحاق الإجابة بأكملها مرة أخرى.
| الأحداث | الدولة للحفاظ عليها |
|---|---|
turn, session_status, step | حالة الانشغال/إعادة المحاولة والخطوة الحالية؛ يمكن أن تتبع خطوة الانتهاء خطوة أخرى في نفس المنعطف. |
tool_call, tool_progress, tool_result | سجل بواسطة callId بالاسم والإدخال وتقدم الوسيطة والإخراج والنجاح؛ يحدد الوكيل المتخصص عند تواجده. |
tool_diagnostic | تم تطبيق نتائج الوسيطة/التنفيذ/الجزئية/التحقق، والخطورة، ونص الاسترداد، والطفرة الاختيارية. |
todo, goal | استبدل لقطة الخطة/الهدف الحالية بدلاً من إلحاق نسخ متكررة. |
retry, compaction, compacted | اشرح العمل المستمر مع الحفاظ على نص المحادثة الحالي وهوية الجلسة. |
heartbeat | لا يوجد محتوى مرئي؛ يرسل الخادم واحدًا كل خمس ثوانٍ تقريبًا بينما يظل الدفق المصدر نشطًا. |
ربط استدعاءات الأداة والنتائج حسب callId، وليس موضع الصفيف الخاص بهم. يمكن للأداة الفاشلة إرجاع تشخيصات مفيدة أثناء استمرار المساعد. لا يؤدي خطأ الموفر بعد نجاح أداة التحرير إلى التراجع عن هذا التغيير الهندسي.
الإجابة على الأذونات والأسئلة
A permission_request يتضمن معرف الجلسة ومعرف الطلب والإذن والأنماط. تقديم الطلب إلى الشخص وإرسال القرار المختار إليه POST /api/ai/permission/reply. الإجراء المكافئ للتحكم في الجلسة هو reply_permission. يتحقق الخادم من أن الطلب لا يزال معلقًا في المحادثة أو في جلسة فرعية مقبولة.
{
"sessionId": "inspection-demo",
"requestId": "request-id-from-the-event",
"decision": "once"
} القرار مرة واحدة، دائما، أو الرفض. إنه قرار إذن الخادم، وهو يختلف عن وسيطة الإجراء مثل التأكيد. معلقة question_request يحتوي على مجموعة أسئلة مرتبة. الرد من خلال /api/ai/sessions/control مع العمل answer_questionومعرف الطلب ومجموعة إجابة واحدة لكل سؤال؛ رفضها مع reject_question.
{
"sessionId": "inspection-demo",
"action": "answer_question",
"requestId": "question-id-from-the-event",
"answers": [["The selected option label"]]
} يجب أن تكون كل إجابة غير فارغة. يرفض سؤال الاختيار الواحد الإجابات المتعددة، ويسمح custom=false بالتسميات المدرجة فقط. الحفاظ على التسميات الدقيقة وترتيب الأسئلة. إذا أخبرك الخادم أن الطلب لم يعد معلقًا، فقم بتحديث عناصر التحكم بدلاً من نشر الرد القديم بشكل متكرر.
قائمة الانتظار، والتوجيه، ووقف العمل صراحة
يستخدم POST /api/ai/chat/queue للرسالة المقدمة أثناء عمل الدور. يستغرق الأمر معرف الجلسة والرسالة ومعرف المشروع الاختياري والتسليم. queue هو الإعداد الافتراضي وينتظر الخطوة الآمنة التالية؛ steer يطلب من المضيف دمجه على الفور. أ user_message يشير الحدث إلى أن الرسالة قد دخلت المحادثة.
يستخدم POST /api/ai/chat/abort مع هوية المحادثة عندما يقوم الشخص بالضغط على Stop. يؤدي هذا إلى إيقاف الدور والانضمام إلى العمال المرتبطين به. لا تقم بتنفيذ الإيقاف فقط عن طريق إلغاء الجلب: إن فقدان دفق HTTP ليس هو نفس العملية. يمكن للجلسة بعد ذلك قبول دور آخر دون إنشاء هوية مشروع جديدة.
POST /api/ai/sessions/remove يزيل الجلسة المضيفة بعد الإلغاء والانضمام إلى أبنائها. تقوم بإرجاع علامة تمت إزالتها. لا تعد إزالة محادثة بمثابة تراجع عن تعديلات المشروع التي تم إجراؤها بواسطة أدواتها؛ قم بمراجعة تلك العناصر أو استعادتها من خلال واجهات برمجة تطبيقات المشروع/السجل عند الحاجة.
التعافي بعد فقدان الدفق
عندما ينتهي القارئ دون حدث نهائي، تعامل مع الإكمال على أنه غير معروف. يتصل POST /api/ai/sessions/state بنفس معرف الجلسة ومعرف المشروع. تحتوي الاستجابة على مشغول ونص، مع أداة استرداد اختيارية/مهام المهام/الأحداث والاستخدام والمهام. إعادة بناء واجهة المستخدم من هذه اللقطة؛ إعادة تشغيل أحداث الأداة المستردة لا يؤدي إلى تنفيذها مرة أخرى.
ثم اطلب /api/ai/sessions/control مع حالة الإجراء لفحص الأذونات/الأسئلة المعلقة ومهام العامل والهدف وسياسة السياق. إن المنعطف المزدحم الذي ينتظر سؤالاً لم تتم الإجابة عليه يحتاج إلى رد، وليس نسخة ثانية من الموجه الأصلي. يمكن عرض الجلسة التي تم الإبلاغ عنها خاملة مع النص المتراكم كمخرجات مستردة على الرغم من اختفاء اتصال TCP الأصلي.
حافظ على التمييز بين فشل الشبكة، وحدث خطأ الموفر/التكوين المكتوب، والإجهاض الصريح، وإزالة الجلسة. أعد محاولة طلب النموذج فقط بعد التحقق مما فعله المنعطف الأول بالفعل. على وجه الخصوص، اقرأ الرسم الحالي بعد تشخيص الطفرة الجزئية أو عامل التحرير المتقطع.
داخل الخادم، يتم استئناف اتصال النموذج الذي تم إسقاطه لمدة تصل إلى خمس مرات مع فترة تراجع تبلغ 2 ثانية والتي تتضاعف حتى 30 ثانية. يجب على العميل الذي يعيد الاتصال بالدفق أن يقرأ حالة الجلسة، لأن تلك السير الذاتية يمكن أن تنهي العمل الذي لم يشاهده العميل الدفق.
السياق والمرفقات والضغط
includeContext: false تعطيل الحصول على السياق المخزن؛ لا يزال من الممكن توفير نص السياق الصريح لهذا المنعطف. يأمر التجميع العادي بالمتطلبات الدائمة، والملاحظات الحالية، ولقطات واجهة المستخدم، والذاكرة، والمراجع الداعمة، ويزيل تكرارها، ويطبق ميزانية إجمالية مكونة من 24،000 أحرف. يتم الإبلاغ عن الكتل المحذوفة/المبتورة؛ هذه الميزانية ليست نافذة الرمز المميز للموفر.
تشتمل المرفقات على الاسم وMIME الاختياري والبيانات وترميز النص/الأساسي الاختياري64. هناك خمسة على الأكثر لكل طلب. تقتصر الصور على 5 MiB لكل منها، وملفات PDF إلى 10 MiB، والنص إلى 2،000، 000 من الأحرف؛ يظهر أيضًا نص قصير يصل إلى 6، 000 حرفًا في السطر. يتم تحديد النص الذي يتم الاحتفاظ به للأدوات على نطاق المشروع في الذاكرة، لذا فإن إعادة الاتصال تختلف عن إعادة تشغيل الخدمة. يتم رفض الملفات غير المدعومة مع الأسباب.
يتطلب سياق إجراء التحكم تفصيلاً تفصيليًا للرمز المميز؛ يتجنب استطلاع الحالة العادية عمل الرمز المميز. يطلب اتفاق الإجراء من المضيف تلخيص مواد المحادثة القديمة. دمج الذاكرة هو عملية مضيفة منفصلة. استمر في الرسم المصدر وطلبات التصنيع من خلال واجهات برمجة التطبيقات الخاصة بهم بدلاً من الاعتماد على محادثة مضغوطة لإعادة إنتاج الشكل الهندسي الدقيق.
أخطاء نقل منفصلة عن نتائج المجال
HTTP 401 يعني أن الطلب لم يجتاز تفويض حامله. عادةً ما يعني 400 قبل البث رسالة فارغة، أو هوية جلسة لا يمكن الوصول إليها، أو مجموعة مشروع/رسم غير صالحة، أو نص مشوه. بمجرد إنشاء التدفق، يمكن للمعالج أن يصدر حدث خطأ مع التكوين أو الموفر أو الإحباط أو التصنيف الداخلي؛ التعامل مع هذا الحدث حتى عندما كانت حالة الاستجابة 200.
نتيجة الأداة التي تحتوي على ok=false هي نتيجة أداة، وليست بالضرورة تدفقًا فاشلاً. أظهر مخرجاته وتشخيصه حتى يتمكن المساعد أو الشخص من التعافي. يمكن أيضًا أن تكون قائمة انتظار العمال ممتلئة: يسمح المجدول بأربع مهام نشطة وستة عشر مهمة انتظار. قد يتم استرداد القارئ الذي تمت مقاطعته خلال المحاولات المقيدة؛ يتم التعامل مع أعمال التحرير على أنها غير مؤكدة بدلاً من إعادة تشغيلها تلقائيًا.
اقرأ العقود القابلة للتنفيذ في packages/api/src/groups/ai.ts و ai-events.ts; القبول والسلوك الأسلاك في apps/sidecar/src/http/ai.ts; رسم خرائط الجلسة في packages/ai/src/service.ts; التحقق من صحة السؤال في nikcli/session-controls.ts; توقيت ضربات القلب في heartbeat.ts. تحدد هذه المصادر النتيجة التي يجب على العميل تقديمها، بشكل مستقل عن نثر المساعد.
مرجع الحدث
كل SSE data: الإطار هو كائن JSON واحد ذو type و أ sessionId. تحليل الإطارات الكاملة فقط؛ قد يصل الإطار منقسمًا عبر أجزاء الشبكة.
| يكتب | الحقول الرئيسية | كيفية التعامل |
|---|---|---|
turn | phase: start, recovering, continuing, finish; خياري reason | إظهار حالة التقدم. recovering يعني أنه يتم استئناف الاتصال بالنموذج. |
session_status | status: busy, idle, retry | تمكين أو تعطيل زر الإرسال وقائمة الانتظار. |
user_message | رسالة المستخدم المخزنة | التوفيق بين واجهة المستخدم المتفائلة. |
delta | text | إلحاق بالإجابة المرئية. |
reasoning | النص الاستدلالي | خياري؛ تظهر في منطقة قابلة للطي. |
step, todo, goal | تقدم الخطة | قم بعرض قائمة المهام أو الخطة. |
tool_call | callId، أداة، الحجج، اختياري agent | أظهر الأداة التي يتم تشغيلها ولأي متخصص. |
tool_progress | callId، تقدم | قم بتحديث أداة تعمل لفترة طويلة. |
tool_result | callId، نتيجة | إظهار النتيجة؛ تطابق بها callId. |
tool_diagnostic | التفاصيل بما في ذلك mutationApplied | قرر ما إذا كان النموذج قد تغير. اقرأها قبل إعادة محاولة التعديل. |
permission_request / permission_resolved / permission_blocked | requestId; رد once, always, reject | اسأل المستخدم إذن POST /api/ai/permission/reply. |
question_request / question_resolved | بيانات السؤال | اعرض السؤال وأرسل الإجابة. |
context_snapshot, compaction, compacted | حالة السياق | أبلغ المستخدم أنه تم تلخيص التاريخ الأقدم. |
retry | attempt, message | إظهار إشعار عابر؛ لا تقم بإعادة إرسال المطالبة. |
done | text، خياري usage، خياري recovered | النص النهائي للدور. استبدل الدلتا المتراكمة بها. |
error | code: provider, config, aborted, internal; message | قم بإنهاء الدور في واجهة المستخدم بالرسالة؛ config عادة ما يعني فقدان بيانات الاعتماد. |
heartbeat | لا أحد | البقاء على قيد الحياة تقريبًا كل 5 ثانية؛ يتجاهل. |
عميل تدفق كامل
const base = process.env.CADCAMFUN_URL! // e.g. http://127.0.0.1:53211
const token = process.env.CADCAMFUN_TOKEN!
const response = await fetch(`${base}/api/ai/chat/stream`, {
method: "POST",
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
body: JSON.stringify({ projectId: "p1", message: "Summarise this project", mode: "general" }),
})
if (!response.ok || !response.body) throw new Error(`HTTP ${response.status}`)
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()
let buffer = "", answer = ""
for (;;) {
const { value, done } = await reader.read()
if (done) break
buffer += value
let end
while ((end = buffer.indexOf("\n\n")) >= 0) { // one SSE frame
const frame = buffer.slice(0, end); buffer = buffer.slice(end + 2)
const data = frame.split("\n").filter((l) => l.startsWith("data:"))
.map((l) => l.slice(5).trimStart()).join("\n")
if (!data) continue
const event = JSON.parse(data)
switch (event.type) {
case "delta": answer += event.text; break
case "permission_request": /* ask, then POST /api/ai/permission/reply */ break
case "done": answer = event.text; console.log(answer); break
case "error": throw new Error(`${event.code}: ${event.message}`)
}
}
} إذا انقطع الاتصال، فلا ترسل الرسالة مرة أخرى. اقرأ الجلسة مع POST /api/ai/sessions/state، لأن الخادم ربما استأنف اتصال النموذج وانتهى الدور بدونك. يستخدم /api/ai/chat/abort لإيقاف الدوران عمدا، و /api/ai/chat/queue لإضافة رسالة أثناء انشغال الجلسة.
كيف يبدو الدفق بدون مزود
مع عدم تكوين موفر الذكاء الاصطناعي، يظل الدفق مفتوحًا ويجيب بخطأ مكتوب، متبوعًا بنبض القلب. تم التقاطها من سايدكار جديدة:
data: {"type":"error","sessionId":"","code":"config","message":"No AI provider is configured. Add an API key in the AI settings."}
data: {"type":"heartbeat"} - لا تزال حالة HTTP 200 ونوع المحتوى
text/event-stream: مشاكل التكوين هي نتائج المجال، تسليمها كأحداث. sessionIdفارغ لأنه لم يتم إنشاء أي جلسة.heartbeatالإطارات تحافظ على الاتصال حيًا؛ تجاهلهم في التقديم ولا تحسبهم تقدمًا.- وينتهي كل إطار بخط فارغ. التحليل عن طريق التقسيم على الأسطر الفارغة والانضمام إلى
data:خطوط الإطار، كما يفعل مكرر SDK.
اختبر عميلك بدون نموذج
قم بتوجيه العميل إلى سايدكار بدون بيانات اعتماد: فهو يمارس مسار SSE الكامل وفرع الخطأ دون أي تكلفة.