This repository has been archived on 2026-08-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
openclaw/cron/scripts/ai-conversation-logger.sh
T

216 lines
7.4 KiB
Bash
Executable File

#!/bin/bash
# AI 对话记录定时任务
# 功能:每天凌晨整理与各个 AI 代理的对话记录
set -e
OUTPUT_BASE="$HOME/work/zq/30.资源/AI会话历史"
ZQ_DIR="$HOME/work/zq"
OPENCLAW_DIR="$HOME/.openclaw"
LOG_FILE="$HOME/work/backup/ai-conversation-logger.log"
export YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
export CURRENT_MONTH=$(date -d "yesterday" +%Y-%m)
# 确保日志目录存在
mkdir -p "$(dirname "$LOG_FILE")"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log "=== AI 对话记录整理开始 ==="
log "处理日期: $YESTERDAY"
# 从 NAS 拉取最新内容
log "从 NAS 拉取最新内容..."
cd "$ZQ_DIR" || { log "错误:无法进入 $ZQ_DIR"; exit 1; }
rclone sync "nas-webdav:/home/Drive/工作区/张青/工作区/" "$ZQ_DIR/" \
--exclude ".git/" --exclude ".gitignore" \
--log-level INFO >> "$LOG_FILE" 2>&1 || log "警告:rclone 拉取失败"
log "同步完成"
mkdir -p "$OUTPUT_BASE"
AGENTS=$(ls -1 "$OPENCLAW_DIR/agents/" 2>/dev/null | grep -v "^\." || true)
if [[ -z "$AGENTS" ]]; then
log "未找到任何代理目录"
exit 0
fi
log "发现代理: $AGENTS"
# Python 脚本处理对话记录
python3 << 'PYEOF'
import json
import os
import re
from datetime import datetime
from pathlib import Path
OUTPUT_BASE = os.path.expanduser("~/work/zq/30.资源/AI会话历史")
OPENCLAW_DIR = os.path.expanduser("~/.openclaw/agents")
YESTERDAY = os.environ.get('YESTERDAY', '2026-03-05')
CURRENT_MONTH = os.environ.get('CURRENT_MONTH', '2026-03')
def clean_user_message(text):
if not text: return ""
if '[Feishu' in text:
match = re.search(r'\[Feishu[^\]]+\]\s*[^:]+:\s*(.+)', text, re.DOTALL)
if match:
content = match.group(1)
content = re.sub(r'\s*\[message_id:[^\]]+\]\s*$', '', content)
return content.strip()
if text.startswith('System:') and '[Feishu' not in text: return ""
return text.strip()
def extract_tags(messages):
tags = set()
all_text = ' '.join([m['content'] for m in messages]).lower()
tag_rules = {
'AI/LLM': ['ai', 'llm', '模型', 'gpt', 'claude', '推理', 'token'],
'OpenClaw': ['openclaw', 'gateway', 'agent', '代理', '会话'],
'知识库': ['知识库', 'kb', '归档', 'memory'],
'编程开发': ['代码', 'code', '开发', 'git', 'python', 'bash'],
'部署运维': ['部署', 'deploy', '服务器', 'docker', '配置'],
'游戏开发': ['ue', 'unreal', 'unity', '游戏', '3d'],
'iOS/Apple': ['ios', 'mac', 'xcode', 'apple', 'swift'],
'问题解决': ['报错', '错误', 'error', '怎么', '如何'],
'技术探讨': ['探讨', '讨论', '原理', '架构', '研究'],
'任务规划': ['计划', '规划', 'todo', '待办', '任务'],
'定时任务': ['cron', '定时', '自动', '备份'],
'飞书相关': ['飞书', 'feishu'],
}
for tag, keywords in tag_rules.items():
for kw in keywords:
if kw in all_text:
tags.add(tag)
break
return sorted(list(tags))[:5] if tags else ['对话记录']
def parse_jsonl(filepath):
messages = []
with open(filepath, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line: continue
try:
entry = json.loads(line)
except: continue
if entry.get('type') != 'message': continue
msg = entry.get('message', {})
role = msg.get('role', '')
if role not in ['user', 'assistant']: continue
content = msg.get('content', '')
if isinstance(content, list):
texts = []
for p in content:
if isinstance(p, dict) and p.get('type') == 'text' and p.get('type') not in ['thinking', 'toolCall']:
t = p.get('text', '')
if t: texts.append(t)
content = '\n\n'.join(texts)
if role == 'user': content = clean_user_message(content)
if content and content.strip():
messages.append({'role': role, 'content': content.strip(), 'timestamp': entry.get('timestamp', 0)})
return messages
def gen_md(agent, date, messages):
if not messages: return "", 0
messages.sort(key=lambda x: x.get('timestamp', 0))
seen, unique = set(), []
for m in messages:
k = (m['role'], m['content'][:100])
if k not in seen: seen.add(k); unique.append(m)
tags = extract_tags(unique)
tags_str = ', '.join([f'"{t}"' for t in tags])
md = f"""---
agent: {agent}
date: {date}
generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
messages: {len(unique)}
tags: [{tags_str}]
---
# {agent} 对话记录 - {date}
**标签**: {', '.join(tags)}
共 {len(unique)} 条消息
---
"""
for m in unique:
icon = "👤" if m['role'] == 'user' else "🤖"
name = "用户" if m['role'] == 'user' else "助手"
md += f"\n### {icon} {name}\n\n{m['content']}\n\n---\n"
return md, len(unique)
agents = [d for d in os.listdir(OPENCLAW_DIR) if not d.startswith('.')]
for agent in agents:
sdir = os.path.join(OPENCLAW_DIR, agent, "sessions")
if not os.path.isdir(sdir): continue
files = []
for f in os.listdir(sdir):
if f.endswith('.jsonl') and not f.endswith('.lock') and '.deleted' not in f:
fp = os.path.join(sdir, f)
if datetime.fromtimestamp(os.stat(fp).st_mtime).strftime('%Y-%m-%d') == YESTERDAY:
files.append(fp)
if not files: continue
msgs = []
for fp in files: msgs.extend(parse_jsonl(fp))
if not msgs: continue
md, cnt = gen_md(agent, YESTERDAY, msgs)
if md:
odir = os.path.join(OUTPUT_BASE, agent, CURRENT_MONTH)
os.makedirs(odir, exist_ok=True)
ofile = os.path.join(odir, f"对话-{YESTERDAY}.md")
with open(ofile, 'w', encoding='utf-8') as f: f.write(md)
print(f"{agent}: {cnt} 条消息, 标签: {extract_tags(msgs)}")
# 更新月度汇总
for agent in agents:
mdir = os.path.join(OUTPUT_BASE, agent, CURRENT_MONTH)
if not os.path.isdir(mdir): continue
dfiles = list(Path(mdir).glob("对话-*.md"))
if not dfiles: continue
sfile = os.path.join(mdir, f"汇总-{CURRENT_MONTH}.md")
smd = f"""---
agent: {agent}
month: {CURRENT_MONTH}
generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
days: {len(dfiles)}
---
# {agent} 月度汇总 - {CURRENT_MONTH}
## 概览
- 对话天数:{len(dfiles)} 天
- 生成时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
## 每日对话索引
"""
for df in sorted(dfiles):
d = df.stem.replace("对话-", "")
smd += f"- [{d}](./对话-{d}.md)\n"
with open(sfile, 'w', encoding='utf-8') as f: f.write(smd)
print(f"{agent}: 更新汇总")
print("处理完成!")
PYEOF
log "=== AI 对话记录整理完成 ==="
# 推送到 Git 和 NAS
cd "$ZQ_DIR"
CHANGES=$(git status --porcelain 2>/dev/null || true)
if [[ -n "$CHANGES" ]]; then
log "提交到 Git..."
git add -A >> "$LOG_FILE" 2>&1
git commit -m "AI对话记录: $YESTERDAY" >> "$LOG_FILE" 2>&1 || true
git push origin main >> "$LOG_FILE" 2>&1 || log "警告:Git push 失败"
fi
log "推送到 NAS..."
rclone copy "$OUTPUT_BASE" "nas-webdav:/home/Drive/工作区/张青/工作区/30.资源/AI会话历史/" \
--log-level INFO >> "$LOG_FILE" 2>&1 || log "警告:rclone 推送失败"
log "推送完成"