الجلسات ومعرّفات الجلسات
الجلسة محادثة دائمة بين عميل واحد ووكيل واحد. تحفظ كل رسالة بالترتيب، وهي ما يمنح الوكيل ذاكرة المحادثة. يمكنك مخاطبة الجلسة بالمعرّف الذي نصدره (sess_…) أو بمعرّفك الخاص (external_id)، مثل رقم طلب أو رقم تذكرة أو محادثة في نظامك.
عقد معرّفات الجلسات
رابط القسم «عقد معرّفات الجلسات»هذا العقد جزء من التزامنا باستقرار الواجهة البرمجية.
- نحن نصدر المعرّف دائمًا. لكل جلسة
id = sess_<26 chars>. لا يمكن تخمينه، ويُرتَّب حسب الوقت. - يمكنك إضافة معرّفك الخاص
external_id:- من 1 إلى 128 حرفًا تطابق
^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$(الشرطة السفلية مقبولة، مثلcus_123)؛ - حساس لحالة الأحرف؛
- فريد داخل المشروع؛
- يُحدَّد مرة واحدة ولا يتغير؛
- لا يبدأ بـ
sess_.
- من 1 إلى 128 حرفًا تطابق
- المعرّفان يعملان في كل مكان تظهر فيه الجلسة في رابط. والاستجابات تعيد المعرّفين دائمًا.
- الجلب أو الإنشاء. الطلب
POST /v1/sessionsمعexternal_id:- يعيد
201(created: true) أو200(created: false)؛ - مع
if_exists: "error"يعيد التطابقُ409 session_exists؛ - المعرّف
external_idنفسه مع وكيل مختلف يعيد409 session_agent_mismatch؛ - الجلسات المغلقة يُعاد فتحها ما لم ترسل
if_closed: "error"؛ - الطلبات الأولى المتزامنة آمنة (فهرس فريد مع إدراج أو تحديث).
- يعيد
- المعرّفات ليست بيانات اعتماد.
- كل طلب يخضع للتحقق من الهوية.
- رموز العميل لا تصل إلا إلى جلسات عميلها نفسه.
- المتصفحات لا تخاطب الجلسات بـ
external_idباستخدام مفتاح قابل للنشر وحده.
- لا بيانات شخصية في المعرّفات. نعيد
warnings: ["external_id_looks_like_phone"](أو…_email) حين يبدو المعرّف بيانات تواصل. اشتقّ هذه القيم أولًا على خادمك بدالة تجزئة (انظر أدناه)، واحفظ بيانات التواصل فيtraitsالخاصة بالعميل بدلًا من ذلك. - الذاكرة والحدود تتبع العميل، لا معرّف الجلسة. تغيير معرّفات الجلسات لا يعيد ضبط سجل الإجراءات ولا الحدود الخاصة بالعميل الموثّق.
- السجل يمتد على الجلسة كلها (آخر
history_limitرسالة). فترات الخمول لا تفعل سوى تحديث التعليمات المجمّدة وإصدار الوكيل (المقاطع)، ولا علاقة لها بنوافذ الفوترة.
ضمانات إضافية
رابط القسم «ضمانات إضافية»- ثلاثة أشياء فقط تنشئ الجلسات:
POST /v1/sessions، وPOST /v1/widget/sessions، والواجهة المتوافقة مع OpenAI عندما تسمّي جلسة. أي رابط آخر يحمل{session}غير معروف يعيد404 session_not_found. - القيم الافتراضية:
if_existsافتراضيًا"resume"، وif_closedافتراضيًا"reopen". - الحقل
inputفيPOST /v1/sessionsيُضاف ويُجاب عنه دائمًا، سواء أُنشئت الجلسة للتو أم استُؤنفت. طلب واحد لكل دور يكفي تكاملًا كاملًا. - عميل الجلسة يُحدَّد عند إنشائها. الاستئناف بـ
end_user.external_idمختلف يعيد409 session_end_user_mismatch. أما حقول الإنشاء الأخرى فتُتجاهل عند الاستئناف، وتخبرك الاستجابة بذلك عبرwarnings: ["create_params_ignored"]. - كيف يُقرأ المسار: لا يُعامل
{session}معرّفًا منّا إلا إذا كانsess_متبوعًا بـ 26 حرفًا صغيرًا بترميز base32، وكل ما عدا ذلك يُبحث عنه بوصفهexternal_id. - التجربة منفصلة. جلسات التجربة من لوحة التحكم في نطاق
external_idخاص بها، ولا تصل أبدًا إلى جلسة حقيقية ولا تكتب فيها.
اجلب جلسة أو أنشئها
رابط القسم «اجلب جلسة أو أنشئها»curl https://api.k-agent.kerneltics.com/v1/sessions \ -H "Authorization: Bearer $KAGENT_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "agent": "store-assistant", "external_id": "order-8812", "end_user": { "external_id": "cus_1042", "name": "فهد" }, "metadata": { "source": "order_page" }, "input": "توصلون أبها؟" }'const res = await fetch('https://api.k-agent.kerneltics.com/v1/sessions', { method: 'POST', headers: { Authorization: `Bearer ${process.env.KAGENT_API_KEY}`, 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID(), }, body: JSON.stringify({ agent: 'store-assistant', external_id: 'order-8812', end_user: { external_id: 'cus_1042', name: 'فهد' }, metadata: { source: 'order_page' }, input: 'توصلون أبها؟', }),});const body = await res.json();// 201 on the first call, 200 when the session already existed.console.log(res.status, body.created, body.session.id, body.run?.output_text);import os, uuid, requests
r = requests.post( "https://api.k-agent.kerneltics.com/v1/sessions", headers={"Authorization": f"Bearer {os.environ['KAGENT_API_KEY']}", "Idempotency-Key": str(uuid.uuid4())}, json={ "agent": "store-assistant", "external_id": "order-8812", "end_user": {"external_id": "cus_1042", "name": "فهد"}, "metadata": {"source": "order_page"}, "input": "توصلون أبها؟", }, timeout=120,)body = r.json()# 201 on the first call, 200 when the session already existed.print(r.status_code, body["created"], body["session"]["id"])يعيد الطلب الأول 201:
{ "session": { "id": "sess_01k6rz4p7h2c9m5x8w3t6v1qbg", "object": "session", "external_id": "order-8812", "end_user_id": "eu_01k6rz3t5v7w9x1y2z4a6b8c0d", "channel": "api", "status": "active", "mode": "agent", "concurrency": "queue", "version_policy": "latest", "metadata": { "source": "order_page" }, "created_at": 1791271920 }, "created": true, "message": { "id": "msg_01k6rz5b2c4d6e8f0g1h3j5k7m", "object": "message", "role": "user" }, "run": { "id": "run_01k6rz5a9d3f6g2h8j4k7m1n5p", "object": "run", "status": "completed", "outcome": "answered", "output_text": "إيه نعم، نوصّل لأبها خلال 3 إلى 5 أيام عمل، والتوصيل مجاني للطلبات فوق 200 ريال." }, "warnings": []}كل طلب لاحق بالمعرّف external_id نفسه يعيد 200 مع "created": false، ويضيف input الجديد ويجيب عنه.
المعاملات
رابط القسم «المعاملات»| الحقل | النوع | ملاحظات |
|---|---|---|
agent |
نص، إلزامي | معرّف الوكيل أو معرّفه النصي. |
external_id |
نص | معرّفك للجلسة؛ انظر العقد أعلاه. احذفه لتكتفي بمعرّفنا sess_. |
end_user |
كائن | {external_id, name?, traits?}. يمكن للمفاتيح السرية ضمان العميل فيصبح موثّقًا. ثابت طوال عمر الجلسة. |
variables |
كائن | قيم المتغيرات التي يعرّفها الوكيل. المتغيرات السرية غير مقبولة هنا. |
metadata |
كائن | حتى 16 مفتاحًا؛ المفتاح حتى 64 حرفًا والقيمة حتى 512. لا تُرسل إلى النموذج أبدًا. |
concurrency |
queue · reject · interrupt |
ما يحدث عند وصول رسالة والوكيل يرد. |
version_policy وversion |
latest · pinned |
ثبّت الجلسة على إصدار منشور واحد، أو اتبع الأحدث. |
if_exists |
resume · error |
الافتراضي resume. |
if_closed |
reopen · error |
الافتراضي reopen. |
input |
نص أو أجزاء نصية | يُضاف ويُجاب عنه في الطلب نفسه. |
overrides |
كائن | تجاوزات على مستوى الجلسة يسمح بها الوكيل، وتسري من المقطع التالي. للمفاتيح السرية فقط. |
client_message_id |
نص | يمنع تكرار input، كما في الرسائل. |
stream وbackground وwait_seconds |
بثّ الرد، أو العودة فورًا، أو الانتظار حتى wait_seconds ثانية (الافتراضي 60 والحد الأقصى 110). وتبقى الحالة 201 أو 200 في كل الأحوال؛ والتشغيل الذي ما زال يعمل يُعاد كما هو. |
حين تكون الاستجابة 409 أو 422
رابط القسم «حين تكون الاستجابة 409 أو 422»| الحالة | الاستجابة |
|---|---|
if_exists: "error" والجلسة موجودة |
409 session_exists |
المعرّف external_id نفسه مع وكيل مختلف |
409 session_agent_mismatch |
المعرّف external_id نفسه مع end_user.external_id مختلف |
409 session_end_user_mismatch |
if_closed: "error" والجلسة مغلقة |
409 session_closed |
external_id يخالف الصيغة، مثل sess_x أو order 8812 |
422 external_id_invalid |
{ "error": { "type": "conflict_error", "code": "session_agent_mismatch", "message": "المعرّف الخارجي (external_id) هذا مرتبط بجلسة لوكيل آخر.", "param": "external_id", "request_id": "req_01k6rz9f1j3m5p7r9t1w3y5a7c", "doc_url": "https://k-agent.kerneltics.com/docs/en/reference/errors/#session_agent_mismatch" }}إذا شغّلت عدة وكلاء على السجلات نفسها فأعطِ كلًا منهم نطاق معرّفات خاصًا، مثل support:order-8812 وsales:order-8812.
لا بيانات شخصية في المعرّفات
رابط القسم «لا بيانات شخصية في المعرّفات»تظهر المعرّفات في السجلات والروابط وملفات التصدير. عندما يبدو external_id رقم هاتف (8 أرقام أو أكثر) أو عنوان بريد إلكتروني، تحمل الاستجابة تحذيرًا:
{ "warnings": ["external_id_looks_like_phone"] }إذا كان مفتاحك الطبيعي بيانات شخصية فعلًا، فاشتقّ المعرّف على خادمك بتجزئة مفتاحية. النتيجة ثابتة للمدخل نفسه، ولا تكشف شيئًا، وتطابق صيغة external_id: base32(HMAC-SHA256(secret_you_keep, value)) بأحرف صغيرة، وأول 32 حرفًا. لا يرى K-Agent سرّك ولا يخزّنه.
import { createHmac } from 'node:crypto';
const ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567';
export function hashExternalId(value, secret) { const digest = createHmac('sha256', secret).update(value, 'utf8').digest(); let out = ''; let bits = 0; let acc = 0; for (const byte of digest) { acc = ((acc << 8) | byte) & 0xffff; bits += 8; while (bits >= 5) { out += ALPHABET[(acc >>> (bits - 5)) & 31]; bits -= 5; } } return out.slice(0, 32);}
hashExternalId('+966501234567', process.env.ID_HASH_SECRET);import base64, hashlib, hmac, os
def hash_external_id(value: str, secret: bytes) -> str: digest = hmac.new(secret, value.encode("utf-8"), hashlib.sha256).digest() return base64.b32encode(digest).decode("ascii").lower()[:32]
hash_external_id("+966501234567", os.environ["ID_HASH_SECRET"].encode())import ( "crypto/hmac" "crypto/sha256" "encoding/base32" "strings")
func hashExternalID(value string, secret []byte) string { mac := hmac.New(sha256.New, secret) mac.Write([]byte(value)) enc := base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(mac.Sum(nil)) return strings.ToLower(enc)[:32]}function hash_external_id(string $value, string $secret): string{ $digest = hash_hmac('sha256', $value, $secret, true); $alphabet = 'abcdefghijklmnopqrstuvwxyz234567'; $bits = ''; foreach (str_split($digest) as $byte) { $bits .= str_pad(decbin(ord($byte)), 8, '0', STR_PAD_LEFT); } $out = ''; foreach (str_split(substr($bits, 0, 160), 5) as $chunk) { $out .= $alphabet[bindec($chunk)]; } return $out;}الدوال الأربع تعطي القيمة نفسها: hashExternalId('+966501234567', 'my-hmac-secret') يساوي cs6vd2wrba2hf5w2ozuvftyoq5dewzhb. حافظ على ثبات السر — تغييره يغيّر كل المعرّفات.
التزامن
رابط القسم «التزامن»قد تصل رسالتان والوكيل ما زال يرد على الأولى. يحدد concurrency في الجلسة ما يحدث:
| السياسة | السلوك | الافتراضي لـ |
|---|---|---|
queue |
تنتظر الرسائل دورها ويُجاب عنها بالترتيب. تنتظر 10 رسائل كحد أقصى، والحادية عشرة تحصل على 409 session_queue_full. |
جلسات الواجهة البرمجية |
reject |
الرسالة التي تصل أثناء الرد تحصل على 409 session_busy مع Retry-After: 2. لا يُحفظ شيء. |
— |
interrupt |
يُترك الرد الجاري (superseded)، ويجيب رد واحد جديد عن كل الرسائل غير المُجاب عنها معًا. |
جلسات الودجت |
التشغيل المُستبدَل لا ينفّذ أي أداة ولا يكتب أي مخرج بعد استبداله. استخدم interrupt في واجهات الدردشة التي يرسل فيها الناس عدة رسائل قصيرة متتالية.
السجل والمقاطع
رابط القسم «السجل والمقاطع»- السجل يمتد على الجلسة كلها. يرى كل رد آخر
history_limitرسالة (الافتراضي 12)، ويبدأ القطع عند رسالة من العميل فلا تنقسم أي مبادلة. والعودة في اليوم التالي لا تعيد المحادثة إلى البداية. - السجل نصّي. تُرسل الأدوار السابقة نصًا عاديًا؛ أما استدعاءات الأدوات وتفكير النموذج فتبقى داخل التشغيل الذي أنتجها.
- ردود فريقك محسوبة. رسائل
human_agentالعامة تظهر للوكيل أدوارًا للمساعد بعد انتهاء التحويل؛ أما الملاحظات الداخلية فلا تظهر أبدًا. - المقاطع أجزاء الجلسة بين فترات الخمول (
idle_timeout_minutes، الافتراضي 30 دقيقة). عند بداية المقطع يُحدَّد إصدار الوكيل والتعليمات وقائمة الأدوات ثم تُجمَّد حتى المقطع التالي. ويبدأ مقطع جديد أيضًا بعد نشر إصدار جديد، أو عند تغيّر متغيرات الجلسة، أو بعد 24 ساعة. المقاطع لا تقطع السجل ولا علاقة لها بالفوترة.
دورة الحياة
رابط القسم «دورة الحياة»| الإجراء | الطريقة |
|---|---|
| قراءة جلسة | GET /v1/sessions/{session} (المعرّف أو external_id) |
تغيير البيانات الوصفية أو المتغيرات أو التجاوزات أو التزامن، أو إلغاء flagged |
PATCH /v1/sessions/{session} |
| الإغلاق | POST /v1/sessions/{session}/close. رسالة جديدة من العميل تعيد فتحها. |
| الحذف النهائي | DELETE /v1/sessions/{session} — يحذف الرسائل والتشغيلات والأحداث والتحويلات والتذاكر. |
| السرد والتصفية | GET /v1/sessions?agent=&end_user=&external_id=&status=&mode=&flagged=&channel=&created_after=&created_before= |
| قراءة السجل | GET /v1/sessions/{session}/messages |
| المتابعة المباشرة | GET /v1/sessions/{session}/events — انظر التشغيلات والبث |
إرسال الرسائل
رابط القسم «إرسال الرسائل»يقبل POST /v1/sessions/{session}/messages الحقل input ومعه اختياريًا client_message_id وstream وbackground وwait_seconds وvariables وmetadata، ويعيد {session, message, run}:
client_message_idيجعل إعادة إرسال الرسالة آمنة: المعرّف نفسه يعيد{session, message, run}الأصلية معIdempotent-Replayed: trueبدل رد ثانٍ. والمعرّف نفسه مع نص مختلف يعيد409 client_message_id_conflict.- وضع الموظف: ما دامت المحادثة بيد فريقك تُحفظ الرسالة، وتكون
runقيمتهاnull، وsession.modeقيمتها"human". background: trueيعيد202فورًا والتشغيل قيد التنفيذ؛ تابعه عبرGET /v1/runs/{run}أو بث أحداث الجلسة.
يرد فريقك عبر نقطة النهاية نفسها مع role: "human_agent" — انظر دليل مكتب التحويل.