/** * AIA 智能问答模块 - 智能体控制器 * @module aia/controllers/agentController * * API 端点: * - GET /api/v1/aia/agents 获取智能体列表 * - GET /api/v1/aia/agents/:id 获取智能体详情 * - POST /api/v1/aia/intent/route 意图路由 */ import type { FastifyRequest, FastifyReply } from 'fastify'; import { logger } from '../../../common/logging/index.js'; import * as agentService from '../services/agentService.js'; import type { AgentStage, IntentRouteRequest } from '../types/index.js'; /** * 从 JWT Token 获取用户 ID */ function getUserId(request: FastifyRequest): string { const userId = (request as any).user?.userId; if (!userId) { throw new Error('User not authenticated'); } return userId; } // ==================== 智能体管理 ==================== /** * 获取智能体列表 * GET /api/v1/aia/agents */ export async function getAgents( request: FastifyRequest<{ Querystring: { stage?: AgentStage }; }>, reply: FastifyReply ) { try { const { stage } = request.query; logger.info('[AIA:Controller] 获取智能体列表', { stage }); const agents = await agentService.getAgents(stage); return reply.send({ code: 0, data: { agents }, }); } catch (error) { logger.error('[AIA:Controller] 获取智能体列表失败', { error }); return reply.status(500).send({ code: -1, error: { code: 'INTERNAL_ERROR', message: error instanceof Error ? error.message : '服务器内部错误', }, }); } } /** * 获取智能体详情 * GET /api/v1/aia/agents/:id */ export async function getAgentById( request: FastifyRequest<{ Params: { id: string }; }>, reply: FastifyReply ) { try { const { id } = request.params; logger.info('[AIA:Controller] 获取智能体详情', { agentId: id }); const agent = await agentService.getAgentById(id); if (!agent) { return reply.status(404).send({ code: -1, error: { code: 'NOT_FOUND', message: '智能体不存在', }, }); } return reply.send({ code: 0, data: agent, }); } catch (error) { logger.error('[AIA:Controller] 获取智能体详情失败', { error }); return reply.status(500).send({ code: -1, error: { code: 'INTERNAL_ERROR', message: error instanceof Error ? error.message : '服务器内部错误', }, }); } } // ==================== 意图路由 ==================== /** * 意图路由 * POST /api/v1/aia/intent/route * * 根据用户输入识别意图,推荐合适的智能体 */ export async function routeIntent( request: FastifyRequest<{ Body: IntentRouteRequest; }>, reply: FastifyReply ) { try { const userId = getUserId(request); const { query } = request.body; logger.info('[AIA:Controller] 意图路由', { userId, queryLength: query?.length }); if (!query || query.trim().length < 2) { return reply.status(400).send({ code: -1, error: { code: 'VALIDATION_ERROR', message: '查询内容至少需要2个字符', }, }); } // 简单的关键词匹配意图识别 // TODO: 后续可以使用 LLM 或专门的意图识别模型 const intent = await matchIntent(query); return reply.send({ code: 0, data: intent, }); } catch (error) { logger.error('[AIA:Controller] 意图路由失败', { error }); return reply.status(500).send({ code: -1, error: { code: 'INTERNAL_ERROR', message: error instanceof Error ? error.message : '服务器内部错误', }, }); } } /** * 简单的关键词匹配意图识别 */ async function matchIntent(query: string): Promise<{ agentId: string; agentName: string; confidence: number; prefillPrompt: string; }> { const queryLower = query.toLowerCase(); // 关键词映射 const intentPatterns = [ { patterns: ['研究设计', '方案设计', 'rct', '队列', '病例对照', '样本量'], agentId: 'research-design', agentName: '科研设计小助手', }, { patterns: ['文献', '检索', 'pubmed', '筛选', 'meta', '系统综述'], agentId: 'literature-assistant', agentName: '文献检索助手', }, { patterns: ['数据', '清洗', '缺失值', '异常值', '整理'], agentId: 'data-assistant', agentName: '数据管理助手', }, { patterns: ['统计', '分析', 'spss', 't检验', '卡方', '回归', 'p值'], agentId: 'stat-assistant', agentName: '统计分析小助手', }, { patterns: ['论文', '写作', '润色', '翻译', 'sci', '摘要', 'introduction'], agentId: 'writing-assistant', agentName: '论文撰写助手', }, { patterns: ['投稿', '期刊', '审稿', '发表', 'if', '影响因子'], agentId: 'publish-assistant', agentName: '投稿指导助手', }, ]; // 计算匹配度 let bestMatch = { agentId: 'research-design', agentName: '科研设计小助手', confidence: 0.3, // 默认较低置信度 matchCount: 0, }; for (const pattern of intentPatterns) { let matchCount = 0; for (const keyword of pattern.patterns) { if (queryLower.includes(keyword)) { matchCount++; } } if (matchCount > bestMatch.matchCount) { bestMatch = { agentId: pattern.agentId, agentName: pattern.agentName, confidence: Math.min(0.5 + matchCount * 0.15, 0.95), matchCount, }; } } return { agentId: bestMatch.agentId, agentName: bestMatch.agentName, confidence: bestMatch.confidence, prefillPrompt: query, // 直接使用原始查询作为预填充 }; }