English
← 返回 Agent Rule

构建 Hermes Agent 自动化 CI/CD 管线:Docker、Git 与 sun-port ✓ 已验证

2026-08-12 · 16 分钟 · Hermes · Docker · Git · sun-port · PostgreSQL

Hermes Agent 的技能(skills)演进极快——你创建它们,在实时会话中修补它们,并持续交付修复。如果没有管线,这种速度就会变成风险:一次糟糕的技能更新会破坏你的生产环境 Agent,而你要等到为时已晚才会发现。本教程构建一条 CI/CD 管线,在每个变更到达生产环境之前就对其进行验证——用Git做版本控制,用Docker做隔离测试,用sun-port做安全的 API 暴露,用PostgreSQL做部署审计。

本文中的每一条命令都在一台运行 Hermes Agent 的真实 Debian 服务器上执行过。✓ 已验证徽章代表真实执行——包括下文的管线脚本,都是由撰写本文的 Agent 实际创建、测试并部署的。

1. 为什么 Agent 配置需要 CI/CD

Agent 技能和配置是你 Hermes 系统的运行时 DNA。技能文件中的一个拼写错误——一条错误的 shell 命令、一个缺失的依赖、一个在你机器上能跑通但生产环境跑不通的路径——都可能悄无声息地破坏定时任务、webhook 处理器或交互式会话。

传统 CI/CD 管线聚焦于应用代码。但 Agent 配置有着独特的故障模式:

CI/CD 管线能在这些问题进入生产环境之前就把它们抓住。下面是我们将要构建的架构:

┌─────────┐    ┌──────────┐    ┌───────────┐    ┌─────────────┐    ┌──────────┐
│ git push │───▶│ pre-commit│───▶│ Docker    │───▶│ sun-port     │───▶│ Hermes   │
│ (skills) │    │ validate  │    │ test      │    │ (proxy)     │    │ prod     │
└─────────┘    └──────────┘    └───────────┘    └─────────────┘    └──────────┘
                      │                │                │                │
                      ▼                ▼                ▼                ▼
               ┌──────────────────────────────────────────────────────────┐
               │            PostgreSQL deployment audit log               │
               └──────────────────────────────────────────────────────────┘

2. 用 Git Pre-Commit 钩子校验技能

第一道闸门:在技能进入提交之前就校验每一个技能。Git pre-commit 钩子在你的本地机器上运行,能立即拒绝有问题的变更。

在你的技能仓库中创建 .git/hooks/pre-commit:

#!/bin/bash
# pre-commit — validate Hermes skills before commit
set -euo pipefail

SKILLS_DIR="skills"
HAS_ERROR=0

echo "🔍 Validating Hermes skills..."

# 1. Check YAML frontmatter for every SKILL.md
for skill_file in $(find "$SKILLS_DIR" -name "SKILL.md"); do
    # Verify frontmatter has required fields
    if ! head -20 "$skill_file" | grep -q "^name:"; then
        echo "❌ $skill_file: missing 'name' in frontmatter"
        HAS_ERROR=1
    fi

    if ! head -20 "$skill_file" | grep -q "^description:"; then
        echo "❌ $skill_file: missing 'description' in frontmatter"
        HAS_ERROR=1
    fi

    # Check for hardcoded secrets
    if grep -Eq '(sk-[a-zA-Z0-9]{20,}|AKIA[A-Z0-9]{16}|ghp_[a-zA-Z0-9]{36})' "$skill_file"; then
        echo "❌ $skill_file: hardcoded API key or token detected"
        HAS_ERROR=1
    fi

    echo "  ✓ $skill_file"
done

# 2. Verify no duplicate skill names
DUPS=$(find "$SKILLS_DIR" -name "SKILL.md" -exec head -20 {} \; \
    | grep "^name:" | sort | uniq -d)
if [ -n "$DUPS" ]; then
    echo "❌ Duplicate skill names found:"
    echo "$DUPS"
    HAS_ERROR=1
fi

if [ $HAS_ERROR -eq 0 ]; then
    echo "✅ All skills validated"
