cannyforge 0.2.0__tar.gz → 0.3.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.
- cannyforge-0.3.0/PKG-INFO +282 -0
- cannyforge-0.3.0/README.md +238 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/__init__.py +8 -1
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/adapters/__init__.py +1 -0
- cannyforge-0.3.0/cannyforge/adapters/autoresearch.py +341 -0
- cannyforge-0.3.0/cannyforge/adapters/langgraph.py +445 -0
- cannyforge-0.3.0/cannyforge/bundled_skills/fsi-workflow/SKILL.md +67 -0
- cannyforge-0.3.0/cannyforge/bundled_skills/tool-use/SKILL.md +106 -0
- cannyforge-0.3.0/cannyforge/bundled_skills/tool-use/assets/templates.yaml +43 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/core.py +87 -3
- cannyforge-0.3.0/cannyforge/corrections.py +259 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/knowledge.py +319 -4
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/learning.py +113 -16
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/llm.py +26 -2
- cannyforge-0.3.0/cannyforge.egg-info/PKG-INFO +282 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge.egg-info/SOURCES.txt +13 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge.egg-info/requires.txt +4 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/pyproject.toml +3 -1
- cannyforge-0.3.0/tests/test_bench_fsi80.py +66 -0
- cannyforge-0.3.0/tests/test_core_portability.py +78 -0
- cannyforge-0.3.0/tests/test_corrections.py +117 -0
- cannyforge-0.3.0/tests/test_eval_trace.py +337 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/tests/test_knowledge.py +112 -1
- cannyforge-0.3.0/tests/test_langgraph_adapter.py +382 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/tests/test_learning.py +3 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/tests/test_llm.py +49 -0
- cannyforge-0.3.0/tests/test_scenario_harness.py +275 -0
- cannyforge-0.3.0/tests/test_tool_use_skill.py +146 -0
- cannyforge-0.2.0/PKG-INFO +0 -472
- cannyforge-0.2.0/README.md +0 -431
- cannyforge-0.2.0/cannyforge.egg-info/PKG-INFO +0 -472
- {cannyforge-0.2.0 → cannyforge-0.3.0}/LICENSE +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/adapters/crewai.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/adapters/langchain.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/bundled_skills/calendar-manager/SKILL.md +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/bundled_skills/calendar-manager/assets/templates.yaml +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/bundled_skills/content-summarizer/SKILL.md +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/bundled_skills/email-writer/SKILL.md +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/bundled_skills/email-writer/assets/templates.yaml +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/bundled_skills/web-searcher/SKILL.md +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/cli.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/dashboard.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/demo.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/export.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/mcp_server.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/registry.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/services/__init__.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/services/crm_service.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/services/email_service.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/services/mock_calendar_mcp.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/services/service_base.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/services/slack_service.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/services/web_search_api.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/skills.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/storage.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/tools.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge/workers.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge.egg-info/dependency_links.txt +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge.egg-info/entry_points.txt +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/cannyforge.egg-info/top_level.txt +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/setup.cfg +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/tests/test_declarative_skill.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/tests/test_integration.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/tests/test_production.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/tests/test_skill_loader.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/tests/test_spec_compliance.py +0 -0
- {cannyforge-0.2.0 → cannyforge-0.3.0}/tests/test_tools.py +0 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cannyforge
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Self-improving agents with closed-loop learning — agents that learn to get it right
|
|
5
|
+
License: BSL-1.1
|
|
6
|
+
Project-URL: Homepage, https://github.com/cannyforge/cannyforge
|
|
7
|
+
Project-URL: Documentation, https://github.com/cannyforge/cannyforge#readme
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
19
|
+
Requires-Dist: requests>=2.31.0
|
|
20
|
+
Requires-Dist: pydantic>=2.5.0
|
|
21
|
+
Requires-Dist: pyyaml>=6.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
25
|
+
Provides-Extra: claude
|
|
26
|
+
Requires-Dist: anthropic>=0.39.0; extra == "claude"
|
|
27
|
+
Provides-Extra: openai
|
|
28
|
+
Requires-Dist: openai>=1.0.0; extra == "openai"
|
|
29
|
+
Provides-Extra: deepseek
|
|
30
|
+
Requires-Dist: openai>=1.0.0; extra == "deepseek"
|
|
31
|
+
Provides-Extra: langgraph
|
|
32
|
+
Requires-Dist: langgraph>=0.2.0; extra == "langgraph"
|
|
33
|
+
Provides-Extra: mcp
|
|
34
|
+
Requires-Dist: mcp[cli]>=1.0.0; extra == "mcp"
|
|
35
|
+
Provides-Extra: dashboard
|
|
36
|
+
Requires-Dist: streamlit>=1.30.0; extra == "dashboard"
|
|
37
|
+
Provides-Extra: all
|
|
38
|
+
Requires-Dist: anthropic>=0.39.0; extra == "all"
|
|
39
|
+
Requires-Dist: openai>=1.0.0; extra == "all"
|
|
40
|
+
Requires-Dist: langgraph>=0.2.0; extra == "all"
|
|
41
|
+
Requires-Dist: mcp[cli]>=1.0.0; extra == "all"
|
|
42
|
+
Requires-Dist: streamlit>=1.30.0; extra == "all"
|
|
43
|
+
Dynamic: license-file
|
|
44
|
+
|
|
45
|
+
# CannyForge
|
|
46
|
+
|
|
47
|
+
**Reliability memory for tool-using LLM agents.**
|
|
48
|
+
|
|
49
|
+
CannyForge watches your agent make mistakes, learns corrections, and injects them as SystemMessages before each LLM call. Your agent gets better over time — no retraining required.
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
Agent makes errors → CannyForge learns corrections → Agent stops repeating them
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+

