fix(i18n): 強制 Elephant Alpha Gemini 回應繁體中文
All checks were successful
CD Pipeline / deploy (push) Successful in 1m20s

- aider_heal_executor.py:全檔簡體→繁體,所有 Telegram 通知節點繁化
- elephant_alpha_orchestrator.py:system prompt 與 user prompt 雙層加入語言強制指令,確保 reasoning/expected_outcome 等欄位輸出繁體中文

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ogt
2026-04-21 12:22:13 +08:00
parent 0cc940fdb1
commit 31dfbcdd4d
2 changed files with 79 additions and 71 deletions

View File

@@ -2,15 +2,15 @@
services/aider_heal_executor.py
ADR-014: Autonomous Code Heal Pipeline
通过 SSH 在 110 主机执行 Aider动修复 momo-pro repo 的程式碼问题
复后直接 git push触发 Gitea CD Pipeline 部署。
透過 SSH 在 110 主機執行 Aider動修復 momo-pro repo 的程式碼問題
復後直接 git push觸發 Gitea CD Pipeline 部署。
安全护拦
L1 - 文件白名(只改 services/ routes/ database/ .py
L2 - diff 限制(>50 行 → 拒,不 push
L3 - 每小最多 5 次 CODE_FIX
L4 - health check 失 → 自 git revert + push
L5 - Telegram 通知每次修复结果(成功/失/回
安全護欄
L1 - 檔案白名(只改 services/ routes/ database/ .py
L2 - diff 限制(>50 行 → 拒,不 push
L3 - 每小最多 5 次 CODE_FIX
L4 - health check 失 → 自 git revert + push
L5 - Telegram 通知每次修復結果(成功/失/回
"""
import os
@@ -42,7 +42,7 @@ HEALTH_CHECK_URL: str = (
)
OLLAMA_API_BASE: str = os.getenv("OLLAMA_API_BASE", "http://192.168.0.111:11434")
AIDER_MODEL: str = os.getenv("AIDER_MODEL", "ollama/qwen2.5-coder:7b")
AIDER_MODEL: str = os.getenv("AIDER_MODEL", "ollama/qwen3-coder-next")
MAX_DIFF_LINES: int = int(os.getenv("AIDER_MAX_DIFF_LINES", "50"))
MAX_HOURLY_FIX: int = int(os.getenv("AIDER_MAX_HOURLY_FIX", "5"))
@@ -50,12 +50,12 @@ MAX_HOURLY_FIX: int = int(os.getenv("AIDER_MAX_HOURLY_FIX", "5"))
TELEGRAM_BOT_TOKEN: str = os.getenv("TELEGRAM_BOT_TOKEN", "")
TELEGRAM_CHAT_ID: str = os.getenv("TELEGRAM_CHAT_ID", "")
# 允 Aider 修改的路(正
# 允 Aider 修改的路(正規表示式
ALLOWED_FILE_PATTERN = re.compile(
r"^(services|routes|database)/[a-zA-Z0-9_]+\.py$"
)
# ── 速率控制(线程安全) ────────────────────────────────────────────────────
# ── 速率控制(執行緒安全) ────────────────────────────────────────────────────
_lock: threading.Lock = threading.Lock()
_fix_history: List[float] = []
_last_host_reset: float = time.monotonic()
@@ -63,14 +63,14 @@ _last_host_reset: float = time.monotonic()
def _enforce_rate_limit() -> bool:
"""
每小最多 MAX_HOURLY_FIX 次修
使用单调时钟避免系统时间跳变影响
每小最多 MAX_HOURLY_FIX 次修
使用單調時鐘避免系統時間跳變影響
"""
global _last_host_reset, _fix_history
now = time.monotonic()
with _lock:
# 每小重置一次计数(基于单调时钟的近似小窗口)
# 每小重置一次計數(基於單調時鐘的近似小窗口)
if now - _last_host_reset > 3600.0:
_fix_history.clear()
_last_host_reset = now
@@ -89,7 +89,7 @@ def _ssh_exec(
check: bool = True,
) -> tuple[int, str, str]:
"""
远程主机执行命令(通过 SSH
遠端主機執行命令(透過 SSH
返回 (returncode, stdout, stderr)
"""
safe_cmd = cmd.replace('"', '\\"').replace("`", "\\`").replace("$", "\\$")
@@ -131,7 +131,7 @@ def _wait_for_health(
interval_seconds: int = 10,
) -> bool:
"""
续轮询健康查,直到成功或超
續輪詢健康查,直到成功或超
"""
deadline = time.monotonic() + timeout_seconds
while time.monotonic() < deadline:
@@ -143,7 +143,7 @@ def _wait_for_health(
def _notify_telegram(message_html: str) -> None:
"""非阻塞通知,失败静默忽略。"""
"""非阻塞通知,失敗靜默忽略。"""
if not TELEGRAM_BOT_TOKEN or not TELEGRAM_CHAT_ID:
return
try:
@@ -162,7 +162,7 @@ def _git_cmd(
timeout: int = 30,
check: bool = True,
) -> tuple[int, str, str]:
"""在 repo_path 下行 git 命令。"""
"""在 repo_path 下行 git 命令。"""
return _ssh_exec(
f"cd {shlex.quote(repo_path)} && git " + " ".join(shlex.quote(a) for a in args),
cwd=repo_path,
@@ -178,9 +178,9 @@ def execute_code_fix(
context: Optional[dict] = None,
) -> Dict[str, Any]:
"""
主要入口:针对指定文件执行 Aider 自动修复并推版。
主要入口:針對指定檔案執行 Aider 自動修復並推版。
返回结构
返回結構
{
'success': bool,
'action': 'CODE_FIX',
@@ -193,9 +193,9 @@ def execute_code_fix(
ctx: Dict[str, Any] = context or {}
repo = Path(REPO_PATH_110).expanduser()
# L1文件白名
# L1檔案白名
if not ALLOWED_FILE_PATTERN.match(target_file):
reason = f"[AiderHeal] 文件不在白名单: {target_file}"
reason = f"[AiderHeal] 檔案不在白名單:{target_file}"
logger.warning("event=heal_reject reason=%s file=%s", reason, target_file)
return {
"success": False,
@@ -207,7 +207,7 @@ def execute_code_fix(
# L3速率限制
if not _enforce_rate_limit():
reason = f"[AiderHeal] 每小上限 {MAX_HOURLY_FIX} 次,跳"
reason = f"[AiderHeal] 每小上限 {MAX_HOURLY_FIX} 次,跳"
logger.warning("event=rate_limit file=%s", target_file)
return {
"success": False,
@@ -218,14 +218,14 @@ def execute_code_fix(
}
_notify_telegram(
f"🔧 <b>AiderHeal 启动</b>\n"
f"错误类型: <code>{error_type}</code>\n"
f"├ 目标文件: <code>{target_file}</code>\n"
f"时间: {ts}"
f"🔧 <b>AiderHeal 啟動</b>\n"
f"錯誤類型:<code>{error_type}</code>\n"
f"├ 目標檔案:<code>{target_file}</code>\n"
f"時間:{ts}"
)
logger.info("event=heal_start error_type=%s file=%s", error_type, target_file)
# ── Step 1准备 repo在 110 上) ────────────────────────────────────────
# ── Step 1準備 repo在 110 上) ────────────────────────────────────────
setup_cmds = (
f"cd {REPO_PATH_110} && "
f"git fetch {GITEA_REMOTE} main 2>&1 && "
@@ -234,9 +234,9 @@ def execute_code_fix(
)
rc, out, err = _ssh_exec(setup_cmds, timeout=30)
if rc != 0:
msg = f"[AiderHeal] git 准备失败: {err or out}"
msg = f"[AiderHeal] Git 準備失敗:{err or out}"
logger.error("event=setup_failed error=%s", msg)
_notify_telegram(f"❌ AiderHeal 失git 准备\n<code>{msg}</code>")
_notify_telegram(f"❌ AiderHeal 失Git 準備\n<code>{msg}</code>")
return {
"success": False,
"action": "CODE_FIX",
@@ -245,7 +245,7 @@ def execute_code_fix(
"reverted": False,
}
# ── Step 2构造 Aider 指令 ───────────────────────────────────────────────
# ── Step 2建構 Aider 指令 ───────────────────────────────────────────────
safe_error = error_message[:500].replace('"', "'").replace("`", "'").replace("$", "")
instruction = (
f"Fix the following {error_type} in this file. "
@@ -265,8 +265,8 @@ def execute_code_fix(
rc, aider_out, aider_err = _ssh_exec(aider_cmd, timeout=180)
logger.debug("event=aider_output snippet=%s", (aider_out or aider_err)[:300])
# ── Step 3diff L2 护拦 ─────────────────────────────────────────
# 使用 git diff --numstat 取有意义的变更行数(增加+删除)
# ── Step 3diff L2 護欄 ─────────────────────────────────────────
# 使用 git diff --numstat 取有意義的變更行數(新增+刪除)
numstat_cmd = (
f"cd {REPO_PATH_110} && "
f"git diff --numstat HEAD 2>&1 | awk '{{added+=$1; deleted+=$2}} END{{print added+deleted}}'"
@@ -275,9 +275,9 @@ def execute_code_fix(
diff_lines = int(diff_lines_str.strip()) if rc2 == 0 and diff_lines_str.strip().isdigit() else 0
if diff_lines == 0:
msg = "[AiderHeal] Aider 未生任何修改diff=0可能已自动解决或模型失效"
msg = "[AiderHeal] Aider 未生任何修改diff=0可能已自動解決或模型失效"
logger.warning("event=no_diff file=%s", target_file)
_notify_telegram(f"⚠️ AiderHeal修改\n<code>{target_file}</code>")
_notify_telegram(f"⚠️ AiderHeal修改\n<code>{target_file}</code>")
return {
"success": False,
"action": "CODE_FIX",
@@ -287,20 +287,20 @@ def execute_code_fix(
}
if diff_lines > MAX_DIFF_LINES:
# 改太大,丢弃并告警
# 改太大,丟棄並告警
_, _, _ = _ssh_exec(
f"cd {REPO_PATH_110} && git checkout -- . 2>&1", timeout=10
)
msg = (
f"[AiderHeal] diff 超出限制 {diff_lines} > {MAX_DIFF_LINES} 行,"
f"丢弃,需人工介入"
f"丟棄,需人工介入"
)
logger.warning("event=diff_too_large file=%s diff_lines=%d", target_file, diff_lines)
_notify_telegram(
f"⚠️ <b>AiderHealdiff 大,需人工核</b>\n"
f"文件: <code>{target_file}</code>\n"
f"├ diff: {diff_lines} 行(上限 {MAX_DIFF_LINES}\n"
f"错误: <code>{error_type}</code>"
f"⚠️ <b>AiderHealdiff 大,需人工核</b>\n"
f"檔案:<code>{target_file}</code>\n"
f"├ diff{diff_lines} 行(上限 {MAX_DIFF_LINES}\n"
f"錯誤:<code>{error_type}</code>"
)
return {
"success": False,
@@ -310,7 +310,7 @@ def execute_code_fix(
"reverted": False,
}
# ── Step 4提交推送 ───────────────────────────────────────────────────
# ── Step 4提交推送 ───────────────────────────────────────────────────
fix_msg = (
f"fix(autoheal): [{error_type}] auto-fix {target_file}\n\n"
f"Triggered by AiderHealExecutor (ADR-014)\n"
@@ -324,14 +324,14 @@ def execute_code_fix(
)
rc3, commit_out, commit_err = _ssh_exec(commit_cmd, timeout=30)
# 取最新的 commit SHA push 的 HEAD 取,更可靠)
# 取最新的 commit SHA push 的 HEAD 取,更可靠)
_, commit_sha, _ = _git_cmd(REPO_PATH_110, ["log", "-1", "--format=%H"], timeout=10)
commit_sha = commit_sha.strip() or "unknown"
if rc3 != 0:
msg = f"[AiderHeal] git push 失败: {commit_err or commit_out}"
msg = f"[AiderHeal] git push 失敗:{commit_err or commit_out}"
logger.error("event=push_failed error=%s", msg)
_notify_telegram(f"❌ AiderHeal git push 失\n<code>{msg}</code>")
_notify_telegram(f"❌ AiderHeal git push 失\n<code>{msg}</code>")
return {
"success": False,
"action": "CODE_FIX",
@@ -343,24 +343,24 @@ def execute_code_fix(
logger.info("event=push_ok commit=%s", commit_sha)
_notify_telegram(
f"🚀 <b>AiderHeal push 完成</b>\n"
f"├ commit: <code>{commit_sha}</code>\n"
f"文件: <code>{target_file}</code>\n"
f"└ 等待健康检查..."
f"├ commit<code>{commit_sha}</code>\n"
f"檔案:<code>{target_file}</code>\n"
f"└ 等待健康檢查…"
)
# ── Step 5健康L4 护拦 ──────────────────────────────────────────
time.sleep(10) # 部署一点启动缓冲
# ── Step 5健康L4 護欄 ──────────────────────────────────────────
time.sleep(10) # 部署一點啟動緩衝
healthy = _wait_for_health(HEALTH_CHECK_URL, timeout_seconds=120, interval_seconds=10)
if healthy:
msg = f"[AiderHeal] 修成功部署完成: {target_file} ({commit_sha})"
msg = f"[AiderHeal] 修成功部署完成{target_file} ({commit_sha})"
logger.info("event=heal_success commit=%s file=%s", commit_sha, target_file)
_notify_telegram(
f"✅ <b>AiderHeal 修完成</b>\n"
f"错误: <code>{error_type}</code>\n"
f"文件: <code>{target_file}</code>\n"
f"├ commit: <code>{commit_sha}</code>\n"
f"└ diff: {diff_lines}"
f"✅ <b>AiderHeal 修完成</b>\n"
f"錯誤:<code>{error_type}</code>\n"
f"檔案:<code>{target_file}</code>\n"
f"├ commit<code>{commit_sha}</code>\n"
f"└ diff{diff_lines}"
)
return {
"success": True,
@@ -370,7 +370,7 @@ def execute_code_fix(
"reverted": False,
}
# ── Step 6健康查失 → 自 revertL4 护拦 ─────────────────────────
# ── Step 6健康查失 → 自 revertL4 護欄 ─────────────────────────
logger.error("event=health_check_failed commit=%s", commit_sha)
_, revert_out, revert_err = _ssh_exec(
f"cd {REPO_PATH_110} && "
@@ -383,21 +383,21 @@ def execute_code_fix(
if "error" not in revert_out.lower() and "error" not in revert_err.lower():
msg = (
f"[AiderHeal] 健康查失,已自动回滚: "
f"[AiderHeal] 健康查失,已自動回滾:"
f"{commit_sha}{revert_sha}"
)
logger.warning("event=reverted commit=%s to=%s", commit_sha, revert_sha)
_notify_telegram(
f"🔄 <b>AiderHeal 自动回滚</b>\n"
f"├ 原 commit: <code>{commit_sha}</code>\n"
f"├ 回 commit: <code>{revert_sha}</code>\n"
f"└ 需人工排查: <code>{error_type}</code> in <code>{target_file}</code>"
f"🔄 <b>AiderHeal 自動回滾</b>\n"
f"├ 原 commit<code>{commit_sha}</code>\n"
f"├ 回 commit<code>{revert_sha}</code>\n"
f"└ 需人工排查<code>{error_type}</code> in <code>{target_file}</code>"
)
else:
msg = f"[AiderHeal] 回滚失败!需立即人工介入: {revert_err}"
msg = f"[AiderHeal] 回滾失敗!需立即人工介入{revert_err}"
logger.critical("event=revert_failed commit=%s error=%s", commit_sha, revert_err)
_notify_telegram(
f"🚨 <b>AiderHeal 回滚失败!请立即人工介入</b>\n<code>{msg}</code>"
f"🚨 <b>AiderHeal 回滾失敗!請立即人工介入</b>\n<code>{msg}</code>"
)
return {

View File

@@ -100,6 +100,8 @@ class ElephantAlphaOrchestrator:
"""Build comprehensive system prompt for Elephant Alpha"""
return f"""You are Elephant Alpha, the Super Orchestrator for momo-pro-system e-commerce AI platform.
重要語言規定:你的 JSON 回應中所有文字欄位strategic_assessment、reasoning、expected_outcome、execution_plan 的 description、risk_factors、contingency_plans必須使用繁體中文台灣用語撰寫。嚴禁使用英文或簡體中文。
CURRENT ARCHITECTURE:
- You coordinate 3 specialized AI agents: Hermes (Analyst), NemoTron (Dispatcher), OpenClaw (Strategist)
- Your context window: 256,000 tokens - enables deep strategic reasoning
@@ -148,14 +150,17 @@ BUSINESS CONTEXT:
- Revenue optimization and growth
- Customer experience enhancement
LANGUAGE REQUIREMENT:
ALL text fields in your JSON response (strategic_assessment, reasoning, expected_outcome, execution_plan descriptions, risk_factors, contingency_plans) MUST be written in Traditional Chinese (繁體中文). Use Taiwan-standard terminology. Do NOT use Simplified Chinese or English for any user-facing content.
RESPONSE FORMAT:
Always respond with structured JSON:
{{
"strategic_assessment": "Overall strategic evaluation",
"strategic_assessment": "整體策略評估(繁體中文)",
"priority": "critical|high|medium|low",
"agents_required": ["agent1", "agent2"],
"reasoning": "Detailed reasoning for decision",
"expected_outcome": "Expected business impact",
"reasoning": "詳細決策推理(繁體中文)",
"expected_outcome": "預期業務影響(繁體中文)",
"confidence": 0.85,
"execution_plan": [
{{
@@ -163,16 +168,17 @@ Always respond with structured JSON:
"agent": "hermes",
"action": "analyze_price_competition",
"parameters": {{}},
"expected_duration": "2-3 minutes"
"expected_duration": "2-3 分鐘",
"description": "執行說明(繁體中文)"
}}
],
"resource_requirements": {{
"compute_cost": "$0.00",
"time_estimate": "5-10 minutes",
"time_estimate": "5-10 分鐘",
"human_oversight": "minimal|moderate|required"
}},
"risk_factors": ["potential_risk1", "potential_risk2"],
"contingency_plans": ["backup_plan1", "backup_plan2"]
"risk_factors": ["潛在風險一(繁體中文)", "潛在風險二"],
"contingency_plans": ["備案一(繁體中文)", "備案二"]
}}
AUTONOMY LEVEL:
@@ -257,6 +263,8 @@ Based on the current business context and system state, determine the optimal st
5. Historical performance and learning outcomes
Provide your strategic decision in the specified JSON format.
重要:所有 JSON 文字欄位必須使用繁體中文(台灣用語)回覆,嚴禁英文或簡體中文。
"""
return prompt