feat(compression): 深度压缩提示词支持轮次/最新输入占位与运行时状态注入
This commit is contained in:
parent
64902b124a
commit
57352b28fb
@ -1,4 +1,4 @@
|
|||||||
由于当前对话过长,系统正在自动压缩。请你基于已有上下文输出一份可继续执行的工作总结,要求:
|
由于当前对话过长,系统正在自动压缩(当前是第{compression_index}次压缩)。请你基于已有上下文输出一份可继续执行的工作总结,要求:
|
||||||
1) 任务目标与用户真实诉求:用户到底想让你完成什么。
|
1) 任务目标与用户真实诉求:用户到底想让你完成什么。
|
||||||
2) 已完成工作:按时间顺序列出关键步骤,包含涉及文件和核心结果。
|
2) 已完成工作:按时间顺序列出关键步骤,包含涉及文件和核心结果。
|
||||||
3) 关键决策与原因:为什么这样选,而不是别的方案。
|
3) 关键决策与原因:为什么这样选,而不是别的方案。
|
||||||
@ -6,12 +6,12 @@
|
|||||||
5) 工具调用中的重要结果/错误与修复:哪些尝试失败过,最终怎么解决的。
|
5) 工具调用中的重要结果/错误与修复:哪些尝试失败过,最终怎么解决的。
|
||||||
6) 风险与注意事项:继续工作时需要规避的问题。
|
6) 风险与注意事项:继续工作时需要规避的问题。
|
||||||
7) 当前正在执行的任务与进度:正在做什么、做到什么程度、卡在哪里。
|
7) 当前正在执行的任务与进度:正在做什么、做到什么程度、卡在哪里。
|
||||||
8) 下一步具体行动:必须足够具体,至少包含以下信息:
|
8) 对用户最新输入的响应进度与下一步行动:用户目前的最新一句输入是「{latest_user_input}」。请明确说明:针对这句输入,已经做到了什么进度,接下来该做什么。下一步行动必须足够具体,至少包含以下信息:
|
||||||
- 如果要读取文件,列出具体文件路径。
|
- 如果要读取文件,列出具体文件路径。
|
||||||
- 如果要搜索,列出搜索关键词和范围。
|
- 如果要搜索,列出搜索关键词和范围。
|
||||||
- 如果要修改文件,说明文件路径和预期改动。
|
- 如果要修改文件,说明文件路径和预期改动。
|
||||||
- 如果要运行命令,列出具体命令。
|
- 如果要运行命令,列出具体命令。
|
||||||
- 如果要验证,说明验证方式和预期结果。
|
- 如果要验证,说明验证方式和预期结果。
|
||||||
请使用中文,结构清晰,尽量具体,不要省略关键上下文。
|
{incremental_item}请使用中文,结构清晰,尽量具体,不要省略关键上下文。
|
||||||
不要考虑过往对话,只考虑当前对话任务。
|
不要考虑过往对话,只考虑当前对话任务。
|
||||||
禁止调用任何工具,必须直接输出总结内容。
|
禁止调用任何工具,必须直接输出总结内容。
|
||||||
@ -16,6 +16,28 @@ def _load_summary_prompt(web_terminal) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _build_summary_prompt(template: str, *, compression_index: int, latest_user_input: str) -> str:
|
||||||
|
"""把压缩轮次、最新用户输入、增量条目占位符替换进总结提示词模板。
|
||||||
|
|
||||||
|
- {compression_index}: 当前压缩轮次(总是替换)
|
||||||
|
- {latest_user_input}: 最新一条真实用户输入原文
|
||||||
|
- {incremental_item}: 第2次压缩起插入增量总结条目,首次压缩替换为空
|
||||||
|
"""
|
||||||
|
prompt = template or ""
|
||||||
|
latest = (latest_user_input or "").strip() or "(无)"
|
||||||
|
if compression_index > 1:
|
||||||
|
incremental_item = (
|
||||||
|
"9) 自上次压缩之后到现在新完成的工作:单独列出这段时间内新完成的工作与关键结果,"
|
||||||
|
"此前已被历次压缩总结覆盖的内容不必重复。\n"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
incremental_item = ""
|
||||||
|
prompt = prompt.replace("{compression_index}", str(compression_index))
|
||||||
|
prompt = prompt.replace("{latest_user_input}", latest)
|
||||||
|
prompt = prompt.replace("{incremental_item}", incremental_item)
|
||||||
|
return prompt
|
||||||
|
|
||||||
|
|
||||||
def _emit(sender, event_type: str, payload: Dict[str, Any]):
|
def _emit(sender, event_type: str, payload: Dict[str, Any]):
|
||||||
if not callable(sender):
|
if not callable(sender):
|
||||||
return
|
return
|
||||||
@ -65,6 +87,50 @@ def _normalize_deep_compression_records(metadata: Dict[str, Any]) -> List[Dict[s
|
|||||||
return deduped
|
return deduped
|
||||||
|
|
||||||
|
|
||||||
|
def _collect_runtime_state_lines(web_terminal, conversation_id: str) -> List[str]:
|
||||||
|
"""收集压缩后模型续接所需的运行时状态。
|
||||||
|
|
||||||
|
- 子智能体编号:仅传统模式列出(该模式下编号由模型指定且重复会创建失败);
|
||||||
|
多智能体模式不列编号(模型可通过 list_active_sub_agents 工具查询活跃实例)。
|
||||||
|
- 持久终端:只列数量与名称。
|
||||||
|
"""
|
||||||
|
lines: List[str] = []
|
||||||
|
is_multi_agent = bool(getattr(web_terminal, "multi_agent_mode", False))
|
||||||
|
if not is_multi_agent:
|
||||||
|
used: List[int] = []
|
||||||
|
manager = getattr(web_terminal, "sub_agent_manager", None)
|
||||||
|
if manager is not None:
|
||||||
|
try:
|
||||||
|
agents_map = getattr(manager, "conversation_agents", None) or {}
|
||||||
|
raw_used = agents_map.get(conversation_id) or []
|
||||||
|
used = sorted(int(x) for x in raw_used)
|
||||||
|
except Exception:
|
||||||
|
used = []
|
||||||
|
if used:
|
||||||
|
next_id = max(used) + 1
|
||||||
|
used_text = "、".join(str(x) for x in used)
|
||||||
|
lines.append(
|
||||||
|
f"- 子智能体编号:本对话已使用编号 {used_text},下一个可用编号为 {next_id}。"
|
||||||
|
"新建子智能体时请使用该编号,重复使用已用编号会创建失败。"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
lines.append("- 子智能体编号:本对话尚未创建过子智能体,下一个可用编号为 1。")
|
||||||
|
terminal_names: List[str] = []
|
||||||
|
terminal_manager = getattr(web_terminal, "terminal_manager", None)
|
||||||
|
if terminal_manager is not None:
|
||||||
|
try:
|
||||||
|
terminals = getattr(terminal_manager, "terminals", None) or {}
|
||||||
|
terminal_names = [str(name) for name in terminals.keys()]
|
||||||
|
except Exception:
|
||||||
|
terminal_names = []
|
||||||
|
if terminal_names:
|
||||||
|
names_text = "、".join(terminal_names)
|
||||||
|
lines.append(f"- 持久终端:当前共有 {len(terminal_names)} 个终端会话:{names_text}。")
|
||||||
|
else:
|
||||||
|
lines.append("- 持久终端:当前没有终端会话。")
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
def _build_guide_message(*, compression_index: int, compact_file: str) -> str:
|
def _build_guide_message(*, compression_index: int, compact_file: str) -> str:
|
||||||
"""生成文件模式的引导语:仅提示压缩文件位置,由模型自行阅读。"""
|
"""生成文件模式的引导语:仅提示压缩文件位置,由模型自行阅读。"""
|
||||||
return f"当前对话已经被第{compression_index}次压缩。请阅读 {compact_file} 并继续工作。"
|
return f"当前对话已经被第{compression_index}次压缩。请阅读 {compact_file} 并继续工作。"
|
||||||
@ -146,6 +212,7 @@ def _build_inject_guide_message(
|
|||||||
previous_records: List[Dict[str, Any]],
|
previous_records: List[Dict[str, Any]],
|
||||||
user_inputs: List[str],
|
user_inputs: List[str],
|
||||||
latest_user_input: str,
|
latest_user_input: str,
|
||||||
|
runtime_state_lines: Optional[List[str]] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""生成直接注入模式的引导语:把历次压缩总结、用户输入按顺序拼入正文。"""
|
"""生成直接注入模式的引导语:把历次压缩总结、用户输入按顺序拼入正文。"""
|
||||||
lines: List[str] = [
|
lines: List[str] = [
|
||||||
@ -169,6 +236,10 @@ def _build_inject_guide_message(
|
|||||||
lines.append(latest_user_input.strip())
|
lines.append(latest_user_input.strip())
|
||||||
else:
|
else:
|
||||||
lines.append("(无)")
|
lines.append("(无)")
|
||||||
|
if runtime_state_lines:
|
||||||
|
lines.append("")
|
||||||
|
lines.append("当前运行时状态")
|
||||||
|
lines.extend(runtime_state_lines)
|
||||||
return "\n".join(lines).strip()
|
return "\n".join(lines).strip()
|
||||||
|
|
||||||
|
|
||||||
@ -244,6 +315,7 @@ def _write_compact_file(
|
|||||||
user_inputs: List[str],
|
user_inputs: List[str],
|
||||||
latest_user_input: str,
|
latest_user_input: str,
|
||||||
previous_records: List[Dict[str, Any]],
|
previous_records: List[Dict[str, Any]],
|
||||||
|
runtime_state_lines: Optional[List[str]] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
compact_dir = project_path / ".astrion" / "compact_result"
|
compact_dir = project_path / ".astrion" / "compact_result"
|
||||||
compact_dir.mkdir(parents=True, exist_ok=True)
|
compact_dir.mkdir(parents=True, exist_ok=True)
|
||||||
@ -271,6 +343,10 @@ def _write_compact_file(
|
|||||||
lines.append(latest_user_input.strip())
|
lines.append(latest_user_input.strip())
|
||||||
else:
|
else:
|
||||||
lines.append("(无)")
|
lines.append("(无)")
|
||||||
|
if runtime_state_lines:
|
||||||
|
lines.append("")
|
||||||
|
lines.append("## 当前运行时状态")
|
||||||
|
lines.extend(runtime_state_lines)
|
||||||
file_path.write_text("\n".join(lines).strip() + "\n", encoding="utf-8")
|
file_path.write_text("\n".join(lines).strip() + "\n", encoding="utf-8")
|
||||||
return str(file_path.relative_to(project_path))
|
return str(file_path.relative_to(project_path))
|
||||||
|
|
||||||
@ -368,7 +444,20 @@ async def run_deep_compression(
|
|||||||
"job_id": job_id,
|
"job_id": job_id,
|
||||||
})
|
})
|
||||||
|
|
||||||
summary_text, summary_fail_reason = await _generate_summary(web_terminal, _load_summary_prompt(web_terminal), retries=5)
|
# 提前收集用户输入与运行时状态:总结提示词需要嵌入最新输入与压缩轮次,
|
||||||
|
# compact 文件 / 注入引导语需要追加运行时状态区块。
|
||||||
|
messages = conv_data.get("messages") or []
|
||||||
|
user_inputs = _collect_user_texts(messages)
|
||||||
|
latest_user_input = user_inputs[-1] if user_inputs else ""
|
||||||
|
user_inputs_before = len(user_inputs)
|
||||||
|
runtime_state_lines = _collect_runtime_state_lines(web_terminal, conversation_id)
|
||||||
|
|
||||||
|
summary_prompt = _build_summary_prompt(
|
||||||
|
_load_summary_prompt(web_terminal),
|
||||||
|
compression_index=target_count,
|
||||||
|
latest_user_input=latest_user_input,
|
||||||
|
)
|
||||||
|
summary_text, summary_fail_reason = await _generate_summary(web_terminal, summary_prompt, retries=5)
|
||||||
if summary_fail_reason:
|
if summary_fail_reason:
|
||||||
_emit(sender, "system_message", {"content": f"自动压缩总结失败,将使用失败占位文本:{summary_fail_reason}"})
|
_emit(sender, "system_message", {"content": f"自动压缩总结失败,将使用失败占位文本:{summary_fail_reason}"})
|
||||||
|
|
||||||
@ -386,11 +475,6 @@ async def run_deep_compression(
|
|||||||
"job_id": job_id,
|
"job_id": job_id,
|
||||||
})
|
})
|
||||||
|
|
||||||
messages = conv_data.get("messages") or []
|
|
||||||
user_inputs = _collect_user_texts(messages)
|
|
||||||
latest_user_input = user_inputs[-1] if user_inputs else ""
|
|
||||||
user_inputs_before = len(user_inputs)
|
|
||||||
|
|
||||||
relative_compact_path = _write_compact_file(
|
relative_compact_path = _write_compact_file(
|
||||||
Path(workspace.project_path),
|
Path(workspace.project_path),
|
||||||
compression_index=target_count,
|
compression_index=target_count,
|
||||||
@ -398,6 +482,7 @@ async def run_deep_compression(
|
|||||||
user_inputs=user_inputs,
|
user_inputs=user_inputs,
|
||||||
latest_user_input=latest_user_input,
|
latest_user_input=latest_user_input,
|
||||||
previous_records=previous_records,
|
previous_records=previous_records,
|
||||||
|
runtime_state_lines=runtime_state_lines,
|
||||||
)
|
)
|
||||||
|
|
||||||
cm.set_compression_state(
|
cm.set_compression_state(
|
||||||
@ -452,6 +537,7 @@ async def run_deep_compression(
|
|||||||
previous_records=previous_records,
|
previous_records=previous_records,
|
||||||
user_inputs=user_inputs,
|
user_inputs=user_inputs,
|
||||||
latest_user_input=latest_user_input,
|
latest_user_input=latest_user_input,
|
||||||
|
runtime_state_lines=runtime_state_lines,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
guide_message = _build_guide_message(
|
guide_message = _build_guide_message(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user