k8s-multi-agent / app.py
roanbrasil's picture
Upload app.py with huggingface_hub
d90e920 verified
Raw
History Blame
9.3 kB
"""
Gradio Space: K8s Multi-Agent Debate Demo
HuggingFace Space: roanbrasil/k8s-multi-agent
"""
import json
import gradio as gr
HARD8_RESULTS = [
{"resource": "Deployment", "rounds": 4, "time": 9.05,
"manifest": """apiVersion: apps/v1
kind: Deployment
metadata:
name: java-app
spec:
replicas: 2
selector:
matchLabels:
app: java-app
template:
metadata:
labels:
app: java-app
spec:
containers:
- name: java-app
image: openjdk:17
resources:
limits:
memory: "512Mi\""""},
{"resource": "HorizontalPodAutoscaler", "rounds": 4, "time": 10.27,
"manifest": """apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50"""},
{"resource": "StatefulSet", "rounds": 4, "time": 7.63,
"manifest": """apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kafka
spec:
serviceName: kafka
replicas: 3
selector:
matchLabels:
app: kafka
template:
metadata:
labels:
app: kafka
spec:
containers:
- name: kafka
image: confluentinc/cp-kafka:7.4.0
volumeMounts:
- name: kafka-data
mountPath: /var/lib/kafka/data
volumeClaimTemplates:
- metadata:
name: kafka-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi"""},
{"resource": "Ingress (TLS)", "rounds": 4, "time": 8.33,
"manifest": """apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: secure-ingress
spec:
tls:
- hosts:
- secure.example.com
secretName: tls-secret
rules:
- host: secure.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80"""},
{"resource": "PersistentVolumeClaim", "rounds": 4, "time": 6.81,
"manifest": """apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: database
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi"""},
{"resource": "CronJob", "rounds": 4, "time": 9.26,
"manifest": """apiVersion: batch/v1
kind: CronJob
metadata:
name: db-backup
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: postgres:15
command: ["/bin/sh", "-c", "pg_dump $DATABASE_URL > /backup/dump.sql"]
restartPolicy: OnFailure"""},
{"resource": "NetworkPolicy", "rounds": 4, "time": 8.29,
"manifest": """apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-netpol
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 80"""},
{"resource": "ClusterRole", "rounds": 4, "time": 8.67,
"manifest": """apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]"""},
]
SINGLE_MODEL_RESULTS = {
"Baseline GPT (46M)": {"yaml": 30.0, "k8s": 36.7, "sem": 96.9, "lat": 0.35},
"AttnRes GPT (48M)": {"yaml": 26.7, "k8s": 36.7, "sem": 97.8, "lat": 0.75},
"Qwen2.5-Coder (7B)": {"yaml": 40.0, "k8s": 33.3, "sem": 98.1, "lat": 1.27},
"DeepSeek-Coder (6.7B)":{"yaml": 16.7, "k8s": 33.3, "sem": 95.0, "lat": 1.61},
}
ARCHITECTURE_MD = """
## System Architecture
```
Problem β†’ BM25 RAG (4,794 K8s docs)
↓
Agent 1 (AttnRes GPT 48M)
Fast domain specialist
↓ draft
kubeconform --strict
↓ error report
Agent 2 (Qwen2.5-Coder 7B)
Reasoning critic
↓ critique + instruction
Agent 1 retries (max 3 rounds)
↓ if not solved
Agent 2 generates directly (fallback)
```
**Key design choices:**
- **Asymmetric roles**: small model drafts fast, large model reasons deeply
- **External validator**: kubeconform provides ground-truth schema signal (no hallucinated validation)
- **BM25 RAG**: top-3 K8s-specific documents grounded to each problem
- **Max 3 rounds**: bounded latency (~30s worst case)
"""
def show_hard8_result(resource_name):
for r in HARD8_RESULTS:
if r["resource"] == resource_name:
summary = f"**Resource:** {r['resource']} \n"
summary += f"**Debate rounds:** {r['rounds']} \n"
summary += f"**Total time:** {r['time']:.2f}s \n"
summary += f"**Solved by:** Agent 2 (Qwen2.5-Coder fallback) \n"
summary += f"**Single-model K8s%:** 0% (all 4 models failed)\n"
return summary, r["manifest"]
return "Not found", ""
def show_benchmark_table():
rows = []
for model, m in SINGLE_MODEL_RESULTS.items():
rows.append([model, f"{m['yaml']:.1f}%", f"{m['k8s']:.1f}%",
f"{m['sem']:.1f}%", f"{m['lat']:.2f}s"])
return rows
with gr.Blocks(title="K8s Multi-Agent Debate Demo", theme=gr.themes.Soft()) as demo:
gr.Markdown("# K8s Multi-Agent Debate (MDA) System")
gr.Markdown(
"Combines **AttnRes GPT (48M)** + **Qwen2.5-Coder-7B** + **BM25 RAG** + **kubeconform** "
"to generate valid Kubernetes manifests. Achieves **100% schema compliance** on the "
"Hard-8 subset where all single models fail.\n\n"
"πŸ“„ [Paper](https://github.com/roanbrasil/llm-pocs) | "
"πŸ€— [Model](https://huggingface.co/roanbrasil/attnres-devops-gpt) | "
"πŸ“Š [K8sBench](https://huggingface.co/datasets/roanbrasil/k8sbench)"
)
with gr.Tabs():
with gr.Tab("Hard-8 Results"):
gr.Markdown("### Hard-8 Subset: Resources all single models fail (0% K8s%)")
gr.Markdown("The MDA system solves all 8 via the Agent 2 fallback after 3 debate rounds.")
resource_dd = gr.Dropdown(
choices=[r["resource"] for r in HARD8_RESULTS],
value="HorizontalPodAutoscaler",
label="Select K8s resource"
)
result_info = gr.Markdown()
manifest_out = gr.Code(language="yaml", label="Final valid manifest (kubeconform βœ“)")
resource_dd.change(show_hard8_result,
inputs=resource_dd,
outputs=[result_info, manifest_out])
demo.load(lambda: show_hard8_result("HorizontalPodAutoscaler"),
outputs=[result_info, manifest_out])
with gr.Tab("K8sBench Leaderboard"):
gr.Markdown("### K8sBench: 30-prompt evaluation across 4 models")
leaderboard = gr.Dataframe(
headers=["Model", "YAML%", "K8s%", "Sem%", "Latency"],
value=show_benchmark_table(),
label="K8sBench Results",
interactive=False
)
gr.Markdown(
"**K8s%** = kubeconform --strict schema compliance \n"
"Domain-specific 48M models **match or exceed** 7B generalists on schema compliance "
"while being **3–4Γ— faster**."
)
with gr.Tab("Architecture"):
gr.Markdown(ARCHITECTURE_MD)
gr.Markdown("""
### Agent Roles
| Agent | Model | Role |
|-------|-------|------|
| **Agent 1** | AttnRes GPT (48M, local GPU) | Fast domain specialist β€” generates initial draft |
| **Agent 2** | Qwen2.5-Coder-7B (Ollama) | Reasoning critic β€” diagnoses kubeconform errors, instructs Agent 1, generates final manifest on fallback |
| **Validator** | kubeconform --strict | External schema arbitrator β€” provides ground-truth correctness signal |
| **RAG** | BM25Okapi over 4,794 K8s docs | Retrieves top-3 relevant examples per problem |
""")
with gr.Tab("About"):
gr.Markdown("""
## About
This demo presents results from:
> Brasil, R. (2025). *Can Small Domain-Specific LLMs Compete with General 7B Models on Kubernetes Configuration Generation?*
### Key Findings
1. A **48M domain-specific model** matches **7B generalists** on Kubernetes schema compliance (36.7% vs 33.3%) while being **3.4Γ— faster**
2. **All single models fail** on HPA, StatefulSet, Ingress, PVC (cross-field constraint resources)
3. The **MDA system achieves 100%** on the Hard-8 subset via asymmetric debate with external validation
4. **AttnRes** architectural improvement: βˆ’2.1% perplexity, βˆ’44% convergence steps
### Resources
- πŸ”— Code: https://github.com/roanbrasil/llm-pocs
- πŸ€— Model: https://huggingface.co/roanbrasil/attnres-devops-gpt
- πŸ“Š Training corpus: https://huggingface.co/datasets/roanbrasil/devops-gitops-corpus
- πŸ“Š K8sBench: https://huggingface.co/datasets/roanbrasil/k8sbench
- πŸ“Š RAG corpus: https://huggingface.co/datasets/roanbrasil/k8s-rag-corpus
""")
demo.launch()