جلسات المحادثة بمعرّفك الخاص
نظامك يسمّي كل محادثة بالفعل: تذكرة دعم، أو طلب، أو محادثة في تطبيقك. ومع external_id يصبح هذا الاسم هو جلسة K-Agent — بلا جدول ربط تحتفظ به. يبني هذا الدليل تكاملًا كاملًا على قاعدة واحدة من عقد الجلسات: POST /v1/sessions يجلب الجلسة أو ينشئها ويجيب دائمًا عن input.
طلب واحد لكل دور
رابط القسم «طلب واحد لكل دور»مع كل رسالة يرسلها عميلك، نفّذ طلبًا واحدًا:
const BASE = 'https://api.k-agent.kerneltics.com/v1';
/** * Sends one customer message to the agent and returns what to show them. * threadId: your conversation ID, e.g. "thread-58123" * messageId: your ID for this message — reused if you retry, so it is answered once */export async function askAgent({ threadId, messageId, customer, text }) { const res = await fetch(`${BASE}/sessions`, { method: 'POST', headers: { Authorization: `Bearer ${process.env.KAGENT_API_KEY}`, 'Content-Type': 'application/json', 'Idempotency-Key': `msg-${messageId}`, }, body: JSON.stringify({ agent: 'store-assistant', external_id: threadId, end_user: { external_id: customer.id, name: customer.firstName }, input: text, }), }); const body = await res.json(); if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
if (!body.run) return { kind: 'with_team' }; // human mode: your team has it if (['queued', 'in_progress'].includes(body.run.status)) return { kind: 'pending', runId: body.run.id }; if (body.run.outcome === 'handed_off') return { kind: 'handed_off', text: body.run.output_text }; return { kind: 'reply', text: body.run.output_text };}import os, requests
BASE = "https://api.k-agent.kerneltics.com/v1"
def ask_agent(thread_id: str, message_id: str, customer: dict, text: str) -> dict: """Sends one customer message to the agent and returns what to show them.""" r = requests.post( f"{BASE}/sessions", headers={ "Authorization": f"Bearer {os.environ['KAGENT_API_KEY']}", "Idempotency-Key": f"msg-{message_id}", # a retry is answered once }, json={ "agent": "store-assistant", "external_id": thread_id, "end_user": {"external_id": customer["id"], "name": customer["first_name"]}, "input": text, }, timeout=120, ) body = r.json() if not r.ok: raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
if body["run"] is None: return {"kind": "with_team"} # human mode: your team has it if body["run"]["status"] in ("queued", "in_progress"): return {"kind": "pending", "run_id": body["run"]["id"]} if body["run"]["outcome"] == "handed_off": return {"kind": "handed_off", "text": body["run"]["output_text"]} return {"kind": "reply", "text": body["run"]["output_text"]}curl https://api.k-agent.kerneltics.com/v1/sessions \ -H "Authorization: Bearer $KAGENT_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: msg-77120" \ -d '{ "agent": "store-assistant", "external_id": "thread-58123", "end_user": { "external_id": "cus_1042", "name": "فهد" }, "input": "وين طلبي؟" }'ما الذي يمنحك إياه هذا:
- الرسالة الأولى تنشئ الجلسة (
201)؛ وكل رسالة بعدها تستأنفها (200). لا تحتاج أبدًا إلى البحث عن معرّفsess_. - إعادة المحاولة آمنة. مفتاح
Idempotency-Keyمشتق من معرّف رسالتك يعني أن إعادة الطلب بعد انقطاع الشبكة تعيد النتيجة المحفوظة بدل إجابة ثانية. انظر عدم التكرار. - السجل محفوظ لك. يرى الوكيل المحادثة حتى الآن (
history_limitرسالة)، حتى بعد أيام. - التحويلات ظاهرة.
outcome: "handed_off"يعني أن فريقك أصبح مسؤولًا عن المحادثة. ومن بعدها تكونsession.modeقيمتها"human"وrunقيمتهاnull، ولا يُنتَج رد ذكاء اصطناعي حتى يُعاد التحويل إلى الوكيل أو تنتهي مدته.
اختر معرّفات جيدة
رابط القسم «اختر معرّفات جيدة»- المعرّفات فريدة في المشروع، والمعرّف الواحد يتبع وكيلًا واحدًا. إذا عمل عدة وكلاء على السجلات نفسها فأضف بادئة:
support:thread-58123وsales:thread-58123. - المحارف المسموحة حروف لاتينية وأرقام و
. _ : -، حتى 128 حرفًا، تبدأ بحرف أو رقم. ولا تبدأ بـsess_. - لا تستخدم رقم جوال أو بريدًا إلكترونيًا معرّفًا أبدًا. وإن كان هذا مفتاحك الطبيعي فاشتقّ منه معرّفًا بالتجزئة على خادمك.
- مرّر الشخص في
end_user. عميل الجلسة لا يمكن تغييره لاحقًا، وذاكرة الوكيل للإجراءات السابقة تتبع العميل لا الجلسة.
ردود تستغرق وقتًا أطول، أو تأتي من فريقك
رابط القسم «ردود تستغرق وقتًا أطول، أو تأتي من فريقك»بعض المحادثات تُدار أفضل دون إبقاء الطلب مفتوحًا: البريد، أو الرسائل النصية، أو أي قناة ترسل فيها الردود إلى العميل بنفسك. أرسل مع background: true واستقبل الردود عبر الويب هوك:
curl https://api.k-agent.kerneltics.com/v1/sessions/thread-58123/messages \ -H "Authorization: Bearer $KAGENT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"input": "تمام، شكرًا!", "client_message_id": "77121", "background": true}'يعيد الطلب 202 فورًا. اشترك بنقطة استقبال في message.created: يُطلق لردود الوكيل ولردود فريقك العامة من مكتب التحويل، مع role وauthor، فيتولى معالج واحد إيصال الاثنين:
{ "type": "message.created", "id": "evt_01k6rz8e0h2k4n6q8s0v2x4z6b", "created_at": 1791272100, "data": { "message": { "id": "msg_01k6rz8e0h2k4n6q8s0v2x4z6b", "object": "message", "session_id": "sess_01k6rz4p7h2c9m5x8w3t6v1qbg", "role": "assistant", "content": [{ "type": "text", "text": "العفو، يوصلك خلال 1 إلى 3 أيام عمل." }], "created_at": 1791272100 } }}يحمل جسم الحدث معرّف الجلسة sess_. احتفظ به من طلبك الأول (الاستجابات تعيد المعرّفين دائمًا)، أو اجلب الجلسة لتقرأ external_id.
القراءة والإغلاق والتنظيف
رابط القسم «القراءة والإغلاق والتنظيف»| المهمة | الطلب |
|---|---|
| عرض سجل المحادثة | GET /v1/sessions/thread-58123/messages |
| متابعة المحادثة مباشرة | GET /v1/sessions/thread-58123/events |
| إغلاق محادثة انتهت | POST /v1/sessions/thread-58123/close — رسالة جديدة تعيد فتحها |
| التحويل لموظف من جهتك | POST /v1/sessions/thread-58123/handoff مع {"reason_type": "customer_requested", "summary": "…"} |
| الحذف الكامل | DELETE /v1/sessions/thread-58123 |
أخطاء شائعة
رابط القسم «أخطاء شائعة»| الرمز | الحل |
|---|---|
session_agent_mismatch |
المعرّف مرتبط بوكيل آخر؛ أضف بادئة لكل وكيل. |
session_end_user_mismatch |
بدأت المحادثة لعميل آخر؛ لا تُعِد استخدام معرّفات المحادثات بين العملاء. |
session_busy / session_queue_full |
وصلت الرسائل أسرع مما يجيب الوكيل؛ أعد المحاولة بعد Retry-After، أو استخدم concurrency: "queue". |
external_id_invalid |
يحتوي المعرّف محارف خارج A–Z a–z 0–9 . _ : -، أو يبدأ بـ sess_. |