核心目的:不是汇报,是从每天每个团队成员跟 AI Coding CLI 的对话里挖工作流可优化点。
.gitea/workflows/daily-report.yml'5 16 * * *' UTC = Beijing 00:05("刚结束的当天")ubuntu-latest + Pin step 强制 dedicated-runner-1workflow_dispatch:date / only-user / dry-run 三参TZ=Asia/Shanghai + Gitea secrets (DAILY_REPORT_USERS_CONFIG / AI_REVIEW_GITEA_TOKEN)Pin 机制:本仓库 ubuntu-latest 匹配多台 runner(dedicated-runner-1 online + workspace-runner offline),yaml 层无法 pin 机器名 → 加 hostname -s 检查 step,调度到非 dedicated-runner-1 立刻 exit 1,避免静默跑空。
scripts/ops/daily-report/run_all.py--users-file → 2. env DAILY_REPORT_USERS_CONFIG → 3. users.local.json[{"name", "email", "ssh_host"}](workspace_path 已删,扫整个 projects/)opt-out 在远端第一步执行(PR #499 ai-review 抓的 risk 已修),命中即 exit 0,jsonl 一行不流回 runner:
[ -f "$HOME/.claude-insight-optout" ] && { echo OPTED_OUT >&2; exit 0; }
find ~/.claude/projects/*/ \ -maxdepth 2 -name '*.jsonl' \ -newermt "$DATE 00:00:00"
扫全部项目(含 worktree / slot 路径)—— PR #499 的 workspace_path 限定已废弃。
find ~/.codex/sessions/*/*/*/ \ -name 'rollout-*.jsonl' \ -newermt "$DATE 00:00:00"
Codex 按 YYYY/MM/DD 路径分目录,但跨天 turn 写在原文件 → 跟 Claude 同款 mtime 筛即可。
跨天处理:mtime ≥ DATE 00:00 自动包含跨午夜 session(mtime 在今天但 turn 时间在昨天),extract_signals.py 按 turn.timestamp.date() == target_date 行级精筛。
无 LLM 全程序代码,确定性。实测压缩比 ~1000×:110 MB raw → ~100 KB 进 LLM。
| Claude type | 处理 | 原因 |
|---|---|---|
user / assistant | 保留 | 对话主体 |
attachment | 丢 | hook 注入(system-reminder 重复几十次) |
file-history-snapshot | 丢 | 编辑器内部状态 |
queue-operation | 丢 | 队列 metadata |
ai-title / permission-mode / last-prompt | 丢 | UI / 配置 metadata |
system | 丢 | subtype 都是 hook 事件 |
行级日期:if ts.date().isoformat() != target_date: continue —— 跨天 session 的非目标日 turn 直接弃
位置关键:阶段 A 紧跟阶段 2(type+日期过滤),**先于** 阶段 3-7 跑——基于"未压缩的完整 turn 列表"算 session-level 全局统计。如果放在阶段 7 之后,元统计会基于"已被信号筛过滤的子集",违背"兜底未知"的目的(被筛掉的 turn 的 tool/file 也不进 histogram)。
每 session 输出(跟阶段 7 的 L2 信号点并列,一起喂 LLM):
{
"session_id": "...",
"turn_count": 1462,
"tool_histogram": {"Edit": 87, "Bash": 234, "WebFetch": 52, ...},
"files_touched": {"foo.py": 23, "bar.ts": 18, ...},
"external_apis_called": {"gitea.x.x.x": 14, ...},
"long_assistant_msgs": [/* top 3 turn 索引 */],
"session_duration_min": 287
}
5 类 L2 规则是白名单——只覆盖已知模式(同 file 反复改 / tool 连续报错 / 长 gap / frustration / ask cluster),未知模式(团队没列规则的)会漏:
→ LLM 在 prompt 里 先看到 全局元统计("这天调了什么工具、动了什么文件"),再看到 信号点 + 邻域上下文("这些 turn 特别值得看")—— 全局 → 局部,是 LLM 自然的认知路径。
RESULT_HEAD_CHARS = 200 字 + 总长 + statusname::file_pathname::<前 80 字 command>name::<前 60 字 pattern>name::<md5(args)[:8]>这一步压缩最猛:Bash find / 的 100 KB 输出 → 200 字。
REDACT_PATTERNS = [
r"sk-[a-zA-Z0-9]{32,}", # Anthropic API key
r"ghp_[a-zA-Z0-9]{36,}", # GitHub PAT
r"ghs_[a-zA-Z0-9]{36,}", # GitHub server token
r"ffai_[A-Za-z0-9_-]{20,}", # FFAI personal token
r"eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+", # JWT
r"-----BEGIN (RSA |EC |OPENSSH |)PRIVATE KEY-----", # PEM private key
]
命中即整 turn 丢弃(不是 mask 字符串)—— 第二道保险,opt-out 是第一道。
| 规则 | 阈值 | 含义 |
|---|---|---|
| edit_repeat | EDIT_REPEAT_WINDOW=10 / THRESHOLD=3 | 同 file 10 turn 内改 ≥ 3 次 → 缺 helper / lint / 文档 |
| tool_error_streak | TOOL_ERROR_STREAK=2 | 连续 ≥ 2 次 tool_use_error → 卡点 / 预检 step 缺失 |
| long_gap | TURN_GAP_LONG_SECONDS=1800 | turn_gap > 30 min → 用户思考 / 切走 / 决策点 |
| frustration | 正则 FRUSTRATION_PATTERNS | "不对/重做/再想想..." → 用户挑战方向 / 真实工作流痛点 |
| ask_cluster | ASK_USER_QUESTION_THRESHOLD=2 | 5 turn 内 ≥ 2 次 AskUserQuestion → 决策密集 / 需求模糊 |
| 类型 | 去重策略 |
|---|---|
edit_repeat | 同 file_path 取 count 最高的一次 |
tool_error_streak | 同一连续段(turn_index 差 ≤ 1)取 max streak |
long_gap | 每 session 留 1 个最长的 |
frustration | 每 session 至多 3 条 |
ask_cluster | 每 session 1 条 |
实测:去重前 231 信号 → 去重后 44 信号(关键避免连续 turn 重复触发)
SIGNAL_CONTEXT_TURNS = 3 → 每信号点附 7 turn(前 3 + 自己 + 后 3)claude-haiku-4-5-20251001(全名 pin,不用别名)claude --print --output-format json(OAuth via act_runner,不带 --bare)System prompt 硬性约束:核心目的是"挖工作流可优化点"不是汇报;「对话洞察」每条 ≤ 3 行;整篇 ≤ 100 行;规则匹配翻译成人话而非原样列。
chore/daily-reports-rolling(永不 merge develop)daily-reports/<user>/YYYY-MM-DD.md[daily-report] user · YYYY-W##daily-report + user/<name>压缩管线的核心是阶段 5 的 5 类信号筛规则——这是白名单,只匹配已知模式。"团队还没意识到的模式" = 规则没列 = 信号丢失。阶段 A 的实现细节见上 §④ 压缩管线,这里讲为什么这么设计 + 未来如何继续兜底。
| 方向 | 机制 | 状态 |
|---|---|---|
| A. 全局元统计 | 每 session 算 tool histogram / file 热点 / API 频次 / 长 msg 索引 + 时长——基于未压缩完整 turn,跟阶段 5 白名单并行喂 LLM | follow-up PR 实现 |
| B. 随机抽样 | L2 信号筛之外,随机抽 N 个非信号 turn 喂 LLM,让 LLM 看到"普通对话"判断是否真的没信号 | 未来扩展 |
| C. 反馈循环 | LLM 周聚合时标"哪些信号有用 / 哪些 false positive",统计调阈值,漏掉的模式补规则 | 攒 4-6 周数据后启动 |
阶段 A 位置在阶段 2 之后、阶段 3 之前,理由不只是技术(基于未压缩数据),更是认知顺序:
如果阶段 A 放在阶段 7 之后(基于已被信号筛过滤的子集),元统计就漏掉了"被规则筛掉的 turn 的信息"——等于"兜底"做成了"叠加",违背设计初衷。