feat: Day 10-11 - Agent Configuration System completed
Backend: - Create agents.yaml config file with 12 agents definition - Create Prompt templates for topic-evaluation agent - Implement agentService.ts for loading and managing agent configs - Create agentController.ts with CRUD operations - Create agent routes (GET /agents, /agents/:id, etc.) - Register agent routes in main server Frontend: - Create agentApi.ts service module - Update AgentChatPage to dynamically load agent config from API - Add loading state and error handling - Display agent details (description, category, model) Build: Both frontend and backend build successfully
This commit is contained in:
215
backend/src/controllers/agentController.ts
Normal file
215
backend/src/controllers/agentController.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import { agentService } from '../services/agentService.js';
|
||||
|
||||
interface AgentParams {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface RenderPromptBody {
|
||||
projectBackground?: string;
|
||||
userInput: string;
|
||||
knowledgeBaseContext?: string;
|
||||
}
|
||||
|
||||
class AgentController {
|
||||
// 获取所有智能体列表
|
||||
async getAllAgents(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
const agents = agentService.getAllAgents();
|
||||
|
||||
return reply.code(200).send({
|
||||
success: true,
|
||||
data: agents,
|
||||
});
|
||||
} catch (error) {
|
||||
request.log.error(error);
|
||||
return reply.code(500).send({
|
||||
success: false,
|
||||
message: '获取智能体列表失败',
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取启用的智能体列表
|
||||
async getEnabledAgents(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
const agents = agentService.getEnabledAgents();
|
||||
|
||||
return reply.code(200).send({
|
||||
success: true,
|
||||
data: agents,
|
||||
});
|
||||
} catch (error) {
|
||||
request.log.error(error);
|
||||
return reply.code(500).send({
|
||||
success: false,
|
||||
message: '获取智能体列表失败',
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取单个智能体详情
|
||||
async getAgentById(
|
||||
request: FastifyRequest<{ Params: AgentParams }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const agent = agentService.getAgentById(id);
|
||||
|
||||
if (!agent) {
|
||||
return reply.code(404).send({
|
||||
success: false,
|
||||
message: '智能体不存在',
|
||||
});
|
||||
}
|
||||
|
||||
return reply.code(200).send({
|
||||
success: true,
|
||||
data: agent,
|
||||
});
|
||||
} catch (error) {
|
||||
request.log.error(error);
|
||||
return reply.code(500).send({
|
||||
success: false,
|
||||
message: '获取智能体详情失败',
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取智能体的系统Prompt
|
||||
async getSystemPrompt(
|
||||
request: FastifyRequest<{ Params: AgentParams }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
|
||||
if (!agentService.agentExists(id)) {
|
||||
return reply.code(404).send({
|
||||
success: false,
|
||||
message: '智能体不存在',
|
||||
});
|
||||
}
|
||||
|
||||
const systemPrompt = agentService.getSystemPrompt(id);
|
||||
|
||||
return reply.code(200).send({
|
||||
success: true,
|
||||
data: {
|
||||
agentId: id,
|
||||
systemPrompt,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
request.log.error(error);
|
||||
return reply.code(500).send({
|
||||
success: false,
|
||||
message: '获取系统Prompt失败',
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染用户Prompt(用于预览或调试)
|
||||
async renderPrompt(
|
||||
request: FastifyRequest<{ Params: AgentParams }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const body = request.body as RenderPromptBody;
|
||||
|
||||
if (!agentService.agentExists(id)) {
|
||||
return reply.code(404).send({
|
||||
success: false,
|
||||
message: '智能体不存在',
|
||||
});
|
||||
}
|
||||
|
||||
if (!body.userInput) {
|
||||
return reply.code(400).send({
|
||||
success: false,
|
||||
message: 'userInput为必填项',
|
||||
});
|
||||
}
|
||||
|
||||
const renderedPrompt = agentService.renderUserPrompt(id, {
|
||||
projectBackground: body.projectBackground,
|
||||
userInput: body.userInput,
|
||||
knowledgeBaseContext: body.knowledgeBaseContext,
|
||||
});
|
||||
|
||||
return reply.code(200).send({
|
||||
success: true,
|
||||
data: {
|
||||
agentId: id,
|
||||
renderedPrompt,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
request.log.error(error);
|
||||
return reply.code(500).send({
|
||||
success: false,
|
||||
message: '渲染Prompt失败',
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 根据分类获取智能体
|
||||
async getAgentsByCategory(
|
||||
request: FastifyRequest<{ Querystring: { category: string } }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const { category } = request.query;
|
||||
|
||||
if (!category) {
|
||||
return reply.code(400).send({
|
||||
success: false,
|
||||
message: 'category参数为必填项',
|
||||
});
|
||||
}
|
||||
|
||||
const agents = agentService.getAgentsByCategory(category);
|
||||
|
||||
return reply.code(200).send({
|
||||
success: true,
|
||||
data: agents,
|
||||
});
|
||||
} catch (error) {
|
||||
request.log.error(error);
|
||||
return reply.code(500).send({
|
||||
success: false,
|
||||
message: '获取智能体列表失败',
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 重新加载配置(管理员功能)
|
||||
async reloadConfig(request: FastifyRequest, reply: FastifyReply) {
|
||||
try {
|
||||
agentService.reloadConfig();
|
||||
|
||||
return reply.code(200).send({
|
||||
success: true,
|
||||
message: '智能体配置已重新加载',
|
||||
});
|
||||
} catch (error) {
|
||||
request.log.error(error);
|
||||
return reply.code(500).send({
|
||||
success: false,
|
||||
message: '重新加载配置失败',
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const agentController = new AgentController();
|
||||
|
||||
@@ -3,6 +3,7 @@ import cors from '@fastify/cors';
|
||||
import { config } from './config/env.js';
|
||||
import { testDatabaseConnection, prisma } from './config/database.js';
|
||||
import { projectRoutes } from './routes/projects.js';
|
||||
import { agentRoutes } from './routes/agents.js';
|
||||
|
||||
const fastify = Fastify({
|
||||
logger: {
|
||||
@@ -55,6 +56,9 @@ fastify.get('/api/v1', async () => {
|
||||
// 注册项目管理路由
|
||||
await fastify.register(projectRoutes, { prefix: '/api/v1' });
|
||||
|
||||
// 注册智能体管理路由
|
||||
await fastify.register(agentRoutes, { prefix: '/api/v1' });
|
||||
|
||||
// 启动服务器
|
||||
const start = async () => {
|
||||
try {
|
||||
|
||||
56
backend/src/routes/agents.ts
Normal file
56
backend/src/routes/agents.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
|
||||
import { agentController } from '../controllers/agentController.js';
|
||||
|
||||
interface AgentParams {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export async function agentRoutes(fastify: FastifyInstance) {
|
||||
// 获取所有智能体列表
|
||||
fastify.get('/agents', async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
return agentController.getAllAgents(request, reply);
|
||||
});
|
||||
|
||||
// 获取启用的智能体列表
|
||||
fastify.get('/agents/enabled', async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
return agentController.getEnabledAgents(request, reply);
|
||||
});
|
||||
|
||||
// 根据分类获取智能体
|
||||
fastify.get<{ Querystring: { category: string } }>(
|
||||
'/agents/by-category',
|
||||
async (request: FastifyRequest<{ Querystring: { category: string } }>, reply: FastifyReply) => {
|
||||
return agentController.getAgentsByCategory(request, reply);
|
||||
}
|
||||
);
|
||||
|
||||
// 获取单个智能体详情
|
||||
fastify.get<{ Params: AgentParams }>(
|
||||
'/agents/:id',
|
||||
async (request: FastifyRequest<{ Params: AgentParams }>, reply: FastifyReply) => {
|
||||
return agentController.getAgentById(request, reply);
|
||||
}
|
||||
);
|
||||
|
||||
// 获取智能体的系统Prompt
|
||||
fastify.get<{ Params: AgentParams }>(
|
||||
'/agents/:id/system-prompt',
|
||||
async (request: FastifyRequest<{ Params: AgentParams }>, reply: FastifyReply) => {
|
||||
return agentController.getSystemPrompt(request, reply);
|
||||
}
|
||||
);
|
||||
|
||||
// 渲染用户Prompt(预览)
|
||||
fastify.post<{ Params: AgentParams }>(
|
||||
'/agents/:id/render-prompt',
|
||||
async (request: FastifyRequest<{ Params: AgentParams }>, reply: FastifyReply) => {
|
||||
return agentController.renderPrompt(request, reply);
|
||||
}
|
||||
);
|
||||
|
||||
// 重新加载配置(管理员功能)
|
||||
fastify.post('/agents/reload-config', async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
return agentController.reloadConfig(request, reply);
|
||||
});
|
||||
}
|
||||
|
||||
212
backend/src/services/agentService.ts
Normal file
212
backend/src/services/agentService.ts
Normal file
@@ -0,0 +1,212 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import yaml from 'js-yaml';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// 智能体配置接口
|
||||
export interface AgentConfig {
|
||||
id: string;
|
||||
name: string;
|
||||
nameEn: string;
|
||||
description: string;
|
||||
category: string;
|
||||
icon: string;
|
||||
enabled: boolean;
|
||||
systemPromptFile: string;
|
||||
userPromptTemplateFile: string;
|
||||
models: {
|
||||
[modelName: string]: {
|
||||
temperature: number;
|
||||
maxTokens: number;
|
||||
topP?: number;
|
||||
};
|
||||
};
|
||||
ragEnabled: boolean;
|
||||
requiresProject: boolean;
|
||||
outputFormat: 'text' | 'structured' | 'document';
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
// 配置文件根结构
|
||||
interface AgentsConfigFile {
|
||||
agents: AgentConfig[];
|
||||
}
|
||||
|
||||
class AgentService {
|
||||
private agents: Map<string, AgentConfig> = new Map();
|
||||
private prompts: Map<string, string> = new Map();
|
||||
private configPath: string;
|
||||
private promptsPath: string;
|
||||
|
||||
constructor() {
|
||||
// 配置文件路径
|
||||
this.configPath = path.resolve(__dirname, '../../config/agents.yaml');
|
||||
this.promptsPath = path.resolve(__dirname, '../../prompts');
|
||||
|
||||
// 初始化加载配置
|
||||
this.loadAgents();
|
||||
}
|
||||
|
||||
// 加载智能体配置
|
||||
private loadAgents() {
|
||||
try {
|
||||
const fileContents = fs.readFileSync(this.configPath, 'utf8');
|
||||
const config = yaml.load(fileContents) as AgentsConfigFile;
|
||||
|
||||
if (!config || !config.agents) {
|
||||
throw new Error('Invalid agents configuration file');
|
||||
}
|
||||
|
||||
// 存储到Map中
|
||||
config.agents.forEach((agent) => {
|
||||
this.agents.set(agent.id, agent);
|
||||
});
|
||||
|
||||
console.log(`✅ Loaded ${this.agents.size} agent configurations`);
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to load agent configurations:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 加载Prompt模板
|
||||
private loadPrompt(filename: string): string {
|
||||
const cacheKey = filename;
|
||||
|
||||
// 检查缓存
|
||||
if (this.prompts.has(cacheKey)) {
|
||||
return this.prompts.get(cacheKey)!;
|
||||
}
|
||||
|
||||
try {
|
||||
const promptPath = path.join(this.promptsPath, filename);
|
||||
const content = fs.readFileSync(promptPath, 'utf8');
|
||||
|
||||
// 缓存到内存
|
||||
this.prompts.set(cacheKey, content);
|
||||
|
||||
return content;
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to load prompt file: ${filename}`, error);
|
||||
throw new Error(`Prompt file not found: ${filename}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取所有智能体列表
|
||||
getAllAgents(): AgentConfig[] {
|
||||
return Array.from(this.agents.values());
|
||||
}
|
||||
|
||||
// 获取启用的智能体列表
|
||||
getEnabledAgents(): AgentConfig[] {
|
||||
return Array.from(this.agents.values()).filter((agent) => agent.enabled);
|
||||
}
|
||||
|
||||
// 根据ID获取智能体配置
|
||||
getAgentById(agentId: string): AgentConfig | null {
|
||||
return this.agents.get(agentId) || null;
|
||||
}
|
||||
|
||||
// 根据分类获取智能体列表
|
||||
getAgentsByCategory(category: string): AgentConfig[] {
|
||||
return Array.from(this.agents.values()).filter(
|
||||
(agent) => agent.category === category
|
||||
);
|
||||
}
|
||||
|
||||
// 获取智能体的系统Prompt
|
||||
getSystemPrompt(agentId: string): string {
|
||||
const agent = this.getAgentById(agentId);
|
||||
if (!agent) {
|
||||
throw new Error(`Agent not found: ${agentId}`);
|
||||
}
|
||||
|
||||
return this.loadPrompt(agent.systemPromptFile);
|
||||
}
|
||||
|
||||
// 获取智能体的用户Prompt模板
|
||||
getUserPromptTemplate(agentId: string): string {
|
||||
const agent = this.getAgentById(agentId);
|
||||
if (!agent) {
|
||||
throw new Error(`Agent not found: ${agentId}`);
|
||||
}
|
||||
|
||||
return this.loadPrompt(agent.userPromptTemplateFile);
|
||||
}
|
||||
|
||||
// 渲染用户Prompt(替换模板变量)
|
||||
renderUserPrompt(
|
||||
agentId: string,
|
||||
variables: {
|
||||
projectBackground?: string;
|
||||
userInput: string;
|
||||
knowledgeBaseContext?: string;
|
||||
}
|
||||
): string {
|
||||
const template = this.getUserPromptTemplate(agentId);
|
||||
|
||||
let rendered = template;
|
||||
|
||||
// 替换变量
|
||||
rendered = rendered.replace(/\{\{projectBackground\}\}/g, variables.projectBackground || '未提供项目背景');
|
||||
rendered = rendered.replace(/\{\{userInput\}\}/g, variables.userInput);
|
||||
|
||||
// 处理条件块(知识库上下文)
|
||||
if (variables.knowledgeBaseContext) {
|
||||
rendered = rendered.replace(
|
||||
/\{\{#if knowledgeBaseContext\}\}([\s\S]*?)\{\{\/if\}\}/g,
|
||||
'$1'
|
||||
);
|
||||
rendered = rendered.replace(/\{\{knowledgeBaseContext\}\}/g, variables.knowledgeBaseContext);
|
||||
} else {
|
||||
// 移除条件块
|
||||
rendered = rendered.replace(
|
||||
/\{\{#if knowledgeBaseContext\}\}[\s\S]*?\{\{\/if\}\}/g,
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
return rendered.trim();
|
||||
}
|
||||
|
||||
// 检查智能体是否存在
|
||||
agentExists(agentId: string): boolean {
|
||||
return this.agents.has(agentId);
|
||||
}
|
||||
|
||||
// 检查智能体是否启用
|
||||
isAgentEnabled(agentId: string): boolean {
|
||||
const agent = this.getAgentById(agentId);
|
||||
return agent ? agent.enabled : false;
|
||||
}
|
||||
|
||||
// 获取智能体的模型配置
|
||||
getModelConfig(agentId: string, modelName: string) {
|
||||
const agent = this.getAgentById(agentId);
|
||||
if (!agent) {
|
||||
throw new Error(`Agent not found: ${agentId}`);
|
||||
}
|
||||
|
||||
const modelConfig = agent.models[modelName];
|
||||
if (!modelConfig) {
|
||||
throw new Error(`Model ${modelName} not configured for agent ${agentId}`);
|
||||
}
|
||||
|
||||
return modelConfig;
|
||||
}
|
||||
|
||||
// 重新加载配置(热更新)
|
||||
reloadConfig() {
|
||||
this.agents.clear();
|
||||
this.prompts.clear();
|
||||
this.loadAgents();
|
||||
console.log('✅ Agent configurations reloaded');
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例
|
||||
export const agentService = new AgentService();
|
||||
|
||||
Reference in New Issue
Block a user