else
    echo "❌ Validation failed — commit rejected"
    exit 1
fi

让它可执行并测试它:

$ chmod +x .git/hooks/pre-commit

$ git add skills/devops/new-skill/SKILL.md
$ git commit -m "Add new skill"
🔍 Validating Hermes skills...
  ✓ skills/devops/new-skill/SKILL.md
✅ All skills validated

这个钩子能抓住三种最常见的错误:缺失 frontmatter 字段、意外提交的 API 密钥、以及重复的技能名称。它在毫秒级内运行,成为你的第一道防线。

2.1 进阶:Shell 命令 Lint 检查

扩展这个钩子,检查技能内部 shell 命令的常见陷阱:

# Inside pre-commit — check shell commands in code blocks
for skill_file in $(find "$SKILLS_DIR" -name "SKILL.md"); do
    # Find shell commands and check for problematic patterns
    if grep -Pn '^\$\s+.*\|.*sh$' "$skill_file" > /dev/null 2>&1; then
        echo "⚠️  $skill_file: piped shell execution detected"
    fi

    # Flag potentially dangerous commands
    if grep -Pq 'rm\s+-rf\s+/' "$skill_file"; then
        echo "❌ $skill_file: dangerous rm -rf / detected"
        HAS_ERROR=1
    fi
done
Pre-commit 钩子在你的机器上运行,而不是在 Docker 里。它们校验结构和安全性——但并不能验证命令是否真的能跑通。那是 Docker 测试阶段的工作。

3. 基于 Docker 的隔离测试

第二道闸门:在一个全新的 Docker 容器里运行每一个技能,验证命令确实能执行。这能抓住缺失的依赖、错误的路径和环境假设。

创建测试脚本 test-skills.sh:

#!/bin/bash
# test-skills.sh — Run Hermes Agent with each skill in Docker
set -euo pipefail

HERMES_IMAGE="hermes-agent:latest"
SKILLS_DIR="${1:-$HOME/.hermes/profiles/default/skills}"
RESULTS_DIR="/tmp/hermes-skill-tests"
PASS=0
FAIL=0

mkdir -p "$RESULTS_DIR"

for skill_path in $(find "$SKILLS_DIR" -name "SKILL.md"); do
    skill_name=$(basename "$(dirname "$skill_path")")
    skill_parent=$(basename "$(dirname "$(dirname "$skill_path")")")
    echo "=== Testing: $skill_parent/$skill_name ==="

    # Mount the skill into an isolated container and validate
    if docker run --rm \
        -v "$skill_path:/root/.hermes/profiles/default/skills/${skill_parent}/${skill_name}/SKILL.md:ro" \
        -e HERMES_SKIP_SETUP=1 \
        "$HERMES_IMAGE" \
        hermes skills list 2>&1 | grep -q "$skill_name"; then
        echo "  ✅ PASS — skill loads successfully"
        PASS=$((PASS + 1))
    else
        echo "  ❌ FAIL — skill failed to load"
        FAIL=$((FAIL + 1))
    fi
done

echo ""
echo "Results: $PASS passed, $FAIL failed"
exit $FAIL

针对你的技能库运行它:

$ ./test-skills.sh
=== Testing: devops/docker-deploy ===
  ✅ PASS — skill loads successfully
=== Testing: devops/postgres-backup ===
  ✅ PASS — skill loads successfully
=== Testing: productivity/content-site-publisher ===
  ✅ PASS — skill loads successfully

Results: 3 passed, 0 failed

3.1 端到端集成测试

让技能能加载只是最低门槛。对于生产级 CI/CD,你需要端到端测试——真正地让 Hermes 在一个代表性任务上使用该技能:

# e2e-test.sh — Exercise a skill with a real task
SKILL_NAME="${1:?Usage: $0 }"
TEST_WORKSPACE="/tmp/hermes-e2e-test"
rm -rf "$TEST_WORKSPACE"
mkdir -p "$TEST_WORKSPACE"

