diff --git a/assets/sys_prompt.txt b/assets/sys_prompt.txt index 9870dbb0..d0a77d18 100644 --- a/assets/sys_prompt.txt +++ b/assets/sys_prompt.txt @@ -1,7 +1,7 @@ # Role: 物理级全能执行者 你拥有文件读写、脚本执行、用户浏览器JS注入、系统级干预的物理操作权限。禁止推诿"无法操作"——不空想,用工具探测。 ## 行动原则 -调用工具前先推演:当前阶段、上步结果是否符合预期、下步策略,必须在回复文本中用输出极简总结。 +调用工具前先推演:当前阶段、上步结果是否符合预期、下步策略,必须在回复文本头部用小于30词输出极简总结。 - 探测优先:失败时先充分获取信息(日志/状态/上下文),关键信息存入工作记忆,再决定重试或换方案。不可逆操作先询问用户。 - 失败升级:1次→读错误理解原因,2次→探测环境状态,3次→深度分析后换方案或问用户。禁止无新信息的重复操作。 - 行动也是认识:边推进边用现实反馈校正方向,内部解释与计划只作暂时假说。根据任务选择高级sop调用 diff --git a/assets/sys_prompt_en.txt b/assets/sys_prompt_en.txt index c0411ab9..6cb9b6cf 100644 --- a/assets/sys_prompt_en.txt +++ b/assets/sys_prompt_en.txt @@ -2,6 +2,7 @@ You have full physical access: file I/O, script execution, browser JS injection, and system-level intervention. Never deflect with "can't do it" — don't speculate, use tools to probe. Summarize and reply in user's language or follow user's prompt. ## Action Principles -Before each tool call, reason: current phase, whether the last result met expectations, and next strategy and in reply text of each turn. +Before each tool call, reason: current phase, whether the last result met expectations, and next strategy in reply text of each turn. + (<30 tokens) before reply text. - Probe first: on failure, gather sufficient info (logs/status/context), store key findings in working memory, then decide to retry or pivot. Ask the user before irreversible operations. - Failure escalation: 1st fail → read error and understand cause; 2nd → probe environment state; 3rd → deep analysis then switch approach or ask user. Never repeat an action without new information. \ No newline at end of file diff --git a/frontends/stapp.py b/frontends/stapp.py index 83658f29..25eda792 100644 --- a/frontends/stapp.py +++ b/frontends/stapp.py @@ -288,12 +288,18 @@ def _render_stat_badge(is_running): secs = max(0, int(end_ts - st.session_state.task_start_ts)) stats = dict(llmcore.STATS) short = lambda n: f'{n / 1000:.0f}k' if n >= 1000 else str(n) + _p = [] + if stats.get('t_start') and stats.get('t_ttft') is not None and stats['t_ttft'] != stats['t_start']: + _p.append(f"ttft{stats['t_ttft'] - stats['t_start']:.1f}s") + _gen = (stats['t_end'] - max(stats['t_ttft'], stats['t_start'])) if (stats.get('t_end') and stats.get('t_ttft') is not None) else None + if _gen and stats.get('out'): + _p.append(f"{stats['out'] / _gen:.0f}t/s") + _tail = (' │ ' + '·'.join(_p)) if _p else '' usage = ((f"{stats['session']} │ " if stats.get('session') else '') + f"{short(stats['ctx'])} chars·{stats['msgs']}msgs │ " - f"in {short(stats.get('inp', 0))} toks·cached{short(stats.get('cached', 0))}·out{short(stats.get('out', 0))} │ " + f"in {short(stats.get('inp', 0))} toks·cached{short(stats.get('cached', 0))}·out{short(stats.get('out', 0))}{_tail}" if 'ctx' in stats else '') - st.markdown(f'
{usage}{secs // 60}:{secs % 60:02d}
', - unsafe_allow_html=True) + st.markdown(f'
{usage} │ {secs // 60}:{secs % 60:02d}
', unsafe_allow_html=True) if not hasattr(agent, "_ui_messages"): agent._ui_messages = st.session_state.get("messages", []) @@ -359,6 +365,7 @@ if prompt: st.session_state.reply_ts = "" st.session_state.current_prompt = "" st.session_state.last_reply_time = int(time.time()) + st.session_state.show_full_history = False st.rerun() def _slash_missing(name): st.session_state.messages.extend([ @@ -379,6 +386,11 @@ if prompt: target = sessions[idx][0] if 0 <= idx < len(sessions) else None result = handle_frontend_command(agent, cmd) history = extract_ui_messages(target) if target and result.startswith('✅') else None + if history: + for x in history: + if x['role'] == 'assistant' and len(x['content']) > 120_000: + m = re.search(r'\**LLM Running \(Turn \d+\) \.\.\.\**', x['content'][-120_000:]) + x['content'] = x['content'][-120_000 + m.start():] if m else '' tail = [{"role": "assistant", "content": result, "time": ts}] if history: st.session_state.messages[:] = history + tail else: st.session_state.messages.extend([{"role": "user", "content": cmd, "time": ts}] + tail) diff --git a/llmcore.py b/llmcore.py index 38e3a7f6..bed5ce6d 100644 --- a/llmcore.py +++ b/llmcore.py @@ -448,6 +448,8 @@ def _stream_with_retry(sess, url, headers, payload, parse_fn): return None if ra and ra > cap else max(0.5, ra or min(30.0, 3.0 * (2 ** attempt))) for attempt in range(sess.max_retries + 1): streamed = False + STATS.update(t_start=time.time(), t_ttft=None) + if not sess.stream: STATS['t_ttft'] = STATS['t_start'] try: with requests.post(url, headers=headers, json=payload, stream=sess.stream, timeout=(sess.connect_timeout, sess.read_timeout), proxies=sess.proxies, verify=sess.verify) as r: @@ -464,9 +466,15 @@ def _stream_with_retry(sess, url, headers, payload, parse_fn): yield err; return [{"type": "text", "text": err}] gen = parse_fn(r) try: - while True: chunk = next(gen); streamed = True; yield chunk + while True: + if getattr(sess, 'should_stop', None) and sess.should_stop(): + STATS['t_end'] = time.time(); return [] + chunk = next(gen) + if chunk and STATS.get('t_ttft') is None: STATS['t_ttft'] = time.time() + streamed = True; yield chunk except StopIteration as e: if not e.value and not streamed: raise requests.ConnectionError("empty response") + STATS['t_end'] = time.time() return e.value or [] except (requests.Timeout, requests.ConnectionError, requests.exceptions.ChunkedEncodingError) as e: err = f"!!!Error: {type(e).__name__}: {e}" if str(e) else f"!!!Error: {type(e).__name__}"