Un pattern molto comune nel settore, utilizzato in una vasta gamma di agenti, è Agent Skills
Le competenze risolvono un problema di contesto per gli agenti.
Invece di inserire tutte le istruzioni e le idee in un unico gigantesco prompt di sistema, le competenze degli agenti consentono una rivelazione progressiva.
Il tuo agente sa cosa può fare e chiama uno strumento molto specifico per scoprire come farlo.
Questo offre vantaggi significativi su Discourse perché possiamo mantenere il catalogo delle competenze negli argomenti e ottenere un editor, una cronologia e dei permessi gradevoli.
Utilizzando gli strumenti personalizzati in Discourse AI, possiamo approssimare questo sistema grazie a due funzionalità molto potenti che forniscono.
- Gli strumenti personalizzati consentono la modifica del prompt di sistema
- Gli strumenti personalizzati possono leggere argomenti e post in categorie particolari.
Il nostro obiettivo
Per approssimare le competenze su Discourse vorremmo
- Un argomento per ogni competenza
- Un directory automatica delle competenze disponibili nel prompt di sistema
Quindi, ad esempio, vorremmo un argomento di questa forma (grezzo):
---
name: doc-coauthoring
description: Guida gli utenti attraverso un flusso di lavoro strutturato per la co-autorizzazione di documentazione. Usa quando l'utente vuole scrivere una proposta, una specifica tecnica, un record decisionale o un documento simile.
---
# Flusso di lavoro di co-autorizzazione del documento
1. Raccogli il contesto dall'utente.
2. Concorda una struttura.
3. Bozza e rifina ogni sezione.
4. Testa il documento dal punto di vista di un nuovo lettore.
Da tradurre nel seguente nel prompt di sistema:
<available_skills>
<skill>
<name>doc-coauthoring</name>
<description>Guida gli utenti attraverso un flusso di lavoro strutturato per la co-autorizzazione di documentazione...</description>
<location>https://example.com/t/skill-doc-coauthoring/123</location>
</skill>
... più competenze qui ...
</available_skills>
Scegliamo questo formato particolare perché molti modelli linguistici sono già ottimizzati per cercare questa forma specifica, il che può migliorare il richiamo. Nota come il corpo della competenza effettiva sia assente.
Poi, se il modello rileva che un utente sta cercando di co-autorizzare un flusso di lavoro documentale, chiamerà lo strumento personalizzato load_skill per ottenere le istruzioni effettive.
Con questo in atto, possiamo definire una categoria dedicata su Discourse che contenga le competenze che vogliamo che i nostri agenti utilizzino e consentire ai membri della comunità di iterare su di esse.
Come funziona?
L’intera logica di load_skill è scritta in JavaScript, utilizzando uno strumento personalizzato.
discourse.filterTopics può essere chiamato per cercare argomenti in una categoria particolare
discourse.getPost può essere chiamato per ottenere il corpo di un post
customSystemMessage viene utilizzato per iniettare un prompt di sistema personalizzato
Con questi mattoni
Prima definiamo un nuovo strumento personalizzato:
- nome: load_skill
- parametri:
- “name”: “Il nome della competenza da available_skills”
Poi insegniamo l’analisi delle competenze:
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;
}
Assicurati di impostare SKILLS_CATEGORY_SLUG sullo slug effettivo della categoria contenente le tue competenze.
Poi usiamo customSystemMessage per iniettare il prompt di sistema
function customSystemMessage() {
var skills = availableSkills();
if (skills.length === 0) {
return null;
}
var lines = [
"Le competenze forniscono istruzioni e flussi di lavoro specializzati per compiti specifici.",
"Usa lo strumento load_skill per caricare una competenza quando un compito corrisponde alla sua descrizione.",
"<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");
}
Infine, costruiamo il caricatore di competenze che va dallo slug al contenuto della competenza.
function invoke(parameters) {
var requestedName = parameters && parameters.name;
if (typeof requestedName !== "string") {
return "Nessun nome di competenza è stato fornito.";
}
requestedName = requestedName.trim();
if (!SKILL_NAME_PATTERN.test(requestedName)) {
return "I nomi delle competenze devono usare lettere minuscole, numeri e trattini.";
}
var matches = availableSkills().filter(function (skill) {
return skill.name === requestedName;
});
if (matches.length === 0) {
return "Nessuna competenza disponibile ha il nome " + requestedName + ".";
}
if (matches.length > 1) {
return "Più di una competenza ha il nome " + requestedName + ".";
}
var skill = matches[0];
return [
'<skill_content name="' + xmlEscape(skill.name) + '">',
"# Competenza: " + skill.name,
"",
skill.content.trim(),
"",
"Argomento sorgente per questa competenza: " + skill.location,
"</skill_content>",
].join("\n");
}
Un agente completo funzionante + json dello strumento personalizzato è disponibile qui, puoi importarlo nella tua istanza e modificarlo come ritieni opportuno:
category-skills-agent.json (5.9 KB)
Vedere questo in azione
Limitazioni
La demo attuale ha ancora una chiamata N+1 che carica ogni post con una competenza ad ogni turno dell’utente. Su larga scala questo potrebbe non essere consigliabile.
Il design potrebbe essere modificato in modo che un post del catalogo delle competenze venga generato automaticamente utilizzando un flusso di lavoro o forse filterTopics potrebbe essere espanso per consentire una chiamata che chieda un prefisso.
Inoltre, le competenze degli agenti supportano risorse e script, le risorse potrebbero essere approssimate consentendo lo strumento “lettura” e collegandosi ad argomenti specifici dalla tua competenza. L’esecuzione di shell è chiaramente fuori portata per Discourse.
Speriamo che tu lo trovi utile


