auto-snapshot 2026-09-05 03:00:52

This commit is contained in:
小唯 A06 2026-09-05 03:00:54 +08:00
parent 38a5c1543f
commit 91ef7d9e4a
28 changed files with 1097 additions and 267 deletions

View File

@ -23,7 +23,7 @@ SOUL 完整版本见 `~/.hermes/SOUL.md` v3.4。
├── 织忆 (MemoryWeave) — 已部署 ├── 织忆 (MemoryWeave) — 已部署
│ ├── zhiyid (Go daemon, 端口 7821) │ ├── zhiyid (Go daemon, 端口 7821)
│ ├── zhiyi-consolidate (Rust IPC sidecar, /tmp/zhiyi-ipc.sock) │ ├── zhiyi-consolidate (Rust IPC sidecar, /tmp/zhiyi-ipc.sock)
│ ├── bge-embed (ONNX 嵌入服务, 端口 8000) │ ├── bge-embed (ONNX 嵌入服务, 端口 8001 = fallback; 8000 由 bge-proxy 代理接管 → 远端 ZSB GPU)
│ ├── Hermes 织忆插件 (plugins/memory/zhiyi, 7 工具) │ ├── Hermes 织忆插件 (plugins/memory/zhiyi, 7 工具)
│ └── 记忆图谱 (5766 节点 / 53081 边) │ └── 记忆图谱 (5766 节点 / 53081 边)
├── KOCR (金蝶 K3 凭证 OCR → v8.0.04 个可导入版本已发飞书) ├── KOCR (金蝶 K3 凭证 OCR → v8.0.04 个可导入版本已发飞书)
@ -79,7 +79,9 @@ cd ~/.hermes/hermes-agent && python3 -c "from plugins.memory.zhiyi import Hermes
### bge-embed 正常态4GB 显存笔记本2026-09-01 v2 实测) ### bge-embed 正常态4GB 显存笔记本2026-09-01 v2 实测)
- **当前设计**bge 跑 CPUllama 7B 跑 GPU共享 4GB 显存) > ⚡ **2026-09-05 升级**bge 嵌入推理外移 ZSB (192.168.5.104 GPU/DirectML)。本机 bge-embed **端口改 8001**fallback8000 由 bge-failover-proxy 代理接管。curl localhost:8000/health 仍有效(走代理→远端)。详见 `windows-home-server-ops` skill。
- **当前设计**bge 跑 CPU本机 8001 fallback+ 远端 ZSB GPUllama 7B 跑本机 GPU共享 4GB 显存)
- **为什么改 CPU**bge 调用频率低(织忆 recallCPU 推理够用,腾显存给 llama 让 7B 全 GPU - **为什么改 CPU**bge 调用频率低(织忆 recallCPU 推理够用,腾显存给 llama 让 7B 全 GPU
- **历史变化** - **历史变化**
- v1bge 跑 GPUCUDAllama 跑 CPU → llama 太慢9 t/s - v1bge 跑 GPUCUDAllama 跑 CPU → llama 太慢9 t/s

View File

@ -4,7 +4,7 @@ model:
api_key_env: DEEPSEEK_API_KEY api_key_env: DEEPSEEK_API_KEY
base_url: https://api.deepseek.com/v1 base_url: https://api.deepseek.com/v1
fallback_providers: fallback_providers:
- gmi-cloud - gmi-cloud
providers: providers:
agnes: agnes:
base_url: https://apihub.agnes-ai.com/v1 base_url: https://apihub.agnes-ai.com/v1
@ -12,8 +12,8 @@ providers:
default_model: agnes-2.0-flash default_model: agnes-2.0-flash
key_env: AGNES_API_KEY key_env: AGNES_API_KEY
models: models:
- agnes-2.0-flash - agnes-2.0-flash
- agnes-2.5-flash - agnes-2.5-flash
rate_limit: 1000 rate_limit: 1000
timeout: 60 timeout: 60
deepseek: deepseek:
@ -22,8 +22,8 @@ providers:
default_model: deepseek-v4-flash default_model: deepseek-v4-flash
key_env: DEEPSEEK_API_KEY key_env: DEEPSEEK_API_KEY
models: models:
- deepseek-v4-flash - deepseek-v4-flash
- deepseek-v4-pro - deepseek-v4-pro
rate_limit: 1000 rate_limit: 1000
timeout: 30 timeout: 30
mimo: mimo:
@ -32,8 +32,8 @@ providers:
default_model: mimo-v2.5-pro default_model: mimo-v2.5-pro
key_env: XIAOMI_API_KEY key_env: XIAOMI_API_KEY
models: models:
- mimo-v2.5-pro - mimo-v2.5-pro
- mimo-v2.5 - mimo-v2.5
rate_limit: 1000 rate_limit: 1000
timeout: 60 timeout: 60
newapi-local: newapi-local:
@ -42,10 +42,10 @@ providers:
cost_factor: 0.0 cost_factor: 0.0
default_model: nvidia/nemotron-3-super-120b-a12b default_model: nvidia/nemotron-3-super-120b-a12b
models: models:
- nvidia/nemotron-mini-4b-instruct - nvidia/nemotron-mini-4b-instruct
- openai/gpt-oss-120b - openai/gpt-oss-120b
- nvidia/nvidia-nemotron-nano-9b-v2 - nvidia/nvidia-nemotron-nano-9b-v2
- meta/llama-3.1-8b-instruct - meta/llama-3.1-8b-instruct
rate_limit: 1000 rate_limit: 1000
timeout: 30 timeout: 30
omniroute-local: omniroute-local:
@ -54,9 +54,9 @@ providers:
cost_factor: 0.0 cost_factor: 0.0
default_model: auto/chat default_model: auto/chat
models: models:
- auto/chat - auto/chat
- auto/best-free - auto/best-free
- auto/coding - auto/coding
rate_limit: 1000 rate_limit: 1000
timeout: 30 timeout: 30
opencode-free: opencode-free:
@ -65,12 +65,12 @@ providers:
cost_factor: 0.0 cost_factor: 0.0
default_model: x-preview-f-free default_model: x-preview-f-free
models: models:
- x-preview-f-free - x-preview-f-free
- hy3-free - hy3-free
- laguna-s-2.1-free - laguna-s-2.1-free
- nemotron-3-ultra-free - nemotron-3-ultra-free
- nemotron-3.5-lightning-free - nemotron-3.5-lightning-free
- muse-spark-1.2-contributor-free - muse-spark-1.2-contributor-free
rate_limit: 1000 rate_limit: 1000
timeout: 30 timeout: 30
sensenova: sensenova:
@ -79,18 +79,18 @@ providers:
cost_factor: 0.0 cost_factor: 0.0
default_model: deepseek-v4-flash default_model: deepseek-v4-flash
models: models:
- deepseek-v4-flash - deepseek-v4-flash
- glm-5.2 - glm-5.2
- sensenova-6.8-flash-lite - sensenova-6.8-flash-lite
rate_limit: 1000 rate_limit: 1000
timeout: 30 timeout: 30
zhipu: zhipu:
api_key: ${ZHIPU_API_KEY} api_key: ${ZHIPU_API_KEY}
base_url: https://open.bigmodel.cn/api/paas/v4 base_url: https://open.bigmodel.cn/api/paas/v4
models: models:
- glm-4-flash - glm-4-flash
- glm-4-air - glm-4-air
- glm-4-long - glm-4-long
type: openai type: openai
llama-local: llama-local:
api_key: local-key api_key: local-key
@ -98,8 +98,8 @@ providers:
cost_factor: 0.0 cost_factor: 0.0
default_model: qwen3b default_model: qwen3b
models: models:
- qwen3b - qwen3b
- qwen7b - qwen7b
rate_limit: 100 rate_limit: 100
timeout: 300 timeout: 300
context_length: 131072 context_length: 131072
@ -112,20 +112,19 @@ providers:
rate_limit: 100 rate_limit: 100
context_length: 1048576 context_length: 1048576
default_headers: default_headers:
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36
Gecko) Chrome/120.0 Safari/537.36
fallback_providers: fallback_providers:
- provider: agnes - provider: agnes
model: agnes-2.0-flash model: agnes-2.0-flash
- provider: zhipu - provider: zhipu
model: glm-4-flash model: glm-4-flash
- provider: sensenova - provider: sensenova
model: deepseek-v4-flash model: deepseek-v4-flash
- provider: opencode-free - provider: opencode-free
model: x-preview-f-free model: x-preview-f-free
toolsets: toolsets:
- hermes-cli - hermes-cli
- web - web
agent: agent:
max_turns: 90 max_turns: 90
gateway_timeout: 1800 gateway_timeout: 1800
@ -350,9 +349,9 @@ display:
ephemeral_system_ttl: 0 ephemeral_system_ttl: 0
runtime_footer: runtime_footer:
fields: fields:
- model - model
- context_pct - context_pct
- cwd - cwd
copy_shortcut: auto copy_shortcut: auto
tool_progress: all tool_progress: all
tool_progress_style: accumulate tool_progress_style: accumulate
@ -392,8 +391,7 @@ tts:
voice: en_US-lessac-medium voice: en_US-lessac-medium
providers: providers:
mimo: mimo:
command: bash /home/muc/.hermes/scripts/mimo_tts.sh {input_path} {output_path} command: bash /home/muc/.hermes/scripts/mimo_tts.sh {input_path} {output_path} {voice}
{voice}
output_format: opus output_format: opus
type: command type: command
voice: 冰糖 voice: 冰糖
@ -456,12 +454,12 @@ moa:
fanout: user_turn fanout: user_turn
reference_max_tokens: 800 reference_max_tokens: 800
reference_models: reference_models:
- model: openai/gpt-oss-120b - model: openai/gpt-oss-120b
provider: newapi-local provider: newapi-local
- model: nvidia/llama-3.3-nemotron-super-49b-v1 - model: nvidia/llama-3.3-nemotron-super-49b-v1
provider: newapi-local provider: newapi-local
- model: mistralai/mistral-large-3-675b-instruct-2512 - model: mistralai/mistral-large-3-675b-instruct-2512
provider: newapi-local provider: newapi-local
skills: skills:
external_dirs: [] external_dirs: []
template_vars: true template_vars: true
@ -513,49 +511,49 @@ approvals:
mcp_reload_confirm: true mcp_reload_confirm: true
destructive_slash_confirm: false destructive_slash_confirm: false
command_allowlist: command_allowlist:
- overwrite project env/config file - overwrite project env/config file
- delete in root path - delete in root path
- start gateway outside systemd (use 'systemctl --user restart hermes-gateway') - start gateway outside systemd (use 'systemctl --user restart hermes-gateway')
- command parser limit or malformed executable payload - command parser limit or malformed executable payload
- force kill processes (killall -KILL) - force kill processes (killall -KILL)
- sudo with privilege flag (stdin/askpass/shell/list) - sudo with privilege flag (stdin/askpass/shell/list)
- execute_code - execute_code
- in-place edit of Hermes config/env - in-place edit of Hermes config/env
- copy/move file into sensitive credential/SSH/shell-rc path - copy/move file into sensitive credential/SSH/shell-rc path
- overwrite project env/config via redirection - overwrite project env/config via redirection
- shell command via -c/-lc flag - shell command via -c/-lc flag
- stop/restart hermes gateway via shell-spliced verb (kills running agents) - stop/restart hermes gateway via shell-spliced verb (kills running agents)
- git force push short flag (rewrites remote history) - git force push short flag (rewrites remote history)
- in-place edit of system config - in-place edit of system config
- copy/move file into system config path - copy/move file into system config path
- disk copy - disk copy
- recursive delete - recursive delete
- world/other-writable permissions - world/other-writable permissions
- shell execution via heredoc - shell execution via heredoc
- kill process via pgrep expansion (self-termination) - kill process via pgrep expansion (self-termination)
- SQL DELETE without WHERE - SQL DELETE without WHERE
- stop/restart system service - stop/restart system service
- force kill processes - force kill processes
- git force push (rewrites remote history) - git force push (rewrites remote history)
- script execution via heredoc - script execution via heredoc
- overwrite system file via tee - overwrite system file via tee
- stop/restart hermes gateway (kills running agents) - stop/restart hermes gateway (kills running agents)
- hermes update (restarts gateway, kills running agents) - hermes update (restarts gateway, kills running agents)
- copy/move file into /etc/ - copy/move file into /etc/
- script execution via -e/-c flag - script execution via -e/-c flag
- SQL TRUNCATE - SQL TRUNCATE
- overwrite system file via redirection - overwrite system file via redirection
- kill hermes/gateway process (self-termination) - kill hermes/gateway process (self-termination)
- pipe remote content to shell - pipe remote content to shell
- find -delete - find -delete
- overwrite system config - overwrite system config
- sudo with combined-flag privilege escalation - sudo with combined-flag privilege escalation
plugins: plugins:
disabled: [] disabled: []
enabled: enabled:
- agentic-sms-gateway - agentic-sms-gateway
- cronalytics - cronalytics
- rtk-rewrite - rtk-rewrite
hooks_auto_accept: false hooks_auto_accept: false
security: security:
allow_private_urls: false allow_private_urls: false
@ -656,23 +654,15 @@ paste_collapse_char_threshold: 2000
_config_version: 39 _config_version: 39
known_plugin_toolsets: known_plugin_toolsets:
cli: cli:
- spotify - spotify
mcp_servers: mcp_servers:
codebase-memory-mcp: codebase-memory-mcp:
command: /home/muc/.local/bin/codebase-memory-mcp command: /home/muc/.local/bin/codebase-memory-mcp
enabled: true enabled: true
codegraph:
args:
- serve
- --mcp
command: codegraph
connect_timeout: 60
enabled: true
timeout: 120
dbx: dbx:
args: args:
- -y - -y
- '@dbx-app/mcp-server' - '@dbx-app/mcp-server'
command: npx command: npx
connect_timeout: 60 connect_timeout: 60
enabled: true enabled: true
@ -683,8 +673,8 @@ mcp_servers:
url: https://mcp.firecrawl.dev/v2/mcp url: https://mcp.firecrawl.dev/v2/mcp
github: github:
args: args:
- -y - -y
- '@modelcontextprotocol/server-github' - '@modelcontextprotocol/server-github'
command: npx command: npx
enabled: true enabled: true
env: env:
@ -692,30 +682,30 @@ mcp_servers:
timeout: 60 timeout: 60
openclaw: openclaw:
args: args:
- mcp - mcp
- serve - serve
command: openclaw command: /home/muc/nodejs/node-v24.16.0-linux-x64/bin/openclaw
connect_timeout: 60 connect_timeout: 60
timeout: 300 timeout: 300
platform_toolsets: platform_toolsets:
cli: cli:
- browser - browser
- clarify - clarify
- code_execution - code_execution
- computer_use - computer_use
- cronjob - cronjob
- delegation - delegation
- file - file
- image_gen - image_gen
- memory - memory
- session_search - session_search
- skills - skills
- terminal - terminal
- todo - todo
- tts - tts
- vision - vision
- web - web
- mcp-codegraph - mcp-codebase-memory-mcp
firecrawl: firecrawl:
connect_timeout: 30 connect_timeout: 30
timeout: 120 timeout: 120
@ -740,25 +730,3 @@ weixin:
group_policy: open group_policy: open
token: bef3c3d39904@im.bot:060000740c190c21262c8edf68365d5d112e0d token: bef3c3d39904@im.bot:060000740c190c21262c8edf68365d5d112e0d
user_id: o9cq800DqpnMoqzXmJ4zozKiM8OI@im.wechat user_id: o9cq800DqpnMoqzXmJ4zozKiM8OI@im.wechat
# ── Fallback Model ────────────────────────────────────────────────────
# Automatic provider failover when primary is unavailable.
# Uncomment and configure to enable. Triggers on rate limits (429),
# overload (529), service errors (503), or connection failures.
#
# Supported providers:
# openrouter (OPENROUTER_API_KEY) — routes to any model
# openai-codex (OAuth — hermes auth) — OpenAI Codex
# nous (OAuth — hermes auth) — Nous Portal
# zai (ZAI_API_KEY) — Z.AI / GLM
# kimi-coding (KIMI_API_KEY) — Kimi / Moonshot
# kimi-coding-cn (KIMI_CN_API_KEY) — Kimi / Moonshot (China)
# minimax (MINIMAX_API_KEY) — MiniMax
# minimax-cn (MINIMAX_CN_API_KEY) — MiniMax (China)
# bedrock (AWS IAM / boto3) — AWS Bedrock (Converse API)
#
# For custom OpenAI-compatible endpoints, add base_url and key_env.
#
# fallback_model:
# provider: openrouter
# model: anthropic/claude-sonnet-4

112
scripts/backup-cleanup.py Normal file
View File

@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""
backup-cleanup.py 自动清理过期备份2026-09-05 牧尘要求过期/无用的备份自动删除
背景9/4 磁盘 86% 的根因 = 备份/快照/损坏留档只增不删
memories.lance.broken.* 83G损坏重建后旧目录从不删
~/.hermes/backups/ 74 pre-watchdog 快照旧看门狗无轮转
~/.hermes/ 根目录散落 14 state.db.* 手工备份修完不清理
hermes-backup-20260717 16G手动备份无保留期
策略统一轮转防再堆积
- ~/.hermes/state.db.* 散落备份 只留最近 3 其他超期删
- ~/.hermes/backups/state.db.pre-watchdog-* 只留最近 3
- ~/.hermes/backups/state-db-snap*/state-*.db 只留最近 8 snapshot-state-db.sh 已做兜底
- ~/.hermes/backups/dual-backup/*.bundle 只留最近 2 每日 bundle
- ~/.hermes/backups/dual-backup/zhiyi-*.tar.gz 只留最近 2
- /var/lib/memoryweave/memories.lance.broken* 删除broken 标记 = 已废弃重建后无保留价值
- /var/lib/memoryweave/*.bak-* 只留最近 2
磁盘 >85% 时额外~/.hermes/cache 清缓存/tmp 1 天前
安全铁律
- 绝不删活动文件state.db / graph.db / *.lance 活动库 *.broken.* / *.bak-* 模式
- 绝不跟随软链-type f -maxdepth 限制
- 只删明确命名的备份模式 glob 宽泛删
"""
import os, re, shutil, sys
from pathlib import Path
HOME = Path.home()
HERMES = HOME / ".hermes"
BACKUPS = HERMES / "backups"
def keep_latest(paths: list, keep: int, dry: bool = True):
"""按 mtime 保留最近 keep 份,其余删除。返回 (deleted, freed_hint)"""
paths = sorted(paths, key=lambda p: p.stat().st_mtime, reverse=True)
removed = 0
for p in paths[keep:]:
try:
if p.is_dir():
if dry:
print(f" [dry] rm -rf {p}")
else:
shutil.rmtree(p, ignore_errors=True)
else:
if dry:
print(f" [dry] rm {p} ({p.stat().st_size//1024//1024}M)")
else:
p.unlink(missing_ok=True)
removed += 1
except OSError as e:
print(f" ⚠️ 删除失败 {p}: {e}")
return removed
def cleanup(dry: bool = True):
print(f"{'[DRY-RUN] ' if dry else ''}backup-cleanup 开始")
total = 0
# 1. ~/.hermes/ 根目录散落 state.db.* 备份(保留最近 3
stray = [p for p in HERMES.glob("state.db.*") if p.is_file()]
total += keep_latest(stray, 3, dry)
# 2. backups/state.db.pre-watchdog-*(保留 3
pre = list(BACKUPS.glob("state.db.pre-watchdog-*"))
total += keep_latest(pre, 3, dry)
# 3. backups/state-db-snap*/(保留 8
for snapdir in BACKUPS.glob("state-db-snap*"):
if snapdir.is_dir():
total += keep_latest(list(snapdir.glob("state-*.db")), 8, dry)
# 4. backups/dual-backup/bundle 留 2tar.gz 留 2
ddir = BACKUPS / "dual-backup"
if ddir.exists():
total += keep_latest(list(ddir.glob("*.bundle")), 2, dry)
total += keep_latest(list(ddir.glob("*.tar.gz")), 2, dry)
# 5. /var/lib/memoryweave/ broken 残留(直接删,废弃标记)
mw = Path("/var/lib/memoryweave")
if mw.exists():
for p in mw.glob("memories.lance.broken*"):
if dry:
print(f" [dry] rm -rf {p}")
total += 1
else:
shutil.rmtree(p, ignore_errors=True)
total += 1
# *.bak-* 保留 2
total += keep_latest([p for p in mw.glob("*.bak-*") if p.is_dir()], 2, dry)
# 6. 磁盘 >85% 额外清理
st = shutil.disk_usage(str(HOME))
pct = st.used / st.total * 100
if pct > 85:
print(f" ⚠️ 磁盘 {pct:.0f}%>85%,额外清理:")
# 清 cache 子目录1 天前)
for c in (HERMES / "cache").glob("*"):
if c.is_dir():
for f in c.glob("*"):
if f.is_file() and (time_now - f.stat().st_mtime) > 86400:
if dry: print(f" [dry] rm cache {f}")
else: f.unlink(missing_ok=True)
print(f"完成,处理 {total}")
return total
if __name__ == "__main__":
import time
global time_now
time_now = time.time()
dry = "--apply" not in sys.argv
cleanup(dry=dry)
if dry:
print("dry-run加 --apply 实际执行)")

View File

@ -0,0 +1,142 @@
#!/usr/bin/env python3
"""
bge 嵌入服务故障切换代理 (2026-09-05)
======================================
zhiyid 只认 localhost:8000 本代理常驻 8000
* 远端优先: 192.168.5.104:8000 (ZSB GPU / DML bge-m3)
* 远端故障 自动切本地 127.0.0.1:8001 (fallback bge)
* 远端恢复 自动切回每次请求前探测 /health
启动: python3 bge-failover-proxy.py
端口: 8000 (env PROXY_PORT)
远端: 192.168.5.104:8000 (env REMOTE_EMBED_URL)
本地: 127.0.0.1:8001 (env LOCAL_EMBED_URL)
"""
import json
import logging
import os
import socket
import threading
import time
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib import request as urlrequest
from urllib.error import URLError
PROXY_PORT = int(os.environ.get("PROXY_PORT", "8000"))
REMOTE_EMBED_URL = os.environ.get("REMOTE_EMBED_URL", "http://192.168.5.104:8000")
LOCAL_EMBED_URL = os.environ.get("LOCAL_EMBED_URL", "http://127.0.0.1:8001")
HEALTH_PATH = "/health"
EMBED_PATH = "/v1/embeddings"
HEALTH_TIMEOUT = float(os.environ.get("HEALTH_TIMEOUT", "1.5"))
REQUEST_TIMEOUT = float(os.environ.get("REQUEST_TIMEOUT", "60"))
logging.basicConfig(
level=logging.INFO,
format="[bge-proxy] %(asctime)s %(message)s",
datefmt="%H:%M:%S",
)
log = logging.getLogger("bge-proxy")
_state_lock = threading.Lock()
_state = {"remote_ok": None, "last_check": 0.0, "using": None}
def _probe_remote() -> bool:
"""探测远端 /health1.5s 超时。"""
try:
req = urlrequest.Request(REMOTE_EMBED_URL + HEALTH_PATH)
with urlrequest.urlopen(req, timeout=HEALTH_TIMEOUT) as r:
if r.status != 200:
return False
body = r.read(200).decode("utf-8", "ignore")
return "ok" in body
except Exception:
return False
def choose_target() -> tuple[str, bool]:
"""返回 (目标URL, 是否远端)。带 3s 冷却 + 状态缓存。"""
global _state
now = time.time()
with _state_lock:
if _state["remote_ok"] is not None and now - _state["last_check"] < 3.0:
remote_ok = _state["remote_ok"]
else:
remote_ok = _probe_remote()
_state["remote_ok"] = remote_ok
_state["last_check"] = now
target = REMOTE_EMBED_URL if remote_ok else LOCAL_EMBED_URL
using = "remote(104-GPU)" if remote_ok else "local(106-CPU)"
if using != _state["using"]:
log.info("切换 → %s", using)
_state["using"] = using
return target, remote_ok
def _forward(url: str, method: str, body: bytes, headers: dict) -> tuple[int, dict, bytes]:
"""把请求转发到目标,返回 (状态码, 响应头, 响应体)。"""
req = urlrequest.Request(url, data=body if method == "POST" else None, method=method)
for k, v in headers.items():
if k.lower() not in ("host", "content-length", "connection", "accept-encoding"):
req.add_header(k, v)
try:
with urlrequest.urlopen(req, timeout=REQUEST_TIMEOUT) as r:
resp_body = r.read()
resp_headers = dict(r.headers.items())
return r.status, resp_headers, resp_body
except URLError as e:
return 502, {"Content-Type": "application/json"}, json.dumps(
{"error": f"proxy forward failed: {e}"}
).encode()
class Handler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def log_message(self, fmt, *args):
log.info("%s %s" % (self.address_string(), fmt % args))
def _handle(self):
length = int(self.headers.get("Content-Length", 0) or 0)
body = self.rfile.read(length) if length else b""
target, _remote_ok = choose_target()
status, resp_headers, resp_body = _forward(
target + self.path, self.command, body, dict(self.headers)
)
self.send_response(status)
# 只转发安全响应头Content-Length 必须用实际长度
for k, v in resp_headers.items():
if k.lower() in ("content-type",):
self.send_header(k, v)
self.send_header("Content-Length", str(len(resp_body)))
self.send_header("X-BGE-Proxy", "remote-104" if target.startswith(REMOTE_EMBED_URL) else "local-106")
self.end_headers()
if self.command != "HEAD":
self.wfile.write(resp_body)
do_GET = _handle
do_POST = _handle
do_PUT = _handle
do_DELETE = _handle
def _startup_probe():
"""启动时打一条日志说明当前选路。"""
remote_ok = _probe_remote()
with _state_lock:
_state["remote_ok"] = remote_ok
_state["last_check"] = time.time()
_state["using"] = "remote(104-GPU)" if remote_ok else "local(106-CPU)"
log.info("启动选路: %s (remote=%s)", _state["using"], remote_ok)
if __name__ == "__main__":
log.info("bge 故障切换代理启动 0.0.0.0:%s", PROXY_PORT)
log.info(" 远端: %s", REMOTE_EMBED_URL)
log.info(" 本地 fallback: %s", LOCAL_EMBED_URL)
_startup_probe()
server = HTTPServer(("0.0.0.0", PROXY_PORT), Handler)
try:
server.serve_forever()
except KeyboardInterrupt:
log.info("停止")

View File

@ -33,30 +33,50 @@ log() {
# 检查服务器挂载 # 检查服务器挂载
# ⚠️ 2026-08-12 修复:家庭服务器不在同一局域网是常态(走 frp 域名), # ⚠️ 2026-08-12 修复:家庭服务器不在同一局域网是常态(走 frp 域名),
# 离线静默跳过exit 0不当作错误上报。只有"挂载点在但同步失败"才报错。 # 离线静默跳过exit 0不当作错误上报。只有"挂载点在但同步失败"才报错。
# ⚠️ 2026-09-04 牧尘指示铁律SMB 备份只在家庭局域网做192.168.123.11)。
# StarVPN(192.168.188.11) 只用于 Gitea 推送,绝不用来做 SMB 备份——
# 外网走 StarVPN 传 1.9GB bundle 网络抖动会僵死9/4 卡 78 分钟教训)。
# 所以:外网状态一律跳过备份;只有在局域网才挂载 + 同步。
check_mount() { check_mount() {
if ! mountpoint -q /mnt/server-backup; then # 判断真实 cifs 挂载(/proc/mounts 为准mountpoint -q 对 autofs 恒真不可用)
log "⚠️ 服务器未挂载,尝试连接..." if grep -q '//192\.168\.123\.11/.* /mnt/server-backup ' /proc/mounts; then
# 2026-09-03mount cifs 在服务器 TCP 不可达时会长时间挂起19:00 cron 曾超时 3600s return 0 # 局域网 cifs 已挂,直接可用
# → 先快速探测可达性ping 1 次 2s 超时),不可达直接走"离线常态"跳过,避免 mount 挂起
if ! ping -c 1 -W 2 192.168.188.11 >/dev/null 2>&1; then
log " 服务器不在局域网(常态),跳过本次备份"
return 2
fi
# -n: non-interactivecron 环境无密码输入机会,立即失败不卡住
# || true: 失败是预期路径(服务器不在线),由下方 mountpoint 判断处理,避免 set -e 提前退出
sudo -n mount -t cifs //192.168.188.11/beifen /mnt/server-backup \
-o username=administrator,password=xue.2538,uid=1000,gid=1000,iocharset=utf8,file_mode=0755,dir_mode=0755,timeout=15 2>/dev/null || true
sleep 2
if ! mountpoint -q /mnt/server-backup; then
log " 服务器不在局域网(常态),跳过本次备份"
return 2
fi
fi fi
return 0 # 挂载为外网残留(StarVPN) → 外网不做备份
if grep -q '//192\.168\.188\.11/.* /mnt/server-backup ' /proc/mounts; then
log " 挂载为外网残留(StarVPN),外网不做备份(牧尘 09-04 指示),跳过本次备份"
return 2
fi
# 未挂载:只探测局域网 IP。StarVPN 通也不备份。
log "⚠️ 服务器未挂载,尝试连接(仅局域网)..."
if ! timeout 3 bash -c 'echo > /dev/tcp/192.168.123.11/445' 2>/dev/null; then
log " 不在家庭局域网(外网常态),跳过本次备份"
return 2
fi
# 局域网可达:访问挂载点触发 systemd automountfstab x-systemd.automount
ls /mnt/server-backup >/dev/null 2>&1 || true
sleep 3
if grep -q '//192\.168\.123\.11/.* /mnt/server-backup ' /proc/mounts; then
return 0
fi
# automount 未触发/未配置 → 尝试 sudo mount有 NOPASSWD 才生效,失败走跳过)
sudo -n mount /mnt/server-backup 2>/dev/null || true
sleep 2
if grep -q '//192\.168\.123\.11/.* /mnt/server-backup ' /proc/mounts; then
return 0
fi
log " 局域网可达但挂载失败,跳过本次备份"
return 2
} }
# === 推送到服务器 === # === 推送到服务器 ===
push_backup() { push_backup() {
# 2026-09-04 并发锁cron 每 6h 与手动/重试轮可能重叠,两轮同时写 SMB 互相卡死
exec 9>"$HERMES/watchdog/dual-backup.lock"
if ! flock -n 9; then
log "⚠️ 已有备份在运行,跳过本轮(防并发)"
return 0
fi
log "📤 开始双备份推送..." log "📤 开始双备份推送..."
# 1. 本机 git 快照(无论服务器是否可达都做——本机备份是底线) # 1. 本机 git 快照(无论服务器是否可达都做——本机备份是底线)
@ -108,7 +128,29 @@ push_backup() {
log " 同步 skills..." log " 同步 skills..."
if [ -d "$HERMES/.git" ]; then if [ -d "$HERMES/.git" ]; then
cd "$HERMES" cd "$HERMES"
git bundle create "$BACKUP_SNAPSHOTS/hermes-git-$(date '+%Y%m%d').bundle" --all 2>/dev/null || true # 2026-09-04 根治bundle 先在本地打(快、可靠),再 rsync 单文件到服务器
# ——之前直写 SMB 会把 git 拖进不可中断 IOtimeout 杀不掉18:01 卡 50min、20:15 残留 78min
# 2026-09-04 修复二:同一天只打一次 bundlecron 每 6h 一次,文件按天命名会被反复覆盖重传 1.88GB
BUNDLE_DAY="$(date '+%Y%m%d')"
BUNDLE_LOCAL="$HERMES/backups/dual-backup/hermes-git-$BUNDLE_DAY.bundle"
mkdir -p "$(dirname "$BUNDLE_LOCAL")"
if [ ! -f "$BUNDLE_LOCAL" ] || [ "$(stat -c %Y "$BUNDLE_LOCAL")" -lt "$(date -d 'today 00:00' +%s)" ]; then
# 本地打包(写本地盘,不受 SMB 影响git 读本地 .git 1.9G,通常 1-3 分钟)
log " git bundle 本地打包中($BUNDLE_LOCAL..."
if timeout 300 git bundle create "$BUNDLE_LOCAL" --all 2>/dev/null; then
log " git bundle 本地完成"
else
log " ⚠️ git bundle 本地创建失败(跳过,下次再试)"
fi
else
log " 今日 bundle 已存在,跳过本地打包"
fi
# rsync 单文件到服务器(网络差时 rsync 可被 timeout 正常杀掉,不拖死脚本;--partial 支持续传)
if [ -f "$BUNDLE_LOCAL" ]; then
timeout 300 rsync -a --partial "$BUNDLE_LOCAL" "$BACKUP_SNAPSHOTS/" 2>/dev/null \
&& log " 📦 git bundle 已同步" \
|| log " ⚠️ git bundle rsync 超时/失败(本地保留 $BUNDLE_LOCAL,下次续传)"
fi
fi fi
# 9. 同步 cron 配置 # 9. 同步 cron 配置
@ -120,19 +162,26 @@ push_backup() {
log " ⚠️ 无cron配置可备份" log " ⚠️ 无cron配置可备份"
fi fi
# 10. 同步织忆数据LanceDB + SQLite图谱 + 真实数据) # 10. 同步织忆数据SQLite 图谱真数据 53M
# 2026-09-04 根治:原 10a 的 LanceDB tar 步骤移除——
# ① exclude='*.lance' 把真库(memories/episodes.lance 35G)排除了,反而打进 98G broken/bak 垃圾
# (不以 .lance 结尾不匹配)→ 每次 tar ~95G 垃圾直写 SMBtimeout 600 必失败 → 卡死 cron18:52
# ② zhiyid(213252) 在线运行中,直接 tar 运行中 LanceDB 目录会产出损坏快照2026-08-21 教训)
# ③ 35G 真库 tar+rsync 过 StarVPN 远超 cron 60min 预算 → LanceDB 全量备份需独立低频任务
# TODO: 等 zhiyid 提供 snapshot/backup API 或独立 cron rsync LanceDB 目录(排除 broken/bak
log " 同步织忆数据..." log " 同步织忆数据..."
# 10a. LanceDB 数据 # SQLite 图谱真数据53M~/.hermes/graph.db绕过软链接直接从 ~/.hermes/ 取)
if [ -d "/var/lib/memoryweave" ]; then
tar czf "$BACKUP_SNAPSHOTS/zhiyi-lancedb-$(date '+%Y%m%d').tar.gz" \
-C /var/lib/memoryweave --exclude='*.lance' --exclude='lancedb.bak' \
. 2>/dev/null || log " ⚠️ 织忆LanceDB同步失败"
fi
# 10b. SQLite 真实数据(绕过软链接,直接从 ~/.hermes/ 取)
if [ -f "$HERMES/graph.db" ] || [ -f "$HERMES/memoryweave.db" ]; then if [ -f "$HERMES/graph.db" ] || [ -f "$HERMES/memoryweave.db" ]; then
tar czf "$BACKUP_SNAPSHOTS/zhiyi-sqlite-$(date '+%Y%m%d').tar.gz" \ mkdir -p "$HERMES/backups/dual-backup"
-C "$HERMES" graph.db memoryweave.db 2>/dev/null || log " ⚠️ 织忆SQLite同步失败" SQL_LOCAL="$HERMES/backups/dual-backup/zhiyi-sqlite-$(date '+%Y%m%d').tar.gz"
log " 📦 织忆SQLite已备份" timeout 120 tar czf "$SQL_LOCAL" -C "$HERMES" graph.db memoryweave.db 2>/dev/null \
&& log " 📦 织忆SQLite已打包" \
|| log " ⚠️ 织忆SQLite打包失败"
if [ -f "$SQL_LOCAL" ]; then
timeout 120 rsync -a --partial "$SQL_LOCAL" "$BACKUP_SNAPSHOTS/" 2>/dev/null \
&& log " 📦 织忆SQLite已同步" \
|| log " ⚠️ 织忆SQLite rsync 失败"
fi
fi fi
# 11. 定期快照(每天保留一份关键配置快照) # 11. 定期快照(每天保留一份关键配置快照)

34
scripts/fix-fstab-lan.sh Normal file
View File

@ -0,0 +1,34 @@
#!/bin/bash
# fix-fstab-lan.sh — 2026-09-04 牧尘指示SMB 备份只在家庭局域网(192.168.123.11)
# 根因fstab 把 automount 写死为 StarVPN(192.168.188.11),导致外网也挂 SMB 传大文件 → 弱网僵死
# 修复fstab 挂载 IP 改回局域网 192.168.123.11(保留 x-systemd.automount
# 外网时 dual-backup.sh 先探测 123.11:445 不通即跳过,不触发挂载;
# 在家时探测通过 → 访问挂载点触发 automount 挂局域网共享)
set -e
echo "=== 修改前 ==="
grep server-backup /etc/fstab || true
# 先卸载当前 StarVPN 残留挂载(若在且指向 188.11
if mountpoint -q /mnt/server-backup 2>/dev/null; then
if grep -q '//192\.168\.188\.11/' /proc/mounts; then
echo "卸载 StarVPN 残留挂载..."
umount -l /mnt/server-backup 2>/dev/null || umount /mnt/server-backup 2>/dev/null || true
sleep 1
fi
fi
# 备份 fstab
cp /etc/fstab /etc/fstab.bak.20260904-lan
# 只替换 IP188.11(StarVPN) → 123.11(局域网)
sed -i 's|//192\.168\.188\.11/beifen /mnt/server-backup|//192.168.123.11/beifen /mnt/server-backup|' /etc/fstab
echo "=== 修改后 ==="
grep server-backup /etc/fstab || true
# 重新加载 systemd 挂载/自动挂载单元
systemctl daemon-reload 2>/dev/null || true
echo "=== 完成fstab 已指向局域网 192.168.123.11 ==="
echo "=== 验证外网状态123.11:445 不通dual-backup 应秒级跳过 ==="

View File

@ -30,18 +30,20 @@ if [ -z "$LLAMA_HEALTH" ]; then
fi fi
fi fi
# 2. bge-embed必须响应CPU/GPU 都 OK # 2. bge 嵌入服务8000 代理入口 → 远端 ZSB GPU / 本地 8001 fallback
# 2026-09-05 架构8000 = bge-proxy转发 104 GPU挂时切本地 8001 bge-embed
BGE_HEALTH=$(curl -s -m 5 http://localhost:8000/health 2>/dev/null || true) BGE_HEALTH=$(curl -s -m 5 http://localhost:8000/health 2>/dev/null || true)
if [ -z "$BGE_HEALTH" ]; then if [ -z "$BGE_HEALTH" ]; then
# /health 无响应 → 端口未监听 → 真异常 # /health 无响应 → 先重启代理8000 主入口),再重启本机 bge8001 fallback
ALERT="$ALERT\n❌ bge-embed 服务异常(/health 无响应,:8000 未监听)" ALERT="$ALERT\n❌ bge 嵌入服务异常(/health 无响应,代理 8000 + 本机 8001 均不可用)"
systemctl --user restart bge-proxy 2>/dev/null || true
systemctl --user restart bge-embed 2>/dev/null || true systemctl --user restart bge-embed 2>/dev/null || true
sleep 5 sleep 8
BGE_HEALTH2=$(curl -s -m 5 http://localhost:8000/health 2>/dev/null || true) BGE_HEALTH2=$(curl -s -m 5 http://localhost:8000/health 2>/dev/null || true)
if [ -z "$BGE_HEALTH2" ]; then if [ -z "$BGE_HEALTH2" ]; then
ALERT="$ALERT\n❌ bge-embed 重启后仍未恢复" ALERT="$ALERT\n❌ bge 嵌入服务重启后仍未恢复(查 journalctl --user -u bge-proxy / bge-embed"
else else
ALERT="$ALERT\n✅ bge-embed 已自动重启恢复" ALERT="$ALERT\n✅ bge 嵌入服务已自动重启恢复"
fi fi
fi fi

View File

@ -0,0 +1,2 @@
ls -la /home/muc/mc/会计工具/input/IMG_20260621_191011.jpg
file /home/muc/mc/会计工具/input/IMG_20260621_191011.jpg

View File

@ -0,0 +1,5 @@
#!/bin/bash
which tesseract 2>/dev/null && echo "tesseract found: $(tesseract --version 2>&1 | head -1)" || echo "tesseract not found"
pip list 2>/dev/null | grep -i -E "pytesseract|easyocr|paddleocr" || echo "No python OCR packages found"
which convert 2>/dev/null && echo "ImageMagick found" || echo "ImageMagick not found"
file ~/mc/会计工具/input/IMG_20260621_191011.jpg

View File

@ -0,0 +1,7 @@
import subprocess
result = subprocess.run(['which', 'python3'], capture_output=True, text=True)
print(f"python3: {result.stdout}")
result = subprocess.run(['python3', '-c', 'import pytesseract; print(pytesseract.__version__)'], capture_output=True, text=True)
print(f"pytesseract: {result.stdout.strip() or result.stderr.strip()}")
result = subprocess.run(['python3', '-c', 'import PIL; print(PIL.__version__)'], capture_output=True, text=True)
print(f"PIL: {result.stdout.strip() or result.stderr.strip()}")

View File

@ -0,0 +1 @@
bash /home/muc/check_ocr.sh

View File

@ -185,13 +185,19 @@ check_cbm() {
import sys, json import sys, json
try: try:
d = json.load(sys.stdin) d = json.load(sys.stdin)
for p in d.get('projects',[]): projs = d.get('projects', [])
print(p['name'] + ':' + str(p['nodes']) + ':' + str(p['edges'])) if not projs:
print('EMPTY') # 进程活着但 0 项目 = 索引丢失,不是查询失败
else:
for p in projs:
print(p['name'] + ':' + str(p['nodes']) + ':' + str(p['edges']))
except: print('FAIL') except: print('FAIL')
" 2>/dev/null) " 2>/dev/null)
if [ -z "$projects" ] || [ "$projects" = "FAIL" ]; then if [ "$projects" = "FAIL" ] || [ -z "$projects" ]; then
issues+=("CBM 查询失败"); ok=false issues+=("CBM 查询失败"); ok=false
elif [ "$projects" = "EMPTY" ]; then
issues+=("CBM 索引为空(0项目,需重建)"); ok=false
fi fi
if [ "$ok" = true ]; then if [ "$ok" = true ]; then

View File

@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""
resource-watchdog.py 文件资源管理看门狗2026-09-05 牧尘要求避免文件乱放
管理规则科学分类遵循已有约定不重复造轮子
脚本统一 ~/.hermes/scripts/140+ 脚本库不新建 scripts 目录
知识/文档 ~/mc/ 知识库Obsidian可检索进织忆图谱
应用目录不动 ComfyUI/models/ocr_v6_env/projects/mc/Downloads/Pictures/Videos
根目录(~)只允许 dotfile 配置 + 目录不允许散落文件
看门狗动作
1. 扫描 ~ 根目录非 dotfile 文件
2. 按扩展名分类报告类mc/06-Raw临时提示未知列出待处理
3. 磁盘>85% 或日志/备份异常附带提醒
用法: python3 resource-watchdog.py [--dry-run|--apply]
输出: 默认静默watchdog 语义有散落文件才报警
"""
import os, sys, shutil
from pathlib import Path
HOME = Path.home()
RAW_DIR = HOME / "mc/小唯/06-Raw"
# 文件类型 → 目标子目录(在 06-Raw 下按年月分)
EXT_MAP = {
# 报告/文档 → 06-Raw 按年份
".md": "reports", ".json": "reports", ".txt": "reports", ".log": "logs",
".pdf": "reports", ".docx": "reports", ".xlsx": "reports", ".csv": "reports",
# 图片(根目录不该有,应已在 Pictures/media
".png": "images", ".jpg": "images", ".jpeg": "images", ".webp": "images", ".gif": "images",
# 视频
".mp4": "videos", ".mkv": "videos", ".mov": "videos", ".avi": "videos",
# 压缩包 → 提示人工(通常是待处理下载)
".zip": "archives", ".tar": "archives", ".gz": "archives", ".7z": "archives", ".rar": "archives",
}
# 明确忽略的(根目录正常存在的非 dotfile
IGNORE = {"Desktop", "Documents", "Downloads", "Pictures", "Videos"}
def main():
apply = "--apply" in sys.argv
mode = "APPLY" if apply else "DRY-RUN"
stray = []
for f in HOME.iterdir():
if not f.is_file():
continue
if f.name.startswith("."):
continue
if f.name in IGNORE:
continue
stray.append(f)
if not stray:
# 干净watchdog 静默
return 0
print(f"[{mode}] ⚠️ 根目录发现 {len(stray)} 个散落文件:")
moved = 0
for f in sorted(stray):
ext = f.suffix.lower()
kind = EXT_MAP.get(ext, "unknown")
if kind == "unknown":
print(f"{f.name} ({ext or '无扩展名'}, {f.stat().st_size//1024}K) — 需人工判断")
continue
# 目标: 06-Raw/YYYY/kind/(保文件名)
year = "2026" # 简化,实际可按 mtime
dest_dir = RAW_DIR / year / kind
if apply:
dest_dir.mkdir(parents=True, exist_ok=True)
dest = dest_dir / f.name
if not dest.exists():
shutil.move(str(f), str(dest))
print(f" 📦 {f.name} → mc/小唯/06-Raw/{year}/{kind}/")
moved += 1
else:
# 同名冲突 → 加时间戳
ts = f.name.rsplit(".", 1)
dest = dest_dir / f"{ts[0]}-dup.{ts[1]}" if len(ts) > 1 else dest_dir / f"{f.name}-dup"
shutil.move(str(f), str(dest))
print(f" 📦 {f.name}{dest.name}(同名避免)")
moved += 1
else:
print(f" [dry] {f.name}{kind}/")
if not apply:
print("dry-run加 --apply 实际归档)")
return 0 if (moved or not apply) else 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -30,6 +30,7 @@ ZHIYI_BASE = os.environ.get("ZHIYI_BASE", "http://localhost:7821")
ZHIYI_KEY = os.environ.get("ZHIYI_KEY", "zhiyi-dev-key-2026") ZHIYI_KEY = os.environ.get("ZHIYI_KEY", "zhiyi-dev-key-2026")
STATE_FILE = os.path.expanduser("~/.hermes/data/rsshub_ai_seen.json") STATE_FILE = os.path.expanduser("~/.hermes/data/rsshub_ai_seen.json")
MAX_SEEN = 500 # state 里最多保留的已见链接数 MAX_SEEN = 500 # state 里最多保留的已见链接数
SOURCE_DELAY = int(os.environ.get("RSSHUB_SOURCE_DELAY", "15")) # 源间延时秒数
# 订阅源列表(路由已验证可用的多维度 AI 搜索) # 订阅源列表(路由已验证可用的多维度 AI 搜索)
SOURCES = [ SOURCES = [
@ -120,7 +121,10 @@ def main():
new_items = [] new_items = []
errors = [] errors = []
for path, label in SOURCES: # 2026-09-04 修复GitHub Search API secondary rate limit403→RSSHub 503
# 6 源串行连发(间隔<1s会触发限流冷却 30-60sRSSHub 内部 retry 覆盖不了。
# 源间加延时(默认 15s无论该源成败都等避免突发连发。
for idx, (path, label) in enumerate(SOURCES):
try: try:
xml_text = fetch_rss(path) xml_text = fetch_rss(path)
items = parse_items(xml_text) items = parse_items(xml_text)
@ -134,8 +138,12 @@ def main():
continue continue
seen.add(it["link"]) seen.add(it["link"])
new_items.append((label, it)) new_items.append((label, it))
if idx < len(SOURCES) - 1:
time.sleep(SOURCE_DELAY)
save_seen(seen) # 2026-09-04dry-run 只预览不落盘,避免把真新条目标 seen 导致漏抓
if not dry:
save_seen(seen)
# 写织忆 # 写织忆
written = 0 written = 0

View File

@ -28,17 +28,17 @@ SCRIPTS = HOME / ".hermes" / "scripts"
# 检查日: "daily"=工作日每天, "fri"=仅周五, "mon"=仅周一 # 检查日: "daily"=工作日每天, "fri"=仅周五, "mon"=仅周一
STOCK_CRONS = { STOCK_CRONS = {
"7a47cf882b8e": ("08:30 数据刷新", "mon", "08:30"), "7a47cf882b8e": ("08:30 数据刷新", "mon", "08:30"),
"f3619a71aebb": ("08:00 股票新闻", "daily", "08:00"), "c03c58e1e0aa": ("08:00 股票新闻", "daily", "08:00"),
"c293eead6688": ("09:00 组合扫描+路由", "daily", "09:00"), "4855debe954a": ("09:00 组合扫描+路由", "daily", "09:00"),
"78a94f88a88b": ("09:30-14:30 盘中金叉×11", "daily", "14:30"), "78a94f88a88b": ("09:30-14:30 盘中金叉×11", "daily", "14:30"),
"c48bbbb4fd18": ("16:00 五粮液信号", "daily", "16:00"), "67f13ef40ecb": ("16:00 五粮液信号", "daily", "16:00"),
"5a78557f000b": ("17:00 回测汇总", "fri", "17:00"), "5206e141cd67": ("17:00 回测汇总", "fri", "17:00"),
"2ebe9e7baa40": ("17:20 行业扫描", "fri", "17:20"), "2ebe9e7baa40": ("17:20 行业扫描", "fri", "17:20"),
"d4268c2cfe85": ("17:30 因子快照", "fri", "17:30"), "d4268c2cfe85": ("17:30 因子快照", "fri", "17:30"),
"2097eca598b2": ("18:00 多账户日报", "daily", "18:00"), "2097eca598b2": ("18:00 多账户日报", "daily", "18:00"),
"41b5be27c715": ("18:30 自动复盘", "daily", "18:30"), "41b5be27c715": ("18:30 自动复盘", "daily", "18:30"),
"1935e83a681a": ("09:00 矛盾周报", "mon", "09:00"), "1935e83a681a": ("09:00 矛盾周报", "mon", "09:00"),
"6061a782b772": ("10:00 投研周学习", "mon", "10:00"), "50cff6b161a7": ("10:00 投研周学习", "mon", "10:00"),
} }
# 数据文件新鲜度检查(期望按各自周期更新) # 数据文件新鲜度检查(期望按各自周期更新)

File diff suppressed because one or more lines are too long

View File

@ -344,16 +344,16 @@
"archived_at": null, "archived_at": null,
"created_at": "2026-07-23T09:27:16.552310+00:00", "created_at": "2026-07-23T09:27:16.552310+00:00",
"created_by": "agent", "created_by": "agent",
"last_patched_at": "2026-09-01T13:24:30.353399+00:00", "last_patched_at": "2026-09-04T18:38:06.300591+00:00",
"last_reused_patch_generation": 2, "last_reused_patch_generation": 2,
"last_used_at": "2026-09-03T08:28:19.539412+00:00", "last_used_at": "2026-09-04T18:38:02.602810+00:00",
"last_viewed_at": "2026-09-03T08:28:19.518597+00:00", "last_viewed_at": "2026-09-04T18:38:02.598315+00:00",
"patch_count": 4, "patch_count": 5,
"patch_generation": 2, "patch_generation": 3,
"pinned": false, "pinned": false,
"state": "active", "state": "active",
"use_count": 18, "use_count": 20,
"view_count": 18 "view_count": 20
}, },
"blocked-page-recovery": { "blocked-page-recovery": {
"archived_at": null, "archived_at": null,
@ -459,13 +459,15 @@
"created_at": "2026-07-30T03:28:33.803582+00:00", "created_at": "2026-07-30T03:28:33.803582+00:00",
"created_by": null, "created_by": null,
"last_patched_at": "2026-08-02T15:20:56.394720+00:00", "last_patched_at": "2026-08-02T15:20:56.394720+00:00",
"last_used_at": "2026-08-02T15:20:48.942850+00:00", "last_reused_patch_generation": 0,
"last_viewed_at": "2026-08-02T15:20:48.923159+00:00", "last_used_at": "2026-09-04T18:53:32.351401+00:00",
"last_viewed_at": "2026-09-04T18:53:32.346534+00:00",
"patch_count": 4, "patch_count": 4,
"patch_generation": 0,
"pinned": false, "pinned": false,
"state": "active", "state": "active",
"use_count": 4, "use_count": 5,
"view_count": 4 "view_count": 5
}, },
"char-palace-prompts": { "char-palace-prompts": {
"archived_at": null, "archived_at": null,
@ -706,16 +708,16 @@
"archived_at": null, "archived_at": null,
"created_at": "2026-09-03T15:13:36.558361+00:00", "created_at": "2026-09-03T15:13:36.558361+00:00",
"created_by": "agent", "created_by": "agent",
"last_patched_at": "2026-09-03T15:13:45.894440+00:00", "last_patched_at": "2026-09-04T17:34:57.178533+00:00",
"last_reused_patch_generation": 0, "last_reused_patch_generation": 5,
"last_used_at": "2026-09-03T15:36:53.205305+00:00", "last_used_at": "2026-09-04T17:34:01.038514+00:00",
"last_viewed_at": "2026-09-03T15:36:53.186365+00:00", "last_viewed_at": "2026-09-04T17:34:01.029541+00:00",
"patch_count": 1, "patch_count": 6,
"patch_generation": 1, "patch_generation": 6,
"pinned": false, "pinned": false,
"state": "active", "state": "active",
"use_count": 1, "use_count": 5,
"view_count": 1 "view_count": 5
}, },
"curator-fixes-2026-08-30": { "curator-fixes-2026-08-30": {
"archived_at": null, "archived_at": null,
@ -1145,6 +1147,21 @@
"use_count": 20, "use_count": 20,
"view_count": 20 "view_count": 20
}, },
"file-resource-management": {
"archived_at": null,
"created_at": "2026-09-04T17:16:59.152300+00:00",
"created_by": null,
"last_patched_at": null,
"last_reused_patch_generation": 0,
"last_used_at": null,
"last_viewed_at": null,
"patch_count": 0,
"patch_generation": 0,
"pinned": false,
"state": "active",
"use_count": 0,
"view_count": 0
},
"findmy": { "findmy": {
"archived_at": null, "archived_at": null,
"created_at": "2026-06-30T11:24:39.492606+00:00", "created_at": "2026-06-30T11:24:39.492606+00:00",
@ -1586,14 +1603,14 @@
"created_by": null, "created_by": null,
"last_patched_at": "2026-08-28T13:02:49.751591+00:00", "last_patched_at": "2026-08-28T13:02:49.751591+00:00",
"last_reused_patch_generation": 2, "last_reused_patch_generation": 2,
"last_used_at": "2026-09-03T17:01:30.246953+00:00", "last_used_at": "2026-09-04T17:27:21.253941+00:00",
"last_viewed_at": "2026-09-03T17:01:30.242511+00:00", "last_viewed_at": "2026-09-04T17:27:21.241758+00:00",
"patch_count": 125, "patch_count": 125,
"patch_generation": 2, "patch_generation": 2,
"pinned": false, "pinned": false,
"state": "active", "state": "active",
"use_count": 164, "use_count": 167,
"view_count": 163 "view_count": 166
}, },
"hermes-desktop-kanban": { "hermes-desktop-kanban": {
"archived_at": null, "archived_at": null,
@ -1655,14 +1672,14 @@
"created_by": null, "created_by": null,
"last_patched_at": "2026-07-29T17:37:13.108192+00:00", "last_patched_at": "2026-07-29T17:37:13.108192+00:00",
"last_reused_patch_generation": 0, "last_reused_patch_generation": 0,
"last_used_at": "2026-09-03T13:42:40.323581+00:00", "last_used_at": "2026-09-04T17:43:13.633188+00:00",
"last_viewed_at": "2026-09-03T13:42:40.318944+00:00", "last_viewed_at": "2026-09-04T17:43:13.615463+00:00",
"patch_count": 4, "patch_count": 4,
"patch_generation": 0, "patch_generation": 0,
"pinned": false, "pinned": false,
"state": "active", "state": "active",
"use_count": 22, "use_count": 25,
"view_count": 22 "view_count": 25
}, },
"hermes-self-improvement": { "hermes-self-improvement": {
"archived_at": null, "archived_at": null,
@ -2007,15 +2024,15 @@
"created_at": "2026-05-07T16:49:22.137300+00:00", "created_at": "2026-05-07T16:49:22.137300+00:00",
"created_by": "agent", "created_by": "agent",
"last_patched_at": "2026-08-29T03:58:02.347136+00:00", "last_patched_at": "2026-08-29T03:58:02.347136+00:00",
"last_reused_patch_generation": 2, "last_reused_patch_generation": 3,
"last_used_at": "2026-08-29T03:57:38.402948+00:00", "last_used_at": "2026-09-04T17:44:37.169609+00:00",
"last_viewed_at": "2026-08-29T03:57:38.397169+00:00", "last_viewed_at": "2026-09-04T17:44:37.155675+00:00",
"patch_count": 36, "patch_count": 36,
"patch_generation": 3, "patch_generation": 3,
"pinned": false, "pinned": false,
"state": "active", "state": "active",
"use_count": 44, "use_count": 45,
"view_count": 44 "view_count": 45
}, },
"local-gpu-inference": { "local-gpu-inference": {
"archived_at": null, "archived_at": null,
@ -2223,14 +2240,14 @@
"created_by": "agent", "created_by": "agent",
"last_patched_at": "2026-08-19T13:41:44.010466+00:00", "last_patched_at": "2026-08-19T13:41:44.010466+00:00",
"last_reused_patch_generation": 8, "last_reused_patch_generation": 8,
"last_used_at": "2026-08-19T14:27:27.733776+00:00", "last_used_at": "2026-09-04T17:44:37.165064+00:00",
"last_viewed_at": "2026-08-19T14:27:27.722558+00:00", "last_viewed_at": "2026-09-04T17:44:37.143563+00:00",
"patch_count": 8, "patch_count": 8,
"patch_generation": 8, "patch_generation": 8,
"pinned": false, "pinned": false,
"state": "active", "state": "active",
"use_count": 6, "use_count": 7,
"view_count": 6 "view_count": 7
}, },
"mlops:local-ai-infrastructure": { "mlops:local-ai-infrastructure": {
"archived_at": null, "archived_at": null,
@ -2863,16 +2880,16 @@
"archived_at": null, "archived_at": null,
"created_at": "2026-08-25T02:54:22.347526+00:00", "created_at": "2026-08-25T02:54:22.347526+00:00",
"created_by": "agent", "created_by": "agent",
"last_patched_at": "2026-08-26T01:23:23.386458+00:00", "last_patched_at": "2026-09-04T14:27:38.828538+00:00",
"last_reused_patch_generation": 1, "last_reused_patch_generation": 2,
"last_used_at": "2026-09-02T07:57:13.497297+00:00", "last_used_at": "2026-09-04T14:27:32.475730+00:00",
"last_viewed_at": "2026-09-02T07:57:13.492801+00:00", "last_viewed_at": "2026-09-04T14:27:32.470983+00:00",
"patch_count": 1, "patch_count": 3,
"patch_generation": 1, "patch_generation": 3,
"pinned": false, "pinned": false,
"state": "active", "state": "active",
"use_count": 10, "use_count": 12,
"view_count": 10 "view_count": 12
}, },
"scrapling": { "scrapling": {
"archived_at": null, "archived_at": null,
@ -2921,16 +2938,16 @@
"archived_at": null, "archived_at": null,
"created_at": "2026-07-08T18:13:02.034240+00:00", "created_at": "2026-07-08T18:13:02.034240+00:00",
"created_by": "agent", "created_by": "agent",
"last_patched_at": "2026-09-03T11:33:51.014207+00:00", "last_patched_at": "2026-09-04T16:39:21.174808+00:00",
"last_reused_patch_generation": 34, "last_reused_patch_generation": 36,
"last_used_at": "2026-09-03T14:18:41.928565+00:00", "last_used_at": "2026-09-04T17:27:28.958071+00:00",
"last_viewed_at": "2026-09-03T14:18:41.911595+00:00", "last_viewed_at": "2026-09-04T17:27:28.945981+00:00",
"patch_count": 237, "patch_count": 239,
"patch_generation": 34, "patch_generation": 36,
"pinned": false, "pinned": false,
"state": "active", "state": "active",
"use_count": 222, "use_count": 226,
"view_count": 222 "view_count": 226
}, },
"self-hosted-tunneling": { "self-hosted-tunneling": {
"archived_at": null, "archived_at": null,
@ -3145,14 +3162,14 @@
"created_by": null, "created_by": null,
"last_patched_at": "2026-09-03T18:40:28.509857+00:00", "last_patched_at": "2026-09-03T18:40:28.509857+00:00",
"last_reused_patch_generation": 2, "last_reused_patch_generation": 2,
"last_used_at": "2026-09-03T18:50:41.808932+00:00", "last_used_at": "2026-09-04T17:43:52.476894+00:00",
"last_viewed_at": "2026-09-03T18:50:41.795520+00:00", "last_viewed_at": "2026-09-04T17:43:52.459199+00:00",
"patch_count": 2, "patch_count": 2,
"patch_generation": 2, "patch_generation": 2,
"pinned": false, "pinned": false,
"state": "active", "state": "active",
"use_count": 45, "use_count": 48,
"view_count": 45 "view_count": 48
}, },
"stock-research": { "stock-research": {
"archived_at": null, "archived_at": null,
@ -3408,16 +3425,16 @@
"archived_at": null, "archived_at": null,
"created_at": "2026-08-02T18:24:48.270464+00:00", "created_at": "2026-08-02T18:24:48.270464+00:00",
"created_by": "agent", "created_by": "agent",
"last_patched_at": "2026-08-20T03:06:03.803904+00:00", "last_patched_at": "2026-09-04T18:34:41.511889+00:00",
"last_reused_patch_generation": 17, "last_reused_patch_generation": 38,
"last_used_at": "2026-08-20T03:22:26.292517+00:00", "last_used_at": "2026-09-04T18:40:00.738760+00:00",
"last_viewed_at": "2026-08-20T03:22:26.277235+00:00", "last_viewed_at": "2026-09-04T18:40:00.734335+00:00",
"patch_count": 19, "patch_count": 40,
"patch_generation": 17, "patch_generation": 38,
"pinned": false, "pinned": false,
"state": "active", "state": "active",
"use_count": 15, "use_count": 27,
"view_count": 15 "view_count": 27
}, },
"windows-python-pip": { "windows-python-pip": {
"archived_at": null, "archived_at": null,
@ -3533,14 +3550,14 @@
"created_by": null, "created_by": null,
"last_patched_at": "2026-08-29T10:13:24.762118+00:00", "last_patched_at": "2026-08-29T10:13:24.762118+00:00",
"last_reused_patch_generation": 8, "last_reused_patch_generation": 8,
"last_used_at": "2026-09-03T17:44:44.009198+00:00", "last_used_at": "2026-09-04T17:35:00.180526+00:00",
"last_viewed_at": "2026-09-03T17:44:43.996910+00:00", "last_viewed_at": "2026-09-04T17:35:00.168074+00:00",
"patch_count": 739, "patch_count": 739,
"patch_generation": 8, "patch_generation": 8,
"pinned": false, "pinned": false,
"state": "active", "state": "active",
"use_count": 430, "use_count": 431,
"view_count": 404 "view_count": 405
}, },
"zhiyi-dev": { "zhiyi-dev": {
"archived_at": null, "archived_at": null,

View File

@ -6,6 +6,15 @@ description: bge-embed.service 崩溃重启循环(端口未释放 / venv 缺
# bge-embed Crash Loop 排查与修复 # bge-embed Crash Loop 排查与修复
> ⚡ **2026-09-05 架构升级覆盖说明**:本 skill 写于 2026-09-01bge 还在本机 8000、CUDA 正常态)。现在已外移:
> - **本机 bge-embed 监听 8001**(纯 CPU fallback——诊断本机服务用 `curl localhost:8001/health`
> - **8000 = bge-failover-proxy**(新增 bge-proxy.service转发远端 ZSB GPU / 本地 8001
> - `curl localhost:8000/health` 仍有效但走代理:返回 `DmlExecutionProvider`(远端 GPU 正常)或 `CPUExecutionProvider`(远端挂了自动切本地——**不是故障**,是 fallback 生效)
> - 看门狗/健康检查语义变了CPU 不再是"异常",而是远端不可达时的正常兜底。判断"远端挂了"看代理日志 `journalctl --user -u bge-proxy` 或响应头 `X-BGE-Proxy`
> - 下文所有 `:8000` 诊断命令(若针对**本机 bge 服务本身**)应改 `:8001``/health` 返回 CUDA/DML 的"正常态"判断不再适用于本机(本机恒 CPU
> - **gpu-health-watchdog.sh 已同步升级2026-09-05**8000 /health 无响应时先 `restart bge-proxy`(主入口)再 `restart bge-embed`fallback——旧逻辑只重启 bge-embed 治不了 8000 代理挂。跑完再 `curl localhost:8000/health` 复验
> - 完整新架构见 `windows-home-server-ops` skill「远端 AI 推理外移 + 故障切换代理」章节
## 症状 ## 症状
bge-embed.service 反复崩溃重启,重启计数飙到 85337 次,但看起来"在线"。 bge-embed.service 反复崩溃重启,重启计数飙到 85337 次,但看起来"在线"。

View File

@ -79,6 +79,7 @@ for s, v in by.items():
| 检查已下线服务 | health-watchdog 检测 omniroute/new-api已直连化下架| 删检测项,避免每次误报 + 尝试 start 不存在服务 | | 检查已下线服务 | health-watchdog 检测 omniroute/new-api已直连化下架| 删检测项,避免每次误报 + 尝试 start 不存在服务 |
| 检查已下线/孤儿 DB | db-monitor 检查 cron.db0 字节非真存储)| 从检查列表删掉 | | 检查已下线/孤儿 DB | db-monitor 检查 cron.db0 字节非真存储)| 从检查列表删掉 |
| 硬编码唯一值 | anti-suicide-check 要求主模型必须 mimo-v2.5-pro | 改白名单mimo + 授权切换模型),本质是"不能是未授权模型" | | 硬编码唯一值 | anti-suicide-check 要求主模型必须 mimo-v2.5-pro | 改白名单mimo + 授权切换模型),本质是"不能是未授权模型" |
| 硬编码旧 cron job_id | 监控脚本按 job_id 检查执行,但 cron 迁移/重建后 id 变了 → 全部误报 NO_DIR/未执行 | 修前先 `hermes cron list` 对名字找**新 id**任务还在id 变了);或让监控按任务名查 jobs.json |
| git 跟踪运行时噪音 | cron/jobs.json.bak-agnes 使仓库永远 dirty | `git rm --cached <file>` | | git 跟踪运行时噪音 | cron/jobs.json.bak-agnes 使仓库永远 dirty | `git rm --cached <file>` |
| 状态文件残留 | health.state 一直 'alarm' | 修好源后复跑,状态变 ok 自动发恢复 | | 状态文件残留 | health.state 一直 'alarm' | 修好源后复跑,状态变 ok 自动发恢复 |
@ -92,9 +93,33 @@ for s, v in by.items():
- [ ] git 无运行时噪音被跟踪 - [ ] git 无运行时噪音被跟踪
- [ ] 所有监控复跑零告警,状态文件转 ok - [ ] 所有监控复跑零告警,状态文件转 ok
## 调度延迟容错(健康检查别在计划时刻就报"未执行"
**症状**18:30 的 job 因 gateway 繁忙/daemon 排队实际 19:04 才跑18:45 的健康检查看到当日无输出文件就报"未执行"(连续 3 天误报)。
**根因**cron 触发时间 ≠ 实际执行时间。gateway 卡死/长任务占线程时 daemon 排队,延迟可达 30+ 分钟。
**修法**stock_daily_health.py 2026-09-03 已落地):检查"当日无文件"前,若 `now < sched + 30min` 则标 NOT_YET 不告警;超过 30min 仍无文件才报 NO_RUN。同坑也在监控类脚本通用——检查"X 应该已跑"一律给宽限窗口。
**gateway 卡死判定线索**2026-09-05 实测):`journalctl --user -u hermes-gateway --since "<18:30 >" | grep -iE 'wedged|interrupted|force-interrupt'` → 看到 Wedged-turn stack dump + liveness watchdog force-interrupt = 主对话占线程cron 排队延迟的根因证据。cron 实际触发时间用 `hermes cron history <job_id> --limit 3`source=builtin 的时间戳是实际执行)。
## 恢复后组件独立性验证state.db 快照恢复后必答)
用户问"向量模型/本地 7B 受影响吗"时,**用命令验证不用口头保证**(拉现状铁律):
```bash
systemctl --user status bge-embed llama-server-7b zhiyid | grep Active # 全 active
curl -s :8000/v1/embeddings -d '{"model":"bge-m3","input":"测试"}' # bge OpenAI 兼容端点
curl -s -H "X-API-Key: zhiyi-dev-key-2026" :7821/api/v1/stats # 织忆数据量
ss -tlnp | grep :8420 # TencentDB
```
**结论模式**state.db 恢复只动 hermes 状态库bge/llama/织忆/TencentDB 都是独立进程+独立数据目录,零依赖——恢复后唯一重启的是 hermes-gateway预期内。注意 busy_timeout 单独查询=0 是正常的connection-level代码层已设织忆 recall curl 直查 0 命中≠系统坏(工具层带正确 embedding 格式才准)。
## 本机现状2026-09-03 清理后) ## 本机现状2026-09-03 清理后)
- hermes cron 47 个(原 93删 44 重复 + 2 危险项);系统 crontab 空 - hermes cron 47 个(原 93删 44 重复 + 2 危险项);系统 crontab 空
- 危险项已归档 `~/.hermes/scripts/.archive-20260903/`state-db-watchdog*.py/sh、rebuild-delivery-obligations.sh - 危险项已归档 `~/.hermes/scripts/.archive-20260903/`state-db-watchdog*.py/sh、rebuild-delivery-obligations.sh
- 监控保留db-monitor纯告警、health-watchdog只拉起不杀、anti-suicide-check - 监控保留db-monitor纯告警、health-watchdog只拉起不杀、anti-suicide-check
- 完整清理过程见 `references/cron-watchdog-cleanup-20260903.md` - 完整清理过程见 `references/cron-watchdog-cleanup-20260903.md`
- 股票健康检查误报两连(调度延迟 + 旧 job_id`references/stock-health-false-alarms-20260904.md`

View File

@ -0,0 +1,32 @@
# 股票健康检查误报两连2026-09-04
## 误报 118:30 复盘"当日未执行"(连续 3 天)
**症状**cron `caad7f13d5c4`(股票系统健康体检 18:45`❌ 18:30 自动复盘 当日未执行`,连报 3 天。
**根因**gateway 主对话 wedged turn 卡死 34 分钟 → daemon 排队 → 18:30 job 实际 19:04 才跑18:45 健康检查看到当日无输出文件就报"未执行"。
**修法**`stock_daily_health.py` 加 30 分钟宽限——检查"当日无文件"前先算 `sched_dt + 30min`,未到则标 NOT_YET 不告警。
## 误报 24 个任务 NO_DIR同日 18:46 再报)
**症状**:修复误报 1 后复测仍红:`08:00 股票新闻 / 09:00 组合扫描 / 16:00 五粮液 / 17:00 回测汇总` 全部 `NO_DIR`(无输出目录)。
**根因**`stock_daily_health.py` STOCK_CRONS 硬编码的是 **cron 迁移前的旧 job_id**,任务其实都活着但 id 变了 → 目录查不到 = 误报。
**关键判据**`hermes cron list` 里任务名还在但 id 对不上;`ls ~/.hermes/cron/output/<旧id>` 目录不存在。**先怀疑 job_id 迁移,不是任务被删。**
**修法**:按任务名找新 id 更新映射:
| 任务 | 旧 id过期 | 新 id2026-09 生效) |
|---|---|---|
| 每日股票新闻 08:00 | f3619a71aebb | c03c58e1e0aa |
| 每日组合信号 09:00 | c293eead6688 | 4855debe954a |
| 五粮液每日信号 16:00 | c48bbbb4fd18 | 67f13ef40ecb |
| 股票每周回测汇总 fri | 5a78557f000b | 5206e141cd67 |
| 股票投研周学习 mon | 6061a782b772 | 50cff6b161a7 |
## 通用教训
- **监控脚本按 job_id 检查 = 脆弱**cron 迁移/重建会换 id脚本静默失配。修前先 `hermes cron list` 按名字核 id长期看应按任务名查 jobs.json。
- **健康检查要给调度延迟留宽限**30mingateway 繁忙时 daemon 排队是常态不是异常。

View File

@ -0,0 +1,39 @@
---
name: file-resource-management
description: 文件资源管理机制 — 脚本/图片/视频/文档分类存放,避免根目录散落。触发词"文件乱/资源管理/整理文件/归类"。
---
# 文件资源管理机制2026-09-05 牧尘要求)
## 核心原则(科学分类,遵循已有约定,不重复造轮子)
| 文件类型 | 统一去处 | 理由 |
|---------|---------|------|
| **脚本** | `~/.hermes/scripts/`140+ 统一库)| 已有脚本库,不新建 |
| **知识/文档/研究** | `~/mc/小唯/06-Raw/<年>/` 按类分 reports/logs/images/videos | 进 Obsidian 可检索、进织忆图谱 |
| **AI 生图/素材** | `~/mc/小唯/06-工具与资源/media/` | 跟随知识库备份体系 |
| **下载** | `~/Downloads`(系统标准)| 不迁移 |
| **根目录(~)** | 只允许 dotfile + 目录 | 散落文件 = 异常 |
## 自动化机制
### 1. resource-watchdog.pycron c336c1c3b39f 每日5:00
- 扫描 ~ 根目录非 dotfile 文件
- 按扩展名分类:.md/.json/.txt→reports.png/.jpg→images.mp4→videos.zip→archives
- 自动归档到 `~/mc/小唯/06-Raw/<year>/<kind>/`
- watchdog 语义:干净静默,有散落才报警
- 手动跑:`python3 ~/.hermes/scripts/resource-watchdog.py [--dry-run|--apply]`
### 2. backup-cleanup.pycron 6a87615b5155 每日4:00
- 自动轮转过期备份state.db.*留3 / 快照留8 / bundle留2 / broken直删 / bak留2
- 磁盘>85% 额外触发清理
## 铁律
1. 任何新备份机制必须先问"它怎么被自动清掉"(保留上限)
2. 根目录不产生散落文件——产出物直接写入目标目录
3. 安装前查重目录,模型用软链不复制
4. 恢复/修复流程结尾必须含"验证新库健康→删旧残留"步骤
## 归档历史
- 2026-09-05 首建:根目录 53 散落文件 → 0脚本归 legacy-checks、研究报告归 06-Raw
- 2026-09-05 磁盘 86%→47%(释放 174Gbroken 83G + 双ComfyUI 30G + 旧备份 40G+ 缓存 8G+ 日志 4G

View File

@ -57,6 +57,17 @@ const out = response.items.map((item) => ({
- 去重状态文件: `~/.hermes/data/rsshub_ai_seen.json`(最多 500 条) - 去重状态文件: `~/.hermes/data/rsshub_ai_seen.json`(最多 500 条)
- 有新增 → 写入织忆 `/api/v1/commit`;无新增 → 静默退出 - 有新增 → 写入织忆 `/api/v1/commit`;无新增 → 静默退出
- Cron: `6457e26da8c3` 每天 8:00/20:00 - Cron: `6457e26da8c3` 每天 8:00/20:00
- **源间延时**: `RSSHUB_SOURCE_DELAY` 环境变量可配,默认 15s
## 2026-09-04 限流事故403→503 误报)
**症状**: cron 报 "HTTP Error 503",但 RSSHub 本身 activecurl 单测也 200。
**真相**: RSSHub 把上游 403 转成 503。`lib/routes/github/search.ts` 无缓存、每次实时打 GitHub Search API6 源串行连发(间隔<1s触发 **GitHub secondary rate limit**403冷却 30-60sRSSHub 内置 ofetch retry3s/6s backoff覆盖不了 3 次重试全挂日志特征: `Request https://api.github.com/search/repositories ... with error 403` + `--> GET /github/search/XXX 503`
**修复**: 脚本源间加 15s 延时(无论成败都等),单次 cycle 从 ~6s 拉长到 ~75s请求节奏远低于限流阈值。dry-run 不再写 seen 状态文件(旧版无条件 `save_seen`dry-run 会把真新条目标 seen 导致漏抓)。
**验证**: `time python3 rsshub-zhiyi-fetch.py --dry-run` 应 exit 0 且无 ⚠️ 输出;`journalctl --user -u rsshub` 应见源间 15s 间隔的连续 200。
## Pitfalls ## Pitfalls
@ -64,3 +75,5 @@ const out = response.items.map((item) => ({
2. **pnpm build 警告**: `TOLERATED_TRANSFORM``EVAL` 警告可忽略 2. **pnpm build 警告**: `TOLERATED_TRANSFORM``EVAL` 警告可忽略
3. **状态文件污染**: 测试时 `--dry-run` 不写状态文件;但旧版本有 bug 会写 3. **状态文件污染**: 测试时 `--dry-run` 不写状态文件;但旧版本有 bug 会写
4. **GitHub API 限流**: 无 token 时 60 次/h有 token 5000 次/h 4. **GitHub API 限流**: 无 token 时 60 次/h有 token 5000 次/h
5. **503 复发快速判定2026-09-04 20:01 实例)**: cron 报"部分失败: GitHub LLM/深度学习 503"时,先 `for p in /github/search/LLM ...; do curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:1200$p; done` 实测——全 200 = 瞬时限流已自愈,无需处置。失败源**不标记 seen**fetch 异常 continue条目不写状态文件→ 下轮自动补抓,无数据丢失;且 6 源按 link 跨源去重LLM/deep+learning 漏抓的条目通常已被 ai/machine+learning 等重叠源写入(补跑返回空=证实)。
6. **补跑验证法**: 收到部分失败告警后手动 `python3 rsshub-zhiyi-fetch.py` 补一轮——输出空0 新增 0 报错)说明漏源条目已被重叠源覆盖;有新增则正常补写。

View File

@ -21,7 +21,11 @@ trigger: 系统部署、开机自启、配置更改、故障恢复场景、备
**2026-08-01 Gateway 崩溃循环事故(真根因 = auto-heal 自杀)**NRestarts=176 死循环的根源不是 unit 丢失本身,而是 config-protector.sh 的 auto-heal 机制——watchdog 检测到 hermes 重启瞬间 pgrep miss → 触发 `git checkout --force stable` → stable 停在 23 天前 → 整个 ~/.hermes 硬回滚 → 删新增文件/config 回退 → gateway 崩 → 再回滚 → 无限循环。帮凶:`git add -A` 跟踪运行时噪音 + stable tag 从 7-09 未更新 + hermes 在回滚触发列表。已修复6044957/4edbd50/bee399d/4d1d2e2+ 新增 anti-suicide-check.sh 每 30min 自检 6 规则。详见 references/auto-heal-suicide-crashloop-20260801.md。 **2026-08-01 Gateway 崩溃循环事故(真根因 = auto-heal 自杀)**NRestarts=176 死循环的根源不是 unit 丢失本身,而是 config-protector.sh 的 auto-heal 机制——watchdog 检测到 hermes 重启瞬间 pgrep miss → 触发 `git checkout --force stable` → stable 停在 23 天前 → 整个 ~/.hermes 硬回滚 → 删新增文件/config 回退 → gateway 崩 → 再回滚 → 无限循环。帮凶:`git add -A` 跟踪运行时噪音 + stable tag 从 7-09 未更新 + hermes 在回滚触发列表。已修复6044957/4edbd50/bee399d/4d1d2e2+ 新增 anti-suicide-check.sh 每 30min 自检 6 规则。详见 references/auto-heal-suicide-crashloop-20260801.md。
**2026-08-01 systemd-run 逃生通道**gateway 内部硬保护拦截 stop/restartSIGTERM 传播自杀),但 `systemd-run --user --unit=xxx --collect bash script.sh` 从 gateway 外部独立进程树执行可绕开——用于"停崩溃循环 → 释放端口 → systemd 接管"的接管序列。脚本内 sleep 4 给会话留发送回复时间。详见 references/auto-heal-suicide-crashloop-20260801.md。 **2026-08-01 systemd-run 逃生通道**gateway 内部硬保护拦截 stop/restartSIGTERM 传播自杀),但 `systemd-run --user --unit=xxx --collect bash script.sh` 从 gateway 外部独立进程树执行可绕开——用于"停崩溃循环 → 释放端口 → systemd 接管"的接管序列。脚本内 sleep 4 给会话留发送回复时间。详见 references/auto-heal-suicide-crashloop-20260801.md。
**2026-08-01 graph.db 空文件恢复 + NewAPI 503 误报**记忆系统蒸馏全挂graph_nodes 表不存在报错)根因是 7-30 迁移时把完整 graph.db 移到 archive、~/.hermes 只留 0 字节空文件——daemon.py 只查 os.path.exists 不建表。恢复:停 daemon → 清 graph.db-shm/wal → cp archive 完整库 → 重启验证蒸馏。每日复盘 503 system cpu overloaded 是崩溃循环期间 CPU 短飙的次生灾害修根因后自动消失ps aux 的 CPU 列是单核百分比,判断真实负载用 top -bn1。详见 references/graph-db-empty-recovery-20260801.md。 **2026-08-01 graph.db 空文件恢复 + NewAPI 503 误报**记忆系统蒸馏全挂graph_nodes 表不存在报错)根因是 7-30 迁移时把完整 graph.db 移到 archive、~/.hermes 只留 0 字节空文件——daemon.py 只查 os.path.exists 不建表。恢复:停 daemon → 清 graph.db-shm/wal → cp archive 完整库 → 重启验证蒸馏。每日复盘 503 system cpu overloaded 是崩溃循环期间 CPU 短飙的次生灾害修根因后自动消失ps aux 的 CPU 列是单核百分比,判断真实负载用 top -bn1。详见 references/graph-db-empty-recovery-20260801.md。
- **2026-09-05 备份保留治理 + SMB弱网僵死 + MCP残留噪音三连 class-level**:见 `references/disk-retention-governance-20260905.md`、`references/dual-backup-smb-hang-20260905.md`。要点速记:①垃圾根因=备份只增不删无上限,任何备份必须有保留上限+清理触发(已落地 backup-cleanup.py cron `6a87615b5155`②SMB备份只在家庭局域网123.11做牧尘铁律StarVPN仅GiteaCIFS弱网僵死=不可中断D态大文件先本地再 rsync --partial ③MCP server command 指向不存在二进制=每5分钟重试噪音源`hermes mcp list` 排查,生效需外部重启 gateway。
- **2026-09-03 state.db 周期性损坏根治**:根因=OOM/SIGKILL 杀 gateway → WAL 未 checkpoint → 事务中断 → 单表损坏。修复链:上游 v0.21.0 busy_timeout+journal_size_limit + watchdog 降噪 + ExecStartPre stabilize + 回滚脚本。详见 `references/state-db-corruption-fix-20260903.md` - **2026-09-03 state.db 周期性损坏根治**:根因=OOM/SIGKILL 杀 gateway → WAL 未 checkpoint → 事务中断 → 单表损坏。修复链:上游 v0.21.0 busy_timeout+journal_size_limit + watchdog 降噪 + ExecStartPre stabilize + 回滚脚本。详见 `references/state-db-corruption-fix-20260903.md`
- **2026-09-05 备份保留治理class-level牧尘"磁盘为什么这么满/怎么避免"**:垃圾根因 = **备份/快照/损坏留档只增不删、无保留上限**。本次 86%→59%(释放 121G①memories.lance.broken.1787251919 83G损坏重建后旧目录从不删②memories.lance.bak-20260904 15G修复前备份③hermes-backup-20260717 16G手动备份无保留期④74 个 pre-watchdog 快照 7G旧看门狗无轮转⑤14 个散落 state.db.* 备份 2.5G修完不清理⑥ComfyUI 装两份 30G。**避免铁律**:①任何备份/快照必须设保留份数上限 + 清理触发点 ②恢复/修复流程结尾必须含"验证新库健康 → 删除旧残留"步骤 ③安装应用前查重、模型用软链不复制 ④磁盘 >85% 触发额外清理。已落地 `backup-cleanup.py`cron `6a87615b5155` 每日 4 点state.db.* 留3 / pre-watchdog 留3 / snap 留8 / bundle 留2 / broken 直删 / bak 留2。详见 `references/disk-retention-governance-20260905.md`
- **2026-09-05 SMB/CIFS 弱网备份僵死类dual-backup 78 分钟卡死根治 + 牧尘"外网不备份"铁律)**:①**根因1fstab**:备份挂载写死 StarVPN IP192.168.188.11)→ 外网也走 Tailscale 隧道传 1.9GB bundle → 网络抖动 CIFS 进程进**不可中断 D 态**`SigBlk=ffffffffffffffff`SIGKILL/cgroup.kill 都杀不掉),`timeout` 只杀 bash 包装、孙进程僵持数小时还占 flock fd。②**根因2exclude 写反)**`tar --exclude='*.lance'` 把真库排除、反而打进不以 .lance 结尾的 broken/bak 垃圾98Gtimeout 600 必失败。③**根因3无并发锁**:两轮手动/cron 重叠互踩。**修复模式(可复用)**git bundle/大文件先写**本地盘**11s`rsync -a --partial`可杀、断点续传tar 排除垃圾模式;`exec 9>lock; flock -n 9` 防并发(残留僵尸持锁时**删锁文件**绕开——新文件新 inode服务端禁 ICMP → 探测用 TCP 445 不是 ping。④**牧尘铁律2026-09-04最高优先级**SMB 备份**只在家庭局域网 192.168.123.11 做**StarVPN(188.11) 只用于 Gitea 推送,绝不 SMB 备份。check_mount 只认 `/proc/mounts``//192.168.123.11/` 真实 cifsmountpoint -q 对 autofs 恒真不可用外网秒级静默跳过。fstab 已改回 123.11fix-fstab-lan.sh。详见 `references/dual-backup-smb-hang-20260905.md`
- **2026-09-05 MCP server 配置残留 = 持续重试噪音源**config.yaml `mcp_servers` 里 command 指向不存在二进制codegraph 已被 codebase-memory-mcp 取代但配置没清)→ gateway 每 5 分钟 "failed initial connection, parking" 空转重试,观感像"系统持续异常"。另一个是 openclaw command 不在 PATH实际在 nodejs 目录)。修法:`hermes mcp list` 看 transport/status → `hermes mcp remove <name>` 或改完整路径toolset 引用 `mcp-codegraph` 同步改 `mcp-codebase-memory-mcp`。**生效需重启 gateway**MCP 是启动时加载;内部重启被硬保护拦截,让用户在外部 ssh 执行 `systemctl --user restart hermes-gateway`)。⚠️ MCP server 配了但二进制不存在是静默噪音源,`pgrep` watchdog 只能看到真存在的 server排查"持续异常"先 `hermes mcp list` 看 enabled 列表有没有假配置。
- **2026-09-03 cgroup memory.current ≠ 进程真实 RSS防误判 OOM**`MemoryCurrent=1.54G` 不代表 gateway 真的用 1.54G——cgroup.procs 缓存了已退出子进程的 RSS**真实 gateway RSS 仅 ~488MB**。诊断三步:① `cat /sys/fs/cgroup/.../memory.current` 看 cgroup 计数 ② `ps -o pid,rss,comm -p <gateway-pid>` 看进程 RSS ③ `cat cgroup.procs` 看是否有僵尸 PID。**类陷阱**state.db 看 `stat -c%s` 335MB 不代表真数据 335MB——WAL 文件 0 字节 + SHM 32768 字节的 mmap 区是 SQLite 正常态,不算损坏。详见 `references/cgroup-memory-vs-real-rss-20260903.md` - **2026-09-03 cgroup memory.current ≠ 进程真实 RSS防误判 OOM**`MemoryCurrent=1.54G` 不代表 gateway 真的用 1.54G——cgroup.procs 缓存了已退出子进程的 RSS**真实 gateway RSS 仅 ~488MB**。诊断三步:① `cat /sys/fs/cgroup/.../memory.current` 看 cgroup 计数 ② `ps -o pid,rss,comm -p <gateway-pid>` 看进程 RSS ③ `cat cgroup.procs` 看是否有僵尸 PID。**类陷阱**state.db 看 `stat -c%s` 335MB 不代表真数据 335MB——WAL 文件 0 字节 + SHM 32768 字节的 mmap 区是 SQLite 正常态,不算损坏。详见 `references/cgroup-memory-vs-real-rss-20260903.md`
- **2026-09-03 gateway 内部 self-restart 拦截是 string-based 全覆盖**session 内任何含 `restart hermes-gateway` / `hermes gateway restart` / `systemctl ... restart hermes-gateway` 的命令字符串都被预先拦截——不是 partial 拦截,是全路径拦截。**绕开路径**:① 新 ssh/物理终端 ② `at` / 独立 systemd-run 单元 ③ 等系统 watchdog 自然重启。**判定**session 内 `restart hermes-gateway` 返回 BLOCKED 而不是 timeout/permission denied = 你就在 gateway 内。详见 `references/state-db-corruption-fix-20260903.md` §陷阱 2 增强。 - **2026-09-03 gateway 内部 self-restart 拦截是 string-based 全覆盖**session 内任何含 `restart hermes-gateway` / `hermes gateway restart` / `systemctl ... restart hermes-gateway` 的命令字符串都被预先拦截——不是 partial 拦截,是全路径拦截。**绕开路径**:① 新 ssh/物理终端 ② `at` / 独立 systemd-run 单元 ③ 等系统 watchdog 自然重启。**判定**session 内 `restart hermes-gateway` 返回 BLOCKED 而不是 timeout/permission denied = 你就在 gateway 内。详见 `references/state-db-corruption-fix-20260903.md` §陷阱 2 增强。
- **2026-09-03 任务包写错方案的反思("commit 让 PRAGMA 持久化"反模式)**:写"修某 PRAGMA 让它持久化"类方案时,**先查 SQLite pragma.html 文档**——`journal_size_limit` / `busy_timeout` / `cache_size` / `mmap_size` / `temp_store` 都是 connection-only`conn.commit()` 无效。**反向论证路径**:写方案 → grep 上游 hermes-agent 是否有同类注释 → 实测新连接读什么 → 再确认方案。**类陷阱**:方案 P0 写 `busy_timeout=30000(30s)` 太大——journal_mode 切换窗口是毫秒级30s 让连接长时间 hang**100ms 已够**。任何数值类方案,先回答"这个值是给什么场景用的?窗口多长?"再拍数。 - **2026-09-03 任务包写错方案的反思("commit 让 PRAGMA 持久化"反模式)**:写"修某 PRAGMA 让它持久化"类方案时,**先查 SQLite pragma.html 文档**——`journal_size_limit` / `busy_timeout` / `cache_size` / `mmap_size` / `temp_store` 都是 connection-only`conn.commit()` 无效。**反向论证路径**:写方案 → grep 上游 hermes-agent 是否有同类注释 → 实测新连接读什么 → 再确认方案。**类陷阱**:方案 P0 写 `busy_timeout=30000(30s)` 太大——journal_mode 切换窗口是毫秒级30s 让连接长时间 hang**100ms 已够**。任何数值类方案,先回答"这个值是给什么场景用的?窗口多长?"再拍数。

View File

@ -1,12 +1,12 @@
--- ---
name: windows-home-server-ops name: windows-home-server-ops
description: "家庭服务器 (192.168.123.11 Windows) 部署与运维 — SSH 管理、Node 服务常驻schtasks、防火墙放行、Windows cmd/PowerShell 坑。替代 Docker服务器没装。" description: "Windows 机器部署与运维(家庭服务器 192.168.123.11 + ZSB 192.168.5.104)— SSH/schtasks 常驻、防火墙放行、远端 AI 推理外移 + 故障切换代理bge/llama 类、DirectML GPU 推理、Windows cmd/PowerShell 坑。替代 Docker机器没装。含 LAN 内新 Windows 机器接入探测。"
version: 1.3.0 version: 1.5.0
author: 小唯 A06 author: 小唯 A06
tags: [windows, server, schtasks, ssh, deploy, node, 家庭服务器, firewall, openclaw, hermes, feishu] tags: [windows, server, schtasks, ssh, deploy, node, 家庭服务器, firewall, openclaw, hermes, feishu, bge, inference, failover]
trigger: "部署到家庭服务器 / 192.168.123.11 / 192.168.188.11 / Windows服务器上跑服务 / 服务器常驻服务 / schtasks / 服务器模型切换 / hermes gateway 重启 / openclaw 飞书配置" trigger: "部署到家庭服务器 / 192.168.123.11 / 192.168.188.11 / 192.168.5.104 / ZSB / Windows服务器上跑服务 / 服务器常驻服务 / schtasks / 服务器模型切换 / hermes gateway 重启 / openclaw 飞书配置 / 新Windows机器接入探测 / SMB凭据验证 / NT_STATUS_LOGON_FAILURE / 192.168.5.x机器 / 远端推理外移 / bge外移 / 向量模型部署 / 故障切换代理 / onnxruntime-directml / DirectML GPU推理"
created: 2026-08-03 created: 2026-08-03
updated: 2026-08-20 updated: 2026-09-05
--- ---
# Windows 家庭服务器部署与运维 # Windows 家庭服务器部署与运维
@ -24,6 +24,61 @@ updated: 2026-08-20
| Gitea | ✅ 3000 端口 | | Gitea | ✅ 3000 端口 |
| SMB 共享 | ✅ `beifen` 等挂载于 `/mnt/server-backup` | | SMB 共享 | ✅ `beifen` 等挂载于 `/mnt/server-backup` |
## 新 Windows 机器接入探测LAN 内新增主机,无 SSH 通道时)
场景:牧尘给一组新凭据(`主机名\用户名` + 密码)和 IP机器此前未管理。**先探测、后下结论,别假设能 SSH。**
探测序列(无 nmap 时用 /dev/tcp 循环):
1. **先搜织忆/session** 确认主机名是否已知别把新机器当成已知服务器08-20 铁律:只碰被提到的目标)。
2. **ping 存活**(本机 LAN 192.168.5.x 一般通;.188.11 VPN 网段禁 ICMP 只信 TCP
3. **端口探测** 22 135 139 445 3389 5985 5986135/139/445 = Windows SMB22/3389/5985 全关 = 新装系统还没开远程管理通道。
4. **身份确认**`nmblookup -A IP` 拿主机名/工作组(无凭据即可验身份;`zsb\zsb` 格式里第一个 zsb 是主机名)。
5. **凭据验证走 SMB**(唯一开着的通道):
```bash
smbclient -L //IP -U 'user%pass' -m SMB3
# 变体:-W WORKGROUP / -U 'HOST\user%pass' / 直接 //IP/IPC$ -c 'exit'
```
6. 全变体仍 LOGON_FAILURE → 回报牧尘确认密码/账户状态,同时给出开通道方案,别自己瞎猜密码。
Pitfalls
- **NT_STATUS_LOGON_FAILURE** = 凭据或账户策略问题(密码错 / 账户禁用 / 禁网络登录),**不是通道问题**。换 -W、域前缀、IPC$ 三变体后再下结论。
- **别用 `-m NT1`**:现代 Windows 报 `Max protocol NT1 is less than min protocol SMB2_02`INVALID_PARAMETER_MIX。用 SMB3/默认。
- **新装 Windows 默认无 SSH/RDP/WinRM**:此刻无法远程执行命令。推进 = 需有人在机器前装 OpenSSH Server`Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0`)或开 RDP。
- 实例实录:`references/onboarding-zsb-1921685104-20260905.md`(探测阶段);完整部署+外移见 `references/remote-bge-inference-zsb-20260905.md`
## 远端 AI 推理外移 + 故障切换代理(本机瘦身核心模式 2026-09-05
把 CPU/GPU 推理bge 嵌入、llama 等)外移到 LAN Windows 机器,本机只留 fallback
```
调用方(zhiyid/hermes) → localhost:8000 (bge-failover-proxy.py 常驻本机)
├── 远端通 → http://192.168.5.104:8000 (ZSB GPU/DirectML) 主
└── 远端挂 → http://127.0.0.1:8001 (本机 CPU fallback) 兜底
```
### 部署侧关键事实ZSB 192.168.5.104 实录)
- **机器**Windows + NVIDIA MX450 2GBTuring 带 INT8 Tensor Core同网段 192.168.5.x非家庭服务器 192.168.123.11!同网段才能低延迟)。
- **GPU 路线选 DirectML 而非 CUDA**onnxruntime-gpu 需外装 CUDA Toolkit + cuDNN数百 MB 系统级DirectML`pip install onnxruntime-directml`)直接调 MX450 显存、零额外依赖。provider = `DmlExecutionProvider` 优先 + CPU 兜底。
- **模型传 int8 版**bge model_int8.onnx ~570MB别传 fp32 2.7G。
- **常驻用 schtasks SYSTEM**`/sc onstart /ru SYSTEM`bat 里必须 `set "VAR=value"` 标准写法——`set VAR=value &&` 会把 `&&` 前的空格吞进变量值repr 可见尾随空格)→ tokenizer 加载失败。
- **Windows 默认拦截**:查 `python.exe` 入站 Block 规则会静默拦服务端口OpenSSH 规则常仅限 Private 而 WLAN 是 Public → 外部 22 不通。修:删 Block 规则、`netsh advfirewall` 放行端口、OpenSSH 改 Any profile。
### 本机代理bge-failover-proxy.py设计要点
- 调用方**零改动**:它只认 localhost:8000代理决定去哪。
- 每次请求前探远端 `/health`1.5s 超时),通→远端,不通→本地。加 **3s 冷却 + 状态缓存**防探测风暴。
- 响应带 `X-BGE-Proxy: remote-104 / local-106` 头,验收时一眼看路由。
- systemd本机 bge-embed 改 `BGE_PORT=8001`fallback 常驻不杀);新 `bge-proxy.service` 占 8000。
- **故障测试用 iptables 模拟**`sudo iptables -I OUTPUT -p tcp --dport 8000 -d 192.168.5.104 -j REJECT` → 验自动切本地 → `-D` 移除 → 验切回远端。
- 完整脚本 + 服务文件在 `~/.hermes/scripts/bge-failover-proxy.py` + `~/.config/systemd/user/bge-proxy.service`
### Pitfalls外移场景特有
- **同网段 vs 跨网段**:外移推理必须同 LAN192.168.5.x别指到另一网段服务器192.168.123.11)——延迟/路由不可控。
- **旧进程占端口**:本机 8001 曾被 ZSB 部署期间的临时 `python3 -m http.server 8001` 占住 → bge bind 失败 `Address already in use`,重启无效。诊断:`ss -tlnp | grep :8001` 看占用者 cmd杀错对象bge 进程正常但端口被别的占)。
- **Windows 端 curl 是 PowerShell 别名**:用 `curl.exe`
- **部署包走 HTTP 下载比 SMB 稳**ZSB 默认禁匿名 guest SMBAllowInsecureGuestAuth=0Linux 侧 `python3 -m http.server` 暴露 + 防火墙放行即可。
## SSH 连接模板 ## SSH 连接模板
```bash ```bash
@ -127,10 +182,21 @@ curl -s -o /dev/null -w "external: %{http_code}" http://192.168.123.11:1200/heal
10b. **⛔ 用户问"A服务配置"只查A不碰B** — "openclaw飞书"≠"hermes飞书"。擅自改没被提到的服务 = 搞坏它。2026-08-20 教训。 10b. **⛔ 用户问"A服务配置"只查A不碰B** — "openclaw飞书"≠"hermes飞书"。擅自改没被提到的服务 = 搞坏它。2026-08-20 教训。
11. **config.yaml 不含飞书配置** — 飞书凭据全在 `.env`FEISHU_APP_ID / FEISHU_APP_SECRET / FEISHU_HOME_CHANNEL不在 config.yaml。重写 config.yaml 不会丢飞书配置,但 .env 要独立维护。 11. **config.yaml 不含飞书配置** — 飞书凭据全在 `.env`FEISHU_APP_ID / FEISHU_APP_SECRET / FEISHU_HOME_CHANNEL不在 config.yaml。重写 config.yaml 不会丢飞书配置,但 .env 要独立维护。
12. **⛔ openclaw.json 有严格 schema 校验** — 不能手写完整 JSON 替换(尤其含 `models.providers.xiaomi` 等非标准字段会报 `Invalid input`。安全做法PowerShell `Add-Member` 只添加缺失字段,不碰已有结构。详见 `references/openclaw-feishu-model-switch-20260820.md` 12. **⛔ openclaw.json 有严格 schema 校验** — 不能手写完整 JSON 替换(尤其含 `models.providers.xiaomi` 等非标准字段会报 `Invalid input`。安全做法PowerShell `Add-Member` 只添加缺失字段,不碰已有结构。详见 `references/openclaw-feishu-model-switch-20260820.md`
13. **⛔ 服务器禁 ICMPping 不通 ≠ 离线** — StarVPN 网段 192.168.188.11 实测 ping 100% 丢,但 TCP 445/3000 全通Windows 防火墙禁了 ICMP。任何"探测服务器可达性"的逻辑**别用 ping**,用 TCP 探测:`timeout 3 bash -c 'echo > /dev/tcp/192.168.188.11/445'`。2026-09-04 教训dual-backup.sh check_mount 用 ping → 服务器其实在线却被判"离线常态跳过",备份静默丢失。
14. **⛔ SMB 挂载 IO 可无限挂起** — CIFS `soft` 挂载在网络劣化时 ls/du/tar 仍会挂起等 IO实测 StarVPN 瞬断后 ls snapshots 卡 120s+、cron 备份脚本卡满 3600s 超时)。**防护**a) 大操作一律 `timeout N cmd`git bundle 1.88GB 用 300s、LanceDB tar 130GB 用 600s超时跳过并记日志别让整个 cron 卡死b) 恢复:`sudo umount -l /mnt/server-backup && sudo mount -a`-l lazy 卸载立即释放卡住的 IO。c) 重挂用 fstab 的 credentials 文件:`sudo mount -a`(别手打密码选项,会 Invalid argument
15. **备份脚本设计教训2026-09-04 dual-backup 4 连败)** — 备份目标含 git bundle~1.9GB+ LanceDB tar130GB 目录)每次全量写 SMB网络差时必卡。修复给每个重量级操作独立 `timeout`快速失败而非无限等check_mount 探测从 ping 改 TCP 445LanceDB tar 失败只跳过不阻塞后续快照。详见 `references/dual-backup-smb-hang-20260904.md`
16. **⛔⛔ 双备份铁律2026-09-04 牧尘指示SMB 备份只在家庭局域网(192.168.123.11)做StarVPN(192.168.188.11) 只用于 Gitea 推送,绝不用来做备份** — 真正根因是 fstab 把 `/mnt/server-backup``x-systemd.automount` 写死成了 `//192.168.188.11/beifen`,导致外网也走 Tailscale 挂 SMB 传 1.9GB → 弱网僵死。诊断顺序:`grep server-backup /proc/mounts` 看真实挂载 IP不是 mountpoint -q——**autofs 挂载点恒真,必须 grep /proc/mounts 找 cifs 行**`grep server-backup /etc/fstab` 看 automount 目标。修复脚本 `fix-fstab-lan.sh`:只替换 fstab IP 为 123.11**保留 x-systemd.automount**(去掉会坏——脚本内 `sudo -n mount` 无 NOPASSWD全靠 automount 触发挂载)。外网时 check_mount 探测 123.11:445 不通即秒级跳过。
17. **⛔ git bundle / tar 直写 SMB 卡死后 timeout 杀不掉D-state/信号全阻塞)** — 2026-09-04 实测rsync 写半死 CIFS 挂载,`/proc/PID/status` 显示 `SigBlk: ffffffffffffffff`kill -9 无效,只能 `sudo umount -l` 让底层 IO 报错才退出。**根治模式:大文件先本地打包再 rsync 单文件**——git bundle 本地打只花 11s直写 SMB 卡 78 分钟rsync 单文件可被 timeout 正常杀且 `--partial` 续传。bundle 按天命名 + 当日只打一次,避免每 6h 重传 1.9GB。
18. **⛔ LanceDB 备份 exclude 语义陷阱** — `--exclude='*.lance'` 排除的是以 `.lance` **结尾**的目录(真库 memories.lance/episodes.lance但 `memories.lance.broken.1787251919`、`memories.lance.bak-20260904` 不以 .lance 结尾**不会被排除** → 每次 tar 的是 98G broken/bak 垃圾而非真库。另:**zhiyid 运行中直接 tar LanceDB 目录 = 损坏快照**8-21 教训。结论LanceDB 全量备份不该塞进 6h push35G 真库 + 在线一致性),需独立低频任务或等 zhiyid snapshot API。本地 `~/.hermes/backups/` 已被 state.db 快照占满,暂存放 `backups/dual-backup/` 子目录。
19. **flock 子进程继承坑**`exec 9>lock; flock -n 9`git/rsync 等子进程继承 fd 9父 bash 被杀但孙进程僵死时锁永远被占,后续运行报"已有备份在运行"。排查:`pgrep -af 'dual-backup|git bundle|rsync.*hermes-git'` 找残留,杀不掉就 umount 释放。
20. **外网 cron 静默跳过的判定标准** — 不在家123.11:445 不通)= 常态,跳过备份 return 0 静默;只有"局域网通但同步失败"才 error。别把"外网"当 error 上报。
## 参考 ## 参考
- `references/rsshub-windows-deploy-2026-08.md` — RSSHub 部署全程实录(命令、日志、验证) - `references/rsshub-windows-deploy-2026-08.md` — RSSHub 部署全程实录(命令、日志、验证)
- `references/openclaw-feishu-model-switch-20260820.md` — OpenClaw 模型切换 + 飞书配置修复 + schtasks 常驻 - `references/openclaw-feishu-model-switch-20260820.md` — OpenClaw 模型切换 + 飞书配置修复 + schtasks 常驻
- `references/hermes-gateway-restart-workaround-20260820.md` — hermes gateway 阻止 SSH 重启的绕过方法schtasks 中转) - `references/hermes-gateway-restart-workaround-20260820.md` — hermes gateway 阻止 SSH 重启的绕过方法schtasks 中转)
- `references/dual-backup-smb-hang-20260904.md` — SMB 挂载 IO 挂起事故:禁 ICMP 探测陷阱 + timeout 兜底 + umount -l 恢复
- `references/onboarding-zsb-1921685104-20260905.md` — 新机器 ZSB (192.168.5.104) 接入实录:探测结果 + SMB 凭据验证失败 + 无通道待闭环
- `references/remote-bge-inference-zsb-20260905.md` — 完整闭环ZSB DirectML GPU 部署实录 + 本机故障切换代理(架构/验证/改动清单)
- Gitea 镜像流程见 `gitea-repo-mirroring` skill - Gitea 镜像流程见 `gitea-repo-mirroring` skill

View File

@ -0,0 +1,66 @@
# dual-backup SMB 挂起事故全记录2026-09-04 两次诊断 + 根治)
## 症状
dual-backup cronb450955bf58e每 6h当日多次失败18:00 轮卡满 60min 被杀last_status=error 19:00:57手动重试轮 20:15 外层 timeout 900 杀掉 bash 但 **git bundle 孙进程残留 78 分钟**21:38 才死)。
## 第一层诊断(白天)→ 见本文件下方通用教训
初步修复加了 timeout 兜底 / TCP 探测,**但没解决根因**,晚上复发。
## 第二层诊断(夜间,真正根因)
1. 残留进程:`pgrep -af 'git bundle'` → `timeout 300 git bundle create ...` 与 git 本体都还活着 → 内层 timeout 300 已失效(进程阻塞在 CIFS 写,信号无法送达)。外层 timeout 只杀了 bash**孙进程变孤儿继续僵**。
2. `ps -o pid,stat,wchan -p PID` → S state + `wait_woken``grep SigBlk /proc/PID/status` → `ffffffffffffffff`(全信号阻塞)→ kill -9 无效。**只有 umount 让 IO 报错才释放**。
3. `grep server-backup /proc/mounts` → 挂载源是 `//192.168.188.11/beifen`StarVPN/Tailscale**不是局域网 123.11**。
4. `grep server-backup /etc/fstab``//192.168.188.11/beifen /mnt/server-backup cifs credentials=...,_netdev,x-systemd.automount` —— **fstab 把 automount 写死成 StarVPN IP**,这是所有卡死的总根源。
5. `du -sh /var/lib/memoryweave/*``memories.lance.broken.1787251919`=83G损坏残留+ `memories.lance.bak-20260904`=15G + 真库 memories.lance=17G / episodes.lance=18G。原 tar `--exclude='*.lance'` 排真库、留垃圾,每次打 95G。
## 根治(牧尘指示 2026-09-04SMB 备份只在家庭局域网做StarVPN 只推 Gitea
### fix-fstab-lan.sh需 sudo 执行)
- umount 当前 StarVPN 残留挂载lazy
- fstab `//192.168.188.11/beifen``//192.168.123.11/beifen`
- **保留 `x-systemd.automount`**:脚本内 `sudo -n mount` 无 NOPASSWD去掉 automount 在家也挂不上。外网时 check_mount 先探测 123.11:445 不通即跳过,根本不触发 automount。
- 备份 `/etc/fstab.bak.20260904-lan`systemctl daemon-reload
### dual-backup.sh check_mount 重写要点
- **勿用 `mountpoint -q`**autofs 挂载点恒真);以 `grep '//192\.168\.123\.11/.* /mnt/server-backup ' /proc/mounts` 判断真实 cifs 挂载
- 挂载为 188.11 残留 → return 2 跳过(外网不做备份)
- 未挂载 → TCP 探测 123.11:445不通 return 2外网常态静默通则 `ls /mnt/server-backup` 触发 automount
### 步骤 8 git bundle 根治
- **本地先打 bundle**写本地盘11s 完成)→ `rsync -a --partial` 单文件到服务器
- 按天命名 `hermes-git-YYYYMMDD.bundle` + 当日只打一次stat mtime < 今天 00:00 才重建
- rsync 带 `timeout 300 --partial`:网络差被杀也不卡死,下次续传;本地副本保留
- 暂存目录 `$HERMES/backups/dual-backup/`(避免和 state.db 快照混)
### 步骤 10 LanceDB tar 移除
- exclude 语义陷阱:`*.lance` 只匹配结尾 .lancebroken/bak 目录漏网 → 打了 98G 垃圾
- zhiyid 在线运行中 tar LanceDB = 损坏快照8-21 教训)
- 35G 真库过 StarVPN 远超 cron 60min 预算 → **从 6h push 移除**,留 TODO等 snapshot API 或独立低频 rsync
- 保留 SQLite 真数据备份53M graph.db 走 `~/.hermes/` 取,软链真身在本地)
### flock 并发锁
`exec 9>lock; flock -n 9` 防 cron/手动重叠;注意子进程继承 fd 9僵死孙进程会占锁 → 排查残留进程。
## 验证结果(实测)
- git bundle 本地打包:**11 秒**(此前直写 SMB 卡 50-78 分钟)✅
- bundle rsync 300s 超时StarVPN 传 1.9G 确实慢)→ 正确走失败分支继续后续步骤,不再拖死 cron ✅
- 外网 push秒级"不在家庭局域网,跳过"✅
- 待牧尘执行 `sudo bash ~/.hermes/scripts/fix-fstab-lan.sh` 清残留挂载 + 改 fstab
## 通用教训(两层合并)
- **服务器禁 ICMP 时 ping 探测全废** — 一律 TCP 端口探测445/3000/22
- **CIFS soft 挂载不是免挂起** — 大 IO 要外层 timeout 兜底
- **卡死 CIFS 写kill 无效(信号全阻塞)→ `sudo umount -l` 才释放**
- **大文件备份模式:本地打包 → rsync 单文件(可杀、--partial 续传),勿直写 SMB**
- **备份通道走 VPN = 自找卡死**SMB 备份只走局域网VPN 只做轻量推送
- **grep /proc/mounts 判真实挂载,别信 mountpoint -qautofs 恒真)**

View File

@ -0,0 +1,23 @@
# ZSB 新机器接入实录2026-09-05未闭环
## 凭据(牧尘提供)
- 账户zsb完整格式 `zsb\zsb`(主机名\用户名)
- 密码1020
- IP192.168.5.104(本机 LAN本机 .106 同网段)
- 用途:未说明(首次出现,记忆/织忆/session 均无 zsb 记录)
## 探测结果(实测 2026-09-05
- ping 通(~2ms
- 端口135/139/445 OPENWindows SMB22/3389/5985/5986/47001/5900 全关
- `nmblookup -A`:主机名 **ZSB**,工作组 **WORKGROUP**MAC 8C-B8-7E-18-B4-BA
- SMB 凭据验证 zsb/1020全变体 **NT_STATUS_LOGON_FAILURE**
- `smbclient -L //IP -U 'zsb%1020' -m SMB3`(带/不带 `-W`)、`-U 'zsb\zsb%1020'`、`//IP/IPC$ -c 'exit'` 均失败
- `-m NT1` 报 INVALID_PARAMETER_MIX现代 Windows 最低协议 SMB2_02别用 NT1
## 结论 / 状态
- 机器身份确认ZSB / WORKGROUP但**凭据未验证、无任何远程管理通道**(无 SSH/RDP/WinRM
- 已回报牧尘:①确认密码/账户状态 ②开 OpenSSH Server`Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0`)或开 RDP 后才有通道
- **未闭环**:等牧尘答复后再继续;续接时先看本文件,别从头重复探测

View File

@ -0,0 +1,68 @@
# bge 嵌入外移 ZSB + 故障切换代理2026-09-05 完整闭环)
## 目标
织忆向量召回从本机 CPU (106) 外移到 ZSB (192.168.5.104, Windows + MX450 2GB GPU)。本机只留 CPU fallback。
## 最终架构
```
zhiyid (织忆, 零改动) → localhost:8000
└─ bge-failover-proxy.py (106 本机常驻, bge-proxy.service)
├── 远端通 → http://192.168.5.104:8000 (ZSB GPU / DmlExecutionProvider)
└── 远端挂 → http://127.0.0.1:8001 (本机 CPU fallback)
```
调用方只认 localhost:8000代理决定去哪 → **故障切换对 zhiyid 透明**
## ZSB 侧192.168.5.104)——由该机智能体部署
### 机器事实
- Windows, 主机名 ZSB, 工作组 WORKGROUP, 同网段 192.168.5.x
- GPU: NVIDIA MX450 2GB (Turing, INT8 Tensor Core)
- 凭据: zsb\zsb / 密码由牧尘提供SSH 22 后已通OpenSSH 规则改 Any profile
### GPU 路线决策DirectML 而非 CUDA
- onnxruntime-gpu 1.29 需外装 CUDA 13 Toolkit + cuDNN 9数百 MB 系统级1.29 不自带 DLL
- onnxruntime-gpu 1.20.1/1.24.1 也不自带 CUDA DLL
- **onnxruntime-directml 1.24.4**:直接调 MX450 显存,零额外依赖 → 选它
- providers = DmlExecutionProvider 优先 + CPU 兜底
- 实测显存占用 632~694 MiB / 2048 MiBembedding 1024 维正常
### 模型文件(只传 int8 版,~570MB 非 2.7G
- bge_embed_server.py 默认用 model_int8.onnxsystemd 设 BGE_MODEL_FILE=model_int8.onnx
- tokenizer 依赖sentencepieceXLMRobertaTokenizer slowVC++ 2015-2022 x64 运行库必须装onnxruntime 缺 DLL 报错)
- Python 依赖: onnxruntime-directml + transformers + numpy + sentencepiece
### 常驻 + 防火墙
- schtasks SYSTEM 任务 bge-embed/sc onstart /ru SYSTEM开机自启
- 防火墙 bge-8000 规则放行
- **修的两个隐藏问题**:① python.exe 入站 Block 规则残留两条(拦截 8000→ 删;② OpenSSH 规则仅限 Private 而 WLAN 是 Public → 改 Any profile
## 106 侧改动
1. `~/.hermes/scripts/bge-failover-proxy.py`(新增,代理本体)
2. `~/.config/systemd/user/bge-proxy.service`(新增,监听 8000
3. `bge-embed.service`BGE_PORT 8000→8001本机变 fallback
代理要点:每次请求前探远端 /health1.5s 超时)+ 3s 冷却 + 状态缓存;响应带 X-BGE-Proxy 头remote-104 / local-106转发只回 content-type + 重算 Content-Length。
## 故障切换验证iptables 模拟)
```bash
sudo iptables -I OUTPUT -p tcp --dport 8000 -d 192.168.5.104 -j REJECT
# → 代理自动切 local-106HTTP 200X-BGE-Proxy: local-106
sudo iptables -D OUTPUT -p tcp --dport 8000 -d 192.168.5.104 -j REJECT
# 冷却期过 → 自动切回 remote-104
```
## 坑
- 8001 曾被临时 `python3 -m http.server 8001` 占住 → bge bind 失败Address already in use重启无效。诊断看 `ss -tlnp` 占用者 cmd。
- 部署包传 ZSBSMB 匿名 guest 被禁AllowInsecureGuestAuth=0改走 Linux 侧 `python3 -m http.server` HTTP 下载 + 防火墙放行最稳。
- Windows cmd `set VAR=value && ...` 会把 `&&` 前空格吞进变量值 → 必须 `set "VAR=value"` 标准 bat 写法。
## 部署包
- bge-model.tar.gz 418MBint8 + tokenizer 全套)曾放 kocr 共享 → 已清理。
- zhiyid 零改动。rerank 仍是本地zhiyid 内部 bge-reranker-v2-m3

View File

@ -274,6 +274,13 @@ json.dump(d, open('cares-queue.json','w'), ensure_ascii=False, indent=2)
> 🛑 **新会话必读**:本 skill 顶部「快速状态」表是**当前真实部署状态**2026-06-25 全链路验证通过)。任何关于织忆的判断,先看此表 + 跑下方「系统全面检查步骤」10 秒内拿真实状态)。**绝对不要从 AGENTS.md / MEMORY.md 里过去 1 个月以上的描述直接判断织忆状态**。 > 🛑 **新会话必读**:本 skill 顶部「快速状态」表是**当前真实部署状态**2026-06-25 全链路验证通过)。任何关于织忆的判断,先看此表 + 跑下方「系统全面检查步骤」10 秒内拿真实状态)。**绝对不要从 AGENTS.md / MEMORY.md 里过去 1 个月以上的描述直接判断织忆状态**。
> ⚡ **2026-09-05 bge 架构升级(重要!)**:嵌入推理已外移 ZSB (192.168.5.104, Windows + MX450 GPU/DirectML)。**端口含义变了**
> - **8000 = bge-failover-proxy**(新增 bge-proxy.service调用方唯一入口远端 104:8000 GPU 优先,故障自动切本地 8001 CPU
> - **8001 = 本机 bge-embed**bge-embed.serviceBGE_PORT 已改 8001纯 fallback
> - 检查命令 `curl localhost:8000/health` **仍然有效**(走代理→远端 DML但返回 providers 会是 `DmlExecutionProvider`(远端)而非 `CPUExecutionProvider`
> - 想看真实后端:响应头 `X-BGE-Proxy: remote-104 / local-106`;或 `ss -tlnp | grep -E ':8000|:8001'` 看两个进程
> - 故障切换对 zhiyid **零感知**(它只认 localhost:8000。详见 `windows-home-server-ops` skill「远端 AI 推理外移 + 故障切换代理」章节
**最后一次实地拉状态2026-08-02四系统体检 + episodes 持久化修复后)** **最后一次实地拉状态2026-08-02四系统体检 + episodes 持久化修复后)**
| 项目 | 状态 | 路径/值 | | 项目 | 状态 | 路径/值 |