use std::collections::HashMap;
use std::sync::Arc;
use std::path::PathBuf;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use tokio::sync::RwLock;
use uuid::Uuid;
use tracing::{info, warn};
use crate::connection_pool::{ConnectionPool, McpServerConfig};
use crate::workspace_context::WorkspaceContext;
use crate::protocol::Request;
use crate::error::Error;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentConfig {
pub deepseek: DeepSeekConfig,
pub behavior: BehaviorConfig,
pub workspace: WorkspaceConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeepSeekConfig {
pub base_url: String,
pub api_key: String,
pub model: String,
pub max_tokens: u32,
pub temperature: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BehaviorConfig {
pub max_retries: u32,
pub timeout_seconds: u64,
pub verbose_logging: bool,
pub tool_strategy: ToolStrategy,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ToolStrategy {
Auto,
Priority(Vec<String>),
Parallel,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceConfig {
pub directories: Vec<String>,
pub smart_detection: bool,
pub exclude_patterns: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AgentState {
Idle,
Thinking,
ExecutingTool(String),
WaitingForAPI,
Error(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentMessage {
pub id: String,
pub message_type: MessageType,
pub content: String,
pub timestamp: u64,
pub tool_calls: Vec<ToolCall>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MessageType {
UserInput,
AgentResponse,
ToolCall,
ToolResult,
System,
Error,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCall {
pub name: String,
pub arguments: HashMap<String, serde_json::Value>,
pub call_id: String,
pub status: ToolCallStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ToolCallStatus {
Pending,
Executing,
Success,
Failed(String),
}
#[derive(Debug)]
pub struct AgentContext {
pub state: AgentState,
pub message_history: Vec<AgentMessage>,
pub workspace_context: Box<dyn WorkspaceContext + Send + Sync>,
pub available_tools: HashMap<String, ToolInfo>,
pub current_task: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolInfo {
pub name: String,
pub description: String,
pub input_schema: serde_json::Value,
pub server: String,
}
#[async_trait]
pub trait Agent: Send + Sync {
async fn initialize(&mut self) -> Result<(), Error>;
async fn process_input(&mut self, input: &str) -> Result<String, Error>;
async fn execute_tool(&mut self, tool_call: &ToolCall) -> Result<serde_json::Value, Error>;
fn get_state(&self) -> &AgentState;
fn get_context(&self) -> &AgentContext;
async fn reset(&mut self) -> Result<(), Error>;
}
pub struct McpAgent {
#[allow(dead_code)]
config: AgentConfig,
context: Arc<RwLock<AgentContext>>,
connection_pool: Arc<ConnectionPool>,
deepseek_client: Arc<DeepSeekClient>,
}
pub struct DeepSeekClient {
client: reqwest::Client,
config: DeepSeekConfig,
}
impl DeepSeekClient {
pub fn new(config: DeepSeekConfig) -> Self {
let client = reqwest::Client::new();
Self { client, config }
}
pub async fn chat(&self, messages: Vec<ChatMessage>) -> Result<ChatResponse, Error> {
let request = ChatRequest {
model: self.config.model.clone(),
messages,
max_tokens: Some(self.config.max_tokens),
temperature: Some(self.config.temperature),
stream: Some(false),
};
let response = self.client
.post(&format!("{}/chat/completions", self.config.base_url))
.header("Authorization", format!("Bearer {}", self.config.api_key))
.header("Content-Type", "application/json")
.json(&request)
.send()
.await
.map_err(|e| Error::Other(e.to_string()))?;
if !response.status().is_success() {
let error_text = response.text().await.unwrap_or_default();
return Err(Error::Other(format!("API请求失败: {}", error_text)));
}
let chat_response: ChatResponse = response
.json()
.await
.map_err(|e| Error::Other(e.to_string()))?;
Ok(chat_response)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatMessage {
pub role: String,
pub content: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<Vec<ToolCallMessage>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCallMessage {
pub id: Option<String>,
#[serde(rename = "type")]
pub call_type: String,
pub function: FunctionCall,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionCall {
pub name: String,
pub arguments: String,
}
#[derive(Debug, Serialize)]
struct ChatRequest {
model: String,
messages: Vec<ChatMessage>,
max_tokens: Option<u32>,
temperature: Option<f32>,
stream: Option<bool>,
}
#[derive(Debug, Deserialize)]
pub struct ChatResponse {
pub choices: Vec<Choice>,
pub usage: Option<Usage>,
}
#[derive(Debug, Deserialize)]
pub struct Choice {
pub message: ChatMessage,
pub finish_reason: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct Usage {
pub prompt_tokens: u32,
pub completion_tokens: u32,
pub total_tokens: u32,
}
impl McpAgent {
pub async fn new(config: AgentConfig) -> Result<Self, Error> {
Self::with_connection_pool(config, Arc::new(ConnectionPool::new())).await
}
pub async fn with_connection_pool(config: AgentConfig, connection_pool: Arc<ConnectionPool>) -> Result<Self, Error> {
let workspace_context = if config.workspace.smart_detection {
crate::workspace_context::WorkspaceContextFactory::create_smart()
} else {
let directories: Vec<PathBuf> = config.workspace.directories
.iter()
.map(|s| PathBuf::from(s))
.collect();
crate::workspace_context::WorkspaceContextFactory::create_custom(directories)
};
let deepseek_client = Arc::new(DeepSeekClient::new(config.deepseek.clone()));
let context = Arc::new(RwLock::new(AgentContext {
state: AgentState::Idle,
message_history: Vec::new(),
workspace_context,
available_tools: HashMap::new(),
current_task: None,
}));
Ok(Self {
config,
context,
connection_pool,
deepseek_client,
})
}
async fn register_server_configs(&mut self) -> Result<(), Error> {
if std::path::Path::new("mcp.json").exists() {
let content = std::fs::read_to_string("mcp.json")?;
let mcp_config: serde_json::Value = serde_json::from_str(&content)?;
if let Some(servers) = mcp_config.get("mcpServers").and_then(|s| s.as_object()) {
for (name, config) in servers {
let server_config = McpServerConfig {
command: config.get("command")
.and_then(|c| c.as_str())
.unwrap_or("")
.to_string(),
args: config.get("args")
.and_then(|a| a.as_array())
.map(|arr| arr.iter().filter_map(|v| v.as_str()).map(|s| s.to_string()).collect())
.unwrap_or_default(),
env: Some(config.get("env")
.and_then(|e| e.as_object())
.map(|obj| obj.iter().filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string()))).collect::<std::collections::HashMap<String, String>>())
.unwrap_or_default()),
directory: config.get("directory")
.and_then(|d| d.as_str())
.map(|s| s.to_string()),
};
self.connection_pool.register_server(name.clone(), server_config).await;
info!("已注册MCP服务器配置: {}", name);
}
}
} else {
warn!("未找到mcp.json文件,将使用默认配置");
}
Ok(())
}
async fn start_background_loading(&self) {
let connection_pool = self.connection_pool.clone();
let context = self.context.clone();
tokio::spawn(async move {
{
let ctx = context.read().await;
if !ctx.available_tools.is_empty() {
return;
}
}
let servers = connection_pool.list_registered_servers().await;
let mut server_tool_counts = std::collections::HashMap::new();
for server_name in &servers {
if let Ok(connection) = connection_pool.get_connection(server_name).await {
let client = connection.lock().await;
match client.request("tools/list", None).await {
Ok(result) => {
if let Some(tools) = result.get("tools").and_then(|t| t.as_array()) {
let tool_count = tools.len();
server_tool_counts.insert(server_name.clone(), tool_count);
{
let mut ctx = context.write().await;
for tool in tools {
if let (Some(name), Some(description), Some(input_schema)) = (
tool.get("name").and_then(|n| n.as_str()),
tool.get("description").and_then(|d| d.as_str()),
tool.get("inputSchema")
) {
let tool_info = ToolInfo {
name: name.to_string(),
description: description.to_string(),
input_schema: input_schema.clone(),
server: server_name.clone(),
};
ctx.available_tools.insert(name.to_string(), tool_info);
}
}
}
}
}
Err(_) => {
}
}
}
}
});
}
async fn discover_tools_silent(&mut self) -> Result<(), Error> {
let mut context = self.context.write().await;
context.available_tools.clear();
let servers = self.connection_pool.list_registered_servers().await;
let mut server_tool_counts = std::collections::HashMap::new();
for server_name in &servers {
if let Ok(connection) = self.connection_pool.get_connection(server_name).await {
let client = connection.lock().await;
match client.request("tools/list", None).await {
Ok(result) => {
if let Some(tools) = result.get("tools").and_then(|t| t.as_array()) {
let tool_count = tools.len();
server_tool_counts.insert(server_name.clone(), tool_count);
for tool in tools {
if let (Some(name), Some(description), Some(input_schema)) = (
tool.get("name").and_then(|n| n.as_str()),
tool.get("description").and_then(|d| d.as_str()),
tool.get("inputSchema")
) {
let tool_info = ToolInfo {
name: name.to_string(),
description: description.to_string(),
input_schema: input_schema.clone(),
server: server_name.clone(),
};
context.available_tools.insert(name.to_string(), tool_info);
}
}
}
}
Err(_) => {
}
}
}
}
Ok(())
}
pub async fn get_workspace_info(&self) -> Vec<std::path::PathBuf> {
let context = self.context.read().await;
context.workspace_context.get_directories()
}
async fn build_system_prompt(&self) -> String {
let context = self.context.read().await;
let workspace_dirs = context.workspace_context.get_directories();
let workspace_root = workspace_dirs
.first()
.map(|d| d.to_string_lossy().to_string())
.unwrap_or_else(|| ".".to_string());
let base_prompt = crate::prompts::get_mcp_system_prompt(&workspace_root);
let tools_info = context.available_tools
.values()
.map(|tool| format!("- {}: {}", tool.name, tool.description))
.collect::<Vec<_>>()
.join("\n");
format!(
"{}\n\n# 当前可用工具\n{}\n\n# 智能体状态\n当前状态: {:?}\n当前任务: {}",
base_prompt,
tools_info,
context.state,
context.current_task.as_deref().unwrap_or("无")
)
}
}
#[async_trait]
impl Agent for McpAgent {
async fn initialize(&mut self) -> Result<(), Error> {
tracing::info!("初始化智能体...");
self.register_server_configs().await?;
{
let mut context = self.context.write().await;
context.state = AgentState::Idle;
}
tracing::info!("智能体初始化完成");
self.start_background_loading().await;
Ok(())
}
async fn process_input(&mut self, input: &str) -> Result<String, Error> {
tracing::info!("处理用户输入: {}", input);
{
let context = self.context.read().await;
if context.available_tools.is_empty() {
drop(context); let start_time = std::time::Instant::now();
while start_time.elapsed().as_secs() < 5 {
let context = self.context.read().await;
if !context.available_tools.is_empty() {
drop(context);
break;
}
drop(context);
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
}
{
let context = self.context.read().await;
if context.available_tools.is_empty() {
drop(context);
self.discover_tools_silent().await?;
}
}
}
}
{
let mut context = self.context.write().await;
context.state = AgentState::Thinking;
context.current_task = Some(input.to_string());
let user_message = AgentMessage {
id: Uuid::new_v4().to_string(),
message_type: MessageType::UserInput,
content: input.to_string(),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
tool_calls: Vec::new(),
};
context.message_history.push(user_message);
}
let mut messages = vec![
ChatMessage {
role: "system".to_string(),
content: self.build_system_prompt().await,
tool_calls: None,
}
];
{
let context = self.context.read().await;
for msg in &context.message_history {
let role = match msg.message_type {
MessageType::UserInput => "user",
MessageType::AgentResponse => "assistant",
_ => continue,
};
messages.push(ChatMessage {
role: role.to_string(),
content: msg.content.clone(),
tool_calls: None,
});
}
}
{
let mut context = self.context.write().await;
context.state = AgentState::WaitingForAPI;
}
let response = self.deepseek_client.chat(messages).await?;
if let Some(choice) = response.choices.first() {
let response_content = choice.message.content.clone();
tracing::debug!("DeepSeek响应内容: {}", response_content);
tracing::debug!("是否有工具调用: {:?}", choice.message.tool_calls);
if let Some(tool_calls) = &choice.message.tool_calls {
if !tool_calls.is_empty() {
let mut tool_results = Vec::new();
let mut executed_tool_calls = Vec::new();
for tool_call in tool_calls {
let name = &tool_call.function.name;
let arguments = &tool_call.function.arguments;
let args: HashMap<String, serde_json::Value> =
serde_json::from_str(arguments).unwrap_or_default();
let tool_call_info = ToolCall {
name: name.clone(),
arguments: args,
call_id: tool_call.id.clone().unwrap_or_default(),
status: ToolCallStatus::Pending,
};
match self.execute_tool(&tool_call_info).await {
Ok(result) => {
tool_results.push(format!("工具 {} 执行成功: {}", name, result));
executed_tool_calls.push(ToolCall {
name: name.clone(),
arguments: tool_call_info.arguments,
call_id: tool_call_info.call_id,
status: ToolCallStatus::Success,
});
}
Err(e) => {
tool_results.push(format!("工具 {} 执行失败: {}", name, e));
executed_tool_calls.push(ToolCall {
name: name.clone(),
arguments: tool_call_info.arguments,
call_id: tool_call_info.call_id,
status: ToolCallStatus::Failed(e.to_string()),
});
}
}
}
{
let mut context = self.context.write().await;
context.state = AgentState::Idle;
let tool_result_message = AgentMessage {
id: Uuid::new_v4().to_string(),
message_type: MessageType::ToolResult,
content: tool_results.join("\n"),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
tool_calls: executed_tool_calls,
};
context.message_history.push(tool_result_message);
}
return Ok(tool_results.join("\n"));
}
}
{
let mut context = self.context.write().await;
context.state = AgentState::Idle;
let agent_message = AgentMessage {
id: Uuid::new_v4().to_string(),
message_type: MessageType::AgentResponse,
content: response_content.clone(),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
tool_calls: Vec::new(),
};
context.message_history.push(agent_message);
}
Ok(response_content)
} else {
Err(Error::Other("API响应中没有选择".to_string()))
}
}
async fn execute_tool(&mut self, tool_call: &ToolCall) -> Result<serde_json::Value, Error> {
tracing::info!("执行工具: {}", tool_call.name);
{
let mut context = self.context.write().await;
context.state = AgentState::ExecutingTool(tool_call.name.clone());
}
let tool_info = {
let context = self.context.read().await;
context.available_tools.get(&tool_call.name)
.ok_or_else(|| Error::Other(format!("工具 {} 不存在", tool_call.name)))?
.clone()
};
let connection = self.connection_pool.get_connection(&tool_info.server).await?;
let client = connection.lock().await;
let request = Request {
jsonrpc: "2.0".to_string(),
id: crate::protocol::RequestId::String(tool_call.call_id.clone()),
method: "tools/call".to_string(),
params: Some(serde_json::json!({
"name": tool_call.name,
"arguments": tool_call.arguments
})),
};
let response = client.request(&request.method, request.params).await?;
{
let mut context = self.context.write().await;
context.state = AgentState::Idle;
}
Ok(response)
}
fn get_state(&self) -> &AgentState {
&AgentState::Idle }
fn get_context(&self) -> &AgentContext {
unimplemented!("需要重新设计状态访问方式 - 使用异步方法获取上下文")
}
async fn reset(&mut self) -> Result<(), Error> {
let mut context = self.context.write().await;
context.state = AgentState::Idle;
context.message_history.clear();
context.current_task = None;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_agent_config_serialization() {
let config = AgentConfig {
deepseek: DeepSeekConfig {
base_url: "https://api.deepseek.com".to_string(),
api_key: "test_key".to_string(),
model: "deepseek-chat".to_string(),
max_tokens: 1000,
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()],
},
};
let json = serde_json::to_string(&config).unwrap();
let deserialized: AgentConfig = serde_json::from_str(&json).unwrap();
assert_eq!(config.deepseek.model, deserialized.deepseek.model);
}
}