Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions agentmain.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@ def run(self):
log_dir = os.path.join(script_dir, 'temp/reflect_logs'); os.makedirs(log_dir, exist_ok=True)
script_name = os.path.splitext(os.path.basename(args.reflect))[0]
open(os.path.join(log_dir, f'{script_name}_{datetime.now():%Y-%m-%d}.log'), 'a', encoding='utf-8').write(f'[{datetime.now():%m-%d %H:%M}]\n{result}\n\n')
try:
from scheduled_context import record_scheduled_output
record_scheduled_output(task, result, script_dir)
except Exception as e:
print(f'[Reflect] scheduled context error: {e}')
if (on_done := getattr(mod, 'on_done', None)):
try: on_done(result)
except Exception as e: print(f'[Reflect] on_done error: {e}')
Expand Down
12 changes: 11 additions & 1 deletion frontends/chatapp_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ def build_help_text(commands=HELP_COMMANDS):
SUMMARY_RE = re.compile(r"<summary>\s*(.*?)\s*</summary>", re.DOTALL)


def with_scheduled_context(text):
"""Prepend recent scheduled-task outputs to a user message."""
try:
from scheduled_context import recent_scheduled_context
ctx = recent_scheduled_context(PROJECT_ROOT)
except Exception:
ctx = ""
return f"{ctx}\n\n{text}" if ctx else text


def clean_reply(text):
for pat in TAG_PATS:
text = re.sub(pat, "", text or "", flags=re.DOTALL)
Expand Down Expand Up @@ -307,7 +317,7 @@ async def run_agent(self, chat_id, text, **ctx):
self.user_tasks[chat_id] = state
try:
await self.send_text(chat_id, "思考中...", **ctx)
dq = self.agent.put_task(f"{FILE_HINT}\n\n{text}", source=self.source)
dq = self.agent.put_task(with_scheduled_context(f"{FILE_HINT}\n\n{text}"), source=self.source)
last_ping = time.time()
while state["running"]:
try:
Expand Down
4 changes: 2 additions & 2 deletions frontends/fsapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
sys.path.insert(0, PROJECT_ROOT)
os.chdir(PROJECT_ROOT)
from agentmain import GeneraticAgent
from frontends.chatapp_common import format_restore
from frontends.chatapp_common import format_restore, with_scheduled_context
from frontends.continue_cmd import handle_frontend_command as handle_continue_frontend, reset_conversation
from llmcore import mykeys

Expand Down Expand Up @@ -604,7 +604,7 @@ def run_agent():
if not hasattr(agent, '_turn_end_hooks'): agent._turn_end_hooks = {}
agent._turn_end_hooks[hook_key] = _make_task_hook(card, done_event, on_final)
try:
agent.put_task(user_input, source="feishu", images=image_paths)
agent.put_task(with_scheduled_context(user_input), source="feishu", images=image_paths)
start = time.time()
while not done_event.wait(timeout=3):
if not user_tasks.get(open_id, {}).get("running", True):
Expand Down
79 changes: 79 additions & 0 deletions scheduled_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Scheduled task output journal: records what the reflect runner just sent.
Frontends read recent entries and inject them into the next user-prompt so the
agent can answer follow-up questions about its own scheduled output.
"""
import json, os, re, time

_ROOT = os.path.dirname(os.path.abspath(__file__))


def _compact(text, limit=1800):
text = re.sub(r"\s+", " ", str(text or "")).strip()
return text[:limit] + ("..." if len(text) > limit else "")


def _field(text, name):
m = re.search(rf"^\[{re.escape(name)}\]\s*(.+)$", text or "", re.M)
return m.group(1).strip() if m else ""


def _read_report(path):
if not path or not os.path.exists(path):
return ""
try:
with open(path, encoding="utf-8", errors="replace") as f:
return f.read()
except Exception:
return ""


def record_scheduled_output(task, result, root=_ROOT, keep=20):
"""Append one entry to the journal after a reflect-mode task finishes."""
task_name = _field(task, "定时任务")
if not task_name:
return
report_path = _field(result, "报告路径") or _field(task, "报告路径")
body = _read_report(report_path) if report_path else result
entry = {
"ts": time.time(),
"task": task_name,
"report": report_path,
"text": _compact(body),
}
path = os.path.join(root, "temp", "scheduled_context.jsonl")
os.makedirs(os.path.dirname(path), exist_ok=True)
rows = []
if os.path.exists(path):
with open(path, encoding="utf-8", errors="replace") as f:
rows = [line for line in f if line.strip()][-keep + 1:]
rows.append(json.dumps(entry, ensure_ascii=False) + "\n")
tmp = path + ".tmp"
with open(tmp, "w", encoding="utf-8") as f:
f.writelines(rows)
os.replace(tmp, path)


def recent_scheduled_context(root=_ROOT, max_age=6 * 3600, limit=3):
"""Return formatted context string from recent scheduled outputs, or ''."""
path = os.path.join(root, "temp", "scheduled_context.jsonl")
if not os.path.exists(path):
return ""
now = time.time()
entries = []
with open(path, encoding="utf-8", errors="replace") as f:
for line in f:
try:
item = json.loads(line)
item["ts"] = float(item.get("ts", 0))
except Exception:
continue
if now - item["ts"] <= max_age:
entries.append(item)
if not entries:
return ""
parts = ["### Recent scheduled task outputs"]
for item in entries[-limit:]:
when = time.strftime("%m-%d %H:%M", time.localtime(item["ts"]))
report = f"\nReport: {item['report']}" if item.get("report") else ""
parts.append(f"[{when}] {item.get('task', 'scheduled task')}{report}\n{item.get('text', '')}")
return "\n\n".join(parts)