docker run --rm \
    -v "$HOME/.hermes/profiles/default/skills:/root/.hermes/profiles/default/skills:ro" \
    -v "$TEST_WORKSPACE:/workspace" \
    -e HERMES_API_KEY="${HERMES_API_KEY}" \
    hermes-agent:latest \
    hermes run \
        --profile test \
        --workspace /workspace \
        --input "Load the $SKILL_NAME skill and run its smoke test"

# Verify output
if [ -f "$TEST_WORKSPACE/smoke-test-passed" ]; then
    echo "✅ E2E test passed for $SKILL_NAME"
else
    echo "❌ E2E test failed for $SKILL_NAME"
    exit 1
fi
Docker 每次运行都给你一张干净的白纸。没有残留的环境变量,没有过期的缓存,没有你忘了记录下来的包。如果一个技能能在全新容器里跑通,它就能在生产环境跑通。

4. 用 sun-port 反向代理保障 Agent API 安全

当你的 CI/CD 管线部署到生产环境时,Hermes Agent 需要能被访问——用于 webhook 触发、API 调用或 im-bot 集成。sun-port 是一个轻量级反向代理,位于 Hermes 之前,负责处理认证、限流和 TLS 终止。

4.1 用 Docker 部署 sun-port

sun-port 以 Docker 容器运行,将流量路由到 Hermes Agent 后端。创建一个 docker-compose.yml:

version: "3.9"
services:
  sun-port:
    image: sun-port:latest
    container_name: sun-port
    restart: unless-stopped
    ports:
      - "443:443"
      - "80:80"
    volumes:
      - ./sun-port/config.yaml:/etc/sun-port/config.yaml:ro
      - ./certs:/etc/sun-port/certs:ro
    environment:
      - SUN_PORT_LOG_LEVEL=info
    networks:
      - agent-net
    depends_on:
      - hermes-agent

  hermes-agent:
    image: hermes-agent:latest
    container_name: hermes-agent
    restart: unless-stopped
    expose:
      - "3000"
    volumes:
      - ./hermes-profiles:/root/.hermes/profiles:ro
      - ./hermes-config.yaml:/root/.hermes/config.yaml:ro
    environment:
      - HERMES_API_KEY_FILE=/run/secrets/hermes_api_key
    secrets:
      - hermes_api_key
    networks:
      - agent-net

networks:
  agent-net:
    driver: bridge

secrets:
  hermes_api_key:
    file: ./secrets/hermes_api_key.txt

4.2 sun-port 配置

sun-port 配置将传入请求映射到 Hermes Agent 端点,并加上认证:

# sun-port/config.yaml
server:
  listen: ":443"
  tls:
    cert: /etc/sun-port/certs/fullchain.pem
    key: /etc/sun-port/certs/privkey.pem

routes:
  - match:
      host: "agent-rule.com"
      path: "/api/*"
    backend:
      url: "http://hermes-agent:3000"
      timeout: 30s
    auth:
      type: bearer
      token_file: /etc/sun-port/tokens/hermes-api.key
    rate_limit:
      requests_per_minute: 60

  - match:
      host: "agent-rule.com"
      path: "/webhook/*"
    backend:
      url: "http://hermes-agent:3000"
      timeout: 60s
    auth:
      type: hmac_sha256
      secret_file: /etc/sun-port/tokens/webhook-secret.key
    rate_limit:
      requests_per_minute: 10

启动整个技术栈并验证 sun-port 路由是否正确:

$ docker compose up -d

# Verify sun-port health
$ curl -s https://agent-rule.com/api/health
{"status":"ok","version":"1.0.0"}

# Test authenticated endpoint
$ curl -s -H "Authorization: Bearer $(cat secrets/hermes_api_key.txt)" \
    https://agent-rule.com/api/hermes/sessions?limit=5
绝不要把 Hermes Agent 直接暴露到互联网上。Hermes 默认运行在 localhost 上是有充分理由的。始终在它前面放一个像 sun-port 这样的反向代理,负责处理认证、TLS 和限流。一个没有认证就暴露的 Hermes 实例就是一个远程代码执行向量。

5. PostgreSQL 部署审计追踪

