Skip to content

Commit 3bcfd76

Browse files
gxklclaude
andcommitted
refactor(skills): replace custom eval framework with skill-creator evals
- Remove automated eval logic (lib/, dynamic/, static/, fixtures/) - Add evals-routing.json, evals-egg-core.json, evals-egg-controller.json - 44 eval cases covering implementation, troubleshooting, and migration scenarios Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d66fa72 commit 3bcfd76

File tree

16 files changed

+371
-790
lines changed

16 files changed

+371
-790
lines changed

packages/skills/eval/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
workspace/
2+
egg-workspace/

packages/skills/eval/dynamic/quality.eval.ts

Lines changed: 0 additions & 54 deletions
This file was deleted.

packages/skills/eval/dynamic/routing.eval.ts

Lines changed: 0 additions & 47 deletions
This file was deleted.
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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

Comments
 (0)