docs: add step-by-step gate architecture to SKILL.md

Adds a complete blueprint for building the lightweight precheck /
external-gate system that reduces model cost by 80-95% while keeping
deep analysis quality intact. Includes file layout, state schema,
pseudocode for precheck and external gate, and cron config examples.
This commit is contained in:
2026-04-16 03:03:25 +08:00
parent a4ba9bb64e
commit 6db86fa0e7

150
SKILL.md
View File

@@ -115,6 +115,156 @@ This pattern preserves Telegram auto-delivery from Hermes cron while reducing mo
- Add a file lock around the external gate script so overlapping system-cron invocations cannot double-trigger.
- Rotate `~/.coinhunter/logs/external_gate.log` with `logrotate` (daily, keep ~14 compressed copies, `copytruncate`) and schedule the rotation a few minutes after the fallback Hermes cron run so they do not overlap.
### Building your own gate (step-by-step)
If you want to replicate this low-cost trigger architecture, here is a complete blueprint.
#### 1. File layout
Create these files under `~/.hermes/scripts/` and state under `~/.coinhunter/state/`:
```
~/.hermes/scripts/
coinhunter_precheck.py # lightweight threshold evaluator
coinhunter_external_gate.py # optional system-crontab wrapper
~/.coinhunter/state/
precheck_state.json # last snapshot + trigger flags
external_gate.lock # flock file for external gate
```
#### 2. State schema (`precheck_state.json`)
```json
{
"last_positions_hash": "sha256_of_positions_json",
"last_top_candidates_hash": "sha256_of_top_5_coins",
"last_btc_regime": "bullish|neutral|bearish",
"last_deep_analysis_at": "2026-04-15T11:00:00Z",
"free_usdt": 12.50,
"account_total_usdt": 150.00,
"run_requested_at": null,
"run_acknowledged_at": "2026-04-15T11:05:00Z",
"volatility_session": "low|medium|high",
"staleness_hours": 2.0
}
```
#### 3. Precheck script logic (pseudocode)
```python
import json, hashlib, os, math
from datetime import datetime, timezone
STATE_PATH = os.path.expanduser("~/.coinhunter/state/precheck_state.json")
POSITIONS_PATH = os.path.expanduser("~/.coinhunter/positions.json")
def load_json(path):
with open(path) as f:
return json.load(f)
def save_json(path, obj):
tmp = path + ".tmp"
with open(tmp, "w") as f:
json.dump(obj, f, indent=2)
os.replace(tmp, path)
def compute_hash(obj):
return hashlib.sha256(json.dumps(obj, sort_keys=True).encode()).hexdigest()[:16]
def adaptive_thresholds(account_total_usdt, volatility_session):
# Micro accounts get wider thresholds; high volatility narrows them
base_price = 0.03 if account_total_usdt >= 100 else 0.08
base_pnl = 0.05 if account_total_usdt >= 100 else 0.12
vol_mult = {"low": 1.2, "medium": 1.0, "high": 0.7}.get(volatility_session, 1.0)
return base_price * vol_mult, base_pnl * vol_mult
def should_analyze():
state = load_json(STATE_PATH) if os.path.exists(STATE_PATH) else {}
positions = load_json(POSITIONS_PATH)
# ... fetch current tickers, BTC regime, free USDT here ...
new_pos_hash = compute_hash(positions.get("positions", []))
new_btc_regime = "neutral" # replace with actual analysis
new_free = positions.get("balances", {}).get("USDT", {}).get("free", 0)
total = positions.get("account_total_usdt", 0)
volatility = "medium" # replace with actual session metric
price_thr, pnl_thr = adaptive_thresholds(total, volatility)
triggers = []
if new_pos_hash != state.get("last_positions_hash"):
triggers.append("position_change")
if new_btc_regime != state.get("last_btc_regime"):
triggers.append("btc_regime_change")
# ... check per-position price/PnL drift vs thresholds ...
# ... check candidate leadership change (skip if free_usdt < min_actionable) ...
staleness = (datetime.now(timezone.utc) - datetime.fromisoformat(state.get("last_deep_analysis_at","2000-01-01T00:00:00+00:00"))).total_seconds() / 3600.0
max_staleness = 4.0 if total >= 100 else 8.0
if staleness >= max_staleness:
triggers.append("staleness")
decision = bool(triggers)
state.update({
"last_positions_hash": new_pos_hash,
"last_btc_regime": new_btc_regime,
"free_usdt": new_free,
"account_total_usdt": total,
"volatility_session": volatility,
"staleness_hours": staleness,
})
if decision:
state["run_requested_at"] = datetime.now(timezone.utc).isoformat()
save_json(STATE_PATH, state)
return {"should_analyze": decision, "triggers": triggers, "state": state}
if __name__ == "__main__":
result = should_analyze()
print(json.dumps(result))
```
#### 4. Hermes cron job configuration
Attach the precheck script as the `script` field of the cron job so its JSON output is injected into the prompt:
```json
{
"id": "coinhunter-trade",
"schedule": "*/15 * * * *",
"prompt": "You are Coin Hunter. If the injected context says should_analyze=false, respond with exactly [SILENT] and do nothing. Otherwise, read ~/.coinhunter/positions.json, run the scientific checklist, decide HOLD/SELL/REBALANCE/BUY, and execute via smart_executor.py. After finishing, run ~/.hermes/scripts/coinhunter_precheck.py --ack to clear the trigger.",
"script": "~/.hermes/scripts/coinhunter_precheck.py",
"deliver": "telegram",
"model": "kimi-for-coding"
}
```
Add an `--ack` handler to the precheck script (or a separate ack script) that sets `run_acknowledged_at` and clears `run_requested_at` so the gate does not re-fire until the next true trigger.
#### 5. External gate (optional, for even lower cost)
If you want to run the precheck every 5 minutes without waking Hermes at all:
`coinhunter_external_gate.py` pseudocode:
```python
import fcntl, os, subprocess, json, sys
LOCK_PATH = os.path.expanduser("~/.coinhunter/state/external_gate.lock")
PRECHECK = os.path.expanduser("~/.hermes/scripts/coinhunter_precheck.py")
JOB_ID = "coinhunter-trade"
with open(LOCK_PATH, "w") as f:
try:
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
except BlockingIOError:
sys.exit(0) # another instance is running
result = json.loads(os.popen(f"python {PRECHECK}").read())
if result.get("should_analyze"):
# Trigger Hermes cron only if not already requested
state_path = os.path.expanduser("~/.coinhunter/state/precheck_state.json")
state = json.load(open(state_path))
if not state.get("run_requested_at"):
subprocess.run(["hermes", "cron", "run", JOB_ID], check=False)
```
System crontab entry:
```cron
*/5 * * * * /usr/bin/python3 /home/user/.hermes/scripts/coinhunter_external_gate.py >> /home/user/.coinhunter/logs/external_gate.log 2>&1
```
With this setup, the model is only invoked when a material market change occurs—preserving intelligence while cutting routine cost by 80-95%.
### Production hardening (mandatory)
The live trading stack must include these safeguards:
1. **Idempotency** — every decision carries a `decision_id`. The executor checks `~/.coinhunter/executions.json` before submitting orders to prevent duplicate trades.