diff --git a/crates/erp-ai/src/agent/orchestrator.rs b/crates/erp-ai/src/agent/orchestrator.rs index 4c4a860..1b05d50 100644 --- a/crates/erp-ai/src/agent/orchestrator.rs +++ b/crates/erp-ai/src/agent/orchestrator.rs @@ -51,7 +51,8 @@ impl AgentOrchestrator { messages.clone(), tools.clone(), system_prompt, - "auto", + &std::env::var("ANTHROPIC_DEFAULT_SONNET_MODEL") + .unwrap_or_else(|_| "claude-sonnet-4-6".to_string()), 0.7, 2048, ) diff --git a/crates/erp-ai/src/handler/chat_handler.rs b/crates/erp-ai/src/handler/chat_handler.rs index 37de2b9..9787506 100644 --- a/crates/erp-ai/src/handler/chat_handler.rs +++ b/crates/erp-ai/src/handler/chat_handler.rs @@ -128,14 +128,16 @@ where tool_call_id: None, }); - // 解析 Provider - let resolved = ai_state + // 解析 Provider — Agent 需要 Function Calling,精确获取 Claude/OpenAI + let provider_arc = ai_state .provider_registry - .resolve("auto") - .await - .map_err(|e| { - tracing::error!(error = %e, "AI provider resolve failed"); - erp_core::error::AppError::Internal("AI 服务暂时不可用,请稍后再试".into()) + .get_provider("claude") + .or_else(|| ai_state.provider_registry.get_provider("openai")) + .ok_or_else(|| { + tracing::error!("No FC-capable provider found (need claude or openai)"); + erp_core::error::AppError::Internal( + "AI Agent 暂时不可用,需要 Claude 或 OpenAI 提供商".into(), + ) })?; // 构建 ToolRegistry — Phase 0 只有 query_patient_vitals @@ -159,7 +161,6 @@ where ); // 执行 Agent ReAct 循环 - let provider_arc = resolved.into_arc(); let orchestrator = AgentOrchestrator::new(provider_arc, std::sync::Arc::new(registry)); let result = orchestrator .run(SYSTEM_PROMPT, &mut messages, &tool_ctx) diff --git a/crates/erp-ai/src/provider/registry.rs b/crates/erp-ai/src/provider/registry.rs index 2f579aa..4802521 100644 --- a/crates/erp-ai/src/provider/registry.rs +++ b/crates/erp-ai/src/provider/registry.rs @@ -105,6 +105,11 @@ impl ProviderRegistry { pub fn provider_names(&self) -> Vec { self.entries.iter().map(|e| e.key().to_string()).collect() } + + /// 精确获取指定名称的 provider(不做 health check,不做 fallback) + pub fn get_provider(&self, name: &str) -> Option> { + self.entries.get(name).map(|e| e.provider.clone()) + } } pub struct ResolvedProvider { diff --git a/crates/erp-server/src/main.rs b/crates/erp-server/src/main.rs index 400f8e4..a9be555 100644 --- a/crates/erp-server/src/main.rs +++ b/crates/erp-server/src/main.rs @@ -477,13 +477,21 @@ async fn main() -> anyhow::Result<()> { // 构建多 Provider 注册表 let registry = std::sync::Arc::new(erp_ai::provider::registry::ProviderRegistry::new()); - // 始终注册默认 Claude provider(兼容旧配置) + // 始终注册默认 Claude provider — 优先用环境变量 { - let mut claude = - erp_ai::provider::claude::ClaudeProvider::new(config.ai.api_key.clone()); - if let Some(ref base_url) = config.ai.base_url { - claude = claude.with_base_url(base_url.clone()); - } + let api_key = if !config.ai.api_key.is_empty() { + config.ai.api_key.clone() + } else { + std::env::var("ANTHROPIC_AUTH_TOKEN") + .or_else(|_| std::env::var("ANTHROPIC_API_KEY")) + .unwrap_or_default() + }; + let base_url = std::env::var("ANTHROPIC_BASE_URL") + .ok() + .or_else(|| config.ai.base_url.clone()) + .unwrap_or_else(|| "https://api.anthropic.com".to_string()); + let claude = + erp_ai::provider::claude::ClaudeProvider::new(api_key).with_base_url(base_url); registry.register("claude".to_string(), std::sync::Arc::new(claude)); }