mirror of
https://github.com/shareAI-lab/analysis_claude_code.git
synced 2026-05-06 16:26:16 +08:00
- s03: inject reminder into tool_result instead of mutating history (#37) - s05: SkillLoader uses rglob("SKILL.md") + frontmatter name priority, matching Agent Skills standard (#30, PR #34) - CI: upgrade actions/checkout and actions/setup-node to v6 (#36) - docs: update s05 skill directory structure in all 3 languages
This commit is contained in:
@@ -163,11 +163,7 @@ TOOLS = [
|
||||
def agent_loop(messages: list):
|
||||
rounds_since_todo = 0
|
||||
while True:
|
||||
# Nag reminder: if 3+ rounds without a todo update, inject reminder
|
||||
if rounds_since_todo >= 3 and messages:
|
||||
last = messages[-1]
|
||||
if last["role"] == "user" and isinstance(last.get("content"), list):
|
||||
last["content"].insert(0, {"type": "text", "text": "<reminder>Update your todos.</reminder>"})
|
||||
# Nag reminder is injected below, alongside tool results
|
||||
response = client.messages.create(
|
||||
model=MODEL, system=SYSTEM, messages=messages,
|
||||
tools=TOOLS, max_tokens=8000,
|
||||
@@ -189,6 +185,8 @@ def agent_loop(messages: list):
|
||||
if block.name == "todo":
|
||||
used_todo = True
|
||||
rounds_since_todo = 0 if used_todo else rounds_since_todo + 1
|
||||
if rounds_since_todo >= 3:
|
||||
results.insert(0, {"type": "text", "text": "<reminder>Update your todos.</reminder>"})
|
||||
messages.append({"role": "user", "content": results})
|
||||
|
||||
|
||||
|
||||
@@ -7,19 +7,25 @@ Two-layer skill injection that avoids bloating the system prompt:
|
||||
Layer 1 (cheap): skill names in system prompt (~100 tokens/skill)
|
||||
Layer 2 (on demand): full skill body in tool_result
|
||||
|
||||
skills/
|
||||
pdf/
|
||||
SKILL.md <-- frontmatter (name, description) + body
|
||||
code-review/
|
||||
SKILL.md
|
||||
|
||||
System prompt:
|
||||
+--------------------------------------+
|
||||
| You are a coding agent. |
|
||||
| Skills available: |
|
||||
| - git: Git workflow helpers | <-- Layer 1: metadata only
|
||||
| - test: Testing best practices |
|
||||
| - pdf: Process PDF files... | <-- Layer 1: metadata only
|
||||
| - code-review: Review code... |
|
||||
+--------------------------------------+
|
||||
|
||||
When model calls load_skill("git"):
|
||||
When model calls load_skill("pdf"):
|
||||
+--------------------------------------+
|
||||
| tool_result: |
|
||||
| <skill> |
|
||||
| Full git workflow instructions... | <-- Layer 2: full body
|
||||
| Full PDF processing instructions | <-- Layer 2: full body
|
||||
| Step 1: ... |
|
||||
| Step 2: ... |
|
||||
| </skill> |
|
||||
@@ -44,10 +50,10 @@ if os.getenv("ANTHROPIC_BASE_URL"):
|
||||
WORKDIR = Path.cwd()
|
||||
client = Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL"))
|
||||
MODEL = os.environ["MODEL_ID"]
|
||||
SKILLS_DIR = WORKDIR / ".skills"
|
||||
SKILLS_DIR = WORKDIR / "skills"
|
||||
|
||||
|
||||
# -- SkillLoader: parse .skills/*.md files with YAML frontmatter --
|
||||
# -- SkillLoader: scan skills/<name>/SKILL.md with YAML frontmatter --
|
||||
class SkillLoader:
|
||||
def __init__(self, skills_dir: Path):
|
||||
self.skills_dir = skills_dir
|
||||
@@ -57,10 +63,10 @@ class SkillLoader:
|
||||
def _load_all(self):
|
||||
if not self.skills_dir.exists():
|
||||
return
|
||||
for f in sorted(self.skills_dir.glob("*.md")):
|
||||
name = f.stem
|
||||
for f in sorted(self.skills_dir.rglob("SKILL.md")):
|
||||
text = f.read_text()
|
||||
meta, body = self._parse_frontmatter(text)
|
||||
name = meta.get("name", f.parent.name)
|
||||
self.skills[name] = {"meta": meta, "body": body, "path": str(f)}
|
||||
|
||||
def _parse_frontmatter(self, text: str) -> tuple:
|
||||
|
||||
@@ -199,7 +199,7 @@ class SkillLoader:
|
||||
def __init__(self, skills_dir: Path):
|
||||
self.skills = {}
|
||||
if skills_dir.exists():
|
||||
for f in sorted(skills_dir.glob("*.md")):
|
||||
for f in sorted(skills_dir.rglob("SKILL.md")):
|
||||
text = f.read_text()
|
||||
match = re.match(r"^---\n(.*?)\n---\n(.*)", text, re.DOTALL)
|
||||
meta, body = {}, text
|
||||
@@ -209,7 +209,8 @@ class SkillLoader:
|
||||
k, v = line.split(":", 1)
|
||||
meta[k.strip()] = v.strip()
|
||||
body = match.group(2).strip()
|
||||
self.skills[f.stem] = {"meta": meta, "body": body}
|
||||
name = meta.get("name", f.parent.name)
|
||||
self.skills[name] = {"meta": meta, "body": body}
|
||||
|
||||
def descriptions(self) -> str:
|
||||
if not self.skills: return "(no skills)"
|
||||
|
||||
Reference in New Issue
Block a user