goblin-agents 0.1.5 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -31
- package/lib/llm/providers.js +5 -1
- package/lib/llm/utils.js +39 -51
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ Le module `goblin-agents` permet de créer et gérer des agents IA qui peuvent i
|
|
|
19
19
|
Les agents peuvent maintenir plusieurs contextes de conversation simultanément, ce qui permet d'avoir des conversations distinctes avec différents utilisateurs ou sur différents sujets. Chaque contexte conserve son propre historique de messages, ce qui permet à l'agent de maintenir la cohérence dans ses réponses.
|
|
20
20
|
|
|
21
21
|
Le module prend en charge diverses fonctionnalités avancées :
|
|
22
|
+
|
|
22
23
|
- Génération de texte à partir de prompts
|
|
23
24
|
- Conversations interactives avec historique
|
|
24
25
|
- Extraction de texte à partir d'images de documents PDF
|
|
@@ -36,7 +37,7 @@ Les agents peuvent être configurés avec une grande variété d'options qui inf
|
|
|
36
37
|
const {AiAgent} = require('goblin-agents/lib/llm/aiAgent.js');
|
|
37
38
|
|
|
38
39
|
// Dans une méthode d'un acteur Elf
|
|
39
|
-
async initAgents() {
|
|
40
|
+
async initAgents() {
|
|
40
41
|
const feedId = await this.newQuestFeed();
|
|
41
42
|
|
|
42
43
|
// Création d'un agent avec Ollama (local)
|
|
@@ -72,15 +73,14 @@ async initAgents() {
|
|
|
72
73
|
### Conversation avec un agent
|
|
73
74
|
|
|
74
75
|
```javascript
|
|
75
|
-
|
|
76
76
|
// Dans une méthode d'un acteur Elf
|
|
77
77
|
async askAgent(desktopId, agentId, contextId, question, saveExchange = false) {
|
|
78
78
|
const feedId = await this.newQuestFeed();
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
// Instancie l'agent approprié par ex. 'aiAgent@gpt-4o-assistant'
|
|
81
81
|
const agent = await new AiAgent(this).create(agentId, feedId);
|
|
82
|
-
|
|
83
|
-
// Continue la conversation sur ce contexte par ex. 'story-xyz' et
|
|
82
|
+
|
|
83
|
+
// Continue la conversation sur ce contexte par ex. 'story-xyz' et
|
|
84
84
|
// obtient une réponse à la question de l'utilisateur
|
|
85
85
|
const response = await agent.chat(contextId, question, desktopId);
|
|
86
86
|
|
|
@@ -95,11 +95,10 @@ async askAgent(desktopId, agentId, contextId, question, saveExchange = false) {
|
|
|
95
95
|
### Résumer et "squasher" un contexte
|
|
96
96
|
|
|
97
97
|
```javascript
|
|
98
|
-
|
|
99
98
|
// Dans une méthode d'un acteur Elf
|
|
100
99
|
async resumeAndSquashContexte(agentId, contextId) {
|
|
101
100
|
const feedId = await this.newQuestFeed();
|
|
102
|
-
|
|
101
|
+
|
|
103
102
|
// Instancie l'agent approprié par ex. 'aiAgent@gpt-4o-assistant'
|
|
104
103
|
const agent = await new AiAgent(this).create(agentId, feedId);
|
|
105
104
|
|
|
@@ -110,7 +109,7 @@ async resumeAndSquashContexte(agentId, contextId) {
|
|
|
110
109
|
// Reset la mémoire contextuel et persiste le changement
|
|
111
110
|
await agent.reset(contextId, true);
|
|
112
111
|
|
|
113
|
-
// Ajoute un nouvel historique d'échange a partir du résumé
|
|
112
|
+
// Ajoute un nouvel historique d'échange a partir du résumé
|
|
114
113
|
await agent.set(contextId, [{role: 'assistant', content: resumedContent}]);
|
|
115
114
|
|
|
116
115
|
// Sauve ce nouvel état
|
|
@@ -121,11 +120,10 @@ async resumeAndSquashContexte(agentId, contextId) {
|
|
|
121
120
|
### Utilisation d'outils externes
|
|
122
121
|
|
|
123
122
|
```javascript
|
|
124
|
-
|
|
125
123
|
// Dans une méthode d'un acteur Elf
|
|
126
124
|
async addWeatherToolSupportToAgent(agentId) {
|
|
127
125
|
const feedId = await this.newQuestFeed();
|
|
128
|
-
|
|
126
|
+
|
|
129
127
|
// Instancie l'agent approprié par ex. 'aiAgent@gpt-4o-assistant'
|
|
130
128
|
const agent = await new AiAgent(this).create(agentId, feedId);
|
|
131
129
|
|
|
@@ -160,13 +158,11 @@ async addWeatherToolSupportToAgent(agentId) {
|
|
|
160
158
|
// L'agent pourra maintenant utiliser l'outil weather.getWeather
|
|
161
159
|
const question = "Quel temps fait-il à Paris aujourd'hui?";
|
|
162
160
|
const response = await agent.chat(contextId, question, desktopId);
|
|
163
|
-
|
|
164
161
|
```
|
|
165
162
|
|
|
166
163
|
### Génération d'embeddings pour la recherche sémantique
|
|
167
164
|
|
|
168
165
|
```javascript
|
|
169
|
-
|
|
170
166
|
// Dans une méthode d'un acteur Elf
|
|
171
167
|
// {
|
|
172
168
|
// 'indexContent@article-1', 'Contenu à indexer'
|
|
@@ -189,9 +185,9 @@ async updateKnowledgeBase(articles) {
|
|
|
189
185
|
// Générer des embeddings pour plusieurs textes en une seule requête
|
|
190
186
|
const vectorsBatch = await agent.embedInBatch(texts);
|
|
191
187
|
|
|
192
|
-
// Upsert les articles dans un archetype IndexedContent
|
|
188
|
+
// Upsert les articles dans un archetype IndexedContent
|
|
193
189
|
for(const articleId of articleIds) {
|
|
194
|
-
// Récupère les vecteurs correspondant au texte dans le lot
|
|
190
|
+
// Récupère les vecteurs correspondant au texte dans le lot
|
|
195
191
|
// et le texte original
|
|
196
192
|
const index = articleIds.indexOf(articleId);
|
|
197
193
|
const vectors = vectorsBatch[index];
|
|
@@ -252,19 +248,21 @@ async queryKnowledgeBase(question = "Comment fonctionne l'apprentissage par renf
|
|
|
252
248
|
|
|
253
249
|
Le module `goblin-agents` interagit avec plusieurs autres composants de l'écosystème Xcraft :
|
|
254
250
|
|
|
255
|
-
- **xcraft-core-goblin** : Fournit l'infrastructure Elf pour la création d'acteurs
|
|
256
|
-
- **xcraft-core-stones** : Utilisé pour la définition des types de données
|
|
257
|
-
- **xcraft-core-utils** : Utilisé pour les appels API REST et la gestion des verrous
|
|
251
|
+
- **[xcraft-core-goblin]** : Fournit l'infrastructure Elf pour la création d'acteurs
|
|
252
|
+
- **[xcraft-core-stones]** : Utilisé pour la définition des types de données
|
|
253
|
+
- **[xcraft-core-utils]** : Utilisé pour les appels API REST et la gestion des verrous
|
|
258
254
|
|
|
259
255
|
Le module peut également interagir avec d'autres services Goblin via le système d'appels d'outils, permettant aux agents d'effectuer des actions concrètes dans l'application.
|
|
260
256
|
|
|
261
257
|
## Configuration avancée
|
|
262
258
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
259
|
+
| Option | Description | Type | Valeur par défaut |
|
|
260
|
+
|--------|-------------|------|------------------|
|
|
261
|
+
| version | Version des agents | Number | 2 |
|
|
262
|
+
| defaultProfile | Profil par défaut des agents | String | null |
|
|
263
|
+
| defaultSettings | Paramètres par défaut | Object | null |
|
|
264
|
+
| profiles | Profils pour remplacer les paramètres | Object | {} |
|
|
265
|
+
| settings | Paramètres par nom | Object | {} |
|
|
268
266
|
|
|
269
267
|
## Détails des sources
|
|
270
268
|
|
|
@@ -276,6 +274,7 @@ Ce fichier est le point d'entrée du module, exportant les commandes Xcraft pour
|
|
|
276
274
|
|
|
277
275
|
Ce fichier définit la configuration du module, permettant de spécifier :
|
|
278
276
|
|
|
277
|
+
- La version des agents (`version`)
|
|
279
278
|
- Le profil par défaut des agents (`defaultProfile`)
|
|
280
279
|
- Les paramètres par défaut (`defaultSettings`)
|
|
281
280
|
- Les profils pour remplacer les paramètres (`profiles`)
|
|
@@ -294,14 +293,53 @@ Ce fichier contient la définition complète de l'acteur AiAgent, avec :
|
|
|
294
293
|
- **AiAgentLogic** : Logique métier de l'agent (mutations d'état)
|
|
295
294
|
- **AiAgent** : Classe principale de l'acteur avec les méthodes d'interaction
|
|
296
295
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
-
|
|
302
|
-
-
|
|
303
|
-
-
|
|
304
|
-
-
|
|
296
|
+
#### État et modèle de données
|
|
297
|
+
|
|
298
|
+
L'état d'un agent IA est défini par la classe `AiAgentShape` qui contient les propriétés suivantes :
|
|
299
|
+
|
|
300
|
+
- `id` : Identifiant unique de l'agent
|
|
301
|
+
- `version` : Version de l'agent
|
|
302
|
+
- `name` : Nom de l'agent
|
|
303
|
+
- `role` : Rôle de l'agent (assistant, etc.)
|
|
304
|
+
- `prompt` : Prompt par défaut utilisé pour définir le comportement de l'agent
|
|
305
|
+
- `provider` : Fournisseur LLM ('ollama' ou 'open-ai')
|
|
306
|
+
- `model` : Modèle de langage utilisé
|
|
307
|
+
- `host` : URL du serveur LLM
|
|
308
|
+
- `headers` : En-têtes HTTP pour les requêtes API
|
|
309
|
+
- `messages` : Historique des messages par contexte
|
|
310
|
+
- `options` : Options de configuration du modèle
|
|
311
|
+
- `format` : Format de sortie attendu
|
|
312
|
+
- `tools` : Outils externes disponibles pour l'agent
|
|
313
|
+
- `toolServiceId` : Identifiant du service d'outils
|
|
314
|
+
- `usability` : État d'utilisabilité ('disabled', 'experimental', 'stable', 'deprecated')
|
|
315
|
+
- `meta` : Métadonnées de l'agent
|
|
316
|
+
|
|
317
|
+
#### Méthodes publiques
|
|
318
|
+
|
|
319
|
+
- **`create(id, desktopId, agentState)`** - Crée un nouvel agent avec l'ID et l'état spécifiés.
|
|
320
|
+
- **`patch(agentState)`** - Met à jour l'état de l'agent avec les propriétés fournies.
|
|
321
|
+
- **`upgrade(version, agentState)`** - Met à niveau l'agent vers une nouvelle version avec l'état spécifié.
|
|
322
|
+
- **`gen(prompt)`** - Génère du texte à partir d'un prompt.
|
|
323
|
+
- **`reset(contextId, save)`** - Réinitialise l'historique des messages pour un contexte spécifique.
|
|
324
|
+
- **`readPDFpages(pdfPath)`** - Extrait le texte des pages d'un document PDF.
|
|
325
|
+
- **`embed(rawText)`** - Génère un embedding vectoriel pour un texte donné.
|
|
326
|
+
- **`embedInBatch(rawTexts)`** - Génère des embeddings vectoriels pour plusieurs textes en une seule requête.
|
|
327
|
+
- **`set(contextId, messages)`** - Définit l'historique des messages pour un contexte spécifique.
|
|
328
|
+
- **`chat(contextId, question, userDesktopId, sessionId)`** - Engage une conversation avec l'agent dans un contexte spécifique.
|
|
329
|
+
- **`callTools(contextId, message, userDesktopId, sessionId)`** - Appelle des outils externes en fonction des demandes de l'agent.
|
|
330
|
+
- **`addToolMessage(contextId, toolId, toolName, answer)`** - Ajoute une réponse d'outil à l'historique des messages.
|
|
331
|
+
- **`ask(contextId, question, questionId)`** - Pose une question à l'agent avec streaming de la réponse.
|
|
332
|
+
- **`resumeExchange(resumePrompt, contextId)`** - Résume un échange de conversation.
|
|
333
|
+
- **`react(reactPrompt, contextId, question)`** - Utilise le paradigme ReAct pour raisonner et agir sur une question.
|
|
334
|
+
- **`callAgent(contextId, agentId, action, feedId)`** - Appelle un autre agent pour effectuer une action.
|
|
335
|
+
- **`addAssistantMessage(contextId, message)`** - Ajoute un message de l'assistant à l'historique.
|
|
336
|
+
- **`save()`** - Persiste l'état de l'agent.
|
|
337
|
+
- **`change(path, newValue)`** - Modifie une propriété spécifique de l'état de l'agent.
|
|
338
|
+
- **`getUserChatContextHistory(contextId, filterTool)`** - Récupère l'historique des messages pour un contexte spécifique.
|
|
339
|
+
- **`getBaseSettings()`** - Récupère les paramètres de base de l'agent.
|
|
340
|
+
- **`getUsability()`** - Récupère l'état d'utilisabilité de l'agent.
|
|
341
|
+
- **`trash()`** - Marque l'agent comme supprimé.
|
|
342
|
+
- **`delete()`** - Supprime l'agent.
|
|
305
343
|
|
|
306
344
|
### `lib/llm/providers.js`
|
|
307
345
|
|
|
@@ -328,4 +366,8 @@ Ces utilitaires sont essentiels pour préparer les données avant de les envoyer
|
|
|
328
366
|
|
|
329
367
|
Le module inclut également des fonctionnalités spécifiques pour l'OCR (reconnaissance optique de caractères) avec un prompt système dédié (`OCR_SYSTEM_PROMPT`) qui guide les modèles multimodaux dans l'extraction de texte à partir d'images.
|
|
330
368
|
|
|
331
|
-
|
|
369
|
+
_Cette documentation a été mise à jour automatiquement._
|
|
370
|
+
|
|
371
|
+
[xcraft-core-goblin]: https://github.com/Xcraft-Inc/xcraft-core-goblin
|
|
372
|
+
[xcraft-core-stones]: https://github.com/Xcraft-Inc/xcraft-core-stones
|
|
373
|
+
[xcraft-core-utils]: https://github.com/Xcraft-Inc/xcraft-core-utils
|
package/lib/llm/providers.js
CHANGED
|
@@ -151,7 +151,11 @@ class OpenAIProvider extends LLMProvider {
|
|
|
151
151
|
const chat = await this.#openAI._post(query, chatRequest);
|
|
152
152
|
const {choices} = chat;
|
|
153
153
|
if (chat.error) {
|
|
154
|
-
const err = new Error(
|
|
154
|
+
const err = new Error(
|
|
155
|
+
chat.error?.metadata
|
|
156
|
+
? JSON.stringify(chat.error?.metadata)
|
|
157
|
+
: chat.error.message
|
|
158
|
+
);
|
|
155
159
|
err.code = chat.error.code;
|
|
156
160
|
throw err;
|
|
157
161
|
}
|
package/lib/llm/utils.js
CHANGED
|
@@ -77,7 +77,7 @@ const splitLongText = (text, maxLength, baseId) => {
|
|
|
77
77
|
return segments;
|
|
78
78
|
};
|
|
79
79
|
|
|
80
|
-
const html2chunks = async (html, chunkSize =
|
|
80
|
+
const html2chunks = async (html, chunkSize = 300, chunkOverlap = 0) => {
|
|
81
81
|
const textSplitter = RecursiveCharacterTextSplitter.fromLanguage('html', {
|
|
82
82
|
chunkSize,
|
|
83
83
|
chunkOverlap,
|
|
@@ -223,58 +223,46 @@ function groupSentences(text, maxSize) {
|
|
|
223
223
|
return chunks;
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
context += segmentContext;
|
|
251
|
-
|
|
252
|
-
// Ajouter du contexte avant et après le segment
|
|
253
|
-
const beforeContext = article.content.substring(
|
|
254
|
-
Math.max(0, s.start - 50),
|
|
255
|
-
s.start
|
|
256
|
-
);
|
|
257
|
-
const afterContext = article.content.substring(
|
|
258
|
-
s.end,
|
|
259
|
-
Math.min(article.content.length, s.end + 50)
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
const additionalContext = `### Contexte avant: ###\n${beforeContext}\n### Contexte après: ###\n${afterContext}\n`;
|
|
263
|
-
currentTokenCount += estimateTokenCount(additionalContext);
|
|
226
|
+
function buildPromptFromArticlesMulti(
|
|
227
|
+
initialQuestion,
|
|
228
|
+
questionsWithArticles,
|
|
229
|
+
maxTotalTokens
|
|
230
|
+
) {
|
|
231
|
+
let prompt = '[CONTEXT]\n\n';
|
|
232
|
+
let currentTokenCount = estimateTokenCount(prompt);
|
|
233
|
+
|
|
234
|
+
const buffer = 16000; // Réserve de tokens pour l'instruction + réponse
|
|
235
|
+
const maxTokensPerQuestion = Math.floor(
|
|
236
|
+
(maxTotalTokens - buffer) / questionsWithArticles.length
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
for (const {articles} of questionsWithArticles) {
|
|
240
|
+
let section = '';
|
|
241
|
+
let sectionTokenCount = estimateTokenCount(section);
|
|
242
|
+
for (const article of articles) {
|
|
243
|
+
const {title, sourceId, description} = article;
|
|
244
|
+
const articleBlock = `\n# ${title} \n## Lien: ${sourceId}\n\n${description}\n`;
|
|
245
|
+
const tokensForArticle = estimateTokenCount(articleBlock);
|
|
246
|
+
|
|
247
|
+
if (sectionTokenCount + tokensForArticle > maxTokensPerQuestion) {
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
264
250
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
251
|
+
section += articleBlock;
|
|
252
|
+
sectionTokenCount += tokensForArticle;
|
|
253
|
+
}
|
|
268
254
|
|
|
269
|
-
|
|
270
|
-
|
|
255
|
+
if (currentTokenCount + sectionTokenCount > maxTotalTokens - buffer) {
|
|
256
|
+
break;
|
|
271
257
|
}
|
|
272
|
-
});
|
|
273
258
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
259
|
+
prompt += section;
|
|
260
|
+
currentTokenCount += sectionTokenCount;
|
|
261
|
+
}
|
|
262
|
+
const questionContext = '[QUESTION]\n\n' + initialQuestion;
|
|
263
|
+
prompt += questionContext;
|
|
264
|
+
return prompt.trim();
|
|
265
|
+
}
|
|
278
266
|
|
|
279
267
|
/**
|
|
280
268
|
* Split an array into smaller chunks of a given size.
|
|
@@ -301,7 +289,7 @@ function chunkArray(array, size) {
|
|
|
301
289
|
* @returns {Promise<object>} A map of embeddings structured as:
|
|
302
290
|
* { [contentId]: { [key]: { chunk, embedding } } }
|
|
303
291
|
*/
|
|
304
|
-
async function embedChunksInBatch(agent, allChunks, batchSize =
|
|
292
|
+
async function embedChunksInBatch(agent, allChunks, batchSize = 50) {
|
|
305
293
|
const embeddingsByContent = {};
|
|
306
294
|
|
|
307
295
|
for (let start = 0; start < allChunks.length; start += batchSize) {
|
|
@@ -340,5 +328,5 @@ module.exports = {
|
|
|
340
328
|
getPDFImages,
|
|
341
329
|
sliceText,
|
|
342
330
|
groupSentences,
|
|
343
|
-
|
|
331
|
+
buildPromptFromArticlesMulti,
|
|
344
332
|
};
|