@eggjs/skills 0.0.0 → 4.1.2-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/PLAN.md ADDED
@@ -0,0 +1,396 @@
1
+ # Skills 评测方案设计
2
+
3
+ ## 目标
4
+
5
+ 为 `packages/skills/` 设计一套评测体系,覆盖两个层面:
6
+
7
+ 1. **静态校验** — 验证 Skill 文件的结构正确性、引用完整性
8
+ 2. **动态评测** — 用 LLM-as-Judge 评估 AI 基于 Skill 生成回答的质量
9
+
10
+ 全部手动触发运行,不集成 CI。
11
+
12
+ ---
13
+
14
+ ## 目录结构
15
+
16
+ ```
17
+ packages/skills/
18
+ ├── egg/
19
+ ├── controller/
20
+ ├── tegg-core/
21
+ ├── eval/ # 新增:评测目录
22
+ │ ├── static/
23
+ │ │ └── validate.test.ts # 静态校验测试
24
+ │ ├── dynamic/
25
+ │ │ ├── routing.eval.ts # 入口路由评测
26
+ │ │ └── quality.eval.ts # 内容质量评测
27
+ │ ├── fixtures/
28
+ │ │ ├── routing-cases.ts # 路由测试用例
29
+ │ │ └── quality-cases.ts # 质量测试用例
30
+ │ └── lib/
31
+ │ ├── skill-loader.ts # Skill 文件加载器
32
+ │ ├── judge.ts # LLM-as-Judge 核心逻辑
33
+ │ └── types.ts # 共享类型定义
34
+ ├── vitest.config.ts
35
+ ├── package.json
36
+ └── tsconfig.json
37
+ ```
38
+
39
+ ---
40
+
41
+ ## 第一部分:静态校验
42
+
43
+ ### 校验项
44
+
45
+ | 校验项 | 说明 |
46
+ | -------------------- | ------------------------------------------------------------- |
47
+ | **Frontmatter 格式** | 每个 SKILL.md 必须包含 `name`、`description`、`allowed-tools` |
48
+ | **引用文件存在性** | SKILL.md 中提到的 `references/*.md` 文件必须存在 |
49
+ | **交叉引用一致性** | 入口 skill 提到的子 skill 目录必须存在且包含 SKILL.md |
50
+ | **Markdown 结构** | 标题层级合理(以 `# ` 开头,不跳级) |
51
+ | **决策表完整性** | 入口 skill 的路由表中每个 skill 都有对应目录 |
52
+
53
+ ### 实现方式
54
+
55
+ 使用 vitest + node:assert 编写测试,通过 Node.js fs API 读取文件并解析:
56
+
57
+ ```typescript
58
+ // eval/static/validate.test.ts
59
+ import { describe, it } from 'vitest';
60
+ import assert from 'node:assert/strict';
61
+ import { loadAllSkills } from '../lib/skill-loader.ts';
62
+
63
+ describe('Skill 静态校验', () => {
64
+ describe('Frontmatter', () => {
65
+ it('每个 SKILL.md 包含必填字段: name, description, allowed-tools', ...);
66
+ });
67
+
68
+ describe('引用完整性', () => {
69
+ it('SKILL.md 中引用的 references/ 文件均存在', ...);
70
+ it('入口 skill 引用的子 skill 目录均存在', ...);
71
+ });
72
+
73
+ describe('Markdown 结构', () => {
74
+ it('标题层级不跳级', ...);
75
+ });
76
+ });
77
+ ```
78
+
79
+ ---
80
+
81
+ ## 第二部分:动态评测(LLM-as-Judge)
82
+
83
+ ### 评测维度
84
+
85
+ 动态评测分为两个子场景:
86
+
87
+ #### 2.1 路由评测 — 入口 Skill 是否正确路由
88
+
89
+ 测试 `egg/SKILL.md` 的决策逻辑:给定用户查询,判断 AI 是否路由到正确的子 skill。
90
+
91
+ **测试用例结构:**
92
+
93
+ ```typescript
94
+ // eval/fixtures/routing-cases.ts
95
+ export const routingCases: RoutingCase[] = [
96
+ {
97
+ query: '如何创建 HTTP controller?',
98
+ expectedSkill: 'controller',
99
+ reason: '明确提到 controller,属于协议实现',
100
+ },
101
+ {
102
+ query: '@SingletonProto 和 @ContextProto 有什么区别?',
103
+ expectedSkill: 'tegg-core',
104
+ reason: '关于对象生命周期,属于核心概念',
105
+ },
106
+ {
107
+ query: '我需要创建一个可以被 HTTP 控制器使用的服务',
108
+ expectedSkill: 'tegg-core',
109
+ reason: '模糊意图,按规则 1(基础优先)应路由到 core',
110
+ },
111
+ // ... 更多用例
112
+ ];
113
+ ```
114
+
115
+ **测试实现:**
116
+
117
+ ```typescript
118
+ // eval/dynamic/routing.eval.ts
119
+ import { describe, it } from 'vitest';
120
+ import assert from 'node:assert/strict';
121
+ import Anthropic from '@anthropic-ai/sdk';
122
+ import { loadSkillContent } from '../lib/skill-loader.ts';
123
+ import { routingCases } from '../fixtures/routing-cases.ts';
124
+
125
+ const client = new Anthropic();
126
+ const AVAILABLE_SKILLS = ['controller', 'tegg-core'];
127
+
128
+ describe('路由评测', () => {
129
+ // 加载入口 skill 作为 system prompt
130
+ const entrySkillContent = loadSkillContent('egg');
131
+
132
+ for (const { query, expectedSkill, reason } of routingCases) {
133
+ it(`"${query}" → ${expectedSkill}`, async () => {
134
+ // 1. 将 SKILL.md 作为 system prompt,发送用户查询
135
+ const response = await client.messages.create({
136
+ model: 'claude-sonnet-4-20250514',
137
+ max_tokens: 1024,
138
+ system: [
139
+ entrySkillContent,
140
+ // 约束输出格式,让 AI 只做路由决策
141
+ `你是 EGG 框架技能路由器。根据上面的决策指南,分析用户查询并选择应该加载的技能。`,
142
+ `可选技能: ${AVAILABLE_SKILLS.join(', ')}`,
143
+ `只输出 JSON: {"skill": "<技能名>", "reason": "<简要理由>"}`,
144
+ ].join('\n\n'),
145
+ messages: [{ role: 'user', content: query }],
146
+ });
147
+
148
+ // 2. 解析 AI 回答中的路由选择
149
+ const text = response.content[0].type === 'text' ? response.content[0].text : '';
150
+ const parsed = JSON.parse(text);
151
+
152
+ // 3. 断言路由正确性
153
+ assert.equal(parsed.skill, expectedSkill,
154
+ `路由错误: 期望 "${expectedSkill}" 但得到 "${parsed.skill}"` +
155
+ `\n 用例理由: ${reason}` +
156
+ `\n AI 理由: ${parsed.reason}`
157
+ );
158
+ });
159
+ }
160
+ });
161
+ ```
162
+
163
+ #### 2.2 内容质量评测 — 子 Skill 回答质量
164
+
165
+ 测试各子 skill 对领域问题的回答质量。
166
+
167
+ **测试用例结构:**
168
+
169
+ ```typescript
170
+ // eval/fixtures/quality-cases.ts
171
+ export const qualityCases: QualityCase[] = [
172
+ {
173
+ skill: 'controller',
174
+ query: '如何创建一个 POST 接口接收 JSON body?',
175
+ criteria: [
176
+ '使用 @HTTPController 装饰器',
177
+ '使用 @HTTPMethod 且 method 为 POST',
178
+ '使用 @HTTPBody() 获取请求体',
179
+ '包含完整可运行的代码示例',
180
+ ],
181
+ references: ['references/http-controller.md'], // 需要加载的参考文档
182
+ },
183
+ {
184
+ skill: 'tegg-core',
185
+ query: '如何让一个服务可以被其他模块访问?',
186
+ criteria: [
187
+ '提到 AccessLevel.PUBLIC',
188
+ '使用 @SingletonProto 装饰器',
189
+ '解释跨模块访问机制',
190
+ ],
191
+ references: [],
192
+ },
193
+ // ... 更多用例
194
+ ];
195
+ ```
196
+
197
+ **测试实现:**
198
+
199
+ ```typescript
200
+ // eval/dynamic/quality.eval.ts
201
+ import { describe, it } from 'vitest';
202
+ import assert from 'node:assert/strict';
203
+ import Anthropic from '@anthropic-ai/sdk';
204
+ import { loadSkillContent, loadReference } from '../lib/skill-loader.ts';
205
+ import { qualityCases } from '../fixtures/quality-cases.ts';
206
+ import { judge } from '../lib/judge.ts';
207
+
208
+ const client = new Anthropic();
209
+
210
+ describe('内容质量评测', () => {
211
+ for (const testCase of qualityCases) {
212
+ describe(`[${testCase.skill}] ${testCase.query}`, () => {
213
+ let aiResponse: string;
214
+
215
+ // Step 1: 加载 skill 内容作为 system prompt,向被测 LLM 提问
216
+ it('生成回答', async () => {
217
+ const skillContent = loadSkillContent(testCase.skill);
218
+ const refContents = testCase.references
219
+ .map(ref => loadReference(testCase.skill, ref));
220
+
221
+ const systemPrompt = [skillContent, ...refContents].join('\n\n---\n\n');
222
+
223
+ const response = await client.messages.create({
224
+ model: 'claude-sonnet-4-20250514',
225
+ max_tokens: 2048,
226
+ system: systemPrompt,
227
+ messages: [{ role: 'user', content: testCase.query }],
228
+ });
229
+
230
+ aiResponse = response.content[0].type === 'text' ? response.content[0].text : '';
231
+ assert.ok(aiResponse.length > 0, 'AI 应该返回非空回答');
232
+ });
233
+
234
+ // Step 2: 用 Judge LLM 对回答逐项评分
235
+ it('通过质量评审', async () => {
236
+ const result = await judge(client, {
237
+ query: testCase.query,
238
+ response: aiResponse,
239
+ criteria: testCase.criteria,
240
+ });
241
+
242
+ // 输出详细评分到 console 供人工查看
243
+ console.log(` 得分: ${result.totalScore} (${result.passed}/${result.total})`);
244
+ for (const item of result.details) {
245
+ const icon = item.score === 1 ? '✓' : '✗';
246
+ console.log(` ${icon} ${item.criterion}: ${item.reason}`);
247
+ }
248
+
249
+ // 断言:所有 criteria 都应满足
250
+ assert.ok(result.totalScore >= 0.8,
251
+ `质量不达标: ${result.totalScore} < 0.8\n` +
252
+ result.details
253
+ .filter(d => d.score === 0)
254
+ .map(d => ` ✗ ${d.criterion}: ${d.reason}`)
255
+ .join('\n')
256
+ );
257
+ });
258
+ });
259
+ }
260
+ });
261
+ ```
262
+
263
+ ### LLM-as-Judge 实现
264
+
265
+ ```typescript
266
+ // eval/lib/judge.ts
267
+ import type Anthropic from '@anthropic-ai/sdk';
268
+ import type { JudgeInput, JudgeResult, JudgeDetail } from './types.ts';
269
+
270
+ export async function judge(
271
+ client: Anthropic,
272
+ input: JudgeInput,
273
+ ): Promise<JudgeResult> {
274
+ const criteriaList = input.criteria
275
+ .map((c, i) => `${i + 1}. ${c}`)
276
+ .join('\n');
277
+
278
+ const response = await client.messages.create({
279
+ model: 'claude-sonnet-4-20250514',
280
+ max_tokens: 1024,
281
+ system: '你是 AI 回答质量评估专家。严格按照 JSON 格式输出评分结果。',
282
+ messages: [{
283
+ role: 'user',
284
+ content: `请根据评分标准,对以下 AI 回答逐项评分。
285
+
286
+ ## 评分标准
287
+ ${criteriaList}
288
+
289
+ ## 用户问题
290
+ ${input.query}
291
+
292
+ ## AI 回答
293
+ ${input.response}
294
+
295
+ ## 输出格式(严格 JSON)
296
+ {
297
+ "details": [
298
+ { "criterion": "标准内容", "score": 0 或 1, "reason": "简要理由" }
299
+ ]
300
+ }`,
301
+ }],
302
+ });
303
+
304
+ const text = response.content[0].type === 'text' ? response.content[0].text : '';
305
+ const parsed = JSON.parse(text);
306
+ const details: JudgeDetail[] = parsed.details;
307
+ const passed = details.filter(d => d.score === 1).length;
308
+
309
+ return {
310
+ details,
311
+ passed,
312
+ total: details.length,
313
+ totalScore: passed / details.length,
314
+ };
315
+ }
316
+ ```
317
+
318
+ ### 评测报告
319
+
320
+ 运行评测后生成 JSON 报告:
321
+
322
+ ```json
323
+ {
324
+ "timestamp": "2026-02-05T10:00:00Z",
325
+ "routing": {
326
+ "total": 10,
327
+ "correct": 9,
328
+ "accuracy": 0.9,
329
+ "failures": [
330
+ {
331
+ "query": "...",
332
+ "expected": "tegg-core",
333
+ "actual": "controller",
334
+ "reason": "..."
335
+ }
336
+ ]
337
+ },
338
+ "quality": {
339
+ "controller": {
340
+ "cases": 5,
341
+ "avg_score": 0.85,
342
+ "details": [...]
343
+ },
344
+ "tegg-core": {
345
+ "cases": 5,
346
+ "avg_score": 0.90,
347
+ "details": [...]
348
+ }
349
+ }
350
+ }
351
+ ```
352
+
353
+ ---
354
+
355
+ ## 第三部分:技术选型与依赖
356
+
357
+ | 组件 | 选型 | 理由 |
358
+ | --------------------- | ------------------ | ------------------------------ |
359
+ | 测试框架 | vitest | 遵循 monorepo 标准 |
360
+ | 断言库 | node:assert/strict | Node.js 内置,零依赖 |
361
+ | YAML frontmatter 解析 | gray-matter | 成熟的 frontmatter 解析库 |
362
+ | LLM 调用 | @anthropic-ai/sdk | 使用 Claude API 做评测和 Judge |
363
+ | 报告输出 | JSON 文件 | 简单可读,方便后续扩展为可视化 |
364
+
365
+ ### package.json scripts
366
+
367
+ ```json
368
+ {
369
+ "scripts": {
370
+ "test": "vitest run --config vitest.config.ts eval/static/",
371
+ "eval": "vitest run --config vitest.config.ts eval/dynamic/",
372
+ "eval:routing": "vitest run --config vitest.config.ts eval/dynamic/routing.eval.ts",
373
+ "eval:quality": "vitest run --config vitest.config.ts eval/dynamic/quality.eval.ts"
374
+ }
375
+ }
376
+ ```
377
+
378
+ - `test` — 运行静态校验(快速,无 API 调用)
379
+ - `eval` — 运行全部动态评测
380
+ - `eval:routing` — 仅运行路由评测
381
+ - `eval:quality` — 仅运行内容质量评测
382
+
383
+ 动态评测需设置 `ANTHROPIC_API_KEY` 环境变量。
384
+
385
+ ---
386
+
387
+ ## 实施步骤
388
+
389
+ 1. 在 worktree (`egg-skills-eval`) 中添加依赖:vitest、gray-matter、@anthropic-ai/sdk
390
+ 2. 添加 `vitest.config.ts` 和更新 `package.json` scripts
391
+ 3. 创建 `eval/lib/` 基础工具:skill-loader、types、judge
392
+ 4. 实现 `eval/static/validate.test.ts` 静态校验
393
+ 5. 编写路由测试用例 `eval/fixtures/routing-cases.ts`
394
+ 6. 实现 `eval/dynamic/routing.eval.ts` 路由评测
395
+ 7. 编写质量测试用例 `eval/fixtures/quality-cases.ts`
396
+ 8. 实现 `eval/dynamic/quality.eval.ts` 内容质量评测
package/egg/SKILL.md ADDED
@@ -0,0 +1,230 @@
1
+ ---
2
+ name: egg
3
+ description: 本技能用于处理 EGG 框架。它提供基于用户意图在核心概念和控制器之间做选择的决策指导。作为所有 EGG 相关问题的入口点使用。
4
+ allowed-tools: Read
5
+ ---
6
+
7
+ # EGG 决策指南
8
+
9
+ ## 概述
10
+
11
+ 本技能帮助根据用户意图和任务类型确定使用哪个专用的 EGG 技能。EGG 文档组织为两个主要领域:
12
+
13
+ 1. **核心概念**(`egg-core` skill):模块架构、依赖注入、对象生命周期
14
+ 2. **控制器**(`egg-controller` skill):用于 API 端点的各种协议特定控制器
15
+
16
+ ## 技能选择逻辑
17
+
18
+ ### 使用 `egg-core` skill 当用户询问:
19
+
20
+ **用户询问关于:**
21
+
22
+ - 模块架构和组织
23
+ - `@SingletonProto` vs `@ContextProto` 的使用
24
+ - 使用 `@Inject` 的依赖注入
25
+ - 对象生命周期和实例化
26
+ - 模块之间的访问控制(`AccessLevel`)
27
+ - 模块配置(`module.yml`、`package.json`)
28
+ - 使用限定符解决命名冲突
29
+
30
+ **触发关键词:**
31
+
32
+ - module、workspace、modules
33
+ - singleton、单例、@SingletonProto
34
+ - context、request context、@ContextProto
35
+ - inject、injection、dependency injection、@Inject
36
+ - prototype、lifecycle、实例化
37
+ - access level、private、public、@ModuleQualifier
38
+ - configuration、module config
39
+
40
+ **示例查询:**
41
+
42
+ - "如何在 EGG 中创建模块?"
43
+ - "SingletonProto 和 ContextProto 有什么区别?"
44
+ - "如何注入服务?"
45
+ - "如何访问其他模块的对象?"
46
+ - "EGG 中的 AccessLevel 是什么?"
47
+
48
+ ### 使用 `egg-controller` skill 当用户询问:
49
+
50
+ **用户询问关于:**
51
+
52
+ - 创建 API 端点或接口
53
+ - 实现特定协议处理器(HTTP、MCP 等)
54
+ - 连接到外部系统或客户端
55
+ - 处理传入的请求/响应
56
+ - 控制器级别的装饰器和模式
57
+
58
+ **触发关键词:**
59
+
60
+ - controller、控制器
61
+ - HTTP、API、REST、endpoint
62
+ - MCP、LLM、AI、tool
63
+ - schedule、timer、cron、scheduled、定时
64
+ - SSE、streaming、server-sent events
65
+
66
+ **示例查询:**
67
+
68
+ - "如何创建 HTTP controller?"
69
+ - "如何实现 MCP 接口?"
70
+ - "怎么实现定时任务?"
71
+
72
+ ---
73
+
74
+ ## 决策框架
75
+
76
+ ### 步骤 1:识别意图类型
77
+
78
+ 询问:**用户是在询问构建块/框架内部 OR 实现特定接口?**
79
+
80
+ **构建块/框架内部** → 使用 `egg-core` skill
81
+
82
+ - 理解 EGG 如何工作
83
+ - 组织代码结构
84
+ - 管理对象生命周期
85
+ - 设置模块
86
+
87
+ **实现特定接口** → 使用 `egg-controller` skill
88
+
89
+ - 创建 API/端点
90
+ - 处理不同协议
91
+ - 处理请求/响应
92
+
93
+ ### 步骤 2:检查模糊意图
94
+
95
+ 如果用户的意图可能是核心 OR 控制器(例如,"如何实现一个需要跨模块访问的服务?"):
96
+
97
+ **决策优先级**:核心概念优先
98
+
99
+ 理由:即使服务将在控制器中使用,关于跨模块访问(`AccessLevel`)的基本问题是一个核心概念。一旦理解了核心结构,用户就可以在控制器中应用它。
100
+
101
+ **行动**:
102
+
103
+ 1. 使用 `egg-core` skill
104
+ 2. 解释概念(例如,`AccessLevel.PUBLIC`)
105
+ 3. 核心解释后,建议:"如果你需要在特定控制器中使用它,请使用 `egg-controller` skill
106
+
107
+ ### 步骤 3:协议/用例特定指示器
108
+
109
+ | 协议/用例 | 主要技能 | 次要技能 |
110
+ | ---------------------- | ---------------- | -------- |
111
+ | HTTP API | `egg-controller` | - |
112
+ | MCP | `egg-controller` | - |
113
+ | Scheduled Tasks | `egg-controller` | - |
114
+ | Cross-module injection | `egg-core` | - |
115
+ | Module structure | `egg-core` | - |
116
+ | Object lifecycle | `egg-core` | - |
117
+
118
+ ## 冲突解决规则
119
+
120
+ ### 规则 1:基础优先
121
+
122
+ 当问题同时涉及核心概念 AND 控制器实现时:
123
+
124
+ - **示例**:"如何实现一个 HTTP 控制器可以使用的单例服务?"
125
+ - **决策**:从 `egg-core` skill 开始解释 SingletonProto 和 AccessLevel
126
+ - **后续**:"现在你理解了服务定义,使用 `egg-controller` skill 实现注入此服务的 HTTP 控制器。"
127
+
128
+ ### 规则 2:显式覆盖
129
+
130
+ 如果用户明确提及特定控制器类型:
131
+
132
+ - **示例**:"如何使用 HTTPController 配合 ContextProto 服务?"
133
+ - **决策**:使用 `egg-controller` skill(HTTPController 是显式的)
134
+ - **后续**:解释 HTTPController 实现,如果需要简要提及来自核心概念的 ContextProto
135
+
136
+ ### 规则 3:学习语境
137
+
138
+ 如果用户问"什么是 X?"或"Y 如何工作?":
139
+
140
+ - **核心概念问题** → 使用 `egg-core` skill
141
+ - **控制器类型问题** → 使用 `egg-controller` skill
142
+ - **一般 EGG 问题** → 使用本技能的决策框架
143
+
144
+ 如果用户问"如何实现 X?"或"给我看 Y 的代码?":
145
+
146
+ - **实现特定问题** → 根据决策框架加载特定 skill
147
+
148
+ ## 快速参考表
149
+
150
+ | 用户意图 | 关键词 | 使用技能 |
151
+ | -------------------- | ------------------------------- | ---------------------- |
152
+ | Module architecture | module、workspace、organization | `egg-core` skill |
153
+ | Object lifecycle | singleton、context、lifecycle | `egg-core` skill |
154
+ | Dependency injection | inject、@Inject、dependency | `egg-core` skill |
155
+ | Access control | private、public、cross-module | `egg-core` skill |
156
+ | HTTP endpoints | HTTP、API、REST | `egg-controller` skill |
157
+ | LLM/AI integration | MCP、tool、prompt | `egg-controller` skill |
158
+ | Scheduling | schedule、cron、timer | `egg-controller` skill |
159
+
160
+ ---
161
+
162
+ ## 示例
163
+
164
+ ### 示例 1:明确的核心意图
165
+
166
+ **用户**:"@SingletonProto 和 @ContextProto 有什么区别?"
167
+
168
+ **分析**:问题关于核心装饰器和对象生命周期
169
+ **决策**:使用 `egg-core` skill
170
+
171
+ **用户**:"如何在 EGG 中创建模块?"
172
+
173
+ **分析**:问题关于模块架构(核心概念)
174
+ **决策**:使用 `egg-core` skill
175
+
176
+ ### 示例 2:明确的控制器意图
177
+
178
+ **用户**:"如何创建返回 JSON 的 HTTP controller?"
179
+
180
+ **分析**:问题关于实现特定协议(HTTP)
181
+ **决策**:使用 `egg-controller` skill
182
+
183
+ ### 示例 3:模糊意图(核心 > 控制器)
184
+
185
+ **用户**:"我需要创建一个可以被 HTTP 控制器使用的服务。如何实现?"
186
+
187
+ **分析**:用户需要理解核心概念(跨模块访问)AND 控制器实现
188
+ **决策**:首先使用 `egg-core` skill(基础)
189
+
190
+ **响应**:
191
+
192
+ 1. 解释 `@SingletonProto` 配合 `AccessLevel.PUBLIC` 使服务可访问
193
+ 2. 展示如何注入服务:`@Inject() myService: MyService`
194
+ 3. 后续:"现在你可以在 HTTPController 中注入此服务。实现详情请使用 `egg-controller` skill。"
195
+
196
+ ### 示例 4:显式控制器加上核心知识
197
+
198
+ **用户**:"如何在 HTTPController 中使用 @Inject 访问用户服务?"
199
+
200
+ **分析**:用户明确提及 HTTPController(控制器类型)但询问 @Inject(核心概念)
201
+ **决策**:使用 `egg-controller` skill(HTTPController 是明确意图)
202
+
203
+ **响应**:
204
+
205
+ 1. 展示配合 `@Inject` 的 HTTPController 实现
206
+ 2. 简要解释 @Inject 如何工作(核心概念摘要)
207
+ 3. 注意:"包含限定符的详细 @Inject 使用,请使用 `egg-core` skill"
208
+
209
+ ## 路由最佳实践
210
+
211
+ 1. **优先考虑显式控制器提及**:如果用户命名特定控制器(HTTP),即使涉及核心概念也使用控制器技能
212
+
213
+ 2. **基础先行**:如果实现前需要理解核心概念,先解释核心概念
214
+
215
+ 3. **简短上下文可以接受**:当路由到一个技能时,提及另一个技能的存在以供后续问题
216
+
217
+ 4. **混合响应可接受**:当意图真正混合时,提供两个技能的简短上下文但专注于主要意图
218
+
219
+ 5. **渐进式披露**:除非明确要求,不要同时加载两个技能。让用户引导探索
220
+
221
+ ---
222
+
223
+ ## 技能交互
224
+
225
+ 本技能(`egg` skill)应该:
226
+
227
+ - 为框架内部概念路由到 `egg-core` skill
228
+ - 为协议特定实现路由到 `egg-controller` skill
229
+ - 当意图模糊时提供决策指导
230
+ - 当存在有用的上下文时交叉引用技能
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: egg-controller
3
+ description: Use when creating API endpoints, implementing protocol handlers, or exposing interfaces for specific clients. Covers HTTP, MCP and Schedule controllers for EGG framework applications.
4
+ allowed-tools: Read
5
+ ---
6
+
7
+ # EGG 控制器
8
+
9
+ ---
10
+
11
+ ## 控制器选择决策树
12
+
13
+ ```
14
+ 需要暴露什么接口/客户端协议?
15
+
16
+ 1. HTTP 接口?例如 HTML/JSON/SSR/SSE,可以使用 HTTPController,参考 `references/http-controller.md`
17
+
18
+ 2. 定时任务,可以使用 Schedule,参考 `references/schedule.md`
19
+
20
+ 3. MCP 接口,可以使用 MCPController,参考 `references/mcp-controller.md`
21
+ ```
22
+
23
+ ---
24
+
25
+ ## 控制器快速参考
26
+
27
+ ### HTTPController
28
+
29
+ - **装饰器**:`@HTTPController`、`@HTTPMethod`
30
+ - **参数**:`@HTTPParam`、`@HTTPQuery`、`@HTTPBody`、`@HTTPHeaders`、`@Cookies`、`@Request`、`@Context`
31
+ - **详细文档**:`references/http-controller.md`
32
+
33
+ ### MCPController
34
+
35
+ - **装饰器**:`@MCPController`、`@MCPTool`、`@MCPPrompt`、`@MCPResource`
36
+ - **特点**:集成 LLM、Zod 验证、登录态支持
37
+ - **详细文档**:`references/mcp-controller.md`
38
+
39
+ ### Schedule
40
+
41
+ - **装饰器**:`@Schedule<T>`
42
+ - **模式**:Worker/All
43
+ - **详细文档**:`references/schedule.md`
44
+
45
+ ---
46
+
47
+ ## 常见问题排查
48
+
49
+ | 现象 | 原因 | 解决方案 |
50
+ | ---------------------- | ------------------------ | ------------------------------------------------------------------ |
51
+ | MCP 装饰器 import 报错 | 从 `'egg'` 导入 | MCP 装饰器从 `'@eggjs/tegg'` 导入,zod 从 `'@eggjs/tegg/zod'` 导入 |
52
+ | MCP Schema 报错 | 用了 `z.object()` 包装 | 直接用普通对象 `{ name: z.string() }` |
53
+ | 定时任务不生效 | 放在 `app/schedule` 目录 | 放在模块目录中,避免和 egg 默认扫描冲突 |
54
+
55
+ ---
56
+
57
+ ## 最佳实践
58
+
59
+ - **控制器精简**:业务逻辑委托给 Service 层
60
+ - **参数验证**:使用装饰器和类型定义
61
+ - **错误处理**:根据协议转换错误和响应码
62
+ - **RESTful 设计**:遵循 HTTP 方法和资源命名
63
+ - **响应一致性**:统一响应格式
64
+
65
+ ---
66
+
67
+ ## 参考资料
68
+
69
+ 详细的控制器开发文档:
70
+
71
+ - `references/http-controller.md` - HTTP 接口完整指南
72
+ - `references/mcp-controller.md` - MCP 接口开发
73
+ - `references/schedule.md` - 定时任务
74
+
75
+ 核心概念(`egg-core` skill):模块、依赖注入、对象生命周期