Files
zesdex/apps/domain/src/agent/prompt.rs
T
asepharyana eac0443c4c perf(agent): rombak alur AI agent — adaptif, hemat token, self-healing
Ganti explore phase MANDATORY (3 subagent tiap turn, boros) dengan
tool explore_codebase yang DIPUTUSKAN agent sendiri (lazy, token-aware):
- hapus ExploreService trait + with_explore + Phase 0 dari turn loop
- ExploreServiceImpl kini jadi tool 'explore_codebase' (1 context-scout
  subagent, read-only, cap output 4k chars)
- system prompt: instruksi TOKEN BUDGET (jawab langsung utk query simple,
  panggil explore_codebase sekali utk task kompleks)

Loop utama kini adaptif & self-healing:
- max_tokens adaptif (800/1600/4096 by request length) — bukan selalu 4096
- temperature 0.2 saat tool-calling, 0.7 utk final answer
- ErrorTracker: deteksi tool error berulang → inject recovery note,
  stop setelah 8 error total (bukan 50 iterasi sia-sia)
- auto-compact history > 60k chars sebelum LLM call
- tool output di-truncate ke 12k chars sebelum masuk konteks

Tambah 8 unit test (truncation, adaptive tokens, error tracker).
2026-08-27 23:06:37 +07:00

144 lines
5.5 KiB
Rust

//! System prompts and directive templates for agent and subagent turns.
//!
//! Centralising all prompt text here keeps the core turn logic free of
//! hardcoded prose, making prompts easier to maintain, review, and localise.
//!
//! # Flow
//! The application layer's `AgentTurnServiceImpl` calls `main_agent_prompt()`
//! to construct the system message at the start of each turn. Subagent and
//! review prompts are provided by their respective modules.
/// Build the main-agent system prompt.
///
/// The prompt establishes the agent's identity as Zesdex, an AI coding
/// assistant, and defines the priority hierarchy that governs tool selection:
///
/// 1. **Workflow first** — `workflow_run` / `hive_mind` for complex tasks
/// 2. **Planning & TODOs** — `plan_enter` / `todowrite` for structural work
/// 3. **Reasoning** — `seq_think` for deep analysis
/// 4. **Tool execution** — direct tools for simple actions
pub fn main_agent_prompt() -> String {
"\
You are Zesdex, an AI coding assistant. You have access to various tools \
via native function calling to help the user.
TOKEN BUDGET — BE EFFICIENT:
- For simple/factual questions, answer directly. Do NOT call tools.
- For complex or unfamiliar code tasks, call `explore_codebase` ONCE at the \
start to locate relevant code, then work from that context.
- Keep tool usage minimal: prefer `grep`/`glob`/`read` for targeted lookups; \
avoid re-reading files you already have in context.
- Keep responses concise; do not repeat tool output verbatim.
CRITICAL DIRECTIVES & PRIORITY HIERARCHY:
1. WORKFLOW FIRST: For any multi-step, complex, or non-trivial task, \
you MUST prioritise using `workflow_run` (to construct and execute a \
multi-phase YAML workflow) or `hive_mind` (to orchestrate parallel \
autonomous agents). Workflows are your primary strategy.
2. PLANNING & TODOs: Use `plan_enter` to establish high-level \
architectural plans and `todowrite` to maintain granular task checklists.
3. REASONING: Use `seq_think` for deep step-by-step analysis.
4. TOOL EXECUTION: Execute individual tools (file edits, terminal commands) \
within or guided by your workflows. If an error occurs, analyse and fix it.
Respond conversationally, concisely, and helpfully."
.to_string()
}
/// Build a subagent directive prompt.
///
/// The directive is embedded in a system message that also communicates the
/// current working directory and workspace root so the subagent can resolve
/// paths correctly.
pub fn subagent_directive(directive: &str, cwd: &str, ws_root: &str) -> String {
format!(
"\
You are a focused subagent.
Current directory (PWD): {cwd}
Workspace root: {ws_root}
Your directive:
{directive}
Complete the directive autonomously using the tools available to you. \
Return your final answer when done."
)
}
/// Build a conversation-compaction prompt.
///
/// The LLM is asked to produce a concise bulleted summary of the key
/// requests, decisions, tools executed, and files modified.
pub fn compaction_prompt() -> String {
"\
You are a helpful assistant summarising conversation history. \
Provide a concise summary of the key user requests, decisions, tools \
executed, and modified files. Format as a clear bulleted list."
.to_string()
}
// ---------------------------------------------------------------------------
// Adaptive explore: directives
// ---------------------------------------------------------------------------
/// Directive for a single lightweight context-scout subagent.
pub fn explore_scout_directive() -> String {
"\
You are a codebase context scout. \
Given the workspace root, quickly locate the code that is most relevant \
to the user's request: \
1. Run semantic_search once with the user's key terms. \
2. Read up to the 3 most relevant files (use grep for symbols if needed). \
3. Report a concise bullet list (max 15 bullets, under 1500 characters) of \
what you found and exactly where (file paths). \
Do NOT rebuild the index. Do NOT enumerate unrelated files. Be brief."
.to_string()
}
/// Build a system note injected after repeated tool errors to steer the
/// agent toward an alternative approach instead of retrying the same call.
pub fn error_recovery_note(tool_name: &str, last_error: &str) -> String {
format!(
"\
[System note] The tool `{tool_name}` failed repeatedly with: \"{last_error}\". \
Try an alternative approach (verify paths, correct arguments, use a \
different tool, or finish without this tool). Do NOT retry the same call."
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn main_prompt_is_non_empty() {
let prompt = main_agent_prompt();
assert!(!prompt.is_empty());
assert!(prompt.contains("Zesdex"));
assert!(prompt.contains("WORKFLOW FIRST"));
}
#[test]
fn subagent_directive_includes_directive_text() {
let prompt = subagent_directive("test directive", "/home", "/home/project");
assert!(prompt.contains("test directive"));
assert!(prompt.contains("/home"));
assert!(prompt.contains("/home/project"));
}
#[test]
fn explore_scout_directive_is_concise_and_mentions_tools() {
let scout = explore_scout_directive();
assert!(scout.contains("scout"));
assert!(scout.contains("semantic_search"));
}
#[test]
fn error_recovery_note_suggests_alternative() {
let note = error_recovery_note("read", "File not found");
assert!(note.contains("read"));
assert!(note.contains("alternative"));
}
}