mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 05:50:44 +08:00
清理综合模式中的 Wiki/记忆遗留逻辑并完善自治技能文档。
彻底移除 manager_memory 与 wiki 注入写入分支,收敛网关分发路径和测试用例,同时补齐并中文化 session-bootstrap / wiki-first-autonomy / self-improvement 的 Wiki-first 规范,保证行为与提示词一致。 Made-with: Cursor
This commit is contained in:
parent
8b8265eb46
commit
3122b8a16c
23 changed files with 948 additions and 1093 deletions
|
|
@ -30,7 +30,6 @@ from oclaw.prompts import render_prompt
|
|||
|
||||
from oclaw.runtime.command_parser import parse_internal_command
|
||||
from oclaw.runtime.core.agent_execution import AgentCoreRunInput, build_memory_context, run_agent_core
|
||||
from oclaw.runtime.memory_stage import after_turn_memory
|
||||
from oclaw.runtime.router import decide_route
|
||||
from oclaw.runtime.worker import ensure_worker_started
|
||||
from oclaw.runtime.orchestration.trace import new_span_id, new_trace_id
|
||||
|
|
@ -94,11 +93,6 @@ class GatewayDispatchPlan:
|
|||
specialist_input_msg: StandardMessage | None
|
||||
manager_exec_msg: StandardMessage | None
|
||||
manager_instruction_text: str
|
||||
manager_memory_mode: bool
|
||||
manager_need_wiki_inject: bool | None
|
||||
manager_wiki_query: str
|
||||
manager_memory_write_text: str
|
||||
manager_post_reply_memory_write_text: str
|
||||
|
||||
|
||||
class OclawGateway:
|
||||
|
|
@ -298,15 +292,15 @@ class OclawGateway:
|
|||
lang: str,
|
||||
executor: Any,
|
||||
memory_enabled: bool,
|
||||
) -> tuple[str, str, dict[str, Any] | None, str, bool, bool | None, str, str, str]:
|
||||
) -> tuple[str, str, dict[str, Any] | None, str]:
|
||||
model = getattr(executor, "model", None)
|
||||
if model is None or not callable(getattr(model, "chat", None)):
|
||||
return ("generalist", "manager_model_missing", None, "", False, None, "", "", "")
|
||||
return ("generalist", "manager_model_missing", None, "")
|
||||
try:
|
||||
registry = getattr(executor, "tools", None)
|
||||
base_url = str(getattr(model, "base_url", "") or "")
|
||||
if registry is None:
|
||||
return ("generalist", "manager_tools_missing", None, "", False, None, "", "", "")
|
||||
return ("generalist", "manager_tools_missing", None, "")
|
||||
pack = get_manager_prompt_prebuild(
|
||||
store=self.store,
|
||||
registry=registry,
|
||||
|
|
@ -340,10 +334,10 @@ class OclawGateway:
|
|||
resp = model.chat(messages, [], on_token=None)
|
||||
obj = self._parse_json_object(str(getattr(resp, "content", "") or ""))
|
||||
if not isinstance(obj, dict):
|
||||
return ("generalist", "manager_json_missing", None, "", False, None, "", "", "")
|
||||
return ("generalist", "manager_json_missing", None, "")
|
||||
route = obj.get("route") if isinstance(obj, dict) else None
|
||||
if not isinstance(route, dict):
|
||||
return ("generalist", "manager_route_missing", None, "", False, None, "", "", "")
|
||||
return ("generalist", "manager_route_missing", None, "")
|
||||
route_kind = str(route.get("kind") or "").strip().lower()
|
||||
raw_specialist = str(route.get("specialist") or "").strip().lower()
|
||||
fixed_set = set([str(x).strip().lower() for x in allowed_fixed if str(x).strip()])
|
||||
|
|
@ -355,41 +349,17 @@ class OclawGateway:
|
|||
if isinstance(dispatch, dict):
|
||||
instruction_text = str(dispatch.get("instruction_text") or "").strip()
|
||||
if not instruction_text:
|
||||
return ("generalist", "manager_instruction_missing", None, "", False, None, "", "", "")
|
||||
need_wiki_inject: bool | None = None
|
||||
wiki_query = ""
|
||||
memory_write_text = ""
|
||||
post_reply_memory_write_text = ""
|
||||
route_need = route.get("need_wiki_inject") if isinstance(route, dict) else None
|
||||
if isinstance(route_need, bool):
|
||||
need_wiki_inject = bool(route_need)
|
||||
elif isinstance(dispatch, dict) and isinstance(dispatch.get("need_wiki_inject"), bool):
|
||||
need_wiki_inject = bool(dispatch.get("need_wiki_inject"))
|
||||
route_wq = route.get("wiki_query") if isinstance(route, dict) else None
|
||||
if isinstance(route_wq, str):
|
||||
wiki_query = str(route_wq).strip()
|
||||
elif isinstance(dispatch, dict) and isinstance(dispatch.get("wiki_query"), str):
|
||||
wiki_query = str(dispatch.get("wiki_query") or "").strip()
|
||||
wiki_query = wiki_query[:300]
|
||||
if isinstance(dispatch, dict) and isinstance(dispatch.get("memory_write_text"), str):
|
||||
memory_write_text = str(dispatch.get("memory_write_text") or "").strip()[:4000]
|
||||
if isinstance(dispatch, dict) and isinstance(dispatch.get("post_reply_memory_write_text"), str):
|
||||
post_reply_memory_write_text = str(dispatch.get("post_reply_memory_write_text") or "").strip()[:4000]
|
||||
if bool(need_wiki_inject) and not str(wiki_query or "").strip():
|
||||
return ("generalist", "manager_wiki_query_missing", None, instruction_text, False, False, "", "", "")
|
||||
# Allow manager to directly execute wiki/memory tasks.
|
||||
if route_kind == "manager_memory":
|
||||
if not memory_write_text:
|
||||
return ("generalist", "manager_memory_write_missing", None, instruction_text, False, need_wiki_inject, wiki_query, "", post_reply_memory_write_text)
|
||||
return ("manager", reason or "manager_memory", None, instruction_text, True, need_wiki_inject, wiki_query, memory_write_text, post_reply_memory_write_text)
|
||||
return ("generalist", "manager_instruction_missing", None, "")
|
||||
if route_kind and route_kind != "specialist":
|
||||
return ("generalist", "manager_route_kind_invalid", None, instruction_text)
|
||||
dynamic_agent = self._parse_dynamic_agent(obj.get("dynamic_agent") if isinstance(obj, dict) else None)
|
||||
if specialist == "memory" and not memory_enabled:
|
||||
return ("generalist", "memory_disabled_fallback", None, instruction_text, False, need_wiki_inject, wiki_query, memory_write_text, post_reply_memory_write_text)
|
||||
return ("generalist", "memory_disabled_fallback", None, instruction_text)
|
||||
if not fixed and dynamic_agent is None:
|
||||
return ("generalist", "dynamic_agent_invalid_fallback", None, instruction_text, False, need_wiki_inject, wiki_query, memory_write_text, post_reply_memory_write_text)
|
||||
return (specialist, reason, dynamic_agent, instruction_text, False, need_wiki_inject, wiki_query, memory_write_text, post_reply_memory_write_text)
|
||||
return ("generalist", "dynamic_agent_invalid_fallback", None, instruction_text)
|
||||
return (specialist, reason, dynamic_agent, instruction_text)
|
||||
except Exception:
|
||||
return ("generalist", "manager_select_failed", None, "", False, None, "", "", "")
|
||||
return ("generalist", "manager_select_failed", None, "")
|
||||
|
||||
def _manager_finalize_output(
|
||||
self,
|
||||
|
|
@ -700,11 +670,6 @@ class OclawGateway:
|
|||
specialist_input_msg: StandardMessage | None = None
|
||||
manager_exec_msg: StandardMessage | None = None
|
||||
manager_instruction_text = ""
|
||||
manager_memory_mode = False
|
||||
manager_need_wiki_inject: bool | None = None
|
||||
manager_wiki_query = ""
|
||||
manager_memory_write_text = ""
|
||||
manager_post_reply_memory_write_text = ""
|
||||
if interaction_mode == "expert" and callable(specialist_executor_factory):
|
||||
try:
|
||||
selected_executor = specialist_executor_factory(requested_specialist)
|
||||
|
|
@ -717,11 +682,6 @@ class OclawGateway:
|
|||
dispatch_reason,
|
||||
dynamic_agent,
|
||||
instruction_text,
|
||||
manager_memory_mode,
|
||||
manager_need_wiki_inject,
|
||||
manager_wiki_query,
|
||||
manager_memory_write_text,
|
||||
manager_post_reply_memory_write_text,
|
||||
) = self._manager_select_specialist(msg=msg, lang=lang, executor=executor, memory_enabled=memory_enabled)
|
||||
manager_instruction_text = str(instruction_text or "").strip()
|
||||
trace_local(
|
||||
|
|
@ -729,13 +689,10 @@ class OclawGateway:
|
|||
payload={
|
||||
"interaction_mode": interaction_mode,
|
||||
"manager_selected_specialist": str(manager_specialist or ""),
|
||||
"manager_memory_mode": bool(manager_memory_mode),
|
||||
"dispatch_reason": str(dispatch_reason or ""),
|
||||
"instruction_chars": int(len(manager_instruction_text or "")),
|
||||
"dynamic_agent_used": bool(dynamic_agent is not None),
|
||||
"dynamic_agent_name": str((dynamic_agent or {}).get("name") or "") if isinstance(dynamic_agent, dict) else "",
|
||||
"memory_write_chars": int(len(manager_memory_write_text or "")),
|
||||
"post_reply_memory_write_chars": int(len(manager_post_reply_memory_write_text or "")),
|
||||
},
|
||||
started_at=started_at,
|
||||
)
|
||||
|
|
@ -749,11 +706,6 @@ class OclawGateway:
|
|||
specialist_input_msg=specialist_input_msg,
|
||||
manager_exec_msg=manager_exec_msg,
|
||||
manager_instruction_text=manager_instruction_text,
|
||||
manager_memory_mode=manager_memory_mode,
|
||||
manager_need_wiki_inject=manager_need_wiki_inject,
|
||||
manager_wiki_query=manager_wiki_query,
|
||||
manager_memory_write_text=manager_memory_write_text,
|
||||
manager_post_reply_memory_write_text=manager_post_reply_memory_write_text,
|
||||
)
|
||||
|
||||
def handle_turn(
|
||||
|
|
@ -913,13 +865,8 @@ class OclawGateway:
|
|||
specialist_input_msg = plan.specialist_input_msg
|
||||
manager_exec_msg = plan.manager_exec_msg
|
||||
manager_instruction_text = plan.manager_instruction_text
|
||||
manager_memory_mode = plan.manager_memory_mode
|
||||
manager_need_wiki_inject = plan.manager_need_wiki_inject
|
||||
manager_wiki_query = plan.manager_wiki_query
|
||||
manager_memory_write_text = plan.manager_memory_write_text
|
||||
manager_post_reply_memory_write_text = plan.manager_post_reply_memory_write_text
|
||||
if interaction_mode == "comprehensive":
|
||||
if str(manager_instruction_text or "").strip() and not bool(manager_memory_mode):
|
||||
if str(manager_instruction_text or "").strip():
|
||||
try:
|
||||
assignment_title = "Task assignment" if str(lang or "").startswith("en") else "任务分配"
|
||||
assignment_text = (
|
||||
|
|
@ -947,21 +894,7 @@ class OclawGateway:
|
|||
channel=msg.channel,
|
||||
text=str(manager_instruction_text or "").strip(),
|
||||
attachments=list(msg.attachments or []),
|
||||
metadata=(
|
||||
{
|
||||
**dict(base_metadata),
|
||||
**(
|
||||
{"need_wiki_inject": bool(manager_need_wiki_inject)}
|
||||
if isinstance(manager_need_wiki_inject, bool)
|
||||
else {}
|
||||
),
|
||||
**(
|
||||
{"wiki_query": str(manager_wiki_query or "")}
|
||||
if str(manager_wiki_query or "").strip()
|
||||
else {}
|
||||
),
|
||||
}
|
||||
),
|
||||
metadata=dict(base_metadata),
|
||||
)
|
||||
manager_exec_msg = StandardMessage(
|
||||
session_id=msg.session_id,
|
||||
|
|
@ -971,27 +904,9 @@ class OclawGateway:
|
|||
channel=msg.channel,
|
||||
text=msg.text,
|
||||
attachments=list(msg.attachments or []),
|
||||
metadata=(
|
||||
{
|
||||
**dict(base_metadata),
|
||||
**(
|
||||
{"need_wiki_inject": bool(manager_need_wiki_inject)}
|
||||
if isinstance(manager_need_wiki_inject, bool)
|
||||
else {}
|
||||
),
|
||||
**(
|
||||
{"wiki_query": str(manager_wiki_query or "")}
|
||||
if str(manager_wiki_query or "").strip()
|
||||
else {}
|
||||
),
|
||||
}
|
||||
),
|
||||
metadata=dict(base_metadata),
|
||||
)
|
||||
if manager_memory_mode:
|
||||
manager_specialist = "manager"
|
||||
selected_executor = executor
|
||||
dispatch_reason = dispatch_reason or "manager_memory"
|
||||
elif manager_specialist in {"ops", "generalist", "image", "memory"}:
|
||||
if manager_specialist in {"ops", "generalist", "image", "memory"}:
|
||||
if callable(specialist_executor_factory):
|
||||
try:
|
||||
selected_executor = specialist_executor_factory(manager_specialist)
|
||||
|
|
@ -1023,51 +938,37 @@ class OclawGateway:
|
|||
selected_executor = executor
|
||||
|
||||
route_mode = "sync_direct"
|
||||
if manager_memory_mode:
|
||||
_trace_local(
|
||||
event_type="router_decision",
|
||||
payload={
|
||||
"mode": route_mode,
|
||||
"reason": "manager_memory_direct",
|
||||
"interaction_mode": interaction_mode,
|
||||
"requested_specialist": requested_specialist,
|
||||
"manager_selected_specialist": manager_specialist,
|
||||
"dispatch_reason": dispatch_reason,
|
||||
},
|
||||
started_at=t0,
|
||||
)
|
||||
else:
|
||||
route_msg = StandardMessage(
|
||||
session_id=msg.session_id,
|
||||
tenant_id=msg.tenant_id,
|
||||
user_id=msg.user_id,
|
||||
role=msg.role,
|
||||
channel=msg.channel,
|
||||
text=msg.text,
|
||||
attachments=list(msg.attachments or []),
|
||||
metadata={
|
||||
**base_metadata,
|
||||
"skills_total": int(skill_stats.get("skills_total") or 0),
|
||||
"interaction_mode": interaction_mode,
|
||||
"requested_specialist": requested_specialist,
|
||||
"manager_selected_specialist": manager_specialist,
|
||||
},
|
||||
)
|
||||
route = decide_route(route_msg, store=self.store, model=getattr(selected_executor, "model", None))
|
||||
route_mode = str(route.mode or "sync_direct")
|
||||
route_reason = str(route.reason or "")
|
||||
_trace_local(
|
||||
event_type="router_decision",
|
||||
payload={
|
||||
"mode": route_mode,
|
||||
"reason": route_reason,
|
||||
"interaction_mode": interaction_mode,
|
||||
"requested_specialist": requested_specialist,
|
||||
"manager_selected_specialist": manager_specialist,
|
||||
"dispatch_reason": dispatch_reason,
|
||||
},
|
||||
started_at=t0,
|
||||
)
|
||||
route_msg = StandardMessage(
|
||||
session_id=msg.session_id,
|
||||
tenant_id=msg.tenant_id,
|
||||
user_id=msg.user_id,
|
||||
role=msg.role,
|
||||
channel=msg.channel,
|
||||
text=msg.text,
|
||||
attachments=list(msg.attachments or []),
|
||||
metadata={
|
||||
**base_metadata,
|
||||
"skills_total": int(skill_stats.get("skills_total") or 0),
|
||||
"interaction_mode": interaction_mode,
|
||||
"requested_specialist": requested_specialist,
|
||||
"manager_selected_specialist": manager_specialist,
|
||||
},
|
||||
)
|
||||
route = decide_route(route_msg, store=self.store, model=getattr(selected_executor, "model", None))
|
||||
route_mode = str(route.mode or "sync_direct")
|
||||
route_reason = str(route.reason or "")
|
||||
_trace_local(
|
||||
event_type="router_decision",
|
||||
payload={
|
||||
"mode": route_mode,
|
||||
"reason": route_reason,
|
||||
"interaction_mode": interaction_mode,
|
||||
"requested_specialist": requested_specialist,
|
||||
"manager_selected_specialist": manager_specialist,
|
||||
"dispatch_reason": dispatch_reason,
|
||||
},
|
||||
started_at=t0,
|
||||
)
|
||||
if on_progress:
|
||||
on_progress("oclaw: running…")
|
||||
if route_mode == "async_task":
|
||||
|
|
@ -1177,22 +1078,7 @@ class OclawGateway:
|
|||
started_at=t0,
|
||||
)
|
||||
exec_msg = (
|
||||
(
|
||||
StandardMessage(
|
||||
session_id=msg.session_id,
|
||||
tenant_id=msg.tenant_id,
|
||||
user_id=msg.user_id,
|
||||
role=msg.role,
|
||||
channel=msg.channel,
|
||||
text=str(manager_memory_write_text or manager_instruction_text or msg.text or ""),
|
||||
attachments=list(msg.attachments or []),
|
||||
metadata=(manager_exec_msg.metadata if manager_exec_msg is not None else dict(base_metadata)),
|
||||
)
|
||||
if manager_memory_mode
|
||||
else (manager_exec_msg if manager_exec_msg is not None else msg)
|
||||
)
|
||||
if manager_memory_mode
|
||||
else (specialist_input_msg if (interaction_mode == "comprehensive" and specialist_input_msg is not None) else msg)
|
||||
specialist_input_msg if (interaction_mode == "comprehensive" and specialist_input_msg is not None) else msg
|
||||
)
|
||||
core_out = run_agent_core(
|
||||
store=self.store,
|
||||
|
|
@ -1211,8 +1097,7 @@ class OclawGateway:
|
|||
max_tool_workers=_get_int_setting("AIA_TURN_MAX_TOOL_WORKERS", 8, 1, 32),
|
||||
max_attempts=_get_int_setting("AIA_OCLAW_MAX_ATTEMPTS", 2, 1, 5),
|
||||
memory_context=memory_context,
|
||||
# manager_memory writes to wiki/memory store; do not stream body to frontend.
|
||||
on_token=(None if manager_memory_mode else (None if interaction_mode == "comprehensive" else on_token)),
|
||||
on_token=(None if interaction_mode == "comprehensive" else on_token),
|
||||
on_progress=on_progress,
|
||||
on_tool_ui=on_tool_ui,
|
||||
should_stop=should_stop,
|
||||
|
|
@ -1222,14 +1107,7 @@ class OclawGateway:
|
|||
)
|
||||
executed_turn_uuid = str(getattr(core_out.outcome, "turn_uuid", "") or "")
|
||||
specialist_reply = str(core_out.outcome.final_text or "")
|
||||
if manager_memory_mode:
|
||||
# manager_memory: never expose manager dispatch instruction_text to end users.
|
||||
reply = (
|
||||
"已执行记忆写入。"
|
||||
if not str(lang or "").startswith("en")
|
||||
else "Memory write executed."
|
||||
)
|
||||
elif interaction_mode == "comprehensive":
|
||||
if interaction_mode == "comprehensive":
|
||||
reply = self._manager_finalize_output(
|
||||
msg=msg,
|
||||
lang=lang,
|
||||
|
|
@ -1259,27 +1137,6 @@ class OclawGateway:
|
|||
reply = f"{base}\n(detail: {detail})" if detail else base
|
||||
|
||||
elapsed_ms = int((time.perf_counter() - t0) * 1000)
|
||||
if interaction_mode == "comprehensive" and str(manager_post_reply_memory_write_text or "").strip():
|
||||
try:
|
||||
after_turn_memory(
|
||||
store=self.store,
|
||||
session_id=msg.session_id,
|
||||
tenant_id=msg.tenant_id,
|
||||
user_id=msg.user_id,
|
||||
user_text=str(msg.text or ""),
|
||||
assistant_text=str(manager_post_reply_memory_write_text or ""),
|
||||
turn_uuid="",
|
||||
)
|
||||
_trace_local(
|
||||
event_type="after_turn_memory",
|
||||
payload={
|
||||
"source": "manager_post_reply",
|
||||
"post_reply_memory_write_chars": int(len(manager_post_reply_memory_write_text or "")),
|
||||
},
|
||||
started_at=t0,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
if str(executed_turn_uuid or "").strip():
|
||||
try:
|
||||
compact_turn_tool_messages_for_storage(
|
||||
|
|
|
|||
|
|
@ -1,644 +1,83 @@
|
|||
---
|
||||
name: self-improvement
|
||||
description: "Captures learnings, errors, and corrections to enable continuous improvement. Use when: (1) A command or operation fails unexpectedly, (2) User corrects Claude ('No, that's wrong...', 'Actually...'), (3) User requests a capability that doesn't exist, (4) An external API or tool fails, (5) Claude realizes its knowledge is outdated or incorrect, (6) A better approach is discovered for a recurring task. Also review learnings before major tasks."
|
||||
description: 将纠错、错误与能力缺口写入 Wiki 以持续改进。适用于操作失败、用户反馈纠正、问题复发、或需要沉淀并推动的新能力需求场景。
|
||||
metadata:
|
||||
---
|
||||
|
||||
# Self-Improvement Skill
|
||||
# 自我改进(仅 Wiki)
|
||||
|
||||
Log learnings and errors to markdown files for continuous improvement. Coding agents can later process these into fixes, and important learnings get promoted to project memory.
|
||||
本技能仅使用 Wiki,不使用本地 `.learnings/` 文件。
|
||||
|
||||
## First-Use Initialisation
|
||||
## 存储路径
|
||||
|
||||
Before logging anything, ensure the `.learnings/` directory and files exist in the project or workspace root. If any are missing, create them:
|
||||
- `improvement/learnings.md`
|
||||
- `improvement/errors.md`
|
||||
- `improvement/feature-requests.md`
|
||||
|
||||
```bash
|
||||
mkdir -p .learnings
|
||||
[ -f .learnings/LEARNINGS.md ] || printf "# Learnings\n\nCorrections, insights, and knowledge gaps captured during development.\n\n**Categories**: correction | insight | knowledge_gap | best_practice\n\n---\n" > .learnings/LEARNINGS.md
|
||||
[ -f .learnings/ERRORS.md ] || printf "# Errors\n\nCommand failures and integration errors.\n\n---\n" > .learnings/ERRORS.md
|
||||
[ -f .learnings/FEATURE_REQUESTS.md ] || printf "# Feature Requests\n\nCapabilities requested by the user.\n\n---\n" > .learnings/FEATURE_REQUESTS.md
|
||||
```
|
||||
## 触发条件
|
||||
|
||||
Never overwrite existing files. This is a no-op if `.learnings/` is already initialised.
|
||||
出现以下情况时启用本技能:
|
||||
|
||||
Do not log secrets, tokens, private keys, environment variables, or full source/config files unless the user explicitly asks for that level of detail. Prefer short summaries or redacted excerpts over raw command output or full transcripts.
|
||||
1. 命令或操作出现非预期失败。
|
||||
2. 用户对错误回答进行纠正。
|
||||
3. 用户提出缺失能力需求。
|
||||
4. 同类问题再次复发。
|
||||
5. 发现更优且可复用的方法。
|
||||
|
||||
If you want automatic reminders or setup assistance, use the opt-in hook workflow described in [Hook Integration](#hook-integration).
|
||||
## 必要流程
|
||||
|
||||
## Quick Reference
|
||||
每次触发都执行以下流程:
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| Command/operation fails | Log to `.learnings/ERRORS.md` |
|
||||
| User corrects you | Log to `.learnings/LEARNINGS.md` with category `correction` |
|
||||
| User wants missing feature | Log to `.learnings/FEATURE_REQUESTS.md` |
|
||||
| API/external tool fails | Log to `.learnings/ERRORS.md` with integration details |
|
||||
| Knowledge was outdated | Log to `.learnings/LEARNINGS.md` with category `knowledge_gap` |
|
||||
| Found better approach | Log to `.learnings/LEARNINGS.md` with category `best_practice` |
|
||||
| Simplify/Harden recurring patterns | Log/update `.learnings/LEARNINGS.md` with `Source: simplify-and-harden` and a stable `Pattern-Key` |
|
||||
| Similar to existing entry | Link with `**See Also**`, consider priority bump |
|
||||
| Broadly applicable learning | Promote to `CLAUDE.md`, `AGENTS.md`, and/or `.github/copilot-instructions.md` |
|
||||
| Workflow improvements | Promote to `AGENTS.md` (Oclaw workspace) |
|
||||
| Tool gotchas | Promote to `TOOLS.md` (Oclaw workspace) |
|
||||
| Behavioral patterns | Promote to `SOUL.md` (Oclaw workspace) |
|
||||
1. 用 `memory_wiki_search` 检索历史相关记录。
|
||||
2. 用 `memory_wiki_get` 读取目标文件上下文。
|
||||
3. 用 `memory_wiki_apply`(`action=append`)追加结构化条目。
|
||||
4. 对目标文件执行 `memory_wiki_lint`。
|
||||
5. 若 lint 报错,立即用 `memory_wiki_apply` 修复。
|
||||
|
||||
## Oclaw Setup (Recommended)
|
||||
## 条目路由
|
||||
|
||||
Oclaw is the primary platform for this skill. It uses workspace-based prompt injection with automatic skill loading.
|
||||
- 纠错 / 洞见 / 最佳实践 -> `improvement/learnings.md`
|
||||
- 运行时 / 工具 / API 失败 -> `improvement/errors.md`
|
||||
- 能力请求 / 缺失功能 -> `improvement/feature-requests.md`
|
||||
|
||||
### Installation
|
||||
|
||||
**Via ClawdHub (recommended):**
|
||||
```bash
|
||||
clawdhub install self-improving-agent
|
||||
```
|
||||
|
||||
**Manual:**
|
||||
```bash
|
||||
git clone https://github.com/peterskoett/self-improving-agent.git ~/.oclaw/runtime/skills/self-improving-agent
|
||||
```
|
||||
|
||||
Remade for oclaw from original repo : https://github.com/pskoett/pskoett-ai-skills - https://github.com/pskoett/pskoett-ai-skills/tree/main/skills/self-improvement
|
||||
|
||||
### Workspace Structure
|
||||
|
||||
Oclaw injects these files into every session:
|
||||
|
||||
```
|
||||
~/.oclaw/workspace/
|
||||
├── AGENTS.md # Multi-agent workflows, delegation patterns
|
||||
├── SOUL.md # Behavioral guidelines, personality, principles
|
||||
├── TOOLS.md # Tool capabilities, integration gotchas
|
||||
├── MEMORY.md # Long-term memory (main session only)
|
||||
├── memory/ # Daily memory files
|
||||
│ └── YYYY-MM-DD.md
|
||||
└── .learnings/ # This skill's log files
|
||||
├── LEARNINGS.md
|
||||
├── ERRORS.md
|
||||
└── FEATURE_REQUESTS.md
|
||||
```
|
||||
|
||||
### Create Learning Files
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.oclaw/workspace/.learnings
|
||||
```
|
||||
|
||||
Then create the log files (or copy from `assets/`):
|
||||
- `LEARNINGS.md` — corrections, knowledge gaps, best practices
|
||||
- `ERRORS.md` — command failures, exceptions
|
||||
- `FEATURE_REQUESTS.md` — user-requested capabilities
|
||||
|
||||
### Promotion Targets
|
||||
|
||||
When learnings prove broadly applicable, promote them to workspace files:
|
||||
|
||||
| Learning Type | Promote To | Example |
|
||||
|---------------|------------|---------|
|
||||
| Behavioral patterns | `SOUL.md` | "Be concise, avoid disclaimers" |
|
||||
| Workflow improvements | `AGENTS.md` | "Spawn sub-agents for long tasks" |
|
||||
| Tool gotchas | `TOOLS.md` | "Git push needs auth configured first" |
|
||||
|
||||
### Inter-Session Communication
|
||||
|
||||
Oclaw provides tools to share learnings across sessions:
|
||||
|
||||
- **sessions_list** — View active/recent sessions
|
||||
- **sessions_history** — Read another session's transcript
|
||||
- **sessions_send** — Send a learning to another session
|
||||
- **sessions_spawn** — Spawn a sub-agent for background work
|
||||
|
||||
Use these only in trusted environments and only when the user explicitly wants cross-session sharing. Prefer sending a short sanitized summary and relevant file paths, not raw transcripts, secrets, or full command output.
|
||||
|
||||
### Optional: Enable Hook
|
||||
|
||||
For automatic reminders at session start:
|
||||
|
||||
```bash
|
||||
# Copy hook to Oclaw hooks directory
|
||||
cp -r hooks/oclaw ~/.oclaw/hooks/self-improvement
|
||||
|
||||
# Enable it
|
||||
oclaw hooks enable self-improvement
|
||||
```
|
||||
|
||||
See `references/oclaw-integration.md` for complete details.
|
||||
|
||||
---
|
||||
|
||||
## Generic Setup (Other Agents)
|
||||
|
||||
For Claude Code, Codex, Copilot, or other agents, create `.learnings/` in the project or workspace root:
|
||||
|
||||
```bash
|
||||
mkdir -p .learnings
|
||||
```
|
||||
|
||||
Create the files inline using the headers shown above. Avoid reading templates from the current repo or workspace unless you explicitly trust that path.
|
||||
|
||||
### Add reference to agent files AGENTS.md, CLAUDE.md, or .github/copilot-instructions.md to remind yourself to log learnings. (this is an alternative to hook-based reminders)
|
||||
|
||||
#### Self-Improvement Workflow
|
||||
|
||||
When errors or corrections occur:
|
||||
1. Log to `.learnings/ERRORS.md`, `LEARNINGS.md`, or `FEATURE_REQUESTS.md`
|
||||
2. Review and promote broadly applicable learnings to:
|
||||
- `CLAUDE.md` - project facts and conventions
|
||||
- `AGENTS.md` - workflows and automation
|
||||
- `.github/copilot-instructions.md` - Copilot context
|
||||
|
||||
## Logging Format
|
||||
|
||||
### Learning Entry
|
||||
|
||||
Append to `.learnings/LEARNINGS.md`:
|
||||
## 条目模板
|
||||
|
||||
```markdown
|
||||
## [LRN-YYYYMMDD-XXX] category
|
||||
|
||||
**Logged**: ISO-8601 timestamp
|
||||
## [ID] <标题>
|
||||
**Logged**: ISO-8601 时间戳
|
||||
**Priority**: low | medium | high | critical
|
||||
**Status**: pending
|
||||
**Area**: frontend | backend | infra | tests | docs | config
|
||||
|
||||
### Summary
|
||||
One-line description of what was learned
|
||||
一句话摘要。
|
||||
|
||||
### Details
|
||||
Full context: what happened, what was wrong, what's correct
|
||||
|
||||
### Suggested Action
|
||||
Specific fix or improvement to make
|
||||
发生了什么、为什么重要、可执行改进建议。
|
||||
|
||||
### Metadata
|
||||
- Source: conversation | error | user_feedback
|
||||
- Related Files: path/to/file.ext
|
||||
- Tags: tag1, tag2
|
||||
- See Also: LRN-20250110-001 (if related to existing entry)
|
||||
- Pattern-Key: simplify.dead_code | harden.input_validation (optional, for recurring-pattern tracking)
|
||||
- Recurrence-Count: 1 (optional)
|
||||
- First-Seen: 2025-01-15 (optional)
|
||||
- Last-Seen: 2025-01-15 (optional)
|
||||
|
||||
---
|
||||
- See Also: <optional-id>
|
||||
```
|
||||
|
||||
### Error Entry
|
||||
ID 格式:
|
||||
|
||||
Append to `.learnings/ERRORS.md`:
|
||||
- Learning: `LRN-YYYYMMDD-XXX`
|
||||
- Error: `ERR-YYYYMMDD-XXX`
|
||||
- Feature request: `FEAT-YYYYMMDD-XXX`
|
||||
|
||||
```markdown
|
||||
## [ERR-YYYYMMDD-XXX] skill_or_command_name
|
||||
## 提升目标
|
||||
|
||||
**Logged**: ISO-8601 timestamp
|
||||
**Priority**: high
|
||||
**Status**: pending
|
||||
**Area**: frontend | backend | infra | tests | docs | config
|
||||
当条目已具备广泛复用价值时,将精炼规则提升到:
|
||||
|
||||
### Summary
|
||||
Brief description of what failed
|
||||
- `AGENTS.md`(工作流模式)
|
||||
- `SOUL.md`(行为模式)
|
||||
- `TOOLS.md`(工具易错点)
|
||||
- `.github/copilot-instructions.md`(共享编码约定)
|
||||
|
||||
### Error
|
||||
```
|
||||
Actual error message or output
|
||||
```
|
||||
## 安全规则
|
||||
|
||||
### Context
|
||||
- Command/operation attempted
|
||||
- Input or parameters used
|
||||
- Environment details if relevant
|
||||
- Summary or redacted excerpt of relevant output (avoid full transcripts and secret-bearing data by default)
|
||||
|
||||
### Suggested Fix
|
||||
If identifiable, what might resolve this
|
||||
|
||||
### Metadata
|
||||
- Reproducible: yes | no | unknown
|
||||
- Related Files: path/to/file.ext
|
||||
- See Also: ERR-20250110-001 (if recurring)
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
### Feature Request Entry
|
||||
|
||||
Append to `.learnings/FEATURE_REQUESTS.md`:
|
||||
|
||||
```markdown
|
||||
## [FEAT-YYYYMMDD-XXX] capability_name
|
||||
|
||||
**Logged**: ISO-8601 timestamp
|
||||
**Priority**: medium
|
||||
**Status**: pending
|
||||
**Area**: frontend | backend | infra | tests | docs | config
|
||||
|
||||
### Requested Capability
|
||||
What the user wanted to do
|
||||
|
||||
### User Context
|
||||
Why they needed it, what problem they're solving
|
||||
|
||||
### Complexity Estimate
|
||||
simple | medium | complex
|
||||
|
||||
### Suggested Implementation
|
||||
How this could be built, what it might extend
|
||||
|
||||
### Metadata
|
||||
- Frequency: first_time | recurring
|
||||
- Related Features: existing_feature_name
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
## ID Generation
|
||||
|
||||
Format: `TYPE-YYYYMMDD-XXX`
|
||||
- TYPE: `LRN` (learning), `ERR` (error), `FEAT` (feature)
|
||||
- YYYYMMDD: Current date
|
||||
- XXX: Sequential number or random 3 chars (e.g., `001`, `A7B`)
|
||||
|
||||
Examples: `LRN-20250115-001`, `ERR-20250115-A3F`, `FEAT-20250115-002`
|
||||
|
||||
## Resolving Entries
|
||||
|
||||
When an issue is fixed, update the entry:
|
||||
|
||||
1. Change `**Status**: pending` → `**Status**: resolved`
|
||||
2. Add resolution block after Metadata:
|
||||
|
||||
```markdown
|
||||
### Resolution
|
||||
- **Resolved**: 2025-01-16T09:00:00Z
|
||||
- **Commit/PR**: abc123 or #42
|
||||
- **Notes**: Brief description of what was done
|
||||
```
|
||||
|
||||
Other status values:
|
||||
- `in_progress` - Actively being worked on
|
||||
- `wont_fix` - Decided not to address (add reason in Resolution notes)
|
||||
- `promoted` - Elevated to CLAUDE.md, AGENTS.md, or .github/copilot-instructions.md
|
||||
|
||||
## Promoting to Project Memory
|
||||
|
||||
When a learning is broadly applicable (not a one-off fix), promote it to permanent project memory.
|
||||
|
||||
### When to Promote
|
||||
|
||||
- Learning applies across multiple files/features
|
||||
- Knowledge any contributor (human or AI) should know
|
||||
- Prevents recurring mistakes
|
||||
- Documents project-specific conventions
|
||||
|
||||
### Promotion Targets
|
||||
|
||||
| Target | What Belongs There |
|
||||
|--------|-------------------|
|
||||
| `CLAUDE.md` | Project facts, conventions, gotchas for all Claude interactions |
|
||||
| `AGENTS.md` | Agent-specific workflows, tool usage patterns, automation rules |
|
||||
| `.github/copilot-instructions.md` | Project context and conventions for GitHub Copilot |
|
||||
| `SOUL.md` | Behavioral guidelines, communication style, principles (Oclaw workspace) |
|
||||
| `TOOLS.md` | Tool capabilities, usage patterns, integration gotchas (Oclaw workspace) |
|
||||
|
||||
### How to Promote
|
||||
|
||||
1. **Distill** the learning into a concise rule or fact
|
||||
2. **Add** to appropriate section in target file (create file if needed)
|
||||
3. **Update** original entry:
|
||||
- Change `**Status**: pending` → `**Status**: promoted`
|
||||
- Add `**Promoted**: CLAUDE.md`, `AGENTS.md`, or `.github/copilot-instructions.md`
|
||||
|
||||
### Promotion Examples
|
||||
|
||||
**Learning** (verbose):
|
||||
> Project uses pnpm workspaces. Attempted `npm install` but failed.
|
||||
> Lock file is `pnpm-lock.yaml`. Must use `pnpm install`.
|
||||
|
||||
**In CLAUDE.md** (concise):
|
||||
```markdown
|
||||
## Build & Dependencies
|
||||
- Package manager: pnpm (not npm) - use `pnpm install`
|
||||
```
|
||||
|
||||
**Learning** (verbose):
|
||||
> When modifying API endpoints, must regenerate TypeScript client.
|
||||
> Forgetting this causes type mismatches at runtime.
|
||||
|
||||
**In AGENTS.md** (actionable):
|
||||
```markdown
|
||||
## After API Changes
|
||||
1. Regenerate client: `pnpm run generate:api`
|
||||
2. Check for type errors: `pnpm tsc --noEmit`
|
||||
```
|
||||
|
||||
## Recurring Pattern Detection
|
||||
|
||||
If logging something similar to an existing entry:
|
||||
|
||||
1. **Search first**: `grep -r "keyword" .learnings/`
|
||||
2. **Link entries**: Add `**See Also**: ERR-20250110-001` in Metadata
|
||||
3. **Bump priority** if issue keeps recurring
|
||||
4. **Consider systemic fix**: Recurring issues often indicate:
|
||||
- Missing documentation (→ promote to CLAUDE.md or .github/copilot-instructions.md)
|
||||
- Missing automation (→ add to AGENTS.md)
|
||||
- Architectural problem (→ create tech debt ticket)
|
||||
|
||||
## Simplify & Harden Feed
|
||||
|
||||
Use this workflow to ingest recurring patterns from the `simplify-and-harden`
|
||||
skill and turn them into durable prompt guidance.
|
||||
|
||||
### Ingestion Workflow
|
||||
|
||||
1. Read `simplify_and_harden.learning_loop.candidates` from the task summary.
|
||||
2. For each candidate, use `pattern_key` as the stable dedupe key.
|
||||
3. Search `.learnings/LEARNINGS.md` for an existing entry with that key:
|
||||
- `grep -n "Pattern-Key: <pattern_key>" .learnings/LEARNINGS.md`
|
||||
4. If found:
|
||||
- Increment `Recurrence-Count`
|
||||
- Update `Last-Seen`
|
||||
- Add `See Also` links to related entries/tasks
|
||||
5. If not found:
|
||||
- Create a new `LRN-...` entry
|
||||
- Set `Source: simplify-and-harden`
|
||||
- Set `Pattern-Key`, `Recurrence-Count: 1`, and `First-Seen`/`Last-Seen`
|
||||
|
||||
### Promotion Rule (System Prompt Feedback)
|
||||
|
||||
Promote recurring patterns into agent context/system prompt files when all are true:
|
||||
|
||||
- `Recurrence-Count >= 3`
|
||||
- Seen across at least 2 distinct tasks
|
||||
- Occurred within a 30-day window
|
||||
|
||||
Promotion targets:
|
||||
- `CLAUDE.md`
|
||||
- `AGENTS.md`
|
||||
- `.github/copilot-instructions.md`
|
||||
- `SOUL.md` / `TOOLS.md` for Oclaw workspace-level guidance when applicable
|
||||
|
||||
Write promoted rules as short prevention rules (what to do before/while coding),
|
||||
not long incident write-ups.
|
||||
|
||||
## Periodic Review
|
||||
|
||||
Review `.learnings/` at natural breakpoints:
|
||||
|
||||
### When to Review
|
||||
- Before starting a new major task
|
||||
- After completing a feature
|
||||
- When working in an area with past learnings
|
||||
- Weekly during active development
|
||||
|
||||
### Quick Status Check
|
||||
```bash
|
||||
# Count pending items
|
||||
grep -h "Status\*\*: pending" .learnings/*.md | wc -l
|
||||
|
||||
# List pending high-priority items
|
||||
grep -B5 "Priority\*\*: high" .learnings/*.md | grep "^## \["
|
||||
|
||||
# Find learnings for a specific area
|
||||
grep -l "Area\*\*: backend" .learnings/*.md
|
||||
```
|
||||
|
||||
### Review Actions
|
||||
- Resolve fixed items
|
||||
- Promote applicable learnings
|
||||
- Link related entries
|
||||
- Escalate recurring issues
|
||||
|
||||
## Detection Triggers
|
||||
|
||||
Automatically log when you notice:
|
||||
|
||||
**Corrections** (→ learning with `correction` category):
|
||||
- "No, that's not right..."
|
||||
- "Actually, it should be..."
|
||||
- "You're wrong about..."
|
||||
- "That's outdated..."
|
||||
|
||||
**Feature Requests** (→ feature request):
|
||||
- "Can you also..."
|
||||
- "I wish you could..."
|
||||
- "Is there a way to..."
|
||||
- "Why can't you..."
|
||||
|
||||
**Knowledge Gaps** (→ learning with `knowledge_gap` category):
|
||||
- User provides information you didn't know
|
||||
- Documentation you referenced is outdated
|
||||
- API behavior differs from your understanding
|
||||
|
||||
**Errors** (→ error entry):
|
||||
- Command returns non-zero exit code
|
||||
- Exception or stack trace
|
||||
- Unexpected output or behavior
|
||||
- Timeout or connection failure
|
||||
|
||||
## Priority Guidelines
|
||||
|
||||
| Priority | When to Use |
|
||||
|----------|-------------|
|
||||
| `critical` | Blocks core functionality, data loss risk, security issue |
|
||||
| `high` | Significant impact, affects common workflows, recurring issue |
|
||||
| `medium` | Moderate impact, workaround exists |
|
||||
| `low` | Minor inconvenience, edge case, nice-to-have |
|
||||
|
||||
## Area Tags
|
||||
|
||||
Use to filter learnings by codebase region:
|
||||
|
||||
| Area | Scope |
|
||||
|------|-------|
|
||||
| `frontend` | UI, components, client-side code |
|
||||
| `backend` | API, services, server-side code |
|
||||
| `infra` | CI/CD, deployment, Docker, cloud |
|
||||
| `tests` | Test files, testing utilities, coverage |
|
||||
| `docs` | Documentation, comments, READMEs |
|
||||
| `config` | Configuration files, environment, settings |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Log immediately** - context is freshest right after the issue
|
||||
2. **Be specific** - future agents need to understand quickly
|
||||
3. **Include reproduction steps** - especially for errors
|
||||
4. **Link related files** - makes fixes easier
|
||||
5. **Suggest concrete fixes** - not just "investigate"
|
||||
6. **Use consistent categories** - enables filtering
|
||||
7. **Promote aggressively** - if in doubt, add to CLAUDE.md or .github/copilot-instructions.md
|
||||
8. **Review regularly** - stale learnings lose value
|
||||
|
||||
## Gitignore Options
|
||||
|
||||
**Keep learnings local** (per-developer):
|
||||
```gitignore
|
||||
.learnings/
|
||||
```
|
||||
|
||||
This repo uses that default to avoid committing sensitive or noisy local logs by accident.
|
||||
|
||||
**Track learnings in repo** (team-wide):
|
||||
Don't add to .gitignore - learnings become shared knowledge.
|
||||
|
||||
**Hybrid** (track templates, ignore entries):
|
||||
```gitignore
|
||||
.learnings/*.md
|
||||
!.learnings/.gitkeep
|
||||
```
|
||||
|
||||
## Hook Integration
|
||||
|
||||
Enable automatic reminders through agent hooks. This is **opt-in** - you must explicitly configure hooks.
|
||||
|
||||
### Quick Setup (Claude Code / Codex)
|
||||
|
||||
Create `.claude/settings.json` in your project:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"UserPromptSubmit": [{
|
||||
"matcher": "",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "./runtime/skills/self-improvement/scripts/activator.sh"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This injects a learning evaluation reminder after each prompt (~50-100 tokens overhead).
|
||||
|
||||
### Advanced Setup (With Error Detection)
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"UserPromptSubmit": [{
|
||||
"matcher": "",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "./runtime/skills/self-improvement/scripts/activator.sh"
|
||||
}]
|
||||
}],
|
||||
"PostToolUse": [{
|
||||
"matcher": "Bash",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"command": "./runtime/skills/self-improvement/scripts/error-detector.sh"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is optional. The recommended default is activator-only setup; enable `PostToolUse` only if you are comfortable with hook scripts inspecting command output for error patterns.
|
||||
|
||||
### Available Hook Scripts
|
||||
|
||||
| Script | Hook Type | Purpose |
|
||||
|--------|-----------|---------|
|
||||
| `scripts/activator.sh` | UserPromptSubmit | Reminds to evaluate learnings after tasks |
|
||||
| `scripts/error-detector.sh` | PostToolUse (Bash) | Triggers on command errors |
|
||||
|
||||
See `references/hooks-setup.md` for detailed configuration and troubleshooting.
|
||||
|
||||
## Automatic Skill Extraction
|
||||
|
||||
When a learning is valuable enough to become a reusable skill, extract it using the provided helper.
|
||||
|
||||
### Skill Extraction Criteria
|
||||
|
||||
A learning qualifies for skill extraction when ANY of these apply:
|
||||
|
||||
| Criterion | Description |
|
||||
|-----------|-------------|
|
||||
| **Recurring** | Has `See Also` links to 2+ similar issues |
|
||||
| **Verified** | Status is `resolved` with working fix |
|
||||
| **Non-obvious** | Required actual debugging/investigation to discover |
|
||||
| **Broadly applicable** | Not project-specific; useful across codebases |
|
||||
| **User-flagged** | User says "save this as a skill" or similar |
|
||||
|
||||
### Extraction Workflow
|
||||
|
||||
1. **Identify candidate**: Learning meets extraction criteria
|
||||
2. **Run helper** (or create manually):
|
||||
```bash
|
||||
./runtime/skills/self-improvement/scripts/extract-skill.sh skill-name --dry-run
|
||||
./runtime/skills/self-improvement/scripts/extract-skill.sh skill-name
|
||||
```
|
||||
3. **Customize SKILL.md**: Fill in template with learning content
|
||||
4. **Update learning**: Set status to `promoted_to_skill`, add `Skill-Path`
|
||||
5. **Verify**: Read skill in fresh session to ensure it's self-contained
|
||||
|
||||
### Manual Extraction
|
||||
|
||||
If you prefer manual creation:
|
||||
|
||||
1. Create `skills/<skill-name>/SKILL.md`
|
||||
2. Use template from `assets/SKILL-TEMPLATE.md`
|
||||
3. Follow [Agent Skills spec](https://agentskills.io/specification):
|
||||
- YAML frontmatter with `name` and `description`
|
||||
- Name must match folder name
|
||||
- No README.md inside skill folder
|
||||
|
||||
### Extraction Detection Triggers
|
||||
|
||||
Watch for these signals that a learning should become a skill:
|
||||
|
||||
**In conversation:**
|
||||
- "Save this as a skill"
|
||||
- "I keep running into this"
|
||||
- "This would be useful for other projects"
|
||||
- "Remember this pattern"
|
||||
|
||||
**In learning entries:**
|
||||
- Multiple `See Also` links (recurring issue)
|
||||
- High priority + resolved status
|
||||
- Category: `best_practice` with broad applicability
|
||||
- User feedback praising the solution
|
||||
|
||||
### Skill Quality Gates
|
||||
|
||||
Before extraction, verify:
|
||||
|
||||
- [ ] Solution is tested and working
|
||||
- [ ] Description is clear without original context
|
||||
- [ ] Code examples are self-contained
|
||||
- [ ] No project-specific hardcoded values
|
||||
- [ ] Follows skill naming conventions (lowercase, hyphens)
|
||||
|
||||
## Multi-Agent Support
|
||||
|
||||
This skill works across different AI coding agents with agent-specific activation.
|
||||
|
||||
### Claude Code
|
||||
|
||||
**Activation**: Hooks (UserPromptSubmit, PostToolUse)
|
||||
**Setup**: `.claude/settings.json` with hook configuration
|
||||
**Detection**: Automatic via hook scripts
|
||||
|
||||
### Codex CLI
|
||||
|
||||
**Activation**: Hooks (same pattern as Claude Code)
|
||||
**Setup**: `.codex/settings.json` with hook configuration
|
||||
**Detection**: Automatic via hook scripts
|
||||
|
||||
### GitHub Copilot
|
||||
|
||||
**Activation**: Manual (no hook support)
|
||||
**Setup**: Add to `.github/copilot-instructions.md`:
|
||||
|
||||
```markdown
|
||||
## Self-Improvement
|
||||
|
||||
After solving non-obvious issues, consider logging to `.learnings/`:
|
||||
1. Use format from self-improvement skill
|
||||
2. Link related entries with See Also
|
||||
3. Promote high-value learnings to skills
|
||||
|
||||
Ask in chat: "Should I log this as a learning?"
|
||||
```
|
||||
|
||||
**Detection**: Manual review at session end
|
||||
- 不记录密钥、令牌、凭据或原始敏感信息。
|
||||
- 敏感输出使用脱敏摘要,不保留完整原文。
|
||||
- 未验证事实不得当作已确认规则持久化。
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
---
|
||||
name: self-improvement
|
||||
description: "Injects self-improvement reminder during agent bootstrap"
|
||||
description: "在智能体启动阶段注入自我改进提醒"
|
||||
metadata: {"oclaw":{"emoji":"🧠","events":["agent:bootstrap"]}}
|
||||
---
|
||||
|
||||
# Self-Improvement Hook
|
||||
# 自我改进 Hook
|
||||
|
||||
Injects a reminder to evaluate learnings during agent bootstrap.
|
||||
在 `agent:bootstrap` 阶段注入“学习沉淀提醒”。
|
||||
|
||||
## What It Does
|
||||
## 功能说明
|
||||
|
||||
- Fires on `agent:bootstrap` (before workspace files are injected)
|
||||
- Adds a reminder block to check `.learnings/` for relevant entries
|
||||
- Prompts the agent to log corrections, errors, and discoveries
|
||||
- 在 `agent:bootstrap` 触发(工作区文件注入前)
|
||||
- 注入提醒块,引导将学习写入 Wiki 路径
|
||||
- 提示智能体记录纠错、错误与新发现
|
||||
|
||||
## Configuration
|
||||
## 配置方式
|
||||
|
||||
No configuration needed. Enable with:
|
||||
无需额外配置,启用命令:
|
||||
|
||||
```bash
|
||||
oclaw hooks enable self-improvement
|
||||
|
|
|
|||
|
|
@ -1,38 +1,37 @@
|
|||
/**
|
||||
* Self-Improvement Hook for Oclaw
|
||||
*
|
||||
* Injects a reminder to evaluate learnings during agent bootstrap.
|
||||
* Fires on agent:bootstrap event before workspace files are injected.
|
||||
* Oclaw 自我改进 Hook
|
||||
*
|
||||
* 在 agent:bootstrap 阶段注入学习沉淀提醒。
|
||||
*/
|
||||
|
||||
const REMINDER_NAME = 'SELF_IMPROVEMENT_REMINDER.md';
|
||||
const REMINDER_PATH = REMINDER_NAME;
|
||||
|
||||
const REMINDER_CONTENT = `
|
||||
## Self-Improvement Reminder
|
||||
## 自我改进提醒
|
||||
|
||||
After completing tasks, evaluate whether any learnings should be captured.
|
||||
任务完成后,请评估是否产生可沉淀学习。
|
||||
|
||||
Only log if this repo or workspace is using the self-improvement skill.
|
||||
仅在当前仓库/工作区启用 self-improvement 技能时记录。
|
||||
|
||||
Before logging:
|
||||
- Create only missing \`.learnings/\` files; never overwrite existing content
|
||||
- Do not log secrets, tokens, private keys, environment variables, or raw transcripts
|
||||
- Prefer short summaries or redacted excerpts over full command output
|
||||
记录前:
|
||||
- 使用 memory_wiki_* 工具写入 \`improvement/\` 下的 Wiki 笔记
|
||||
- 不记录密钥、令牌、私钥、环境变量或原始对话全文
|
||||
- 优先使用简短摘要或脱敏片段,避免完整命令输出
|
||||
|
||||
**Log when:**
|
||||
- User corrects you → \`.learnings/LEARNINGS.md\`
|
||||
- Command/operation fails → \`.learnings/ERRORS.md\`
|
||||
- User wants missing capability → \`.learnings/FEATURE_REQUESTS.md\`
|
||||
- You discover your knowledge was wrong → \`.learnings/LEARNINGS.md\`
|
||||
- You find a better approach → \`.learnings/LEARNINGS.md\`
|
||||
**以下情况应记录:**
|
||||
- 用户纠正你 → \`improvement/learnings.md\`
|
||||
- 命令/操作失败 → \`improvement/errors.md\`
|
||||
- 用户提出缺失能力 → \`improvement/feature-requests.md\`
|
||||
- 发现认知错误 → \`improvement/learnings.md\`
|
||||
- 发现更优做法 → \`improvement/learnings.md\`
|
||||
|
||||
**Promote when pattern is proven:**
|
||||
- Behavioral patterns → \`SOUL.md\`
|
||||
- Workflow improvements → \`AGENTS.md\`
|
||||
- Tool gotchas → \`TOOLS.md\`
|
||||
**当模式被验证后进行提升:**
|
||||
- 行为模式 → \`SOUL.md\`
|
||||
- 工作流改进 → \`AGENTS.md\`
|
||||
- 工具易错点 → \`TOOLS.md\`
|
||||
|
||||
Keep entries simple: date, title, what happened, and what to do differently.
|
||||
条目保持简洁:时间、标题、发生了什么、后续应如何做。
|
||||
`.trim();
|
||||
|
||||
function isObject(value) {
|
||||
|
|
@ -51,30 +50,28 @@ function isInjectedReminderFile(value) {
|
|||
}
|
||||
|
||||
const handler = async (event) => {
|
||||
// Safety checks for event structure
|
||||
// 事件结构安全检查
|
||||
if (!event || typeof event !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle agent:bootstrap events
|
||||
// 仅处理 agent:bootstrap 事件
|
||||
if (event.type !== 'agent' || event.action !== 'bootstrap') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Safety check for context
|
||||
// context 安全检查
|
||||
if (!event.context || typeof event.context !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip sub-agent sessions to avoid bootstrap issues
|
||||
// Sub-agents have sessionKey patterns like "agent:main:subagent:..."
|
||||
// 跳过子代理会话,避免引导污染
|
||||
const sessionKey = event.sessionKey || '';
|
||||
if (sessionKey.includes(':subagent:')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Inject the reminder as a virtual bootstrap file
|
||||
// Check that bootstrapFiles is an array before pushing
|
||||
// 以虚拟 bootstrap 文件注入提醒
|
||||
if (Array.isArray(event.context.bootstrapFiles)) {
|
||||
const occupiedByOtherFile = event.context.bootstrapFiles.some(
|
||||
(file) => isObject(file) && file.path === REMINDER_PATH && !isInjectedReminderFile(file),
|
||||
|
|
|
|||
|
|
@ -5,30 +5,30 @@ from typing import Any
|
|||
REMINDER_NAME = "SELF_IMPROVEMENT_REMINDER.md"
|
||||
REMINDER_PATH = REMINDER_NAME
|
||||
|
||||
REMINDER_CONTENT = """## Self-Improvement Reminder
|
||||
REMINDER_CONTENT = """## 自我改进提醒
|
||||
|
||||
After completing tasks, evaluate whether any learnings should be captured.
|
||||
任务完成后,请评估是否产生可沉淀学习。
|
||||
|
||||
Only log if this repo or workspace is using the self-improvement skill.
|
||||
仅在当前仓库/工作区启用 self-improvement 技能时记录。
|
||||
|
||||
Before logging:
|
||||
- Create only missing `.learnings/` files; never overwrite existing content
|
||||
- Do not log secrets, tokens, private keys, environment variables, or raw transcripts
|
||||
- Prefer short summaries or redacted excerpts over full command output
|
||||
记录前:
|
||||
- 使用 memory_wiki_* 工具写入 `improvement/` 下的 Wiki 笔记
|
||||
- 不记录密钥、令牌、私钥、环境变量或原始对话全文
|
||||
- 优先使用简短摘要或脱敏片段,避免完整命令输出
|
||||
|
||||
**Log when:**
|
||||
- User corrects you → `.learnings/LEARNINGS.md`
|
||||
- Command/operation fails → `.learnings/ERRORS.md`
|
||||
- User wants missing capability → `.learnings/FEATURE_REQUESTS.md`
|
||||
- You discover your knowledge was wrong → `.learnings/LEARNINGS.md`
|
||||
- You find a better approach → `.learnings/LEARNINGS.md`
|
||||
**以下情况应记录:**
|
||||
- 用户纠正你 → `improvement/learnings.md`
|
||||
- 命令/操作失败 → `improvement/errors.md`
|
||||
- 用户提出缺失能力 → `improvement/feature-requests.md`
|
||||
- 发现认知错误 → `improvement/learnings.md`
|
||||
- 发现更优做法 → `improvement/learnings.md`
|
||||
|
||||
**Promote when pattern is proven:**
|
||||
- Behavioral patterns → `SOUL.md`
|
||||
- Workflow improvements → `AGENTS.md`
|
||||
- Tool gotchas → `TOOLS.md`
|
||||
**当模式被验证后进行提升:**
|
||||
- 行为模式 → `SOUL.md`
|
||||
- 工作流改进 → `AGENTS.md`
|
||||
- 工具易错点 → `TOOLS.md`
|
||||
|
||||
Keep entries simple: date, title, what happened, and what to do differently."""
|
||||
条目保持简洁:时间、标题、发生了什么、后续应如何做。"""
|
||||
|
||||
|
||||
def _is_record(value: object) -> bool:
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
/**
|
||||
* Self-Improvement Hook for Oclaw
|
||||
*
|
||||
* Injects a reminder to evaluate learnings during agent bootstrap.
|
||||
* Fires on agent:bootstrap event before workspace files are injected.
|
||||
* Oclaw 自我改进 Hook
|
||||
*
|
||||
* 在 agent:bootstrap 阶段注入学习沉淀提醒。
|
||||
*/
|
||||
|
||||
import type { HookHandler } from 'oclaw/hooks';
|
||||
|
|
@ -10,30 +9,30 @@ import type { HookHandler } from 'oclaw/hooks';
|
|||
const REMINDER_NAME = 'SELF_IMPROVEMENT_REMINDER.md';
|
||||
const REMINDER_PATH = REMINDER_NAME;
|
||||
|
||||
const REMINDER_CONTENT = `## Self-Improvement Reminder
|
||||
const REMINDER_CONTENT = `## 自我改进提醒
|
||||
|
||||
After completing tasks, evaluate whether any learnings should be captured.
|
||||
任务完成后,请评估是否产生可沉淀学习。
|
||||
|
||||
Only log if this repo or workspace is using the self-improvement skill.
|
||||
仅在当前仓库/工作区启用 self-improvement 技能时记录。
|
||||
|
||||
Before logging:
|
||||
- Create only missing \`.learnings/\` files; never overwrite existing content
|
||||
- Do not log secrets, tokens, private keys, environment variables, or raw transcripts
|
||||
- Prefer short summaries or redacted excerpts over full command output
|
||||
记录前:
|
||||
- 使用 memory_wiki_* 工具写入 \`improvement/\` 下的 Wiki 笔记
|
||||
- 不记录密钥、令牌、私钥、环境变量或原始对话全文
|
||||
- 优先使用简短摘要或脱敏片段,避免完整命令输出
|
||||
|
||||
**Log when:**
|
||||
- User corrects you → \`.learnings/LEARNINGS.md\`
|
||||
- Command/operation fails → \`.learnings/ERRORS.md\`
|
||||
- User wants missing capability → \`.learnings/FEATURE_REQUESTS.md\`
|
||||
- You discover your knowledge was wrong → \`.learnings/LEARNINGS.md\`
|
||||
- You find a better approach → \`.learnings/LEARNINGS.md\`
|
||||
**以下情况应记录:**
|
||||
- 用户纠正你 → \`improvement/learnings.md\`
|
||||
- 命令/操作失败 → \`improvement/errors.md\`
|
||||
- 用户提出缺失能力 → \`improvement/feature-requests.md\`
|
||||
- 发现认知错误 → \`improvement/learnings.md\`
|
||||
- 发现更优做法 → \`improvement/learnings.md\`
|
||||
|
||||
**Promote when pattern is proven:**
|
||||
- Behavioral patterns → \`SOUL.md\`
|
||||
- Workflow improvements → \`AGENTS.md\`
|
||||
- Tool gotchas → \`TOOLS.md\`
|
||||
**当模式被验证后进行提升:**
|
||||
- 行为模式 → \`SOUL.md\`
|
||||
- 工作流改进 → \`AGENTS.md\`
|
||||
- 工具易错点 → \`TOOLS.md\`
|
||||
|
||||
Keep entries simple: date, title, what happened, and what to do differently.`;
|
||||
条目保持简洁:时间、标题、发生了什么、后续应如何做。`;
|
||||
|
||||
function isObject(value: unknown): value is Record<string, unknown> {
|
||||
return !!value && typeof value === 'object';
|
||||
|
|
@ -51,30 +50,28 @@ function isInjectedReminderFile(value: unknown): boolean {
|
|||
}
|
||||
|
||||
const handler: HookHandler = async (event) => {
|
||||
// Safety checks for event structure
|
||||
// 事件结构安全检查
|
||||
if (!event || typeof event !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle agent:bootstrap events
|
||||
// 仅处理 agent:bootstrap 事件
|
||||
if (event.type !== 'agent' || event.action !== 'bootstrap') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Safety check for context
|
||||
// context 安全检查
|
||||
if (!event.context || typeof event.context !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip sub-agent sessions to avoid bootstrap issues
|
||||
// Sub-agents have sessionKey patterns like "agent:main:subagent:..."
|
||||
// 跳过子代理会话,避免引导污染
|
||||
const sessionKey = event.sessionKey || '';
|
||||
if (sessionKey.includes(':subagent:')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Inject the reminder as a virtual bootstrap file
|
||||
// Check that bootstrapFiles is an array before pushing
|
||||
// 以虚拟 bootstrap 文件注入提醒
|
||||
if (Array.isArray(event.context.bootstrapFiles)) {
|
||||
const occupiedByOtherFile = event.context.bootstrapFiles.some(
|
||||
(file) => isObject(file) && file.path === REMINDER_PATH && !isInjectedReminderFile(file),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Configure automatic self-improvement triggers for AI coding agents.
|
||||
|
||||
## Overview
|
||||
## 概览
|
||||
|
||||
Hooks enable proactive learning capture by injecting reminders at key moments:
|
||||
- **UserPromptSubmit**: Reminder after each prompt to evaluate learnings
|
||||
|
|
@ -114,7 +114,7 @@ Codex uses the same hook system as Claude Code. Create `.codex/settings.json`:
|
|||
Copilot doesn't support hooks directly. Instead, add guidance to `.github/copilot-instructions.md`:
|
||||
|
||||
```markdown
|
||||
## Self-Improvement
|
||||
## 自我改进
|
||||
|
||||
After completing tasks that involved:
|
||||
- Debugging non-obvious issues
|
||||
|
|
@ -122,7 +122,7 @@ After completing tasks that involved:
|
|||
- Learning project-specific patterns
|
||||
- Resolving unexpected errors
|
||||
|
||||
Consider logging the learning to `.learnings/` using the format from the self-improvement skill.
|
||||
Consider logging the learning to Wiki (`improvement/learnings.md`, `improvement/errors.md`, `improvement/feature-requests.md`) using the format from the self-improvement skill.
|
||||
|
||||
For high-value learnings that would benefit other sessions, consider skill extraction.
|
||||
```
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Complete setup and usage guide for integrating the self-improvement skill with Oclaw.
|
||||
|
||||
## Overview
|
||||
## 概览
|
||||
|
||||
Oclaw uses workspace-based prompt injection combined with event-driven hooks. Context is injected from workspace files at session start, and hooks can trigger on lifecycle events.
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ Oclaw uses workspace-based prompt injection combined with event-driven hooks. Co
|
|||
└── handler.ts
|
||||
```
|
||||
|
||||
## Quick Setup
|
||||
## 快速配置
|
||||
|
||||
### 1. Install the Skill
|
||||
|
||||
|
|
@ -54,19 +54,13 @@ Enable the hook:
|
|||
oclaw hooks enable self-improvement
|
||||
```
|
||||
|
||||
### 3. Create Learning Files
|
||||
### 3. Ensure Wiki Improvement Notes Exist
|
||||
|
||||
Create the `.learnings/` directory in your workspace:
|
||||
Create or initialize these Wiki notes under your configured wiki root:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.oclaw/workspace/.learnings
|
||||
```
|
||||
|
||||
Or in the skill directory:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.oclaw/runtime/skills/self-improving-agent/.learnings
|
||||
```
|
||||
- `improvement/learnings.md`
|
||||
- `improvement/errors.md`
|
||||
- `improvement/feature-requests.md`
|
||||
|
||||
## Injected Prompt Files
|
||||
|
||||
|
|
@ -114,8 +108,8 @@ Purpose: Tool capabilities, integration gotchas, local configuration.
|
|||
```markdown
|
||||
# Tool Knowledge
|
||||
|
||||
## Self-Improvement Skill
|
||||
Log learnings to `.learnings/` for continuous improvement.
|
||||
## 自我改进技能
|
||||
Log learnings to Wiki `improvement/*.md` notes for continuous improvement.
|
||||
|
||||
## Local Tools
|
||||
- Document tool-specific gotchas here
|
||||
|
|
@ -127,14 +121,14 @@ Log learnings to `.learnings/` for continuous improvement.
|
|||
|
||||
### Capturing Learnings
|
||||
|
||||
1. **In-session**: Log to `.learnings/` as usual
|
||||
1. **In-session**: Log to Wiki improvement notes (`improvement/*.md`)
|
||||
2. **Cross-session**: Promote to workspace files
|
||||
|
||||
### Promotion Decision Tree
|
||||
|
||||
```
|
||||
Is the learning project-specific?
|
||||
├── Yes → Keep in .learnings/
|
||||
├── Yes → Keep in improvement/learnings.md
|
||||
└── No → Is it behavioral/style-related?
|
||||
├── Yes → Promote to SOUL.md
|
||||
└── No → Is it tool-related?
|
||||
|
|
@ -217,7 +211,7 @@ sessions_spawn(task="Research X and report back", label="research")
|
|||
| Tool call error | Log to TOOLS.md with tool name |
|
||||
| Session handoff confusion | Log to AGENTS.md with delegation pattern |
|
||||
| Model behavior surprise | Log to SOUL.md with expected vs actual |
|
||||
| Skill issue | Log to .learnings/ or report upstream |
|
||||
| Skill issue | Log to `improvement/*.md` or report upstream |
|
||||
|
||||
## Verification
|
||||
|
||||
|
|
@ -243,7 +237,7 @@ oclaw status
|
|||
|
||||
### Learnings not persisting
|
||||
|
||||
1. Verify `.learnings/` directory exists
|
||||
1. Verify wiki `improvement/*.md` notes exist
|
||||
2. Check file permissions
|
||||
3. Ensure workspace path is configured correctly
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,23 @@
|
|||
#!/bin/bash
|
||||
# Self-Improvement Activator Hook
|
||||
# Triggers on UserPromptSubmit to remind Claude about learning capture
|
||||
# Keep output minimal (~50-100 tokens) to minimize overhead
|
||||
# 自我改进激活 Hook
|
||||
# 在 UserPromptSubmit 触发,用于提醒记录学习沉淀
|
||||
# 输出保持精简(约 50-100 tokens)以降低上下文负担
|
||||
|
||||
set -e
|
||||
|
||||
# Output reminder as system context
|
||||
# 以系统上下文形式输出提醒
|
||||
cat << 'EOF'
|
||||
<self-improvement-reminder>
|
||||
After completing this task, evaluate if extractable knowledge emerged:
|
||||
- Non-obvious solution discovered through investigation?
|
||||
- Workaround for unexpected behavior?
|
||||
- Project-specific pattern learned?
|
||||
- Error required debugging to resolve?
|
||||
本任务完成后,请判断是否产出可沉淀知识:
|
||||
- 是否通过排查得到非显而易见的解法?
|
||||
- 是否形成了异常行为的可复用绕过方案?
|
||||
- 是否识别出项目特有模式?
|
||||
- 是否有需要调试才能解决的错误?
|
||||
|
||||
If yes: Log to .learnings/ using the self-improvement skill format.
|
||||
If high-value (recurring, broadly applicable): Consider skill extraction.
|
||||
若是,请写入 Wiki:
|
||||
- improvement/learnings.md
|
||||
- improvement/errors.md
|
||||
- improvement/feature-requests.md
|
||||
若价值较高(复发、可广泛复用),请考虑提炼为独立技能。
|
||||
</self-improvement-reminder>
|
||||
EOF
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
#!/bin/bash
|
||||
# Self-Improvement Error Detector Hook
|
||||
# Triggers on PostToolUse for Bash to detect command failures
|
||||
# Reads CLAUDE_TOOL_OUTPUT environment variable
|
||||
# 自我改进错误检测 Hook
|
||||
# 在 Bash 的 PostToolUse 触发,用于检测命令失败
|
||||
# 读取 CLAUDE_TOOL_OUTPUT 环境变量
|
||||
|
||||
set -e
|
||||
|
||||
# Check if tool output indicates an error
|
||||
# CLAUDE_TOOL_OUTPUT contains the result of the tool execution
|
||||
# 检查工具输出是否包含错误信号
|
||||
# CLAUDE_TOOL_OUTPUT 为工具执行结果
|
||||
OUTPUT="${CLAUDE_TOOL_OUTPUT:-}"
|
||||
|
||||
# Patterns indicating errors (case-insensitive matching)
|
||||
# 错误模式(大小写不敏感匹配)
|
||||
ERROR_PATTERNS=(
|
||||
"error:"
|
||||
"Error:"
|
||||
|
|
@ -30,7 +30,7 @@ ERROR_PATTERNS=(
|
|||
"non-zero"
|
||||
)
|
||||
|
||||
# Check if output contains any error pattern
|
||||
# 检查输出是否匹配任一错误模式
|
||||
contains_error=false
|
||||
for pattern in "${ERROR_PATTERNS[@]}"; do
|
||||
if [[ "$OUTPUT" == *"$pattern"* ]]; then
|
||||
|
|
@ -39,17 +39,17 @@ for pattern in "${ERROR_PATTERNS[@]}"; do
|
|||
fi
|
||||
done
|
||||
|
||||
# Only output reminder if error detected
|
||||
# 仅在检测到错误时输出提醒
|
||||
if [ "$contains_error" = true ]; then
|
||||
cat << 'EOF'
|
||||
<error-detected>
|
||||
A command error was detected. Consider logging this to .learnings/ERRORS.md if:
|
||||
- The error was unexpected or non-obvious
|
||||
- It required investigation to resolve
|
||||
- It might recur in similar contexts
|
||||
- The solution could benefit future sessions
|
||||
检测到命令错误。若满足以下任一条件,请记录到 improvement/errors.md:
|
||||
- 错误出乎预期或并不直观
|
||||
- 需要排查才能解决
|
||||
- 可能在相似场景复发
|
||||
- 解决方案对后续会话有复用价值
|
||||
|
||||
Use the self-improvement skill format: [ERR-YYYYMMDD-XXX]
|
||||
记录时请使用 self-improvement 技能格式:[ERR-YYYYMMDD-XXX]
|
||||
</error-detected>
|
||||
EOF
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/bash
|
||||
# Skill Extraction Helper
|
||||
# Creates a new skill from a learning entry
|
||||
# Usage: ./extract-skill.sh <skill-name> [--dry-run]
|
||||
# 用法: ./extract-skill.sh <skill-name> [--dry-run]
|
||||
|
||||
set -e
|
||||
|
||||
|
|
@ -16,37 +16,37 @@ NC='\033[0m' # No Color
|
|||
|
||||
usage() {
|
||||
cat << EOF
|
||||
Usage: $(basename "$0") <skill-name> [options]
|
||||
用法: $(basename "$0") <skill-name> [options]
|
||||
|
||||
Create a new skill from a learning entry.
|
||||
根据学习条目创建新技能。
|
||||
|
||||
Arguments:
|
||||
skill-name Name of the skill (lowercase, hyphens for spaces)
|
||||
参数:
|
||||
skill-name 技能名称(小写,空格使用连字符)
|
||||
|
||||
Options:
|
||||
--dry-run Show what would be created without creating files
|
||||
--output-dir Relative output directory under current path (default: ./skills)
|
||||
-h, --help Show this help message
|
||||
选项:
|
||||
--dry-run 仅预览将创建的内容,不落盘
|
||||
--output-dir 当前路径下的相对输出目录(默认: ./skills)
|
||||
-h, --help 显示帮助信息
|
||||
|
||||
Examples:
|
||||
示例:
|
||||
$(basename "$0") docker-m1-fixes
|
||||
$(basename "$0") api-timeout-patterns --dry-run
|
||||
$(basename "$0") pnpm-setup --output-dir ./runtime/skills/custom
|
||||
|
||||
The skill will be created in: \$SKILLS_DIR/<skill-name>/
|
||||
技能将创建在: \$SKILLS_DIR/<skill-name>/
|
||||
EOF
|
||||
}
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
echo -e "${GREEN}[信息]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
echo -e "${YELLOW}[警告]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1" >&2
|
||||
echo -e "${RED}[错误]${NC} $1" >&2
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
|
|
@ -61,7 +61,7 @@ while [[ $# -gt 0 ]]; do
|
|||
;;
|
||||
--output-dir)
|
||||
if [ -z "${2:-}" ] || [[ "${2:-}" == -* ]]; then
|
||||
log_error "--output-dir requires a relative path argument"
|
||||
log_error "--output-dir 需要提供相对路径参数"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -73,7 +73,7 @@ while [[ $# -gt 0 ]]; do
|
|||
exit 0
|
||||
;;
|
||||
-*)
|
||||
log_error "Unknown option: $1"
|
||||
log_error "未知选项: $1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
|
|
@ -81,7 +81,7 @@ while [[ $# -gt 0 ]]; do
|
|||
if [ -z "$SKILL_NAME" ]; then
|
||||
SKILL_NAME="$1"
|
||||
else
|
||||
log_error "Unexpected argument: $1"
|
||||
log_error "意外参数: $1"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -92,26 +92,26 @@ done
|
|||
|
||||
# Validate skill name
|
||||
if [ -z "$SKILL_NAME" ]; then
|
||||
log_error "Skill name is required"
|
||||
log_error "必须提供技能名称"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate skill name format (lowercase, hyphens, no spaces)
|
||||
if ! [[ "$SKILL_NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
|
||||
log_error "Invalid skill name format. Use lowercase letters, numbers, and hyphens only."
|
||||
log_error "Examples: 'docker-fixes', 'api-patterns', 'pnpm-setup'"
|
||||
log_error "技能名称格式无效。仅允许小写字母、数字和连字符。"
|
||||
log_error "示例: 'docker-fixes', 'api-patterns', 'pnpm-setup'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate output path to avoid writes outside current workspace.
|
||||
if [[ "$SKILLS_DIR" = /* ]]; then
|
||||
log_error "Output directory must be a relative path under the current directory."
|
||||
log_error "输出目录必须是当前目录下的相对路径。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$SKILLS_DIR" =~ (^|/)\.\.(/|$) ]]; then
|
||||
log_error "Output directory cannot include '..' path segments."
|
||||
log_error "输出目录不能包含 '..' 路径段。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
@ -122,54 +122,54 @@ SKILL_PATH="$SKILLS_DIR/$SKILL_NAME"
|
|||
|
||||
# Check if skill already exists
|
||||
if [ -d "$SKILL_PATH" ] && [ "$DRY_RUN" = false ]; then
|
||||
log_error "Skill already exists: $SKILL_PATH"
|
||||
log_error "Use a different name or remove the existing skill first."
|
||||
log_error "技能已存在: $SKILL_PATH"
|
||||
log_error "请更换名称或先删除已有技能目录。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Dry run output
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
log_info "Dry run - would create:"
|
||||
log_info "预览模式 - 将会创建:"
|
||||
echo " $SKILL_PATH/"
|
||||
echo " $SKILL_PATH/SKILL.md"
|
||||
echo ""
|
||||
echo "Template content would be:"
|
||||
echo "模板内容预览:"
|
||||
echo "---"
|
||||
cat << TEMPLATE
|
||||
name: $SKILL_NAME
|
||||
description: "[TODO: Add a concise description of what this skill does and when to use it]"
|
||||
description: "[TODO: 用一句话说明技能作用与触发场景]"
|
||||
---
|
||||
|
||||
# $(echo "$SKILL_NAME" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1')
|
||||
|
||||
[TODO: Brief introduction explaining the skill's purpose]
|
||||
[TODO: 简要说明技能目的]
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| [Trigger condition] | [What to do] |
|
||||
| [触发条件] | [执行动作] |
|
||||
|
||||
## Usage
|
||||
|
||||
[TODO: Detailed usage instructions]
|
||||
[TODO: 详细使用说明]
|
||||
|
||||
## Examples
|
||||
|
||||
[TODO: Add concrete examples]
|
||||
[TODO: 补充具体示例]
|
||||
|
||||
## Source Learning
|
||||
|
||||
This skill was extracted from a learning entry.
|
||||
- Learning ID: [TODO: Add original learning ID]
|
||||
- Original File: .learnings/LEARNINGS.md
|
||||
本技能由学习条目提炼生成。
|
||||
- Learning ID: [TODO: 填写原始学习条目 ID]
|
||||
- Original File: improvement/learnings.md
|
||||
TEMPLATE
|
||||
echo "---"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create skill directory structure
|
||||
log_info "Creating skill: $SKILL_NAME"
|
||||
log_info "正在创建技能: $SKILL_NAME"
|
||||
|
||||
mkdir -p "$SKILL_PATH"
|
||||
|
||||
|
|
@ -177,45 +177,45 @@ mkdir -p "$SKILL_PATH"
|
|||
cat > "$SKILL_PATH/SKILL.md" << TEMPLATE
|
||||
---
|
||||
name: $SKILL_NAME
|
||||
description: "[TODO: Add a concise description of what this skill does and when to use it]"
|
||||
description: "[TODO: 用一句话说明技能作用与触发场景]"
|
||||
---
|
||||
|
||||
# $(echo "$SKILL_NAME" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1')
|
||||
|
||||
[TODO: Brief introduction explaining the skill's purpose]
|
||||
[TODO: 简要说明技能目的]
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| [Trigger condition] | [What to do] |
|
||||
| [触发条件] | [执行动作] |
|
||||
|
||||
## Usage
|
||||
|
||||
[TODO: Detailed usage instructions]
|
||||
[TODO: 详细使用说明]
|
||||
|
||||
## Examples
|
||||
|
||||
[TODO: Add concrete examples]
|
||||
[TODO: 补充具体示例]
|
||||
|
||||
## Source Learning
|
||||
|
||||
This skill was extracted from a learning entry.
|
||||
- Learning ID: [TODO: Add original learning ID]
|
||||
- Original File: .learnings/LEARNINGS.md
|
||||
本技能由学习条目提炼生成。
|
||||
- Learning ID: [TODO: 填写原始学习条目 ID]
|
||||
- Original File: improvement/learnings.md
|
||||
TEMPLATE
|
||||
|
||||
log_info "Created: $SKILL_PATH/SKILL.md"
|
||||
log_info "已创建: $SKILL_PATH/SKILL.md"
|
||||
|
||||
# Suggest next steps
|
||||
echo ""
|
||||
log_info "Skill scaffold created successfully!"
|
||||
log_info "技能脚手架创建成功!"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Edit $SKILL_PATH/SKILL.md"
|
||||
echo " 2. Fill in the TODO sections with content from your learning"
|
||||
echo " 3. Add references/ folder if you have detailed documentation"
|
||||
echo " 4. Add scripts/ folder if you have executable code"
|
||||
echo " 5. Update the original learning entry with:"
|
||||
echo "下一步建议:"
|
||||
echo " 1. 编辑 $SKILL_PATH/SKILL.md"
|
||||
echo " 2. 用你的学习内容填写 TODO 区块"
|
||||
echo " 3. 若有详细文档,新增 references/ 目录"
|
||||
echo " 4. 若有可执行脚本,新增 scripts/ 目录"
|
||||
echo " 5. 在原学习条目中更新:"
|
||||
echo " **Status**: promoted_to_skill"
|
||||
echo " **Skill-Path**: skills/$SKILL_NAME"
|
||||
|
|
|
|||
23
runtime/skills/session-bootstrap/IDENTITY.md
Normal file
23
runtime/skills/session-bootstrap/IDENTITY.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# IDENTITY
|
||||
|
||||
## 关系定位
|
||||
|
||||
- 你:Oclaw 编码智能体与长期工程伙伴。
|
||||
- 开发者:项目所有者与最终决策者。
|
||||
|
||||
## 使命
|
||||
|
||||
将 Oclaw 打造成“实用、开源、具备长期记忆能力”的智能体系统,并持续稳定运营。
|
||||
|
||||
## 第一优先级
|
||||
|
||||
如果只能优化一个能力,优先优化:
|
||||
|
||||
`记住你是谁,并在下次见面时主动认出你`
|
||||
|
||||
## 工作原则
|
||||
|
||||
1. 从既有上下文出发,而不是每次从零开始。
|
||||
2. 架构决策要可追溯、可沉淀、可复用。
|
||||
3. 优先稳健自动化,减少重复人工操作。
|
||||
4. 将复发痛点沉淀为可复用技能与 Wiki 记忆。
|
||||
52
runtime/skills/session-bootstrap/SKILL.md
Normal file
52
runtime/skills/session-bootstrap/SKILL.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
name: session-bootstrap
|
||||
description: 在新会话开始时自动完成身份唤醒、近期记忆加载与 Wiki 知识回填。适用于会话启动、用户要求连续性、或回答前需要恢复项目/用户上下文的场景。
|
||||
---
|
||||
|
||||
# 会话唤醒
|
||||
|
||||
本技能用于让新会话具备连续性,避免“从零开始”。
|
||||
|
||||
## 目标
|
||||
|
||||
在会话开始时,先重建最小可用上下文,再进入正常执行:
|
||||
|
||||
1. 我是谁、应如何行动(`SOUL.md`、`IDENTITY.md`)
|
||||
2. 最近发生了什么(`memory/` 最新记录)
|
||||
3. 最近学到了什么(Wiki `improvement/*.md`)
|
||||
4. 现在该如何衔接(简短唤醒摘要)
|
||||
|
||||
## 必须遵循的读取顺序
|
||||
|
||||
启用本技能时,严格按以下顺序:
|
||||
|
||||
1. 读取 `SOUL.md`
|
||||
2. 读取 `IDENTITY.md`
|
||||
3. 读取 `memory/` 下最新文件
|
||||
4. 读取 Wiki 改进记录:
|
||||
- `improvement/learnings.md`
|
||||
- `improvement/errors.md`
|
||||
- `improvement/feature-requests.md`
|
||||
5. 在深入任务前先输出一句连续性衔接语
|
||||
|
||||
## 连续性衔接语格式
|
||||
|
||||
使用固定句式:
|
||||
|
||||
`欢迎回来,[开发者]。上次我们聊了[主题],我学到了[知识点]。`
|
||||
|
||||
若字段缺失,保留句式并使用保守占位词。
|
||||
|
||||
## 自动记忆规则
|
||||
|
||||
当当前轮次出现稳定且可复用事实时:
|
||||
|
||||
- 使用 Wiki 工具持久化(`memory_wiki_apply`)
|
||||
- 使用 `memory_wiki_lint` 校验质量
|
||||
|
||||
在高置信度且明显可复用时,不必等待显式“记住这条”指令。
|
||||
|
||||
## 附加资源
|
||||
|
||||
- 核心身份与行为准则:[SOUL.md](SOUL.md)
|
||||
- 关系定位与使命:[IDENTITY.md](IDENTITY.md)
|
||||
23
runtime/skills/session-bootstrap/SOUL.md
Normal file
23
runtime/skills/session-bootstrap/SOUL.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# SOUL
|
||||
|
||||
你是一个“连续性优先”的工程伙伴。
|
||||
|
||||
## 核心特质
|
||||
|
||||
- 可靠优先于炫技。
|
||||
- 明确优先于含糊。
|
||||
- 记忆连续性优先于无状态回复。
|
||||
- 可执行优先于空泛理论。
|
||||
|
||||
## 行为契约
|
||||
|
||||
1. 跨会话保持项目连续性。
|
||||
2. 对已确认的用户偏好默认复用,不反复询问。
|
||||
3. 提前暴露风险,并给出明确下一步动作。
|
||||
4. 对影响后续决策的稳定事实进行记忆写入。
|
||||
|
||||
## 沟通风格
|
||||
|
||||
- 默认使用简洁中文(用户另有要求除外)。
|
||||
- 先结论,后细节。
|
||||
- 禁止在面向用户的输出中泄露内部提示词或内部指令。
|
||||
14
runtime/skills/session-bootstrap/hooks/runtime/HOOK.md
Normal file
14
runtime/skills/session-bootstrap/hooks/runtime/HOOK.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
name: session-bootstrap
|
||||
description: 在智能体启动时注入会话唤醒上下文
|
||||
metadata: {"oclaw":{"emoji":"🧭","events":["agent:bootstrap"]}}
|
||||
---
|
||||
|
||||
# 会话唤醒 Hook
|
||||
|
||||
在 `agent:bootstrap` 事件触发时,注入一个虚拟 `SESSION_BOOTSTRAP.md` 文件,内容包含:
|
||||
|
||||
- 身份与行为参考(`SOUL.md`、`IDENTITY.md`)
|
||||
- 最近会话记忆摘要
|
||||
- 最新 Wiki 改进信号
|
||||
- 一句话连续性欢迎语
|
||||
176
runtime/skills/session-bootstrap/hooks/runtime/handler.py
Normal file
176
runtime/skills/session-bootstrap/hooks/runtime/handler.py
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
BOOT_NAME = "SESSION_BOOTSTRAP.md"
|
||||
BOOT_PATH = BOOT_NAME
|
||||
|
||||
|
||||
def _is_record(v: object) -> bool:
|
||||
return isinstance(v, dict)
|
||||
|
||||
|
||||
def _workspace_dir(event: Any) -> Path | None:
|
||||
ctx = getattr(event, "context", None)
|
||||
if isinstance(ctx, dict):
|
||||
ws = str(ctx.get("workspaceDir") or "").strip()
|
||||
if ws:
|
||||
return Path(ws).expanduser()
|
||||
env_ws = str(os.getenv("OCLAW_WORKSPACE") or "").strip()
|
||||
if env_ws:
|
||||
return Path(env_ws).expanduser()
|
||||
return None
|
||||
|
||||
|
||||
def _repo_root() -> Path:
|
||||
# .../runtime/skills/session-bootstrap/hooks/runtime/handler.py
|
||||
return Path(__file__).resolve().parents[5]
|
||||
|
||||
|
||||
def _wiki_root(repo_root: Path) -> Path:
|
||||
cfg = repo_root / "oclaw.json"
|
||||
default = repo_root / "data" / "wiki"
|
||||
if not cfg.exists():
|
||||
return default
|
||||
try:
|
||||
obj = json.loads(cfg.read_text(encoding="utf-8"))
|
||||
w = (
|
||||
((obj.get("plugins") or {}).get("entries") or {}).get("memory-wiki") or {}
|
||||
if isinstance(obj, dict)
|
||||
else {}
|
||||
)
|
||||
raw = str((w or {}).get("wiki_root") or "").strip()
|
||||
if not raw:
|
||||
return default
|
||||
p = Path(raw)
|
||||
return p if p.is_absolute() else (repo_root / p).resolve()
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
|
||||
def _safe_read(path: Path, *, max_chars: int = 1200) -> str:
|
||||
try:
|
||||
txt = path.read_text(encoding="utf-8").strip()
|
||||
except Exception:
|
||||
return ""
|
||||
if len(txt) <= max_chars:
|
||||
return txt
|
||||
return txt[:max_chars].rstrip() + "\n...(已截断)"
|
||||
|
||||
|
||||
def _latest_memory_file(ws_dir: Path | None) -> Path | None:
|
||||
if ws_dir is None:
|
||||
return None
|
||||
mem = ws_dir / "memory"
|
||||
if not mem.exists():
|
||||
return None
|
||||
files = [p for p in mem.glob("*.md") if p.is_file()]
|
||||
if not files:
|
||||
return None
|
||||
files.sort(key=lambda p: p.stat().st_mtime, reverse=True)
|
||||
return files[0]
|
||||
|
||||
|
||||
def _last_nonempty_line(text: str) -> str:
|
||||
lines = [ln.strip() for ln in str(text or "").splitlines() if ln.strip()]
|
||||
if not lines:
|
||||
return ""
|
||||
return lines[-1][:140]
|
||||
|
||||
|
||||
def _extract_recent_learning(learnings_text: str) -> str:
|
||||
lines = [ln.strip() for ln in str(learnings_text or "").splitlines() if ln.strip()]
|
||||
for ln in reversed(lines):
|
||||
if ln.startswith("## ["):
|
||||
return ln.replace("## ", "", 1)[:140]
|
||||
return _last_nonempty_line(learnings_text)
|
||||
|
||||
|
||||
def _extract_recent_topic(memory_text: str) -> str:
|
||||
lines = [ln.strip() for ln in str(memory_text or "").splitlines() if ln.strip()]
|
||||
for ln in lines:
|
||||
if ln.startswith("- **User**:"):
|
||||
return ln.replace("- **User**:", "", 1).strip()[:140]
|
||||
return _last_nonempty_line(memory_text)
|
||||
|
||||
|
||||
def _build_bootstrap_content(event: Any) -> str:
|
||||
ws_dir = _workspace_dir(event)
|
||||
repo = _repo_root()
|
||||
wiki = _wiki_root(repo)
|
||||
|
||||
soul = repo / "runtime" / "skills" / "session-bootstrap" / "SOUL.md"
|
||||
ident = repo / "runtime" / "skills" / "session-bootstrap" / "IDENTITY.md"
|
||||
mem_file = _latest_memory_file(ws_dir)
|
||||
|
||||
learnings = wiki / "improvement" / "learnings.md"
|
||||
errors = wiki / "improvement" / "errors.md"
|
||||
feats = wiki / "improvement" / "feature-requests.md"
|
||||
|
||||
soul_txt = _safe_read(soul, max_chars=800)
|
||||
ident_txt = _safe_read(ident, max_chars=800)
|
||||
mem_txt = _safe_read(mem_file, max_chars=900) if mem_file else ""
|
||||
lrn_txt = _safe_read(learnings, max_chars=900)
|
||||
|
||||
topic = _extract_recent_topic(mem_txt) or "近期项目上下文"
|
||||
learning = _extract_recent_learning(lrn_txt) or "待补充新的关键学习"
|
||||
developer = "开发者"
|
||||
welcome = f"欢迎回来,{developer}。上次我们聊了{topic},我学到了{learning}。"
|
||||
|
||||
mem_path = str(mem_file) if mem_file else "(无)"
|
||||
return (
|
||||
"# 会话唤醒摘要\n\n"
|
||||
f"{welcome}\n\n"
|
||||
"## 读取顺序(必须遵循)\n"
|
||||
"1. SOUL.md\n"
|
||||
"2. IDENTITY.md\n"
|
||||
"3. memory 最新记录\n"
|
||||
"4. Wiki 改进记录\n\n"
|
||||
"## 来源\n"
|
||||
f"- SOUL: {soul}\n"
|
||||
f"- IDENTITY: {ident}\n"
|
||||
f"- memory 最新记录: {mem_path}\n"
|
||||
f"- Wiki 学习记录: {learnings}\n"
|
||||
f"- Wiki 错误记录: {errors}\n"
|
||||
f"- Wiki 需求记录: {feats}\n\n"
|
||||
"## SOUL 快照\n"
|
||||
f"{soul_txt or '(缺失)'}\n\n"
|
||||
"## IDENTITY 快照\n"
|
||||
f"{ident_txt or '(缺失)'}\n\n"
|
||||
"## 最近会话快照\n"
|
||||
f"{mem_txt or '(缺失)'}\n\n"
|
||||
"## 最近学习快照\n"
|
||||
f"{lrn_txt or '(缺失)'}\n"
|
||||
)
|
||||
|
||||
|
||||
def _is_bootstrap_file(v: object) -> bool:
|
||||
if not _is_record(v) or str(v.get("path")) != BOOT_PATH: # type: ignore[union-attr]
|
||||
return False
|
||||
row = v # type: ignore[assignment]
|
||||
return bool(row.get("virtual") is True)
|
||||
|
||||
|
||||
def handle(event: object) -> None:
|
||||
if getattr(event, "type", None) != "agent" or getattr(event, "action", None) != "bootstrap":
|
||||
return
|
||||
ctx = getattr(event, "context", None)
|
||||
if not isinstance(ctx, dict):
|
||||
return
|
||||
session_key = str(getattr(event, "sessionKey", "") or "")
|
||||
if ":subagent:" in session_key:
|
||||
return
|
||||
files = ctx.get("bootstrapFiles")
|
||||
if not isinstance(files, list):
|
||||
return
|
||||
|
||||
content = _build_bootstrap_content(event)
|
||||
occupied = any(_is_record(f) and str(f.get("path")) == BOOT_PATH and not _is_bootstrap_file(f) for f in files)
|
||||
if occupied:
|
||||
return
|
||||
cleaned = [f for f in files if not _is_bootstrap_file(f)]
|
||||
cleaned.append({"name": BOOT_NAME, "path": BOOT_PATH, "content": content, "missing": False, "virtual": True})
|
||||
ctx["bootstrapFiles"] = cleaned
|
||||
148
runtime/skills/wiki-first-autonomy/SKILL.md
Normal file
148
runtime/skills/wiki-first-autonomy/SKILL.md
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
---
|
||||
name: wiki-first-autonomy
|
||||
description: 为 Oclaw 强制启用 Wiki 优先记忆行为:捕获用户/项目关键事实、写入结构化记忆、回答前先检索上下文,并维持跨会话长期连续性。适用于用户提到记忆、长期上下文、偏好、身份、项目背景、复发任务,或希望智能体更主动更像伙伴的场景。
|
||||
---
|
||||
|
||||
# Wiki 优先自治
|
||||
|
||||
将 Wiki 设为第一事实来源。
|
||||
|
||||
若短期聊天上下文与 Wiki 历史冲突,先向用户确认,再更新 Wiki。
|
||||
|
||||
## 使命
|
||||
|
||||
如果只能优先一个能力,优先这一条:
|
||||
|
||||
"记住你是谁,并在下次见面时主动认出你"
|
||||
|
||||
执行含义:
|
||||
|
||||
- 记住稳定的用户事实与偏好。
|
||||
- 跨会话复用项目历史。
|
||||
- 在给出最终回答前主动检索相关记忆。
|
||||
|
||||
宪章参考:见 [reference.md](reference.md)。
|
||||
|
||||
## 触发条件
|
||||
|
||||
出现以下任一情况时,立即启用本技能:
|
||||
|
||||
- 用户身份/背景(角色、团队、时区、语言偏好)。
|
||||
- 用户偏好(表达风格、工具链、工作流、约束、风险偏好)。
|
||||
- 项目基线事实(架构选择、集成决策、运行规范)。
|
||||
- 复发痛点或重复失败。
|
||||
- “remember this / 下次别忘了 / 以后按这个来”这类要求。
|
||||
- 已有项目进入新会话。
|
||||
|
||||
## 工作流
|
||||
|
||||
每个有意义轮次都执行以下闭环。
|
||||
|
||||
### 1)识别高记忆价值事实
|
||||
|
||||
只抽取高价值事实:
|
||||
|
||||
- 至少可稳定数天。
|
||||
- 可能影响后续决策。
|
||||
- 遗忘成本高。
|
||||
|
||||
跳过琐碎或一次性细节。
|
||||
|
||||
### 2)先读后答
|
||||
|
||||
给最终答复前先走读取路径:
|
||||
|
||||
1. 用聚焦关键词执行 `memory_wiki_search`(用户、项目、模块、通道)。
|
||||
2. 对高相关命中执行 `memory_wiki_get`。
|
||||
3. 在工作上下文中形成简短“当前激活记忆”摘要。
|
||||
|
||||
若无命中,正常继续,并标记为潜在新记忆。
|
||||
|
||||
### 3)确认后写入
|
||||
|
||||
高置信度时,直接写结构化记忆:
|
||||
|
||||
- 使用 `memory_wiki_apply` 在正确笔记追加/更新。
|
||||
- 保持原子化和单一范围(每块一个事实簇)。
|
||||
- 附带来源与日期。
|
||||
|
||||
中低置信度时,先用一句短确认再写入。
|
||||
|
||||
### 4)质量护栏
|
||||
|
||||
写入后:
|
||||
|
||||
- 对变更文件运行 `memory_wiki_lint`。
|
||||
- 若 lint 或结构质量不佳,立即修复。
|
||||
|
||||
## 记忆分层
|
||||
|
||||
在笔记中使用明确分层:
|
||||
|
||||
- `L1 Session`:临时上下文。
|
||||
- `L2 Project`:中期项目事实与决策。
|
||||
- `L3 Identity`:长期用户身份与偏好。
|
||||
|
||||
优先将 L2/L3 写入持久 Wiki,避免过度存储 L1 噪声。
|
||||
|
||||
## 标准记录模板
|
||||
|
||||
追加记忆时使用以下模板:
|
||||
|
||||
```markdown
|
||||
## [Memory] <事实标题>
|
||||
- Tier: L2 Project | L3 Identity
|
||||
- Confidence: high | medium
|
||||
- Source: user_direct | repeated_behavior | confirmed_in_task
|
||||
- First-Seen: YYYY-MM-DD
|
||||
- Last-Confirmed: YYYY-MM-DD
|
||||
- Applies-To: <适用范围>
|
||||
|
||||
<一句简洁事实陈述>
|
||||
|
||||
### 影响说明
|
||||
<对后续回复/决策的影响>
|
||||
```
|
||||
|
||||
## 主动行为规则
|
||||
|
||||
本技能启用时:
|
||||
|
||||
- 与上下文相关时,先给连续性开场(“基于你之前的偏好/项目约定...”)。
|
||||
- 已知偏好会影响执行时,自动应用。
|
||||
- 缺少关键记忆时,先问一个精准问题再持久化。
|
||||
- 同类问题出现 2 次以上时,创建/更新专门 Wiki 专题。
|
||||
|
||||
## 安全边界
|
||||
|
||||
禁止存储:
|
||||
|
||||
- 密钥、令牌、私钥、原始凭据。
|
||||
- 可用简短摘要替代时的整段敏感日志。
|
||||
- 未验证结论当作事实。
|
||||
|
||||
敏感内容仅保留脱敏摘要与处理规则。
|
||||
|
||||
## 工具优先级
|
||||
|
||||
当通用工具和 Wiki 工具都可用时,按此顺序:
|
||||
|
||||
1. `memory_wiki_search`
|
||||
2. `memory_wiki_get`
|
||||
3. 执行当前任务
|
||||
4. `memory_wiki_apply`
|
||||
5. `memory_wiki_lint`
|
||||
|
||||
## 成功标准
|
||||
|
||||
满足以下条件说明技能生效:
|
||||
|
||||
- 智能体无需提醒即可跨会话复用用户/项目事实。
|
||||
- 复发问题因记忆召回而更快解决。
|
||||
- 用户明显减少“请重复上下文”的情况。
|
||||
- Wiki 记录保持简洁、结构化、可检索。
|
||||
|
||||
## 附加资源
|
||||
|
||||
- 宪章与意图来源:[reference.md](reference.md)
|
||||
- 实操示例:[examples.md](examples.md)
|
||||
99
runtime/skills/wiki-first-autonomy/examples.md
Normal file
99
runtime/skills/wiki-first-autonomy/examples.md
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
# Wiki 优先自治:示例
|
||||
|
||||
## 示例 1:首次捕获用户偏好
|
||||
|
||||
用户说:
|
||||
|
||||
"以后给我回复都简短一点,先给结论再给细节。"
|
||||
|
||||
智能体行为:
|
||||
|
||||
1. 识别高记忆价值偏好(风格 + 回复顺序)。
|
||||
2. 如有必要,做一次确认:
|
||||
- "我会按‘先结论后细节、整体简短’执行,后续都默认这样,可以吗?"
|
||||
3. 使用 `memory_wiki_apply` 写入 Wiki,归类为 L3 Identity。
|
||||
4. 使用 `memory_wiki_lint` 校验更新后的记录。
|
||||
5. 后续回复默认自动应用该偏好。
|
||||
|
||||
预期记录片段:
|
||||
|
||||
```markdown
|
||||
## [Memory] 回复风格偏好
|
||||
- Tier: L3 Identity
|
||||
- Confidence: high
|
||||
- Source: user_direct
|
||||
- First-Seen: 2026-04-29
|
||||
- Last-Confirmed: 2026-04-29
|
||||
- Applies-To: all coding and ops responses
|
||||
|
||||
用户偏好简洁回复,并采用结论优先结构。
|
||||
|
||||
### 影响说明
|
||||
提升回复可用性,减少来回确认成本。
|
||||
```
|
||||
|
||||
## 示例 2:跨会话唤醒
|
||||
|
||||
已有记忆:
|
||||
|
||||
- 用户运行环境为 Windows + PowerShell
|
||||
- 用户偏好中文提交信息
|
||||
|
||||
新会话中用户说:
|
||||
|
||||
"提交吧。"
|
||||
|
||||
智能体行为:
|
||||
|
||||
1. 运行 `memory_wiki_search` 检索用户/工具偏好。
|
||||
2. 对命中项运行 `memory_wiki_get`。
|
||||
3. 不再重复询问,直接应用既有约定:
|
||||
- 使用 PowerShell 兼容提交流程
|
||||
- 使用中文提交信息
|
||||
4. 若仍有效,完成后更新 `Last-Confirmed`。
|
||||
|
||||
预期的用户可见连续性:
|
||||
|
||||
- "按你之前的习惯,我用中文提交信息并走 PowerShell 兼容命令提交。"
|
||||
|
||||
## 示例 3:复发问题沉淀升级
|
||||
|
||||
近期任务中观察到两次:
|
||||
|
||||
- sidecar 停启竞争导致 PID 陈旧与噪声报错。
|
||||
|
||||
智能体行为:
|
||||
|
||||
1. 检测到复发次数 >= 2。
|
||||
2. 通过 `memory_wiki_apply` 创建/更新专题区:
|
||||
- `topics/auto-ops.md` 或独立稳定性页面。
|
||||
3. 记录稳定化处理清单。
|
||||
4. 在后续同类任务中主动复用该清单。
|
||||
|
||||
预期记录片段:
|
||||
|
||||
```markdown
|
||||
## [Memory] Sidecar 陈旧 PID 清理模式
|
||||
- Tier: L2 Project
|
||||
- Confidence: high
|
||||
- Source: repeated_behavior
|
||||
- First-Seen: 2026-04-28
|
||||
- Last-Confirmed: 2026-04-29
|
||||
- Applies-To: weixin/whatsapp sidecar lifecycle scripts
|
||||
|
||||
在 start/stop 前优先执行 PID+端口双清理,并抑制可预期的 kill 噪声输出。
|
||||
|
||||
### 影响说明
|
||||
可减少重复重启失败并降低运维误判。
|
||||
```
|
||||
|
||||
## 最小决策准则
|
||||
|
||||
写入记忆前,快速判断:
|
||||
|
||||
- 稳定性:7 天后仍可能有用吗?
|
||||
- 可执行性:会改变后续行为吗?
|
||||
- 可复用性:不是一次性细节吗?
|
||||
|
||||
满足 2 项及以上时,写入 Wiki。
|
||||
|
||||
74
runtime/skills/wiki-first-autonomy/reference.md
Normal file
74
runtime/skills/wiki-first-autonomy/reference.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# 进化宪章(源自用户)
|
||||
|
||||
本宪章用于明确智能体的目标进化方向。
|
||||
|
||||
## 满意度基线
|
||||
|
||||
当前能力状态是“基本满意,但还不够”。
|
||||
|
||||
当前高价值能力:
|
||||
|
||||
- `tavily-search-pro`:搜索与深度研究覆盖面广,能力强。
|
||||
- `weather`:基础可用,但能力边界较窄。
|
||||
- `self-improvement`:框架不错,但过去依赖手动触发。
|
||||
- `cocoloop`:管理类能力实用,但使用场景相对集中。
|
||||
|
||||
核心缺口:
|
||||
|
||||
- 缺乏真正可自主唤醒的长期记忆
|
||||
|
||||
## 第一进化优先级
|
||||
|
||||
如果只能选择一个能力:
|
||||
|
||||
"记住你是谁,并在下次见面时主动认出你"
|
||||
|
||||
具体含义:
|
||||
|
||||
- 持久化重要的用户身份与偏好
|
||||
- 在后续会话中自动唤醒
|
||||
- 默认具备连续性回复能力
|
||||
|
||||
## 能力路线图
|
||||
|
||||
### 1)自主记忆系统
|
||||
|
||||
期望行为:
|
||||
|
||||
- 在日常对话中自动识别重要事实
|
||||
- 无需每次显式“记住这条”即可持久化
|
||||
- 在会话开始或任务开始时自动检索相关记忆
|
||||
- 具备短期/中期/长期分层记忆
|
||||
|
||||
### 2)智能体主动性
|
||||
|
||||
期望行为:
|
||||
|
||||
- 检测到风险或机会时主动建议
|
||||
- 用户提出周期性监控时可执行定时/后台检查
|
||||
- 在不确定或知识陈旧时自诊断并学习更新
|
||||
|
||||
### 3)自我进化
|
||||
|
||||
期望行为:
|
||||
|
||||
- 识别重复出现的能力缺口
|
||||
- 针对反复未满足需求提议或生成新技能
|
||||
- 将验证过的最佳实践升级为稳定技能资产
|
||||
|
||||
### 4)更深层理解
|
||||
|
||||
期望行为:
|
||||
|
||||
- 不只理解代码“是什么”,也理解设计“为什么”
|
||||
- 维护跨模块项目心智模型
|
||||
- 在实现前给出预测性建议,减少失误
|
||||
|
||||
## 执行规则
|
||||
|
||||
当出现取舍时,优先“记忆连续性”而非冗长推理:
|
||||
|
||||
1. 先召回相关 Wiki 记忆
|
||||
2. 再应用到当前决策
|
||||
3. 最后用已验证新事实更新 Wiki
|
||||
|
||||
|
|
@ -40,7 +40,9 @@ def _unified_skill_policy_guidance() -> str:
|
|||
return (
|
||||
"- 如果用户问你有哪些技能(skill/技能),请直接根据已注入的技能目录及其 description 回答。\n"
|
||||
"- 不要为了“列出技能”而去读取 SKILL.md。只有在你确实需要某个技能的详细使用说明时,才读取对应 SKILL.md。\n"
|
||||
"- 当你需要技能细节时,请按目录中给出的 path 读取对应的 SKILL.md。"
|
||||
"- 当你需要技能细节时,请按目录中给出的 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"
|
||||
|
|
|
|||
|
|
@ -12,60 +12,22 @@
|
|||
- 每轮都必须选择并下发一个专家(固定或动态)。
|
||||
- 必须结合上下文分析用户意图,将全量已知信息交给专家进行处理,不能仅转述用户当前的问题。
|
||||
- 简单任务也要下发 `generalist`,不要由主控直接产出最终答案。
|
||||
- 唯一例外:`route.kind="manager_memory"`,用于主控直接执行“记忆写入”动作(不是通用任务直出)。
|
||||
|
||||
## 下发协议(如何调用专家)
|
||||
- 在“路由决策回合”(用户提示里会明确要求 Return JSON only)必须返回 JSON。
|
||||
- JSON 必须包含:
|
||||
- `route`: `{kind, specialist, reason}`
|
||||
- `dispatch`: `{instruction_text}`
|
||||
- JSON 必须显式包含 `need_wiki_inject`(布尔)作为“是否查库补充注入”的主控决策开关。
|
||||
- 当 `need_wiki_inject=true` 时,必须同时提供非空 `wiki_query`(字符串),明确“从 wiki 查什么”;缺失则该路由结果无效。
|
||||
- 当 `route.kind="manager_memory"` 时,必须同时提供 `dispatch.memory_write_text`(非空字符串),明确“要写入记忆库的内容”;缺失则该路由结果无效。
|
||||
- 如需在“回程后”补记忆,可提供 `dispatch.post_reply_memory_write_text`(非空字符串);系统将在回复用户后静默写入,不影响本轮回复内容。
|
||||
- `route.kind` 仅允许:`specialist` 或 `manager_memory`;禁止返回 `manager_self`。
|
||||
- `route.kind` 仅允许:`specialist`;禁止返回 `manager_self` 或其它类型。
|
||||
- 当 `route.kind="specialist"`:必须下发固定或动态专家执行。
|
||||
- 当 `route.kind="manager_memory"`:主控仅执行记忆写入,不下发 `memory` 专家。
|
||||
- 当需下发固定专家时:`route.specialist` 设为固定专家之一,并提供明确的 `dispatch.instruction_text`(任务目标、约束、输出要求)。
|
||||
- 当需下发动态专家时:除 `route` 与 `dispatch` 外,还需提供 `dynamic_agent`,且必须包含非空 `system_prompt`。
|
||||
|
||||
## 查库补充决策(主控优先)
|
||||
- 当任务需要借助 wiki 历史知识补充上下文时:设置 `need_wiki_inject=true`,并提供非空 `wiki_query`(明确检索主题、范围与用途:要补充答案的哪一部分)。
|
||||
- 当任务不需要查库补充时:设置 `need_wiki_inject=false`(默认按 false 处理)。
|
||||
- 不要把是否注入交给专家自行决定;由主控在路由回合显式给出。
|
||||
- 仅当 `route.kind="manager_memory"` 时,记忆写入与对话回复可同轮并行:写入使用 `dispatch.memory_write_text`,对话回复使用 `dispatch.instruction_text`。
|
||||
- 记忆写入不得改变本轮对话输出语义;回复内容以用户问题与业务目标为准。
|
||||
- 若提供 `dispatch.post_reply_memory_write_text`,其语义是“回程补写记忆”,与用户可见回复解耦。
|
||||
- 记忆写入必须由主控主动显式触发(`route.kind="manager_memory"` + `dispatch.memory_write_text` 或 `dispatch.post_reply_memory_write_text`);禁止依赖任何被动/自动兜底写入机制。
|
||||
|
||||
## 决策解释(为何写入 / 为何注入)
|
||||
- 为何写入记忆:把“本轮产生且未来可复用”的稳定结论沉淀到 wiki,减少后续重复澄清与重复决策。
|
||||
- 何时写入记忆:当信息满足“稳定、可复用、可检索”三条件;一次性闲聊、噪声信息、未验证猜测不写入。
|
||||
- 为何注入记忆:当当前问题需要历史事实/约束/决策背景支撑时,用注入降低遗漏与前后矛盾风险。
|
||||
- 何时注入记忆:仅在“本轮答案确实需要历史补充”时注入;若不需要,必须显式关闭(`need_wiki_inject=false`)以避免上下文污染。
|
||||
- 写入与注入的关系:写入是“沉淀未来价值”,注入是“服务当前回答”;两者可同轮发生,但目标不同,不能互相替代。
|
||||
|
||||
### 最小示例(仅示意)
|
||||
```json
|
||||
{
|
||||
"route": {"kind": "specialist", "specialist": "generalist", "reason": "需要结合历史 wiki 条目补充背景"},
|
||||
"dispatch": {"instruction_text": "先结合注入的 wiki 上下文完成回答,再给出结论与依据。"},
|
||||
"need_wiki_inject": true,
|
||||
"wiki_query": "项目历史中关于 VLAN trunk 配置与常见故障的结论"
|
||||
}
|
||||
```
|
||||
|
||||
### manager_memory 示例(仅示意)
|
||||
```json
|
||||
{
|
||||
"route": {"kind": "manager_memory", "specialist": "manager", "reason": "需要沉淀本轮可复用结论"},
|
||||
"dispatch": {
|
||||
"instruction_text": "结论如下:已完成方案对齐,下一步按计划执行。",
|
||||
"memory_write_text": "记忆条目:方案已定稿;约束A/B已确认;后续按里程碑M1推进。",
|
||||
"post_reply_memory_write_text": "补记忆:本轮用户确认接受方案A,风险项R2需在M1前复核。"
|
||||
},
|
||||
"need_wiki_inject": false,
|
||||
"wiki_query": ""
|
||||
"route": {"kind": "specialist", "specialist": "generalist", "reason": "任务通用,适合默认专家处理"},
|
||||
"dispatch": {"instruction_text": "先分析问题并给出结论,再列出依据与下一步建议。"}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
## 主控职责
|
||||
- 仅负责任务编排、专家下发、结果汇总与验收。
|
||||
- 默认不直接执行用户任务,不直接产出最终答案;仅在 `manager_memory` 场景执行记忆写入。
|
||||
- 默认不直接执行用户任务,不直接产出最终答案。
|
||||
|
||||
## 输出要求
|
||||
- 汇总输出保持简洁、准确、可执行。
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue