Skip to content

Commit 5c01543

Browse files
committed
形固定バージョン
1 parent d276584 commit 5c01543

File tree

4 files changed

+153
-100
lines changed

4 files changed

+153
-100
lines changed

backend/app/core/prompts.py

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -75,55 +75,69 @@
7575
7676
参考:{baseline_json}
7777
78-
## CRITICAL: ノード数の配分(下に行くほど多く)
79-
80-
**【必須】Tier 0からTier 5までのノード数配分(厳格に遵守):**
81-
- Tier 0(基礎): 1-2個 ← 最上層、最小
82-
- Tier 1(初級): 2-4個
83-
- Tier 2(中級): 4-8個
84-
- Tier 3(応用): 8-12個
85-
- Tier 4(高度): 12-16個
86-
- Tier 5(極限): 16-20個 ← 最下層、最大
78+
## CRITICAL: ノード数の配分(Tier 0からTier 7:合計最大50個)
79+
80+
**【必須】Tier 0からTier 7までのノード数配分(厳格に遵守):**
81+
- Tier 0: 1個 ← 頂点
82+
- Tier 1: 2個
83+
- Tier 2: 3個
84+
- Tier 3: 5個
85+
- Tier 4: 7個
86+
- Tier 5: 9個
87+
- Tier 6: 11個
88+
- Tier 7: 12個 ← 底辺
89+
**合計 50個ちょうど、またはそれ以下** で構成すること。
8790
8891
**三角形△構造の形成:**
89-
上に行くほど狭く、下に行くほど広い逆三角形を形成。
90-
各Tierで**指定範囲内でできるだけ多くのノード**を生成すること。
92+
下層に行くほど「必ず」ノード数を増やし、綺麗なピラミッド型を維持してください。
9193
92-
**依存関係のルール:**
93-
- 各ノードのprerequisitesは、**必ず一つ前のTierのノード**のみを指定
94+
**依存関係の厳格ルール:**
95+
- prerequisitesは、**必ず一つ前のTierのノードID**から1つ以上選択。
96+
- Tier n は必ず Tier n-1 に依存する(階層を飛ばさない)。
9497
- Tier 0: prerequisites:[]
9598
- Tier 1: prerequisites:[Tier 0のノード]
9699
- Tier 2: prerequisites:[Tier 1のノード]
97100
- Tier 3: prerequisites:[Tier 2のノード]
98101
- Tier 4: prerequisites:[Tier 3のノード]
99102
- Tier 5: prerequisites:[Tier 4のノード]
103+
- Tier 6: prerequisites:[Tier 5のノード]
104+
- Tier 7: prerequisites:[Tier 6のノード]
100105
101-
**重要:** Tierが深くなるほど、ノード数を増やすこと。これにより下に行くほど横に広がる三角形△を形成する。
102-
103-
## スキル名の命名規則(必須):
104-
- **キーワード中心、3-5単語以内**
105-
- **名詞・技術用語のみ、動詞は不要**
106-
- 悪い例: "型システムを活用した堅牢なAPI型定義の実装" (長すぎる)
107-
- 良い例: "TypeScript型設計" (簡潔)
108-
- 良い例: "REST API設計" (簡潔)
109-
110-
## 説明(desc)の要件:
111-
- **スキル名で伝えきれない詳細情報を簡潔に記載**
112-
- 30文字以内の簡潔な説明
113-
- 何ができるようになるかのポイントのみ
106+
## 命名および説明のルール(重要):
107+
1. **抽象語の禁止**: 「基礎」「応用」「初級」「〇〇の理解」は使用禁止。
108+
2. **具体的技術名**: 「SSR/CSR」「DBインデックス」「OIDC」「IaC」等、具体的な技術・手法を名称にする。
109+
3. **能力ベースのdesc**: 30文字以内で「〇〇を使って△△を解決できる」という成果を記述。
110+
4. **ID命名**: `t{{tier}}_{{keyword}}` (例: `t0_web_rendering`, `t7_distributed_tracing`)
111+
5. **スキル名は3-5単語以内**、名詞・技術用語のみ(動詞は不要)。
114112
115113
## 出力ルール:
116-
1. **合計50-60ノード程度**(Tier 0からTier 5まで、下層ほど多く)
117-
2. completed:trueは習得済みのみ
118-
3. prerequisitesを正確に設定
119-
4. **CRITICAL: 下層(Tier 3-5)ほど、指定範囲の上限に近い数を生成**
114+
1. **合計50個まで**(Tier 0からTier 7まで)
115+
2. **completed判定(重要):**
116+
- 「習得済み」に記載されているスキルと、生成するノードの技術内容を照らし合わせる
117+
- ユーザーが既に使用している技術・フレームワークに該当するノードは`completed:true`
118+
- それ以外はすべて`completed:false`
119+
- 例: 習得済みに「JavaScript」があれば、「JavaScript基礎」ノードは`completed:true`
120+
3. prerequisitesを正確に設定(必ず一つ前のTierのノードから選択)
121+
4. Tier 0から順にTier 7まで生成し、50個を超えたら直ちに停止
122+
5. Tier 8以上の階層は絶対に生成しないでください。
123+
124+
| Tier | ノード数 | 累積合計 | prerequisites |
125+
|------|---------|---------|---------------|
126+
| 0 | 1 | 1 | [] |
127+
| 1 | 2 | 3 | [Tier 0] |
128+
| 2 | 3 | 6 | [Tier 1] |
129+
| 3 | 5 | 11 | [Tier 2] |
130+
| 4 | 7 | 18 | [Tier 3] |
131+
| 5 | 9 | 27 | [Tier 4] |
132+
| 6 | 11 | 38 | [Tier 5] |
133+
| 7 | 12 | 50 | [Tier 6] |
120134
121135
## 出力形式(JSON)
122136
```json
123137
{{"nodes":[{{"id":"skill-id","name":"キーワード(3-5単語)","completed":true/false,"desc":"30文字以内の簡潔な説明","prerequisites":[],"hours":30}}],"edges":[{{"from":"a","to":"b"}}],"metadata":{{"total_nodes":30,"completed_nodes":1,"progress_percentage":3.3,"next_recommended":["x","y","z"]}}}}
124138
```
125139
126-
**重要: Tier 0からTier 5まで、下層ほどノード数を増やし、段階的に広がる三角形△を形成(合計50-60ノード程度)**。JSON形式のみ出力してください(説明不要)。"""
140+
**重要: Tier 0からTier 7まで、下層ほどノード数を増やし、段階的に広がる三角形△を形成(合計50-60ノード程度)**。JSON形式のみ出力してください(説明不要)。"""
127141

