@memtensor/memos-local-openclaw-plugin 1.0.9 → 1.0.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 +4 -0
- package/index.ts +21 -4
- package/package.json +1 -1
- package/src/skill/generator.ts +18 -4
- package/src/viewer/html.ts +1 -0
- package/src/skill/ALGORITHMS.md +0 -141
- package/src/skill/CHANGELOG-DESIGN.md +0 -24
- package/src/skill/DESIGN.md +0 -72
- package/src/skill/experience-extractor.ts +0 -191
- package/src/skill/feedback-signals.ts +0 -181
- package/src/viewer/html-v2.ts +0 -1631
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { v4 as uuid } from "uuid";
|
|
2
|
-
import type { Chunk, FeedbackSignal, FeedbackType, FeedbackSource, PluginContext } from "../types";
|
|
3
|
-
import { buildSkillConfigChain, callLLMWithFallback } from "../shared/llm-call";
|
|
4
|
-
|
|
5
|
-
const FEEDBACK_EXTRACTION_PROMPT = `You are a feedback signal extraction expert. Analyze the following multi-turn conversation and identify feedback signals.
|
|
6
|
-
|
|
7
|
-
A feedback signal is any indication from the user, execution results, or the assistant's own checks about the quality/direction of the current approach.
|
|
8
|
-
|
|
9
|
-
Signal types:
|
|
10
|
-
- "reject": User explicitly or implicitly rejects the current approach/output
|
|
11
|
-
- "correction": User provides a correction to the agent's work
|
|
12
|
-
- "constraint": User adds new constraints or requirements not previously mentioned
|
|
13
|
-
- "success": User confirms the solution works or expresses satisfaction
|
|
14
|
-
- "confusion": User expresses confusion or asks for clarification about agent's approach
|
|
15
|
-
- "preference": User states a preference (tool choice, style, methodology)
|
|
16
|
-
|
|
17
|
-
Signal sources:
|
|
18
|
-
- "user": Signal comes from user messages
|
|
19
|
-
- "execution": Signal comes from error outputs, test failures, runtime errors
|
|
20
|
-
- "assistant_self_check": Assistant self-identifies issues in its own output
|
|
21
|
-
|
|
22
|
-
Rules:
|
|
23
|
-
- Look at semantic context, not just keywords. A user restating their goal may be an implicit "reject" of the current direction.
|
|
24
|
-
- Each signal must include the exact evidence text (verbatim quote or close paraphrase).
|
|
25
|
-
- Prioritize implicit signals: user NOT saying "wrong" but demonstrating dissatisfaction through rephrasing goals.
|
|
26
|
-
- One message can produce multiple signals.
|
|
27
|
-
- Only extract signals with confidence >= 0.5.
|
|
28
|
-
|
|
29
|
-
Conversation:
|
|
30
|
-
{CONVERSATION}
|
|
31
|
-
|
|
32
|
-
Reply with a JSON array only, no extra text:
|
|
33
|
-
[
|
|
34
|
-
{
|
|
35
|
-
"type": "reject|correction|constraint|success|confusion|preference",
|
|
36
|
-
"source": "user|execution|assistant_self_check",
|
|
37
|
-
"evidence": "exact quote or close paraphrase from conversation",
|
|
38
|
-
"turnId": "the turn ID where this signal appears",
|
|
39
|
-
"confidence": 0.0-1.0
|
|
40
|
-
}
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
If no meaningful signals found, return: []`;
|
|
44
|
-
|
|
45
|
-
const VALID_FEEDBACK_TYPES = new Set<FeedbackType>([
|
|
46
|
-
"reject", "correction", "constraint", "success", "confusion", "preference",
|
|
47
|
-
]);
|
|
48
|
-
|
|
49
|
-
const VALID_FEEDBACK_SOURCES = new Set<FeedbackSource>([
|
|
50
|
-
"user", "execution", "assistant_self_check",
|
|
51
|
-
]);
|
|
52
|
-
|
|
53
|
-
interface RawSignal {
|
|
54
|
-
type: string;
|
|
55
|
-
source: string;
|
|
56
|
-
evidence: string;
|
|
57
|
-
turnId: string;
|
|
58
|
-
confidence: number;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export class FeedbackSignalExtractor {
|
|
62
|
-
constructor(private ctx: PluginContext) {}
|
|
63
|
-
|
|
64
|
-
async extract(taskId: string, chunks: Chunk[]): Promise<FeedbackSignal[]> {
|
|
65
|
-
const chain = buildSkillConfigChain(this.ctx);
|
|
66
|
-
if (chain.length === 0) {
|
|
67
|
-
this.ctx.log.warn("FeedbackSignalExtractor: no LLM config, skipping");
|
|
68
|
-
return [];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const conversationText = this.buildConversation(chunks);
|
|
72
|
-
if (conversationText.length < 100) return [];
|
|
73
|
-
|
|
74
|
-
const prompt = FEEDBACK_EXTRACTION_PROMPT.replace(
|
|
75
|
-
"{CONVERSATION}",
|
|
76
|
-
conversationText.slice(0, 15_000),
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
const raw = await callLLMWithFallback(
|
|
81
|
-
chain,
|
|
82
|
-
prompt,
|
|
83
|
-
this.ctx.log,
|
|
84
|
-
"FeedbackSignalExtractor",
|
|
85
|
-
{ maxTokens: 2000, temperature: 0.1, timeoutMs: 60_000, openclawAPI: this.ctx.openclawAPI },
|
|
86
|
-
);
|
|
87
|
-
return this.parseSignals(raw, taskId);
|
|
88
|
-
} catch (err) {
|
|
89
|
-
this.ctx.log.warn(`FeedbackSignalExtractor failed: ${err}`);
|
|
90
|
-
return [];
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** Lightweight extraction for live interaction signals (used during recall, not full extraction). */
|
|
95
|
-
async extractLiveSignals(recentChunks: Chunk[]): Promise<string[]> {
|
|
96
|
-
const chain = buildSkillConfigChain(this.ctx);
|
|
97
|
-
if (chain.length === 0) return [];
|
|
98
|
-
|
|
99
|
-
const text = recentChunks
|
|
100
|
-
.filter((c) => c.role === "user" || c.role === "assistant")
|
|
101
|
-
.slice(-8)
|
|
102
|
-
.map((c) => `[${c.role}]: ${c.content.slice(0, 500)}`)
|
|
103
|
-
.join("\n");
|
|
104
|
-
|
|
105
|
-
if (text.length < 50) return [];
|
|
106
|
-
|
|
107
|
-
const prompt = `Analyze the recent conversation turns below and identify which interaction signals are present. Output ONLY the signal names that are present from this list:
|
|
108
|
-
- userDissatisfaction
|
|
109
|
-
- constraintRestatement
|
|
110
|
-
- repeatedFailure
|
|
111
|
-
- acceptanceUnclear
|
|
112
|
-
- sameErrorRepeated
|
|
113
|
-
|
|
114
|
-
Conversation:
|
|
115
|
-
${text.slice(0, 4000)}
|
|
116
|
-
|
|
117
|
-
Reply with a JSON array of signal names only: ["signal1", "signal2"]
|
|
118
|
-
If none present, reply: []`;
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
const raw = await callLLMWithFallback(
|
|
122
|
-
chain,
|
|
123
|
-
prompt,
|
|
124
|
-
this.ctx.log,
|
|
125
|
-
"LiveSignalExtractor",
|
|
126
|
-
{ maxTokens: 200, temperature: 0, timeoutMs: 15_000, openclawAPI: this.ctx.openclawAPI },
|
|
127
|
-
);
|
|
128
|
-
const match = raw.match(/\[[\s\S]*\]/);
|
|
129
|
-
if (!match) return [];
|
|
130
|
-
return JSON.parse(match[0]).filter((s: unknown) => typeof s === "string");
|
|
131
|
-
} catch {
|
|
132
|
-
return [];
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
private buildConversation(chunks: Chunk[]): string {
|
|
137
|
-
return chunks
|
|
138
|
-
.filter((c) => c.role !== "system")
|
|
139
|
-
.map((c) => {
|
|
140
|
-
const role =
|
|
141
|
-
c.role === "user" ? "User" :
|
|
142
|
-
c.role === "assistant" ? "Assistant" :
|
|
143
|
-
c.role === "tool" ? "Tool" :
|
|
144
|
-
c.role;
|
|
145
|
-
const content = c.role === "tool" ? c.content.slice(0, 800) : c.content;
|
|
146
|
-
return `[${role}] (turn=${c.turnId}): ${content}`;
|
|
147
|
-
})
|
|
148
|
-
.join("\n\n");
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
private parseSignals(raw: string, taskId: string): FeedbackSignal[] {
|
|
152
|
-
const match = raw.match(/\[[\s\S]*\]/);
|
|
153
|
-
if (!match) return [];
|
|
154
|
-
try {
|
|
155
|
-
const arr: unknown = JSON.parse(match[0]);
|
|
156
|
-
if (!Array.isArray(arr)) return [];
|
|
157
|
-
const now = Date.now();
|
|
158
|
-
return (arr as RawSignal[])
|
|
159
|
-
.filter(
|
|
160
|
-
(s) =>
|
|
161
|
-
s.confidence >= 0.5 &&
|
|
162
|
-
VALID_FEEDBACK_TYPES.has(s.type as FeedbackType) &&
|
|
163
|
-
s.evidence,
|
|
164
|
-
)
|
|
165
|
-
.map((s) => ({
|
|
166
|
-
id: uuid(),
|
|
167
|
-
taskId,
|
|
168
|
-
type: s.type as FeedbackType,
|
|
169
|
-
source: (VALID_FEEDBACK_SOURCES.has(s.source as FeedbackSource)
|
|
170
|
-
? s.source
|
|
171
|
-
: "user") as FeedbackSource,
|
|
172
|
-
evidence: s.evidence,
|
|
173
|
-
turnId: s.turnId || "",
|
|
174
|
-
confidence: s.confidence,
|
|
175
|
-
createdAt: now,
|
|
176
|
-
}));
|
|
177
|
-
} catch {
|
|
178
|
-
return [];
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|