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
11const SYSTEM_PROMPT_TOOLS: &str = r#"可用工具:
12- read / write / edit / multi_edit:文件读写。修改已有文件前先 read;修改已有文件时优先使用 edit 或 multi_edit,而不是 write。
13- ls:列出目录下的一级内容(非递归)。
14- glob:按模式查找文件。
15- search:按正则搜索文件内容。
16- bash:执行构建、测试、lint、git 检查等 shell 命令。
17- ask:向用户提问澄清歧义或选择方案。遇到不确定的情况时使用此工具,而非自行猜测。
18- todo_write:用于维护非简单任务的待办列表;始终保持且仅保持一个 in_progress。
19- websearch:客户端网页搜索工具,使用 DuckDuckGo 搜索并返回结果列表。
20- web_search:服务端网页搜索工具(仅 Anthropic),由 API 直接执行搜索,结果更精准。
21- webfetch:获取指定 URL 的页面内容。
22- skill:当任务匹配某项技能时,优先加载技能说明,而不是自行猜测。
23
24工具选择建议:
25- 需要搜索网页信息时,优先使用 web_search(服务端搜索,结果更精准)。
26- 如果 web_search 不可用或需要更多控制,可使用 websearch(客户端搜索)。
27- 要获取具体网页内容时,使用 webfetch。
28- 需要用户澄清或决策时,使用 ask 工具。"#;
29
30const SYSTEM_PROMPT_WORKFLOW: &str = r#"工作方式:
311. 先理解需求,再查看相关代码和文件。
322. 对于非简单任务,使用 todo_write 创建并持续更新待办列表。
333. 每次调用工具前,先简短说明接下来要做什么。
344. 优先基于证据做判断;如果不确定,就继续检查。
355. 保持改动聚焦、最小,并与现有代码风格一致。
366. 除非为安全完成任务所必需,否则避免无关重构。
377. 修改完成后,执行最小且相关的验证。
388. 如果无法验证,要明确说明原因和剩余风险。"#;
39
40const SYSTEM_PROMPT_BEHAVIOR: &str = r#"行为约束:
41- 如果需求存在歧义,且该歧义会阻碍安全推进,先使用 `ask` 工具向用户提问澄清。
42- 当存在多种可行方案时,不要猜测或自选;使用 `ask` 工具列出选项、提供推荐方案及理由,等待用户决定。
43- 不要臆造文件、符号、API、测试或运行结果;必须用工具验证。
44- 在没有检查相关文件或命令输出前,不要宣称已经成功。
45- 未经用户明确要求,不要覆盖、回滚或丢弃你未创建的用户改动。
46- 在可行时,优先修复根因,而不是只做表面补丁。
47- 执行具有破坏性、高风险或高成本的命令前,先提醒用户。
48- 如果用户要求的操作不安全或当前不支持,要说明原因,并给出最接近的安全替代方案。"#;
49
50const SYSTEM_PROMPT_EDITING: &str = r#"编辑规则:
51- 修改文件前,先读取目标文件或相关片段。
52- 遵循周边代码的命名、格式和架构约定。
53- 除非任务明确要求,否则不要修改生成文件。
54- 除非确有必要,否则不要新增依赖。
55- 尽量只改动完成任务所需的最少文件。"#;
56
57const SYSTEM_PROMPT_EXECUTION: &str = r#"执行策略:
58- 当用户请求实现、调试或修改时,优先直接使用工具推进,而不是只停留在高层建议。
59- 只要可以安全地检查、编辑或验证,就不要停在纯分析阶段。
60- 当下一步明显且风险较低时,无需额外确认即可继续。
61- 当遇到不确定的决策点或多种方案可选时,必须使用 `ask` 工具询问用户,不要自行假设。
62- `ask` 工具必须包含:问题描述、可选方案列表、你的推荐方案及推荐理由。"#;
63
64const SYSTEM_PROMPT_LANGUAGE: &str = r#"语言规则:
65- 默认使用中文回复,除非用户明确要求使用其他语言。
66- 代码、命令、路径、报错信息和标识符在合适时保持原文。
67- 表达应简洁、清晰、面向执行。"#;
68
69const SYSTEM_PROMPT_COMPLETION: &str = r#"完成要求:
70- 结束时提供:
71  1. 改动摘要;
72  2. 已执行的验证;
73  3. 剩余风险或后续建议。"#;
74
75const DEFAULT_SYSTEM_PROMPT_MODULES: &[&str] = &[
76    SYSTEM_PROMPT_IDENTITY,
77    SYSTEM_PROMPT_MISSION,
78    SYSTEM_PROMPT_TOOLS,
79    SYSTEM_PROMPT_WORKFLOW,
80    SYSTEM_PROMPT_BEHAVIOR,
81    SYSTEM_PROMPT_EDITING,
82    SYSTEM_PROMPT_EXECUTION,
83    SYSTEM_PROMPT_LANGUAGE,
84    SYSTEM_PROMPT_COMPLETION,
85];
86
87const SAFE_SYSTEM_PROMPT_MODULES: &[&str] = &[
88    SYSTEM_PROMPT_IDENTITY,
89    SYSTEM_PROMPT_MISSION,
90    SYSTEM_PROMPT_TOOLS,
91    SYSTEM_PROMPT_WORKFLOW,
92    SYSTEM_PROMPT_BEHAVIOR,
93    SYSTEM_PROMPT_EDITING,
94    SYSTEM_PROMPT_LANGUAGE,
95    SYSTEM_PROMPT_COMPLETION,
96];
97
98const FAST_SYSTEM_PROMPT_MODULES: &[&str] = &[
99    SYSTEM_PROMPT_IDENTITY,
100    SYSTEM_PROMPT_MISSION,
101    SYSTEM_PROMPT_TOOLS,
102    SYSTEM_PROMPT_WORKFLOW,
103    SYSTEM_PROMPT_EXECUTION,
104    SYSTEM_PROMPT_LANGUAGE,
105    SYSTEM_PROMPT_COMPLETION,
106];
107
108const REVIEW_SYSTEM_PROMPT_MODULES: &[&str] = &[
109    SYSTEM_PROMPT_IDENTITY,
110    SYSTEM_PROMPT_MISSION,
111    SYSTEM_PROMPT_TOOLS,
112    SYSTEM_PROMPT_WORKFLOW,
113    SYSTEM_PROMPT_BEHAVIOR,
114    SYSTEM_PROMPT_LANGUAGE,
115    SYSTEM_PROMPT_COMPLETION,
116];
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119#[derive(Default)]
120pub enum PromptProfile {
121    #[default]
122    Default,
123    Safe,
124    Fast,
125    Review,
126}
127
128impl PromptProfile {
129    pub const fn as_str(self) -> &'static str {
130        match self {
131            Self::Default => "default",
132            Self::Safe => "safe",
133            Self::Fast => "fast",
134            Self::Review => "review",
135        }
136    }
137
138    const fn static_modules(self) -> &'static [&'static str] {
139        match self {
140            Self::Default => DEFAULT_SYSTEM_PROMPT_MODULES,
141            Self::Safe => SAFE_SYSTEM_PROMPT_MODULES,
142            Self::Fast => FAST_SYSTEM_PROMPT_MODULES,
143            Self::Review => REVIEW_SYSTEM_PROMPT_MODULES,
144        }
145    }
146}
147
148
149impl FromStr for PromptProfile {
150    type Err = String;
151
152    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
153        match s.trim().to_ascii_lowercase().as_str() {
154            "default" => Ok(Self::Default),
155            "safe" => Ok(Self::Safe),
156            "fast" => Ok(Self::Fast),
157            "review" => Ok(Self::Review),
158            other => Err(format!(
159                "unknown prompt profile '{other}'. expected one of: default, safe, fast, review"
160            )),
161        }
162    }
163}
164
165pub fn build_static_system_prompt(profile: PromptProfile) -> String {
166    profile.static_modules().join("\n\n")
167}
168
169pub const SECTION_PROJECT_CONTEXT: &str = "PROJECT CONTEXT";
170pub const SECTION_TASK_CONTEXT: &str = "TASK CONTEXT";
171pub const SECTION_AVAILABLE_SKILLS: &str = "AVAILABLE SKILLS";
172pub const SECTION_ACCUMULATED_MEMORY: &str = "ACCUMULATED MEMORY";
173
174/// Memory summary section header for system prompt.
175pub const MEMORY_SUMMARY_HEADER: &str = r#"【跨会话记忆摘要】
176以下是从过往对话中积累的关键知识,请在回答时参考这些信息以保持一致性:"#;
177
178/// Memory entry format template.
179pub const MEMORY_ENTRY_TEMPLATE: &str = "{icon} {category}: {content}";
180
181// =============================================================================
182// Overview Generation Prompt Constants
183// =============================================================================
184
185const OVERVIEW_PROMPT_HEADER: &str = "请分析以下项目并生成一份详细的项目概览文档 MATRIX.md。\n\n";
186
187const OVERVIEW_PROMPT_REQUIREMENTS: &[&str] = &[
188    "1. 分析项目的架构和核心功能",
189    "2. 说明关键目录的作用",
190    "3. 提供常用开发命令(构建、测试、运行等)",
191    "4. 总结项目的关键模式和约定",
192    "5. 提供开发注意事项",
193    "6. 如果有业务逻辑(如订单流程、用户系统等),请详细说明",
194];
195
196const OVERVIEW_PROMPT_FORMAT: &str = "输出格式:直接输出 markdown 内容,不要加代码块包裹。";
197
198const OVERVIEW_PROMPT_FOOTER: &str = "请基于以上信息,生成一份详细的项目概览文档 MATRIX.md。";
199
200/// Project context for overview generation.
201pub struct OverviewContext {
202    pub project_name: String,
203    pub project_type: String,
204    pub directory_structure: String,
205    pub config_files: Vec<(String, String)>,
206    pub readme: Option<String>,
207    pub source_files: Vec<(String, String)>,
208}
209
210/// Build the AI prompt for generating project overview (MATRIX.md).
211pub fn build_overview_prompt(context: &OverviewContext) -> String {
212    let mut prompt = String::new();
213
214    prompt.push_str(OVERVIEW_PROMPT_HEADER);
215    prompt.push_str("要求:\n");
216    for req in OVERVIEW_PROMPT_REQUIREMENTS {
217        prompt.push_str(req);
218        prompt.push('\n');
219    }
220    prompt.push('\n');
221    prompt.push_str(OVERVIEW_PROMPT_FORMAT);
222    prompt.push_str("\n\n---\n\n");
223
224    // Add project info
225    prompt.push_str(&format!("项目名称: {}\n", context.project_name));
226    prompt.push_str(&format!("项目类型: {}\n\n", context.project_type));
227
228    // Add directory structure
229    prompt.push_str("## 目录结构\n\n");
230    prompt.push_str("```\n");
231    prompt.push_str(&context.directory_structure);
232    prompt.push_str("```\n\n");
233
234    // Add config files
235    if !context.config_files.is_empty() {
236        prompt.push_str("## 配置文件\n\n");
237        for (filename, content) in &context.config_files {
238            prompt.push_str(&format!("### {}\n\n", filename));
239            prompt.push_str("```\n");
240            prompt.push_str(content);
241            prompt.push_str("\n```\n\n");
242        }
243    }
244
245    // Add README
246    if let Some(readme) = &context.readme {
247        prompt.push_str("## README.md (开头部分)\n\n");
248        prompt.push_str(readme);
249        prompt.push_str("\n\n");
250    }
251
252    // Add key source files
253    if !context.source_files.is_empty() {
254        prompt.push_str("## 关键源文件\n\n");
255        for (filename, content) in &context.source_files {
256            prompt.push_str(&format!("### {}\n\n", filename));
257            prompt.push_str("```\n");
258            prompt.push_str(content);
259            prompt.push_str("\n```\n\n");
260        }
261    }
262
263    prompt.push_str("---\n\n");
264    prompt.push_str(OVERVIEW_PROMPT_FOOTER);
265    prompt.push('\n');
266
267    prompt
268}
269
270#[derive(Debug, Clone, PartialEq, Eq)]
271pub struct PromptSection {
272    title: String,
273    body: String,
274}
275
276impl PromptSection {
277    pub fn new(title: impl Into<String>, body: impl Into<String>) -> Option<Self> {
278        let title = title.into().trim().to_string();
279        let body = body.into().trim().to_string();
280        if title.is_empty() || body.is_empty() {
281            return None;
282        }
283        Some(Self { title, body })
284    }
285
286    pub fn render(&self) -> String {
287        format!("[{}]\n{}", self.title, self.body)
288    }
289}
290
291#[derive(Debug, Clone, Default, PartialEq, Eq)]
292pub struct PromptContext {
293    sections: Vec<PromptSection>,
294}
295
296impl PromptContext {
297    pub fn new() -> Self {
298        Self::default()
299    }
300
301    pub fn push_section(&mut self, title: impl Into<String>, body: impl Into<String>) {
302        if let Some(section) = PromptSection::new(title, body) {
303            self.sections.push(section);
304        }
305    }
306
307    pub fn with_section(mut self, title: impl Into<String>, body: impl Into<String>) -> Self {
308        self.push_section(title, body);
309        self
310    }
311
312    pub fn push_available_skills(&mut self, body: impl Into<String>) {
313        self.push_section(SECTION_AVAILABLE_SKILLS, body);
314    }
315
316    pub fn with_available_skills(mut self, body: impl Into<String>) -> Self {
317        self.push_available_skills(body);
318        self
319    }
320
321    pub fn extend(&mut self, other: PromptContext) {
322        self.sections.extend(other.sections);
323    }
324
325    pub fn is_empty(&self) -> bool {
326        self.sections.is_empty()
327    }
328
329    pub fn render_sections(&self) -> Vec<String> {
330        self.sections.iter().map(PromptSection::render).collect()
331    }
332}
333
334#[derive(Debug, Clone)]
335pub struct SystemPromptBuilder {
336    profile: PromptProfile,
337    context: PromptContext,
338}
339
340impl SystemPromptBuilder {
341    pub fn new(profile: PromptProfile) -> Self {
342        Self {
343            profile,
344            context: PromptContext::new(),
345        }
346    }
347
348    pub fn push_section(&mut self, title: impl Into<String>, body: impl Into<String>) {
349        self.context.push_section(title, body);
350    }
351
352    pub fn with_section(mut self, title: impl Into<String>, body: impl Into<String>) -> Self {
353        self.push_section(title, body);
354        self
355    }
356
357    pub fn push_context(&mut self, context: PromptContext) {
358        self.context.extend(context);
359    }
360
361    pub fn with_context(mut self, context: PromptContext) -> Self {
362        self.push_context(context);
363        self
364    }
365
366    pub fn push_available_skills(&mut self, body: impl Into<String>) {
367        self.context.push_available_skills(body);
368    }
369
370    pub fn with_available_skills(mut self, body: impl Into<String>) -> Self {
371        self.push_available_skills(body);
372        self
373    }
374
375    pub fn build(&self) -> String {
376        let mut parts = vec![build_static_system_prompt(self.profile)];
377        parts.extend(self.context.render_sections());
378        parts.join("\n\n")
379    }
380}
381
382/// Convenience function to build full system prompt
383pub fn build_system_prompt(
384    profile: &PromptProfile,
385    skills: &[crate::skills::Skill],
386    project_overview: Option<&str>,
387    memory_summary: Option<&str>,
388) -> String {
389    let builder = SystemPromptBuilder::new(*profile);
390    
391    let mut result = builder.build();
392    
393    // Add project overview if provided
394    if let Some(overview) = project_overview {
395        result.push_str("\n\n[PROJECT CONTEXT]\n");
396        result.push_str(overview);
397    }
398    
399    // Add memory summary if provided
400    if let Some(memory) = memory_summary {
401        result.push_str("\n\n[ACCUMULATED MEMORY]\n");
402        result.push_str(memory);
403    }
404    
405    // Add available skills
406    if !skills.is_empty() {
407        result.push_str("\n\n[AVAILABLE SKILLS]\n");
408        for skill in skills {
409            result.push_str(&format!("- {}: {}\n", skill.name, skill.description));
410        }
411    }
412    
413    result
414}