diff --git a/frontends/continue_cmd.py b/frontends/continue_cmd.py index 75889806..3e926c11 100644 --- a/frontends/continue_cmd.py +++ b/frontends/continue_cmd.py @@ -145,8 +145,9 @@ def parse_native_log(path, allow_empty=False): def _derive_hist_info(history): """从 native history 重建 history_info(轮级纪要):真实用户提问 → `[USER]: …`;每条 - assistant(=一轮) → `[Agent] `(无 summary 取首行)。与 ga.turn_end_callback / - worldline 树同口径。纯函数、不依赖 worldline,供续接 opt-in 恢复工作记忆(见 restore_wm)。""" + assistant(=一轮) → 有字才写 `[Agent] …`(`` 优先,否则全文;无字跳过)。 + 与 ga.turn_end_callback / worldline 同口径;截断延用 [:80]。纯函数、不依赖 worldline, + 供续接 opt-in 恢复工作记忆(见 restore_wm)。""" def _all_text(m): c = m.get('content') if isinstance(m, dict) else None if isinstance(c, str): return c @@ -170,10 +171,9 @@ def _derive_hist_info(history): elif role == 'assistant': txt = re.sub(r'```.*?```|.*?', '', _all_text(m), flags=re.DOTALL) mt = re.search(r'(.*?)', txt, re.DOTALL) - s = mt.group(1).strip()[:80] if (mt and mt.group(1).strip()) else '' - if not s: - s = next((ln.strip()[:80] for ln in txt.splitlines() if ln.strip()), '(无摘要)') - out.append(f'[Agent] {s}') + raw = (mt.group(1) if mt else txt).strip() + if raw: + out.append('[Agent] ' + raw.replace('\n', '')[:80]) return out diff --git a/frontends/worldline.py b/frontends/worldline.py index de458f4c..0a49eccf 100644 --- a/frontends/worldline.py +++ b/frontends/worldline.py @@ -198,7 +198,11 @@ class RewindStore: hinfo = None hinfo_len = None if hist_info is not None: - parent_hlen = len(self.rebuild_hist_info(parent)) if parent is not None else 0 + # 老树节点按旧口径存过「(无摘要)」条目,stored 长度会偏大切丢新纪要; + # history 在手时用同口径派生 parent 前缀定切片基点(前缀派生恒为全量前缀)。 + parent_hlen = 0 if parent is None else ( + len(self._derive_hist_info(history[:parent_len])) if history is not None + else len(self.rebuild_hist_info(parent))) hdelta = list(hist_info[parent_hlen:]) hinfo_len = len(hist_info) hinfo = self._put_blob( @@ -713,24 +717,22 @@ class RewindStore: return "" @classmethod - def _summary_of(cls, msg) -> str: - """从 assistant 消息提取 ``(GA 轮级纪要的来源);无则退化取首行。 - 与 ga.turn_end_callback 的提取口径一致,使从日志重建的 history_info 贴近原值。""" + def _summary_of(cls, msg) -> Optional[str]: + """与 ga.turn_end_callback 同口径: `` 优先,否则全文;无字 → None(不进 history_info)。 + 截断延用 [:80](重建路径不依赖 smart_format)。""" text = re.sub(r'```.*?```|.*?', '', cls._all_text(msg), flags=re.DOTALL) m = re.search(r'(.*?)', text, re.DOTALL) - if m and m.group(1).strip(): - return m.group(1).strip()[:80] - for line in text.splitlines(): - if line.strip(): - return line.strip()[:80] - return "(无摘要)" + raw = (m.group(1) if m else text).strip() + if not raw: + return None + return raw.replace('\n', '')[:80] @classmethod def _derive_hist_info(cls, history) -> list: """从对话历史重建 `handler.history_info`(轮级纪要):真实用户提问 → `[USER]: …`; - 每条 assistant(=一轮) → `[Agent] `。这正是 GA 原本构建 history_info 的 - 方式,故 reconcile 从日志吸收外部/老会话轮次时可据此把工作记忆补回(否则续接后 + 每条 assistant(=一轮) → 有字才写 `[Agent] …`(与 ga.turn_end_callback 同口径)。 + reconcile 从日志吸收外部/老会话轮次时可据此把工作记忆补回(否则续接后 rewind 到这些轮,纪要会缺失 → 串味)。""" out: list = [] for m in history: @@ -742,7 +744,9 @@ class RewindStore: if q: out.append(f"[USER]: {q}") elif role == "assistant": - out.append(f"[Agent] {cls._summary_of(m)}") + s = cls._summary_of(m) + if s is not None: + out.append('[Agent] ' + s) return out @classmethod diff --git a/ga.py b/ga.py index d7ab8649..1611ebe4 100644 --- a/ga.py +++ b/ga.py @@ -571,13 +571,12 @@ class GenericAgentHandler(BaseHandler): def turn_end_callback(self, response, tool_calls, tool_results, turn, next_prompt, exit_reason): _c = re.sub(r'```.*?```|.*?', '', response.content, flags=re.DOTALL) rsumm = re.search(r"(.*?)", _c, re.DOTALL) - if rsumm: summary = rsumm.group(1).strip() - else: - tc = tool_calls[0]; clean_args = {k: v for k, v in tc['args'].items() if not k.startswith('_')} # at least one because no_tool - summary = _c.strip() or smart_format("直接回答了用户问题" if tc['tool_name'] == 'no_tool' else f"{tc['tool_name']}, args: {clean_args}", max_str_len=40) + raw = (rsumm.group(1) if rsumm else _c).strip() + if raw: + summary = smart_format(raw.replace('\n', ''), max_str_len=80) + self.history_info.append('[Agent] ' + summary) + if not rsumm and tool_calls and tool_calls[0]['tool_name'] != 'no_tool': next_prompt += "\n\n\n[TIPS] 必须在回复文本中包含!\n\n" - summary = smart_format(summary.replace('\n', ''), max_str_len=80) - self.history_info.append(f'[Agent] {summary}') _plan = self._in_plan_mode() if turn % 175 == 0 and (not _plan):