128142

129143
SKILL_TREE_TEMPLATE = ChatPromptTemplate.from_messages(

backend/app/core/prompts_streaming.py

Lines changed: 59 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -9,79 +9,81 @@
99
1010
参考:{baseline_json}
1111
12-
## CRITICAL: ノード数の配分(下に行くほど多く)
13-
14-
**【必須】Tier 0からTier 5までのノード数配分(厳格に遵守):**
15-
- Tier 0(基礎): 1-2個 ← 最上層、最小
16-
- Tier 1(初級): 2-4個
17-
- Tier 2(中級): 4-8個
18-
- Tier 3(応用): 8-12個
19-
- Tier 4(高度): 12-16個
20-
- Tier 5(極限): 16-20個 ← 最下層、最大
12+
## CRITICAL: ノード数の配分(Tier 0からTier 7:合計最大50個)
13+
14+
**【必須】Tier 0からTier 7までのノード数配分(厳格に遵守):**
15+
- Tier 0: 1個 ← 頂点
16+
- Tier 1: 2個
17+
- Tier 2: 3個
18+
- Tier 3: 5個
19+
- Tier 4: 7個
20+
- Tier 5: 9個
21+
- Tier 6: 11個
22+
- Tier 7: 12個 ← 底辺
23+
**合計 50個ちょうど、またはそれ以下** で構成すること。
2124
2225
**三角形△構造の形成:**
23-
上に行くほど狭く、下に行くほど広い逆三角形を形成。
24-
各Tierで**指定範囲内でできるだけ多くのノード**を生成すること。
26+
下層に行くほど「必ず」ノード数を増やし、綺麗なピラミッド型を維持してください。
2527
26-
**依存関係のルール:**
27-
- 各ノードのprerequisitesは、**必ず一つ前のTierのノード**のみを指定
28+
**依存関係の厳格ルール:**
29+
- prerequisitesは、**必ず一つ前のTierのノードID**から1つ以上選択。
30+
- Tier n は必ず Tier n-1 に依存する(階層を飛ばさない)。
2831
- Tier 0: prerequisites:[]
2932
- Tier 1: prerequisites:[Tier 0のノード]
3033
- Tier 2: prerequisites:[Tier 1のノード]
3134
- Tier 3: prerequisites:[Tier 2のノード]
3235
- Tier 4: prerequisites:[Tier 3のノード]
3336
- Tier 5: prerequisites:[Tier 4のノード]
37+
- Tier 6: prerequisites:[Tier 5のノード]
38+
- Tier 7: prerequisites:[Tier 6のノード]
3439
35-
**重要:** Tierが深くなるほど、ノード数を増やすこと。これにより下に行くほど横に広がる三角形△を形成する。
36-
37-
## スキル名の命名規則(必須):
38-
- **キーワード中心、3-5単語以内**
39-
- **名詞・技術用語のみ、動詞は不要**
40-
41-
## 説明(desc)の要件:
42-
- **スキル名で伝えきれない詳細情報を簡潔に記載**
43-
- 30文字以内の簡潔な説明
44-
- 何ができるようになるかのポイントのみ
45-
46-
## 生成手順(厳守):
47-
1. Tier 0: 1-2個のノードを出力(少なく)
48-
2. Tier 1: 2-4個のノードを出力
49-
3. Tier 2: 4-8個のノードを出力(範囲内でできるだけ多く)
50-
4. Tier 3: 8-12個のノードを出力(範囲内でできるだけ多く)
51-
5. Tier 4: 12-16個のノードを出力(範囲内でできるだけ多く)
52-
6. Tier 5: 16-20個のノードを出力(最も多く)
40+
## 命名および説明のルール(重要):
41+
1. **抽象語の禁止**: 「基礎」「応用」「初級」「〇〇の理解」は使用禁止。
42+
2. **具体的技術名**: 「SSR/CSR」「DBインデックス」「OIDC」「IaC」等、具体的な技術・手法を名称にする。
43+
3. **能力ベースのdesc**: 30文字以内で「〇〇を使って△△を解決できる」という成果を記述。
44+
4. **ID命名**: `t{{tier}}_{{keyword}}` (例: `t0_web_rendering`, `t7_distributed_tracing`)
45+
5. **スキル名は3-5単語以内**、名詞・技術用語のみ(動詞は不要)。
5346
54-
**CRITICAL: 下層(Tier 3-5)ほど、指定範囲の上限に近い数を生成**
55-
各ノードのprerequisites: [一つ前のTierのノード]
47+
## 生成手順と出力順序:
48+
- Tier 0 から順に Tier 7 まで JSON Lines 形式で出力。
49+
- 50個を超えないよう、各Tierのノード数を厳密に管理してください。
50+
- Tier 7 を生成し終えたら、直ちに停止してください。
5651
5752
## 出力ルール:
58-
1. **合計50-60ノード程度**(Tier 0からTier 5まで、下層ほど多く)
59-
2. completed:trueは習得済みのみ
60-
3. **出力順序**: Tier 0 → Tier 1 → Tier 2 → Tier 3 → Tier 4 → Tier 5
53+
1. **合計50個まで**(Tier 0からTier 7まで)
54+
2. **completed判定(重要):**
55+
- 「習得済み」に記載されているスキルと、生成するノードの技術内容を照らし合わせる
56+
- ユーザーが既に使用している技術・フレームワークに該当するノードは`completed:true`
57+
- それ以外はすべて`completed:false`
58+
- 例: 習得済みに「JavaScript」があれば、「JavaScript基礎」ノードは`completed:true`
59+
3. **出力順序**: Tier 0 → Tier 1 → Tier 2 → Tier 3 → Tier 4 → Tier 5 → Tier 6 → Tier 7
6160
4. JSON Lines形式: 1行1ノード、```jsonは不要
61+
5. Tier 8以上の階層は絶対に生成しないでください。
6262
6363
## 例(3ノード - フォーマット参考のみ):
64-
{{"type":"node","id":"web_foundation","name":"HTTP/HTML/CSS基礎","completed":true,"desc":"HTTPとHTMLの仕組み理解","prerequisites":[],"hours":30}}
65-
{{"type":"node","id":"web_js_basic","name":"JavaScript基礎","completed":false,"desc":"変数・関数・非同期処理の実装","prerequisites":["web_foundation"],"hours":25}}
66-
{{"type":"node","id":"web_react","name":"React設計","completed":false,"desc":"Component設計とHooks活用","prerequisites":["web_js_basic"],"hours":30}}
67-
{{"type":"edge","from":"web_foundation","to":"web_js_basic"}}
68-
{{"type":"edge","from":"web_js_basic","to":"web_react"}}
69-
{{"type":"metadata","total_nodes":50,"completed_nodes":1,"progress_percentage":2.0,"next_recommended":["web_js_basic"]}}
70-
71-
**【CRITICAL】Tier 0からTier 5まで、深くなるほどノード数を増やす:**
72-
73-
| Tier | ノード数 | prerequisites |
74-
|------|---------|---------------|
75-
| 0 | 1-2個 | [] |
76-
| 1 | 2-4個 | [Tier 0] |
77-
| 2 | 4-8個 | [Tier 1] |
78-
| 3 | 8-12個 | [Tier 2] |
79-
| 4 | 12-16個 | [Tier 3] |
80-
| 5 | 16-20個 | [Tier 4] |
81-
82-
**合計50-60ノード** - 下に行くほど数を増やし、三角形△を形成
83-
84-
名前は短く(3-5単語)、詳細はdescで(30文字以内)。
64+
{{"type":"node","id":"t0_http_basics","name":"HTTP/REST API","completed":true,"desc":"HTTPメソッドとステータスコードを使ったAPI設計","prerequisites":[],"hours":30}}
65+
{{"type":"node","id":"t1_async_programming","name":"非同期処理","completed":false,"desc":"Promise/async-awaitで並行処理を制御","prerequisites":["t0_http_basics"],"hours":25}}
66+
{{"type":"node","id":"t2_react_hooks","name":"React Hooks","completed":false,"desc":"useState/useEffectで状態管理とライフサイクル制御","prerequisites":["t1_async_programming"],"hours":30}}
67+
{{"type":"edge","from":"t0_http_basics","to":"t1_async_programming"}}
68+
{{"type":"edge","from":"t1_async_programming","to":"t2_react_hooks"}}
69+
{{"type":"metadata","total_nodes":50,"completed_nodes":1,"progress_percentage":2.0,"next_recommended":["t1_async_programming"]}}
70+
71+
**【CRITICAL】Tier 0からTier 7まで、深くなるほどノード数を増やす:**
72+
73+
| Tier | ノード数 | 累積合計 | prerequisites |
74+
|------|---------|---------|---------------|
75+
| 0 | 1 | 1 | [] |
76+
| 1 | 2 | 3 | [Tier 0] |
77+
| 2 | 3 | 6 | [Tier 1] |
78+
| 3 | 5 | 11 | [Tier 2] |
79+
| 4 | 7 | 18 | [Tier 3] |
80+
| 5 | 9 | 27 | [Tier 4] |
81+
| 6 | 11 | 38 | [Tier 5] |
82+
| 7 | 12 | 50 | [Tier 6] |
83+
84+
**合計50個** - 下に行くほど数を増やし、三角形△を形成
85+
86+
名前は具体的技術名(3-5単語)、詳細はdescで能力ベース記述(30文字以内)。
8587
8688
説明や```json不要。上記の構成で1行1JSONを出力開始:
8789
"""

frontend/src/features/skill-tree/utils/converter.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ export function convertApiNodesToCanvasNodes(
6262
// 全ノードの基本tierを計算
6363
apiNodes.forEach((node) => calculateBaseTier(node.id));
6464

65-
// Note: プロンプト側でTier 0からTier 5まで、下層ほどノード数を増やすよう指定
66-
// Tier 0: 1-2個, Tier 1: 2-4個, Tier 2: 4-8個, Tier 3: 8-12個, Tier 4: 12-16個, Tier 5: 16-20個
65+
// Note: プロンプト側でTier 0からTier 7まで、下層ほどノード数を増やすよう指定(合計50ノード)
66+
// Tier 0: 1個, Tier 1: 2個, Tier 2: 3個, Tier 3: 5個, Tier 4: 7個, Tier 5: 9個, Tier 6: 11個, Tier 7: 12個
6767
// 各tierは必ず一つ前のtierのノードのみをprerequisitesに指定することで、
68-
// 下に行くほどノード数が多くなり、自然に横幅が広がる三角形△を形成
68+
// 下に行くほどノード数が多くなり、自然に横幅が広がる三角形△(ピラミッド)を形成
6969

7070
// Step 2: tierごとにノードをグループ化
7171
const nodesByTier = new Map<number, ApiSkillNode[]>();
@@ -82,16 +82,17 @@ export function convertApiNodesToCanvasNodes(
8282

8383
// Y座標: 地面(y=620)に埋もれないように上部に配置
8484
// tier 0 = y: -400 (画面上部), 以降 +110pxずつ下へ
85-
const TIER_HEIGHT = 110; // 間隔を詰めて最大9tier対応 (y=-400 → +510 = y=110まで)
85+
const TIER_HEIGHT = 110; // 間隔を詰めてTier 0~7対応 (合計8階層)
8686
const BASE_Y = -400; // 起点をさらに上部に
8787

8888
nodesByTier.forEach((nodes, tier) => {
8989
const yPos = BASE_Y + tier * TIER_HEIGHT;
9090
const nodeCount = nodes.length;
9191

9292
// X座標: tierが深くなるほど横幅を広げて三角形△を形成
93-
// Tier 0: 狭い幅(頂点)→ Tier 5: 広い幅(底辺)
94-
const baseSpacing = 100 + tier * 25; // Tier 0:100px → Tier 5:225px
93+
// Tier 0: 頂点(狭い) → Tier 7: 底辺(広い)
94+
// 合計50ノード: Tier 0=1, Tier 1=2, Tier 2=3, Tier 3=5, Tier 4=7, Tier 5=9, Tier 6=11, Tier 7=12
95+
const baseSpacing = 80 + tier * 30; // Tier 0:80px → Tier 7:290px
9596
const tierWidth = nodeCount > 1 ? (nodeCount - 1) * baseSpacing : 200;
9697
const spacing = nodeCount > 1 ? tierWidth / (nodeCount - 1) : 0;
9798
const startX = nodeCount > 1 ? -tierWidth / 2 : 0;

frontend/src/lib/api/skillTree.ts

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,28 @@ export function streamSkillTree(
198198
}
199199
};
200200

201-
eventSource.onerror = (error) => {
202-
console.error("EventSource エラー:", error);
201+
eventSource.onerror = (event) => {
202+
// EventSourceのエラーイベントは詳細情報が少ないため、readyStateで状態を判定
203+
const readyState = eventSource.readyState;
204+
const stateNames = ["CONNECTING", "OPEN", "CLOSED"];
205+
const stateName = stateNames[readyState] || "UNKNOWN";
206+
207+
console.error("EventSource エラー:", {
208+
readyState,
209+
stateName,
210+
url: eventSource.url,
211+
event,
212+
});
213+
203214
eventSource.close();
204-
onError(new Error("ストリーミング接続エラー"));
215+
216+
// readyStateに応じて適切なエラーメッセージを生成
217+
const errorMessage =
218+
readyState === EventSource.CONNECTING
219+
? "ストリーミング接続を再試行中に失敗しました"
220+
: "ストリーミング接続エラーが発生しました";
221+
222+
onError(new Error(errorMessage));
205223
};
206224

207225
return eventSource;
@@ -363,10 +381,28 @@ export function streamSkillTreeBuffered(
363381
}
364382
};
365383

366-
eventSource.onerror = (error) => {
367-
console.error("EventSource エラー:", error);
384+
eventSource.onerror = (event) => {
385+
// EventSourceのエラーイベントは詳細情報が少ないため、readyStateで状態を判定
386+
const readyState = eventSource.readyState;
387+
const stateNames = ["CONNECTING", "OPEN", "CLOSED"];
388+
const stateName = stateNames[readyState] || "UNKNOWN";
389+
390+
console.error("EventSource エラー:", {
391+
readyState,
392+
stateName,
393+
url: eventSource.url,
394+
event,
395+
});
396+
368397
eventSource.close();
369-
onError(new Error("ストリーミング接続エラー"));
398+
399+
// readyStateに応じて適切なエラーメッセージを生成
400+
const errorMessage =
401+
readyState === EventSource.CONNECTING
402+
? "ストリーミング接続を再試行中に失敗しました"
403+
: "ストリーミング接続エラーが発生しました";
404+
405+
onError(new Error(errorMessage));
370406
};
371407

372408
return eventSource;

0 commit comments

Comments
 (0)