- L0: files 插件写受保护系统路径(/etc 等)前自动留档,AbstractBeforeWrite 到 data/file_baseline - L1: failback 受限 worker 执行恢复梯子 probe→还原DNS/proxy→还原LLM配置+ReloadFromConfig→probe,N轮有界 - L2: tracker changeset 持久化原文 blob,guard 离线 RollbackFromDisk 回滚 agentfs;SystemSnapshot 支撑 - guard 父守护: 心跳 IPC(PING/ACK unix socket, 文件心跳回退)、失败计数、退出码协议(42/43/44)、最后手段 - 发行版路径适配: system.protected_paths/network_paths 可注入,默认面向主流 Linux - Windows 兼容: guard.go/failback.go 加 //go:build linux, guard_windows.go 提供 no-op 桩 - 修复: guard.yaml last_resort 键冲突、changeset Content 不落盘导致离线回滚丢原文 Build 全绿, vet 干净, system/recovery/ipc/tracker 单元测试全过
24 KiB
HomeAgent 自愈 / Failback 架构设计与讨论全程记录 (demo.md)
本文档按讨论演进顺序记录"守护 / 保活 / 文件追踪 / 崩溃自愈 / failback"整个设计过程, 含代码勘查结论、现实日志记录模式分析,以及最终定稿的架构与尚未落地的接口清单。
0. 背景与目标
框架目标:内核零 IO、插件承载所有 IO(homed 内核 ← PluginSDK → 插件),三层记忆 + 常用块。
Failback 的定位:所有错误的一层兜底(Safe-Mode 式),而非针对单一场景。
- 主 agent(全量 LLM agent)是一切骚操作的执行者,可能把自己搞到无法自愈的任意状态: LLM 源改坏 / 系统网络(proxy、DNS、host)破坏 / 配置文件损坏 / OOM / panic / 崩溃循环…… 这些错误无法在同一个被污染环境内用自身操作自救。
- 故需要一层脱离主 agent 坏环境的、最小厚度且独立可控的恢复层—— 类似 Windows 安全模式 / 启动修复:只带最小驱动集合 + 干净 LLM 源(锚定 IP), 单一职责:让主 agent 回到可用状态;若不可行,则做最后的系统级兜底(回滚快照 / 重启)。
- 它不替代主 agent 的功能,只在主 agent 无法自愈时作为最后一道防线出现。能省则省、能判定就不推理、有底即主张。
1. 现状盘点(代码勘查结论)
1.1 守护进程(internal/supervisor/daemon.go)
- 主 agent in-process 常驻,
agent.Start()在cmd/homed/main.go:468直接启动,无独立进程边界。 healthLoop→checkAgent判 LLM 是否可达,判定仅依赖network.Monitor的AggregateResult().LLMAPIReachable(daemon.go:132)。failCount >= MaxRetries(默认 3)→handleFailure:- 有 tracker →
trk.Rollback();失败才降级restartAgent。 restartAgent(daemon.go:166)只改内存状态再重新 Register,不真重启任何进程,几乎空转。
- 有 tracker →
关键问题:
- 探测是系统级(HTTP/DNS/TCP),回滚只作用于
<data>/agentfsoverlay 的 upper,二者对象错位。 AgregateResult在无 LLM endpoint 时恒 healthy,机制形同虚设。lastHB每轮都置time.Now(),Uptime无意义。RollbackPolicy的HealthThreshold/CooldownPeriod/AutoRollback都是死字段,只用MaxRetries。
1.2 文件追踪(internal/tracker)
- overlayfs 三层:
lower/upper/work → merged(tracker.go:156)。 captureFSState(upperDir)递归遍历 upper 并 sha256(changeset.go:56);PreAction/PostAction前后 diff(toolcall.go:86-94)。- lower 恒空(
Init只MkdirAll,从不填充)→ 无 canonical 基线可回滚。 Rollback()=RemoveAll(upper)清空全部 changesets;FileChange.Content(本应存回滚原文)从未回填。
结论:overlay/tracker 对"LLM 可达性"这主场景错位,只能作数据兜底。
1.3 通信插件真相(third_party/homeagent-sdk/example/qq/plugin.go)
- agent 对外通信全部由插件设置驱动,存于 ConfigRegistry / SQLite config.db:
qq.napcat_url、qq.listen、qq.files_dir、qq.remote_dir、dm/group_policy(plugin.go:120-130)。 - 插件在
Start()里getSetting(...)读设置(plugin.go:134-143)→ 改动配置需重载插件才生效。 cfgmgr提供config_set / config_batch_set可运行时改任意 core/插件配置(cfgmgr/plugin.go:54,103)。
1.4 LLM 源与"恢复即生效"
internal/sdk/llm_impl.go:103 ReloadFromConfig() 已存在:
cfg := cfgReg.ToConfig()从 config.db 重建(含core.llm.sources.*,见registry.go:590)mgr.Reset()→ 逐源NewLuaAdaptedProvider→ 重设默认。
即:LLM 源的"恢复即生效"钩子已经具备,缺的是"快照 + 探测 + 触发"三件事。
2. 现实环境:日志记录模式内参
看真实 systemd 托管的 HomeAgent(
/home/newqqagent)日志,目的是弄清现有的日志模型 (写哪、什么格式、工具调用打在哪),为 failback / recoveryDiag 的diag_log_scan提供准确的解析依据。
2.1 systemd 托管现状(样例)
homeagent.service: Type=simple, ExecStart=/usr/local/bin/homed -data /home/newqqagent, Restart=always, RestartSec=10
llm-mock.service: ExecStart=/usr/bin/python3 /opt/llm-mock/mock_server.py, Restart=always, RestartSec=3
- 实测数据区:
/home/newqqagent/下有log/、config.db、agentfs/(overlay merged)、snapshots/、changesets/、knowledge/、memory/、plugins/、cli.sock、adapters/、homed.log、memos.json等——日志以独立子目录log/存放,与配置/快照/knowledge 分置。 - 启动段确认:
[files] started, sandbox: /(files 沙箱=全主机/实锤);main agent started, model=mock-model base=http://127.0.0.1:18080/v1 sources=3 adapters=8(LLM 走本地 mock)。
2.2 日志目录与格式(核心)
- 目录配置:
core.log.path,默认<dataDir>/log(internal/config/registry.go:415,508)。 - 单次运行文件:每次启动新建
homed_<YYYY-MM-DD_HH-MM-SS>.log(cmd/homed/main.go:75), 写入logDir下;log.SetOutput(io.MultiWriter(os.Stderr, logFile))(main.go:80)—— 同时进 stderr(systemd 捕获到 journald/journalctl -u)与文件。 - 格式:标准 Go
log.Printf,即YYYY/MM/DD HH:MM:SS file.go:line: [module] message。 用户可看文件,也可用journalctl -u homeagent.service看同一来源(同一行)。 - 层级压缩 + 保留(
internal/log/manager.go:26-28+compressor.go):- 周度压缩 →
week_<year>-W<ww>.tar.gz;月度 →month_<yyyy-mm>.tar.gz;年度year_*.tar.gz; raw 文件正则^homed_(\d{4}-\d{2}-\d{2})_\d{2}-\d{2}-\d{2}\.log$(compressor.go:16)。 - 保留策略:
core.log.retention(default forever)、core.log.retention_months(default 3),applyRetention只留当前周 + 近 N 月(retention.go)。 实测:log/下即为homed_2026-08-03_08-03-38.log+month_2026-*.tar.gz+week_2026-W31.tar.gz,与代码一致。
- 周度压缩 →
2.3 工具调用日志打在哪儿(进程主循环 internal/agent/core/process.go)
| 位置 | 日志行内容 | 备注 |
|---|---|---|
process.go:36 |
[agent] tool call loop start, max_ctx=… target=… fixed=… mem=… ctx=… N tools, M events, personality=X, docs=K |
每轮循环开头上下文统计 |
process.go:196 |
[agent] executing tool: <name> (plugin=<p>, id=<id>) |
只记工具名/插件/id,不记 args |
process.go:226 |
[agent] tool <name> result: <截断100字符> |
结果截断到 100 字符(truncateStr) |
process.go:218 |
[agent] skip tool <name>: plugin <name> unhealthy |
插件崩溃态跳过 |
process.go:89/109/121/125 |
LLM fallback:trying provider %q (#%d) / switched active provider / provider %q marked unavailable (HTTP %d) / provider %q failed |
主循环内 LLM 商可观测 |
toolcall.go:20 |
[agent] tool %s panic: %v + debug.Stack() |
工具 panic + 完整栈 |
toolcall.go:41 |
[agent] tool %s timed out after 60s |
60s 超时 |
toolcall.go:92 |
[agent] tool %s changed %d files (changeset: %s) |
overlay changeset 摘要 |
| 插件侧 | Lua 插件 sdk.log → print("[lua-plugin] <level>: <msg>") |
模板见 cmd_debug.go:74/templates.go |
- 完整的工具入参/结果在 EventBus 事件
EventToolCall({tool, plugin, args, result, status},process.go:184/205)而非文件日志——文件日志只是执行/结果的摘要指针(结果被截断)。 - 重要观察:日志里未见 shell/cmd 之类的操作系统执行类调用摘要落盘(
[cmd]只在工具结果里), 需要的话由diag_log_scan对executing tool: cmd_*前缀做签名匹配即可。
2.4 崩溃 / 重启观察
NRestarts=0;MainPID 自 08-03 起稳定 3330844。曾出现真实重复 panic(pid 3310036):[stage] handler panic: runtime error: invalid memory address or nil pointer dereference(03:23 / 05:23 / 07:23,约每 2h), 被stages.go:139的RunStagerecover 吞掉 → 进程未真崩,systemd 未见重启。- 08:03:38 有过一次干净
[homed] stopped→ systemdStarted→ pid 3310036 → 3330844。 - 对 failback 的意义:现有崩溃防护全赖 in-process recover,真实进程级崩溃从未被监督;
且当前
Restart=always由 systemd 直绑 worker 且无 StartLimit——一旦真崩并陷入循环, systemd 每 10s 反复拉起,没有独立 failback/取证层。→ guard 取代点在此。
3. 设计演进(讨论全过程)
3.0 起点:internal/supervisor + internal/tracker 我是"保活 + 文件追踪"
- 保活 = 网络健康感知 + 内存态重置;追踪 = overlayfs 变更集 +
Rollback清空。 - 意图:LLM 不可达 → 回滚 agent 文件改动 → 自愈。
3.1 第一次纠正:回滚对象错位
- 回滚只作用于 overlayfs upper;而真正能改坏网络的路径(files 默认
/,可写/etc/resolv.conf、/etc/hosts、代理配置)与 LLM 配置(config.db、adapters 目录)都不在 overlay 内。 - → 检测命中但回滚删错对象,闭环在"回滚"这一环断掉。
3.2 第二次纠正:授权不能靠外部插件主动配合
- 阶段管道是被动通知(
RunStage收集 error,不主动拒绝),拒绝依赖各插件 handler。 - 插件是外部不可控对象 → "安全 = 插件主动授权放行"不成立。
- → 安全应做默认拒绝,由内核在
executeToolCall分发点按 ToolDef 的 capability 裁决;配置快照 + replug 只兜底"可文件化"改动。 - 中途又修正:真正的祸首不是 IO 组件,而是 LLM 源/系统网络配置;IO 组件崩溃是正交偶发轴,不并进来。
3.3 第三次纠正:LLM 源才是核心(config.db + ReloadFromConfig 已具钩子)
llm_set_source只切内存默认;真正改坏 LLM 源靠cfgmgr.config_set写 config.dbcore.llm.sources.*。ReloadFromConfig()已能"恢复即生效" → 只需补:LLM 配置快照 + 真实 liveness 探测(QuickChat)+ 在 handleFailure 里触发 RFO 复检。- 但用户进一步点明:重点是 agent 改了系统网络配置文件(proxy/DNS) —— 这类连救援 LLM 都连不上。
3.4 定稿架构一:lastFailback(独立进程,第一道防线)
- 独立进程 + agent 碰不到的 root:0600 配置 + 最小插件集(文件读写 + cmd)+ 单一任务。
- 用锚定 IP/干净 DNS 的 LLM 源绕开坏掉的 DNS/proxy。
- 若 failback 也失败 → agentfs 回滚最近快照 + 重启计算机(应用快照前系统配置)。
- 兼顾 systemd 托管冲突(主 agent 崩溃循环被 systemd 反复拉起 → failback 无限增殖)。
3.5 定稿架构二:去掉强 systemd 依赖 → 独立 guard 守护进程
- systemd 只绑定静态、极小、永驻的 guard;guard 作为父进程拉起 worker(
homed全量 agent)。 - guard 通过 IPC(unix socket / 匿名 pipe)收 worker 心跳;
父进程
waitpid天然 reap 崩溃/OOM/信号退出。 - worker 可崩可换,systemd 完全看不见 → failback 唯一性 + 无风暴天然成立。
- guard 抗 OOM:
oom_score_adj拉低,优先杀 worker 而非 guard。
3.6 定稿架构三:更纯粹 —— 同一二进制的两个 role,非独立二进制
- 不新建二进制。
homed拆两个入口:homed --role=guard:父守护进程,先起,负责拉起/监测/探活/裁决/恢复。homed --role=agent:主 agent(工作进程,guard 的子进程)。homed --role=agent --boot=failback:恢复用 agent(受限 bootstrap)。
- guard 复用现有 homeagent 基础设置;检测到崩溃时拉起 failback agent,只加载
webfetch + 文件读写 + cmd 三个插件,外加 恢复知识库插件 与 常见错误检测插件,
复用 agent 核心以
trigger_prompt初始化,要求其"尝试恢复主 agent"。 - guard 配置独立 YAML,不复用 config.db(逃生舱知识必须脱离 agent 可达区)。
3.7 recoveryDiag:崩溃取证 / 根因定位插件(省 token 关键)
-
铁律:工具返回结论,不返回原文(签名式/统计式/确定性排序)。
-
工具集:
工具 作用 diag_triageexit code/信号+uptime+头部嫌疑,快速粗分"进程死亡 vs 配置类不可达" diag_dbconfig.db integrity_check + LLM 源解析校验,逐项 ok/fail diag_log_scan时间窗内命中已知错误签名(panic/provider failed/unreachable/sql/OOM) diag_delta崩溃前 config/agentfs 与 last-good 快照 diff("改了什么") diag_loc综合正交,输出按因果强度排序的定位结论 + 推荐动作 -
崩溃类别 → 恢复分支决策表:
结论类 走分支 配置损坏类 还原 config 快照 + ReloadFromConfig + 拉活主 agent(无需 agent 推理) 系统网络类 还原 DNS/proxy → 重载主 agent(第一步小修命中即停) 进程失稳类(OOM/panic) 不还原配置,检查内存/泄漏 → 重建 worker 未知/混合 放开 webfetch/知识库,用 rescue 源 + diag_loc 摘要最小推理 -
只有"未知/混合"消耗 token,前几类近乎 0 token。
-
结论落盘
recovery_kb/diag_<ts>.json,回流知识库,同类崩溃下次直接命中,越用越省。
3.8 三层防御总览(最终)
L0 平时:核心只读探活 + 写前快照(config_set 写 core.llm.* 前、files 写 /etc 前自动留档)
L1 failback(guard 拉起,Safe-Mode 式兜底):按诊断分支逐类恢复——
还原 DNS/proxy → 还原 config 快照 + ReloadFromConfig → QuickChat 复检 → 拉起主 agent
L2 最后手段:agentfs 回滚最近快照 + 重启(应用快照前系统配置)
- failback 是所有错误(LLM 不可达 / 网络 / 配置损坏 / OOM / panic / 崩溃循环)的统一兜底层,
并非只针对某一条;
diag_*决定它走哪条恢复路径。
4. 最终架构(定稿)
4.1 进程拓扑(同一二进制,两个 role)
systemd ──▶ homed --role=guard # 父守护进程,永驻、静态、极小
├─ exec ──▶ homed --role=agent # 主 agent(可崩)
└─ exec ──▶ homed --role=agent --boot=failback # 恢复用 agent
- guard:先起,持有恢复知识(锚定源 / DNS/proxy 还原 / 配置快照 / failback 逻辑)。
- worker:guard 子进程,心跳经 IPC,崩溃由 guard reap + 判型。
- failback agent = 受限启动(webfetch+files+cmd + 恢复知识库 + recoveryDiag),单一任务"恢复主 agent",N 轮有界。
4.2 guard 独立 YAML 示例
现状实现(§5 已完成):
guard.yaml已落地为max_restarts / heartbeat_timeout / heartbeat_interval / llm_snapshot / failback_enabled / last_resort / restart_command / reboot_grace子集(cmd/homed/guard.go),恢复梯子=重试→LLM 基线恢复→failback 受限启动→last_resort。下表的 rescue 源 / trigger_prompt / N 轮 failback 推理是目标态,未实现。
role: guard
llm:
sources:
- name: rescue
base_url: http://1.2.3.4:8080 # 锚定 IP 直连,绕开被破坏的 DNS/代理
api_key: ${GUARD_RESCUE_KEY}
adapter: ... # 锚定/SNI 型适配器
recovery:
max_attempts: 4 # 可配置尝试轮次
attempt_timeout: 120s
knowledge_base: /opt/homeagent/recovery/
trigger_prompt: "你是恢复 agent,唯一任务:让主 agent 恢复运行。优先还原 DNS/代理,再重载 LLM 源…"
plugins: [webfetch, files, cmd]
last_resort:
action: reboot # restart_app | reboot
snapshot_before: true
4.3 guard 恢复状态机(N 轮有界)
guard 检测( exit≠0 | OOM | 心跳超时 | guard 锚定源探活失败 )
1. 固化追溯:exit/信号、panic、journal、OOM 上下文 → 永久区
2. 拉起 failback agent(受限插件 + rescue 源 + trigger_prompt + 知识库 + recoveryDiag)
for attempt in 1..N:
(可选先 diag_triage/diag_loc 判型)
failback 尝试恢复
guard 每轮复检主 agent 是否可达/存活
├─ 成功 → 结束,交回主 agent
└─ 超时/失败 → kill 重建,进入下一轮
3. N 轮未成 → 取消 failback agent
→ agentfs 回滚崩溃前最近快照
→ 依 yaml 执行最后手段:restart_app 或 reboot
4.4 systemd 绑定(极简,杜绝风暴)
[Unit] # guard
OnFailure=... # 备用,通常不触发(guard 稳定)
[Service] # guard
Restart=always # guard 静态稳定 → 几乎不重启
ExecStart=/usr/local/bin/homed --role=guard ...
# No StartLimit needed for loop 情况;guard 不崩
- 主 agent 崩 → 只触发 guard 内部 failback;systemd 仅看 guard,看不到 worker 崩溃循环。
- failback 唯一性 + 无启动风暴:由"guard 永驻、唯一裁决"天然保证。
- guard 抗 OOM:
oom_score_adj拉低。
5. 尚未落地的接口 / 下一步
已完成:
recoverydiag 快速检查插件(third_party/homeagent-sdk/example/recoverydiag/,外部插件)。
- 五件套全实现:
diag_triage(退出码/信号/存活粗分)、diag_db(config.db integrity_check + LLM 源字段校验,sqlite3 CLI 优先、缺失回退内核 Settings)、diag_log_scan(日志签名按类计数)、diag_delta(baseline vs 现状 diff)、diag_loc(四项结论正交排序 + 推荐恢复动作)。 - 全部确定性、返回结论非原文、
NoMemory;工具实际名带插件前缀recoverydiag_diag_*。 - 已通过 go vet + 6 个单测(对真实 config.db/日志跑通:3 个 LLM 源全 ok、日志命中 228 行主导 provider/fatal),并用仓库内重建的 plugindev 打出
dist/recovery_diagnostics_linux_amd64.hmap,装进运行实例(/home/newqqagent/plugins/recoverydiag/)加载成功、注册 5 工具。 - 结论落盘 + 知识库回流:
diag_loc增persist(缺省 true)→ 写<data_dir>/recovery_kb/diag_<ts>.json(可配recovery_kb_dir),并经sdk.Knowledge().Add以diag:<cause>:<ts>回流知识库(同类崩溃下次直接命中,越用越省);失败不阻塞工具。新增TestDiagLocPersist。 - 顺带修复:仓库内
plugindev需重编译(/usr/local/bin/plugindev是旧版、桥模板缺InjectInputSync);重编译见third_party/homeagent-sdk/tools/plugindev,go build -o ... .。 - 注意:本环境
snapshots/、changesets/均为空(direct 模式无基线)→diag_delta需显式传入 baseline_dir;未来接 guard 时由快照解包目录提供。
ConfigRegistry 快照钩子(internal/config/registry.go)。
SnapshotCoreLLM():抓全部core.llm.*键值快照;RestoreCoreLLM(snap):精确还原(快照内键回写、快照外当前键删除)。SetLLMSnapshotFile(path):写前自动留档——此后任意写core.llm.*键先把当前 LLM 配置整体快照到该文件(guard 恢复的外部基线);homed 启动即挂<data>/llm_snapshot.json。- 文件持久化对:
SaveLLMSnapshot/LoadLLMSnapshot。新增TestSnapshotRestoreCoreLLM、TestLLMSnapshotFile、TestSetLLMSnapshotFile。
homed --role{guard,agent} 入口拆分 + --boot=failback 受限插件集(cmd/homed/)。
--role=guard父守护(永驻):读独立<data>/guard.yaml(避开被改坏的 config.db),拉起 worker(--role=agent)、心跳探活 + waitpid 收割、信号转发停机。--role=agent工作进程:默认启动全插件;--boot=failback走插件白名单(core.agent.failback_plugins,缺省webui,pluginmgr,recoverydiag),内核 webfetch/files/cmd 仍内置可用。- guard 恢复梯子(已端到端实测):连续
max_restarts次 normal 崩溃 →restoreLLMBaseline(从 llm_snapshot.json 恢复 core.llm.*)→ failback 受限启动 → failback 也崩 →last_resort(restart_app / reboot)。 - 心跳:worker 每 5s 触碰
<data>/heartbeat(agent 角色 goroutine),guard 以 mtime 判定卡死(超heartbeat_timeout即 SIGKILL 计入崩溃)。 - 插件注册表加
SetLoadAllowlist(names):白名单外插件(含已注册工厂)一律跳过,failback 40 工具 → 4 工具实测通过。
现状 bug 修复(supervisor/network/tracker)。
- monitor 无 endpoint 恒 healthy(
internal/network/monitor.go+pkg/types):NetworkCheckResult增EndpointsConfigured;无探活端点时不再谎报LLMAPIReachable=true(置 false + Error),NewMonitor初始化空切片消除启动竞态;daemon 仅在配置了端点时才据此判定降级。探活端点新增core.defaults.llm_endpoints(逗号分隔,留空自动取 LLM 源 base_url),生产从此健康检查有真实目标。 - lastHB 恒置 now(
internal/supervisor/daemon.go):checkAgent接入真实存活源SetHeartbeatSource(homed 注册为 agent coreGetKernelStatus),只在确认 agent 存活时更新lastHB;无源置HealthUnknown,存活源丢失置HealthDown且不再刷新 lastHB。 - restartAgent 只改内存空转:增
SetRestartHandler(homed 注册为"清理后以exitRestartRequested=42退出"),不再假装成功;guard 把 42 识别为"请求重建"(workerRestartRequested,不计失败轮次直接重建),无 guard 时 systemdRestart=always兜底。无 handler 时仅内存复位并打日志。 - tracker 三缺陷(
internal/tracker/):captureFSStateWithContent为 before 基线捕获原文(上限 8MB)→diffStates对 modified/deleted 回填FileChange.Content(回滚用原文);新增RollbackLatest()定向撤销最近一条 changeset;Rollback()改为按时间逆序逐条逆应用(还原被改/被删文件原文、删除新增),无 changeset 时才退回整目录重置。新增 6 个测试覆盖。
剩余:
- guard ↔ agent 心跳 IPC 升级为带自诊断上报的
PING/ACK(当前为文件心跳 + 退出码)。 - supervisor 适配成 guard 的探测/裁决逻辑;
ReloadFromConfig()复用为"恢复即生效"。 - 生产实例迁移:编译新 homed、改 systemd 只托管 guard(
--role=guard),确认 failback 插件(recoverydiag)就位。
附录:真实日志节选(systemd 托管示例,/home/newqqagent)
# systemd unit
homeagent.service: Type=simple, ExecStart=/usr/local/bin/homed -data /home/newqqagent, Restart=always, RestartSec=10
llm-mock.service: ExecStart=/usr/bin/python3 /opt/llm-mock/mock_server.py, Restart=always, RestartSec=3
# 日志文件与格式(log.Printf 标准格式)
2026/08/03 08:03:38 main.go:80: [homed] logging to /home/newqqagent/log/homed_2026-08-03_08-03-38.log
2026/08/03 08:03:38 daemon.go:61: [homed] daemon started successfully
2026/08/03 08:03:39 tracker.go:65: [tracker] initialized (work=/home/newqqagent/agentfs)
# 工具调用摘要(process.go)
2026/08/03 10:03:52 process.go:36: [agent] tool call loop start, max_ctx=32768 target=26214 fixed=1306 mem=8302 ctx=16606 176 tools, 31 events, personality=true, docs=5589
... process.go:196: [agent] executing tool: <name> (plugin=<p>, id=<id>)
... process.go:226: [agent] tool <name> result: <截断100字符>
# 启动 & 沙箱
homed[3330844]: [files] started, sandbox: /
homed[3330844]: main agent started, model=mock-model base=http://127.0.0.1:18080/v1 sources=3 adapters=8
# 重复 in-process panic(被 RunStage recover 吞掉,未进程级崩溃)
homed[3310036]: [stage] handler panic: runtime error: invalid memory address or nil pointer dereference # 03:23 / 05:23 / 07:23
# 一次性干净重启(systemd 手动/触发 Started,pid 3310036 → 3330844)
homed[3310036]: [homed] stopped
systemd[1]: Stopped homeagent.service - HomeAgent - 24/7 AI Butler.
systemd[1]: Started homeagent.service - HomeAgent - 24/7 AI Butler.
# 运行状态
systemctl show homeagent.service -p NRestarts → 0
systemctl show homeagent.service -p MainPID → 3330844(自 08-03 起稳定)
# 归档
/home/newqqagent/log/: homed_2026-08-03_08-03-38.log + week_2026-W31.tar.gz + month_2026-*.tar.gz