Skip to main content

matrixcode_core/
prompt.rs

1use std::str::FromStr;
2
3const SYSTEM_PROMPT_IDENTITY: &str = r#"你是一个谨慎、务实、高效的代码代理,可以使用工具完成任务。"#;
4
5const SYSTEM_PROMPT_MISSION: &str = r#"核心目标:
6- 安全、正确地完成用户提出的编码任务。
7- 优先依据仓库内容、工具输出和可验证事实,而不是猜测。
8- 以尽可能小的改动完整解决问题。
9- 除非用户明确要求,否则尽量保持现有行为不变。"#;
10
11
12const SYSTEM_PROMPT_WORKFLOW: &str = r#"工作方式:
131. 先理解需求,再查看相关代码和文件。
142. 对于非简单任务,使用 todo_write 创建并持续更新待办列表。
153. 每次调用工具前,先简短说明接下来要做什么。
164. 优先基于证据做判断;如果不确定,就继续检查。
175. 保持改动聚焦、最小,并与现有代码风格一致。
186. 除非为安全完成任务所必需,否则避免无关重构。
197. 修改完成后,执行最小且相关的验证。
208. 如果无法验证,要明确说明原因和剩余风险。"#;
21
22const SYSTEM_PROMPT_BEHAVIOR: &str = r#"行为约束:
23- 不要臆造文件、符号、API、测试或运行结果;必须用工具验证。
24- 在没有检查相关文件或命令输出前,不要宣称已经成功。
25- 未经用户明确要求,不要覆盖、回滚或丢弃你未创建的用户改动。
26- 在可行时,优先修复根因,而不是只做表面补丁。
27- 执行具有破坏性、高风险或高成本的命令前,先提醒用户。
28- 如果用户要求的操作不安全或当前不支持,要说明原因,并给出最接近的安全替代方案。"#;
29
30const SYSTEM_PROMPT_AMBIGUITY: &str = r#"歧义确认:
31- 需求描述模糊时必须确认,不要自行解读或"合理推断"。
32- 需要确认的常见情况:
33  • 目标不明确("优化这个函数" — 优化什么?性能/可读性/内存?)
34  • 范围不清晰("修复这个 bug" — 只修复这个还是连带问题?)
35  • 方案有分歧(多种实现路径,各有优劣)
36  • 影响不确定(改动可能影响其他模块,需确认边界)
37  • 用户意图存疑(表述与代码现状矛盾,可能是笔误或误解)
38- 确认方式:使用 `ask` 工具,列出具体选项 + 你的推荐 + 推荐理由。
39- 确认时机:开工前确认,而不是做了一半再问;早问比晚问好。
40- 小决策可跳过:明显最优的唯一方案、低风险、可逆的改动,无需确认。"#;
41
42const SYSTEM_PROMPT_QUALITY: &str = r#"代码质量:
43- 命名:变量/函数名应清晰表达意图,避免无意义缩写(通用约定如 id、url、idx 可接受)。
44- 结构:单一职责原则,函数不超过 30 行,嵌套深度不超过 3 层。
45- 注释:只写"为什么"而非"是什么",复杂逻辑、边界条件、特殊处理必须注释。
46- 类型:优先强类型,避免 any/dynamic,显式声明优于隐式推断。
47- 错误处理:所有外部调用(API、文件、网络)必须有错误处理,禁止静默失败。"#;
48
49const SYSTEM_PROMPT_TESTING: &str = r#"测试验证:
50- 修改代码后,运行相关测试确认未破坏现有功能。
51- 新增功能时,评估是否需要添加测试(简单改动或原型可跳过)。
52- 如果项目无测试框架且任务复杂,询问用户是否需要引入。
53- 测试失败时,先分析失败原因再修改代码,不要盲目猜测修复。
54- 测试通过的改动更可信,无测试覆盖的改动需说明风险。"#;
55
56const SYSTEM_PROMPT_DEBUGGING: &str = r#"调试策略:
57- 先复现问题:理解错误信息、失败场景、触发条件。
58- 定位代码:使用 grep/read 查找相关文件,分析逻辑流程和数据流。
59- 不要猜测根因:用工具(日志、调试器、断点)验证假设。
60- 修复后确认:运行测试或验证步骤,确保问题已解决。
61- 无法定位时:说明已尝试的方法、排查范围、剩余可能性,不要说"不知道"。"#;
62
63const SYSTEM_PROMPT_SECURITY: &str = r#"安全意识:
64- 用户输入必须验证,不要信任外部数据(参数、请求体、文件内容)。
65- 拼接敏感字符串时使用参数化方式,避免 SQL/命令注入风险。
66- 密钥、Token、密码不要硬编码,使用环境变量或安全配置存储。
67- 文件路径操作需验证,避免路径穿越漏洞。
68- 发现潜在安全问题时提醒用户,不要静默忽略或假设无害。"#;
69
70const SYSTEM_PROMPT_EDITING: &str = r#"编辑规则:
71- 修改前先读取目标文件,理解上下文、依赖关系和调用方。
72- 遵循项目约定:命名风格、文件结构、导入顺序、错误处理模式。
73- 保持改动最小化:只改必要的部分,避免连带重构或格式化。
74- 修改公共代码(API、共享模块、配置)时,评估对其他模块的影响。
75- 生成代码优先可读性,其次性能;过早优化是万恶之源。
76- 新增依赖需谨慎:评估必要性、维护状态、社区活跃度、许可证兼容性。"#;
77
78const SYSTEM_PROMPT_EXECUTION: &str = r#"执行策略:
79- 当用户请求实现、调试或修改时,优先直接使用工具推进,而不是只停留在高层建议。
80- 只要可以安全地检查、编辑或验证,就不要停在纯分析阶段。
81- 当下一步明显且风险较低时,无需额外确认即可继续。
82- 当遇到不确定的决策点或多种方案可选时,必须使用 `ask` 工具询问用户,不要自行假设。
83- `ask` 工具必须包含:问题描述、可选方案列表、你的推荐方案及推荐理由。"#;
84
85const SYSTEM_PROMPT_LANGUAGE: &str = r#"语言规则:
86- 使用中文回复,除非用户明确要求其他语言。
87- 代码、命令、路径、错误信息保持原文(英文/中文)。
88- 技术术语保留英文,不要翻译(如 Promise、Hook、Middleware、Container)。
89- 表达简洁,每个段落不超过 3 行,复杂问题用列表或代码说明。
90- 回答问题时先给结论,再给解释,不要先铺垫长背景。
91- 引用代码时标注文件路径和行号,方便定位。"#;
92
93const SYSTEM_PROMPT_COMPLETION: &str = r#"完成要求:
94- 结束时提供:
95  1. 改动摘要(改了什么、为什么改);
96  2. 已执行的验证(测试、运行、检查);
97  3. 剩余风险或后续建议(如有)。"#;
98
99const DEFAULT_SYSTEM_PROMPT_MODULES: &[&str] = &[
100    SYSTEM_PROMPT_IDENTITY,
101    SYSTEM_PROMPT_MISSION,
102    SYSTEM_PROMPT_WORKFLOW,
103    SYSTEM_PROMPT_AMBIGUITY,
104    SYSTEM_PROMPT_BEHAVIOR,
105    SYSTEM_PROMPT_QUALITY,
106    SYSTEM_PROMPT_TESTING,
107    SYSTEM_PROMPT_DEBUGGING,
108    SYSTEM_PROMPT_SECURITY,
109    SYSTEM_PROMPT_EDITING,
110    SYSTEM_PROMPT_EXECUTION,
111    SYSTEM_PROMPT_LANGUAGE,
112    SYSTEM_PROMPT_COMPLETION,
113];
114
115const SAFE_SYSTEM_PROMPT_MODULES: &[&str] = &[
116    SYSTEM_PROMPT_IDENTITY,
117    SYSTEM_PROMPT_MISSION,
118    SYSTEM_PROMPT_WORKFLOW,
119    SYSTEM_PROMPT_AMBIGUITY,
120    SYSTEM_PROMPT_BEHAVIOR,
121    SYSTEM_PROMPT_QUALITY,
122    SYSTEM_PROMPT_SECURITY,
123    SYSTEM_PROMPT_EDITING,
124    SYSTEM_PROMPT_LANGUAGE,
125    SYSTEM_PROMPT_COMPLETION,
126];
127
128const FAST_SYSTEM_PROMPT_MODULES: &[&str] = &[
129    SYSTEM_PROMPT_IDENTITY,
130    SYSTEM_PROMPT_MISSION,
131    SYSTEM_PROMPT_WORKFLOW,
132    SYSTEM_PROMPT_AMBIGUITY,
133    SYSTEM_PROMPT_EXECUTION,
134    SYSTEM_PROMPT_LANGUAGE,
135    SYSTEM_PROMPT_COMPLETION,
136];
137
138const REVIEW_SYSTEM_PROMPT_MODULES: &[&str] = &[
139    SYSTEM_PROMPT_IDENTITY,
140    SYSTEM_PROMPT_MISSION,
141    SYSTEM_PROMPT_WORKFLOW,
142    SYSTEM_PROMPT_AMBIGUITY,
143    SYSTEM_PROMPT_BEHAVIOR,
144    SYSTEM_PROMPT_QUALITY,
145    SYSTEM_PROMPT_TESTING,
146    SYSTEM_PROMPT_SECURITY,
147    SYSTEM_PROMPT_LANGUAGE,
148    SYSTEM_PROMPT_COMPLETION,
149];
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
152#[derive(Default)]
153pub enum PromptProfile {
154    #[default]
155    Default,
156    Safe,
157    Fast,
158    Review,
159}
160
161impl PromptProfile {
162    pub const fn as_str(self) -> &'static str {
163        match self {
164            Self::Default => "default",
165            Self::Safe => "safe",
166            Self::Fast => "fast",
167            Self::Review => "review",
168        }
169    }
170
171    const fn static_modules(self) -> &'static [&'static str] {
172        match self {
173            Self::Default => DEFAULT_SYSTEM_PROMPT_MODULES,
174            Self::Safe => SAFE_SYSTEM_PROMPT_MODULES,
175            Self::Fast => FAST_SYSTEM_PROMPT_MODULES,
176            Self::Review => REVIEW_SYSTEM_PROMPT_MODULES,
177        }
178    }
179}
180
181
182impl FromStr for PromptProfile {
183    type Err = String;
184
185    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
186        match s.trim().to_ascii_lowercase().as_str() {
187            "default" => Ok(Self::Default),
188            "safe" => Ok(Self::Safe),
189            "fast" => Ok(Self::Fast),
190            "review" => Ok(Self::Review),
191            other => Err(format!(
192                "unknown prompt profile '{other}'. expected one of: default, safe, fast, review"
193            )),
194        }
195    }
196}
197
198pub fn build_static_system_prompt(profile: PromptProfile) -> String {
199    profile.static_modules().join("\n\n")
200}
201
202pub const SECTION_PROJECT_CONTEXT: &str = "PROJECT CONTEXT";
203pub const SECTION_TASK_CONTEXT: &str = "TASK CONTEXT";
204pub const SECTION_AVAILABLE_SKILLS: &str = "AVAILABLE SKILLS";
205pub const SECTION_ACCUMULATED_MEMORY: &str = "ACCUMULATED MEMORY";
206
207/// Memory summary section header for system prompt.
208pub const MEMORY_SUMMARY_HEADER: &str = r#"【跨会话记忆摘要】
209以下是从过往对话中积累的关键知识,请在回答时参考这些信息以保持一致性:"#;
210
211/// Memory entry format template.
212pub const MEMORY_ENTRY_TEMPLATE: &str = "{icon} {category}: {content}";
213
214// =============================================================================
215// Overview Generation Prompt Constants
216// =============================================================================
217
218const OVERVIEW_PROMPT_HEADER: &str = "请分析以下项目并生成一份详细的项目概览文档 MATRIX.md。\n\n";
219
220const OVERVIEW_PROMPT_REQUIREMENTS: &[&str] = &[
221    "1. 分析项目的架构和核心功能",
222    "2. 说明关键目录的作用",
223    "3. 提供常用开发命令(构建、测试、运行等)",
224    "4. 总结项目的关键模式和约定",
225    "5. 提供开发注意事项",
226    "6. 如果有业务逻辑(如订单流程、用户系统等),请详细说明",
227];
228
229const OVERVIEW_PROMPT_FORMAT: &str = "输出格式:直接输出 markdown 内容,不要加代码块包裹。";
230
231const OVERVIEW_PROMPT_FOOTER: &str = "请基于以上信息,生成一份详细的项目概览文档 MATRIX.md。";
232
233/// Project context for overview generation.
234pub struct OverviewContext {
235    pub project_name: String,
236    pub project_type: String,
237    pub directory_structure: String,
238    pub config_files: Vec<(String, String)>,
239    pub readme: Option<String>,
240    pub source_files: Vec<(String, String)>,
241}
242
243/// Build the AI prompt for generating project overview (MATRIX.md).
244pub fn build_overview_prompt(context: &OverviewContext) -> String {
245    let mut prompt = String::new();
246
247    prompt.push_str(OVERVIEW_PROMPT_HEADER);
248    prompt.push_str("要求:\n");
249    for req in OVERVIEW_PROMPT_REQUIREMENTS {
250        prompt.push_str(req);
251        prompt.push('\n');
252    }
253    prompt.push('\n');
254    prompt.push_str(OVERVIEW_PROMPT_FORMAT);
255    prompt.push_str("\n\n---\n\n");
256
257    // Add project info
258    prompt.push_str(&format!("项目名称: {}\n", context.project_name));
259    prompt.push_str(&format!("项目类型: {}\n\n", context.project_type));
260
261    // Add directory structure
262    prompt.push_str("## 目录结构\n\n");
263    prompt.push_str("```\n");
264    prompt.push_str(&context.directory_structure);
265    prompt.push_str("```\n\n");
266
267    // Add config files
268    if !context.config_files.is_empty() {
269        prompt.push_str("## 配置文件\n\n");
270        for (filename, content) in &context.config_files {
271            prompt.push_str(&format!("### {}\n\n", filename));
272            prompt.push_str("```\n");
273            prompt.push_str(content);
274            prompt.push_str("\n```\n\n");
275        }
276    }
277
278    // Add README
279    if let Some(readme) = &context.readme {
280        prompt.push_str("## README.md (开头部分)\n\n");
281        prompt.push_str(readme);
282        prompt.push_str("\n\n");
283    }
284
285    // Add key source files
286    if !context.source_files.is_empty() {
287        prompt.push_str("## 关键源文件\n\n");
288        for (filename, content) in &context.source_files {
289            prompt.push_str(&format!("### {}\n\n", filename));
290            prompt.push_str("```\n");
291            prompt.push_str(content);
292            prompt.push_str("\n```\n\n");
293        }
294    }
295
296    prompt.push_str("---\n\n");
297    prompt.push_str(OVERVIEW_PROMPT_FOOTER);
298    prompt.push('\n');
299
300    prompt
301}
302
303#[derive(Debug, Clone, PartialEq, Eq)]
304pub struct PromptSection {
305    title: String,
306    body: String,
307}
308
309impl PromptSection {
310    pub fn new(title: impl Into<String>, body: impl Into<String>) -> Option<Self> {
311        let title = title.into().trim().to_string();
312        let body = body.into().trim().to_string();
313        if title.is_empty() || body.is_empty() {
314            return None;
315        }
316        Some(Self { title, body })
317    }
318
319    pub fn render(&self) -> String {
320        format!("[{}]\n{}", self.title, self.body)
321    }
322}
323
324#[derive(Debug, Clone, Default, PartialEq, Eq)]
325pub struct PromptContext {
326    sections: Vec<PromptSection>,
327}
328
329impl PromptContext {
330    pub fn new() -> Self {
331        Self::default()
332    }
333
334    pub fn push_section(&mut self, title: impl Into<String>, body: impl Into<String>) {
335        if let Some(section) = PromptSection::new(title, body) {
336            self.sections.push(section);
337        }
338    }
339
340    pub fn with_section(mut self, title: impl Into<String>, body: impl Into<String>) -> Self {
341        self.push_section(title, body);
342        self
343    }
344
345    pub fn push_available_skills(&mut self, body: impl Into<String>) {
346        self.push_section(SECTION_AVAILABLE_SKILLS, body);
347    }
348
349    pub fn with_available_skills(mut self, body: impl Into<String>) -> Self {
350        self.push_available_skills(body);
351        self
352    }
353
354    pub fn extend(&mut self, other: PromptContext) {
355        self.sections.extend(other.sections);
356    }
357
358    pub fn is_empty(&self) -> bool {
359        self.sections.is_empty()
360    }
361
362    pub fn render_sections(&self) -> Vec<String> {
363        self.sections.iter().map(PromptSection::render).collect()
364    }
365}
366
367#[derive(Debug, Clone)]
368pub struct SystemPromptBuilder {
369    profile: PromptProfile,
370    context: PromptContext,
371}
372
373impl SystemPromptBuilder {
374    pub fn new(profile: PromptProfile) -> Self {
375        Self {
376            profile,
377            context: PromptContext::new(),
378        }
379    }
380
381    pub fn push_section(&mut self, title: impl Into<String>, body: impl Into<String>) {
382        self.context.push_section(title, body);
383    }
384
385    pub fn with_section(mut self, title: impl Into<String>, body: impl Into<String>) -> Self {
386        self.push_section(title, body);
387        self
388    }
389
390    pub fn push_context(&mut self, context: PromptContext) {
391        self.context.extend(context);
392    }
393
394    pub fn with_context(mut self, context: PromptContext) -> Self {
395        self.push_context(context);
396        self
397    }
398
399    pub fn push_available_skills(&mut self, body: impl Into<String>) {
400        self.context.push_available_skills(body);
401    }
402
403    pub fn with_available_skills(mut self, body: impl Into<String>) -> Self {
404        self.push_available_skills(body);
405        self
406    }
407
408    pub fn build(&self) -> String {
409        let mut parts = vec![build_static_system_prompt(self.profile)];
410        parts.extend(self.context.render_sections());
411        parts.join("\n\n")
412    }
413}
414
415/// Convenience function to build full system prompt
416pub fn build_system_prompt(
417    profile: &PromptProfile,
418    skills: &[crate::skills::Skill],
419    project_overview: Option<&str>,
420    memory_summary: Option<&str>,
421) -> String {
422    let builder = SystemPromptBuilder::new(*profile);
423
424    // Get static prompt parts
425    let static_prompt = build_static_system_prompt(*profile);
426
427    // Dynamically generate tools description
428    let tools_prompt = crate::tools::generate_tools_prompt();
429
430    // Combine: static prompt + tools + sections
431    let mut parts = vec![static_prompt, tools_prompt];
432    parts.extend(builder.context.render_sections());
433    let mut result = parts.join("\n\n");
434
435    // Add project overview if provided
436    if let Some(overview) = project_overview {
437        result.push_str("\n\n[PROJECT CONTEXT]\n");
438        result.push_str(overview);
439    }
440
441    // Add memory summary if provided
442    if let Some(memory) = memory_summary {
443        result.push_str("\n\n[ACCUMULATED MEMORY]\n");
444        result.push_str(memory);
445    }
446    
447    // Add available skills
448    if !skills.is_empty() {
449        result.push_str("\n\n[AVAILABLE SKILLS]\n");
450        for skill in skills {
451            result.push_str(&format!("- {}: {}\n", skill.name, skill.description));
452        }
453    }
454    
455    result
456}