refactor(wm): unify turn-summary extraction (<summary> first, else full text, skip empty) across ga/worldline/continue_cmd; fix hinfo delta slicing base for old-tree mixed lineage
This commit is contained in:
1 parent
a6ea3fa53c
commit
e55e198dc2
3 files changed
+28
-25
No files matched your search
@@ -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>`(无 summary 取首行)。与 ga.turn_end_callback /
|
||||
worldline 树同口径。纯函数、不依赖 worldline,供续接 opt-in 恢复工作记忆(见 restore_wm)。"""
|
||||
assistant(=一轮) → 有字才写 `[Agent] …`(`<summary>` 优先,否则全文;无字跳过)。
|
||||
与 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'```.*?```|<thinking>.*?</thinking>', '', _all_text(m), flags=re.DOTALL)
|
||||
mt = re.search(r'<summary>(.*?)</summary>', 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
|
||||
|
||||
|
||||
|
||||
+17
-13
@@ -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 消息提取 `<summary>`(GA 轮级纪要的来源);无则退化取首行。
|
||||
与 ga.turn_end_callback 的提取口径一致,使从日志重建的 history_info 贴近原值。"""
|
||||
def _summary_of(cls, msg) -> Optional[str]:
|
||||
"""与 ga.turn_end_callback 同口径: `<summary>` 优先,否则全文;无字 → None(不进 history_info)。
|
||||
截断延用 [:80](重建路径不依赖 smart_format)。"""
|
||||
text = re.sub(r'```.*?```|<thinking>.*?</thinking>', '',
|
||||
cls._all_text(msg), flags=re.DOTALL)
|
||||
m = re.search(r'<summary>(.*?)</summary>', 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] <summary>`。这正是 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
|
||||
|
||||
@@ -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'```.*?```|<thinking>.*?</thinking>', '', response.content, flags=re.DOTALL)
|
||||
rsumm = re.search(r"<summary>(.*?)</summary>", _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] 必须在回复文本中包含<summary>!\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):
|
||||
|
||||
Reference in new issue
Block a user