feat: implement automated knowledge graph extraction pipeline and visualization component

This commit is contained in:
RaymondVerhoef
2026-05-20 08:55:27 +02:00
parent 08aaed591f
commit d5655d2232
3 changed files with 35 additions and 7 deletions

View File

@@ -311,11 +311,12 @@ Rules:
1. Identify topics that mean exactly the same thing. Choose one to keep, and one to delete. 1. Identify topics that mean exactly the same thing. Choose one to keep, and one to delete.
2. Identify topics that are too vague, irrelevant, or malformed to delete. 2. Identify topics that are too vague, irrelevant, or malformed to delete.
3. Identify missing logical relations (depends_on, part_of, related_to) if two topics are conceptually linked but missing a relation. 3. Identify missing logical relations (depends_on, part_of, related_to) if two topics are conceptually linked but missing a relation.
4. Return ONLY a valid JSON object describing the ACTIONS to take. Do not return the entire graph. Do not wrap in markdown blocks.`; 4. Evaluate the learning_relevance of each topic. If a topic is purely operational/mundane (like a printer guide), mark it as "exclude". If it's low priority, mark "peripheral".
5. Return ONLY a valid JSON object describing the ACTIONS to take. Do not return the entire graph. Do not wrap in markdown blocks.`;
// Send a compact representation to minimize token usage and avoid rate limits. // Send a compact representation to minimize token usage and avoid rate limits.
// The AI only needs id, label, and type to identify duplicates/merges. // The AI only needs id, label, type, and relevance to identify duplicates/merges and adjust relevance.
const compactTopics = currentTopics.map(({ id, label, type }) => ({ id, label, type })); const compactTopics = currentTopics.map(({ id, label, type, learning_relevance }) => ({ id, label, type, learning_relevance }));
const compactRelations = currentRelations.map(r => ({ const compactRelations = currentRelations.map(r => ({
source: r.source?.id || r.source, source: r.source?.id || r.source,
target: r.target?.id || r.target, target: r.target?.id || r.target,
@@ -329,7 +330,8 @@ Analyze this graph and return ONLY the optimized JSON object with this EXACT str
{ {
"merges": [ { "keepId": "id_to_keep", "deleteId": "id_to_delete" } ], "merges": [ { "keepId": "id_to_keep", "deleteId": "id_to_delete" } ],
"deletions": [ "id_to_delete_completely" ], "deletions": [ "id_to_delete_completely" ],
"newRelations": [ { "source": "id1", "target": "id2", "type": "depends_on" } ] "newRelations": [ { "source": "id1", "target": "id2", "type": "depends_on" } ],
"relevanceUpdates": [ { "id": "topic_id", "learning_relevance": "exclude" } ]
}`; }`;
const responseText = await anthropicApi.generateContent(systemPrompt, userPrompt); const responseText = await anthropicApi.generateContent(systemPrompt, userPrompt);
@@ -364,6 +366,15 @@ Analyze this graph and return ONLY the optimized JSON object with this EXACT str
} }
} }
if (actions.relevanceUpdates && Array.isArray(actions.relevanceUpdates)) {
for (const update of actions.relevanceUpdates) {
const topicIndex = updatedTopics.findIndex(t => t.id === update.id);
if (topicIndex !== -1) {
updatedTopics[topicIndex] = { ...updatedTopics[topicIndex], learning_relevance: update.learning_relevance };
}
}
}
// Ensure all relations only reference existing nodes and are normalized to string IDs // Ensure all relations only reference existing nodes and are normalized to string IDs
const finalTopicIds = new Set(updatedTopics.map(t => t.id)); const finalTopicIds = new Set(updatedTopics.map(t => t.id));
updatedRelations = updatedRelations updatedRelations = updatedRelations

View File

@@ -27,7 +27,11 @@ export async function upsertTopic(topic) {
await pb.collection('topics').getOne(topic.id); await pb.collection('topics').getOne(topic.id);
return await pb.collection('topics').update(topic.id, topic); return await pb.collection('topics').update(topic.id, topic);
} catch { } catch {
return await pb.collection('topics').create({ id: topic.id, ...topic }); return await pb.collection('topics').create({
id: topic.id,
learning_relevance: 'standard',
...topic
});
} }
} }

View File

@@ -6,6 +6,12 @@ You receive a source text. Your task is to extract core concepts, roles, and pro
Facts should be integrated into the descriptions of the other labels and NOT be extracted as unique topics. Facts should be integrated into the descriptions of the other labels and NOT be extracted as unique topics.
CRITICAL: To ensure the response fits within limits, extract a maximum of 15 key topics and their most important relations. Keep descriptions very concise. CRITICAL: To ensure the response fits within limits, extract a maximum of 15 key topics and their most important relations. Keep descriptions very concise.
You MUST assign a learning_relevance to each topic:
- "core": Fundamental company knowledge.
- "standard": Normal learning topics.
- "peripheral": Good to know, but low priority.
- "exclude": Pure operational reference material (e.g., printer guides, wifi passwords) that should NEVER be tested.
ALWAYS return a valid JSON object in the following format: ALWAYS return a valid JSON object in the following format:
{ {
"topics": [ "topics": [
@@ -13,7 +19,8 @@ ALWAYS return a valid JSON object in the following format:
"id": "unique-slug", "id": "unique-slug",
"label": "Topic title", "label": "Topic title",
"type": "concept | role | process", "type": "concept | role | process",
"description": "A concise, clear explanation of max 3 sentences." "description": "A concise, clear explanation of max 3 sentences.",
"learning_relevance": "core | standard | peripheral | exclude"
} }
], ],
"relations": [ "relations": [
@@ -35,10 +42,16 @@ You must explicitly identify and create relations between Roles, Processes, and
Every Process must have a Role attached (who does it). Every Process must have a Role attached (who does it).
Every Concept must have a relation to a Process or Role. Every Concept must have a relation to a Process or Role.
You MUST assign a learning_relevance to each topic:
- "core": Fundamental company knowledge.
- "standard": Normal learning topics.
- "peripheral": Good to know, but low priority.
- "exclude": Pure operational reference material (e.g., printer guides, wifi passwords) that should NEVER be tested.
Return a JSON object: Return a JSON object:
{ {
"topics": [ "topics": [
{ "id": "...", "label": "...", "type": "role | process | concept", "description": "...", "metadata": { "source": "github_handbook" } } { "id": "...", "label": "...", "type": "role | process | concept", "description": "...", "learning_relevance": "standard", "metadata": { "source": "github_handbook" } }
], ],
"relations": [ "relations": [
{ "source": "role-id", "target": "process-id", "type": "executes | related_to | depends_on | part_of", "description": "Brief metadata about this specific relation" } { "source": "role-id", "target": "process-id", "type": "executes | related_to | depends_on | part_of", "description": "Brief metadata about this specific relation" }