Unified Agent + reusable Go agent core.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Refactor memory prompts using template-based rendering

Lyric 0866ebd7 21f805a0

+376 -106
+29 -101
cmd/mistermorph/telegramcmd/command.go
··· 1941 1941 return memory.SessionDraft{}, fmt.Errorf("nil llm client") 1942 1942 } 1943 1943 1944 - payload := map[string]any{ 1945 - "session_context": ctxInfo, 1946 - "conversation": buildMemoryContextMessages(history, task, output), 1947 - "existing_tasks": existing.Tasks, 1948 - "existing_follow_ups": existing.FollowUps, 1949 - "rules": []string{ 1950 - "Short-term memory is public. Do NOT include private or sensitive info in summary/session_summary/temporary_facts/tasks/follow_ups.", 1951 - "Use the same language as the user for titles and values; keep Session Summary keys labeled as Users/Datetime/Event/Result.", 1952 - "Session summary items must be single-topic. Set title to the topic name.", 1953 - "Session summary value must be newline-separated key-value lines: Users: ..., Datetime: ..., Event: ..., Result: ... (omit unknowns).", 1954 - "If session_context includes counterparty info, use it instead of generic labels like \"user\".", 1955 - "Temporary facts title is the fact group name; value is newline-separated key-value lines (e.g., 网站 URL: ..., API 示例: ...).", 1956 - "Temporary facts should preserve key metadata such as URLs, terms, identifiers, IDs, or ticket numbers when they matter to future work.", 1957 - "Keep items concise but specific.", 1958 - "If an existing task or follow-up was completed in this session, include it with done=true.", 1959 - "Prefer to reuse the wording from existing_tasks when updating status.", 1960 - "If session_context.counterparty_label is present, use that exact label in tasks/follow_ups instead of generic words like user/用户/对方.", 1961 - "Long-term promotion must be extremely strict: only include ONE precious, long-lived item at most, and only if the user explicitly asked to remember it.", 1962 - "Do NOT promote short-term tasks, one-off details, or time-bound items.", 1963 - "If unsure, leave the field empty.", 1964 - }, 1944 + conversation := buildMemoryContextMessages(history, task, output) 1945 + sys, user, err := renderMemoryDraftPrompts(ctxInfo, conversation, existing.Tasks, existing.FollowUps) 1946 + if err != nil { 1947 + return memory.SessionDraft{}, fmt.Errorf("render memory draft prompts: %w", err) 1965 1948 } 1966 - b, _ := json.Marshal(payload) 1967 - 1968 - sys := "You summarize a single agent session into a markdown-based memory draft. " + 1969 - "Use session_context for who/when details. " + 1970 - "Return ONLY a JSON object with keys: " + 1971 - "summary (string), " + 1972 - "session_summary (array of {title, value}), " + 1973 - "temporary_facts (array of {title, value}), " + 1974 - "tasks (array of {text, done}), " + 1975 - "follow_ups (array of {text, done}), " + 1976 - "promote (object with goals_projects and key_facts arrays of {title, value}). " + 1977 - "If nothing applies, use empty arrays and empty strings. " + 1978 - "Promote only stable, high-signal items." 1979 1949 1980 1950 res, err := client.Chat(ctx, llm.Request{ 1981 1951 Model: model, 1982 1952 ForceJSON: true, 1983 1953 Messages: []llm.Message{ 1984 1954 {Role: "system", Content: sys}, 1985 - {Role: "user", Content: string(b)}, 1955 + {Role: "user", Content: user}, 1986 1956 }, 1987 1957 Parameters: map[string]any{ 1988 1958 "max_tokens": 10240, ··· 2089 2059 return memory.KVItem{}, false 2090 2060 } 2091 2061 2092 - type semanticMergeInput struct { 2093 - Existing semanticMergeContent `json:"existing"` 2094 - Incoming semanticMergeContent `json:"incoming"` 2095 - Rules []string `json:"rules"` 2096 - } 2097 - 2098 2062 type semanticMergeContent struct { 2099 2063 SessionSummary []memory.KVItem `json:"session_summary"` 2100 2064 TemporaryFacts []memory.KVItem `json:"temporary_facts"` ··· 2115 2079 return memory.ShortTermContent{}, "", fmt.Errorf("nil llm client") 2116 2080 } 2117 2081 2118 - input := semanticMergeInput{ 2119 - Existing: semanticMergeContent{ 2120 - SessionSummary: existing.SessionSummary, 2121 - TemporaryFacts: existing.TemporaryFacts, 2122 - Tasks: existing.Tasks, 2123 - FollowUps: existing.FollowUps, 2124 - }, 2125 - Incoming: semanticMergeContent{ 2126 - SessionSummary: draft.SessionSummary, 2127 - TemporaryFacts: draft.TemporaryFacts, 2128 - Tasks: draft.Tasks, 2129 - FollowUps: draft.FollowUps, 2130 - }, 2131 - Rules: []string{ 2132 - "These are same-day short-term items. Merge semantically and deduplicate.", 2133 - "Short-term memory is public. Do NOT include private or sensitive info.", 2134 - "Session summary title is the topic name; value is newline-separated key-value lines (Users/Datetime/Event/Result).", 2135 - "Temporary facts title is the fact group name; value is newline-separated key-value lines.", 2136 - "Merge items with overlapping meaning even if titles differ. You may rename titles to unify duplicates.", 2137 - "Prefer a single canonical title when multiple entries describe the same topic.", 2138 - "Preserve all concrete details when merging; do not lose facts from incoming or existing items.", 2139 - "Merge overlapping link lists into one list (dedupe URLs).", 2140 - "Within a Session Summary value, each key (Users/Datetime/Event/Result) must appear at most once.", 2141 - "Within a Temporary Facts value, do not repeat the same key or URL.", 2142 - "Prefer the most recent information when conflicts occur.", 2143 - "Preserve important metadata in temporary_facts such as URLs, terms, identifiers, IDs, or ticket numbers.", 2144 - "Keep items concise.", 2145 - "Tasks/Follow-ups: merge duplicates even if wording differs; keep one canonical item.", 2146 - "If any duplicate is done=true, the merged item must be done=true.", 2147 - "If unsure, keep the existing item and add the new one only if distinct.", 2148 - }, 2082 + existingContent := semanticMergeContent{ 2083 + SessionSummary: existing.SessionSummary, 2084 + TemporaryFacts: existing.TemporaryFacts, 2085 + Tasks: existing.Tasks, 2086 + FollowUps: existing.FollowUps, 2087 + } 2088 + incomingContent := semanticMergeContent{ 2089 + SessionSummary: draft.SessionSummary, 2090 + TemporaryFacts: draft.TemporaryFacts, 2091 + Tasks: draft.Tasks, 2092 + FollowUps: draft.FollowUps, 2093 + } 2094 + sys, user, err := renderMemoryMergePrompts(existingContent, incomingContent) 2095 + if err != nil { 2096 + return memory.ShortTermContent{}, "", fmt.Errorf("render semantic merge prompts: %w", err) 2149 2097 } 2150 - payload, _ := json.Marshal(input) 2151 - 2152 - sys := "You merge short-term memory entries for the same day. " + 2153 - "Return ONLY a JSON object with keys: summary, session_summary, temporary_facts, tasks, follow_ups. " + 2154 - "Each list item must keep the same shape as input. " + 2155 - "Summary must reflect the merged content and be one short sentence." 2156 2098 2157 2099 res, err := client.Chat(ctx, llm.Request{ 2158 2100 Model: model, 2159 2101 ForceJSON: true, 2160 2102 Messages: []llm.Message{ 2161 2103 {Role: "system", Content: sys}, 2162 - {Role: "user", Content: string(payload)}, 2104 + {Role: "user", Content: user}, 2163 2105 }, 2164 2106 Parameters: map[string]any{ 2165 2107 "max_tokens": 40960, ··· 2317 2259 if client == nil || len(base) == 0 || len(updates) == 0 { 2318 2260 return nil 2319 2261 } 2320 - payload := map[string]any{ 2321 - "existing": base, 2322 - "updates": updates, 2323 - "rules": []string{ 2324 - "Match updates to existing tasks if they refer to the same task even with different wording.", 2325 - "Return match_index = -1 if no existing task matches.", 2326 - "Each update should map to at most one existing task.", 2327 - }, 2262 + sys, user, err := renderMemoryTaskMatchPrompts(base, updates) 2263 + if err != nil { 2264 + return nil 2328 2265 } 2329 - b, _ := json.Marshal(payload) 2330 - sys := "You match task updates to existing tasks. Return ONLY JSON: {\"matches\":[{\"update_index\":int,\"match_index\":int}]}." 2331 2266 res, err := client.Chat(ctx, llm.Request{ 2332 2267 Model: model, 2333 2268 ForceJSON: true, 2334 2269 Messages: []llm.Message{ 2335 2270 {Role: "system", Content: sys}, 2336 - {Role: "user", Content: string(b)}, 2271 + {Role: "user", Content: user}, 2337 2272 }, 2338 2273 Parameters: map[string]any{ 2339 2274 "max_tokens": 40960, ··· 2357 2292 if client == nil || len(items) == 0 { 2358 2293 return items 2359 2294 } 2360 - payload := map[string]any{ 2361 - "tasks": items, 2362 - "rules": []string{ 2363 - "Merge duplicate tasks even if wording differs.", 2364 - "Keep one canonical task per meaning.", 2365 - "If any duplicate is done=true, the merged task must be done=true.", 2366 - "Do not invent new tasks.", 2367 - }, 2295 + sys, user, err := renderMemoryTaskDedupPrompts(items) 2296 + if err != nil { 2297 + return items 2368 2298 } 2369 - b, _ := json.Marshal(payload) 2370 - sys := "You deduplicate tasks semantically. Return ONLY JSON: {\"tasks\":[{\"text\":string,\"done\":bool}]}." 2371 2299 res, err := client.Chat(ctx, llm.Request{ 2372 2300 Model: model, 2373 2301 ForceJSON: true, 2374 2302 Messages: []llm.Message{ 2375 2303 {Role: "system", Content: sys}, 2376 - {Role: "user", Content: string(b)}, 2304 + {Role: "user", Content: user}, 2377 2305 }, 2378 2306 Parameters: map[string]any{ 2379 2307 "max_tokens": 1200,
+140
cmd/mistermorph/telegramcmd/memory_prompts.go
··· 1 + package telegramcmd 2 + 3 + import ( 4 + _ "embed" 5 + "encoding/json" 6 + "text/template" 7 + 8 + "github.com/quailyquaily/mistermorph/internal/prompttmpl" 9 + "github.com/quailyquaily/mistermorph/memory" 10 + ) 11 + 12 + //go:embed prompts/memory_draft_system.tmpl 13 + var memoryDraftSystemPromptTemplateSource string 14 + 15 + //go:embed prompts/memory_draft_user.tmpl 16 + var memoryDraftUserPromptTemplateSource string 17 + 18 + //go:embed prompts/memory_merge_system.tmpl 19 + var memoryMergeSystemPromptTemplateSource string 20 + 21 + //go:embed prompts/memory_merge_user.tmpl 22 + var memoryMergeUserPromptTemplateSource string 23 + 24 + //go:embed prompts/memory_task_match_system.tmpl 25 + var memoryTaskMatchSystemPromptTemplateSource string 26 + 27 + //go:embed prompts/memory_task_match_user.tmpl 28 + var memoryTaskMatchUserPromptTemplateSource string 29 + 30 + //go:embed prompts/memory_task_dedup_system.tmpl 31 + var memoryTaskDedupSystemPromptTemplateSource string 32 + 33 + //go:embed prompts/memory_task_dedup_user.tmpl 34 + var memoryTaskDedupUserPromptTemplateSource string 35 + 36 + var memoryPromptTemplateFuncs = template.FuncMap{ 37 + "toJSON": func(v any) (string, error) { 38 + b, err := json.Marshal(v) 39 + if err != nil { 40 + return "", err 41 + } 42 + return string(b), nil 43 + }, 44 + } 45 + 46 + var memoryDraftSystemPromptTemplate = prompttmpl.MustParse("telegram_memory_draft_system", memoryDraftSystemPromptTemplateSource, nil) 47 + var memoryDraftUserPromptTemplate = prompttmpl.MustParse("telegram_memory_draft_user", memoryDraftUserPromptTemplateSource, memoryPromptTemplateFuncs) 48 + var memoryMergeSystemPromptTemplate = prompttmpl.MustParse("telegram_memory_merge_system", memoryMergeSystemPromptTemplateSource, nil) 49 + var memoryMergeUserPromptTemplate = prompttmpl.MustParse("telegram_memory_merge_user", memoryMergeUserPromptTemplateSource, memoryPromptTemplateFuncs) 50 + var memoryTaskMatchSystemPromptTemplate = prompttmpl.MustParse("telegram_memory_task_match_system", memoryTaskMatchSystemPromptTemplateSource, nil) 51 + var memoryTaskMatchUserPromptTemplate = prompttmpl.MustParse("telegram_memory_task_match_user", memoryTaskMatchUserPromptTemplateSource, memoryPromptTemplateFuncs) 52 + var memoryTaskDedupSystemPromptTemplate = prompttmpl.MustParse("telegram_memory_task_dedup_system", memoryTaskDedupSystemPromptTemplateSource, nil) 53 + var memoryTaskDedupUserPromptTemplate = prompttmpl.MustParse("telegram_memory_task_dedup_user", memoryTaskDedupUserPromptTemplateSource, memoryPromptTemplateFuncs) 54 + 55 + type memoryDraftUserPromptData struct { 56 + SessionContext MemoryDraftContext 57 + Conversation []map[string]string 58 + ExistingTasks []memory.TaskItem 59 + ExistingFollowUps []memory.TaskItem 60 + } 61 + 62 + func renderMemoryDraftPrompts( 63 + ctxInfo MemoryDraftContext, 64 + conversation []map[string]string, 65 + existingTasks []memory.TaskItem, 66 + existingFollowUps []memory.TaskItem, 67 + ) (string, string, error) { 68 + systemPrompt, err := prompttmpl.Render(memoryDraftSystemPromptTemplate, struct{}{}) 69 + if err != nil { 70 + return "", "", err 71 + } 72 + userPrompt, err := prompttmpl.Render(memoryDraftUserPromptTemplate, memoryDraftUserPromptData{ 73 + SessionContext: ctxInfo, 74 + Conversation: conversation, 75 + ExistingTasks: existingTasks, 76 + ExistingFollowUps: existingFollowUps, 77 + }) 78 + if err != nil { 79 + return "", "", err 80 + } 81 + return systemPrompt, userPrompt, nil 82 + } 83 + 84 + type memoryMergeUserPromptData struct { 85 + Existing semanticMergeContent 86 + Incoming semanticMergeContent 87 + } 88 + 89 + func renderMemoryMergePrompts(existing semanticMergeContent, incoming semanticMergeContent) (string, string, error) { 90 + systemPrompt, err := prompttmpl.Render(memoryMergeSystemPromptTemplate, struct{}{}) 91 + if err != nil { 92 + return "", "", err 93 + } 94 + userPrompt, err := prompttmpl.Render(memoryMergeUserPromptTemplate, memoryMergeUserPromptData{ 95 + Existing: existing, 96 + Incoming: incoming, 97 + }) 98 + if err != nil { 99 + return "", "", err 100 + } 101 + return systemPrompt, userPrompt, nil 102 + } 103 + 104 + type memoryTaskMatchUserPromptData struct { 105 + Existing []memory.TaskItem 106 + Updates []memory.TaskItem 107 + } 108 + 109 + func renderMemoryTaskMatchPrompts(existing []memory.TaskItem, updates []memory.TaskItem) (string, string, error) { 110 + systemPrompt, err := prompttmpl.Render(memoryTaskMatchSystemPromptTemplate, struct{}{}) 111 + if err != nil { 112 + return "", "", err 113 + } 114 + userPrompt, err := prompttmpl.Render(memoryTaskMatchUserPromptTemplate, memoryTaskMatchUserPromptData{ 115 + Existing: existing, 116 + Updates: updates, 117 + }) 118 + if err != nil { 119 + return "", "", err 120 + } 121 + return systemPrompt, userPrompt, nil 122 + } 123 + 124 + type memoryTaskDedupUserPromptData struct { 125 + Tasks []memory.TaskItem 126 + } 127 + 128 + func renderMemoryTaskDedupPrompts(tasks []memory.TaskItem) (string, string, error) { 129 + systemPrompt, err := prompttmpl.Render(memoryTaskDedupSystemPromptTemplate, struct{}{}) 130 + if err != nil { 131 + return "", "", err 132 + } 133 + userPrompt, err := prompttmpl.Render(memoryTaskDedupUserPromptTemplate, memoryTaskDedupUserPromptData{ 134 + Tasks: tasks, 135 + }) 136 + if err != nil { 137 + return "", "", err 138 + } 139 + return systemPrompt, userPrompt, nil 140 + }
+91
cmd/mistermorph/telegramcmd/memory_prompts_test.go
··· 1 + package telegramcmd 2 + 3 + import ( 4 + "encoding/json" 5 + "strings" 6 + "testing" 7 + 8 + "github.com/quailyquaily/mistermorph/memory" 9 + ) 10 + 11 + func TestRenderMemoryDraftPrompts(t *testing.T) { 12 + sys, user, err := renderMemoryDraftPrompts( 13 + MemoryDraftContext{SessionID: "telegram:1", ChatType: "private"}, 14 + []map[string]string{{"role": "user", "content": "hi"}}, 15 + []memory.TaskItem{{Text: "t1", Done: false}}, 16 + []memory.TaskItem{{Text: "f1", Done: false}}, 17 + ) 18 + if err != nil { 19 + t.Fatalf("renderMemoryDraftPrompts() error = %v", err) 20 + } 21 + if !strings.Contains(sys, "single agent session") { 22 + t.Fatalf("unexpected system prompt: %q", sys) 23 + } 24 + var payload map[string]any 25 + if err := json.Unmarshal([]byte(user), &payload); err != nil { 26 + t.Fatalf("user prompt is not valid json: %v", err) 27 + } 28 + if payload["session_context"] == nil { 29 + t.Fatalf("missing session_context") 30 + } 31 + if payload["rules"] == nil { 32 + t.Fatalf("missing rules") 33 + } 34 + } 35 + 36 + func TestRenderMemoryMergePrompts(t *testing.T) { 37 + sys, user, err := renderMemoryMergePrompts( 38 + semanticMergeContent{Tasks: []memory.TaskItem{{Text: "a"}}}, 39 + semanticMergeContent{Tasks: []memory.TaskItem{{Text: "b"}}}, 40 + ) 41 + if err != nil { 42 + t.Fatalf("renderMemoryMergePrompts() error = %v", err) 43 + } 44 + if !strings.Contains(sys, "merge short-term memory entries") { 45 + t.Fatalf("unexpected system prompt: %q", sys) 46 + } 47 + var payload map[string]any 48 + if err := json.Unmarshal([]byte(user), &payload); err != nil { 49 + t.Fatalf("user prompt is not valid json: %v", err) 50 + } 51 + if payload["existing"] == nil || payload["incoming"] == nil { 52 + t.Fatalf("missing existing/incoming payload") 53 + } 54 + } 55 + 56 + func TestRenderMemoryTaskMatchPrompts(t *testing.T) { 57 + sys, user, err := renderMemoryTaskMatchPrompts( 58 + []memory.TaskItem{{Text: "existing"}}, 59 + []memory.TaskItem{{Text: "update"}}, 60 + ) 61 + if err != nil { 62 + t.Fatalf("renderMemoryTaskMatchPrompts() error = %v", err) 63 + } 64 + if !strings.Contains(sys, "match task updates") { 65 + t.Fatalf("unexpected system prompt: %q", sys) 66 + } 67 + var payload map[string]any 68 + if err := json.Unmarshal([]byte(user), &payload); err != nil { 69 + t.Fatalf("user prompt is not valid json: %v", err) 70 + } 71 + if payload["existing"] == nil || payload["updates"] == nil { 72 + t.Fatalf("missing existing/updates payload") 73 + } 74 + } 75 + 76 + func TestRenderMemoryTaskDedupPrompts(t *testing.T) { 77 + sys, user, err := renderMemoryTaskDedupPrompts([]memory.TaskItem{{Text: "task", Done: false}}) 78 + if err != nil { 79 + t.Fatalf("renderMemoryTaskDedupPrompts() error = %v", err) 80 + } 81 + if !strings.Contains(sys, "deduplicate tasks semantically") { 82 + t.Fatalf("unexpected system prompt: %q", sys) 83 + } 84 + var payload map[string]any 85 + if err := json.Unmarshal([]byte(user), &payload); err != nil { 86 + t.Fatalf("user prompt is not valid json: %v", err) 87 + } 88 + if payload["tasks"] == nil || payload["rules"] == nil { 89 + t.Fatalf("missing tasks/rules payload") 90 + } 91 + }
+11
cmd/mistermorph/telegramcmd/prompts/memory_draft_system.tmpl
··· 1 + You summarize a single agent session into a markdown-based memory draft. 2 + Use session_context for who/when details. 3 + Return ONLY a JSON object with keys: 4 + summary (string), 5 + session_summary (array of {title, value}), 6 + temporary_facts (array of {title, value}), 7 + tasks (array of {text, done}), 8 + follow_ups (array of {text, done}), 9 + promote (object with goals_projects and key_facts arrays of {title, value}). 10 + If nothing applies, use empty arrays and empty strings. 11 + Promote only stable, high-signal items.
+22
cmd/mistermorph/telegramcmd/prompts/memory_draft_user.tmpl
··· 1 + { 2 + "session_context": {{toJSON .SessionContext}}, 3 + "conversation": {{toJSON .Conversation}}, 4 + "existing_tasks": {{toJSON .ExistingTasks}}, 5 + "existing_follow_ups": {{toJSON .ExistingFollowUps}}, 6 + "rules": [ 7 + "Short-term memory is public. Do NOT include private or sensitive info in summary/session_summary/temporary_facts/tasks/follow_ups.", 8 + "Use the same language as the user for titles and values; keep Session Summary keys labeled as Users/Datetime/Event/Result.", 9 + "Session summary items must be single-topic. Set title to the topic name.", 10 + "Session summary value must be newline-separated key-value lines: Users: ..., Datetime: ..., Event: ..., Result: ... (omit unknowns).", 11 + "If session_context includes counterparty info, use it instead of generic labels like \"user\".", 12 + "Temporary facts title is the fact group name; value is newline-separated key-value lines (e.g., 网站 URL: ..., API 示例: ...).", 13 + "Temporary facts should preserve key metadata such as URLs, terms, identifiers, IDs, or ticket numbers when they matter to future work.", 14 + "Keep items concise but specific.", 15 + "If an existing task or follow-up was completed in this session, include it with done=true.", 16 + "Prefer to reuse the wording from existing_tasks when updating status.", 17 + "If session_context.counterparty_label is present, use that exact label in tasks/follow_ups instead of generic words like user/用户/对方.", 18 + "Long-term promotion must be extremely strict: only include ONE precious, long-lived item at most, and only if the user explicitly asked to remember it.", 19 + "Do NOT promote short-term tasks, one-off details, or time-bound items.", 20 + "If unsure, leave the field empty." 21 + ] 22 + }
+4
cmd/mistermorph/telegramcmd/prompts/memory_merge_system.tmpl
··· 1 + You merge short-term memory entries for the same day. 2 + Return ONLY a JSON object with keys: summary, session_summary, temporary_facts, tasks, follow_ups. 3 + Each list item must keep the same shape as input. 4 + Summary must reflect the merged content and be one short sentence.
+22
cmd/mistermorph/telegramcmd/prompts/memory_merge_user.tmpl
··· 1 + { 2 + "existing": {{toJSON .Existing}}, 3 + "incoming": {{toJSON .Incoming}}, 4 + "rules": [ 5 + "These are same-day short-term items. Merge semantically and deduplicate.", 6 + "Short-term memory is public. Do NOT include private or sensitive info.", 7 + "Session summary title is the topic name; value is newline-separated key-value lines (Users/Datetime/Event/Result).", 8 + "Temporary facts title is the fact group name; value is newline-separated key-value lines.", 9 + "Merge items with overlapping meaning even if titles differ. You may rename titles to unify duplicates.", 10 + "Prefer a single canonical title when multiple entries describe the same topic.", 11 + "Preserve all concrete details when merging; do not lose facts from incoming or existing items.", 12 + "Merge overlapping link lists into one list (dedupe URLs).", 13 + "Within a Session Summary value, each key (Users/Datetime/Event/Result) must appear at most once.", 14 + "Within a Temporary Facts value, do not repeat the same key or URL.", 15 + "Prefer the most recent information when conflicts occur.", 16 + "Preserve important metadata in temporary_facts such as URLs, terms, identifiers, IDs, or ticket numbers.", 17 + "Keep items concise.", 18 + "Tasks/Follow-ups: merge duplicates even if wording differs; keep one canonical item.", 19 + "If any duplicate is done=true, the merged item must be done=true.", 20 + "If unsure, keep the existing item and add the new one only if distinct." 21 + ] 22 + }
+1
cmd/mistermorph/telegramcmd/prompts/memory_task_dedup_system.tmpl
··· 1 + You deduplicate tasks semantically. Return ONLY JSON: {"tasks":[{"text":string,"done":bool}]}.
+9
cmd/mistermorph/telegramcmd/prompts/memory_task_dedup_user.tmpl
··· 1 + { 2 + "tasks": {{toJSON .Tasks}}, 3 + "rules": [ 4 + "Merge duplicate tasks even if wording differs.", 5 + "Keep one canonical task per meaning.", 6 + "If any duplicate is done=true, the merged task must be done=true.", 7 + "Do not invent new tasks." 8 + ] 9 + }
+1
cmd/mistermorph/telegramcmd/prompts/memory_task_match_system.tmpl
··· 1 + You match task updates to existing tasks. Return ONLY JSON: {"matches":[{"update_index":int,"match_index":int}]}.
+9
cmd/mistermorph/telegramcmd/prompts/memory_task_match_user.tmpl
··· 1 + { 2 + "existing": {{toJSON .Existing}}, 3 + "updates": {{toJSON .Updates}}, 4 + "rules": [ 5 + "Match updates to existing tasks if they refer to the same task even with different wording.", 6 + "Return match_index = -1 if no existing task matches.", 7 + "Each update should map to at most one existing task." 8 + ] 9 + }
+37 -5
docs/prompt.md
··· 87 87 88 88 These are prompts sent through separate `llm.Request` calls outside the main tool-using turn loop. 89 89 90 + ## Template Index (Per File) 91 + 92 + | Template | Role | Purpose | 93 + |---|---|---| 94 + | `agent/prompts/system.tmpl` | system | Renders the main system prompt (Identity, Blocks, Tools, response format, Rules). | 95 + | `agent/prompts/intent_system.tmpl` | system | Constrains intent inference output schema (JSON contract). | 96 + | `agent/prompts/intent_user.tmpl` | user | Carries `task/history` plus built-in intent inference rules. | 97 + | `telegramcmd/prompts/memory_draft_system.tmpl` | system | Defines the output contract for single-session memory draft generation. | 98 + | `telegramcmd/prompts/memory_draft_user.tmpl` | user | Carries session context, dialogue snippets, existing tasks/follow-ups, and summarization rules. | 99 + | `telegramcmd/prompts/memory_merge_system.tmpl` | system | Defines the output contract for same-day short-term memory merge. | 100 + | `telegramcmd/prompts/memory_merge_user.tmpl` | user | Carries existing/incoming memory content and merge rules. | 101 + | `telegramcmd/prompts/memory_task_match_system.tmpl` | system | Defines the output contract for task mapping (`update_index/match_index`). | 102 + | `telegramcmd/prompts/memory_task_match_user.tmpl` | user | Carries existing tasks, updates, and matching rules. | 103 + | `telegramcmd/prompts/memory_task_dedup_system.tmpl` | system | Defines the output contract for semantic task deduplication. | 104 + | `telegramcmd/prompts/memory_task_dedup_user.tmpl` | user | Carries tasks and deduplication rules. | 105 + 90 106 ### 1) Intent inference 91 107 92 108 - File/Function: `agent/intent.go` / `InferIntent(...)` ··· 95 111 - `agent/prompts/intent_user.tmpl` 96 112 - Renderer: `agent/intent_template.go` (via `internal/prompttmpl`) 97 113 - Purpose: infer structured user intent and ambiguity level 98 - - Primary input: current `task`, trimmed recent `history`(规则由模板内置) 114 + - Primary input: current `task`, trimmed recent `history` (rules are embedded in the template) 99 115 - Output: `Intent{goal, deliverable, constraints, ambiguities, ask}` 100 116 - JSON required: **Yes** (`ForceJSON=true`) 101 117 ··· 182 198 ### 12) Telegram memory draft generation 183 199 184 200 - File/Function: `cmd/mistermorph/telegramcmd/command.go` / `BuildMemoryDraft(...)` 201 + - Templates: 202 + - `cmd/mistermorph/telegramcmd/prompts/memory_draft_system.tmpl` 203 + - `cmd/mistermorph/telegramcmd/prompts/memory_draft_user.tmpl` 204 + - Renderer: `cmd/mistermorph/telegramcmd/memory_prompts.go` 185 205 - Purpose: convert one session into structured short-term memory draft 186 - - Primary input: session context, conversation, existing tasks/follow-ups, memory rules 206 + - Primary input: session context, conversation, existing tasks/follow-ups 187 207 - Output: `memory.SessionDraft` 188 208 - JSON required: **Yes** (`ForceJSON=true`) 189 209 190 210 ### 13) Telegram semantic merge for short-term memory 191 211 192 212 - File/Function: `cmd/mistermorph/telegramcmd/command.go` / `SemanticMergeShortTerm(...)` 213 + - Templates: 214 + - `cmd/mistermorph/telegramcmd/prompts/memory_merge_system.tmpl` 215 + - `cmd/mistermorph/telegramcmd/prompts/memory_merge_user.tmpl` 216 + - Renderer: `cmd/mistermorph/telegramcmd/memory_prompts.go` 193 217 - Purpose: semantically merge same-day short-term memory 194 - - Primary input: existing content + incoming draft + merge rules 218 + - Primary input: existing content + incoming draft 195 219 - Output: merged `memory.ShortTermContent` + summary string 196 220 - JSON required: **Yes** (`ForceJSON=true`) 197 221 198 222 ### 14) Telegram semantic task matching 199 223 200 224 - File/Function: `cmd/mistermorph/telegramcmd/command.go` / `semanticMatchTasks(...)` 225 + - Templates: 226 + - `cmd/mistermorph/telegramcmd/prompts/memory_task_match_system.tmpl` 227 + - `cmd/mistermorph/telegramcmd/prompts/memory_task_match_user.tmpl` 228 + - Renderer: `cmd/mistermorph/telegramcmd/memory_prompts.go` 201 229 - Purpose: map incoming task updates onto existing tasks 202 - - Primary input: existing task list + update task list + matching rules 230 + - Primary input: existing task list + update task list 203 231 - Output: `[]taskMatch{update_index, match_index}` 204 232 - JSON required: **Yes** (`ForceJSON=true`) 205 233 206 234 ### 15) Telegram semantic task deduplication 207 235 208 236 - File/Function: `cmd/mistermorph/telegramcmd/command.go` / `semanticDedupTaskItems(...)` 237 + - Templates: 238 + - `cmd/mistermorph/telegramcmd/prompts/memory_task_dedup_system.tmpl` 239 + - `cmd/mistermorph/telegramcmd/prompts/memory_task_dedup_user.tmpl` 240 + - Renderer: `cmd/mistermorph/telegramcmd/memory_prompts.go` 209 241 - Purpose: deduplicate semantically equivalent task items 210 - - Primary input: task list + dedup rules 242 + - Primary input: task list 211 243 - Output: deduplicated `[]memory.TaskItem` 212 244 - JSON required: **Yes** (`ForceJSON=true`) 213 245