bamboo_engine/runtime/task_evaluation/
message_builder.rs1use std::cmp::Reverse;
2
3use bamboo_agent_core::{Message, Session};
4
5use super::super::task_context::{TaskLoopContext, ToolCallRecord};
6
7pub fn build_task_evaluation_messages(ctx: &TaskLoopContext, _session: &Session) -> Vec<Message> {
9 let mut messages = Vec::new();
10
11 let system_prompt = r#"You are a task progress evaluator. Your job is to evaluate whether tasks are complete based on the execution context.
12
13## Your Task
14Review the task list and execution history, then decide if any tasks should be marked as completed or blocked.
15
16## Rules
171. Mark as "completed" if the task goal has been achieved
182. Mark as "blocked" if there are unresolvable issues
193. Keep as "in_progress" if more work is needed
204. Add brief notes explaining your decision
215. If a task includes completion criteria, only mark it completed when every criterion is met
22
23## Available Actions
24- update_task_item: Update the status of a task item
25
26## Constraints
27- Only update items that are currently "in_progress"
28- You MUST call update_task_item if a task is complete
29- Provide clear reasoning in notes
30- When status is "completed" for a task with completion criteria, `criteria_met` MUST use criterion IDs (for example `c1`, `c2`); do not paraphrase criteria text
31"#;
32
33 messages.push(Message::system(system_prompt));
34
35 let task_context = format!(
36 r#"
37## Current Task List (Round {}/{})
38
39{}
40
41## Recent Tool Executions
42{}
43
44## Instructions
45Review each "in_progress" task above. For each task:
461. Check if the goal has been achieved based on tool execution results
472. If complete, call update_task_item with status="completed", brief notes, and `criteria_met` criterion IDs for tasks that define criteria
483. If blocked, call update_task_item with status="blocked" and explain the issue
49
50Remember: You are NOT executing the task. You are only evaluating if existing work has completed it.
51"#,
52 ctx.current_round + 1,
53 ctx.max_rounds,
54 ctx.format_for_prompt(),
55 format_recent_tools(ctx, 5),
56 );
57
58 messages.push(Message::user(task_context));
59 messages
60}
61
62pub(super) fn format_recent_tools(ctx: &TaskLoopContext, limit: usize) -> String {
64 let mut all_calls: Vec<(String, &ToolCallRecord)> = Vec::new();
65
66 for item in &ctx.items {
67 for call in &item.tool_calls {
68 all_calls.push((item.description.clone(), call));
69 }
70 }
71
72 all_calls.sort_by_key(|(_, call)| Reverse(call.timestamp));
73
74 let recent: Vec<_> = all_calls.into_iter().take(limit).collect();
75
76 if recent.is_empty() {
77 return "No tool executions yet.".to_string();
78 }
79
80 let mut output = String::new();
81 for (index, (task_desc, call)) in recent.iter().enumerate() {
82 output.push_str(&format!(
83 "{}. [{}] Tool: {} ({})\n Task: {}\n",
84 index + 1,
85 if call.success { "✓" } else { "✗" },
86 call.tool_name,
87 call.round + 1,
88 task_desc
89 ));
90 }
91
92 output
93}