|
|
56
|
+
|
|
57
|
+
## Quick Start (LangGraph)
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from cannyforge import CannyForge
|
|
61
|
+
from cannyforge.adapters.langgraph import CannyForgeMiddleware
|
|
62
|
+
from langgraph.prebuilt import create_react_agent
|
|
63
|
+
|
|
64
|
+
forge = CannyForge()
|
|
65
|
+
middleware = CannyForgeMiddleware(forge)
|
|
66
|
+
agent = create_react_agent(model, tools,
|
|
67
|
+
pre_model_hook=middleware.before_model,
|
|
68
|
+
post_model_hook=middleware.after_model)
|
|
69
|
+
|
|
70
|
+
# Just run tasks. CannyForge records errors via after_model.
|
|
71
|
+
# After learning, before_model injects corrections as SystemMessages.
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## v0.3 Direction
|
|
75
|
+
|
|
76
|
+
The benchmark branch is focused on three adoption-oriented improvements:
|
|
77
|
+
|
|
78
|
+
- better reliability loops for tool-using agents
|
|
79
|
+
- broader benchmark coverage for arguments, multi-step execution, and recovery
|
|
80
|
+
- portable learned skills for assistant and workflow reuse
|
|
81
|
+
|
|
82
|
+
This is aimed at modern agent stacks, including LangGraph, LangChain-style tool agents,
|
|
83
|
+
CrewAI-style orchestration, MCP-connected assistants, and personal assistant workflows.
|
|
84
|
+
|
|
85
|
+
See [docs/v0.3-public.md](/home/xiwei/pg/cannyforge/docs/v0.3-public.md) for the public summary.
|
|
86
|
+
|
|
87
|
+
## How It Works
|
|
88
|
+
|
|
89
|
+
1. **Record errors** — `after_model` detects tool failures and records them
|
|
90
|
+
2. **Learn corrections** — `run_learning_cycle()` clusters errors and generates specific correction text (template or LLM-generated)
|
|
91
|
+
3. **Inject corrections** — `before_model` prepends a SystemMessage with all active corrections before each LLM call
|
|
92
|
+
4. **Track effectiveness** — corrections that prevent recurrence are kept; ineffective ones can be regenerated
|
|
93
|
+
|
|
94
|
+
The correction is specific and actionable:
|
|
95
|
+
```
|
|
96
|
+
[CANNYFORGE] Learned rules for this request:
|
|
97
|
+
- When the task involves report, summary, sales, use `generate_report`, NOT `get_data`.
|
|
98
|
+
Example: "Create a summary of Q4 sales performance"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Demo: 60% → 100% on Real LLM
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pip install langgraph langchain-openai
|
|
105
|
+
# Set LLM_API_KEY in .env
|
|
106
|
+
python scenarios/demo_cannyforge.py
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This runs 15 ambiguous tool-selection tasks twice:
|
|
110
|
+
- **Phase 1**: baseline without corrections — records errors
|
|
111
|
+
- **Learning**: generates corrections from observed errors
|
|
112
|
+
- **Phase 2**: same tasks with correction injection — accuracy improves
|
|
113
|
+
|
|
114
|
+
Real output with DeepSeek:
|
|
115
|
+
```
|
|
116
|
+
Phase 1 accuracy: 9/15 (60%)
|
|
117
|
+
Phase 2 accuracy: 15/15 (100%)
|
|
118
|
+
Tasks fixed:
|
|
119
|
+
- Restart the staging server -> execute_action
|
|
120
|
+
- Send an alert to the on-call team -> execute_action
|
|
121
|
+
- Deploy the latest build to production -> execute_action
|
|
122
|
+
- Create a summary of Q4 sales performance -> generate_report
|
|
123
|
+
- Write up a status report for this sprint -> generate_report
|
|
124
|
+
- Generate a monthly uptime report -> generate_report
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
No simulated errors. No hand-crafted rules. Real LLM decisions, real corrections from the pipeline.
|
|
128
|
+
|
|
129
|
+
## Install
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
pip install cannyforge # from PyPI
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Or from source:
|
|
136
|
+
```bash
|
|
137
|
+
git clone https://github.com/cannyforge/cannyforge.git
|
|
138
|
+
cd cannyforge
|
|
139
|
+
pip install -e .
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Scenarios
|
|
143
|
+
|
|
144
|
+
| Script | Purpose |
|
|
145
|
+
|--------|---------|
|
|
146
|
+
| `scenarios/demo_cannyforge.py` | **Canonical demo** — full pipeline: baseline → learn → improve |
|
|
147
|
+
| `scenarios/demo_langgraph_tool_use.py` | Minimal quickstart — 3-line integration |
|
|
148
|
+
| `scenarios/demo.py` | Animated terminal demo (internal skill system) |
|
|
149
|
+
|
|
150
|
+
Older demo scripts are in `scenarios/archive/` for reference.
|
|
151
|
+
|
|
152
|
+
## Framework Coverage
|
|
153
|
+
|
|
154
|
+
| Surface | Current repo path |
|
|
155
|
+
|--------|-------------------|
|
|
156
|
+
| LangGraph middleware | `cannyforge/adapters/langgraph.py` |
|
|
157
|
+
| LangChain adapter | `cannyforge/adapters/langchain.py` |
|
|
158
|
+
| CrewAI adapter | `cannyforge/adapters/crewai.py` |
|
|
159
|
+
| MCP server | `cannyforge/mcp_server.py` |
|
|
160
|
+
| Bundled assistant skills | `cannyforge/bundled_skills/` |
|
|
161
|
+
|
|
162
|
+
CannyForge is designed to sit on top of existing agent frameworks rather than replace them.
|
|
163
|
+
The public focus for v0.3 is improved tool routing, argument quality, multi-step execution, and
|
|
164
|
+
reusable skills.
|
|
165
|
+
|
|
166
|
+
## Core Architecture
|
|
167
|
+
|
|
168
|
+
### Corrections Pipeline (LangGraph integration)
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
cannyforge/corrections.py — Correction dataclass + CorrectionGenerator
|
|
172
|
+
cannyforge/adapters/langgraph.py — CannyForgeMiddleware (pre/post model hooks)
|
|
173
|
+
cannyforge/knowledge.py — KnowledgeBase stores corrections + rules
|
|
174
|
+
cannyforge/learning.py — PatternDetector + LearningEngine
|
|
175
|
+
cannyforge/core.py — CannyForge orchestrator
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**CorrectionGenerator** turns error clusters into actionable text:
|
|
179
|
+
- **Template mode** (no LLM): groups by `(wrong_tool, right_tool)`, extracts keywords, formats guidance
|
|
180
|
+
- **LLM mode**: sends error cluster to LLM asking for a generalized rule covering unseen tasks
|
|
181
|
+
|
|
182
|
+
**CannyForgeMiddleware** hooks into LangGraph's `create_react_agent`:
|
|
183
|
+
- `before_model`: injects always-on corrections + conditional rules as a SystemMessage
|
|
184
|
+
- `after_model`: records tool failures, tracks correction effectiveness
|
|
185
|
+
|
|
186
|
+
### Internal Skill System
|
|
187
|
+
|
|
188
|
+
CannyForge also includes a declarative skill system for standalone use (without LangGraph):
|
|
189
|
+
|
|
190
|
+
- Skills defined via `SKILL.md` files ([AgentSkills.io](https://agentskills.io/specification) spec)
|
|
191
|
+
- Three-tier execution: custom handler → LLM multi-step → template fallback
|
|
192
|
+
- PATTERN_LIBRARY with condition-based rules for internal context signals
|
|
193
|
+
- Rule lifecycle: ACTIVE → PROBATION → DORMANT → resurrection
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
from cannyforge import CannyForge
|
|
197
|
+
forge = CannyForge()
|
|
198
|
+
result = forge.execute("Write an email about the 3 PM meeting")
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
See `scenarios/demo.py` for the animated terminal demo of this path.
|
|
202
|
+
|
|
203
|
+
## How Learning Works
|
|
204
|
+
|
|
205
|
+
### 1. Error Recording
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
# Via middleware (automatic):
|
|
209
|
+
agent = create_react_agent(llm, tools, post_model_hook=middleware.after_model)
|
|
210
|
+
|
|
211
|
+
# Or manual:
|
|
212
|
+
forge.learning_engine.record_error(
|
|
213
|
+
skill_name="tool_use",
|
|
214
|
+
task_description="Create a Q4 summary",
|
|
215
|
+
error_type="WrongToolError",
|
|
216
|
+
error_message="Called get_data instead of generate_report",
|
|
217
|
+
context_snapshot={...},
|
|
218
|
+
)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 2. Learning Cycle
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
metrics = forge.run_learning_cycle(min_frequency=2, min_confidence=0.3)
|
|
225
|
+
# Produces:
|
|
226
|
+
# - Condition-based rules (for internal skill system)
|
|
227
|
+
# - Corrections (for LangGraph injection)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 3. Correction Injection
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
corrections = forge.knowledge_base.get_corrections("tool_use")
|
|
234
|
+
# [Correction(content="When task involves report, summary... use generate_report, NOT get_data")]
|
|
235
|
+
|
|
236
|
+
# Automatically injected by middleware.before_model() as a SystemMessage
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Run Tests
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
pytest tests/ -v
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Project Structure
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
cannyforge/
|
|
249
|
+
├── cannyforge/
|
|
250
|
+
│ ├── core.py # CannyForge orchestrator
|
|
251
|
+
│ ├── corrections.py # Correction + CorrectionGenerator
|
|
252
|
+
│ ├── knowledge.py # KnowledgeBase, Rules, Conditions, Actions
|
|
253
|
+
│ ├── learning.py # PatternDetector, LearningEngine
|
|
254
|
+
│ ├── skills.py # Declarative skill system
|
|
255
|
+
│ ├── llm.py # LLM providers (Claude, OpenAI, DeepSeek)
|
|
256
|
+
│ ├── tools.py # Tool definitions and execution
|
|
257
|
+
│ ├── storage.py # Storage backends (JSON, SQLite)
|
|
258
|
+
│ └── adapters/
|
|
259
|
+
│ └── langgraph.py # LangGraph middleware (pre/post model hooks)
|
|
260
|
+
│
|
|
261
|
+
├── scenarios/
|
|
262
|
+
│ ├── demo_cannyforge.py # Canonical demo (corrections pipeline)
|
|
263
|
+
│ ├── demo_langgraph_tool_use.py # Minimal quickstart
|
|
264
|
+
│ └── demo.py # Animated demo (internal skill system)
|
|
265
|
+
│
|
|
266
|
+
├── tests/ # Test suite
|
|
267
|
+
└── skills/ # Built-in skill definitions (SKILL.md)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Further Reading
|
|
271
|
+
|
|
272
|
+
- Blog post: [From Prompt Tweaks to Learning Machines: The Agent Skill Primitive](https://medium.com/@xiweizhou/from-prompt-tweaks-to-learning-machines-the-agent-skill-primitive-93c8fa9dec8c?sk=ac888430da699bce7b635456ae2b1166)
|
|
273
|
+
|
|
274
|
+
## License
|
|
275
|
+
|
|
276
|
+
Licensed under [BSL 1.1](LICENSE). Free to use in production, but you may not offer CannyForge as a competing hosted service. Converts to Apache 2.0 on 2030-03-01. See LICENSE for full terms.
|
|
277
|
+
|
|
278
|
+
For commercial licensing inquiries: cannyforge@gmail.com
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
**CannyForge** — Your agent makes fewer repeated mistakes over time, with measurable evidence.
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# CannyForge
|
|
2
|
+
|
|
3
|
+
**Reliability memory for tool-using LLM agents.**
|
|
4
|
+
|
|
5
|
+
CannyForge watches your agent make mistakes, learns corrections, and injects them as SystemMessages before each LLM call. Your agent gets better over time — no retraining required.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Agent makes errors → CannyForge learns corrections → Agent stops repeating them
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
## Quick Start (LangGraph)
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from cannyforge import CannyForge
|
|
17
|
+
from cannyforge.adapters.langgraph import CannyForgeMiddleware
|
|
18
|
+
from langgraph.prebuilt import create_react_agent
|
|
19
|
+
|
|
20
|
+
forge = CannyForge()
|
|
21
|
+
middleware = CannyForgeMiddleware(forge)
|
|
22
|
+
agent = create_react_agent(model, tools,
|
|
23
|
+
pre_model_hook=middleware.before_model,
|
|
24
|
+
post_model_hook=middleware.after_model)
|
|
25
|
+
|
|
26
|
+
# Just run tasks. CannyForge records errors via after_model.
|
|
27
|
+
# After learning, before_model injects corrections as SystemMessages.
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## v0.3 Direction
|
|
31
|
+
|
|
32
|
+
The benchmark branch is focused on three adoption-oriented improvements:
|
|
33
|
+
|
|
34
|
+
- better reliability loops for tool-using agents
|
|
35
|
+
- broader benchmark coverage for arguments, multi-step execution, and recovery
|
|
36
|
+
- portable learned skills for assistant and workflow reuse
|
|
37
|
+
|
|
38
|
+
This is aimed at modern agent stacks, including LangGraph, LangChain-style tool agents,
|
|
39
|
+
CrewAI-style orchestration, MCP-connected assistants, and personal assistant workflows.
|
|
40
|
+
|
|
41
|
+
See [docs/v0.3-public.md](/home/xiwei/pg/cannyforge/docs/v0.3-public.md) for the public summary.
|
|
42
|
+
|
|
43
|
+
## How It Works
|
|
44
|
+
|
|
45
|
+
1. **Record errors** — `after_model` detects tool failures and records them
|
|
46
|
+
2. **Learn corrections** — `run_learning_cycle()` clusters errors and generates specific correction text (template or LLM-generated)
|
|
47
|
+
3. **Inject corrections** — `before_model` prepends a SystemMessage with all active corrections before each LLM call
|
|
48
|
+
4. **Track effectiveness** — corrections that prevent recurrence are kept; ineffective ones can be regenerated
|
|
49
|
+
|
|
50
|
+
The correction is specific and actionable:
|
|
51
|
+
```
|
|
52
|
+
[CANNYFORGE] Learned rules for this request:
|
|
53
|
+
- When the task involves report, summary, sales, use `generate_report`, NOT `get_data`.
|
|
54
|
+
Example: "Create a summary of Q4 sales performance"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Demo: 60% → 100% on Real LLM
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install langgraph langchain-openai
|
|
61
|
+
# Set LLM_API_KEY in .env
|
|
62
|
+
python scenarios/demo_cannyforge.py
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
This runs 15 ambiguous tool-selection tasks twice:
|
|
66
|
+
- **Phase 1**: baseline without corrections — records errors
|
|
67
|
+
- **Learning**: generates corrections from observed errors
|
|
68
|
+
- **Phase 2**: same tasks with correction injection — accuracy improves
|
|
69
|
+
|
|
70
|
+
Real output with DeepSeek:
|
|
71
|
+
```
|
|
72
|
+
Phase 1 accuracy: 9/15 (60%)
|
|
73
|
+
Phase 2 accuracy: 15/15 (100%)
|
|
74
|
+
Tasks fixed:
|
|
75
|
+
- Restart the staging server -> execute_action
|
|
76
|
+
- Send an alert to the on-call team -> execute_action
|
|
77
|
+
- Deploy the latest build to production -> execute_action
|
|
78
|
+
- Create a summary of Q4 sales performance -> generate_report
|
|
79
|
+
- Write up a status report for this sprint -> generate_report
|
|
80
|
+
- Generate a monthly uptime report -> generate_report
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
No simulated errors. No hand-crafted rules. Real LLM decisions, real corrections from the pipeline.
|
|
84
|
+
|
|
85
|
+
## Install
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pip install cannyforge # from PyPI
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Or from source:
|
|
92
|
+
```bash
|
|
93
|
+
git clone https://github.com/cannyforge/cannyforge.git
|
|
94
|
+
cd cannyforge
|
|
95
|
+
pip install -e .
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Scenarios
|
|
99
|
+
|
|
100
|
+
| Script | Purpose |
|
|
101
|
+
|--------|---------|
|
|
102
|
+
| `scenarios/demo_cannyforge.py` | **Canonical demo** — full pipeline: baseline → learn → improve |
|
|
103
|
+
| `scenarios/demo_langgraph_tool_use.py` | Minimal quickstart — 3-line integration |
|
|
104
|
+
| `scenarios/demo.py` | Animated terminal demo (internal skill system) |
|
|
105
|
+
|
|
106
|
+
Older demo scripts are in `scenarios/archive/` for reference.
|
|
107
|
+
|
|
108
|
+
## Framework Coverage
|
|
109
|
+
|
|
110
|
+
| Surface | Current repo path |
|
|
111
|
+
|--------|-------------------|
|
|
112
|
+
| LangGraph middleware | `cannyforge/adapters/langgraph.py` |
|
|
113
|
+
| LangChain adapter | `cannyforge/adapters/langchain.py` |
|
|
114
|
+
| CrewAI adapter | `cannyforge/adapters/crewai.py` |
|
|
115
|
+
| MCP server | `cannyforge/mcp_server.py` |
|
|
116
|
+
| Bundled assistant skills | `cannyforge/bundled_skills/` |
|
|
117
|
+
|
|
118
|
+
CannyForge is designed to sit on top of existing agent frameworks rather than replace them.
|
|
119
|
+
The public focus for v0.3 is improved tool routing, argument quality, multi-step execution, and
|
|
120
|
+
reusable skills.
|
|
121
|
+
|
|
122
|
+
## Core Architecture
|
|
123
|
+
|
|
124
|
+
### Corrections Pipeline (LangGraph integration)
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
cannyforge/corrections.py — Correction dataclass + CorrectionGenerator
|
|
128
|
+
cannyforge/adapters/langgraph.py — CannyForgeMiddleware (pre/post model hooks)
|
|
129
|
+
cannyforge/knowledge.py — KnowledgeBase stores corrections + rules
|
|
130
|
+
cannyforge/learning.py — PatternDetector + LearningEngine
|
|
131
|
+
cannyforge/core.py — CannyForge orchestrator
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**CorrectionGenerator** turns error clusters into actionable text:
|
|
135
|
+
- **Template mode** (no LLM): groups by `(wrong_tool, right_tool)`, extracts keywords, formats guidance
|
|
136
|
+
- **LLM mode**: sends error cluster to LLM asking for a generalized rule covering unseen tasks
|
|
137
|
+
|
|
138
|
+
**CannyForgeMiddleware** hooks into LangGraph's `create_react_agent`:
|
|
139
|
+
- `before_model`: injects always-on corrections + conditional rules as a SystemMessage
|
|
140
|
+
- `after_model`: records tool failures, tracks correction effectiveness
|
|
141
|
+
|
|
142
|
+
### Internal Skill System
|
|
143
|
+
|
|
144
|
+
CannyForge also includes a declarative skill system for standalone use (without LangGraph):
|
|
145
|
+
|
|
146
|
+
- Skills defined via `SKILL.md` files ([AgentSkills.io](https://agentskills.io/specification) spec)
|
|
147
|
+
- Three-tier execution: custom handler → LLM multi-step → template fallback
|
|
148
|
+
- PATTERN_LIBRARY with condition-based rules for internal context signals
|
|
149
|
+
- Rule lifecycle: ACTIVE → PROBATION → DORMANT → resurrection
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from cannyforge import CannyForge
|
|
153
|
+
forge = CannyForge()
|
|
154
|
+
result = forge.execute("Write an email about the 3 PM meeting")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
See `scenarios/demo.py` for the animated terminal demo of this path.
|
|
158
|
+
|
|
159
|
+
## How Learning Works
|
|
160
|
+
|
|
161
|
+
### 1. Error Recording
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
# Via middleware (automatic):
|
|
165
|
+
agent = create_react_agent(llm, tools, post_model_hook=middleware.after_model)
|
|
166
|
+
|
|
167
|
+
# Or manual:
|
|
168
|
+
forge.learning_engine.record_error(
|
|
169
|
+
skill_name="tool_use",
|
|
170
|
+
task_description="Create a Q4 summary",
|
|
171
|
+
error_type="WrongToolError",
|
|
172
|
+
error_message="Called get_data instead of generate_report",
|
|
173
|
+
context_snapshot={...},
|
|
174
|
+
)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 2. Learning Cycle
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
metrics = forge.run_learning_cycle(min_frequency=2, min_confidence=0.3)
|
|
181
|
+
# Produces:
|
|
182
|
+
# - Condition-based rules (for internal skill system)
|
|
183
|
+
# - Corrections (for LangGraph injection)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 3. Correction Injection
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
corrections = forge.knowledge_base.get_corrections("tool_use")
|
|
190
|
+
# [Correction(content="When task involves report, summary... use generate_report, NOT get_data")]
|
|
191
|
+
|
|
192
|
+
# Automatically injected by middleware.before_model() as a SystemMessage
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Run Tests
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
pytest tests/ -v
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Project Structure
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
cannyforge/
|
|
205
|
+
├── cannyforge/
|
|
206
|
+
│ ├── core.py # CannyForge orchestrator
|
|
207
|
+
│ ├── corrections.py # Correction + CorrectionGenerator
|
|
208
|
+
│ ├── knowledge.py # KnowledgeBase, Rules, Conditions, Actions
|
|
209
|
+
│ ├── learning.py # PatternDetector, LearningEngine
|
|
210
|
+
│ ├── skills.py # Declarative skill system
|
|
211
|
+
│ ├── llm.py # LLM providers (Claude, OpenAI, DeepSeek)
|
|
212
|
+
│ ├── tools.py # Tool definitions and execution
|
|
213
|
+
│ ├── storage.py # Storage backends (JSON, SQLite)
|
|
214
|
+
│ └── adapters/
|
|
215
|
+
│ └── langgraph.py # LangGraph middleware (pre/post model hooks)
|
|
216
|
+
│
|
|
217
|
+
├── scenarios/
|
|
218
|
+
│ ├── demo_cannyforge.py # Canonical demo (corrections pipeline)
|
|
219
|
+
│ ├── demo_langgraph_tool_use.py # Minimal quickstart
|
|
220
|
+
│ └── demo.py # Animated demo (internal skill system)
|
|
221
|
+
│
|
|
222
|
+
├── tests/ # Test suite
|
|
223
|
+
└── skills/ # Built-in skill definitions (SKILL.md)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Further Reading
|
|
227
|
+
|
|
228
|
+
- Blog post: [From Prompt Tweaks to Learning Machines: The Agent Skill Primitive](https://medium.com/@xiweizhou/from-prompt-tweaks-to-learning-machines-the-agent-skill-primitive-93c8fa9dec8c?sk=ac888430da699bce7b635456ae2b1166)
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
Licensed under [BSL 1.1](LICENSE). Free to use in production, but you may not offer CannyForge as a competing hosted service. Converts to Apache 2.0 on 2030-03-01. See LICENSE for full terms.
|
|
233
|
+
|
|
234
|
+
For commercial licensing inquiries: cannyforge@gmail.com
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
**CannyForge** — Your agent makes fewer repeated mistakes over time, with measurable evidence.
|
|
@@ -48,8 +48,10 @@ from cannyforge.learning import (
|
|
|
48
48
|
ErrorRepository,
|
|
49
49
|
)
|
|
50
50
|
from cannyforge.tools import ToolDefinition, ToolExecutor, ToolRegistry
|
|
51
|
+
from cannyforge.adapters.langgraph import CannyForgeMiddleware
|
|
52
|
+
from cannyforge.corrections import Correction, CorrectionGenerator
|
|
51
53
|
|
|
52
|
-
__version__ = "0.
|
|
54
|
+
__version__ = "0.3.0"
|
|
53
55
|
|
|
54
56
|
__all__ = [
|
|
55
57
|
# Core
|
|
@@ -91,4 +93,9 @@ __all__ = [
|
|
|
91
93
|
"ToolDefinition",
|
|
92
94
|
"ToolExecutor",
|
|
93
95
|
"ToolRegistry",
|
|
96
|
+
# Adapters
|
|
97
|
+
"CannyForgeMiddleware",
|
|
98
|
+
# Corrections
|
|
99
|
+
"Correction",
|
|
100
|
+
"CorrectionGenerator",
|
|
94
101
|
]
|