نمط شائع جداً في الصناعة يُستخدم عبر مجموعة واسعة من الوكلاء هو مهارات الوكلاء (Agent Skills)
تُحل المهارات مشكلة السياق للوكلاء.
بدلاً من حشو جميع التعليمات والأفكار التي لديك في موجه نظام واحد ضخم، تسمح مهارات الوكلاء بالإفصاح التدريجي.
يعرف وكيلك ما يمكنه فعله ويستدعي أداة محددة جداً لمعرفة كيفية القيام بذلك.
هذا يوفر فوائد كبيرة على Discourse لأننا نستطيع إبقاء فهرس المهارات في المواضيع والحصول على محرر جيد، وسجل تاريخي، وصلاحيات.
باستخدام الأدوات المخصصة في ذكاء Discourse الاصطناعي، يمكننا تقريب هذا النظام بسبب ميزتين قويتين جداً تقدمهما.
- تسمح الأدوات المخصصة بتعديل موجه النظام.
- يمكن للأدوات المخصصة قراءة المواضيع والمشاركات في فئات معينة.
هدفنا
لتقريب المهارات على Discourse، نريد
- موضوع واحد لكل مهارة
- دليل مهارات متاح تلقائياً في موجه النظام
لذلك، على سبيل المثال، نريد موضوعاً بهذا الشكل (خام):
---
name: doc-coauthoring
description: Guide users through a structured workflow for co-authoring documentation. Use when the user wants to write a proposal, technical specification, decision record, or similar document.
---
# Document co-authoring workflow
1. Gather context from the user.
2. Agree on an outline.
3. Draft and refine each section.
4. Test the document from a new reader's perspective.
لترجمته إلى ما يلي في موجه النظام:
<available_skills>
<skill>
<name>doc-coauthoring</name>
<description>Guide users through a structured workflow for co-authoring documentation...</description>
<location>https://example.com/t/skill-doc-coauthoring/123</location>
</skill>
... more skills here ...
</available_skills>
نختار هذا التنسيق المحدد لأن العديد من نماذج اللغة مُضبوطة مسبقاً للبحث عن هذا الشكل المحدد، مما يمكن أن يحسن الاسترجاع. لاحظ كيف أن جسم المهارة الفعلية غائب.
ثم، إذا اكتشف النموذج أن مستخدماً يحاول التعاون في كتابة وثيقة، سيقوم باستدعاء الأداة المخصصة load_skill للحصول على التعليمات الفعلية.
مع وضع ذلك في مكانه، يمكننا تعريف فئة مخصصة على Discourse تحتوي على المهارات التي نريد أن يستخدمها وكلاؤنا والسماح لأعضاء المجتمع بالتكرار عليها.
كيف يعمل؟
كل منطق load_skill مُصاغ بلغة JavaScript، باستخدام أداة مخصصة.
يمكن استدعاء discourse.filterTopics للبحث عن مواضيع في فئة معينة.
يمكن استدعاء discourse.getPost للحصول على نص المشاركة.
يُستخدم customSystemMessage لحقن موجه نظام مخصص.
مع هذه اللبنات الأساسية
أولاً، نعرّف أداة مخصصة جديدة:
- الاسم: load_skill
- المعاملات:
- “name”: “اسم المهارة من available_skills”
بعد ذلك، نعلم تحليل المهارات:
var SKILLS_CATEGORY_SLUG = "agent-skills";
var SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
function skillTopics() {
var result = discourse.filterTopics({
q: "category:" + SKILLS_CATEGORY_SLUG + " order:created",
limit: 200,
});
return result && Array.isArray(result.topics) ? result.topics : [];
}
function topicOp(topic) {
if (!topic || !topic.first_post_id) {
return null;
}
return discourse.getPost(topic.first_post_id);
}
function frontmatter(raw) {
if (typeof raw !== "string") {
return null;
}
var match = raw.match(
/^---[ \t]*\n([\s\S]*?)\n---(?:[ \t]*\n|$)/
);
return match ? match[1] : null;
}
function scalarField(yaml, field) {
var match = yaml.match(
new RegExp("^" + field + ":[ \\t]*(.+)[ \\t]*$", "m")
);
return match ? match[1].trim() : null;
}
function xmlEscape(value) {
return String(value)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/\"/g, """)
.replace(/'/g, "'");
}
function skillRecord(topic) {
var post = topicOp(topic);
var yaml = post && frontmatter(post.raw);
if (!yaml) {
return null;
}
var name = scalarField(yaml, "name");
var description = scalarField(yaml, "description");
if (
!name ||
!description ||
!SKILL_NAME_PATTERN.test(name) ||
name.length > 64 ||
description.length > 1024
) {
return null;
}
return {
name: name,
description: description,
location: discourse.baseUrl + topic.url,
content: post.raw,
};
}
function availableSkills() {
var records = [];
var topics = skillTopics();
for (var i = 0; i < topics.length; i++) {
var record = skillRecord(topics[i]);
if (record) {
records.push(record);
}
}
records.sort(function (a, b) {
return a.name.localeCompare(b.name);
});
return records;
}
تأكد من ضبط SKILLS_CATEGORY_SLUG على الـ slug الفعلي للفئة التي تحتوي على مهاراتك.
بعد ذلك، نستخدم customSystemMessage لحقن موجه النظام
function customSystemMessage() {
var skills = availableSkills();
if (skills.length === 0) {
return null;
}
var lines = [
"Skills provide specialized instructions and workflows for specific tasks.",
"Use the load_skill tool to load a skill when a task matches its description.",
"<available_skills>",
];
skills.forEach(function (skill) {
lines.push(" <skill>");
lines.push(" <name>" + xmlEscape(skill.name) + "</name>");
lines.push(
" <description>" +
xmlEscape(skill.description) +
"</description>"
);
lines.push(
" <location>" + xmlEscape(skill.location) + "</location>"
);
lines.push(" </skill>");
});
lines.push("</available_skills>");
return lines.join("\n");
}
أخيراً، نبني محمل المهارات الذي ينتقل من الـ slug إلى محتوى المهارة.
function invoke(parameters) {
var requestedName = parameters && parameters.name;
if (typeof requestedName !== "string") {
return "No skill name was provided.";
}
requestedName = requestedName.trim();
if (!SKILL_NAME_PATTERN.test(requestedName)) {
return "Skill names must use lowercase letters, numbers, and hyphens.";
}
var matches = availableSkills().filter(function (skill) {
return skill.name === requestedName;
});
if (matches.length === 0) {
return "No available skill has the name " + requestedName + ".";
}
if (matches.length > 1) {
return "More than one skill has the name " + requestedName + ".";
}
var skill = matches[0];
return [
'<skill_content name="' + xmlEscape(skill.name) + '">',
"# Skill: " + skill.name,
"",
skill.content.trim(),
"",
"Source topic for this skill: " + skill.location,
"</skill_content>",
].join("\n");
}
متوفر وكيل كامل يعمل + ملف JSON للأداة المخصصة هنا، يمكنك استيراده إلى نسختك وتعديله كما ترى مناسباً:
category-skills-agent.json (5.9 KB)
رؤية ذلك في العمل
القيود
لا يزال العرض التوضيحي الحالي يحتوي على استدعاء N+1 لتحميل كل مشاركة بها مهارة في كل دور للمستخدم. قد لا يكون هذا مستحسنًا على نطاق واسع.
يمكن تعديل التصميم بحيث يتم إنشاء مشاركة فهرس المهارات تلقائياً باستخدام سير عمل، أو ربما يمكن توسيع filterTopics للسماح باستدعاء يطلب بادئة.
بالإضافة إلى ذلك، تدعم مهارات الوكلاء الموارد والسيناريوهات، ويمكن تقريب الموارد عن طريق السماح لأداة “القراءة” والربط بمواضيع محددة من مهارةك. تنفيذ Shell واضحاً أنه خارج نطاق Discourse.
آمل أن تجد هذا مفيداً


