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

This commit is contained in:
2026-08-20 13:12:50 +00:00
commit b119135836
10275 changed files with 3284984 additions and 0 deletions
+142
View File
@@ -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