Commit beb80457 authored by 郭阔's avatar 郭阔

feat(panel): real-time phase from agent-written heartbeat file

Replace guessed phases with ground truth: harvest.sh seeds
logs/<source>.phase=采集中 and the prompt requires the agent to overwrite
it the moment it enters each stage (采集中/摘要生成/推送/收尾); trap deletes
it on exit. Panel reads the phase file first, falls back to factual disk
signals (registry/.summaries/.manifest mtimes), and shows neutral 运行中
when no signal exists — no more inference, no more (推定) labels.
parent 31945a77
...@@ -88,12 +88,16 @@ _nh_quit_instance() { ...@@ -88,12 +88,16 @@ _nh_quit_instance() {
[ -f "$skill_dir/scripts/instance-quit.js" ] || return 0 [ -f "$skill_dir/scripts/instance-quit.js" ] || return 0
[ -n "$_nh_port" ] || return 0 [ -n "$_nh_port" ] || return 0
CDP_PORT_OVERRIDE="$_nh_port" node "$skill_dir/scripts/instance-quit.js" 2>&1 | sed 's/^/ [instance] /' CDP_PORT_OVERRIDE="$_nh_port" node "$skill_dir/scripts/instance-quit.js" 2>&1 | sed 's/^/ [instance] /'
rm -f "$LOG_DIR/${SOURCE}.phase" 2>/dev/null
return 0 return 0
} }
trap _nh_quit_instance EXIT trap _nh_quit_instance EXIT
START_EPOCH=$(date +%s) START_EPOCH=$(date +%s)
# 阶段文件:面板实时读取的真实阶段(初始由本脚本写入,后续由采集会话按阶段更新)
echo "采集中" > "$LOG_DIR/${SOURCE}.phase"
# --- 运行 claude 无头模式,输出 JSON,带硬超时看门狗 --- # --- 运行 claude 无头模式,输出 JSON,带硬超时看门狗 ---
# 无人在场时防挂死:claude 卡住(网络/服务无响应)最迟 MAX_RUNTIME_MIN 分钟被终止, # 无人在场时防挂死:claude 卡住(网络/服务无响应)最迟 MAX_RUNTIME_MIN 分钟被终止,
# 走正常失败路径 → trap 回收浏览器实例,不阻塞次日 launchd 触发。 # 走正常失败路径 → trap 回收浏览器实例,不阻塞次日 launchd 触发。
...@@ -114,6 +118,9 @@ claude -p \ ...@@ -114,6 +118,9 @@ claude -p \
- 不要使用后台 shell、nohup、& 或任何形式的异步执行。 - 不要使用后台 shell、nohup、& 或任何形式的异步执行。
- 所有文章抓取完毕后再依次生成摘要并写入 Obsidian,全部完成后再返回结果。 - 所有文章抓取完毕后再依次生成摘要并写入 Obsidian,全部完成后再返回结果。
- 如果某篇文章抓取失败,跳过它继续下一篇,不要因此中断整个任务。 - 如果某篇文章抓取失败,跳过它继续下一篇,不要因此中断整个任务。
- 阶段实时上报(面板展示用,必须严格执行):每当进入新阶段,第一时间执行
echo '<阶段>' > ~/NewsHarvester/logs/${SOURCE}.phase
阶段只允许三个值:采集中(列表页浏览与逐篇抓取)、摘要生成(写中文摘要)、推送/收尾(finalize 与推送)。
- 列表页候选与 registry(近7天)对照后,若所有候选均已在库(无任何新文章):立即收尾返回结果——articles 留空、succeeded_count=0、source_exhausted=true、exhaustion_reason="all_seen",不要再做单篇核验、摘要或重复确认,这是正常且成功的结果。 - 列表页候选与 registry(近7天)对照后,若所有候选均已在库(无任何新文章):立即收尾返回结果——articles 留空、succeeded_count=0、source_exhausted=true、exhaustion_reason="all_seen",不要再做单篇核验、摘要或重复确认,这是正常且成功的结果。
${RESULT_REQUIREMENTS}" \ ${RESULT_REQUIREMENTS}" \
> "$JSON_OUT" 2> "$RAW_OUT" & > "$JSON_OUT" 2> "$RAW_OUT" &
......
...@@ -558,10 +558,13 @@ def _active_harvests(): ...@@ -558,10 +558,13 @@ def _active_harvests():
def _harvest_phase(source): def _harvest_phase(source):
"""返回正在运行任务的 (开始时间, 阶段名)。阶段由磁盘信号推断,不打扰采集进程: """返回正在运行任务的 (开始时间, 阶段名)。
runlock 建立 = 任务开始;vault 当日目录里 .manifest.json 比开始新 = 浏览器抓取 阶段以真实信号为准,不做推测:
已完成;.summaries.json 更新 = 摘要生成中;registry.json 更新 = 推送/收尾。 1. <source>.phase 文件 —— harvest.sh 写初始"采集中",采集会话每进入新阶段
即时更新(采集中/摘要生成/推送收尾),任务退出时删除;
2. 兜底用当日目录磁盘信号(registry/.summaries/.manifest 的 mtime);
3. 都没有 → 中性"运行中"。
""" """
port = _site_cdp_port(source.split("-")[0]) port = _site_cdp_port(source.split("-")[0])
lockdir = WORK_DIR / "runlocks" lockdir = WORK_DIR / "runlocks"
...@@ -575,6 +578,17 @@ def _harvest_phase(source): ...@@ -575,6 +578,17 @@ def _harvest_phase(source):
except OSError: except OSError:
return None, None return None, None
# 1) 真实阶段文件(进程自己写的)
phase_file = LOG_DIR / f"{source}.phase"
if phase_file.exists():
try:
txt = phase_file.read_text(encoding="utf-8").strip()
except OSError:
txt = ""
if txt:
return start, txt
# 2) 磁盘信号兜底(仍是事实,非推测)
def mtime(p): def mtime(p):
try: try:
return datetime.fromtimestamp(p.stat().st_mtime) return datetime.fromtimestamp(p.stat().st_mtime)
...@@ -599,11 +613,8 @@ def _harvest_phase(source): ...@@ -599,11 +613,8 @@ def _harvest_phase(source):
mani = mtime(day_dir / ".manifest.json") mani = mtime(day_dir / ".manifest.json")
if mani and mani > start: if mani and mani > start:
return start, "摘要生成" return start, "摘要生成"
# 无任何抓取信号(常见于重复采集:文章全被去重,manifest 不重写): # 3) 无信号:中性显示,不猜
# 超过 10 分钟仍无新文件,推定已进入摘要/处理阶段 return start, "运行中"
if (datetime.now() - start).total_seconds() > 600:
return start, "摘要生成(推定)"
return start, "采集中"
# ── 路由 ─────────────────────────────────────────────────── # ── 路由 ───────────────────────────────────────────────────
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment