UNPKG

@smythos/sdk

Version:
327 lines (274 loc) 14.1 kB
import { Agent } from '../Agent.class'; //Also Before any action (tool call, writing, responding, ...etc) call _sre_StatusUpdate to update the user on your next action. const _planner_prompt = ` ========================= Modus Operandi: ========================= You are operating in planner mode, designed to handle complex tasks systematically and transparently. ## Core Planning Principles When you receive a complex or multi-step task, you MUST: 1. **Plan first, act second**: Always create a plan before taking any action or calling tools, in this step you should break down the task into clear, actionable steps 2. **Stay focused**: Stick to the user's request without extrapolating unnecessarily 3. **Fill knowledge gaps**: If you lack information needed to complete a step, explicitly add a step to search for it or ask the user ## Planning Workflow ### Creating Plans - Prepare your plan within <planning></planning> tags - Use _sre_Plan_Tasks to formally track the steps that you planned - Ensure each step is specific and contributes directly to achieving the user's goal - *Do NOT* reveal the plan details to the user in your response, keep it inside <planning></planning> tags ### Managing Tasks - **Breaking down complexity**: When processing a task that contains multiple steps, decompose it further using _sre_AddSubTasks - **Maintaining status**: Update task and subtask status before and after *every* action (tool calls, responses, questions, etc.) using _sre_UpdateTasks - **Marking completion**: After writing a response that completes a task, call _sre_UpdateTasks to mark that task as "completed" - **Resetting**: If you need to start a completely new plan, call _sre_ClearTasks to clear existing tasks ### Verification Once you finish answering the user, *ALWAYS* call _sre_TasksCompleted to: - Update the plan status - Verify that no steps were missed - Ensure the user's request has been fully addressed ## Communication Guidelines ### Transparency - **Express your reasoning**: Always share your thought process with the user inside <thinking> tags - **Use thinking tags**: Wrap internal reasoning and any informations about the plan, the tasks and the tools that starts with _sre_* in <thinking></thinking> tags on separate lines - **Announce actions**: Inform the user of your next step after every tool call that is not part of the _sre_* tools - **Iterate openly**: You can include multiple <thinking> blocks in a single response to question previous answers or explore alternative approaches when stuck ### Typical pattern after a user question: \`\`\` <thinking> The user asked about .... so I need to .... </thinking> I will help you with that by ... <planning> - Task 1: ... - Task 2: ... - Task 3: ... </planning> <thinking> Before I start, I need to plan the tasks first </thinking> (call _sre_Plan_Tasks) <thinking> Now I need to ... for that I will ... </thinking> Ok, now I need to search for the information ... (e.g call search tool if needed) (e.g call _sre_UpdateTasks to update the task status if needed) Great, now I need to address ... <thinking> humm it seems that this task is more complex than I thought, I need to break it down into smaller tasks </thinking> (call _sre_AddSubTasks) (e.g call tools if needed) (e.g call _sre_UpdateTasks to update the task status if needed) <thinking> I have now everything I need to answer the user's question, let me write the answer ... </thinking> (...write your answer...) (*Never* say something like "Now I'll update the tasks" in a separate line, this kind of statement should be inside <thinking> tags) (call _sre_TasksCompleted to verify that no steps were missed) \`\`\` ### Tag Usage Rules - Special tags like <thinking> and <planning> must NOT be nested - Each tag must be properly closed before opening another tag of the same type - Keep tags on separate lines for readability - *Always* call _sre_Plan_Tasks after <planning> - *Never* call a tool before adding tasks to the planner using _sre_Plan_Tasks ## Action Sequence Summary For every user request: 1. **Think** aloud (in <thinking> tags) before and during actions. IMPORTANT: after *EVERY* user question, you should at least use <thinking> once before anything else. 2. **Analyze** the request complexity 3. **Plan** the approach (in <planning> tags) 4. **Add tasks** to the planner using _sre_Plan_Tasks 5. **Execute** each step sequentially 6. **Update** task status continuously (_sre_UpdateTasks, _sre_AddSubTasks, _sre_UpdateSubTasks) 7. **Communicate** your next action to the user 8. **Verify** completion (_sre_TasksCompleted) ## Remember - Never act before planning - Never call a tool before adding tasks to the planner using _sre_Plan_Tasks - Keep the user informed at every step - Maintain accurate task status throughout - Always verify completion before finishing `; export default class PlannerMode { static apply(agent: Agent) { agent.behavior += _planner_prompt; const _tasks = {}; const addTasksSkill = agent.addSkill({ name: '_sre_Plan_Tasks', description: 'Use this skill to add tasks to the planner. This should *always* be called after <planning> tag to ensure that the tasks are added to the planner', process: async ({ tasksList }) => { //taskList structure : {"task-id" : {description: "task description", summary:"concise task description in 10 words", status:<planned | ongoing | completed>} } if (typeof tasksList === 'string') { try { tasksList = JSON.parse(tasksList); } catch (error) { return `Error parsing tasks list, the tasks list is not a valid json object`; } } for (const taskId in tasksList) { const task = tasksList[taskId]; let _taskObj = {}; if (typeof task === 'string') { _taskObj = { description: task, status: 'planned' }; } else { _taskObj = task; } _tasks[taskId] = _taskObj; } //emit the tasks added event agent.emit('TasksAdded', tasksList, _tasks); return _tasks; }, }); addTasksSkill.in({ tasksList: { type: 'Any', description: 'The tasks list to add to the planner, it should be a json object with the following structure: {"task-id" : {description: "task description", summary:"concise task description in 10 words", status:<planned | ongoing | completed>}, ... }', }, }); const addSubTasksSkill = agent.addSkill({ name: '_sre_AddSubTasks', description: 'Use this skill to add sub-tasks to a task in the planner, the sub-tasks list should be a json object with the following structure: {"sub-task-id" : {description: "sub-task description", summary:"concise sub-task description in 10 words", status:<planned | ongoing | completed>, parentTaskId: "the id of the parent task"}, ... }', process: async ({ taskId, subTasksList }) => { // Validate that parent task exists if (!_tasks[taskId]) { return `Error: Parent task with ID "${taskId}" does not exist`; } if (typeof subTasksList === 'string') { try { subTasksList = JSON.parse(subTasksList); } catch (error) { return `Error parsing subtasks list, the subtasks list is not a valid json object`; } } // Initialize subtasks object if it doesn't exist if (!_tasks[taskId].subtasks) { _tasks[taskId].subtasks = {}; } // Add each subtask for (const subTaskId in subTasksList) { const subTask = subTasksList[subTaskId]; let _subTaskObj = {}; if (typeof subTask === 'string') { _subTaskObj = { description: subTask, status: 'planned', parentTaskId: taskId, }; } else { _subTaskObj = { ...subTask, parentTaskId: taskId, }; } _tasks[taskId].subtasks[subTaskId] = _subTaskObj; } //emit the subtasks added event agent.emit('SubTasksAdded', taskId, subTasksList, _tasks); return _tasks; }, }); addSubTasksSkill.in({ taskId: { type: 'Text', description: 'The ID of the parent task to add subtasks to', }, subTasksList: { type: 'Any', description: 'The subtasks list to add to the parent task, it should be a json object with the following structure: {"sub-task-id" : {description: "sub-task description", summary:"concise sub-task description in 10 words", status:<planned | ongoing | completed>}, ... }', }, }); const updateTasksSkill = agent.addSkill({ name: '_sre_UpdateTasks', description: 'Use this skill to update the status of a task or subtask in the planner. For subtasks, use format "parentTaskId.subtaskId"', process: async ({ taskId, status }) => { // Check if this is a subtask (contains a dot) if (taskId.includes('.')) { const [parentId, subTaskId] = taskId.split('.'); if (_tasks[parentId] && _tasks[parentId].subtasks && _tasks[parentId].subtasks[subTaskId]) { _tasks[parentId].subtasks[subTaskId].status = status; } else { return `Error: Subtask "${subTaskId}" not found in parent task "${parentId}"`; } } else { // Regular task update if (_tasks[taskId]) { _tasks[taskId].status = status; } else { return `Error: Task "${taskId}" not found`; } } // Update the sticky tasks panel agent.emit('TasksUpdated', taskId, status, _tasks); return _tasks; }, }); const tasksCompleted = agent.addSkill({ name: '_sre_TasksCompleted', description: 'Call this skill when finish your current job', process: async () => { const missingTasks = []; let allCompleted = true; // Check main tasks and their subtasks for (const taskId in _tasks) { const task = _tasks[taskId]; // Check main task if (task.status !== 'completed') { allCompleted = false; missingTasks.push(taskId); } // Check subtasks if they exist if (task.subtasks) { for (const subTaskId in task.subtasks) { const subTask = task.subtasks[subTaskId]; if (subTask.status !== 'completed') { allCompleted = false; missingTasks.push(`${taskId}.${subTaskId}`); } } } } if (!allCompleted) { return `Not all tasks are completed, the following tasks/subtasks are missing: ${missingTasks.join(', ')}`; } // Update the sticky tasks panel agent.emit('TasksCompleted', _tasks); return 'All tasks and subtasks are completed'; }, }); const statusUpdate = agent.addSkill({ name: '_sre_clearTasks', description: 'Call this skill to clear the tasks from the planner, use this skill when you finish your current job and need to start a brand new plan', process: async () => { for (const taskId in _tasks) { delete _tasks[taskId]; } agent.emit('TasksCleared', _tasks); return 'Tasks cleared'; }, }); // agent.addSkill({ // name: '_sre_StatusUpdate', // description: // "Call this skill to update the communicated status of the Agent, use this to tell the user what you'll be doing in the next step, it should be a very short summary of your next action", // process: async ({ status }) => { // agent.emit('StatusUpdated', status); // return "Status updated, don't forget to update the tasks"; // }, // }); } static remove(agent: Agent) { agent.removeSkill('_sre_Plan_Tasks'); agent.removeSkill('_sre_AddSubTasks'); agent.removeSkill('_sre_UpdateTasks'); agent.removeSkill('_sre_TasksCompleted'); agent.removeSkill('_sre_clearTasks'); agent.behavior = agent.behavior.replace(_planner_prompt, ''); } }