feat(compression): 深度压缩提示词支持轮次/最新输入占位与运行时状态注入
This commit is contained in:
parent
64902b124a
commit
57352b28fb
@ -1,4 +1,4 @@
|
||||
由于当前对话过长,系统正在自动压缩。请你基于已有上下文输出一份可继续执行的工作总结,要求:
|
||||
由于当前对话过长,系统正在自动压缩(当前是第{compression_index}次压缩)。请你基于已有上下文输出一份可继续执行的工作总结,要求:
|
||||
1) 任务目标与用户真实诉求:用户到底想让你完成什么。
|
||||
2) 已完成工作:按时间顺序列出关键步骤,包含涉及文件和核心结果。
|
||||
3) 关键决策与原因:为什么这样选,而不是别的方案。
|
||||
@ -6,12 +6,12 @@
|
||||
5) 工具调用中的重要结果/错误与修复:哪些尝试失败过,最终怎么解决的。
|
||||
6) 风险与注意事项:继续工作时需要规避的问题。
|
||||
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]):
|
||||
if not callable(sender):
|
||||
return
|
||||
@ -65,6 +87,50 @@ def _normalize_deep_compression_records(metadata: Dict[str, Any]) -> List[Dict[s
|
||||
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:
|
||||
"""生成文件模式的引导语:仅提示压缩文件位置,由模型自行阅读。"""
|
||||
return f"当前对话已经被第{compression_index}次压缩。请阅读 {compact_file} 并继续工作。"
|
||||
@ -146,6 +212,7 @@ def _build_inject_guide_message(
|
||||
previous_records: List[Dict[str, Any]],
|
||||
user_inputs: List[str],
|
||||
latest_user_input: str,
|
||||
runtime_state_lines: Optional[List[str]] = None,
|
||||
) -> str:
|
||||
"""生成直接注入模式的引导语:把历次压缩总结、用户输入按顺序拼入正文。"""
|
||||
lines: List[str] = [
|
||||
@ -169,6 +236,10 @@ def _build_inject_guide_message(
|
||||
lines.append(latest_user_input.strip())
|
||||
else:
|
||||
lines.append("(无)")
|
||||
if runtime_state_lines:
|
||||
lines.append("")
|
||||
lines.append("当前运行时状态")
|
||||
lines.extend(runtime_state_lines)
|
||||
return "\n".join(lines).strip()
|
||||
|
||||
|
||||
@ -244,6 +315,7 @@ def _write_compact_file(
|
||||
user_inputs: List[str],
|
||||
latest_user_input: str,
|
||||
previous_records: List[Dict[str, Any]],
|
||||
runtime_state_lines: Optional[List[str]] = None,
|
||||
) -> str:
|
||||
compact_dir = project_path / ".astrion" / "compact_result"
|
||||
compact_dir.mkdir(parents=True, exist_ok=True)
|
||||
@ -271,6 +343,10 @@ def _write_compact_file(
|
||||
lines.append(latest_user_input.strip())
|
||||
else:
|
||||
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")
|
||||
return str(file_path.relative_to(project_path))
|
||||
|
||||
@ -368,7 +444,20 @@ async def run_deep_compression(
|
||||
"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:
|
||||
_emit(sender, "system_message", {"content": f"自动压缩总结失败,将使用失败占位文本:{summary_fail_reason}"})
|
||||
|
||||
@ -386,11 +475,6 @@ async def run_deep_compression(
|
||||
"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(
|
||||
Path(workspace.project_path),
|
||||
compression_index=target_count,
|
||||
@ -398,6 +482,7 @@ async def run_deep_compression(
|
||||
user_inputs=user_inputs,
|
||||
latest_user_input=latest_user_input,
|
||||
previous_records=previous_records,
|
||||
runtime_state_lines=runtime_state_lines,
|
||||
)
|
||||
|
||||
cm.set_compression_state(
|
||||
@ -452,6 +537,7 @@ async def run_deep_compression(
|
||||
previous_records=previous_records,
|
||||
user_inputs=user_inputs,
|
||||
latest_user_input=latest_user_input,
|
||||
runtime_state_lines=runtime_state_lines,
|
||||
)
|
||||
else:
|
||||
guide_message = _build_guide_message(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user