mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 03:30:48 +08:00
彻底移除 manager_memory 与 wiki 注入写入分支,收敛网关分发路径和测试用例,同时补齐并中文化 session-bootstrap / wiki-first-autonomy / self-improvement 的 Wiki-first 规范,保证行为与提示词一致。 Made-with: Cursor
169 lines
6.5 KiB
Python
169 lines
6.5 KiB
Python
from __future__ import annotations
|
||
|
||
import threading
|
||
from typing import Any
|
||
|
||
from oclaw.runtime.memory_stage import render_memory_context_block
|
||
from oclaw.runtime.project_context_prompt import build_project_context_block
|
||
from oclaw.runtime.skills_prompt import build_skills_catalog_block
|
||
from oclaw.runtime.types import OclawMemoryContext
|
||
from oclaw.runtime.workspaces.experts import expert_workspace_signature_token
|
||
from oclaw.prompts.loader import render_runtime_prompt
|
||
from oclaw.runtime.tools.base import ToolRegistry
|
||
|
||
_EXECUTOR_STATIC_PROMPT_CACHE_LOCK = threading.Lock()
|
||
_EXECUTOR_STATIC_PROMPT_CACHE: dict[tuple[Any, ...], str] = {}
|
||
|
||
|
||
def _executor_prompt_settings_signature(store: Any) -> tuple[str, ...]:
|
||
keys = (
|
||
"AIA_SKILL_RUNTIME_ENABLED",
|
||
"AIA_SKILLS_PROMPT_IN_SYSTEM",
|
||
"AIA_SKILL_DISABLED_NAMES",
|
||
"AIA_SKILL_ROLE_BINDING_ENABLED",
|
||
"AIA_SKILL_ROLE_BINDING_MANAGER_INHERIT",
|
||
"AIA_PROJECT_CONTEXT_MAX_FILE_CHARS",
|
||
"AIA_PROJECT_CONTEXT_MAX_TOTAL_CHARS",
|
||
)
|
||
parts: list[str] = []
|
||
for key in keys:
|
||
try:
|
||
val = str(store.get_setting(key) or "")
|
||
except Exception:
|
||
val = ""
|
||
parts.append(f"{key}={val}")
|
||
return tuple(parts)
|
||
|
||
|
||
def _unified_skill_policy_guidance() -> str:
|
||
# Global policy for all agents (including dynamic/ephemeral) — appended to base_system in build_executor_system_prompt.
|
||
return (
|
||
"- 如果用户问你有哪些技能(skill/技能),请直接根据已注入的技能目录及其 description 回答。\n"
|
||
"- 不要为了“列出技能”而去读取 SKILL.md。只有在你确实需要某个技能的详细使用说明时,才读取对应 SKILL.md。\n"
|
||
"- 当你需要技能细节时,请按目录中给出的 path 读取对应的 SKILL.md。\n"
|
||
"- 当对话涉及长期记忆、用户身份/偏好、项目背景延续、复发问题沉淀时,优先启用 wiki-first-autonomy 技能,并优先使用 memory_wiki_search/memory_wiki_get 检索上下文,再执行与回复。\n"
|
||
"- 当新增事实会影响后续决策时,完成当前任务后使用 memory_wiki_apply 写入结构化记忆,并用 memory_wiki_lint 做质量检查。\n"
|
||
"- 技能包由说明文档和可选文件组成。运行时不会自动执行技能 `scripts/` 目录下的文件;\n"
|
||
"- internal hooks 是独立系统,也不会自动执行这些脚本。\n"
|
||
"- 当用户明确要求运行技能脚本时,请使用项目允许的 terminal/bash/exec(或同类)工具执行;\n"
|
||
"- 如果脚本依赖相对路径(例如 `.learnings/`),请将工作目录设置为用户工作区。\n"
|
||
"- 在没有显式工具调用成功结果前,不要假设脚本已经执行。\n"
|
||
"- 在 Windows 上,`.sh` 可能需要 Git Bash、WSL 或等效环境。\n"
|
||
)
|
||
|
||
|
||
def build_executor_system_prompt(
|
||
*,
|
||
store: Any,
|
||
tools: ToolRegistry | None,
|
||
base_url: str,
|
||
base_system: str,
|
||
memory_context: OclawMemoryContext | None,
|
||
lang: str,
|
||
workspace_dir: str | None = None,
|
||
skill_binding_role: str | None = None,
|
||
) -> str:
|
||
"""Build the final system string for the oclaw executor (memory block + skills catalog).
|
||
|
||
`lang` is reserved for future localized system fragments; current templates are bilingual/static.
|
||
"""
|
||
_ = lang # reserved for i18n extensions
|
||
final_system = get_executor_prompt_static(
|
||
store=store,
|
||
tools=tools,
|
||
base_url=base_url,
|
||
base_system=base_system,
|
||
workspace_dir=workspace_dir,
|
||
skill_binding_role=skill_binding_role,
|
||
)
|
||
mem_block = render_memory_context_block(memory_context or OclawMemoryContext())
|
||
if not mem_block:
|
||
return final_system
|
||
return render_runtime_prompt(
|
||
"runtime/system_with_memory.md",
|
||
variables={"system_prompt": final_system, "memory_context": mem_block},
|
||
strict=True,
|
||
)
|
||
|
||
|
||
def get_executor_prompt_static(
|
||
*,
|
||
store: Any,
|
||
tools: ToolRegistry | None,
|
||
base_url: str,
|
||
base_system: str,
|
||
workspace_dir: str | None = None,
|
||
skill_binding_role: str | None = None,
|
||
) -> str:
|
||
cache_key = (
|
||
str(base_url or "").strip(),
|
||
str(base_system or "").strip(),
|
||
str(workspace_dir or "").strip(),
|
||
str(skill_binding_role or "").strip().lower(),
|
||
expert_workspace_signature_token(),
|
||
_executor_prompt_settings_signature(store),
|
||
bool(tools is not None),
|
||
)
|
||
with _EXECUTOR_STATIC_PROMPT_CACHE_LOCK:
|
||
cached = _EXECUTOR_STATIC_PROMPT_CACHE.get(cache_key)
|
||
if isinstance(cached, str):
|
||
return cached
|
||
final_system = str(base_system or "").strip()
|
||
guide = _unified_skill_policy_guidance().strip()
|
||
if guide and guide not in final_system:
|
||
final_system = f"{final_system}\n\n{guide}".strip()
|
||
project_block = build_project_context_block(store=store, workspace_dir=workspace_dir)
|
||
if project_block:
|
||
final_system = f"{final_system}\n\n{project_block}".strip()
|
||
if tools is not None:
|
||
cat = build_skills_catalog_block(
|
||
store=store,
|
||
registry=tools,
|
||
base_url=str(base_url or ""),
|
||
skill_binding_role=skill_binding_role,
|
||
)
|
||
if cat.strip():
|
||
final_system = render_runtime_prompt(
|
||
"runtime/system_with_skills.md",
|
||
variables={"system_body": final_system, "skills_catalog": cat},
|
||
strict=True,
|
||
)
|
||
with _EXECUTOR_STATIC_PROMPT_CACHE_LOCK:
|
||
_EXECUTOR_STATIC_PROMPT_CACHE[cache_key] = final_system
|
||
if len(_EXECUTOR_STATIC_PROMPT_CACHE) > 256:
|
||
_EXECUTOR_STATIC_PROMPT_CACHE.clear()
|
||
return final_system
|
||
|
||
|
||
def warm_executor_prompt_cache(
|
||
*,
|
||
store: Any,
|
||
tools: ToolRegistry | None,
|
||
base_url: str,
|
||
role_base_systems: dict[str, str],
|
||
workspace_dir: str | None = None,
|
||
) -> dict[str, int]:
|
||
warmed = 0
|
||
for role, base_system in (role_base_systems or {}).items():
|
||
_ = get_executor_prompt_static(
|
||
store=store,
|
||
tools=tools,
|
||
base_url=base_url,
|
||
base_system=str(base_system or ""),
|
||
workspace_dir=workspace_dir,
|
||
skill_binding_role=str(role or "").strip().lower() or None,
|
||
)
|
||
warmed += 1
|
||
return {"roles_warmed": int(warmed)}
|
||
|
||
|
||
def build_oclaw_executor_system_prompt(**kwargs: Any) -> str:
|
||
return build_executor_system_prompt(**kwargs)
|
||
|
||
|
||
__all__ = [
|
||
"build_executor_system_prompt",
|
||
"build_oclaw_executor_system_prompt",
|
||
"get_executor_prompt_static",
|
||
"warm_executor_prompt_cache",
|
||
]
|