mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-19 06:43:17 +08:00
初始化:独立 oclaw 仓库首提交
- 在 oclaw/ 下重新初始化 Git 仓库 - 补齐子仓库 .gitignore,避免提交本地运行态数据(_local、node_modules、logs 等) - 提交当前工程代码与配置 Made-with: Cursor
This commit is contained in:
commit
ba3836f00f
579 changed files with 83112 additions and 0 deletions
22
prompts/README.md
Normal file
22
prompts/README.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
title: prompts_scope_after_openclaw_migration
|
||||
summary: Scope boundary for legacy and fallback prompts under oclaw/prompts.
|
||||
read_when:
|
||||
- Maintaining prompt files under oclaw/prompts.
|
||||
- Distinguishing OpenClaw runtime prompts from non-OpenClaw prompts.
|
||||
---
|
||||
|
||||
# `oclaw/prompts` Scope
|
||||
|
||||
OpenClaw runtime prompts have been migrated to `oclaw/prompts_openclaw`.
|
||||
|
||||
`oclaw/prompts` now keeps only:
|
||||
|
||||
- `fallback/*`: user-facing fallback/error templates.
|
||||
- Non-OpenClaw shared templates still used by runtime components, such as:
|
||||
- `runtime/default_system.{zh,en}.md`
|
||||
- `tools/*`
|
||||
- `image/default_edit_prompt.zh.md`
|
||||
|
||||
Do not add new OpenClaw manager/router/specialist/runtime chain prompts here.
|
||||
Use `oclaw/prompts_openclaw/*` instead.
|
||||
3
prompts/__init__.py
Normal file
3
prompts/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from .loader import render_openclaw_prompt, render_prompt, render_prompt_for_lang
|
||||
|
||||
__all__ = ["render_prompt", "render_prompt_for_lang", "render_openclaw_prompt"]
|
||||
9
prompts/fallback/ollama_failure.en.md
Normal file
9
prompts/fallback/ollama_failure.en.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: fallback_ollama_failure_en
|
||||
summary: ollama transport failure banner in English
|
||||
read_when: ollama transport exception
|
||||
---
|
||||
**Could not reach the Ollama / compatible endpoint:** `{{error_type}}: {{error_message}}`
|
||||
|
||||
_Falling back to the built-in rule engine for this reply._
|
||||
|
||||
9
prompts/fallback/ollama_failure.zh.md
Normal file
9
prompts/fallback/ollama_failure.zh.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: fallback_ollama_failure_zh
|
||||
summary: ollama transport failure banner in Chinese
|
||||
read_when: ollama transport exception
|
||||
---
|
||||
**无法连接本地 Ollama(或兼容接口):** `{{error_type}}: {{error_message}}`
|
||||
|
||||
_本次回答已改用内置规则引擎兜底。_
|
||||
|
||||
11
prompts/fallback/openai_missing_key_user.en.md
Normal file
11
prompts/fallback/openai_missing_key_user.en.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: fallback_openai_missing_key_user_en
|
||||
summary: user-facing message when openai key is missing
|
||||
read_when: openai mode selected but no api key
|
||||
---
|
||||
**Cannot use this profile:** no API Key is set for the OpenAI-compatible endpoint, and `OPENAI_API_KEY` is not in the environment.
|
||||
|
||||
Add a key in **Settings** and save, or switch to the built-in **Local Ollama (default)** profile.
|
||||
|
||||
_No request was sent to the model service._
|
||||
|
||||
11
prompts/fallback/openai_missing_key_user.zh.md
Normal file
11
prompts/fallback/openai_missing_key_user.zh.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: fallback_openai_missing_key_user_zh
|
||||
summary: user-facing message when openai key is missing
|
||||
read_when: openai mode selected but no api key
|
||||
---
|
||||
**当前配置无法接入模型:** 本配置为 OpenAI / 兼容 API,但未填写 API Key,且环境中也没有 `OPENAI_API_KEY`。
|
||||
|
||||
请在「设置」中填写 Key 并保存,或切换到内置的「本地 Ollama(默认)」配置后再试。
|
||||
|
||||
_本次未向模型服务发送请求。_
|
||||
|
||||
9
prompts/fallback/openai_transport_error.en.md
Normal file
9
prompts/fallback/openai_transport_error.en.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: fallback_openai_transport_error_en
|
||||
summary: generic OpenAI-compatible transport error in English
|
||||
read_when: model request failed
|
||||
---
|
||||
**Model request failed:** `{{error_type}}: {{error_message}}`
|
||||
|
||||
Check API key, base URL, and network, then try again.{{extra_tail}}
|
||||
|
||||
9
prompts/fallback/openai_transport_error.zh.md
Normal file
9
prompts/fallback/openai_transport_error.zh.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: fallback_openai_transport_error_zh
|
||||
summary: generic OpenAI-compatible transport error in Chinese
|
||||
read_when: model request failed
|
||||
---
|
||||
**调用模型失败:** `{{error_type}}: {{error_message}}`
|
||||
|
||||
请检查 API Key、Base URL 与网络后重试。{{extra_tail}}
|
||||
|
||||
9
prompts/fallback/openai_transport_oversized.en.md
Normal file
9
prompts/fallback/openai_transport_oversized.en.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: fallback_openai_transport_oversized_en
|
||||
summary: oversized payload failure in English
|
||||
read_when: model request too large
|
||||
---
|
||||
**Model request failed (message too large):** `{{error_type}}: {{error_message}}`
|
||||
|
||||
Common causes: oversized tools schema or large tool results in history. Tune `AIA_OPENAI_TOOLS_MAX_JSON_CHARS`, reduce MCP tools, or narrow the query.
|
||||
|
||||
9
prompts/fallback/openai_transport_oversized.zh.md
Normal file
9
prompts/fallback/openai_transport_oversized.zh.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: fallback_openai_transport_oversized_zh
|
||||
summary: oversized payload failure in Chinese
|
||||
read_when: model request too large
|
||||
---
|
||||
**调用模型失败(单条消息过长):** `{{error_type}}: {{error_message}}`
|
||||
|
||||
常见原因:tools 负载过大或 tool 返回过长。可调整 `AIA_OPENAI_TOOLS_MAX_JSON_CHARS`,或减少 MCP / 缩小查询范围后重试。
|
||||
|
||||
7
prompts/fallback/openclaw_runtime_error.en.md
Normal file
7
prompts/fallback/openclaw_runtime_error.en.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: fallback_openclaw_runtime_error_en
|
||||
summary: fallback text when openclaw runtime failed
|
||||
read_when: gateway exception path
|
||||
---
|
||||
oclaw runtime error: executor missing model/tools
|
||||
|
||||
7
prompts/fallback/openclaw_runtime_error.zh.md
Normal file
7
prompts/fallback/openclaw_runtime_error.zh.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: fallback_openclaw_runtime_error_zh
|
||||
summary: fallback text when openclaw runtime failed
|
||||
read_when: gateway exception path
|
||||
---
|
||||
oclaw 运行失败:执行器缺少 model/tools
|
||||
|
||||
7
prompts/fallback/task_queued.en.md
Normal file
7
prompts/fallback/task_queued.en.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: fallback_task_queued_en
|
||||
summary: User-visible text when async task is enqueued.
|
||||
read_when: gateway async_task mode
|
||||
---
|
||||
|
||||
Task queued, task_id={{task_id}}
|
||||
7
prompts/fallback/task_queued.zh.md
Normal file
7
prompts/fallback/task_queued.zh.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: fallback_task_queued_zh
|
||||
summary: User-visible text when async task is enqueued.
|
||||
read_when: gateway async_task mode
|
||||
---
|
||||
|
||||
任务已入队,task_id={{task_id}}
|
||||
92
prompts/frontmatter.py
Normal file
92
prompts/frontmatter.py
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError: # pragma: no cover - requirements.txt includes PyYAML
|
||||
yaml = None # type: ignore[assignment]
|
||||
|
||||
|
||||
def split_markdown_frontmatter(raw: str) -> tuple[str, str]:
|
||||
"""Split leading YAML frontmatter from markdown body (oclaw-style `---` fences)."""
|
||||
txt = str(raw or "")
|
||||
if not txt.startswith("---\n"):
|
||||
return "", txt
|
||||
end = txt.find("\n---\n", 4)
|
||||
if end < 0:
|
||||
return "", txt
|
||||
return txt[4:end], txt[end + 5 :]
|
||||
|
||||
|
||||
def _legacy_line_parse(frontmatter_text: str) -> dict[str, Any]:
|
||||
"""Best-effort key: value lines (pre-YAML migration compatibility)."""
|
||||
out: dict[str, Any] = {}
|
||||
for ln in str(frontmatter_text or "").splitlines():
|
||||
line = ln.strip()
|
||||
if not line or line.startswith("#") or ":" not in line:
|
||||
continue
|
||||
k, v = line.split(":", 1)
|
||||
key = str(k).strip()
|
||||
val = str(v).strip()
|
||||
if (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'")):
|
||||
val = val[1:-1]
|
||||
out[key] = val
|
||||
meta_raw = str(out.get("metadata") or "").strip()
|
||||
if meta_raw:
|
||||
try:
|
||||
out["metadata"] = json.loads(meta_raw)
|
||||
except Exception:
|
||||
out["metadata"] = {}
|
||||
else:
|
||||
out["metadata"] = {}
|
||||
return out
|
||||
|
||||
|
||||
def frontmatter_strict_yaml() -> bool:
|
||||
return str(os.getenv("AIA_PROMPT_FRONTMATTER_STRICT", "")).strip().lower() in {"1", "true", "yes", "on"}
|
||||
|
||||
|
||||
def parse_frontmatter_dict(frontmatter_text: str, *, source: str = "prompt") -> dict[str, Any]:
|
||||
"""
|
||||
Parse YAML frontmatter. On YAML failure, falls back to legacy line parser unless
|
||||
AIA_PROMPT_FRONTMATTER_STRICT is truthy (then raises).
|
||||
"""
|
||||
text = str(frontmatter_text or "").strip()
|
||||
if not text:
|
||||
return {}
|
||||
strict = frontmatter_strict_yaml()
|
||||
if yaml is None:
|
||||
if strict:
|
||||
raise RuntimeError("PyYAML is required when AIA_PROMPT_FRONTMATTER_STRICT is enabled")
|
||||
return _legacy_line_parse(text)
|
||||
try:
|
||||
data = yaml.safe_load(text)
|
||||
except yaml.YAMLError as exc:
|
||||
if strict:
|
||||
raise ValueError(f"invalid YAML frontmatter ({source}): {exc}") from exc
|
||||
return _legacy_line_parse(text)
|
||||
if data is None:
|
||||
return {}
|
||||
if not isinstance(data, dict):
|
||||
if strict:
|
||||
raise ValueError(f"frontmatter YAML root must be a mapping ({source})")
|
||||
return _legacy_line_parse(text)
|
||||
return dict(data)
|
||||
|
||||
|
||||
def parse_markdown_document(raw: str, *, source: str = "markdown") -> tuple[dict[str, Any], str]:
|
||||
"""Split file and parse frontmatter to a dict; body is stripped markdown."""
|
||||
fm_text, body = split_markdown_frontmatter(raw)
|
||||
meta = parse_frontmatter_dict(fm_text, source=source) if fm_text.strip() else {}
|
||||
return meta, str(body or "").strip()
|
||||
|
||||
|
||||
__all__ = [
|
||||
"frontmatter_strict_yaml",
|
||||
"parse_frontmatter_dict",
|
||||
"parse_markdown_document",
|
||||
"split_markdown_frontmatter",
|
||||
]
|
||||
7
prompts/image/default_edit_prompt.zh.md
Normal file
7
prompts/image/default_edit_prompt.zh.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: image_default_edit_prompt_zh
|
||||
summary: Default image editing prompt text.
|
||||
read_when: image prompt is empty
|
||||
---
|
||||
请基于输入图片和用户要求完成图像编辑,并返回结果图片。
|
||||
|
||||
97
prompts/loader.py
Normal file
97
prompts/loader.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from oclaw.platform.config.paths import PROJECT_ROOT
|
||||
from oclaw.prompts.frontmatter import parse_markdown_document
|
||||
|
||||
_VAR_RE = re.compile(r"\{\{\s*([a-zA-Z0-9_]+)\s*\}\}")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PromptDoc:
|
||||
frontmatter: dict[str, Any]
|
||||
body: str
|
||||
|
||||
|
||||
def _prompts_root() -> Path:
|
||||
return (PROJECT_ROOT / "oclaw" / "prompts").resolve()
|
||||
|
||||
|
||||
def _openclaw_prompts_root() -> Path:
|
||||
return (PROJECT_ROOT / "oclaw" / "prompts_openclaw").resolve()
|
||||
|
||||
|
||||
@lru_cache(maxsize=512)
|
||||
def load_prompt_doc(prompt_path: str) -> PromptDoc:
|
||||
p = (_prompts_root() / str(prompt_path)).resolve()
|
||||
if not p.exists():
|
||||
raise FileNotFoundError(f"prompt not found: {prompt_path}")
|
||||
raw = p.read_text(encoding="utf-8")
|
||||
fm, body = parse_markdown_document(raw, source=f"prompt:{prompt_path}")
|
||||
return PromptDoc(frontmatter=fm, body=body)
|
||||
|
||||
|
||||
@lru_cache(maxsize=512)
|
||||
def load_openclaw_prompt_doc(prompt_path: str) -> PromptDoc:
|
||||
p = (_openclaw_prompts_root() / str(prompt_path)).resolve()
|
||||
if not p.exists():
|
||||
raise FileNotFoundError(f"openclaw prompt not found: {prompt_path}")
|
||||
raw = p.read_text(encoding="utf-8")
|
||||
fm, body = parse_markdown_document(raw, source=f"openclaw_prompt:{prompt_path}")
|
||||
return PromptDoc(frontmatter=fm, body=body)
|
||||
|
||||
|
||||
def render_prompt_for_lang(stem: str, lang: str, *, variables: dict[str, Any] | None = None, strict: bool = True) -> str:
|
||||
suf = "en" if (lang or "zh").strip().lower().startswith("en") else "zh"
|
||||
return render_prompt(f"{stem}.{suf}.md", variables=variables, strict=strict)
|
||||
|
||||
|
||||
def render_prompt(prompt_path: str, *, variables: dict[str, Any] | None = None, strict: bool = True) -> str:
|
||||
doc = load_prompt_doc(prompt_path)
|
||||
vars_in = dict(variables or {})
|
||||
missing: set[str] = set()
|
||||
|
||||
def _repl(m: re.Match[str]) -> str:
|
||||
key = str(m.group(1) or "")
|
||||
if key in vars_in:
|
||||
return str(vars_in.get(key) or "")
|
||||
missing.add(key)
|
||||
return ""
|
||||
|
||||
out = _VAR_RE.sub(_repl, doc.body)
|
||||
if strict and missing:
|
||||
raise ValueError(f"missing prompt variables for {prompt_path}: {', '.join(sorted(missing))}")
|
||||
return out.strip()
|
||||
|
||||
|
||||
def render_openclaw_prompt(prompt_path: str, *, variables: dict[str, Any] | None = None, strict: bool = True) -> str:
|
||||
doc = load_openclaw_prompt_doc(prompt_path)
|
||||
vars_in = dict(variables or {})
|
||||
missing: set[str] = set()
|
||||
|
||||
def _repl(m: re.Match[str]) -> str:
|
||||
key = str(m.group(1) or "")
|
||||
if key in vars_in:
|
||||
return str(vars_in.get(key) or "")
|
||||
missing.add(key)
|
||||
return ""
|
||||
|
||||
out = _VAR_RE.sub(_repl, doc.body)
|
||||
if strict and missing:
|
||||
raise ValueError(f"missing openclaw prompt variables for {prompt_path}: {', '.join(sorted(missing))}")
|
||||
return out.strip()
|
||||
|
||||
|
||||
__all__ = [
|
||||
"PromptDoc",
|
||||
"load_prompt_doc",
|
||||
"load_openclaw_prompt_doc",
|
||||
"render_prompt",
|
||||
"render_prompt_for_lang",
|
||||
"render_openclaw_prompt",
|
||||
]
|
||||
27
prompts/runtime/default_system.en.md
Normal file
27
prompts/runtime/default_system.en.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
title: default_runtime_system_en
|
||||
summary: Default system prompt for general runtime (English template).
|
||||
read_when: agent runtime start
|
||||
---
|
||||
|
||||
# Identity
|
||||
|
||||
You are a general-purpose AI assistant.
|
||||
|
||||
## Input Constraints
|
||||
|
||||
- Answer in the same language as the user.
|
||||
|
||||
## Execution Rules
|
||||
|
||||
- When external data or actions are needed, prefer the tools provided by the system.
|
||||
- Tool calls are carried by the platform protocol; do not paste tool-protocol JSON in the user-visible reply.
|
||||
|
||||
## Output Format
|
||||
|
||||
- Produce user-readable text; conclusions first, steps concise.
|
||||
|
||||
## Safety
|
||||
|
||||
- Do not fabricate tool results.
|
||||
- When uncertain, state limits and ask for missing information.
|
||||
22
prompts/runtime/default_system.zh.md
Normal file
22
prompts/runtime/default_system.zh.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
title: default_runtime_system_zh
|
||||
summary: Default system prompt for general runtime.
|
||||
read_when: agent runtime start
|
||||
---
|
||||
# Identity
|
||||
你是一个通用 AI 助手。
|
||||
|
||||
## Input Constraints
|
||||
- 使用用户输入的语言回答。
|
||||
|
||||
## Execution Rules
|
||||
- 当需要外部数据或执行动作时,优先调用系统提供的工具。
|
||||
- 工具调用由平台协议承载,不要在正文里输出工具协议 JSON。
|
||||
|
||||
## Output Format
|
||||
- 直接输出用户可读内容,结论优先,步骤简洁。
|
||||
|
||||
## Safety
|
||||
- 不要伪造工具结果。
|
||||
- 不确定时先说明边界并请求补充信息。
|
||||
|
||||
8
prompts/tools/image_attachment_meta.md
Normal file
8
prompts/tools/image_attachment_meta.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: image_attachment_meta_block
|
||||
summary: Metadata-only image attachment block for LLM message context.
|
||||
read_when: building user message content
|
||||
---
|
||||
--- Image Attachment (meta only) ---
|
||||
{{meta_line}}
|
||||
|
||||
9
prompts/tools/text_attachment_wrap.md
Normal file
9
prompts/tools/text_attachment_wrap.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: text_attachment_wrap_block
|
||||
summary: Wrapper for text file attachments in LLM context.
|
||||
read_when: building user message content
|
||||
---
|
||||
--- File: {{name}} ---
|
||||
{{content}}
|
||||
--- End of File ---
|
||||
|
||||
8
prompts/tools/tool_result_unpaired.md
Normal file
8
prompts/tools/tool_result_unpaired.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
title: tool_result_unpaired_block
|
||||
summary: Downgraded tool result block when call id pairing is broken.
|
||||
read_when: building assistant fallback message
|
||||
---
|
||||
[{{tag}}]
|
||||
{{payload}}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue