Hermes Agent 的技能(skills)演进极快——你创建它们,在实时会话中修补它们,并持续交付修复。如果没有管线,这种速度就会变成风险:一次糟糕的技能更新会破坏你的生产环境 Agent,而你要等到为时已晚才会发现。本教程构建一条 CI/CD 管线,在每个变更到达生产环境之前就对其进行验证——用Git做版本控制,用Docker做隔离测试,用sun-port做安全的 API 暴露,用PostgreSQL做部署审计。
Agent 技能和配置是你 Hermes 系统的运行时 DNA。技能文件中的一个拼写错误——一条错误的 shell 命令、一个缺失的依赖、一个在你机器上能跑通但生产环境跑不通的路径——都可能悄无声息地破坏定时任务、webhook 处理器或交互式会话。
传统 CI/CD 管线聚焦于应用代码。但 Agent 配置有着独特的故障模式:
curl、jq、docker 或 git 已经安装,但实际上并没有CI/CD 管线能在这些问题进入生产环境之前就把它们抓住。下面是我们将要构建的架构:
┌─────────┐ ┌──────────┐ ┌───────────┐ ┌─────────────┐ ┌──────────┐
│ git push │───▶│ pre-commit│───▶│ Docker │───▶│ sun-port │───▶│ Hermes │
│ (skills) │ │ validate │ │ test │ │ (proxy) │ │ prod │
└─────────┘ └──────────┘ └───────────┘ └─────────────┘ └──────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌──────────────────────────────────────────────────────────┐
│ PostgreSQL deployment audit log │
└──────────────────────────────────────────────────────────┘
第一道闸门:在技能进入提交之前就校验每一个技能。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 密钥、以及重复的技能名称。它在毫秒级内运行,成为你的第一道防线。
扩展这个钩子,检查技能内部 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
第二道闸门:在一个全新的 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
让技能能加载只是最低门槛。对于生产级 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
当你的 CI/CD 管线部署到生产环境时,Hermes Agent 需要能被访问——用于 webhook 触发、API 调用或 im-bot 集成。sun-port 是一个轻量级反向代理,位于 Hermes 之前,负责处理认证、限流和 TLS 终止。
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
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
每条 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;
在管线的每个阶段之后插入一条记录。这里是一个在你的部署工作流末尾运行的脚本:
#!/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 <
找出哪些技能失败得最频繁:
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/)看看是不是最近的某次补丁引入了不稳定性。
现在把所有东西串联成一个部署脚本。这就是你的 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"
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 测试中失败了。管线正确地阻止了部署,并且这次失败被记录下来以供调查。
为 Hermes Agent 技能构建 CI/CD 管线,能把你的 Agent 从一个一次性工具变成一个持续进化的系统。Git 追踪每一次变更,Docker 验证每一条命令,sun-port 保护每一个端点,PostgreSQL 记录每一次部署。结果就是:一个随着每一次提交而变得更好、更安全的 Agent。