package com.company.agentfoundation.agent; import com.company.agentfoundation.domain.ChatRequest; import com.company.agentfoundation.domain.ChatResponse; import com.company.agentfoundation.directory.EmployeeAccountMapping; import com.company.agentfoundation.directory.EmployeeDirectoryResponse; import com.company.agentfoundation.directory.EmployeeDirectoryService; import com.company.agentfoundation.security.AgentContext; import com.company.agentfoundation.service.ChatService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.List; @Service public class AgentOrchestratorService { private static final Logger log = LoggerFactory.getLogger(AgentOrchestratorService.class); private final IntentRecognitionService intentRecognitionService; private final OperationReportService operationReportService; private final ChatService chatService; private final EmployeeDirectoryService employeeDirectoryService; public AgentOrchestratorService( IntentRecognitionService intentRecognitionService, OperationReportService operationReportService, ChatService chatService, EmployeeDirectoryService employeeDirectoryService ) { this.intentRecognitionService = intentRecognitionService; this.operationReportService = operationReportService; this.chatService = chatService; this.employeeDirectoryService = employeeDirectoryService; } public String handleMessage(String agentId, String fromUser, String userText) { AgentIntent intent = intentRecognitionService.recognize(agentId, userText); log.info("agent orchestrator route fromUser={} intent={} group={}", fromUser, intent.code(), intent.mcpGroup()); if (intent != AgentIntent.GENERAL_CHAT) { return appendDirectoryInfo( toWecomText(operationReportService.handle(intent, AgentRequestContext.of(agentId, fromUser, userText))), fromUser ); } ChatRequest request = new ChatRequest( null, List.of( new ChatRequest.Message("system", "You are an enterprise CRM assistant. Reply concisely in Chinese."), new ChatRequest.Message("user", userText) ), 0.3 ); ChatResponse response = chatService.chat(new AgentContext(agentId), request); return appendDirectoryInfo(response.content(), fromUser); } private String toWecomText(AgentCapabilityResponse response) { return """ \u3010%s\u3011 %s \u80fd\u529b\u5206\u7ec4\uff1a%s \u80fd\u529b\u7f16\u7801\uff1a%s \u6570\u636e\u6e90\uff1a%s \u72b6\u6001\uff1a%s """.formatted( response.title(), response.message(), response.mcpGroup(), response.intentCode(), String.join("\u3001", response.dataSources()), response.status() ).trim(); } private String appendDirectoryInfo(String reply, String wecomUserId) { String text = reply == null || reply.isBlank() ? "" : reply.trim(); return text + "\n\n" + buildDirectoryInfo(wecomUserId); } private String buildDirectoryInfo(String wecomUserId) { if (wecomUserId == null || wecomUserId.isBlank()) { return "【身份信息】\n企业微信账号:未获取\n学邦账号:未匹配\n部门:未匹配\n身份:未匹配"; } try { return employeeDirectoryService.findByWecomUserId(wecomUserId) .map(this::formatDirectoryInfo) .orElse(""" 【身份信息】 企业微信账号:%s 学邦账号:未匹配 部门:未匹配 身份:未匹配 """.formatted(wecomUserId).trim()); } catch (Exception e) { log.warn("employee directory lookup failed wecomUserId={} err={}", wecomUserId, e.getMessage()); return """ 【身份信息】 企业微信账号:%s 学邦账号:查询失败 部门:查询失败 身份:查询失败 """.formatted(wecomUserId).trim(); } } private String formatDirectoryInfo(EmployeeDirectoryResponse response) { EmployeeAccountMapping account = response.account(); String department = firstNotBlank(account.xuebangPrimaryOrg(), account.wecomDepartmentPath(), "未匹配"); String xuebangAccount = firstNotBlank(account.xuebangAccount(), "未匹配"); String roleName = firstNotBlank(account.primaryRoleName(), "未匹配"); String roleCode = firstNotBlank(account.primaryRoleCode(), "未匹配"); String scopeType = firstNotBlank(account.dataScopeType(), "未匹配"); String roleLevel = account.roleLevel() == null ? "未匹配" : account.roleLevel().toString(); return """ 【身份信息】 企业微信账号:%s 学邦账号:%s 部门:%s 身份:%s(%s,级别%s,范围%s) """.formatted(account.wecomUserId(), xuebangAccount, department, roleName, roleCode, roleLevel, scopeType).trim(); } private String firstNotBlank(String... values) { for (String value : values) { if (value != null && !value.isBlank()) { return value; } } return ""; } }