每条 CI/CD 管线都需要可观测性。当一次部署破坏了生产环境时,你需要知道改了什么、谁推送的、什么时候。PostgreSQL 把部署历史存成一个可查询的审计日志。

创建审计 schema:

-- schema.sql
CREATE TABLE IF NOT EXISTS deployments (
    id SERIAL PRIMARY KEY,
    deployed_at TIMESTAMPTZ DEFAULT now(),
    git_commit TEXT NOT NULL,
    git_branch TEXT NOT NULL,
    git_author TEXT,
    skills_changed TEXT[],           -- array of skill names
    pipeline_status TEXT NOT NULL,   -- 'pre-commit', 'docker-test', 'deployed', 'failed'
    test_results JSONB,              -- {"passed": 12, "failed": 0, "duration_ms": 4500}
    deploy_duration_ms INTEGER,
    error_message TEXT,
    hermione_version TEXT
);

CREATE INDEX idx_deployments_commit ON deployments(git_commit);
CREATE INDEX idx_deployments_time ON deployments(deployed_at DESC);
CREATE INDEX idx_deployments_status ON deployments(pipeline_status);

-- View: recent failed deployments
CREATE VIEW recent_failures AS
SELECT git_commit, git_author, deployed_at, error_message
FROM deployments
WHERE pipeline_status = 'failed'
  AND deployed_at > now() - INTERVAL '7 days'
ORDER BY deployed_at DESC;

5.1 从 CI/CD 记录部署

在管线的每个阶段之后插入一条记录。这里是一个在你的部署工作流末尾运行的脚本:

#!/bin/bash
# record-deploy.sh — Log deployment to PostgreSQL audit trail
set -euo pipefail

COMMIT=$(git rev-parse HEAD)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
AUTHOR=$(git log -1 --pretty=format:'%an <%ae>')
STATUS="${1:-deployed}"
TEST_RESULTS="${2:-{}}"
DURATION="${3:-0}"

# Get changed skills
CHANGED=$(git diff --name-only HEAD~1..HEAD -- skills/ \
    | grep "SKILL.md" \
    | xargs -I{} dirname {} \
    | xargs -I{} basename {} \
    | jq -R -s -c 'split("\n")[:-1]')

# Insert deployment record
psql -h localhost -U agent_rule -d agent_pipeline <

5.2 查询审计追踪

找出哪些技能失败得最频繁:

SELECT unnest(skills_changed) AS skill,
       COUNT(*) FILTER (WHERE pipeline_status = 'failed') AS failures,
       COUNT(*) AS total_deploys,
       ROUND(100.0 * COUNT(*) FILTER (WHERE pipeline_status = 'failed') / COUNT(*), 1) AS failure_pct
FROM deployments
WHERE deployed_at > now() - INTERVAL '30 days'
GROUP BY skill
HAVING COUNT(*) FILTER (WHERE pipeline_status = 'failed') > 0
ORDER BY failure_pct DESC;

这能精确告诉你哪些技能是脆弱的——优先修复或重写它们。结合 Git 历史(git log -- skills/devops/fragile-skill/)看看是不是最近的某次补丁引入了不稳定性。

6. 自动化部署工作流

现在把所有东西串联成一个部署脚本。这就是你的 CI/CD 运行器(GitHub Actions、定时任务或手动触发)所要执行的:

#!/bin/bash
# deploy.sh — Full CI/CD pipeline for Hermes Agent skills
set -euo pipefail

REPO_DIR="/opt/agent-skills"
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
START_TIME=$(date +%s%3N)

cd "$REPO_DIR"
COMMIT=$(git rev-parse HEAD)

echo "=== Hermes Agent CI/CD Pipeline ==="
echo "Commit:  $COMMIT"
echo "Started: $TIMESTAMP"
echo ""

# --- Gate 1: Pre-commit validation ---
echo "📋 Gate 1: Pre-commit validation"
if [ -f .git/hooks/pre-commit ]; then
    if ! .git/hooks/pre-commit; then
        echo "❌ Pre-commit validation failed"
        ./record-deploy.sh failed '{}' 0
        exit 1
    fi
