use std::env;
use std::sync::Arc;
use clap::{Parser, Subcommand};
use anyhow::Result;
use tracing::{info, error, warn, debug};
use tokio;
use std::time::Duration;
use alou::agent::{
Agent, McpAgent, AgentConfig, DeepSeekConfig, BehaviorConfig,
WorkspaceConfig, ToolStrategy
};
use alou::connection_pool::ConnectionPool;
#[derive(Parser)]
#[command(name = "agent-cli")]
#[command(about = "智能体CLI工具,使用MCP工具和DeepSeek API")]
struct Cli {
#[arg(short, long)]
quiet: bool,
#[arg(long)]
clean: bool,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Chat {
#[arg(short, long, default_value = "agent_config.json")]
config: String,
},
Test {
#[arg(short, long, default_value = "你好,请介绍一下你的功能")]
message: String,
#[arg(short, long, default_value = "agent_config.json")]
config: String,
},
Init {
#[arg(short, long, default_value = "agent_config.json")]
output: String,
},
}
fn get_default_config() -> AgentConfig {
AgentConfig {
deepseek: DeepSeekConfig {
base_url: env::var("DEEPSEEK_BASE_URL")
.unwrap_or_else(|_| "https://api.deepseek.com".to_string()),
api_key: env::var("DEEPSEEK_API_KEY")
.unwrap_or_else(|_| {
warn!("未设置DEEPSEEK_API_KEY环境变量,请设置正确的API密钥");
"your-api-key-here".to_string()
}),
model: env::var("DEEPSEEK_MODEL")
.unwrap_or_else(|_| "deepseek-chat".to_string()),
max_tokens: 2000,
temperature: 0.7,
},
behavior: BehaviorConfig {
max_retries: 3,
timeout_seconds: 30,
verbose_logging: true,
tool_strategy: ToolStrategy::Auto,
},
workspace: WorkspaceConfig {
directories: vec![".".to_string()],
smart_detection: true,
exclude_patterns: vec!["target".to_string(), "node_modules".to_string()],
},
}
}
fn load_config(config_path: &str) -> Result<AgentConfig> {
let mut config = if std::path::Path::new(config_path).exists() {
let content = std::fs::read_to_string(config_path)?;
let config: AgentConfig = serde_json::from_str(&content)?;
config
} else {
warn!("配置文件 {} 不存在,使用默认配置", config_path);
get_default_config()
};
if let Ok(api_key) = env::var("DEEPSEEK_API_KEY") {
if !api_key.is_empty() && api_key != "your-api-key-here" {
config.deepseek.api_key = api_key;
info!("使用环境变量中的DeepSeek API密钥");
}
}
if config.deepseek.api_key == "your-api-key-here" || config.deepseek.api_key.is_empty() {
error!("DeepSeek API密钥未设置或无效!");
error!("请设置环境变量 DEEPSEEK_API_KEY 或编辑配置文件 {}", config_path);
return Err(anyhow::anyhow!("API密钥未设置"));
}
Ok(config)
}
fn save_config(config: &AgentConfig, config_path: &str) -> Result<()> {
let content = serde_json::to_string_pretty(config)?;
std::fs::write(config_path, content)?;
info!("配置文件已保存到: {}", config_path);
Ok(())
}
async fn show_loading_animation(message: &str) {
let spinner_chars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
let mut index = 0;
print!("\r{} {}", spinner_chars[index], message);
std::io::Write::flush(&mut std::io::stdout()).unwrap();
loop {
tokio::time::sleep(Duration::from_millis(100)).await;
index = (index + 1) % spinner_chars.len();
print!("\r{} {}", spinner_chars[index], message);
std::io::Write::flush(&mut std::io::stdout()).unwrap();
}
}
async fn init_connection_pool() -> Result<ConnectionPool> {
let pool = ConnectionPool::new();
Ok(pool)
}
async fn start_chat(config_path: &str) -> Result<()> {
let config = load_config(config_path)?;
let connection_pool = Arc::new(init_connection_pool().await?);
let mut agent = McpAgent::with_connection_pool(config, connection_pool.clone()).await?;
println!("🚀 正在启动Alou智能助手...");
agent.initialize().await?;
println!("\n✨ Alou智能助手已就绪!");
println!("💡 输入 'exit' 或 'quit' 退出程序");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
loop {
print!("👤 我: ");
std::io::Write::flush(&mut std::io::stdout())?;
let mut input = String::new();
std::io::stdin().read_line(&mut input)?;
let input = input.trim();
if input.is_empty() {
continue;
}
if input == "exit" || input == "quit" {
println!("\n👋 感谢使用Alou智能助手,再见!");
break;
}
let loading_handle = tokio::spawn(async {
show_loading_animation("🤔 正在思考,请稍候...").await;
});
let result = agent.process_input(input).await;
loading_handle.abort();
print!("\r{}", " ".repeat(50));
print!("\r");
std::io::Write::flush(&mut std::io::stdout()).unwrap();
match result {
Ok(response) => {
println!("🧠 Alou: {}", response);
}
Err(e) => {
error!("处理输入时出错: {}", e);
}
}
}
if let Err(e) = connection_pool.close_all_connections().await {
debug!("关闭连接时出现错误: {}", e);
}
Ok(())
}
async fn test_agent(config_path: &str, message: &str) -> Result<()> {
let config = load_config(config_path)?;
let connection_pool = Arc::new(init_connection_pool().await?);
let mut agent = McpAgent::with_connection_pool(config, connection_pool.clone()).await?;
agent.initialize().await?;
info!("测试消息: {}", message);
let loading_handle = tokio::spawn(async {
show_loading_animation("🤔 正在思考,请稍候...").await;
});
let result = agent.process_input(message).await;
loading_handle.abort();
print!("\r{}", " ".repeat(50));
print!("\r");
std::io::Write::flush(&mut std::io::stdout()).unwrap();
match result {
Ok(response) => {
println!("智能体响应: {}", response);
}
Err(e) => {
error!("测试失败: {}", e);
return Err(e.into());
}
}
Ok(())
}
fn init_config(output_path: &str) -> Result<()> {
let config = get_default_config();
save_config(&config, output_path)?;
println!("配置文件已创建: {}", output_path);
println!("请编辑配置文件,设置正确的DeepSeek API密钥和其他参数");
Ok(())
}
#[tokio::main]
async fn main() -> Result<()> {
if let Err(e) = dotenv::dotenv() {
warn!("无法加载 .env 文件: {}", e);
}
let cli = Cli::parse();
if cli.clean {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::ERROR)
.with_target(false)
.with_ansi(false)
.init();
} else if cli.quiet {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::ERROR)
.with_target(false)
.with_ansi(false)
.init();
} else {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::WARN)
.with_target(false)
.with_ansi(true)
.with_thread_ids(false)
.with_thread_names(false)
.with_file(false)
.with_line_number(false)
.init();
}
match cli.command {
Commands::Chat { config } => {
start_chat(&config).await?;
}
Commands::Test { message, config } => {
test_agent(&config, &message).await?;
}
Commands::Init { output } => {
init_config(&output)?;
}
}
Ok(())
}