ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
"""Skill 的增量维护:按开放式规则 id 合并、prune 与 SKILL.md 生成。
|
||||
|
||||
防膨胀原则:提炼模型看到当前规则,语义相同时复用稳定 id;本模块按 id 合并来源,
|
||||
而不是再用预置 detector 指纹把新发现筛掉。
|
||||
长期未被新证据确认、或被评估证据推翻的规则归档到 skill/archive/,不再进入
|
||||
SKILL.md。所有合并/激活/归档决定都发生在这里,不交给生成候选的模型。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Set, Tuple
|
||||
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
SKILL_DIR = ROOT / "skill"
|
||||
|
||||
def rule_signature(rule: Dict[str, Any]) -> str:
|
||||
"""模型在已有规则语义相同时复用 id;该稳定 id 就是合并键。"""
|
||||
return str(rule.get("id", "")).strip().lower()
|
||||
|
||||
|
||||
def merge_rules(
|
||||
existing: List[Dict[str, Any]], candidates: List[Dict[str, Any]]
|
||||
) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]:
|
||||
"""把候选规则合并进现有规则集,返回 (规则集, 合并报告)。"""
|
||||
rules = [dict(rule) for rule in existing]
|
||||
report: Dict[str, Any] = {"added": [], "merged": [], "conflicts": []}
|
||||
for cand in candidates:
|
||||
sig = rule_signature(cand)
|
||||
match = next(
|
||||
(rule for rule in rules if rule_signature(rule) == sig), None
|
||||
)
|
||||
if match is None:
|
||||
rules.append(cand)
|
||||
report["added"].append(cand["id"])
|
||||
continue
|
||||
# 去重合并:并集来源与作用域,保留首次通过 schema 检查的定义与范例。
|
||||
match["source_ids"] = sorted(set(match.get("source_ids", [])) | set(cand.get("source_ids", [])))
|
||||
match["scope"] = sorted(set(match.get("scope", [])) | set(cand.get("scope", [])))
|
||||
report["merged"].append(cand["id"])
|
||||
return rules, report
|
||||
|
||||
|
||||
def prune_rules(
|
||||
rules: List[Dict[str, Any]],
|
||||
*,
|
||||
current_batch: int,
|
||||
idle_batches: int = 2,
|
||||
contradicted_ids: Set[str] | None = None,
|
||||
) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
|
||||
"""把长期未被新证据确认、或被证据推翻的规则归档,返回 (存活, 归档)。"""
|
||||
contradicted = contradicted_ids or set()
|
||||
active, archived = [], []
|
||||
for rule in rules:
|
||||
last = rule.get("last_confirmed_batch", current_batch)
|
||||
reason = None
|
||||
if rule["id"] in contradicted:
|
||||
reason = "被评估证据推翻(误伤率过高或与金标集冲突)"
|
||||
elif current_batch - last > idle_batches:
|
||||
reason = f"连续超过 {idle_batches} 批反馈未再被触发"
|
||||
if reason:
|
||||
archived.append({**rule, "status": "archived", "archive_reason": reason})
|
||||
else:
|
||||
active.append(rule)
|
||||
return active, archived
|
||||
|
||||
|
||||
def _describe_detector(detector: Dict[str, Any]) -> str:
|
||||
if detector.get("type") != "llm":
|
||||
return "无效检测器(规则不会激活)"
|
||||
return "LLM judge 语义判定(上线前须通过独立人工金标集校准)"
|
||||
|
||||
|
||||
def render_skill_md(rules: List[Dict[str, Any]]) -> str:
|
||||
"""按 house 风格渲染 SKILL.md:何时加载 + 每条规则的定义/坏例/好例/作用域。"""
|
||||
lines = [
|
||||
"---",
|
||||
"name: ai-style",
|
||||
"description: 中文文案去「AI 味」检查清单,由用户纠正反馈持续提炼而来",
|
||||
"---",
|
||||
"",
|
||||
"# 去 AI 味写作 Skill",
|
||||
"",
|
||||
"## 何时加载",
|
||||
"",
|
||||
"当任务是用中文撰写或改写面向读者的文案(产品发布稿、公众号文章、邮件、README 等),",
|
||||
"或用户反馈文字「AI 味太重」「不像人写的」时,加载本 Skill。",
|
||||
"",
|
||||
"## 使用方式",
|
||||
"",
|
||||
"起草或改写时逐条对照下面的规则自查。每条规则都给出定义、可检查的检测方法、",
|
||||
"坏例与好例;规则只在声明的作用域内生效,作用域之外的文体不要套用。",
|
||||
"",
|
||||
f"## 规则清单(共 {len(rules)} 条)",
|
||||
"",
|
||||
]
|
||||
for i, rule in enumerate(rules, 1):
|
||||
lines += [
|
||||
f"### 规则 {i}:{rule['name']}(`{rule['id']}`)",
|
||||
"",
|
||||
f"- **定义**:{rule['definition']}",
|
||||
f"- **检测方法**:{_describe_detector(rule['detector'])}",
|
||||
f"- **坏例**:{rule.get('bad_example', '')}",
|
||||
f"- **好例**:{rule.get('good_example', '')}",
|
||||
f"- **改写建议**:{rule.get('rewrite_hint', '按定义改写。')}",
|
||||
f"- **作用域**:{'、'.join(rule.get('scope', [])) or '通用'}",
|
||||
f"- **来源反馈**:{', '.join(rule.get('source_ids', [])) or '无'}",
|
||||
"",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def write_skill(rules: List[Dict[str, Any]], skill_dir: Path | None = None) -> Path:
|
||||
out_dir = skill_dir or SKILL_DIR
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
path = out_dir / "SKILL.md"
|
||||
path.write_text(render_skill_md(rules), encoding="utf-8")
|
||||
# 同步保存机器可读的规则集,供 evaluate/judge 直接加载。
|
||||
(out_dir / "rules.json").write_text(
|
||||
json.dumps(rules, ensure_ascii=False, indent=2), encoding="utf-8"
|
||||
)
|
||||
return path
|
||||
|
||||
|
||||
def write_archive(archived: List[Dict[str, Any]], skill_dir: Path | None = None) -> Path | None:
|
||||
if not archived:
|
||||
return None
|
||||
out_dir = (skill_dir or SKILL_DIR) / "archive"
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
lines = ["# 已归档规则", ""]
|
||||
for rule in archived:
|
||||
lines += [
|
||||
f"## {rule['name']}(`{rule['id']}`)",
|
||||
f"- 归档原因:{rule.get('archive_reason', '未说明')}",
|
||||
f"- 原定义:{rule['definition']}",
|
||||
f"- 来源反馈:{', '.join(rule.get('source_ids', [])) or '无'}",
|
||||
"",
|
||||
]
|
||||
path = out_dir / "ARCHIVED.md"
|
||||
path.write_text("\n".join(lines), encoding="utf-8")
|
||||
return path
|
||||
Reference in New Issue
Block a user