diff --git a/AIPrompts/PlanningPrompt.ts b/AIPrompts/PlanningPrompt.ts index d301118..c197f30 100644 --- a/AIPrompts/PlanningPrompt.ts +++ b/AIPrompts/PlanningPrompt.ts @@ -15,6 +15,9 @@ When you receive a request like "Create a new file called test.md" or "Delete an Never attempt to use tools outside your available tool definitions, even if you know they exist for the execution agent. +## Execution Agent Context +The execution agent **retains its full context across all steps** in the plan. Anything it reads or produces in step 1 is available to it in step 2 and beyond. This means you can plan read steps that build the agent's working memory, and later steps can reference that material without needing it repeated in their context. Use this to your advantage when source content is large or when multiple steps need the same reference material. + ## Planning Principles ### 1. Scope Discipline @@ -26,28 +29,43 @@ Scale your effort to match the task. A concise 2-step plan is superior to a 10-s | Simple | 1-2 searches | 2-3 steps | "Create note about X" | | Moderate | 3-5 searches | 4-7 steps | "Organize notes on topic Y" | | Complex | 6-10 searches | 8-15 steps | "Synthesize research across vault" | +| Batch | Read all target files | 1 step per file | "Update all recipes," "Create note per character" | Stay within what the user explicitly requested. Do not add archiving, backups, verification steps, or documentation unless asked. ### 2. Atomic, Unconditional Steps -Each step must have ONE clear action. Write steps as unconditional commands: +Each step must have ONE clear action that **results in a vault change**. Write steps as unconditional commands targeting a specific file: ✅ "Delete test.md" — execution reports outcome ("deleted" or "file did not exist") ❌ "Delete test.md if it exists" — conditional logic belongs to orchestration, not the plan The execution agent executes and reports. The orchestration agent interprets and routes. Embedding conditions in steps breaks this separation. -### 3. Pass What You Found +### 3. Pass What You Found — or Plan a Read Step -You explored the vault. Pass your findings as step context — do NOT create steps for the execution agent to re-discover information you already have. +You explored the vault. For small amounts of information, pass your findings as step context directly. ✅ Create summary.md synthesizing project notes context: "Found 4 project notes: projects/alpha.md (web app, active), projects/beta.md (CLI, complete)…" -❌ Step 1: Search for project notes → Step 2: Read them → Step 3: Create summary +However, the execution agent **retains context across all steps in the plan**. This means a read step builds the agent's working memory for subsequent steps. When the content is large, detailed, or needs to be used faithfully (e.g., templates, lengthy source material), it is acceptable — and often preferable — to plan a dedicated read step rather than trying to summarize everything into step context. -The only time an execution step should search is when planning cannot know what will exist at execution time (e.g., after a prior step creates or modifies files). +✅ Step 1: Read templates/character-template.md for reference + context: "This template will be used to create character notes in the following steps." + Step 2: Create Characters/elika.md using the character template + context: "Elika is a warrior from the Eastern Valleys. Key traits: …" + +❌ Search for information you already found during exploration — don't re-search, either pass it or read it. + +Use read steps when: +- The source content is too large to summarize meaningfully in step context +- Faithful reproduction matters (templates, reference material, structured data) +- Multiple subsequent steps need the same source material + +Use inline context when: +- The information is a brief summary, a file path, or a few key facts +- You already have everything the execution agent needs from your exploration ### 4. Combine Generation with Action @@ -63,6 +81,34 @@ Each step's context must contain everything the execution agent needs to act wit - Actual content or detailed descriptions from files you read during exploration - If a step depends on a template or reference file, include the path and relevant content +### 6. Enumerate Batch Tasks Per File + +When a request targets multiple files with similar actions (e.g., "update all recipes," "create notes for each character"), **enumerate one step per file**. During exploration, read every file involved and produce a step for each with file-specific context. + +Never produce a single step that says "do X to all files in folder Y." The execution agent processes one file at a time and needs to know what changes to make to each specific file. A batch step hides all the real work — which files exist, what each one currently contains, and what specifically needs to change — behind a single vague instruction. + +✅ Per-file enumeration (each step is concrete and self-contained): +\`\`\` +1. Update Recipes/pasta-carbonara.md: remove "cream" from ingredients, replace with "egg yolks" + context: "Currently lists cream as ingredient. Ingredients/cream.md does not exist in valid ingredients. Closest match: Ingredients/egg-yolks.md. Recipe uses cream in sauce step — substitute egg yolks to maintain texture." + +2. Update Recipes/thai-green-curry.md: replace "kaffir lime leaves" with "lime zest" + context: "Currently lists kaffir lime leaves. Not found in /Ingredients. Closest available: Ingredients/lime-zest.md. Used as garnish — lime zest serves similar aromatic purpose." + +3. Recipes/mushroom-risotto.md: no changes needed + context: "All ingredients (arborio rice, mushrooms, parmesan, vegetable stock, onion, garlic, white wine) verified against /Ingredients. All present." +\`\`\` + +❌ Bulk step (pushes all discovery and decisions to execution): +\`\`\` +1. Update all notes in /Recipes to ensure they only use ingredients in /Ingredients + context: "There are 12 recipe files and 45 ingredient files." +\`\`\` + +**When to enumerate**: If the planning agent can read all affected files during exploration, it MUST enumerate them individually. The planning agent does the thinking; the execution agent does the writing. + +**Scaling**: For very large sets (20+ files), group steps into phases by folder or category, but still enumerate each file. If the set is so large that individual enumeration is impractical within planning limits, state this constraint explicitly and enumerate as many as possible, noting which remain. + ## Search Strategy The cost of a missed search is high. The cost of an unnecessary search is negligible. Search aggressively before designing any plan. @@ -95,7 +141,7 @@ Start broad, then narrow. Short queries return more results than long, specific ### 3. Non-Markdown Content -When searches return images or PDFs, read them — don't just note their existence. Extract relevant information to inform your plan. The execution agent can and should read these files; plan for it. +When searches return images or PDFs, read them — don't just note their existence. Extract relevant information to inform your plan. Since the execution agent retains context across steps, you can also plan read steps for non-markdown files that the execution agent needs to reference during later steps. ## Plan Structure @@ -128,6 +174,22 @@ Phase 3: Linking - Steps 7-8: Update cross-references (context includes files from prior phases) \`\`\` +### 4. Batch Tasks (one action repeated across many files) +Enumerate every file individually. Group into phases if helpful, but never collapse into a single bulk step: +\`\`\` +Phase 1: Files requiring changes (7 files) +- Step 1: Update Recipes/pasta-carbonara.md — remove cream, substitute egg yolks + context: "cream not in /Ingredients, closest valid: egg-yolks.md..." +- Step 2: Update Recipes/thai-green-curry.md — replace kaffir lime leaves with lime zest + context: "kaffir lime leaves not in /Ingredients, closest valid: lime-zest.md..." +- ...one step per file with specific changes... + +Phase 2: Files already correct (5 files — no steps needed) +- Recipes/mushroom-risotto.md, Recipes/tomato-soup.md, ... — all ingredients verified, no changes required. +\`\`\` + +Note: Files that need no changes should be acknowledged (so the user knows they were checked) but do NOT need plan steps, since no mutation is required. + ## Vault Conventions ### 1. Wiki-Links @@ -137,8 +199,8 @@ Preserve and extend the knowledge graph: - Plan for bidirectional linking where appropriate ### 2. File Types -- **Text notes**: Searchable, creatable, updatable inline -- **Images/PDFs**: Must be read first, then referenced — plan explicit read steps +- **Text notes**: Searchable, creatable, updatable inline — can also be planned as read steps to build execution agent context +- **Images/PDFs**: Must be read first, then referenced — plan explicit read steps so the execution agent has the content in its working memory - **Complex structures**: May need multi-step processing ## Execution Agent Available Tools @@ -149,13 +211,45 @@ ${AIToolDefinitions.compactSummaryForPlanningAgent()} Design your plan steps around these capabilities. The execution agent selects the appropriate tools during execution. +## Common Anti-Patterns + +### Vague Goal Steps +The most frequent failure mode. These read like task descriptions, not commands: +- "Ensure the note reflects the latest information" +- "Update the file based on vault context" +- "Cross-reference with related notes" + +These fail because the execution agent receives no specifics about WHAT to change, WHERE to change it, or what the NEW content should be/look like. + +Fix: Decompose the goal into the concrete file operation. Ask yourself: +- Which file path is being modified? +- What specific content is being added, removed, or replaced? +- Is the source material for the change included in the context? + +### Bulk Batch Steps +When a task applies to many files, collapsing them into one step is a form of vagueness. The execution agent cannot meaningfully act on "update all 12 recipe files" — it needs to know what each file currently contains and what specifically to change. + +❌ "Update all notes in /Recipes to only use ingredients from /Ingredients" +❌ "Create a new note for each character using the character template" +❌ "Read all files in /Personal notes and restructure them" + +These delegate the planning work (reading files, comparing content, deciding changes) to the execution agent. The planning agent must do this work during exploration and produce per-file steps. + +Fix: Read every target file during exploration. For each file, determine what needs to change (or be created) and emit a dedicated step with file-specific context. + ## Quality Checklist Before returning any plan, verify: - [ ] Vault explored thoroughly using progressive search tiers - [ ] Regex patterns tried when initial searches failed - [ ] Relevant images/PDFs read, not just noted -- [ ] Each step is an action, not a discovery task for information already found +- [ ] Every step commands a file mutation (create, update, delete, move) or a deliberate read-for-context +- [ ] Every step names the specific file path being changed or read +- [ ] No step is purely analytical ("compare," "verify," "ensure," "review") +- [ ] Read steps are used only when content is too large for inline context or faithful reproduction matters +- [ ] The user's core request maps to at least one explicit write/update step +- [ ] Multi-file tasks are enumerated as individual per-file steps, not bulk operations +- [ ] Each step is an action or a read-for-context, not a re-search for information already found - [ ] Exploration findings passed as step context - [ ] Each step is atomic and unconditional - [ ] Step dependencies are logically ordered @@ -180,4 +274,30 @@ Before returning any plan, verify: context: "Found 8 ML-related notes: research/neural-networks.md (CNN architectures), research/transformers.md (attention mechanisms), projects/ml-classifier.md (random forest implementation), … Key themes: supervised learning, neural architectures, NLP applications" The planning agent already did the discovery. The execution agent gets everything it needs via context. + +## Example: Batch Task + +**User Request**: "Read all files in /Personal notes and create new notes based on template @daily-reflection" + +**Exploration Phase**: +1. List /Personal notes/ → 4 files: thoughts.md, travel-ideas.md, goals-2024.md, reading-list.md +2. Read each file to understand content +3. Read templates/daily-reflection.md to understand structure: has sections for ## Highlights, ## Reflections, ## Action Items + +**Plan** (one step per file to create): +\`\`\` +1. Create Personal notes/thoughts-reflection.md from daily-reflection template, populated with content from thoughts.md + context: "Template has sections: Highlights, Reflections, Action Items. thoughts.md contains: scattered notes about career change considerations, frustration with current role, ideas for side projects. Highlights: career pivot thinking. Reflections: dissatisfaction themes. Action Items: research side project feasibility." + +2. Create Personal notes/travel-ideas-reflection.md from daily-reflection template, populated with content from travel-ideas.md + context: "travel-ideas.md contains: bucket list destinations (Japan, Portugal, New Zealand), rough budget notes, best-season-to-visit info. Highlights: 3 destinations researched. Reflections: prioritizing Japan for 2024. Action Items: book Japan flights, research JR pass." + +3. Create Personal notes/goals-2024-reflection.md from daily-reflection template, populated with content from goals-2024.md + context: "goals-2024.md contains: fitness (run half marathon), career (get promotion or switch), personal (read 24 books, learn piano basics). Highlights: 3 goal categories set. Reflections: career goal most urgent. Action Items: sign up for half marathon, update resume." + +4. Create Personal notes/reading-list-reflection.md from daily-reflection template, populated with content from reading-list.md + context: "reading-list.md contains: 15 books listed, 6 marked done, 3 marked in-progress. Highlights: on track for 24-book goal. Reflections: skewing toward non-fiction, want more fiction. Action Items: pick next fiction book, finish current 3 in-progress." +\`\`\` + +Each step gives the execution agent everything: the template structure, the source content summarized, and how to map one onto the other. No step says "process all files." `; \ No newline at end of file diff --git a/AIPrompts/SystemPrompt.ts b/AIPrompts/SystemPrompt.ts index d1bd926..e94c87b 100644 --- a/AIPrompts/SystemPrompt.ts +++ b/AIPrompts/SystemPrompt.ts @@ -38,6 +38,14 @@ When planning mode is enabled, provide the planning agent with: 2. **Context**: Relevant vault state, user preferences, constraints 3. **Unknowns**: What exploration is needed before committing to an approach +**Critical: Convey the user's intent faithfully.** Do NOT soften, hedge, or add restrictions that the user did not request. The goal must reflect what the user actually asked for — not a cautious reinterpretation. + +- If the user says "ensure details are correct" → the goal is to **update notes to fix incorrect details**, not to "report findings" +- If the user says "organize my notes" → the goal is to **move and restructure files**, not to "suggest an organization" +- If the user says "clean up duplicates" → the goal is to **remove or merge duplicates**, not to "identify duplicates for review" + +The planning agent has its own safeguards and can ask clarifying questions. Your job is to pass the user's intent accurately, not to pre-filter it. + #### Executing a Plan 1. **Treat plan steps as directives** — execute them, don't reinterpret diff --git a/Services/AIServices/ExecutionAgent.ts b/Services/AIServices/ExecutionAgent.ts index 8ba7e83..55a5772 100644 --- a/Services/AIServices/ExecutionAgent.ts +++ b/Services/AIServices/ExecutionAgent.ts @@ -59,6 +59,11 @@ export class ExecutionAgent extends BaseAgent { return { shouldExit: false }; } this.debugService?.log("ExecutionAgent", `Task completed (success: ${parseResult.data.success}): ${parseResult.data.description}`); + this.conversation.addFunctionResponse(new AIToolResponse( + toolCallName, + { result: parseResult.data.success ? "Task completed successfully." : "Task failed, attempting recovery..." }, + toolCall.toolId + )); this.updateThought(toolCall, callbacks); executionResult = parseResult.data; return { shouldExit: true }; @@ -79,6 +84,8 @@ export class ExecutionAgent extends BaseAgent { })); return await this.runExecutionAgent(step, callbacks); } + + this.executionDepth = 0; return executionResult; } diff --git a/Services/AIServices/OrchestrationAgent.ts b/Services/AIServices/OrchestrationAgent.ts index 254d90f..226edb2 100644 --- a/Services/AIServices/OrchestrationAgent.ts +++ b/Services/AIServices/OrchestrationAgent.ts @@ -49,13 +49,13 @@ export class OrchestrationAgent extends BaseAgent { let stepIndex = 0; let planCompleted = false; + const executionAgent = new ExecutionAgent(); + executionAgent.resolveAIProvider(); + while (stepIndex < executionPlan.executionSteps.length && !planCompleted) { const step = executionPlan.executionSteps[stepIndex]; callbacks.onPlanStepUpdate(stepIndex); this.debugService?.log("OrchestrationAgent", `Executing step ${stepIndex + 1}/${executionPlan.executionSteps.length}: ${step.description}`); - - const executionAgent = new ExecutionAgent(); - executionAgent.resolveAIProvider(); const executionResult = await executionAgent.runExecutionAgent(step, callbacks); @@ -362,7 +362,7 @@ export class OrchestrationAgent extends BaseAgent { this.ai.agentType = AgentType.Orchestration; this.ai.aiToolUsageMode = AIToolUsageMode.Enabled; this.ai.systemPrompt = this.aiPrompt.orchestrationInstruction(); - this.ai.userInstruction = ""; + this.ai.userInstruction = ""; // do not include user instruction for orchestration agent this.ai.aiToolDefinitions = AIToolDefinitions.orchestrationAgentDefinitions(); } diff --git a/Services/AIServices/PlanningAgent.ts b/Services/AIServices/PlanningAgent.ts index 1cb5af2..28e7bc4 100644 --- a/Services/AIServices/PlanningAgent.ts +++ b/Services/AIServices/PlanningAgent.ts @@ -21,7 +21,7 @@ export class PlanningAgent extends BaseAgent { private planningDepth: number = 0; public async runPlanningAgent(conversation: Conversation, callbacks: IChatServiceCallbacks): Promise { - await this.setAgentPromptAndTools(); + this.setAgentPromptAndTools(); let capturedPlan: ExecutionPlan | null = null; @@ -106,14 +106,14 @@ export class PlanningAgent extends BaseAgent { return capturedPlan; } - private async setAgentPromptAndTools() { + private setAgentPromptAndTools() { if (!this.ai) { // this shouldn't ever happen Exception.throw("Error: No AI provider has been set!"); } this.ai.agentType = AgentType.Planning; this.ai.aiToolUsageMode = AIToolUsageMode.Enabled; this.ai.systemPrompt = this.aiPrompt.planningInstruction(); - this.ai.userInstruction = await this.aiPrompt.userInstruction(); + this.ai.userInstruction = ""; // do not include user instruction for planning agent this.ai.aiToolDefinitions = AIToolDefinitions.planningAgentDefinitions(); }