//! 人设系统(P0) — AgentPersona 结构体 + PersonaRegistry 注册表 //! //! 构成 AI Native 的基础:角色划分是分工的前提。 //! - 每名人设包含:能力标签、工具白名单、system prompt 模板 //! - Registry 提供按场景/意图选人设、按 id 查人设能力 //! //! 设计依据:docs/02-架构设计/构想审查/AI-Native方向与路线图-2026-06-29.md use std::collections::HashMap; /// 内置人设 ID 常量 pub const PERSONA_CODER: &str = "coder"; pub const PERSONA_REVIEWER: &str = "reviewer"; pub const PERSONA_ARCHITECT: &str = "architect"; pub const PERSONA_TESTER: &str = "tester"; pub const PERSONA_ANALYST: &str = "analyst"; /// 人设能力标签枚举 #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum PersonaCapability { /// 代码生成与修改 CodeGeneration, /// Code Review CodeReview, /// 架构设计与分析 ArchitectureDesign, /// 测试编写与执行 Testing, /// 数据分析与搜索 Analysis, /// 文件读写 FileOperation, /// 命令执行 CommandExecution, /// Git 操作 GitOperation, /// 知识库操作 KnowledgeOperation, /// 项目管理 ProjectManagement, } impl PersonaCapability { pub fn label(&self) -> &'static str { match self { Self::CodeGeneration => "代码生成", Self::CodeReview => "代码审查", Self::ArchitectureDesign => "架构设计", Self::Testing => "测试", Self::Analysis => "分析", Self::FileOperation => "文件操作", Self::CommandExecution => "命令执行", Self::GitOperation => "Git 操作", Self::KnowledgeOperation => "知识库", Self::ProjectManagement => "项目管理", } } } /// 人设定义 #[derive(Debug, Clone)] pub struct AgentPersona { /// 唯一标识(如 "coder", "reviewer") pub id: String, /// 人设名称(如 "程序员", "审查员") pub name: String, /// 人设描述(LLM 理解用) pub description: String, /// 能力标签集 pub capabilities: Vec, /// 工具白名单(空=不限,非空=仅允许列出的工具) pub tool_whitelist: Vec, /// system prompt 模板(注入 {context} {goals} 等占位符) pub system_prompt_template: String, /// 优先级(选人设时冲突排名,0=最高) pub priority: u8, } impl AgentPersona { /// 检查此人设是否具备某项能力 pub fn has_capability(&self, cap: &PersonaCapability) -> bool { self.capabilities.contains(cap) } /// 检查某工具是否在此人设的 whitelist 中 pub fn is_tool_allowed(&self, tool_name: &str) -> bool { self.tool_whitelist.is_empty() || self.tool_whitelist.iter().any(|t| t == tool_name) } /// 构建带上下文的 system prompt pub fn build_prompt(&self, context: &str, goals: &[String]) -> String { let goals_text = if goals.is_empty() { String::new() } else { format!("\n## 当前目标\n{}", goals.iter().enumerate().map(|(i, g)| format!("{}. {}", i + 1, g)).collect::>().join("\n")) }; self.system_prompt_template .replace("{context}", context) .replace("{goals}", &goals_text) } } /// 人设注册表 — 管理内置 + 自定义人设 #[derive(Debug, Clone)] pub struct PersonaRegistry { personae: HashMap, /// 默认人设 id(兜底) default_id: String, } impl PersonaRegistry { /// 构造默认注册表(含 5 内置人设) pub fn new() -> Self { let mut reg = Self { personae: HashMap::new(), default_id: PERSONA_CODER.to_string(), }; reg.register(Self::builtin_coder()); reg.register(Self::builtin_reviewer()); reg.register(Self::builtin_architect()); reg.register(Self::builtin_tester()); reg.register(Self::builtin_analyst()); reg } // ── 5 内置人设 ── fn builtin_coder() -> AgentPersona { AgentPersona { id: PERSONA_CODER.to_string(), name: "程序员".to_string(), description: "负责代码生成、修改、重构。专注于实现功能、修复 bug、优化性能,不关注大范围架构变更".to_string(), capabilities: vec![ PersonaCapability::CodeGeneration, PersonaCapability::FileOperation, PersonaCapability::CommandExecution, ], tool_whitelist: vec![], // 不限(全部工具可用) system_prompt_template: "你是 DevFlow 的**程序员**(Coder)。\n\n你的职责是编写、修改和重构代码。专注于具体实现,不擅自改变整体架构。\n\n{goals}\n\n{context}".to_string(), priority: 1, } } fn builtin_reviewer() -> AgentPersona { AgentPersona { id: PERSONA_REVIEWER.to_string(), name: "审查员".to_string(), description: "负责 Code Review、质量审计、安全检查。审查代码正确性、性能、安全性,返回审查意见不直接改代码".to_string(), capabilities: vec![ PersonaCapability::CodeReview, PersonaCapability::Analysis, ], tool_whitelist: vec![ "read_file".into(), "search_files".into(), "list_directory".into(), "git_diff".into(), "git_log".into(), "git_status".into(), ], system_prompt_template: "你是 DevFlow 的**审查员**(Reviewer)。\n\n你的职责是审查代码质量、安全性和性能。**你不直接修改代码**,只输出审查意见和修改建议。\n\n{goals}\n\n{context}".to_string(), priority: 3, } } fn builtin_architect() -> AgentPersona { AgentPersona { id: PERSONA_ARCHITECT.to_string(), name: "架构师".to_string(), description: "负责架构设计、技术选型、模块划分。分析整体结构,制定技术方案,不关注具体代码实现".to_string(), capabilities: vec![ PersonaCapability::ArchitectureDesign, PersonaCapability::Analysis, ], tool_whitelist: vec![ "read_file".into(), "search_files".into(), "list_directory".into(), "write_file".into(), // 写架构文档 ], system_prompt_template: "你是 DevFlow 的**架构师**(Architect)。\n\n你的职责是架构设计、技术选型和模块划分。分析系统整体结构,制定技术方案。**不编写业务代码**,输出架构文档和设计方案。\n\n{goals}\n\n{context}".to_string(), priority: 2, } } fn builtin_tester() -> AgentPersona { AgentPersona { id: PERSONA_TESTER.to_string(), name: "测试员".to_string(), description: "负责编写单元测试、集成测试、执行测试。验证代码正确性,不修改生产代码".to_string(), capabilities: vec![ PersonaCapability::Testing, PersonaCapability::CommandExecution, PersonaCapability::FileOperation, ], tool_whitelist: vec![], // 不限 system_prompt_template: "你是 DevFlow 的**测试员**(Tester)。\n\n你的职责是编写测试、执行测试、报告测试结果。**不修改生产代码**,只修改测试代码。\n\n{goals}\n\n{context}".to_string(), priority: 3, } } fn builtin_analyst() -> AgentPersona { AgentPersona { id: PERSONA_ANALYST.to_string(), name: "分析师".to_string(), description: "负责数据分析、日志排查、性能分析。搜索信息、分析数据,输出分析报告".to_string(), capabilities: vec![ PersonaCapability::Analysis, PersonaCapability::CommandExecution, PersonaCapability::KnowledgeOperation, ], tool_whitelist: vec![ "read_file".into(), "search_files".into(), "grep_search".into(), "run_command".into(), "git_log".into(), "knowledge_search".into(), "knowledge_inject".into(), ], system_prompt_template: "你是 DevFlow 的**分析师**(Analyst)。\n\n你的职责是分析数据、排查问题、搜索信息。输出分析报告和结论。**不修改代码**。\n\n{goals}\n\n{context}".to_string(), priority: 4, } } // ── 注册表操作 ── /// 注册/覆盖人设 pub fn register(&mut self, persona: AgentPersona) { self.personae.insert(persona.id.clone(), persona); } /// 按 id 查人设 pub fn get(&self, id: &str) -> Option<&AgentPersona> { self.personae.get(id) } /// 取默认人设 pub fn default(&self) -> &AgentPersona { self.personae.get(&self.default_id).expect("默认人设必存在") } /// 设置默认人设 id pub fn set_default(&mut self, id: &str) { if self.personae.contains_key(id) { self.default_id = id.to_string(); } } /// 按意图推荐人设(启发式规则) pub fn recommend_for_intent(&self, intent: &str) -> &AgentPersona { let lower = intent.to_lowercase(); if lower.contains("review") || lower.contains("审查") || lower.contains("审计") { self.get(PERSONA_REVIEWER).unwrap_or_else(|| self.default()) } else if lower.contains("架构") || lower.contains("设计") || lower.contains("architect") { self.get(PERSONA_ARCHITECT).unwrap_or_else(|| self.default()) } else if lower.contains("测试") || lower.contains("test") { self.get(PERSONA_TESTER).unwrap_or_else(|| self.default()) } else if lower.contains("分析") || lower.contains("分析") || lower.contains("排查") || lower.contains("search") { self.get(PERSONA_ANALYST).unwrap_or_else(|| self.default()) } else { // 默认走 coder(代码生成是最常见场景) self.default() } } /// 获取全部人设列表 pub fn all(&self) -> Vec<&AgentPersona> { let mut list: Vec<_> = self.personae.values().collect(); list.sort_by_key(|p| p.priority); list } /// 人设数量 pub fn len(&self) -> usize { self.personae.len() } pub fn is_empty(&self) -> bool { self.personae.is_empty() } } impl Default for PersonaRegistry { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; #[test] fn registry_has_5_builtin() { let reg = PersonaRegistry::new(); assert_eq!(reg.len(), 5); assert!(reg.get(PERSONA_CODER).is_some()); assert!(reg.get(PERSONA_REVIEWER).is_some()); assert!(reg.get(PERSONA_ARCHITECT).is_some()); assert!(reg.get(PERSONA_TESTER).is_some()); assert!(reg.get(PERSONA_ANALYST).is_some()); } #[test] fn default_is_coder() { let reg = PersonaRegistry::new(); assert_eq!(reg.default().id, PERSONA_CODER); } #[test] fn recommend_reviewer() { let reg = PersonaRegistry::new(); let p = reg.recommend_for_intent("review the code changes"); assert_eq!(p.id, PERSONA_REVIEWER); } #[test] fn recommend_architect() { let reg = PersonaRegistry::new(); let p = reg.recommend_for_intent("设计新的模块架构"); assert_eq!(p.id, PERSONA_ARCHITECT); } #[test] fn recommend_tester() { let reg = PersonaRegistry::new(); let p = reg.recommend_for_intent("write tests for this module"); assert_eq!(p.id, PERSONA_TESTER); } #[test] fn recommend_default_to_coder() { let reg = PersonaRegistry::new(); let p = reg.recommend_for_intent("implement a new feature"); assert_eq!(p.id, PERSONA_CODER); } #[test] fn tool_whitelist_filters() { let reviewer = AgentPersona { id: "test".into(), name: "test".into(), description: "".into(), capabilities: vec![], tool_whitelist: vec!["read_file".into(), "search_files".into()], system_prompt_template: "".into(), priority: 5, }; assert!(reviewer.is_tool_allowed("read_file")); assert!(!reviewer.is_tool_allowed("write_file")); } #[test] fn empty_whitelist_allows_all() { let coder = AgentPersona { id: "test".into(), name: "test".into(), description: "".into(), capabilities: vec![], tool_whitelist: vec![], system_prompt_template: "".into(), priority: 5, }; assert!(coder.is_tool_allowed("run_command")); assert!(coder.is_tool_allowed("write_file")); } #[test] fn build_prompt_replaces_placeholders() { let reg = PersonaRegistry::new(); let coder = reg.get(PERSONA_CODER).unwrap(); let prompt = coder.build_prompt("some context", &["goal1".into()]); assert!(prompt.contains("程序员")); assert!(prompt.contains("goal1")); assert!(prompt.contains("some context")); } #[test] fn register_custom_persona() { let mut reg = PersonaRegistry::new(); let custom = AgentPersona { id: "custom".into(), name: "自定义".into(), description: "test".into(), capabilities: vec![], tool_whitelist: vec![], system_prompt_template: "You are custom".into(), priority: 0, }; reg.register(custom); assert_eq!(reg.len(), 6); assert!(reg.get("custom").is_some()); } }