skillbay 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- skillbay-0.1.0/.gitattributes +13 -0
- skillbay-0.1.0/.github/workflows/publish.yml +69 -0
- skillbay-0.1.0/.github/workflows/test.yml +35 -0
- skillbay-0.1.0/.gitignore +21 -0
- skillbay-0.1.0/.python-version +1 -0
- skillbay-0.1.0/AGENTS.md +74 -0
- skillbay-0.1.0/LICENSE +21 -0
- skillbay-0.1.0/PKG-INFO +192 -0
- skillbay-0.1.0/README.md +163 -0
- skillbay-0.1.0/README.zh-CN.md +128 -0
- skillbay-0.1.0/pyproject.toml +66 -0
- skillbay-0.1.0/src/skillbay/__init__.py +68 -0
- skillbay-0.1.0/src/skillbay/core.py +260 -0
- skillbay-0.1.0/src/skillbay/expansion.py +145 -0
- skillbay-0.1.0/src/skillbay/frontmatter.py +173 -0
- skillbay-0.1.0/src/skillbay/middleware.py +662 -0
- skillbay-0.1.0/tests/test_core.py +103 -0
- skillbay-0.1.0/tests/test_expansion.py +113 -0
- skillbay-0.1.0/tests/test_frontmatter.py +56 -0
- skillbay-0.1.0/tests/test_middleware.py +420 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Normalize line endings: LF in the repository and in working trees.
|
|
2
|
+
* text=auto eol=lf
|
|
3
|
+
|
|
4
|
+
# Source files are always LF.
|
|
5
|
+
*.py text eol=lf
|
|
6
|
+
*.toml text eol=lf
|
|
7
|
+
*.md text eol=lf
|
|
8
|
+
*.yaml text eol=lf
|
|
9
|
+
*.yml text eol=lf
|
|
10
|
+
|
|
11
|
+
# Windows scripts keep CRLF.
|
|
12
|
+
*.bat text eol=crlf
|
|
13
|
+
*.ps1 text eol=crlf
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write # Required for creating releases
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
name: Build distribution
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
|
|
23
|
+
- name: Install build tools
|
|
24
|
+
run: pip install build
|
|
25
|
+
|
|
26
|
+
- name: Build package
|
|
27
|
+
run: python -m build
|
|
28
|
+
|
|
29
|
+
- name: Upload artifacts
|
|
30
|
+
uses: actions/upload-artifact@v4
|
|
31
|
+
with:
|
|
32
|
+
name: dist
|
|
33
|
+
path: dist/
|
|
34
|
+
|
|
35
|
+
github-release:
|
|
36
|
+
name: Create GitHub Release
|
|
37
|
+
needs: build
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
|
|
42
|
+
- name: Download artifacts
|
|
43
|
+
uses: actions/download-artifact@v4
|
|
44
|
+
with:
|
|
45
|
+
name: dist
|
|
46
|
+
path: dist/
|
|
47
|
+
|
|
48
|
+
- name: Create Release
|
|
49
|
+
uses: softprops/action-gh-release@v2
|
|
50
|
+
with:
|
|
51
|
+
files: dist/*
|
|
52
|
+
generate_release_notes: true
|
|
53
|
+
|
|
54
|
+
pypi-publish:
|
|
55
|
+
name: Publish to PyPI
|
|
56
|
+
needs: build
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
environment: pypi
|
|
59
|
+
permissions:
|
|
60
|
+
id-token: write # Required for trusted publishing
|
|
61
|
+
steps:
|
|
62
|
+
- name: Download artifacts
|
|
63
|
+
uses: actions/download-artifact@v4
|
|
64
|
+
with:
|
|
65
|
+
name: dist
|
|
66
|
+
path: dist/
|
|
67
|
+
|
|
68
|
+
- name: Publish to PyPI
|
|
69
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Run tests
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.12", "3.13"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install uv
|
|
26
|
+
uses: astral-sh/setup-uv@v4
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: uv sync
|
|
30
|
+
|
|
31
|
+
- name: Lint
|
|
32
|
+
run: uv run ruff check src tests
|
|
33
|
+
|
|
34
|
+
- name: Run tests
|
|
35
|
+
run: uv run pytest
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Python artifacts
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
|
|
8
|
+
# Virtual environments
|
|
9
|
+
.venv/
|
|
10
|
+
|
|
11
|
+
# Dependency lock file (library project - let consumers resolve versions)
|
|
12
|
+
uv.lock
|
|
13
|
+
|
|
14
|
+
# Test and tooling caches
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
|
|
19
|
+
# IDE
|
|
20
|
+
.idea/
|
|
21
|
+
.vscode/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
skillbay-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## 项目定位
|
|
4
|
+
|
|
5
|
+
skillbay 是一个面向 LangChain 的、可插拔的、面向后端业务服务的 Agent Skill 系统。
|
|
6
|
+
核心思路 1:1 借鉴 Claude Code 的 Skill 系统(参考其 Python 移植版),但按后端
|
|
7
|
+
场景做了两项改造:技能是部署产物(构造时加载并锁定)、allowed-tools 工具闸
|
|
8
|
+
(wrap_tool_call 强制执行)。
|
|
9
|
+
|
|
10
|
+
完整的业务改造设计思路见 `README.md` / `README.zh-CN.md`,不要把设计论述写进
|
|
11
|
+
py 文件头。包已从 `skillkit` 完成改名迁移,现在一律叫 `skillbay`。
|
|
12
|
+
|
|
13
|
+
## 目录结构
|
|
14
|
+
|
|
15
|
+
- `src/skillbay/` — 核心包(src 布局),五个模块:
|
|
16
|
+
- `middleware.py` — 主入口 `SkillMiddleware`(LangChain `AgentMiddleware`)。
|
|
17
|
+
含 skill 工具、allowed-tools 闸门(`check_allowed_tools` 纯函数)、
|
|
18
|
+
`before_model`(清单播报 + 压缩存活重注入)。改它之前先读 README 的
|
|
19
|
+
「业务改造」章节。
|
|
20
|
+
- `core.py` — `Skill` 数据模型、目录加载(约定 `<skills_dir>/<name>/SKILL.md`,
|
|
21
|
+
靠后目录覆盖靠前目录、按 realpath 去重)、清单格式化(1% 上下文预算 +
|
|
22
|
+
三级降级)。
|
|
23
|
+
- `expansion.py` — SKILL.md 展开管线,五步顺序固定不能颠倒:
|
|
24
|
+
Base directory 头 → 参数替换($ARGUMENTS/$0/$foo)→ ${SKILL_DIR} →
|
|
25
|
+
${SESSION_ID} → 可选 !`命令` shell 块。
|
|
26
|
+
- `frontmatter.py` — YAML 头解析。PyYAML 可选(没有则用平铺子集解析器),
|
|
27
|
+
解析失败先修特殊字符再重试——一个技能写坏不能拖垮 agent 启动。
|
|
28
|
+
- `__init__.py` — 公共 API 面;新增导出需同步 `__all__`。
|
|
29
|
+
- `tests/` — pytest 测试(纯函数级 + 中间件集成)。
|
|
30
|
+
- `pyproject.toml` — 包元数据与工具配置(Python >= 3.12,hatchling 构建,
|
|
31
|
+
pytest / ruff 配置)。
|
|
32
|
+
- `uv.lock` / `.venv/` — uv 管理的锁文件与虚拟环境(Windows,
|
|
33
|
+
激活脚本在 `.venv/Scripts/`)。
|
|
34
|
+
- `.python-version` — 钉住 Python 3.12。
|
|
35
|
+
|
|
36
|
+
## 架构边界(改动必读)
|
|
37
|
+
|
|
38
|
+
- 三层渐进式披露:① `before_model` 把预算化清单包进 `<system-reminder>` 作为
|
|
39
|
+
user 消息写入 state;② 模型调 `skill` 工具时展开 SKILL.md 全文作为
|
|
40
|
+
ToolMessage 返回;③ 正文里的相对路径靠 "Base directory" 头解析,由 agent
|
|
41
|
+
自己的文件工具按需读取(本中间件不提供文件工具)。
|
|
42
|
+
- 技能记账(`announced_skills` / `skill_invocations`)放在 `SkillState`
|
|
43
|
+
(AgentState 扩展),不放进程级 dict——为了随 checkpointer 持久化、按
|
|
44
|
+
thread 隔离、resume 后不重复播报。
|
|
45
|
+
- allowed-tools 闸门 `check_allowed_tools` 是纯函数(便于单测):生效窗口从
|
|
46
|
+
成功的 "Launching skill:" ToolMessage 起,到下一条真实 user 消息止;多个
|
|
47
|
+
技能的白名单取并集;限制只紧不松;窗口内连 skill 工具本身也拦(防提权)。
|
|
48
|
+
- 只有成功展开的技能调用才会激活其白名单(失败不能成为提权通道)。
|
|
49
|
+
- `` !`命令` `` shell 块默认关闭(`enable_shell_blocks=False`),是安全开关。
|
|
50
|
+
Windows 注意:PATH 里的 bash 可能解析到未配置的 WSL 存根,输出需按 UTF-8
|
|
51
|
+
解码(`errors="replace"`,见 expansion.py)。
|
|
52
|
+
- 技能集合在构造时一次性加载并锁定,不做运行时动态发现。
|
|
53
|
+
|
|
54
|
+
## 环境与命令
|
|
55
|
+
|
|
56
|
+
- 依赖已声明:`langchain`(1.x 的 `langchain.agents.middleware` API)/
|
|
57
|
+
`langgraph` / `pydantic` / `typing-extensions`;`pyyaml` 为可选 extras
|
|
58
|
+
(dev 组里也装了)。
|
|
59
|
+
- `uv sync` 装依赖;`uv run pytest` 跑测试(当前 52 个全过);
|
|
60
|
+
`uv run ruff check src tests` 与 `uv run ruff format src tests` 做 lint
|
|
61
|
+
和格式化;不要用裸 pip / python。
|
|
62
|
+
|
|
63
|
+
## 约定
|
|
64
|
+
|
|
65
|
+
- **py 文件只放 `src/` 或 `tests/`,绝不放仓库根目录。**
|
|
66
|
+
- 代码注释与 docstring 用英文、保持简短;文件开头不写长篇设计论述(设计思路
|
|
67
|
+
进 README)。日志与提示文案用英文。
|
|
68
|
+
- 日志用 `logging`(各模块 `logging.getLogger(__name__)`,包 logger 名
|
|
69
|
+
`skillbay`);`verbose=True` 时由 `_install_verbose_handler` 挂
|
|
70
|
+
StreamHandler(`[skillbay]` 前缀)。不要用 print。
|
|
71
|
+
- 技能名以目录名为准,frontmatter 里的 name 只是显示名。
|
|
72
|
+
- Windows 环境运行:展开时把路径反斜杠统一为正斜杠(${SKILL_DIR} 处理),
|
|
73
|
+
新增路径相关逻辑沿用此约定。
|
|
74
|
+
- 主分支是 `main`;提交信息用英文祈使句(chore/feat/test/docs 前缀)。
|
skillbay-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ezzi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
skillbay-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: skillbay
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Pluggable skill middleware for LangChain agents: a skill system designed for business services.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Ezzi/skillbay
|
|
6
|
+
Project-URL: Repository, https://github.com/Ezzi/skillbay
|
|
7
|
+
Project-URL: Issues, https://github.com/Ezzi/skillbay/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/Ezzi/skillbay#readme
|
|
9
|
+
Author: Ezzi
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agent,langchain,llm,middleware,skill
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Requires-Dist: langchain>=1.0
|
|
23
|
+
Requires-Dist: langgraph>=1.0
|
|
24
|
+
Requires-Dist: pydantic>=2.7
|
|
25
|
+
Requires-Dist: typing-extensions>=4.12
|
|
26
|
+
Provides-Extra: yaml
|
|
27
|
+
Requires-Dist: pyyaml>=6.0.1; extra == 'yaml'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
<div align="center">
|
|
31
|
+
|
|
32
|
+
# skillbay
|
|
33
|
+
|
|
34
|
+
**Pluggable skill middleware for LangChain — a skill system designed for business services.**
|
|
35
|
+
|
|
36
|
+
[English](./README.md) | [简体中文](./README.zh-CN.md)
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+

|
|
40
|
+

|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
## Why skillbay
|
|
46
|
+
|
|
47
|
+
Anthropic proposes a remarkably good skill model: a single `SKILL.md` file turns a
|
|
48
|
+
directory of procedures, references and scripts into capabilities the model can discover
|
|
49
|
+
on demand, load lazily, and execute precisely. It is one of the standout contributions of
|
|
50
|
+
Anthropic's Harness team in this area, solving a classic problem — *how does one agent
|
|
51
|
+
carry many specialties without blowing its context window?*
|
|
52
|
+
|
|
53
|
+
The mechanism itself is general, but the mainstream implementation was built for one
|
|
54
|
+
specific scenario: a developer's coding and workbench. skillbay moves it into another —
|
|
55
|
+
**business services**: a consumer app's support entrance, a company's finance/HR
|
|
56
|
+
assistant, an ops copilot wired into internal systems. The context-window problem to
|
|
57
|
+
solve is the same, but the runtime environment and rules are completely different.
|
|
58
|
+
|
|
59
|
+
| | Claude Code: coding / workbench | skillbay: business services |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| Who talks to the agent | A developer, able to judge "should this command run?" | An end customer or an employee — nobody able to review a tool call, and no conversation that can hang on a confirmation prompt |
|
|
62
|
+
| What a skill is | A coding workflow, installed ad hoc by its user | A business capability — refund handling, reimbursement policy, leave inquiry — owned by the company and governed like any business code |
|
|
63
|
+
| Whose authority the agent acts with | The developer's own account | The company's — customers never opted into the agent's internals, so blast radius must be bounded by design |
|
|
64
|
+
| Session shape | One developer, one terminal session | Thousands of concurrent conversations, checkpointed and resumable |
|
|
65
|
+
|
|
66
|
+
**skillbay** therefore ports the skill system 1:1 in semantics onto LangChain's middleware
|
|
67
|
+
API, then deliberately diverges wherever the scenario — not the mechanics — demands it.
|
|
68
|
+
These divergences are the real content of this project.
|
|
69
|
+
|
|
70
|
+
## Business transformation
|
|
71
|
+
|
|
72
|
+
### 1. Skills are deploy artifacts, not runtime discoveries
|
|
73
|
+
|
|
74
|
+
In Claude Code, the person who installs a skill is the person affected by it — runtime
|
|
75
|
+
discovery and skill marketplaces are reasonable. A business service breaks that symmetry:
|
|
76
|
+
the company operates the agent, while customers and employees bear the consequences. The
|
|
77
|
+
skills also differ in kind — refund rules, reimbursement workflows and HR policies are
|
|
78
|
+
business capabilities, not developer conveniences. A skill "appearing" in a mounted
|
|
79
|
+
directory would put unreviewed behavior in front of customers: no review, no version, no
|
|
80
|
+
rollback.
|
|
81
|
+
|
|
82
|
+
In a coding agent, the person who installs a skill is the person who uses it, so runtime
|
|
83
|
+
discovery is reasonable. In a business service, developers provide the skills, customers
|
|
84
|
+
use them, and the company bears the consequences. Skills must therefore be supplied by the
|
|
85
|
+
backend team — there is no runtime discovery.
|
|
86
|
+
|
|
87
|
+
skillbay therefore loads the skill set **once, at construction, and freezes it**:
|
|
88
|
+
|
|
89
|
+
- Skills live in git, go through code review, and ship with the release.
|
|
90
|
+
- Runtime file changes have no effect until the process restarts.
|
|
91
|
+
- Skill identity is the directory name; the frontmatter `name` is display-only — renames on
|
|
92
|
+
disk cannot silently re-route behavior.
|
|
93
|
+
|
|
94
|
+
### 2. The allowed-tools gate
|
|
95
|
+
|
|
96
|
+
An agent in a business service acts as a customer-service/smart assistant — a
|
|
97
|
+
customer-service skill must never reach tools beyond its charter. A skill's frontmatter can
|
|
98
|
+
declare `allowed-tools`; while that skill is active, the middleware's `wrap_tool_call` hook
|
|
99
|
+
**enforces** the whitelist on every single tool call — the model is not trusted to comply,
|
|
100
|
+
it is prevented.
|
|
101
|
+
|
|
102
|
+
The enforcement window is derived purely from the message history (no extra state to
|
|
103
|
+
corrupt):
|
|
104
|
+
|
|
105
|
+
- **Opens** when a skill call succeeds — its ToolMessage starts with `Launching skill:`.
|
|
106
|
+
- **Closes** at the next real user message; `<system-reminder>` injections don't count as a
|
|
107
|
+
new task and don't close the window.
|
|
108
|
+
- **Union**: when several skills are active at once, their whitelists are unioned, or
|
|
109
|
+
multiple skills are disallowed from being active simultaneously (a subagent mechanism can
|
|
110
|
+
be used to invoke multiple skills) — and it **only tightens**: a skill without
|
|
111
|
+
`allowed-tools` can never loosen an active restriction.
|
|
112
|
+
- **The `skill` tool itself is blocked inside the window**: this closes the escalation path
|
|
113
|
+
of calling a restricted skill and then chaining into an unrestricted one.
|
|
114
|
+
|
|
115
|
+
### 3. Accounting lives in agent state, not process globals
|
|
116
|
+
|
|
117
|
+
The reference implementation records announced/invoked skills in module-level dicts — fine
|
|
118
|
+
for one developer's single-session process; wrong for a customer-service system serving
|
|
119
|
+
thousands of resumable, checkpointed conversations at once. skillbay puts both ledgers in
|
|
120
|
+
`SkillState` (an `AgentState` extension), which gives three things:
|
|
121
|
+
|
|
122
|
+
- **Persistence** — the ledgers survive checkpoints; no duplicate announcements after a
|
|
123
|
+
process resumes.
|
|
124
|
+
- **Isolation** — per-thread state; concurrent conversations don't interfere with each
|
|
125
|
+
other.
|
|
126
|
+
- **Summarization survival** — when a `SummarizationMiddleware` compresses away the turn
|
|
127
|
+
that invoked the skill, the invocation record stays in state. `before_model` notices the
|
|
128
|
+
`tool_call_id` is gone from the message list, re-expands the body, and injects it as a
|
|
129
|
+
system-reminder. The skill's guidance survives the compaction that killed its transcript.
|
|
130
|
+
|
|
131
|
+
### 4. Skill dismissal — the model can release skills it no longer needs
|
|
132
|
+
|
|
133
|
+
Other skill systems (Claude Code, Codex, Cursor, and every LangChain skill middleware we
|
|
134
|
+
have seen) treat skill activation as fire-and-once: once a skill's body is injected, it
|
|
135
|
+
occupies the context window until the conversation ends or the context is compacted away.
|
|
136
|
+
That is fine for a developer session — the human knows when the task is done — but wrong
|
|
137
|
+
for a service agent handling multi-turn conversations, which naturally drift across topics.
|
|
138
|
+
|
|
139
|
+
skillbay introduces **skill dismissal**: the model can call `skill_dismiss` to release a
|
|
140
|
+
skill whose scope no longer matches the conversation. After dismissal:
|
|
141
|
+
|
|
142
|
+
- The skill's full body is **no longer re-injected** into the post-compaction context.
|
|
143
|
+
- The dismissal is **persisted in agent state** (survives checkpoint/resume).
|
|
144
|
+
- An **audit event** (`skill_dismissed`) records which skill was dismissed and why.
|
|
145
|
+
|
|
146
|
+
The model decides when to dismiss based on the conversation — a customer who asks about
|
|
147
|
+
refund policy and then pivots to shipping times does not need the refund skill's 12-step
|
|
148
|
+
procedure consuming context for the rest of the session.
|
|
149
|
+
|
|
150
|
+
It is a small mechanism, but it matters for long-lived service conversations, for two
|
|
151
|
+
reasons: it reduces the number of tokens a backend service consumes, and it helps avoid
|
|
152
|
+
topic drift.
|
|
153
|
+
|
|
154
|
+
## Robustness design
|
|
155
|
+
|
|
156
|
+
- **One broken skill never breaks startup.** Frontmatter parsing never throws: it tries the
|
|
157
|
+
raw text first, retries with auto-quoting on failure (guarding against the classic
|
|
158
|
+
`paths: **/*.{ts,tsx}` mistake), and degrades to an empty header as a last resort. A
|
|
159
|
+
skill missing its `description` is skipped with a warning, not fatal.
|
|
160
|
+
- **Audit built in.** Five audit events (`skill_invoked`, `skill_reinjected`,
|
|
161
|
+
`skills_announced`, `tool_call_blocked`, `skill_dismissed`) flow through one callback
|
|
162
|
+
seam; wire it to your logging/metrics stack. Audit failures never take down the agent.
|
|
163
|
+
- **Context budget with graceful degradation.** When the listing exceeds its 1% budget,
|
|
164
|
+
each description is truncated to an equal share of what remains; in the extreme case only
|
|
165
|
+
names are announced. The discovery layer never crowds out the task itself.
|
|
166
|
+
|
|
167
|
+
### LangChain integration
|
|
168
|
+
|
|
169
|
+
Wire it into any LangChain `create_agent` agent:
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from skillbay import SkillMiddleware
|
|
173
|
+
|
|
174
|
+
mw = SkillMiddleware(
|
|
175
|
+
skills_dirs=["skills"],
|
|
176
|
+
audit=lambda event: print(event), # route to your observability stack
|
|
177
|
+
)
|
|
178
|
+
agent = create_agent(model, tools=[...], middleware=[mw])
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Frontmatter reference
|
|
182
|
+
|
|
183
|
+
| Field | Required | Effect |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| `description` | yes | Listing text; drives model selection. Missing → skill skipped. |
|
|
186
|
+
| `allowed-tools` | no | Tool whitelist enforced while the skill's window is open. |
|
|
187
|
+
| `arguments` | no | Declares named arguments (`$foo`), mapped positionally. |
|
|
188
|
+
| `argument-hint` | no | Human-facing hint for the argument string. |
|
|
189
|
+
| `when_to_use` | no | Extra trigger guidance, appended to the listing description. |
|
|
190
|
+
| `disable-model-invocation` | no | Kept out of the listing; user-triggered only. |
|
|
191
|
+
| `shell` | no | Interpreter for `` !`...` `` blocks (feature is off by default). |
|
|
192
|
+
| `model` / `paths` / `version` | no | Parsed and carried; enforcement is on the roadmap. |
|
skillbay-0.1.0/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# skillbay
|
|
4
|
+
|
|
5
|
+
**Pluggable skill middleware for LangChain — a skill system designed for business services.**
|
|
6
|
+
|
|
7
|
+
[English](./README.md) | [简体中文](./README.zh-CN.md)
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
## Why skillbay
|
|
17
|
+
|
|
18
|
+
Anthropic proposes a remarkably good skill model: a single `SKILL.md` file turns a
|
|
19
|
+
directory of procedures, references and scripts into capabilities the model can discover
|
|
20
|
+
on demand, load lazily, and execute precisely. It is one of the standout contributions of
|
|
21
|
+
Anthropic's Harness team in this area, solving a classic problem — *how does one agent
|
|
22
|
+
carry many specialties without blowing its context window?*
|
|
23
|
+
|
|
24
|
+
The mechanism itself is general, but the mainstream implementation was built for one
|
|
25
|
+
specific scenario: a developer's coding and workbench. skillbay moves it into another —
|
|
26
|
+
**business services**: a consumer app's support entrance, a company's finance/HR
|
|
27
|
+
assistant, an ops copilot wired into internal systems. The context-window problem to
|
|
28
|
+
solve is the same, but the runtime environment and rules are completely different.
|
|
29
|
+
|
|
30
|
+
| | Claude Code: coding / workbench | skillbay: business services |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| Who talks to the agent | A developer, able to judge "should this command run?" | An end customer or an employee — nobody able to review a tool call, and no conversation that can hang on a confirmation prompt |
|
|
33
|
+
| What a skill is | A coding workflow, installed ad hoc by its user | A business capability — refund handling, reimbursement policy, leave inquiry — owned by the company and governed like any business code |
|
|
34
|
+
| Whose authority the agent acts with | The developer's own account | The company's — customers never opted into the agent's internals, so blast radius must be bounded by design |
|
|
35
|
+
| Session shape | One developer, one terminal session | Thousands of concurrent conversations, checkpointed and resumable |
|
|
36
|
+
|
|
37
|
+
**skillbay** therefore ports the skill system 1:1 in semantics onto LangChain's middleware
|
|
38
|
+
API, then deliberately diverges wherever the scenario — not the mechanics — demands it.
|
|
39
|
+
These divergences are the real content of this project.
|
|
40
|
+
|
|
41
|
+
## Business transformation
|
|
42
|
+
|
|
43
|
+
### 1. Skills are deploy artifacts, not runtime discoveries
|
|
44
|
+
|
|
45
|
+
In Claude Code, the person who installs a skill is the person affected by it — runtime
|
|
46
|
+
discovery and skill marketplaces are reasonable. A business service breaks that symmetry:
|
|
47
|
+
the company operates the agent, while customers and employees bear the consequences. The
|
|
48
|
+
skills also differ in kind — refund rules, reimbursement workflows and HR policies are
|
|
49
|
+
business capabilities, not developer conveniences. A skill "appearing" in a mounted
|
|
50
|
+
directory would put unreviewed behavior in front of customers: no review, no version, no
|
|
51
|
+
rollback.
|
|
52
|
+
|
|
53
|
+
In a coding agent, the person who installs a skill is the person who uses it, so runtime
|
|
54
|
+
discovery is reasonable. In a business service, developers provide the skills, customers
|
|
55
|
+
use them, and the company bears the consequences. Skills must therefore be supplied by the
|
|
56
|
+
backend team — there is no runtime discovery.
|
|
57
|
+
|
|
58
|
+
skillbay therefore loads the skill set **once, at construction, and freezes it**:
|
|
59
|
+
|
|
60
|
+
- Skills live in git, go through code review, and ship with the release.
|
|
61
|
+
- Runtime file changes have no effect until the process restarts.
|
|
62
|
+
- Skill identity is the directory name; the frontmatter `name` is display-only — renames on
|
|
63
|
+
disk cannot silently re-route behavior.
|
|
64
|
+
|
|
65
|
+
### 2. The allowed-tools gate
|
|
66
|
+
|
|
67
|
+
An agent in a business service acts as a customer-service/smart assistant — a
|
|
68
|
+
customer-service skill must never reach tools beyond its charter. A skill's frontmatter can
|
|
69
|
+
declare `allowed-tools`; while that skill is active, the middleware's `wrap_tool_call` hook
|
|
70
|
+
**enforces** the whitelist on every single tool call — the model is not trusted to comply,
|
|
71
|
+
it is prevented.
|
|
72
|
+
|
|
73
|
+
The enforcement window is derived purely from the message history (no extra state to
|
|
74
|
+
corrupt):
|
|
75
|
+
|
|
76
|
+
- **Opens** when a skill call succeeds — its ToolMessage starts with `Launching skill:`.
|
|
77
|
+
- **Closes** at the next real user message; `<system-reminder>` injections don't count as a
|
|
78
|
+
new task and don't close the window.
|
|
79
|
+
- **Union**: when several skills are active at once, their whitelists are unioned, or
|
|
80
|
+
multiple skills are disallowed from being active simultaneously (a subagent mechanism can
|
|
81
|
+
be used to invoke multiple skills) — and it **only tightens**: a skill without
|
|
82
|
+
`allowed-tools` can never loosen an active restriction.
|
|
83
|
+
- **The `skill` tool itself is blocked inside the window**: this closes the escalation path
|
|
84
|
+
of calling a restricted skill and then chaining into an unrestricted one.
|
|
85
|
+
|
|
86
|
+
### 3. Accounting lives in agent state, not process globals
|
|
87
|
+
|
|
88
|
+
The reference implementation records announced/invoked skills in module-level dicts — fine
|
|
89
|
+
for one developer's single-session process; wrong for a customer-service system serving
|
|
90
|
+
thousands of resumable, checkpointed conversations at once. skillbay puts both ledgers in
|
|
91
|
+
`SkillState` (an `AgentState` extension), which gives three things:
|
|
92
|
+
|
|
93
|
+
- **Persistence** — the ledgers survive checkpoints; no duplicate announcements after a
|
|
94
|
+
process resumes.
|
|
95
|
+
- **Isolation** — per-thread state; concurrent conversations don't interfere with each
|
|
96
|
+
other.
|
|
97
|
+
- **Summarization survival** — when a `SummarizationMiddleware` compresses away the turn
|
|
98
|
+
that invoked the skill, the invocation record stays in state. `before_model` notices the
|
|
99
|
+
`tool_call_id` is gone from the message list, re-expands the body, and injects it as a
|
|
100
|
+
system-reminder. The skill's guidance survives the compaction that killed its transcript.
|
|
101
|
+
|
|
102
|
+
### 4. Skill dismissal — the model can release skills it no longer needs
|
|
103
|
+
|
|
104
|
+
Other skill systems (Claude Code, Codex, Cursor, and every LangChain skill middleware we
|
|
105
|
+
have seen) treat skill activation as fire-and-once: once a skill's body is injected, it
|
|
106
|
+
occupies the context window until the conversation ends or the context is compacted away.
|
|
107
|
+
That is fine for a developer session — the human knows when the task is done — but wrong
|
|
108
|
+
for a service agent handling multi-turn conversations, which naturally drift across topics.
|
|
109
|
+
|
|
110
|
+
skillbay introduces **skill dismissal**: the model can call `skill_dismiss` to release a
|
|
111
|
+
skill whose scope no longer matches the conversation. After dismissal:
|
|
112
|
+
|
|
113
|
+
- The skill's full body is **no longer re-injected** into the post-compaction context.
|
|
114
|
+
- The dismissal is **persisted in agent state** (survives checkpoint/resume).
|
|
115
|
+
- An **audit event** (`skill_dismissed`) records which skill was dismissed and why.
|
|
116
|
+
|
|
117
|
+
The model decides when to dismiss based on the conversation — a customer who asks about
|
|
118
|
+
refund policy and then pivots to shipping times does not need the refund skill's 12-step
|
|
119
|
+
procedure consuming context for the rest of the session.
|
|
120
|
+
|
|
121
|
+
It is a small mechanism, but it matters for long-lived service conversations, for two
|
|
122
|
+
reasons: it reduces the number of tokens a backend service consumes, and it helps avoid
|
|
123
|
+
topic drift.
|
|
124
|
+
|
|
125
|
+
## Robustness design
|
|
126
|
+
|
|
127
|
+
- **One broken skill never breaks startup.** Frontmatter parsing never throws: it tries the
|
|
128
|
+
raw text first, retries with auto-quoting on failure (guarding against the classic
|
|
129
|
+
`paths: **/*.{ts,tsx}` mistake), and degrades to an empty header as a last resort. A
|
|
130
|
+
skill missing its `description` is skipped with a warning, not fatal.
|
|
131
|
+
- **Audit built in.** Five audit events (`skill_invoked`, `skill_reinjected`,
|
|
132
|
+
`skills_announced`, `tool_call_blocked`, `skill_dismissed`) flow through one callback
|
|
133
|
+
seam; wire it to your logging/metrics stack. Audit failures never take down the agent.
|
|
134
|
+
- **Context budget with graceful degradation.** When the listing exceeds its 1% budget,
|
|
135
|
+
each description is truncated to an equal share of what remains; in the extreme case only
|
|
136
|
+
names are announced. The discovery layer never crowds out the task itself.
|
|
137
|
+
|
|
138
|
+
### LangChain integration
|
|
139
|
+
|
|
140
|
+
Wire it into any LangChain `create_agent` agent:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from skillbay import SkillMiddleware
|
|
144
|
+
|
|
145
|
+
mw = SkillMiddleware(
|
|
146
|
+
skills_dirs=["skills"],
|
|
147
|
+
audit=lambda event: print(event), # route to your observability stack
|
|
148
|
+
)
|
|
149
|
+
agent = create_agent(model, tools=[...], middleware=[mw])
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Frontmatter reference
|
|
153
|
+
|
|
154
|
+
| Field | Required | Effect |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| `description` | yes | Listing text; drives model selection. Missing → skill skipped. |
|
|
157
|
+
| `allowed-tools` | no | Tool whitelist enforced while the skill's window is open. |
|
|
158
|
+
| `arguments` | no | Declares named arguments (`$foo`), mapped positionally. |
|
|
159
|
+
| `argument-hint` | no | Human-facing hint for the argument string. |
|
|
160
|
+
| `when_to_use` | no | Extra trigger guidance, appended to the listing description. |
|
|
161
|
+
| `disable-model-invocation` | no | Kept out of the listing; user-triggered only. |
|
|
162
|
+
| `shell` | no | Interpreter for `` !`...` `` blocks (feature is off by default). |
|
|
163
|
+
| `model` / `paths` / `version` | no | Parsed and carried; enforcement is on the roadmap. |
|