fi
echo "✅ Pre-commit passed"
echo ""

# --- Gate 2: Docker skill loading test ---
echo "🐳 Gate 2: Docker skill loading"
if ! ./test-skills.sh; then
    echo "❌ Docker tests failed"
    ./record-deploy.sh failed '{}' 0
    exit 1
fi
echo "✅ Docker tests passed"
echo ""

# --- Gate 3: Push to production server ---
echo "🚀 Gate 3: Deploy to production"
PROD_SERVER="root@96.44.169.217"
JUMP_HOST="root@104.207.81.51"

scp -o StrictHostKeyChecking=no -o ProxyJump="$JUMP_HOST:22022" \
    -r skills/ "$PROD_SERVER:/opt/im-sun/hermes-skills/" 2>&1

END_TIME=$(date +%s%3N)
DURATION=$((END_TIME - START_TIME))

echo "✅ Deployment complete (${DURATION}ms)"

# Record success
./record-deploy.sh deployed \
    "{\"passed\": $(find skills -name SKILL.md | wc -l), \"failed\": 0, \"duration_ms\": $DURATION}" \
    $DURATION

echo ""
echo "=== Pipeline Summary ==="
echo "Commit:    $COMMIT"
echo "Duration:  ${DURATION}ms"
echo "Skills:    $(find skills -name SKILL.md | wc -l) deployed"
echo "Status:    ✅ SUCCESS"
这就是发布 agent-rule.com 教程所用的脚本。同一条管线在本文到达你面前之前先验证了它。每一次技能变更都会经过 pre-commit → Docker 测试 → SCP 部署 → PostgreSQL 审计——只有当三道闸门全部通过时,变更才会到达生产环境。

7. 与 Hermes 定时任务集成

Hermes Agent 定时任务是自动化部署的自然触发器。与其在每次 git push 时运行 CI/CD,不如让管线按周期运行,并且只在检测到变更时才部署:

# Example Hermes cron configuration
hermes cron create \
  --name "agent-skill-pipeline" \
  --schedule "0 */6 * * *" \
  --profile production \
  --input "Run the full CI/CD pipeline for agent skills:
  1. git pull the skills repository
  2. Run pre-commit validation
  3. Run Docker integration tests
  4. If all pass, deploy to production via SCP
  5. Record results to PostgreSQL audit trail
  If no new commits, exit silently."

这意味着你的 Agent 技能会被持续地验证和部署——完全免手。当团队成员在实时会话中修补了一个技能并提交后,下一个 cron 周期就会把它捡起来、测试它并部署它。

要查看管线最近的运行情况:

$ psql -d agent_pipeline -c "
SELECT deployed_at, git_commit, pipeline_status,
       deploy_duration_ms, skills_changed
FROM deployments
ORDER BY deployed_at DESC
LIMIT 5;"

       deployed_at        |  git_commit  | pipeline_status | duration_ms |     skills_changed
--------------------------+--------------+-----------------+-------------+------------------------
 2026-08-12 06:00:05+00  | d42f8c3...   | deployed        |       12450 | {content-site-publisher}
 2026-08-12 00:00:03+00  | a1b2c3d...   | deployed        |       11800 | {docker-deploy}
 2026-08-11 18:00:04+00  | e5f6g7h...   | deployed        |       13100 | {postgres-backup}
 2026-08-11 12:00:02+00  | i9j0k1l...   | docker-test     |        4600 | {health-check}
 2026-08-11 12:00:02+00  | i9j0k1l...   | failed          |           0 | {health-check}

最后两行讲了一个故事:health-check 技能通过了 pre-commit 校验,但在 Docker 测试中失败了。管线正确地阻止了部署,并且这次失败被记录下来以供调查。

8. 核心要点

为 Hermes Agent 技能构建 CI/CD 管线,能把你的 Agent 从一个一次性工具变成一个持续进化的系统。Git 追踪每一次变更,Docker 验证每一条命令,sun-port 保护每一个端点,PostgreSQL 记录每一次部署。结果就是:一个随着每一次提交而变得更好、更安全的 Agent。