docs: add github service workflow contract
This commit is contained in:
parent
d419f452cc
commit
eed3967061
339
workflow/02_skills/github_service_request.json
Normal file
339
workflow/02_skills/github_service_request.json
Normal file
@ -0,0 +1,339 @@
|
||||
{
|
||||
"name": "robeing-github-service-request",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "skills/github/request",
|
||||
"responseMode": "responseNode",
|
||||
"options": {}
|
||||
},
|
||||
"id": "gh-req-001",
|
||||
"name": "Webhook In",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 1,
|
||||
"position": [200, 260]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const body = $json.body || {};\nreturn {\n intent: body.intent || '',\n slots: body.slots || {},\n user_id: body.user_id || '',\n channel: body.channel || 'slack',\n robeing_id: body.robeing_id || 'rb8001'\n};"
|
||||
},
|
||||
"id": "gh-req-002",
|
||||
"name": "Normalize Payload",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [440, 260]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const validIntents = new Set(['github_analyze', 'github_manage', 'git_ops']);\nreturn {\n ...$json,\n valid_intent: validIntents.has($json.intent)\n};"
|
||||
},
|
||||
"id": "gh-req-003",
|
||||
"name": "Validate Intent",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [700, 260]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"boolean": [
|
||||
{
|
||||
"value1": "={{ $json.valid_intent }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "gh-req-004",
|
||||
"name": "Intent OK?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [940, 260]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const slots = $json.slots || {};\nconst risk = slots.risk_level || 'low';\nconst requiresConfirmation = Boolean(slots.requires_confirmation);\nconst criticalActions = new Set(['force_push', 'reset_hard', 'delete_branch', 'delete_release']);\nconst action = slots.action || '';\nreturn {\n ...$json,\n risk_level: risk,\n denied: criticalActions.has(action) || risk === 'critical',\n requires_confirmation: requiresConfirmation || risk === 'high'\n};"
|
||||
},
|
||||
"id": "gh-req-005",
|
||||
"name": "Risk Decision",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1180, 260]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"boolean": [
|
||||
{
|
||||
"value1": "={{ $json.denied }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "gh-req-006",
|
||||
"name": "Denied?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [1420, 180]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"boolean": [
|
||||
{
|
||||
"value1": "={{ $json.requires_confirmation }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "gh-req-007",
|
||||
"name": "Needs Confirmation?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [1420, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"string": [
|
||||
{
|
||||
"value1": "={{ $json.intent }}",
|
||||
"operation": "equals",
|
||||
"value2": "github_analyze"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "gh-req-008",
|
||||
"name": "Route Analyze",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [1660, 500]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"string": [
|
||||
{
|
||||
"value1": "={{ $json.intent }}",
|
||||
"operation": "equals",
|
||||
"value2": "github_manage"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "gh-req-009",
|
||||
"name": "Route Manage",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [1900, 560]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ { success: false, message: 'unknown_intent', intent: $json.intent } }}"
|
||||
},
|
||||
"id": "gh-req-010",
|
||||
"name": "Reject Unknown Intent",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1,
|
||||
"position": [1180, 420]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ { success: false, message: 'critical_action_denied', intent: $json.intent, slots: $json.slots } }}"
|
||||
},
|
||||
"id": "gh-req-011",
|
||||
"name": "Reject Critical",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1,
|
||||
"position": [1660, 120]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ { success: false, message: 'confirmation_required', intent: $json.intent, slots: $json.slots } }}"
|
||||
},
|
||||
"id": "gh-req-012",
|
||||
"name": "Return Confirmation",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1,
|
||||
"position": [1660, 320]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ { success: true, dispatch: 'analyze_target', intent: $json.intent, slots: $json.slots } }}"
|
||||
},
|
||||
"id": "gh-req-013",
|
||||
"name": "Dispatch Analyze",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1,
|
||||
"position": [1900, 460]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ { success: true, dispatch: 'manage_resource', intent: $json.intent, slots: $json.slots } }}"
|
||||
},
|
||||
"id": "gh-req-014",
|
||||
"name": "Dispatch Manage",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1,
|
||||
"position": [2140, 520]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ { success: true, dispatch: 'run_git_operation', intent: $json.intent, slots: $json.slots } }}"
|
||||
},
|
||||
"id": "gh-req-015",
|
||||
"name": "Dispatch Git Ops",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1,
|
||||
"position": [2140, 620]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Webhook In": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Normalize Payload",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Normalize Payload": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Validate Intent",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Validate Intent": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Intent OK?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Intent OK?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Risk Decision",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Reject Unknown Intent",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Risk Decision": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Denied?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Denied?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Reject Critical",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Needs Confirmation?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Needs Confirmation?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Return Confirmation",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Route Analyze",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Route Analyze": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Dispatch Analyze",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Route Manage",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Route Manage": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Dispatch Manage",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Dispatch Git Ops",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {},
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "robeing-skills"
|
||||
}
|
||||
}
|
||||
125
workflow/02_skills/github_service_request.md
Normal file
125
workflow/02_skills/github_service_request.md
Normal file
@ -0,0 +1,125 @@
|
||||
# github_service_request 워크플로우
|
||||
|
||||
## 목적
|
||||
GitHub 관련 의도(`github_analyze`, `github_manage`, `git_ops`)를 하나의 서비스 계약으로 묶는다. `github_service.py`는 LLM이 SKILL.md 기반으로 분류한 intent와 slots를 받아, 분석/관리/실행을 위험도 기준으로 처리한다.
|
||||
|
||||
## 흐름
|
||||
```text
|
||||
Webhook In -> Normalize Payload -> Validate Intent/Slots -> Risk Gate
|
||||
-> (github_analyze) Analyze Target
|
||||
-> (github_manage) Manage GitHub Resource
|
||||
-> (git_ops) Run Git Operation
|
||||
-> Return Result
|
||||
```
|
||||
|
||||
## 주요 노드
|
||||
| 노드 | 설명 |
|
||||
|---|---|
|
||||
| Webhook In | `POST /skills/github/request` 수신 |
|
||||
| Normalize Payload | intent, slots, user_id, channel, robeing_id 정규화 |
|
||||
| Validate Intent/Slots | `github_analyze`, `github_manage`, `git_ops`만 허용. `target_url`, `repo`, `action`, `risk_level`, `requires_confirmation` 확인 |
|
||||
| Risk Gate | `risk_level`과 `requires_confirmation`으로 실행 가능 여부 판정 |
|
||||
| Analyze Target | 읽기 전용 GitHub 분석. repo/PR/issue/blob/tree 요약, 코드 리뷰 초안 |
|
||||
| Manage GitHub Resource | PR/Issue/Branch 메타 작업 |
|
||||
| Run Git Operation | clone/status/log/diff/pull/commit/push 등 git 명령 실행 |
|
||||
| Return Result | 실행 결과 또는 확인 요청 반환 |
|
||||
|
||||
## `github_service.py` 인터페이스 계약
|
||||
|
||||
### 핵심 함수 시그니처
|
||||
```python
|
||||
async def execute_github_intent(
|
||||
*,
|
||||
intent: str,
|
||||
slots: dict[str, object],
|
||||
user_id: str,
|
||||
channel: str,
|
||||
robeing_id: str = "rb8001",
|
||||
) -> dict[str, object]:
|
||||
...
|
||||
|
||||
async def analyze_target(
|
||||
*,
|
||||
target_url: str | None,
|
||||
repo: str | None,
|
||||
resource_type: str,
|
||||
action: str,
|
||||
user_id: str,
|
||||
) -> dict[str, object]:
|
||||
...
|
||||
|
||||
async def manage_resource(
|
||||
*,
|
||||
repo: str,
|
||||
resource_type: str,
|
||||
action: str,
|
||||
slots: dict[str, object],
|
||||
user_id: str,
|
||||
) -> dict[str, object]:
|
||||
...
|
||||
|
||||
async def run_git_operation(
|
||||
*,
|
||||
repo: str | None,
|
||||
action: str,
|
||||
slots: dict[str, object],
|
||||
user_id: str,
|
||||
) -> dict[str, object]:
|
||||
...
|
||||
|
||||
def evaluate_risk(
|
||||
*,
|
||||
intent: str,
|
||||
action: str,
|
||||
slots: dict[str, object],
|
||||
) -> dict[str, object]:
|
||||
...
|
||||
```
|
||||
|
||||
### slots 계약
|
||||
- `target_url`: GitHub URL. repo/blob/tree/pull/issue/commit 중 하나
|
||||
- `repo`: `owner/name`
|
||||
- `resource_type`: `repo|pull|issue|commit|blob|tree|branch`
|
||||
- `action`: `summarize|review|history|clone|status|diff|pull|commit|push|create_pr|comment_pr|close_issue`
|
||||
- `risk_level`: `low|medium|high|critical`
|
||||
- `requires_confirmation`: `true|false`
|
||||
- `branch`: 선택
|
||||
- `pr_number`, `issue_number`, `commit_sha`, `path`: 리소스별 선택
|
||||
|
||||
### 안전 계약
|
||||
- `low`: 자동 실행 가능. 읽기/분석/조회
|
||||
- `medium`: 로컬 변경 가능. 원격 반영 전 확인 필요
|
||||
- `high`: push, merge, close, delete 계열. 확인 필수
|
||||
- `critical`: `push --force`, `reset --hard`, branch delete, release delete. 기본 거부
|
||||
|
||||
## 인바운드 payload 예시
|
||||
```json
|
||||
{
|
||||
"intent": "github_analyze",
|
||||
"slots": {
|
||||
"target_url": "https://github.com/owner/repo",
|
||||
"repo": "owner/repo",
|
||||
"resource_type": "repo",
|
||||
"action": "summarize",
|
||||
"risk_level": "low",
|
||||
"requires_confirmation": false
|
||||
},
|
||||
"user_id": "uuid",
|
||||
"channel": "slack",
|
||||
"robeing_id": "rb8001"
|
||||
}
|
||||
```
|
||||
|
||||
## 엔드포인트
|
||||
- 인바운드: `POST /skills/github/request`
|
||||
- 내부 실행: `github_service.py`
|
||||
|
||||
## 적용 기준
|
||||
- 1차 분류 기준은 `DOCS/skills/{skill-name}/SKILL.md`
|
||||
- 이 워크플로우는 LLM이 분류한 intent를 실행하는 계약이다
|
||||
- 정규식 예외, URL 화이트리스트, command 하드코딩으로 intent를 덮어쓰지 않는다
|
||||
|
||||
## 관련 문서
|
||||
- [skill_calendar_request](./skill_calendar_request.md)
|
||||
- [skill_email_send_request](./skill_email_send_request.md)
|
||||
- [../README.md](../README.md)
|
||||
@ -35,7 +35,11 @@
|
||||
- 구글 캘린더 연동을 위한 브리지 워크플로우입니다.
|
||||
- 일정 조회, 등록, 삭제 요청을 `skill-calendar` 서비스로 전달합니다.
|
||||
|
||||
### 5. RAG 워크플로우 인덱스 (`03_rag/README.md`)
|
||||
### 5. GitHub 스킬 브리지 (`02_skills/github_service_request.json`)
|
||||
- GitHub 분석/관리/git 실행 의도를 하나의 서비스 계약으로 정규화합니다.
|
||||
- `github_analyze`, `github_manage`, `git_ops` intent와 `risk_level`, `requires_confirmation` 슬롯을 받아 안전 정책에 따라 실행 또는 확인 요청으로 분기합니다.
|
||||
|
||||
### 6. RAG 워크플로우 인덱스 (`03_rag/README.md`)
|
||||
- Company X 내부 문서를 로빙이 읽고 답변하도록 만드는 RAG 흐름의 진입점입니다.
|
||||
- 업로드, 검색, Grounding, 임베딩 브리지의 역할을 분리해서 봅니다.
|
||||
- 세부 절차는 각 흐름 문서에서 확인합니다.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user