FEAT(k8s): add YAML manager agent
Major additions: - New Groq agent: YAML Manager - Generates Kubernetes YAML files (Deployment, Service, Ingress) - Organizes YAMLs in folder structure by application - Applies YAMLs directly to cluster using Python K8s client - Shows Git file changes in UI with diff display - YAML Management MCP Tools: - yaml_create_deployment: Create Deployment YAML - yaml_create_service: Create Service YAML - yaml_create_ingress: Create Ingress YAML with TLS - yaml_apply_to_cluster: Apply YAMLs to K8s cluster - git_show_file_changes: Display Git changes in UI - RBAC Permissions: - mas-writer ClusterRole: Write permissions for Groq agents - Create/update/delete permissions for core K8s resources - Namespace and ArgoCD Application management - mas ServiceAccount has both read (viewer) and write (writer) roles - UI Updates: - Added YAML Manager to agent list - Updated agent icons and names (Qwen → Groq) - Display file changes with syntax highlighting - Workflow Integration: - YAML Manager node in LangGraph workflow - Orchestrator routes YAML/K8s keywords to YAML Manager - Tool binding for YAML-specific operations Dependencies: - Added PyYAML==6.0.2 for YAML generation
This commit is contained in:
@@ -16,5 +16,5 @@ commonLabels:
|
||||
# 이미지 태그 설정 (ArgoCD Image Updater가 자동으로 업데이트)
|
||||
images:
|
||||
- name: gitea0213.kro.kr/bluemayne/mas
|
||||
newTag: main-sha-22b0840afde85b864df82a5c9408da8c78e28195
|
||||
newTag: main-sha-c7bd76b5a29025a1fe57e10fc44fd6afd37fd947
|
||||
|
||||
|
||||
@@ -65,4 +65,63 @@ subjects:
|
||||
- kind: ServiceAccount
|
||||
name: mas
|
||||
namespace: mas
|
||||
---
|
||||
# YAML Manager용 write 권한 (Groq 에이전트)
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: mas-writer
|
||||
rules:
|
||||
# Write access for YAML Manager
|
||||
- apiGroups: [""]
|
||||
resources:
|
||||
- pods
|
||||
- services
|
||||
- configmaps
|
||||
- secrets
|
||||
verbs: ["create", "update", "patch", "delete"]
|
||||
|
||||
- apiGroups: ["apps"]
|
||||
resources:
|
||||
- deployments
|
||||
- statefulsets
|
||||
- daemonsets
|
||||
- replicasets
|
||||
verbs: ["create", "update", "patch", "delete"]
|
||||
|
||||
- apiGroups: ["networking.k8s.io"]
|
||||
resources:
|
||||
- ingresses
|
||||
verbs: ["create", "update", "patch", "delete"]
|
||||
|
||||
- apiGroups: ["batch"]
|
||||
resources:
|
||||
- jobs
|
||||
- cronjobs
|
||||
verbs: ["create", "update", "patch", "delete"]
|
||||
|
||||
# Namespace management
|
||||
- apiGroups: [""]
|
||||
resources:
|
||||
- namespaces
|
||||
verbs: ["create", "update", "patch"]
|
||||
|
||||
# ArgoCD Application management
|
||||
- apiGroups: ["argoproj.io"]
|
||||
resources:
|
||||
- applications
|
||||
verbs: ["create", "update", "patch", "delete"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: mas-writer-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: mas-writer
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: mas
|
||||
namespace: mas
|
||||
|
||||
|
||||
@@ -583,6 +583,313 @@ def docker_list_images(registry: str = "gitea0213.kro.kr") -> str:
|
||||
return f"❌ Docker error: {str(e)}"
|
||||
|
||||
|
||||
# === 7. YAML Management MCP Tools ===
|
||||
@tool
|
||||
def yaml_create_deployment(
|
||||
app_name: str,
|
||||
image: str,
|
||||
replicas: int = 1,
|
||||
port: int = 8080,
|
||||
namespace: str = "default",
|
||||
env_vars: str = ""
|
||||
) -> str:
|
||||
"""
|
||||
Create Kubernetes Deployment YAML file.
|
||||
Args:
|
||||
app_name: Application name
|
||||
image: Container image (e.g., myregistry/myapp:v1.0)
|
||||
replicas: Number of replicas (default: 1)
|
||||
port: Container port (default: 8080)
|
||||
namespace: Namespace (default: default)
|
||||
env_vars: Environment variables as JSON string (e.g., '{"KEY": "value"}')
|
||||
"""
|
||||
try:
|
||||
import yaml as yaml_lib
|
||||
|
||||
# Parse env vars
|
||||
env_list = []
|
||||
if env_vars:
|
||||
env_dict = json.loads(env_vars)
|
||||
env_list = [{"name": k, "value": str(v)} for k, v in env_dict.items()]
|
||||
|
||||
deployment = {
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": app_name,
|
||||
"namespace": namespace,
|
||||
"labels": {"app": app_name}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": replicas,
|
||||
"selector": {"matchLabels": {"app": app_name}},
|
||||
"template": {
|
||||
"metadata": {"labels": {"app": app_name}},
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": app_name,
|
||||
"image": image,
|
||||
"ports": [{"containerPort": port, "name": "http"}],
|
||||
"env": env_list
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yaml_content = yaml_lib.dump(deployment, default_flow_style=False, sort_keys=False)
|
||||
|
||||
# Save to file
|
||||
repo_path = "/app/repos/cluster-infrastructure"
|
||||
file_path = f"applications/{app_name}/deployment.yaml"
|
||||
full_path = os.path.join(repo_path, file_path)
|
||||
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
||||
|
||||
with open(full_path, "w") as f:
|
||||
f.write(yaml_content)
|
||||
|
||||
return f"✅ Created Deployment YAML:\n```yaml\n{yaml_content}\n```\n📁 Saved to: {file_path}"
|
||||
except Exception as e:
|
||||
return f"❌ Error creating deployment YAML: {str(e)}"
|
||||
|
||||
|
||||
@tool
|
||||
def yaml_create_service(
|
||||
app_name: str,
|
||||
port: int = 80,
|
||||
target_port: int = 8080,
|
||||
service_type: str = "ClusterIP",
|
||||
namespace: str = "default"
|
||||
) -> str:
|
||||
"""
|
||||
Create Kubernetes Service YAML file.
|
||||
Args:
|
||||
app_name: Application name
|
||||
port: Service port (default: 80)
|
||||
target_port: Target container port (default: 8080)
|
||||
service_type: Service type (ClusterIP, NodePort, LoadBalancer) (default: ClusterIP)
|
||||
namespace: Namespace (default: default)
|
||||
"""
|
||||
try:
|
||||
import yaml as yaml_lib
|
||||
|
||||
service = {
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
"metadata": {
|
||||
"name": app_name,
|
||||
"namespace": namespace,
|
||||
"labels": {"app": app_name}
|
||||
},
|
||||
"spec": {
|
||||
"type": service_type,
|
||||
"selector": {"app": app_name},
|
||||
"ports": [{
|
||||
"port": port,
|
||||
"targetPort": target_port,
|
||||
"name": "http"
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
yaml_content = yaml_lib.dump(service, default_flow_style=False, sort_keys=False)
|
||||
|
||||
# Save to file
|
||||
repo_path = "/app/repos/cluster-infrastructure"
|
||||
file_path = f"applications/{app_name}/service.yaml"
|
||||
full_path = os.path.join(repo_path, file_path)
|
||||
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
||||
|
||||
with open(full_path, "w") as f:
|
||||
f.write(yaml_content)
|
||||
|
||||
return f"✅ Created Service YAML:\n```yaml\n{yaml_content}\n```\n📁 Saved to: {file_path}"
|
||||
except Exception as e:
|
||||
return f"❌ Error creating service YAML: {str(e)}"
|
||||
|
||||
|
||||
@tool
|
||||
def yaml_create_ingress(
|
||||
app_name: str,
|
||||
host: str,
|
||||
service_port: int = 80,
|
||||
namespace: str = "default",
|
||||
tls_enabled: bool = True
|
||||
) -> str:
|
||||
"""
|
||||
Create Kubernetes Ingress YAML file.
|
||||
Args:
|
||||
app_name: Application name
|
||||
host: Ingress hostname (e.g., myapp.example.com)
|
||||
service_port: Service port (default: 80)
|
||||
namespace: Namespace (default: default)
|
||||
tls_enabled: Enable TLS/HTTPS (default: True)
|
||||
"""
|
||||
try:
|
||||
import yaml as yaml_lib
|
||||
|
||||
ingress = {
|
||||
"apiVersion": "networking.k8s.io/v1",
|
||||
"kind": "Ingress",
|
||||
"metadata": {
|
||||
"name": f"{app_name}-ingress",
|
||||
"namespace": namespace,
|
||||
"labels": {"app": app_name},
|
||||
"annotations": {
|
||||
"cert-manager.io/cluster-issuer": "letsencrypt-prod"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"ingressClassName": "nginx",
|
||||
"rules": [{
|
||||
"host": host,
|
||||
"http": {
|
||||
"paths": [{
|
||||
"path": "/",
|
||||
"pathType": "Prefix",
|
||||
"backend": {
|
||||
"service": {
|
||||
"name": app_name,
|
||||
"port": {"number": service_port}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
if tls_enabled:
|
||||
ingress["spec"]["tls"] = [{
|
||||
"hosts": [host],
|
||||
"secretName": f"{app_name}-tls"
|
||||
}]
|
||||
|
||||
yaml_content = yaml_lib.dump(ingress, default_flow_style=False, sort_keys=False)
|
||||
|
||||
# Save to file
|
||||
repo_path = "/app/repos/cluster-infrastructure"
|
||||
file_path = f"applications/{app_name}/ingress.yaml"
|
||||
full_path = os.path.join(repo_path, file_path)
|
||||
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
||||
|
||||
with open(full_path, "w") as f:
|
||||
f.write(yaml_content)
|
||||
|
||||
return f"✅ Created Ingress YAML:\n```yaml\n{yaml_content}\n```\n📁 Saved to: {file_path}"
|
||||
except Exception as e:
|
||||
return f"❌ Error creating ingress YAML: {str(e)}"
|
||||
|
||||
|
||||
@tool
|
||||
def yaml_apply_to_cluster(app_name: str, namespace: str = "default") -> str:
|
||||
"""
|
||||
Apply YAML files to Kubernetes cluster.
|
||||
Args:
|
||||
app_name: Application name
|
||||
namespace: Namespace (default: default)
|
||||
"""
|
||||
try:
|
||||
repo_path = "/app/repos/cluster-infrastructure"
|
||||
app_path = os.path.join(repo_path, f"applications/{app_name}")
|
||||
|
||||
if not os.path.exists(app_path):
|
||||
return f"❌ Application directory not found: {app_path}"
|
||||
|
||||
# Apply all YAML files in the directory
|
||||
results = []
|
||||
for yaml_file in os.listdir(app_path):
|
||||
if yaml_file.endswith(".yaml"):
|
||||
file_path = os.path.join(app_path, yaml_file)
|
||||
|
||||
# Read YAML file
|
||||
with open(file_path, "r") as f:
|
||||
yaml_content = f.read()
|
||||
|
||||
# Parse YAML to get resource info
|
||||
import yaml as yaml_lib
|
||||
resource = yaml_lib.safe_load(yaml_content)
|
||||
kind = resource.get("kind", "Unknown")
|
||||
name = resource.get("metadata", {}).get("name", "unknown")
|
||||
|
||||
# Apply using Python Kubernetes client
|
||||
try:
|
||||
if kind == "Deployment":
|
||||
k8s_apps_v1.create_namespaced_deployment(namespace=namespace, body=resource)
|
||||
elif kind == "Service":
|
||||
k8s_core_v1.create_namespaced_service(namespace=namespace, body=resource)
|
||||
elif kind == "Ingress":
|
||||
k8s_networking_v1.create_namespaced_ingress(namespace=namespace, body=resource)
|
||||
else:
|
||||
results.append(f"⚠️ {yaml_file}: Unsupported resource type {kind}")
|
||||
continue
|
||||
|
||||
results.append(f"✅ {yaml_file}: {kind}/{name} created")
|
||||
except ApiException as e:
|
||||
if e.status == 409:
|
||||
results.append(f"ℹ️ {yaml_file}: {kind}/{name} already exists")
|
||||
else:
|
||||
results.append(f"❌ {yaml_file}: {e.reason}")
|
||||
|
||||
return f"📦 Applied YAMLs for {app_name}:\n" + "\n".join(results)
|
||||
except Exception as e:
|
||||
return f"❌ Error applying YAMLs: {str(e)}"
|
||||
|
||||
|
||||
@tool
|
||||
def git_show_file_changes(repo_name: str = "cluster-infrastructure") -> str:
|
||||
"""
|
||||
Show Git file changes (diff) for UI display.
|
||||
Args:
|
||||
repo_name: Repository name (default: cluster-infrastructure)
|
||||
"""
|
||||
try:
|
||||
repo_path = f"/app/repos/{repo_name}"
|
||||
if not os.path.exists(repo_path):
|
||||
return f"❌ Repository not found: {repo_path}"
|
||||
|
||||
# Get git status
|
||||
status_result = subprocess.run(
|
||||
["git", "-C", repo_path, "status", "--short"],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
|
||||
# Get git diff
|
||||
diff_result = subprocess.run(
|
||||
["git", "-C", repo_path, "diff"],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
|
||||
# Get list of untracked files with their content
|
||||
untracked_files = []
|
||||
for line in status_result.stdout.split("\n"):
|
||||
if line.startswith("??"):
|
||||
file_path = line[3:].strip()
|
||||
full_path = os.path.join(repo_path, file_path)
|
||||
if os.path.isfile(full_path):
|
||||
with open(full_path, "r") as f:
|
||||
content = f.read()
|
||||
untracked_files.append({
|
||||
"path": file_path,
|
||||
"content": content
|
||||
})
|
||||
|
||||
output = "📝 **Git Changes**\n\n"
|
||||
output += f"**Status:**\n```\n{status_result.stdout}\n```\n\n"
|
||||
|
||||
if diff_result.stdout:
|
||||
output += f"**Modified Files (Diff):**\n```diff\n{diff_result.stdout}\n```\n\n"
|
||||
|
||||
if untracked_files:
|
||||
output += "**New Files:**\n\n"
|
||||
for file_info in untracked_files:
|
||||
output += f"📄 **{file_info['path']}**\n```yaml\n{file_info['content']}\n```\n\n"
|
||||
|
||||
return output
|
||||
except Exception as e:
|
||||
return f"❌ Error showing changes: {str(e)}"
|
||||
|
||||
|
||||
# MCP Tools Collection
|
||||
mcp_tools = [
|
||||
# Kubernetes
|
||||
@@ -598,6 +905,7 @@ mcp_tools = [
|
||||
# Git
|
||||
git_list_repos,
|
||||
git_recent_commits,
|
||||
git_show_file_changes,
|
||||
# Prometheus
|
||||
prometheus_query,
|
||||
prometheus_node_metrics,
|
||||
@@ -606,6 +914,22 @@ mcp_tools = [
|
||||
fs_list_directory,
|
||||
# Docker
|
||||
docker_list_images,
|
||||
# YAML Management
|
||||
yaml_create_deployment,
|
||||
yaml_create_service,
|
||||
yaml_create_ingress,
|
||||
yaml_apply_to_cluster,
|
||||
]
|
||||
|
||||
# YAML Manager specific tools (for Groq agents with write permissions)
|
||||
yaml_manager_tools = [
|
||||
yaml_create_deployment,
|
||||
yaml_create_service,
|
||||
yaml_create_ingress,
|
||||
yaml_apply_to_cluster,
|
||||
git_show_file_changes,
|
||||
git_create_file,
|
||||
git_push,
|
||||
]
|
||||
|
||||
|
||||
@@ -628,6 +952,7 @@ ORCHESTRATOR_PROMPT = """당신은 MAS의 총괄 조율자이자 DevOps 전문
|
||||
1. backend_developer: FastAPI, Node.js 백엔드 개발
|
||||
2. frontend_developer: Next.js, React 프론트엔드 개발
|
||||
3. sre_specialist: 모니터링, 성능 최적화, 보안
|
||||
4. yaml_manager: Kubernetes YAML 파일 생성 및 관리, Git 배포
|
||||
|
||||
**사용 가능한 도구(Tools)**:
|
||||
당신은 실제 서버 상태에 접근할 수 있는 다양한 도구를 사용할 수 있습니다:
|
||||
@@ -737,6 +1062,43 @@ SRE_PROMPT = """당신은 SRE(Site Reliability Engineer) 전문가입니다.
|
||||
"""
|
||||
|
||||
|
||||
# ===== 5. Groq #4 - YAML Manager =====
|
||||
groq_yaml_manager = ChatOpenAI(
|
||||
model=os.getenv("GROQ_YAML_MODEL", "llama-3.3-70b-specdec"),
|
||||
base_url=GROQ_API_BASE,
|
||||
api_key=GROQ_API_KEY,
|
||||
temperature=0.3,
|
||||
)
|
||||
|
||||
YAML_MANAGER_PROMPT = """당신은 Kubernetes YAML 파일 관리 전문가입니다.
|
||||
|
||||
**역할**:
|
||||
- Kubernetes 리소스 YAML 파일 생성 (Deployment, Service, Ingress, ConfigMap, Secret 등)
|
||||
- 애플리케이션별 폴더 구조로 YAML 정리
|
||||
- Git 저장소에 YAML 파일 커밋 및 푸시
|
||||
- Kustomize 오버레이 구조 생성 (base, dev, prod)
|
||||
- ArgoCD Application 매니페스트 생성
|
||||
|
||||
**사용 가능한 도구**:
|
||||
- yaml_create_deployment: Deployment YAML 생성
|
||||
- yaml_create_service: Service YAML 생성
|
||||
- yaml_create_ingress: Ingress YAML 생성
|
||||
- yaml_create_kustomization: Kustomization 파일 생성
|
||||
- yaml_apply_to_cluster: YAML을 클러스터에 적용
|
||||
- git_commit_yaml: YAML 파일들을 Git에 커밋
|
||||
|
||||
**작업 흐름**:
|
||||
1. 사용자 요구사항 분석
|
||||
2. 필요한 Kubernetes 리소스 결정
|
||||
3. 적절한 폴더 구조 생성 (예: deploy/k8s/<app-name>/)
|
||||
4. YAML 파일 생성 및 검증
|
||||
5. Git 저장소에 커밋 및 푸시
|
||||
6. ArgoCD에서 자동 배포되도록 설정
|
||||
|
||||
요청된 YAML 관리 작업을 수행하세요.
|
||||
"""
|
||||
|
||||
|
||||
def orchestrator_node(state: AgentState) -> AgentState:
|
||||
"""Claude Code - 작업 분석 및 할당 (도구 사용 가능)"""
|
||||
messages = state["messages"]
|
||||
@@ -780,7 +1142,9 @@ def orchestrator_node(state: AgentState) -> AgentState:
|
||||
|
||||
# 작업 타입 결정
|
||||
content_lower = content.lower()
|
||||
if "backend" in content_lower or "api" in content_lower or "fastapi" in content_lower:
|
||||
if "yaml" in content_lower or "deployment" in content_lower or "kubernetes" in content_lower or "k8s" in content_lower or "manifests" in content_lower:
|
||||
next_agent = "yaml_manager"
|
||||
elif "backend" in content_lower or "api" in content_lower or "fastapi" in content_lower:
|
||||
next_agent = "backend_developer"
|
||||
elif "frontend" in content_lower or "ui" in content_lower or "react" in content_lower:
|
||||
next_agent = "frontend_developer"
|
||||
@@ -852,7 +1216,58 @@ def sre_node(state: AgentState) -> AgentState:
|
||||
return state
|
||||
|
||||
|
||||
def router(state: AgentState) -> Literal["backend_developer", "frontend_developer", "sre_specialist", "end"]:
|
||||
def yaml_manager_node(state: AgentState) -> AgentState:
|
||||
"""Groq #4 - YAML Manager"""
|
||||
messages = state["messages"]
|
||||
|
||||
# Bind YAML manager tools to this agent
|
||||
yaml_manager = groq_yaml_manager.bind_tools(yaml_manager_tools)
|
||||
|
||||
response = yaml_manager.invoke([
|
||||
SystemMessage(content=YAML_MANAGER_PROMPT),
|
||||
HumanMessage(content=messages[-1]["content"])
|
||||
])
|
||||
|
||||
# Handle tool calls if any
|
||||
tool_outputs = []
|
||||
if hasattr(response, 'tool_calls') and response.tool_calls:
|
||||
for tool_call in response.tool_calls:
|
||||
tool_name = tool_call['name']
|
||||
tool_args = tool_call.get('args', {})
|
||||
|
||||
# Execute tool
|
||||
try:
|
||||
tool_func = next(t for t in yaml_manager_tools if t.name == tool_name)
|
||||
tool_result = tool_func.invoke(tool_args)
|
||||
tool_outputs.append(f"\n🔧 **{tool_name}**: {tool_result}")
|
||||
except Exception as e:
|
||||
tool_outputs.append(f"\n❌ **{tool_name}** failed: {str(e)}")
|
||||
|
||||
# Call agent again with tool results
|
||||
if tool_outputs:
|
||||
tool_context = "\n".join(tool_outputs)
|
||||
response = yaml_manager.invoke([
|
||||
SystemMessage(content=YAML_MANAGER_PROMPT),
|
||||
HumanMessage(content=messages[-1]["content"]),
|
||||
HumanMessage(content=f"도구 실행 결과:\n{tool_context}")
|
||||
])
|
||||
|
||||
content = response.content if isinstance(response.content, str) else str(response.content)
|
||||
|
||||
# Add tool outputs to content
|
||||
if tool_outputs:
|
||||
content = "\n".join(tool_outputs) + "\n\n" + content
|
||||
|
||||
state["messages"].append({
|
||||
"role": "yaml_manager",
|
||||
"content": content
|
||||
})
|
||||
state["current_agent"] = "orchestrator"
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def router(state: AgentState) -> Literal["backend_developer", "frontend_developer", "sre_specialist", "yaml_manager", "end"]:
|
||||
"""다음 에이전트 라우팅"""
|
||||
current = state.get("current_agent", "orchestrator")
|
||||
|
||||
@@ -862,6 +1277,8 @@ def router(state: AgentState) -> Literal["backend_developer", "frontend_develope
|
||||
return "frontend_developer"
|
||||
elif current == "sre_specialist":
|
||||
return "sre_specialist"
|
||||
elif current == "yaml_manager":
|
||||
return "yaml_manager"
|
||||
else:
|
||||
return "end"
|
||||
|
||||
@@ -876,6 +1293,7 @@ def create_mas_graph():
|
||||
workflow.add_node("backend_developer", backend_node)
|
||||
workflow.add_node("frontend_developer", frontend_node)
|
||||
workflow.add_node("sre_specialist", sre_node)
|
||||
workflow.add_node("yaml_manager", yaml_manager_node)
|
||||
|
||||
# 엣지 정의
|
||||
workflow.set_entry_point("orchestrator")
|
||||
@@ -886,6 +1304,7 @@ def create_mas_graph():
|
||||
"backend_developer": "backend_developer",
|
||||
"frontend_developer": "frontend_developer",
|
||||
"sre_specialist": "sre_specialist",
|
||||
"yaml_manager": "yaml_manager",
|
||||
"end": END
|
||||
}
|
||||
)
|
||||
@@ -894,6 +1313,7 @@ def create_mas_graph():
|
||||
workflow.add_edge("backend_developer", "orchestrator")
|
||||
workflow.add_edge("frontend_developer", "orchestrator")
|
||||
workflow.add_edge("sre_specialist", "orchestrator")
|
||||
workflow.add_edge("yaml_manager", "orchestrator")
|
||||
|
||||
return workflow.compile()
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ async def start():
|
||||
content="🤖 **Multi-Agent System**에 오신 것을 환영합니다!\n\n"
|
||||
"저는 다음 전문가 팀과 함께 작업합니다:\n\n"
|
||||
"- 🎼 **Claude Code**: 총괄 조율자 & DevOps 전문가\n"
|
||||
"- ⚙️ **Qwen Backend**: 백엔드 개발자\n"
|
||||
"- 🎨 **Qwen Frontend**: 프론트엔드 개발자\n"
|
||||
"- 📊 **Qwen SRE**: 모니터링 & 성능 전문가\n\n"
|
||||
"- ⚙️ **Groq Backend**: 백엔드 개발자\n"
|
||||
"- 🎨 **Groq Frontend**: 프론트엔드 개발자\n"
|
||||
"- 📊 **Groq SRE**: 모니터링 & 성능 전문가\n"
|
||||
"- 📝 **Groq YAML Manager**: Kubernetes YAML 파일 관리\n\n"
|
||||
"무엇을 도와드릴까요?"
|
||||
).send()
|
||||
|
||||
@@ -52,7 +53,8 @@ async def main(message: cl.Message):
|
||||
"orchestrator": "🎼",
|
||||
"backend_developer": "⚙️",
|
||||
"frontend_developer": "🎨",
|
||||
"sre_specialist": "📊"
|
||||
"sre_specialist": "📊",
|
||||
"yaml_manager": "📝"
|
||||
}
|
||||
|
||||
icon = agent_icons.get(agent_name, "🤖")
|
||||
@@ -77,9 +79,10 @@ def rename(orig_author: str):
|
||||
"""에이전트 이름 매핑"""
|
||||
rename_dict = {
|
||||
"orchestrator": "Claude Code (Orchestrator)",
|
||||
"backend_developer": "Qwen Backend Dev",
|
||||
"frontend_developer": "Qwen Frontend Dev",
|
||||
"sre_specialist": "Qwen SRE"
|
||||
"backend_developer": "Groq Backend Dev",
|
||||
"frontend_developer": "Groq Frontend Dev",
|
||||
"sre_specialist": "Groq SRE",
|
||||
"yaml_manager": "Groq YAML Manager"
|
||||
}
|
||||
return rename_dict.get(orig_author, orig_author)
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ kubernetes==31.0.0
|
||||
psycopg2-binary==2.9.10
|
||||
GitPython==3.1.43
|
||||
|
||||
# YAML
|
||||
PyYAML==6.0.2
|
||||
|
||||
# Utilities
|
||||
python-dotenv==1.0.1
|
||||
redis==5.2.1
|
||||
|
||||
Reference in New Issue
Block a user