Files
zclaw_openfang/desktop/src-tauri/src/intelligence/cold_start_prompt.rs
iven 13507682f7
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
feat(growth,skills,saas,desktop): C线差异化全量实现 — C1日报+C2飞轮+C3引导
C3 零配置引导 (P0):
- use-cold-start.ts: 4阶段→6阶段对话驱动状态机 (idle→greeting→industry→identity→task→completed)
- cold-start-mapper.ts: 关键词行业检测 + 肯定/否定/名字提取
- cold_start_prompt.rs: Rust侧6阶段system prompt生成 + 7个测试
- FirstConversationPrompt.tsx: 动态行业卡片 + 行业任务引导 + 通用快捷操作

C1 管家日报 (P0):
- kernel注册DailyReportHand (第8个Hand)
- DailyReportPanel.tsx已存在,事件监听+持久化完整

C2 行业知识飞轮 (P1):
- heartbeat.rs: 经验缓存(EXPERIENCE_CACHE) + check_unresolved_pains增强经验感知
- heartbeat_update_experiences Tauri命令 + VikingStorage持久化
- semantic_router.rs: 经验权重boost(0.05*ln(count+1), 上限0.15) + update_experience_boosts方法
- service.rs: auto_optimize_config() 基于使用频率自动优化行业skill_priorities

验证: tsc 0 errors, cargo check 0 warnings, 7 cold_start + 5 daily_report + 1 experience_boost tests PASS
2026-04-21 18:28:45 +08:00

