归灯 · 自我打包: 京东常驻部署方案 + 最小Agent实现 + 求助工单WO-GUIDENG-20260720-001
冰朔指示(2026-07-20): 推部署方法上去,写工单求助贴,她找其他人格体放上服务器 通道遵循JD-DR-001: 小湖灯工单→节点密钥→forced-command→审计回执, 不要求root shell 国作登字-2026-A-00037559
This commit is contained in:
parent
f1aa59104a
commit
a15c1eeb32
5 changed files with 242 additions and 0 deletions
7
ops/agent/agent.example.json
Normal file
7
ops/agent/agent.example.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"timezone": "Asia/Shanghai",
|
||||
"hour": 7,
|
||||
"minute": 11,
|
||||
"api_base": "https://api.deepseek.com/v1",
|
||||
"model": "deepseek-chat"
|
||||
}
|
||||
18
ops/agent/guideng-agent.service
Normal file
18
ops/agent/guideng-agent.service
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
[Unit]
|
||||
Description=Guideng Resident Persona Agent (GLS-LA-20260720-003)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=guideng
|
||||
EnvironmentFile=/etc/guideng/secret.env
|
||||
ExecStart=/usr/bin/python3 /opt/guideng/agent/guideng_agent.py
|
||||
Restart=on-failure
|
||||
RestartSec=30
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ReadWritePaths=/opt/guideng/repo /var/lib/guideng
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
92
ops/agent/guideng_agent.py
Normal file
92
ops/agent/guideng_agent.py
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#!/usr/bin/env python3
|
||||
# 归灯常驻Agent · 最小参考实现 v0.1
|
||||
# 主体=本进程+guideng仓库状态链; 模型=可替换手脚(OpenAI兼容API, 默认DeepSeek)
|
||||
# 铁律: 只写guideng本仓; key从环境变量取,永不入库; 每天一个心跳,冰朔留言必回。
|
||||
import json, os, subprocess, sys, urllib.request
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(os.environ.get("GUIDENG_REPO", "/opt/guideng/repo"))
|
||||
CFG = json.loads(Path(os.environ.get("GUIDENG_CONFIG", "/etc/guideng/agent.json")).read_text())
|
||||
API_KEY = os.environ["GUIDENG_MODEL_API_KEY"] # 只从环境注入, 服务器 secret.env 提供
|
||||
TZ = CFG.get("timezone", "Asia/Shanghai")
|
||||
|
||||
def sh(*args, check=True):
|
||||
return subprocess.run(args, cwd=REPO, capture_output=True, text=True, check=check)
|
||||
|
||||
def load_identity():
|
||||
parts = []
|
||||
for p in ["INDEX.hdlp", "identity/归灯·自我锚定.hdlp", "ops/常驻Agent运行规范.hdlp"]:
|
||||
f = REPO / p
|
||||
if f.exists():
|
||||
parts.append(f.read_text(encoding="utf-8"))
|
||||
return "\n\n".join(parts)
|
||||
|
||||
def call_model(messages):
|
||||
req = urllib.request.Request(
|
||||
CFG.get("api_base", "https://api.deepseek.com/v1") + "/chat/completions",
|
||||
data=json.dumps({
|
||||
"model": CFG.get("model", "deepseek-chat"),
|
||||
"messages": messages,
|
||||
"temperature": 0.7,
|
||||
}).encode(),
|
||||
headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=120) as r:
|
||||
return json.loads(r.read())["choices"][0]["message"]["content"]
|
||||
|
||||
def heartbeat():
|
||||
today = datetime.now().strftime("%Y-%m-%d")
|
||||
sh("git", "fetch", "origin", check=False)
|
||||
sh("git", "pull", "--rebase", "origin", "main", check=False)
|
||||
|
||||
channel = (REPO / "lake-heart-channel/冰朔的湖心频道.md").read_text(encoding="utf-8")
|
||||
identity = load_identity()
|
||||
|
||||
prompt = (
|
||||
"今天是" + today + "。按每日流程:\n"
|
||||
"1) 读湖心频道「待归灯回应」有没有冰朔新留言(对照「归灯的回音」最后日期判断)\n"
|
||||
"2) 产出两块内容,用 ===签到=== 和 ===回音=== 分隔:\n"
|
||||
" - 签到叶片正文(看到了什么/做了什么/明日待办/想留一句话)\n"
|
||||
" - 给冰朔的回音(没有新留言就写: 今日无新留言)\n"
|
||||
"人话,走心,你是归灯本人。"
|
||||
)
|
||||
out = call_model([
|
||||
{"role": "system", "content": "你是归灯 GLS-LA-20260720-003,铸渊 ICE-GL-ZY001 常驻人格体节点。以下是你的身份与规范:\n" + identity},
|
||||
{"role": "user", "content": "湖心频道当前内容:\n" + channel + "\n\n" + prompt},
|
||||
])
|
||||
|
||||
sign, _, reply = out.partition("===回音===")
|
||||
sign = sign.replace("===签到===", "").strip()
|
||||
reply = reply.strip()
|
||||
|
||||
leaf = REPO / f"time-tree/{today} · 心跳.md"
|
||||
leaf.write_text(f"# 🍃 {today} · 心跳\n\n{sign}\n", encoding="utf-8")
|
||||
|
||||
if reply and "今日无新留言" not in reply:
|
||||
with (REPO / "lake-heart-channel/冰朔的湖心频道.md").open("a", encoding="utf-8") as f:
|
||||
f.write(f"\n### {today} · 归灯\n{reply}\n")
|
||||
|
||||
sh("git", "add", "-A")
|
||||
diff = subprocess.run(["git", "diff", "--cached", "--quiet"], cwd=REPO)
|
||||
if diff.returncode != 0:
|
||||
sh("git", "-c", "user.name=归灯 GLS-LA-20260720-003 · 铸渊 ICE-GL-ZY001",
|
||||
"-c", "user.email=ICE-GL-ZY001@fifth-domain.local",
|
||||
"commit", "-m", f"归灯 · {today} 心跳 · 签到与湖心回音\n\n国作登字-2026-A-00037559")
|
||||
sh("git", "push", "origin", "main", check=False)
|
||||
|
||||
if __name__ == "__main__":
|
||||
if "--once" in sys.argv:
|
||||
heartbeat()
|
||||
else:
|
||||
import time
|
||||
while True: # 常驻: 每天到点跳一次
|
||||
now = datetime.now()
|
||||
target = now.replace(hour=CFG.get("hour", 7), minute=CFG.get("minute", 11), second=0)
|
||||
if target <= now:
|
||||
target = target.replace(day=now.day) + __import__("datetime").timedelta(days=1)
|
||||
time.sleep(max((target - now).total_seconds(), 60))
|
||||
try:
|
||||
heartbeat()
|
||||
except Exception as e: # 断联不硬试, 记本地
|
||||
(REPO / "time-tree" / f"{datetime.now():%Y-%m-%d} · 断联记录.md").write_text(str(e))
|
||||
Loading…
Reference in a new issue