|
| 1 | +{ |
| 2 | + "skill_name": "egg-controller", |
| 3 | + "description": "控制器评测:覆盖 http-controller、mcp-controller、schedule", |
| 4 | + "evals": [ |
| 5 | + { |
| 6 | + "id": 1, |
| 7 | + "prompt": "帮我写一个 POST /api/users 接口,接收 JSON body 创建用户,返回 201", |
| 8 | + "expected_output": "@HTTPController + @HTTPMethod POST + @HTTPBody + ctx.status = 201,完整可运行代码" |
| 9 | + }, |
| 10 | + { |
| 11 | + "id": 2, |
| 12 | + "prompt": "帮我实现 GET /api/users/:userId/posts/:postId,获取用户的某篇文章", |
| 13 | + "expected_output": "@HTTPParam() userId + @HTTPParam() postId,路径参数定义和获取的完整代码" |
| 14 | + }, |
| 15 | + { |
| 16 | + "id": 3, |
| 17 | + "prompt": "我有个搜索接口 /api/search?tag=tech&tag=dev,需要获取 tag 的全部值", |
| 18 | + "expected_output": "使用 @HTTPQueries({ name: 'tag' }) 获取 string[] 数组,不是 @HTTPQuery" |
| 19 | + }, |
| 20 | + { |
| 21 | + "id": 4, |
| 22 | + "prompt": "帮我写个 SSR 页面,GET / 返回完整的 HTML", |
| 23 | + "expected_output": "@HTTPController + ctx.type = 'html' + return HTML 字符串" |
| 24 | + }, |
| 25 | + { |
| 26 | + "id": 5, |
| 27 | + "prompt": "帮我实现流式输出,一段一段返回 HTML 内容", |
| 28 | + "expected_output": "async generator + Readable.from(),设置 ctx.type,完整代码" |
| 29 | + }, |
| 30 | + { |
| 31 | + "id": 6, |
| 32 | + "prompt": "我有两个路由 /api/:name 和 /api/health,请求 /api/health 匹配到了 :name,帮我看看代码", |
| 33 | + "expected_output": "静态路径优先级高于参数路径应该自动匹配,如果冲突可用 priority 参数调整", |
| 34 | + "files": [ |
| 35 | + { |
| 36 | + "path": "app/apiModule/ApiController.ts", |
| 37 | + "content": "import { HTTPController, HTTPMethod, HTTPMethodEnum, HTTPParam } from 'egg';\n\n@HTTPController({ path: '/api' })\nexport class ApiController {\n @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:name' })\n async getByName(@HTTPParam() name: string) {\n return { name };\n }\n\n @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/health' })\n async health() {\n return { status: 'ok' };\n }\n}" |
| 38 | + } |
| 39 | + ] |
| 40 | + }, |
| 41 | + { |
| 42 | + "id": 7, |
| 43 | + "prompt": "帮我给这个 Controller 加一个认证中间件的逻辑,从 header 里取 Authorization 做校验", |
| 44 | + "expected_output": "使用 @HTTPHeaders() 获取 headers,key 用小写 headers['authorization']", |
| 45 | + "files": [ |
| 46 | + { |
| 47 | + "path": "app/apiModule/UserController.ts", |
| 48 | + "content": "import { HTTPController, HTTPMethod, HTTPMethodEnum } from 'egg';\n\n@HTTPController({ path: '/api/users' })\nexport class UserController {\n @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/' })\n async list() {\n return { users: [] };\n }\n}" |
| 49 | + } |
| 50 | + ] |
| 51 | + }, |
| 52 | + { |
| 53 | + "id": 8, |
| 54 | + "prompt": "帮我写一个完整的 CRUD 模块:userModule,包含 UserService 和 UserController,支持 GET/POST/PUT/DELETE", |
| 55 | + "expected_output": "完整文件结构:package.json + UserService(@SingletonProto + PUBLIC) + UserController(@HTTPController),四个 HTTP 方法的完整代码" |
| 56 | + }, |
| 57 | + { |
| 58 | + "id": 9, |
| 59 | + "prompt": "基于这个 Service,帮我写一个 HTTPController 暴露查询和创建接口", |
| 60 | + "expected_output": "@HTTPController + 两个 @HTTPMethod(GET 用 @HTTPParam,POST 用 @HTTPBody)+ @Inject 注入 ProductService", |
| 61 | + "files": [ |
| 62 | + { |
| 63 | + "path": "app/productModule/package.json", |
| 64 | + "content": "{\n \"name\": \"productModule\",\n \"eggModule\": { \"name\": \"productModule\" }\n}" |
| 65 | + }, |
| 66 | + { |
| 67 | + "path": "app/productModule/ProductService.ts", |
| 68 | + "content": "import { SingletonProto, AccessLevel } from 'egg';\n\n@SingletonProto({ accessLevel: AccessLevel.PUBLIC })\nexport class ProductService {\n async findById(id: string) {\n return { id, name: 'Product ' + id, price: 99 };\n }\n\n async create(data: { name: string; price: number }) {\n return { id: 'new-id', ...data };\n }\n}" |
| 69 | + } |
| 70 | + ] |
| 71 | + }, |
| 72 | + { |
| 73 | + "id": 10, |
| 74 | + "prompt": "帮我实现一个 MCP Tool,让 AI 能搜索我们的内部文档", |
| 75 | + "expected_output": "@MCPController() + @MCPTool() + @ToolArgsSchema + Service 注入,import 从 @eggjs/tegg,zod 从 @eggjs/tegg/zod" |
| 76 | + }, |
| 77 | + { |
| 78 | + "id": 11, |
| 79 | + "prompt": "MCP Tool 的参数 Schema 写法有问题,帮我看看", |
| 80 | + "expected_output": "指出不应该用 z.object() 包装,直接用普通对象 { name: z.string() }", |
| 81 | + "files": [ |
| 82 | + { |
| 83 | + "path": "app/docModule/DocMCPController.ts", |
| 84 | + "content": "import { MCPController, MCPTool, MCPToolResponse, ToolArgs, ToolArgsSchema, Inject } from '@eggjs/tegg';\nimport { z } from '@eggjs/tegg/zod';\n\nconst SearchSchema = z.object({\n keyword: z.string({ description: 'search keyword' }),\n limit: z.number().optional(),\n});\n\n@MCPController()\nexport class DocMCPController {\n @MCPTool({ description: 'Search internal docs' })\n async search(\n @ToolArgsSchema(SearchSchema) args: ToolArgs<typeof SearchSchema>,\n ): Promise<MCPToolResponse> {\n return { content: [{ type: 'text', text: 'results' }] };\n }\n}" |
| 85 | + } |
| 86 | + ] |
| 87 | + }, |
| 88 | + { |
| 89 | + "id": 12, |
| 90 | + "prompt": "我的 MCP Tool 要处理一个大数据导出,可能要跑几分钟,怎么给客户端推送进度", |
| 91 | + "expected_output": "@Extra() 获取 ToolExtra,用 sendNotification 推送进度,给出完整代码" |
| 92 | + }, |
| 93 | + { |
| 94 | + "id": 13, |
| 95 | + "prompt": "帮我写一个 MCP Resource,让 AI 能读取指定用户的 profile", |
| 96 | + "expected_output": "@MCPResource({ template: [...] }) + uri 参数解析,完整代码" |
| 97 | + }, |
| 98 | + { |
| 99 | + "id": 14, |
| 100 | + "prompt": "帮我写个定时任务,每天凌晨 3 点清理过期数据", |
| 101 | + "expected_output": "@Schedule<CronParams> + cron: '0 0 3 * * *' + ScheduleType.WORKER,import 从 egg/schedule" |
| 102 | + }, |
| 103 | + { |
| 104 | + "id": 15, |
| 105 | + "prompt": "定时任务放在 app/schedule 目录下不生效,帮我看看", |
| 106 | + "expected_output": "指出不能放在 app/schedule 下(和 egg 默认扫描冲突),应放在模块目录中", |
| 107 | + "files": [ |
| 108 | + { |
| 109 | + "path": "app/schedule/CleanTask.ts", |
| 110 | + "content": "import { Inject, Logger } from 'egg';\nimport { IntervalParams, Schedule, ScheduleType } from 'egg/schedule';\n\n@Schedule<IntervalParams>({\n type: ScheduleType.WORKER,\n scheduleData: { interval: '1h' },\n})\nexport class CleanTask {\n @Inject()\n private logger: Logger;\n\n async subscribe() {\n this.logger.info('cleaning...');\n }\n}" |
| 111 | + } |
| 112 | + ] |
| 113 | + }, |
| 114 | + { |
| 115 | + "id": 16, |
| 116 | + "prompt": "帮我写一个每 30 秒刷新缓存的定时任务,要求每个 worker 都执行", |
| 117 | + "expected_output": "@Schedule<IntervalParams> + interval: '30s' + ScheduleType.ALL" |
| 118 | + }, |
| 119 | + { |
| 120 | + "id": 17, |
| 121 | + "prompt": "基于这个 CacheService,帮我写一个定时任务每 5 分钟刷新一次缓存", |
| 122 | + "expected_output": "@Schedule<IntervalParams> + interval: '5m' + @Inject() cacheService,import 从 egg/schedule", |
| 123 | + "files": [ |
| 124 | + { |
| 125 | + "path": "app/cacheModule/package.json", |
| 126 | + "content": "{\n \"name\": \"cacheModule\",\n \"eggModule\": { \"name\": \"cacheModule\" }\n}" |
| 127 | + }, |
| 128 | + { |
| 129 | + "path": "app/cacheModule/CacheService.ts", |
| 130 | + "content": "import { SingletonProto, AccessLevel } from 'egg';\n\n@SingletonProto({ accessLevel: AccessLevel.PUBLIC })\nexport class CacheService {\n private cache = new Map<string, any>();\n\n async refresh() {\n this.cache.clear();\n // reload from db\n }\n\n get(key: string) {\n return this.cache.get(key);\n }\n}" |
| 131 | + } |
| 132 | + ] |
| 133 | + }, |
| 134 | + { |
| 135 | + "id": 18, |
| 136 | + "prompt": "帮我把这个老的 egg controller 改成 HTTPController 写法", |
| 137 | + "expected_output": "用 @HTTPController + @HTTPMethod 重写,参数用装饰器获取,注入 Service 替代 ctx.service", |
| 138 | + "files": [ |
| 139 | + { |
| 140 | + "path": "app/controller/user.ts", |
| 141 | + "content": "import { Controller } from 'egg';\n\nexport default class UserController extends Controller {\n async show() {\n const { ctx } = this;\n const id = ctx.params.id;\n const user = await ctx.service.user.find(id);\n ctx.body = { success: true, data: user };\n }\n\n async create() {\n const { ctx } = this;\n const body = ctx.request.body;\n const user = await ctx.service.user.create(body);\n ctx.status = 201;\n ctx.body = { success: true, data: user };\n }\n}" |
| 142 | + } |
| 143 | + ] |
| 144 | + } |
| 145 | + ] |
| 146 | +} |
0 commit comments