fix(ai): Agent chat handler 精确选择 FC-capable provider + 环境变量适配
- chat_handler: 使用 get_provider("claude") 精确获取,避免 resolve fallback 到 Ollama
- ProviderRegistry: 新增 get_provider() 方法(无 health check,无 fallback)
- orchestrator: 从 ANTHROPIC_DEFAULT_SONNET_MODEL 读取模型名,兼容智谱代理
- erp-server: Claude provider 注册优先读 ANTHROPIC_AUTH_TOKEN + ANTHROPIC_BASE_URL
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -51,7 +51,8 @@ impl AgentOrchestrator {
|
|||||||
messages.clone(),
|
messages.clone(),
|
||||||
tools.clone(),
|
tools.clone(),
|
||||||
system_prompt,
|
system_prompt,
|
||||||
"auto",
|
&std::env::var("ANTHROPIC_DEFAULT_SONNET_MODEL")
|
||||||
|
.unwrap_or_else(|_| "claude-sonnet-4-6".to_string()),
|
||||||
0.7,
|
0.7,
|
||||||
2048,
|
2048,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -128,14 +128,16 @@ where
|
|||||||
tool_call_id: None,
|
tool_call_id: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 解析 Provider
|
// 解析 Provider — Agent 需要 Function Calling,精确获取 Claude/OpenAI
|
||||||
let resolved = ai_state
|
let provider_arc = ai_state
|
||||||
.provider_registry
|
.provider_registry
|
||||||
.resolve("auto")
|
.get_provider("claude")
|
||||||
.await
|
.or_else(|| ai_state.provider_registry.get_provider("openai"))
|
||||||
.map_err(|e| {
|
.ok_or_else(|| {
|
||||||
tracing::error!(error = %e, "AI provider resolve failed");
|
tracing::error!("No FC-capable provider found (need claude or openai)");
|
||||||
erp_core::error::AppError::Internal("AI 服务暂时不可用,请稍后再试".into())
|
erp_core::error::AppError::Internal(
|
||||||
|
"AI Agent 暂时不可用,需要 Claude 或 OpenAI 提供商".into(),
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// 构建 ToolRegistry — Phase 0 只有 query_patient_vitals
|
// 构建 ToolRegistry — Phase 0 只有 query_patient_vitals
|
||||||
@@ -159,7 +161,6 @@ where
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 执行 Agent ReAct 循环
|
// 执行 Agent ReAct 循环
|
||||||
let provider_arc = resolved.into_arc();
|
|
||||||
let orchestrator = AgentOrchestrator::new(provider_arc, std::sync::Arc::new(registry));
|
let orchestrator = AgentOrchestrator::new(provider_arc, std::sync::Arc::new(registry));
|
||||||
let result = orchestrator
|
let result = orchestrator
|
||||||
.run(SYSTEM_PROMPT, &mut messages, &tool_ctx)
|
.run(SYSTEM_PROMPT, &mut messages, &tool_ctx)
|
||||||
|
|||||||
@@ -105,6 +105,11 @@ impl ProviderRegistry {
|
|||||||
pub fn provider_names(&self) -> Vec<String> {
|
pub fn provider_names(&self) -> Vec<String> {
|
||||||
self.entries.iter().map(|e| e.key().to_string()).collect()
|
self.entries.iter().map(|e| e.key().to_string()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 精确获取指定名称的 provider(不做 health check,不做 fallback)
|
||||||
|
pub fn get_provider(&self, name: &str) -> Option<Arc<dyn AiProvider>> {
|
||||||
|
self.entries.get(name).map(|e| e.provider.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ResolvedProvider {
|
pub struct ResolvedProvider {
|
||||||
|
|||||||
@@ -477,13 +477,21 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
// 构建多 Provider 注册表
|
// 构建多 Provider 注册表
|
||||||
let registry = std::sync::Arc::new(erp_ai::provider::registry::ProviderRegistry::new());
|
let registry = std::sync::Arc::new(erp_ai::provider::registry::ProviderRegistry::new());
|
||||||
|
|
||||||
// 始终注册默认 Claude provider(兼容旧配置)
|
// 始终注册默认 Claude provider — 优先用环境变量
|
||||||
{
|
{
|
||||||
let mut claude =
|
let api_key = if !config.ai.api_key.is_empty() {
|
||||||
erp_ai::provider::claude::ClaudeProvider::new(config.ai.api_key.clone());
|
config.ai.api_key.clone()
|
||||||
if let Some(ref base_url) = config.ai.base_url {
|
} else {
|
||||||
claude = claude.with_base_url(base_url.clone());
|
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));
|
registry.register("claude".to_string(), std::sync::Arc::new(claude));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user