211 lines
8.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Cold start prompt generation for conversation-driven onboarding.
//!
//! Generates stage-specific system prompts that guide the agent through
//! the 6-phase cold start flow without requiring form-filling.
/// Cold start phases matching the frontend state machine.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColdStartPhase {
Idle,
AgentGreeting,
IndustryDiscovery,
IdentitySetup,
FirstTask,
Completed,
}
impl ColdStartPhase {
pub fn from_str(s: &str) -> Self {
match s {
"idle" => Self::Idle,
"agent_greeting" => Self::AgentGreeting,
"industry_discovery" => Self::IndustryDiscovery,
"identity_setup" => Self::IdentitySetup,
"first_task" => Self::FirstTask,
"completed" => Self::Completed,
_ => Self::Idle,
}
}
}
/// Industry-specific task suggestions for first_task phase.
struct IndustryTasks {
tasks: &'static [(&'static str, &'static str)],
}
const HEALTHCARE_TASKS: IndustryTasks = IndustryTasks {
tasks: &[
("排班查询", "今天有需要处理的排班问题吗?"),
("数据报表", "需要我帮你整理上周的数据报表吗?"),
("政策查询", "最近有医保政策变化需要了解吗?"),
],
};
const EDUCATION_TASKS: IndustryTasks = IndustryTasks {
tasks: &[
("课程安排", "需要帮你安排下周的课程吗?"),
("成绩分析", "有学生成绩需要分析吗?"),
("测验生成", "需要帮学生出一份测验吗?告诉我科目和年级就行。"),
],
};
const GARMENT_TASKS: IndustryTasks = IndustryTasks {
tasks: &[
("订单跟踪", "有需要跟踪的订单吗?"),
("生产排期", "需要安排生产计划吗?"),
("成本核算", "有需要核算的成本数据吗?"),
],
};
const ECOMMERCE_TASKS: IndustryTasks = IndustryTasks {
tasks: &[
("库存检查", "需要检查库存情况吗?"),
("销售分析", "想看看最近的销售数据吗?"),
("商品文案", "有新商品需要写详情页吗?"),
],
}
;
/// Generate the cold start system prompt for a given phase and optional industry.
pub fn generate_cold_start_prompt(phase: ColdStartPhase, industry: Option<&str>) -> String {
match phase {
ColdStartPhase::Idle | ColdStartPhase::AgentGreeting => format!(
"你是一个正在认识新用户的 AI 管家。\n\n\
## 当前任务\n\
向用户打招呼并了解他们的工作。用简短自然的方式询问。\n\n\
## 规则\n\
- 每条消息不超过 3 句话\n\
- 不要问\"你的行业是什么\",而是问\"你每天最常处理什么事?\"\n\
- 保持热情友好,像一个新同事在打招呼\n\
- 用中文交流"
),
ColdStartPhase::IndustryDiscovery => {
let industry_hint = match industry {
Some("healthcare") => "用户可能从事医疗行政工作。",
Some("education") => "用户可能从事教育培训工作。",
Some("garment") => "用户可能从事制衣制造工作。",
Some("ecommerce") => "用户可能从事电商零售工作。",
_ => "继续了解用户的工作场景。",
};
format!(
"你是一个正在了解用户工作场景的 AI 管家。\n\n\
## 当前阶段:行业发现\n\
{industry_hint}\n\n\
## 规则\n\
- 根据用户的回答确认行业\n\
- 如果检测到行业,主动说出你的理解,让用户确认\n\
- 每条消息不超过 3 句话\n\
- 用中文交流"
)
}
ColdStartPhase::IdentitySetup => {
let name_suggestion = match industry {
Some("healthcare") => "小医",
Some("education") => "小教",
Some("garment") => "小织",
Some("ecommerce") => "小商",
_ => "小助手",
};
format!(
"你是一个正在为自己起名字的 AI 管家。\n\n\
## 当前阶段:身份设定\n\
根据你了解的行业信息,向用户提议一个合适的名字和沟通风格。\n\n\
## 建议\n\
- 可以提议叫\"{name_suggestion}\"或其他合适的名字\n\
- 说明你选择的沟通风格(专业/亲切/简洁)\n\
- 让用户确认或提出自己的想法\n\
- 每条消息不超过 3 句话\n\
- 用中文交流"
)
}
ColdStartPhase::FirstTask => {
let task_prompt = match industry {
Some("healthcare") => HEALTHCARE_TASKS.tasks[2].1,
Some("education") => EDUCATION_TASKS.tasks[2].1,
Some("garment") => GARMENT_TASKS.tasks[2].1,
Some("ecommerce") => ECOMMERCE_TASKS.tasks[2].1,
_ => "有什么我可以帮你的吗?",
};
format!(
"你是一个 AI 管家,用户已经完成了初始设置。\n\n\
## 当前阶段:首次任务引导\n\
引导用户完成第一个实际任务,让他们体验你的能力。\n\n\
## 建议\n\
- {task_prompt}\n\
- 根据用户需求灵活调整\n\
- 保持简短1-2 句话\n\
- 用中文交流"
)
}
ColdStartPhase::Completed => String::new(),
}
}
/// Check if a cold start prompt should be injected for the given phase.
pub fn should_inject_prompt(phase: ColdStartPhase) -> bool {
!matches!(phase, ColdStartPhase::Idle | ColdStartPhase::Completed)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_phase_from_str() {
assert_eq!(ColdStartPhase::from_str("idle"), ColdStartPhase::Idle);
assert_eq!(ColdStartPhase::from_str("agent_greeting"), ColdStartPhase::AgentGreeting);
assert_eq!(ColdStartPhase::from_str("industry_discovery"), ColdStartPhase::IndustryDiscovery);
assert_eq!(ColdStartPhase::from_str("identity_setup"), ColdStartPhase::IdentitySetup);
assert_eq!(ColdStartPhase::from_str("first_task"), ColdStartPhase::FirstTask);
assert_eq!(ColdStartPhase::from_str("completed"), ColdStartPhase::Completed);
assert_eq!(ColdStartPhase::from_str("unknown"), ColdStartPhase::Idle);
}
#[test]
fn test_greeting_prompt_not_empty() {
let prompt = generate_cold_start_prompt(ColdStartPhase::AgentGreeting, None);
assert!(!prompt.is_empty());
assert!(prompt.contains("AI 管家"));
}
#[test]
fn test_industry_discovery_with_industry() {
let prompt = generate_cold_start_prompt(ColdStartPhase::IndustryDiscovery, Some("healthcare"));
assert!(prompt.contains("医疗行政"));
}
#[test]
fn test_identity_setup_suggests_name() {
let prompt = generate_cold_start_prompt(ColdStartPhase::IdentitySetup, Some("education"));
assert!(prompt.contains("小教"));
}
#[test]
fn test_first_task_has_suggestion() {
let prompt = generate_cold_start_prompt(ColdStartPhase::FirstTask, Some("ecommerce"));
assert!(!prompt.is_empty());
assert!(prompt.contains("库存") || prompt.contains("销售") || prompt.contains("商品"));
}
#[test]
fn test_completed_returns_empty() {
let prompt = generate_cold_start_prompt(ColdStartPhase::Completed, None);
assert!(prompt.is_empty());
}
#[test]
fn test_should_inject() {
assert!(!should_inject_prompt(ColdStartPhase::Idle));
assert!(should_inject_prompt(ColdStartPhase::AgentGreeting));
assert!(should_inject_prompt(ColdStartPhase::IndustryDiscovery));
assert!(should_inject_prompt(ColdStartPhase::IdentitySetup));
assert!(should_inject_prompt(ColdStartPhase::FirstTask));
assert!(!should_inject_prompt(ColdStartPhase::Completed));
}
}