Files
agent_management/.omc/plans/code_ai_agent_cicd.md

624 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# code_ai_agent CI/CD 工作流方案设计
**计划文件:** `.omc/plans/code_ai_agent_cicd.md`
**创建日期:** 2026-03-27
**状态:** 待用户确认
---
## 1. 方案概述
将 `code_ai_agent` 从单纯的代码生成服务升级为具备完整 DevOps 工作流能力的「代码员工 Agent」。新增 Git 操作、SSH 远程执行、K8s 部署触发能力,全部通过 HTTP API 暴露。
### 完整工作流
```
外部调用方 (agent-manager / 人工)
│
▼
code_ai_agent Pod
┌──────────────────────────────────────────┐
│ api_server.py (HTTP 路由层) │
│ ┌──────────┬──────────┬──────────────┐ │
│ │ /git/* │ /ssh/* │ /deploy/k8s │ │
│ └────┬─────┴────┬─────┴──────┬───────┘ │
│ │ │ │ │
│ src/server/tools/ (工具实现层) │
│ ┌────▼─────┐ ┌──▼──────┐ ┌──▼────────┐ │
│ │git_tools │ │ssh_tools│ │deploy_tools│ │
│ └────┬─────┘ └──┬──────┘ └──┬────────┘ │
│ │ │ │ │
│ /workspace/{task_id}/ (隔离工作空间) │
└───┬───┴──────────┴────────────┴───────────┘
│
├─► Gitee (http://gitee.ath.cx:3000)
├─► Azure VM (SSH 22)
└─► K8s API Server
```
### 典型工作流序列
```
1. POST /api/v1/git/clone → 克隆仓库到 /workspace/{task_id}
2. POST /api/v1/git/branch → 创建 feature/xxx 分支
3. POST /api/v1/code/generate → 使用现有能力生成/修改代码
4. POST /api/v1/git/status → 确认变更
5. POST /api/v1/git/commit-push → 提交并推送
6. POST /api/v1/ssh/exec → SSH 到 Azure VM 执行测试
7. POST /api/v1/deploy/k8s → 测试通过后触发 K8s 部署
```
---
## 2. 新增 API 端点设计(api_server.py)
### 2.1 Git 操作端点
#### `POST /api/v1/git/clone`
```json
// 请求
{
"repo_url": "http://gitee.ath.cx:3000/zhanggangyong/agent_management.git",
"task_id": "task-20260327-001",
"branch": "main",
"depth": 1
}
// 响应
{
"success": true,
"task_id": "task-20260327-001",
"workspace": "/workspace/task-20260327-001",
"branch": "main",
"commit": "abc1234"
}
```
#### `POST /api/v1/git/branch`
```json
// 请求
{
"task_id": "task-20260327-001",
"branch_name": "feature/auto-fix-bug-123",
"from_branch": "main"
}
// 响应
{ "success": true, "branch": "feature/auto-fix-bug-123", "base_commit": "abc1234" }
```
#### `POST /api/v1/git/status`
```json
// 请求
{ "task_id": "task-20260327-001" }
// 响应
{
"success": true,
"branch": "feature/auto-fix-bug-123",
"staged": ["src/main.py"],
"unstaged": ["README.md"],
"untracked": ["new_file.py"],
"raw_output": "M src/main.py\n?? new_file.py"
}
```
#### `POST /api/v1/git/commit-push`
```json
// 请求
{
"task_id": "task-20260327-001",
"message": "fix: resolve null pointer in agent executor",
"files": ["src/agent.py"],
"push": true
}
// 响应
{ "success": true, "commit": "def5678", "pushed": true, "branch": "feature/auto-fix-bug-123" }
```
#### `POST /api/v1/git/diff`
```json
// 请求
{ "task_id": "task-20260327-001", "staged": false }
// 响应
{ "success": true, "diff": "--- a/src/main.py\n+++ b/src/main.py\n..." }
```
### 2.2 SSH 操作端点
#### `POST /api/v1/ssh/exec`
```json
// 请求
{
"host": "<azure-vm-ip>",
"user": "azureuser",
"command": "cd /app && pytest tests/ -v --tb=short",
"timeout": 300,
"task_id": "task-20260327-001"
}
// 响应
{
"success": true,
"exit_code": 0,
"stdout": "collected 42 items ... 42 passed",
"stderr": "",
"duration_seconds": 45.2
}
```
**说明:** `host` 若不传,从环境变量 `SSH_TEST_HOST` 读取;`user` 从 `SSH_USER` 读取,默认 `azureuser`。
### 2.3 部署端点
#### `POST /api/v1/deploy/k8s`
```json
// 请求
{
"namespace": "agent-manager",
"deployment": "agent-manager",
"image": "agnettaiji.azurecr.io/ai-agents/agent-manager:v1.2.3",
"strategy": "set-image",
"wait": true,
"timeout": 300
}
// strategy: "rollout-restart" | "set-image"
// 响应
{ "success": true, "deployment": "agent-manager", "status": "rolled out", "duration_seconds": 62 }
```
---
## 3. 新增工具函数设计(mcp_server.py + tools/ 模块)
### 3.1 文件结构变化
```
agent_templates/agents/code_ai_agent/
├── Dockerfile # 修改:增加 git/ssh/kubectl
├── requirements.txt # 修改:增加 paramiko, gitpython
├── src/server/
│ ├── api_server.py # 修改:新增 /git /ssh /deploy 路由
│ ├── mcp_server.py # 修改:新增工具注册
│ ├── mcp_http_server.py # 不变
│ └── tools/ # 新增目录
│ ├── __init__.py
│ ├── git_tools.py # Git 操作实现
│ ├── ssh_tools.py # SSH 操作实现
│ ├── deploy_tools.py # K8s 部署实现
│ └── workspace.py # 工作空间管理
└── k8s/ # 新增:agent 专属 K8s 配置
├── code-ai-agent-deployment.yaml
└── code-ai-agent-secret.yaml
```
### 3.2 git_tools.py 核心接口
```python
class GitTools:
def __init__(self):
self.workspace_root = "/workspace"
self._gitee_user = os.getenv("GITEE_USERNAME")
self._gitee_token = os.getenv("GITEE_TOKEN")
def clone(self, repo_url, task_id, branch="main", depth=1) -> dict
def create_branch(self, task_id, branch_name, from_branch=None) -> dict
def get_status(self, task_id) -> dict
def stage_files(self, task_id, files=None) -> dict # None = git add -A
def commit(self, task_id, message) -> dict
def push(self, task_id, branch=None) -> dict
def get_diff(self, task_id, staged=False) -> dict
def cleanup(self, task_id) -> dict # 删除工作空间
def _inject_credentials(self, repo_url) -> str:
# http://user:token@gitee.ath.cx:3000/...
parsed = urlparse(repo_url)
return parsed._replace(
netloc=f"{self._gitee_user}:{self._gitee_token}@{parsed.hostname}:{parsed.port}"
).geturl()
def _run(self, cmd, cwd) -> tuple[int, str, str]
# subprocess.run,捕获 stdout/stderr,设置超时
```
### 3.3 ssh_tools.py 核心接口
```python
class SSHTools:
def __init__(self):
self._key_path = "/root/.ssh/id_rsa" # 从 Secret 挂载
self._default_host = os.getenv("SSH_TEST_HOST")
self._default_user = os.getenv("SSH_USER", "azureuser")
def exec(self, command, host=None, user=None, timeout=120, task_id=None) -> dict:
# 使用 paramiko 连接,执行命令,返回 stdout/stderr/exit_code
# 每次调用建立新连接,操作完毕后关闭
def _get_client(self, host, user) -> paramiko.SSHClient
```
### 3.4 deploy_tools.py 核心接口
```python
class DeployTools:
def __init__(self):
# 优先使用挂载的 kubeconfig,其次 in-cluster config
self._kubeconfig = "/root/.kube/config"
def rollout_restart(self, namespace, deployment, wait=True, timeout=300) -> dict
def set_image(self, namespace, deployment, image, wait=True, timeout=300) -> dict
def get_status(self, namespace, deployment) -> dict
def _run_kubectl(self, args) -> tuple[int, str, str]
```
### 3.5 workspace.py — 工作空间管理
```python
class WorkspaceManager:
ROOT = "/workspace"
@staticmethod
def get_path(task_id: str) -> str:
# 返回 /workspace/{task_id}
# task_id 只允许 [a-zA-Z0-9_-],防止路径注入
@staticmethod
def create(task_id: str) -> str
@staticmethod
def cleanup(task_id: str) -> None
@staticmethod
def list_tasks() -> list[str]
@staticmethod
def disk_usage() -> dict # 返回各 task_id 占用磁盘大小
```
---
## 4. 安全设计
### 4.1 SSH 私钥注入
**方案:K8s Secret → Volume Mount(只读)**
```yaml
# 新建 Secret(在 code-ai-agent 命名空间下)
apiVersion: v1
kind: Secret
metadata:
name: code-ai-agent-ssh-secret
namespace: agent-manager
type: Opaque
data:
id_rsa: <base64-encoded-private-key>
id_rsa.pub: <base64-encoded-public-key>
known_hosts: <base64-encoded-known_hosts> # 预置 Azure VM
```
```yaml
# Deployment volumeMounts
volumeMounts:
- name: ssh-secret
mountPath: /root/.ssh
readOnly: true
volumes:
- name: ssh-secret
secret:
secretName: code-ai-agent-ssh-secret
defaultMode: 0400 # 私钥必须 0400,否则 SSH 拒绝
```
初始化:容器 entrypoint 或 initContainer 执行 `chmod 700 /root/.ssh && chmod 600 /root/.ssh/id_rsa`。
### 4.2 Git 凭证安全传递
| 方案 | 说明 | 推荐度 |
|------|------|--------|
| Token 嵌入 URL | `http://user:token@host/repo` 内存拼接,不落盘 | P0 首选 |
| git credential store | 写入 `~/.git-credentials` 文件权限 600 | 备选 |
| SSH key for git | gitee 配置 deploy key,统一 SSH | P2 升级 |
实现要点:`_inject_credentials()` 在内存拼接带 token 的 URL;clone 完成后用 `git remote set-url origin <无密码URL>` 替换;日志中对 URL 做 token 脱敏。
### 4.3 权限隔离
- code_ai_agent 使用独立 ServiceAccount `code-ai-agent`
- RBAC 只授予 `agent-manager` 命名空间下 Deployment 的 `get/patch/update`
- SSH 连接只允许白名单 host(`SSH_ALLOWED_HOSTS` 环境变量,ssh_tools.py 校验)
- `/workspace` 挂载独立 emptyDir,不与其他 agent 共享
- API 通过现有 `X-API-Key` header 鉴权
---
## 5. 工作空间设计
### 5.1 目录结构
```
/workspace/
├── task-20260327-001/
│ ├── agent_management/ # 克隆的仓库
│ └── .meta.json # 任务元数据(时间、branch、状态)
├── task-20260327-002/
│ └── agent_management/
└── .workspace_index.json
```
### 5.2 并发隔离策略
- `task_id` 由调用方传入或服务端 `uuid4()` 自动生成
- 每个 task_id 对应独立目录,无共享文件
- 任务完成后调用清理接口或设置 TTL 自动清理
- 磁盘告警:workspace 总占用超过 10GB 返回 503
- `task_id` 只允许 `[a-zA-Z0-9_-]`,防止路径穿越注入
### 5.3 新增管理端点
```
GET /api/v1/workspace/list → 列出所有 task_id 和磁盘占用
DELETE /api/v1/workspace/{task_id} → 清理指定工作空间
```
---
## 6. Dockerfile 修改
**当前状态:** 只安装 `gcc`,无 git/ssh/kubectl。
**修改后关键变更:**
```dockerfile
FROM python:3.12-slim
WORKDIR /app
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
# 新增:git + openssh-client + curl(kubectl 安装需要)
RUN apt-get update && apt-get install -y \
gcc git openssh-client curl ca-certificates gnupg \
&& rm -rf /var/lib/apt/lists/*
# 新增:安装 kubectl
RUN curl -LO "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" \
&& chmod +x kubectl && mv kubectl /usr/local/bin/
# 新增:paramiko(SSH)、gitpython(可选,subprocess git 为主)
RUN pip install --no-cache-dir -r requirements.txt requests paramiko gitpython
# 新增:工作空间目录(PVC 挂载时会覆盖)
RUN mkdir -p /workspace /tmp/projects
EXPOSE 8000 8001
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
CMD ["python", "run_api_server.py"]
```
**镜像大小预估影响:** git + openssh ≈ +30MB,kubectl ≈ +50MB,paramiko ≈ +5MB。总增量约 85MB,可接受。
---
## 7. K8s 部署配置修改
### 7.1 新增文件:code-ai-agent-deployment.yaml
code_ai_agent 需要独立 Deployment(与 agent-manager 主服务分离),关键新增配置段:
```yaml
spec:
template:
spec:
serviceAccountName: code-ai-agent
containers:
- name: code-ai-agent
env:
- name: GITEE_USERNAME
valueFrom:
secretKeyRef:
name: agent-manager-secret
key: GITEE_USERNAME
- name: GITEE_TOKEN
valueFrom:
secretKeyRef:
name: agent-manager-secret
key: GITEE_TOKEN
- name: SSH_TEST_HOST
valueFrom:
secretKeyRef:
name: code-ai-agent-ssh-secret
key: SSH_TEST_HOST
- name: SSH_USER
value: "azureuser"
volumeMounts:
- name: ssh-secret
mountPath: /root/.ssh
readOnly: true
- name: kubeconfig
mountPath: /root/.kube
readOnly: true
- name: workspace
mountPath: /workspace
resources:
requests:
memory: "512Mi"
cpu: "300m"
limits:
memory: "1Gi"
cpu: "1000m"
volumes:
- name: ssh-secret
secret:
secretName: code-ai-agent-ssh-secret
defaultMode: 0400
- name: kubeconfig
secret:
secretName: kubeconfig-secret
optional: true
- name: workspace
emptyDir:
sizeLimit: 20Gi
```
### 7.2 agent-manager-secret 新增 key
在现有 `k8s/agent-manager-secret.yaml` 补充:
```yaml
GITEE_USERNAME: "zhanggangyong"
```
### 7.3 新建 code-ai-agent-ssh-secret.yaml
```yaml
apiVersion: v1
kind: Secret
metadata:
name: code-ai-agent-ssh-secret
namespace: agent-manager
type: Opaque
data:
id_rsa: <base64-encoded-private-key>
known_hosts: <base64-encoded-known_hosts>
SSH_TEST_HOST: <base64-encoded-azure-vm-ip>
```
### 7.4 RBAC 新增 Role + RoleBinding
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: code-ai-agent-role
namespace: agent-manager
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "patch", "update"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
```
---
## 8. 实现优先级
### P0 — 核心能力(第一阶段,必须先完成)
| 编号 | 内容 | 验收标准 |
|------|------|----------|
| P0-1 | Dockerfile 安装 git + openssh-client + kubectl | `docker run ... git --version` 输出正常 |
| P0-2 | workspace.py 工作空间管理 | 单元测试覆盖路径注入防护(task_id 含 `../` 时拒绝)|
| P0-3 | git_tools.py:clone + branch + status + commit + push | 成功 clone gitee 仓库,创建分支并推送 |
| P0-4 | ssh_tools.py:exec | SSH 到 Azure VM 执行 `echo ok`,返回 exit_code=0 |
| P0-5 | api_server.py 新增 /git/* 和 /ssh/exec 路由 | HTTP 调用返回正确 JSON,异常时返回 4xx/5xx |
| P0-6 | SSH Secret + Volume Mount K8s 配置 | Pod 启动后 `/root/.ssh/id_rsa` 权限为 0400 |
### P1 — 完整工作流(第二阶段)
| 编号 | 内容 | 验收标准 |
|------|------|----------|
| P1-1 | deploy_tools.py:rollout-restart + set-image | 成功触发 K8s 滚动更新,等待就绪返回 |
| P1-2 | api_server.py 新增 /deploy/k8s 路由 | 调用后 deployment 完成更新,status 字段正确 |
| P1-3 | RBAC:code-ai-agent ServiceAccount + Role | `kubectl auth can-i patch deployment` 返回 yes |
| P1-4 | git diff 接口 | 返回正确 unified diff 格式 |
| P1-5 | workspace list/cleanup 管理端点 | GET /workspace/list 返回含磁盘占用的列表 |
| P1-6 | mcp_server.py 注册新工具 | MCP 工具列表中出现 git_clone、ssh_exec、k8s_deploy |
### P2 — 增强与优化(第三阶段)
| 编号 | 内容 | 说明 |
|------|------|------|
| P2-1 | 替换 HTTP token 为 SSH key 方式访问 git | 更安全,需 gitee 配置 deploy key |
| P2-2 | workspace 磁盘告警 + TTL 自动清理 | 防止 emptyDir 耗尽,定时任务每小时扫描 |
| P2-3 | SSH 连接池(paramiko Transport 复用) | 减少高频调用连接建立开销 |
| P2-4 | /api/v1/pipeline/run 编排端点 | 单次调用完成 clone→修改→测试→部署全流程 |
| P2-5 | 操作审计日志(structured log) | 所有 git/ssh/deploy 操作可追溯,含 task_id |
---
## 9. 潜在风险与注意事项
### 风险 1:Git Token 泄露
- **场景:** token 嵌入 URL 后被 `git remote -v`、进程环境变量或日志打印
- **缓解:** clone 后立即 `git remote set-url origin <无密码URL>`;日志中 URL 做正则脱敏;不将 token 写入任何文件
### 风险 2:workspace 磁盘耗尽
- **场景:** 大量任务未清理,emptyDir 超限导致 Pod 被驱逐
- **缓解:** emptyDir 设 `sizeLimit: 20Gi`;API 层磁盘检查(超 10GB 返回 503);P2 阶段加 TTL 自动清理
### 风险 3:SSH 私钥被容器内进程读取
- **场景:** 容器内其他进程或代码执行漏洞读取 `/root/.ssh/id_rsa`
- **缓解:** Volume `defaultMode: 0400`;容器以非 root 用户运行(P2 阶段);考虑使用 Vault Agent Injector 替代 Secret Volume
### 风险 4:K8s 部署权限过宽
- **场景:** code_ai_agent 被攻击后可滥用 kubectl 权限影响其他服务
- **缓解:** RBAC 严格限制到 `agent-manager` 命名空间,只允许 get/patch/update Deployment;禁止 delete、exec、secret 等危险操作
### 风险 5:并发 git 操作冲突
- **场景:** 两个任务使用相同 task_id 或同一仓库并发操作
- **缓解:** task_id 全局唯一(UUID);每个 task_id 独立目录;api_server.py 对同一 task_id 的写操作加文件锁
### 风险 6:Azure VM SSH 连接超时或不可达
- **场景:** 网络抖动或 VM 重启导致 SSH 命令挂起
- **缓解:** paramiko 设置 `banner_timeout`、`auth_timeout`、`timeout`;所有 ssh.exec 调用强制设置 `timeout` 参数(默认 120s);超时后返回明确错误而非挂起
### 风险 7:CI/CD 循环触发
- **场景:** code_ai_agent 推送代码触发 CI,CI 再触发 code_ai_agent,形成死循环
- **缓解:** commit message 加 `[skip-ci]` 标记;部署端点需要明确的 image tag 参数,不自动推断
---
## 10. 工作计划(Task Flow)
### Step 1:基础设施准备(P0-1, P0-6)
- 修改 `Dockerfile`,安装 git/openssh/kubectl
- 创建 `code-ai-agent-ssh-secret.yaml`
- 更新 `agent-manager-secret.yaml` 补充 `GITEE_USERNAME`
- **验收:** Pod 启动正常,`/root/.ssh/id_rsa` 权限 0400
### Step 2:工作空间与 Git 工具(P0-2, P0-3)
- 实现 `src/server/tools/workspace.py`
- 实现 `src/server/tools/git_tools.py`
- 编写单元测试
- **验收:** 能 clone gitee 仓库,创建分支,commit+push
### Step 3:SSH 工具与 API 路由(P0-4, P0-5)
- 实现 `src/server/tools/ssh_tools.py`
- 在 `api_server.py` 注册 `/git/*` 和 `/ssh/exec` 路由
- **验收:** HTTP 调用 clone + ssh exec 全流程通
### Step 4:部署工具与完整流程(P1-1 ~ P1-3)
- 实现 `src/server/tools/deploy_tools.py`
- 注册 `/deploy/k8s` 路由
- 配置 RBAC
- **验收:** 调用 `/deploy/k8s` 触发滚动更新成功
### Step 5:MCP 工具注册与增强(P1-4 ~ P1-6, P2)
- 在 `mcp_server.py` 注册新工具
- workspace 管理端点
- 按需推进 P2 优化项
---
## 成功标准
1. 完整工作流(clone → branch → 代码修改 → commit/push → SSH 测试 → K8s 部署)可通过 HTTP API 驱动,无人工干预
2. 所有凭证(git token、SSH 私钥)通过 K8s Secret 注入,不硬编码
3. 并发多任务互不干扰(task_id 隔离)
4. 单个操作失败有明确错误信息,不影响其他任务
5. Pod 重启后工作空间可按需重建(无状态设计)
---
**Does this plan capture your intent?**
- `proceed` — 开始实现,移交 executor
- `adjust [X]` — 返回调整某个模块设计
- `restart` — 废弃重新开始