agent-readable 0.1.2__tar.gz → 0.2.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.
- {agent_readable-0.1.2 → agent_readable-0.2.0}/PKG-INFO +79 -59
- {agent_readable-0.1.2 → agent_readable-0.2.0}/README.md +78 -58
- {agent_readable-0.1.2 → agent_readable-0.2.0}/docs/agent_help_vs_help.gif +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/pyproject.toml +1 -1
- agent_readable-0.1.2/skills/agent-readable/SKILL.md +0 -183
- {agent_readable-0.1.2 → agent_readable-0.2.0}/.github/workflows/publish.yml +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/.github/workflows/test.yml +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/.gitignore +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/CHANGELOG.md +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/LICENSE +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/examples/any_class.py +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/examples/duck_type.py +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/examples/modules_and_functions.py +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/examples/sqlite_connection.py +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/examples/temperature.py +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/src/agent_readable/__init__.py +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/src/agent_readable/__main__.py +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/src/agent_readable/_model.py +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/src/agent_readable/_protocol.py +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/src/agent_readable/_render.py +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/src/agent_readable/py.typed +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/tests/__init__.py +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/tests/test_cli.py +0 -0
- {agent_readable-0.1.2 → agent_readable-0.2.0}/tests/test_protocol.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-readable
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A lightweight Python protocol for agent-oriented documentation
|
|
5
5
|
Project-URL: Repository, https://github.com/zydo/agent-readable
|
|
6
6
|
Project-URL: Issues, https://github.com/zydo/agent-readable/issues
|
|
@@ -27,7 +27,13 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|
|
|
28
28
|
# agent-readable
|
|
29
29
|
|
|
30
|
-
Stop coding agents from hallucinating
|
|
30
|
+
Stop coding agents from hallucinating Python APIs. `agent_help(target)` reads the live public-API surface off any class, module, function, or method — no author opt-in needed — and optionally returns class-level usage rules (lifecycle order, pre-conditions, anti-patterns) when the library ships them.
|
|
31
|
+
|
|
32
|
+
To let your coding agent automatically call `agent_help()` before using an unfamiliar API, install the skill:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx skills add zydo/skills --skill agent-readable
|
|
36
|
+
```
|
|
31
37
|
|
|
32
38
|
<!-- markdownlint-disable MD033 -->
|
|
33
39
|
<p align="center">
|
|
@@ -42,7 +48,7 @@ AI coding agents recognize established libraries from their training data, but t
|
|
|
42
48
|
|
|
43
49
|
Today there's nowhere to put those rules where an agent will reliably find them. Docstrings document the API surface, not behavioral rules. `AGENTS.md` and `llms.txt` work at project granularity, drift fast, and don't travel with refactors. `help()` only describes interfaces — and mixes inherited dunders and MRO detail in with the methods you actually want to use.
|
|
44
50
|
|
|
45
|
-
`agent-readable`
|
|
51
|
+
`agent-readable` has two halves, and the lighter one stands alone. The **consume side** is one function — `agent_help(target)` — that works on every Python library today, annotated or not. It introspects the live, installed class/module/function and returns a curated public-API list with current signatures, no inherited-dunder or MRO noise to wade through. That alone closes the *what exists* hallucination — agents stop inventing methods that aren't there and stop calling real ones with stale signatures. The **author side** adds two dunders: `__agent_help__` for full custom output and `__agent_notes__` for additive behavioral rules that accumulate across inheritance. When a library opts in, those rules show up next to the public-API list in the same `agent_help()` output — so the rules travel with refactors instead of rotting in a sidecar file.
|
|
46
52
|
|
|
47
53
|
## Installation
|
|
48
54
|
|
|
@@ -54,11 +60,51 @@ Requires Python 3.10+. No runtime dependencies.
|
|
|
54
60
|
|
|
55
61
|
## Quickstart
|
|
56
62
|
|
|
63
|
+
`agent_help()` works on **any Python class, module, function, or method — no opt-in required**. Point it at a stdlib class:
|
|
64
|
+
|
|
57
65
|
```python
|
|
58
|
-
from agent_readable import
|
|
66
|
+
from agent_readable import agent_help
|
|
67
|
+
import logging
|
|
59
68
|
|
|
69
|
+
print(agent_help(logging.Logger))
|
|
70
|
+
```
|
|
60
71
|
|
|
61
|
-
|
|
72
|
+
Output (excerpted):
|
|
73
|
+
|
|
74
|
+
````
|
|
75
|
+
# Logger
|
|
76
|
+
|
|
77
|
+
## Constructor
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
Logger(name, level=0)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Purpose
|
|
84
|
+
|
|
85
|
+
Instances of the Logger class represent a single logging channel. ...
|
|
86
|
+
|
|
87
|
+
## Public API
|
|
88
|
+
|
|
89
|
+
- `addHandler(hdlr)` method: Add the specified handler to this logger.
|
|
90
|
+
- `debug(msg, *args, **kwargs)` method: Log 'msg % args' with severity 'DEBUG'.
|
|
91
|
+
- `info(msg, *args, **kwargs)` method: Log 'msg % args' with severity 'INFO'.
|
|
92
|
+
- `setLevel(level)` method: Set the logging level of this logger.
|
|
93
|
+
- `warning(msg, *args, **kwargs)` method: Log 'msg % args' with severity 'WARNING'.
|
|
94
|
+
- ...
|
|
95
|
+
````
|
|
96
|
+
|
|
97
|
+
The `## Public API` list comes from **runtime introspection of the live, installed version** — current signatures, no inherited-dunder noise, no stale training-data guesses. Substitute any class, module, function, or method (yours, a third party's, or a stdlib one); zero setup on the target side.
|
|
98
|
+
|
|
99
|
+
### Library authors: ship usage rules next to the code (optional)
|
|
100
|
+
|
|
101
|
+
When you own the class, you can also ship class-level usage rules — lifecycle order, pre-conditions, anti-patterns — that `agent_help()` returns alongside the auto-generated public-API list. Define a `__agent_notes__()` classmethod (no mixin, no inheritance required); notes accumulate across the MRO so subclasses don't lose parent rules:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from agent_readable import agent_help
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class Sensor:
|
|
62
108
|
"""Reads a value from a hardware sensor."""
|
|
63
109
|
|
|
64
110
|
def __init__(self, pin: int, *, unit: str = "C"): ...
|
|
@@ -85,43 +131,7 @@ class Sensor(AgentReadableMixin):
|
|
|
85
131
|
print(agent_help(Sensor))
|
|
86
132
|
```
|
|
87
133
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
````
|
|
91
|
-
# Sensor
|
|
92
|
-
|
|
93
|
-
## Constructor
|
|
94
|
-
|
|
95
|
-
```python
|
|
96
|
-
Sensor(pin: int, *, unit: str = 'C')
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
## Purpose
|
|
100
|
-
|
|
101
|
-
Reads a value from a hardware sensor.
|
|
102
|
-
|
|
103
|
-
## Public API
|
|
104
|
-
|
|
105
|
-
- `calibrate(offset: float)` method: Apply a calibration offset.
|
|
106
|
-
- `read() -> float` method: Read the current sensor value.
|
|
107
|
-
|
|
108
|
-
## Agent usage rules
|
|
109
|
-
|
|
110
|
-
- Prefer the public API listed above.
|
|
111
|
-
- Do not use private methods or attributes starting with `_`.
|
|
112
|
-
- Do not invent unsupported behavior.
|
|
113
|
-
- If usage is ambiguous, prefer the simplest documented usage pattern.
|
|
114
|
-
|
|
115
|
-
## Notes from class Sensor
|
|
116
|
-
|
|
117
|
-
## Do
|
|
118
|
-
|
|
119
|
-
- Call `calibrate()` once during setup, before `read()`.
|
|
120
|
-
|
|
121
|
-
## Do not
|
|
122
|
-
|
|
123
|
-
- Do not call `read()` before `calibrate()` on first use.
|
|
124
|
-
````
|
|
134
|
+
`agent_help(Sensor)` now returns the same auto-generated public-API list, plus a `## Notes from class Sensor` section carrying the rules above. Full output and inheritance-with-notes behavior in [Example 2](#example-2-inheritance-with-accumulated-notes) below.
|
|
125
135
|
|
|
126
136
|
## Why it matters
|
|
127
137
|
|
|
@@ -153,7 +163,7 @@ class Connection(builtins.object)
|
|
|
153
163
|
|
|
154
164
|
`agent_help(sqlite3.Connection)` produces a curated public-API list with current signatures and any class-level rules attached — see Example 1 below. Notes from `__agent_notes__()` accumulate across inheritance. Class docs travel with the code in commits, reviews, and refactors. Drift gets caught in code review, not weeks later in a sidecar file.
|
|
155
165
|
|
|
156
|
-
The examples below demonstrate
|
|
166
|
+
The examples below demonstrate five ways to use `agent_help()`, three with library-author opt-in (Examples 1–3) and two on plain classes/modules with no setup (Examples 4–5).
|
|
157
167
|
|
|
158
168
|
## Example 1: Wrapping an existing class
|
|
159
169
|
|
|
@@ -536,9 +546,13 @@ Outputs agent-oriented documentation for the given class, module, function, or m
|
|
|
536
546
|
|
|
537
547
|
### How does my agent know to call `agent_help()` instead of `help()`?
|
|
538
548
|
|
|
539
|
-
Install the
|
|
549
|
+
Install the companion agent skill:
|
|
550
|
+
|
|
551
|
+
```bash
|
|
552
|
+
npx skills add zydo/skills --skill agent-readable
|
|
553
|
+
```
|
|
540
554
|
|
|
541
|
-
The
|
|
555
|
+
The skill teaches the agent to install `agent-readable`, call `agent_help(target)` before writing code against a class, module, function, or method, and add `__agent_notes__()` (or improve docstrings) when authoring new public APIs. (Cursor is among the adopters too, but its skill integration is manual — you invoke the skill explicitly there rather than relying on description-match auto-activation.)
|
|
542
556
|
|
|
543
557
|
### How is this different from `AGENTS.md` / `llms.txt` / Cursor rules?
|
|
544
558
|
|
|
@@ -564,19 +578,23 @@ Yes. Two ways:
|
|
|
564
578
|
|
|
565
579
|
Yes — `agent_help()` falls back to introspection (Example 4). You get a structured summary of every plain class, mixin or not. Notes are added on top *if* the class defines them; otherwise the auto-doc is what you see.
|
|
566
580
|
|
|
581
|
+
### Is `agent_help()` the strongest fix for API hallucination?
|
|
582
|
+
|
|
583
|
+
No, and worth being upfront about it. The strongest known mitigation is **constrained decoding** at the harness or decoder layer — masking illegal API tokens before generation, so the model can't produce a nonexistent method at all. `agent_help()` takes the lighter, library-side path: an authoritative usage guide injected into the agent's context, leaving the agent to read and follow it. Recent research on Python API misuse shows the in-context route leaves a meaningful slice of hallucinations on the table even when verified API references are provided. The two approaches compose. Pick `agent_help()` when you have leverage on the library side and want a fix that works on every agent and every library today, without harness changes. Reach for constrained decoding when you control the harness or the decoder. Either way, verify generated code before merging.
|
|
584
|
+
|
|
567
585
|
## Keeping agent docs up to date
|
|
568
586
|
|
|
569
|
-
Agent docs can go stale when classes change — new methods, changed behavior, removed APIs. Install the skill
|
|
587
|
+
Agent docs can go stale when classes change — new methods, changed behavior, removed APIs. Install the companion skill (`npx skills add zydo/skills --skill agent-readable`) to teach your agent to run `agent_help()` before modifying a class, prefer docstrings over `__agent_notes__()`, and verify that the output stays accurate after changes.
|
|
570
588
|
|
|
571
589
|
## The `__agent_help__` protocol
|
|
572
590
|
|
|
573
|
-
`__agent_help__()` is a dunder protocol, similar in spirit to
|
|
591
|
+
`__agent_help__()` is a dunder protocol for tool-specific documentation, similar in spirit to:
|
|
574
592
|
|
|
575
|
-
- `
|
|
576
|
-
- `__rich_repr__` (Rich) —
|
|
577
|
-
- `__html__` (Django, Jinja2) — HTML
|
|
578
|
-
|
|
579
|
-
|
|
593
|
+
- `__doc__` (Python `help()`, Sphinx, IDEs, REPLs) — the canonical "this is my human-readable documentation" slot, read by inspection tools and surfaced through `help()`
|
|
594
|
+
- `__rich_repr__` (Rich) — Rich-specific console representation read only when Rich renders the object
|
|
595
|
+
- `__html__` (Django, Jinja2, MarkupSafe) — HTML-renderer-specific representation read only when those template engines render the object
|
|
596
|
+
|
|
597
|
+
Unlike `__str__` or `__fspath__`, these dunders don't change Python runtime behavior — they're metadata slots a specific tool reads when it wants a representation. `__agent_help__` follows the same pattern for the agent-docs case.
|
|
580
598
|
|
|
581
599
|
Classes that define a `@classmethod` named `__agent_help__` returning a `str` are considered agent-readable. Modules can define a top-level `__agent_help__` attribute (callable or string). Call the top-level `agent_help(obj)` function to get the docs — just like `str()` calls `__str__()`. The `AgentReadable` `typing.Protocol` and `AgentReadableMixin` are provided for convenience and type-checking, but neither is required.
|
|
582
600
|
|
|
@@ -584,13 +602,15 @@ Classes that define a `@classmethod` named `__agent_help__` returning a `str` ar
|
|
|
584
602
|
|
|
585
603
|
The two dunders intentionally encode different composition rules:
|
|
586
604
|
|
|
587
|
-
| Aspect | `__agent_help__()` | `__agent_notes__()`
|
|
588
|
-
| --------------- | ----------------------------------------------- |
|
|
589
|
-
| Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs
|
|
590
|
-
| Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this)
|
|
591
|
-
| When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules"
|
|
592
|
-
| Skipped when | (always called if defined) |
|
|
593
|
-
| Mixin required? | No — duck-typed classmethod is enough | No — defining `__agent_notes__` on any class is enough
|
|
605
|
+
| Aspect | `__agent_help__()` | `__agent_notes__()` |
|
|
606
|
+
| --------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
607
|
+
| Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs |
|
|
608
|
+
| Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this) |
|
|
609
|
+
| When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules" |
|
|
610
|
+
| Skipped when | (always called if defined) | Silently dropped (with `UserWarning`) when a custom `__agent_help__` is present (it owns the output) |
|
|
611
|
+
| Mixin required? | No — duck-typed classmethod is enough | No — defining `__agent_notes__` on any class is enough |
|
|
612
|
+
|
|
613
|
+
**Heads up on the both-defined case.** When a class defines both `__agent_help__()` and `__agent_notes__()`, the notes are silently dropped — `__agent_help__()` owns the output, and the auto-doc + notes path never runs. A `UserWarning` is emitted, but warnings are easy to miss in agent shells, CI logs, and notebooks — **treat "both defined" as a hard review error** rather than something the warning will reliably catch. Fix it by folding the notes into `__agent_help__()`, or by dropping the custom `__agent_help__()` and letting the auto-doc + notes path run.
|
|
594
614
|
|
|
595
615
|
## Class docstring hints
|
|
596
616
|
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# agent-readable
|
|
2
2
|
|
|
3
|
-
Stop coding agents from hallucinating
|
|
3
|
+
Stop coding agents from hallucinating Python APIs. `agent_help(target)` reads the live public-API surface off any class, module, function, or method — no author opt-in needed — and optionally returns class-level usage rules (lifecycle order, pre-conditions, anti-patterns) when the library ships them.
|
|
4
|
+
|
|
5
|
+
To let your coding agent automatically call `agent_help()` before using an unfamiliar API, install the skill:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx skills add zydo/skills --skill agent-readable
|
|
9
|
+
```
|
|
4
10
|
|
|
5
11
|
<!-- markdownlint-disable MD033 -->
|
|
6
12
|
<p align="center">
|
|
@@ -15,7 +21,7 @@ AI coding agents recognize established libraries from their training data, but t
|
|
|
15
21
|
|
|
16
22
|
Today there's nowhere to put those rules where an agent will reliably find them. Docstrings document the API surface, not behavioral rules. `AGENTS.md` and `llms.txt` work at project granularity, drift fast, and don't travel with refactors. `help()` only describes interfaces — and mixes inherited dunders and MRO detail in with the methods you actually want to use.
|
|
17
23
|
|
|
18
|
-
`agent-readable`
|
|
24
|
+
`agent-readable` has two halves, and the lighter one stands alone. The **consume side** is one function — `agent_help(target)` — that works on every Python library today, annotated or not. It introspects the live, installed class/module/function and returns a curated public-API list with current signatures, no inherited-dunder or MRO noise to wade through. That alone closes the *what exists* hallucination — agents stop inventing methods that aren't there and stop calling real ones with stale signatures. The **author side** adds two dunders: `__agent_help__` for full custom output and `__agent_notes__` for additive behavioral rules that accumulate across inheritance. When a library opts in, those rules show up next to the public-API list in the same `agent_help()` output — so the rules travel with refactors instead of rotting in a sidecar file.
|
|
19
25
|
|
|
20
26
|
## Installation
|
|
21
27
|
|
|
@@ -27,11 +33,51 @@ Requires Python 3.10+. No runtime dependencies.
|
|
|
27
33
|
|
|
28
34
|
## Quickstart
|
|
29
35
|
|
|
36
|
+
`agent_help()` works on **any Python class, module, function, or method — no opt-in required**. Point it at a stdlib class:
|
|
37
|
+
|
|
30
38
|
```python
|
|
31
|
-
from agent_readable import
|
|
39
|
+
from agent_readable import agent_help
|
|
40
|
+
import logging
|
|
32
41
|
|
|
42
|
+
print(agent_help(logging.Logger))
|
|
43
|
+
```
|
|
33
44
|
|
|
34
|
-
|
|
45
|
+
Output (excerpted):
|
|
46
|
+
|
|
47
|
+
````
|
|
48
|
+
# Logger
|
|
49
|
+
|
|
50
|
+
## Constructor
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
Logger(name, level=0)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Purpose
|
|
57
|
+
|
|
58
|
+
Instances of the Logger class represent a single logging channel. ...
|
|
59
|
+
|
|
60
|
+
## Public API
|
|
61
|
+
|
|
62
|
+
- `addHandler(hdlr)` method: Add the specified handler to this logger.
|
|
63
|
+
- `debug(msg, *args, **kwargs)` method: Log 'msg % args' with severity 'DEBUG'.
|
|
64
|
+
- `info(msg, *args, **kwargs)` method: Log 'msg % args' with severity 'INFO'.
|
|
65
|
+
- `setLevel(level)` method: Set the logging level of this logger.
|
|
66
|
+
- `warning(msg, *args, **kwargs)` method: Log 'msg % args' with severity 'WARNING'.
|
|
67
|
+
- ...
|
|
68
|
+
````
|
|
69
|
+
|
|
70
|
+
The `## Public API` list comes from **runtime introspection of the live, installed version** — current signatures, no inherited-dunder noise, no stale training-data guesses. Substitute any class, module, function, or method (yours, a third party's, or a stdlib one); zero setup on the target side.
|
|
71
|
+
|
|
72
|
+
### Library authors: ship usage rules next to the code (optional)
|
|
73
|
+
|
|
74
|
+
When you own the class, you can also ship class-level usage rules — lifecycle order, pre-conditions, anti-patterns — that `agent_help()` returns alongside the auto-generated public-API list. Define a `__agent_notes__()` classmethod (no mixin, no inheritance required); notes accumulate across the MRO so subclasses don't lose parent rules:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from agent_readable import agent_help
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class Sensor:
|
|
35
81
|
"""Reads a value from a hardware sensor."""
|
|
36
82
|
|
|
37
83
|
def __init__(self, pin: int, *, unit: str = "C"): ...
|
|
@@ -58,43 +104,7 @@ class Sensor(AgentReadableMixin):
|
|
|
58
104
|
print(agent_help(Sensor))
|
|
59
105
|
```
|
|
60
106
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
````
|
|
64
|
-
# Sensor
|
|
65
|
-
|
|
66
|
-
## Constructor
|
|
67
|
-
|
|
68
|
-
```python
|
|
69
|
-
Sensor(pin: int, *, unit: str = 'C')
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Purpose
|
|
73
|
-
|
|
74
|
-
Reads a value from a hardware sensor.
|
|
75
|
-
|
|
76
|
-
## Public API
|
|
77
|
-
|
|
78
|
-
- `calibrate(offset: float)` method: Apply a calibration offset.
|
|
79
|
-
- `read() -> float` method: Read the current sensor value.
|
|
80
|
-
|
|
81
|
-
## Agent usage rules
|
|
82
|
-
|
|
83
|
-
- Prefer the public API listed above.
|
|
84
|
-
- Do not use private methods or attributes starting with `_`.
|
|
85
|
-
- Do not invent unsupported behavior.
|
|
86
|
-
- If usage is ambiguous, prefer the simplest documented usage pattern.
|
|
87
|
-
|
|
88
|
-
## Notes from class Sensor
|
|
89
|
-
|
|
90
|
-
## Do
|
|
91
|
-
|
|
92
|
-
- Call `calibrate()` once during setup, before `read()`.
|
|
93
|
-
|
|
94
|
-
## Do not
|
|
95
|
-
|
|
96
|
-
- Do not call `read()` before `calibrate()` on first use.
|
|
97
|
-
````
|
|
107
|
+
`agent_help(Sensor)` now returns the same auto-generated public-API list, plus a `## Notes from class Sensor` section carrying the rules above. Full output and inheritance-with-notes behavior in [Example 2](#example-2-inheritance-with-accumulated-notes) below.
|
|
98
108
|
|
|
99
109
|
## Why it matters
|
|
100
110
|
|
|
@@ -126,7 +136,7 @@ class Connection(builtins.object)
|
|
|
126
136
|
|
|
127
137
|
`agent_help(sqlite3.Connection)` produces a curated public-API list with current signatures and any class-level rules attached — see Example 1 below. Notes from `__agent_notes__()` accumulate across inheritance. Class docs travel with the code in commits, reviews, and refactors. Drift gets caught in code review, not weeks later in a sidecar file.
|
|
128
138
|
|
|
129
|
-
The examples below demonstrate
|
|
139
|
+
The examples below demonstrate five ways to use `agent_help()`, three with library-author opt-in (Examples 1–3) and two on plain classes/modules with no setup (Examples 4–5).
|
|
130
140
|
|
|
131
141
|
## Example 1: Wrapping an existing class
|
|
132
142
|
|
|
@@ -509,9 +519,13 @@ Outputs agent-oriented documentation for the given class, module, function, or m
|
|
|
509
519
|
|
|
510
520
|
### How does my agent know to call `agent_help()` instead of `help()`?
|
|
511
521
|
|
|
512
|
-
Install the
|
|
522
|
+
Install the companion agent skill:
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
npx skills add zydo/skills --skill agent-readable
|
|
526
|
+
```
|
|
513
527
|
|
|
514
|
-
The
|
|
528
|
+
The skill teaches the agent to install `agent-readable`, call `agent_help(target)` before writing code against a class, module, function, or method, and add `__agent_notes__()` (or improve docstrings) when authoring new public APIs. (Cursor is among the adopters too, but its skill integration is manual — you invoke the skill explicitly there rather than relying on description-match auto-activation.)
|
|
515
529
|
|
|
516
530
|
### How is this different from `AGENTS.md` / `llms.txt` / Cursor rules?
|
|
517
531
|
|
|
@@ -537,19 +551,23 @@ Yes. Two ways:
|
|
|
537
551
|
|
|
538
552
|
Yes — `agent_help()` falls back to introspection (Example 4). You get a structured summary of every plain class, mixin or not. Notes are added on top *if* the class defines them; otherwise the auto-doc is what you see.
|
|
539
553
|
|
|
554
|
+
### Is `agent_help()` the strongest fix for API hallucination?
|
|
555
|
+
|
|
556
|
+
No, and worth being upfront about it. The strongest known mitigation is **constrained decoding** at the harness or decoder layer — masking illegal API tokens before generation, so the model can't produce a nonexistent method at all. `agent_help()` takes the lighter, library-side path: an authoritative usage guide injected into the agent's context, leaving the agent to read and follow it. Recent research on Python API misuse shows the in-context route leaves a meaningful slice of hallucinations on the table even when verified API references are provided. The two approaches compose. Pick `agent_help()` when you have leverage on the library side and want a fix that works on every agent and every library today, without harness changes. Reach for constrained decoding when you control the harness or the decoder. Either way, verify generated code before merging.
|
|
557
|
+
|
|
540
558
|
## Keeping agent docs up to date
|
|
541
559
|
|
|
542
|
-
Agent docs can go stale when classes change — new methods, changed behavior, removed APIs. Install the skill
|
|
560
|
+
Agent docs can go stale when classes change — new methods, changed behavior, removed APIs. Install the companion skill (`npx skills add zydo/skills --skill agent-readable`) to teach your agent to run `agent_help()` before modifying a class, prefer docstrings over `__agent_notes__()`, and verify that the output stays accurate after changes.
|
|
543
561
|
|
|
544
562
|
## The `__agent_help__` protocol
|
|
545
563
|
|
|
546
|
-
`__agent_help__()` is a dunder protocol, similar in spirit to
|
|
564
|
+
`__agent_help__()` is a dunder protocol for tool-specific documentation, similar in spirit to:
|
|
547
565
|
|
|
548
|
-
- `
|
|
549
|
-
- `__rich_repr__` (Rich) —
|
|
550
|
-
- `__html__` (Django, Jinja2) — HTML
|
|
551
|
-
|
|
552
|
-
|
|
566
|
+
- `__doc__` (Python `help()`, Sphinx, IDEs, REPLs) — the canonical "this is my human-readable documentation" slot, read by inspection tools and surfaced through `help()`
|
|
567
|
+
- `__rich_repr__` (Rich) — Rich-specific console representation read only when Rich renders the object
|
|
568
|
+
- `__html__` (Django, Jinja2, MarkupSafe) — HTML-renderer-specific representation read only when those template engines render the object
|
|
569
|
+
|
|
570
|
+
Unlike `__str__` or `__fspath__`, these dunders don't change Python runtime behavior — they're metadata slots a specific tool reads when it wants a representation. `__agent_help__` follows the same pattern for the agent-docs case.
|
|
553
571
|
|
|
554
572
|
Classes that define a `@classmethod` named `__agent_help__` returning a `str` are considered agent-readable. Modules can define a top-level `__agent_help__` attribute (callable or string). Call the top-level `agent_help(obj)` function to get the docs — just like `str()` calls `__str__()`. The `AgentReadable` `typing.Protocol` and `AgentReadableMixin` are provided for convenience and type-checking, but neither is required.
|
|
555
573
|
|
|
@@ -557,13 +575,15 @@ Classes that define a `@classmethod` named `__agent_help__` returning a `str` ar
|
|
|
557
575
|
|
|
558
576
|
The two dunders intentionally encode different composition rules:
|
|
559
577
|
|
|
560
|
-
| Aspect | `__agent_help__()` | `__agent_notes__()`
|
|
561
|
-
| --------------- | ----------------------------------------------- |
|
|
562
|
-
| Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs
|
|
563
|
-
| Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this)
|
|
564
|
-
| When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules"
|
|
565
|
-
| Skipped when | (always called if defined) |
|
|
566
|
-
| Mixin required? | No — duck-typed classmethod is enough | No — defining `__agent_notes__` on any class is enough
|
|
578
|
+
| Aspect | `__agent_help__()` | `__agent_notes__()` |
|
|
579
|
+
| --------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
580
|
+
| Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs |
|
|
581
|
+
| Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this) |
|
|
582
|
+
| When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules" |
|
|
583
|
+
| Skipped when | (always called if defined) | Silently dropped (with `UserWarning`) when a custom `__agent_help__` is present (it owns the output) |
|
|
584
|
+
| Mixin required? | No — duck-typed classmethod is enough | No — defining `__agent_notes__` on any class is enough |
|
|
585
|
+
|
|
586
|
+
**Heads up on the both-defined case.** When a class defines both `__agent_help__()` and `__agent_notes__()`, the notes are silently dropped — `__agent_help__()` owns the output, and the auto-doc + notes path never runs. A `UserWarning` is emitted, but warnings are easy to miss in agent shells, CI logs, and notebooks — **treat "both defined" as a hard review error** rather than something the warning will reliably catch. Fix it by folding the notes into `__agent_help__()`, or by dropping the custom `__agent_help__()` and letting the auto-doc + notes path run.
|
|
567
587
|
|
|
568
588
|
## Class docstring hints
|
|
569
589
|
|
|
Binary file
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: agent-readable
|
|
3
|
-
description: Use agent_readable to get authoritative usage guidance for Python classes, modules, functions, and methods before writing code against them, and to make new Python APIs agent-readable. Activate when writing or modifying Python code that calls into a class, module, function, or method from any library, OR when adding/changing a public Python API. Covers calling `agent_help(obj)` for structured docs + behavioral rules, and authoring docstrings + `__agent_notes__()` so usage rules travel with the code.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# agent-readable
|
|
7
|
-
|
|
8
|
-
`agent-readable` is a tiny Python library (`pip install agent-readable`, zero
|
|
9
|
-
runtime deps, Python 3.10+) that gives any class, module, function, or method a
|
|
10
|
-
structured, agent-oriented "usage guide" — the canonical answer to *how do I
|
|
11
|
-
correctly use this?* — produced by one call: `agent_help(obj)`.
|
|
12
|
-
|
|
13
|
-
This skill teaches you two jobs:
|
|
14
|
-
|
|
15
|
-
1. **Consume** — before writing Python code that uses a class, module, function,
|
|
16
|
-
or method, call `agent_help(target)` and use the output as the source of truth.
|
|
17
|
-
2. **Author** — when adding or modifying a public Python API, make it
|
|
18
|
-
agent-readable so future agents get correct usage on the first try.
|
|
19
|
-
|
|
20
|
-
**Current language support:** Python 3.10+. Counterparts in other languages are
|
|
21
|
-
on the roadmap; when they ship, this skill will gain sections for them and the
|
|
22
|
-
same trigger will fan out across languages.
|
|
23
|
-
|
|
24
|
-
## When to activate this skill
|
|
25
|
-
|
|
26
|
-
* The user asks you to write, modify, or refactor Python code that calls into a
|
|
27
|
-
class, module, function, or method (from a third-party library or from this
|
|
28
|
-
project).
|
|
29
|
-
* The user asks you to add or change a public Python class, module, or function
|
|
30
|
-
(especially one other code or other agents will use).
|
|
31
|
-
* The user mentions `agent_help`, `__agent_notes__`, `AgentReadableMixin`, the
|
|
32
|
-
`AgentReadable` protocol, or the `agent-readable` library by name.
|
|
33
|
-
|
|
34
|
-
## Install
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
pip install agent-readable # or: uv add agent-readable
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Python 3.10+. No runtime dependencies. The library exposes one top-level function
|
|
41
|
-
(`agent_help`), one protocol (`AgentReadable`), and one optional mixin
|
|
42
|
-
(`AgentReadableMixin`).
|
|
43
|
-
|
|
44
|
-
## Job 1 — Consume: call `agent_help(target)` first
|
|
45
|
-
|
|
46
|
-
**Before writing code against a Python target, always call `agent_help(target)`.**
|
|
47
|
-
Even if the target does *not* opt in: `agent_help()` falls back to introspection
|
|
48
|
-
and returns a structured, current-signature public-API listing — strictly better
|
|
49
|
-
than guessing from training data.
|
|
50
|
-
|
|
51
|
-
```python
|
|
52
|
-
from agent_readable import agent_help
|
|
53
|
-
|
|
54
|
-
print(agent_help(SomeClass)) # class — constructor, public API, notes
|
|
55
|
-
print(agent_help(some_instance)) # instance — dispatches to its class
|
|
56
|
-
print(agent_help(some_module)) # module — docstring + public functions/classes
|
|
57
|
-
print(agent_help(some_func)) # function or method — signature + docstring
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
From a coding-agent shell:
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
python -c "from agent_readable import agent_help; import target_lib; print(agent_help(target_lib.SomeClass))"
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### How to read the output
|
|
67
|
-
|
|
68
|
-
`agent_help()` returns Markdown with these sections (a subset appears depending
|
|
69
|
-
on the target):
|
|
70
|
-
|
|
71
|
-
* `# <name>` and `## Purpose` — the docstring summary.
|
|
72
|
-
* `## Constructor` / `## Signature` — the **current** signature (the source of
|
|
73
|
-
truth, beats anything in training data).
|
|
74
|
-
* `## Public API` — every public member with current signatures. Treat this as
|
|
75
|
-
the authoritative list; do not call methods not listed here.
|
|
76
|
-
* `## Agent usage rules` — generic rules (prefer the public API, no private
|
|
77
|
-
names, etc.).
|
|
78
|
-
* `## Notes from class <X>` — **load-bearing** class-specific behavioral rules:
|
|
79
|
-
lifecycle order, pre-conditions, anti-patterns. If a note says *"call
|
|
80
|
-
`calibrate()` once before `read()`"*, honor it. If multiple `Notes from class`
|
|
81
|
-
sections appear, the leaf class wins on conflict (the header marks this).
|
|
82
|
-
|
|
83
|
-
If you are unsure how to use a target, run `agent_help()` again — it is cheap and
|
|
84
|
-
the docstrings/notes are the canonical source.
|
|
85
|
-
|
|
86
|
-
## Job 2 — Author: make new Python APIs agent-readable
|
|
87
|
-
|
|
88
|
-
Default to **better docstrings** first; only reach for the dunders when
|
|
89
|
-
docstrings cannot compose the rule.
|
|
90
|
-
|
|
91
|
-
### 2a. Docstrings are the primary surface
|
|
92
|
-
|
|
93
|
-
`agent_help()` already extracts docstrings: the class docstring becomes
|
|
94
|
-
`## Purpose`, and each method's first paragraph becomes its `## Public API`
|
|
95
|
-
summary. So the bar for a good docstring here is the same as for any
|
|
96
|
-
well-documented library: concise summary line, then params/returns/raises if
|
|
97
|
-
nontrivial. Keep per-method behavior in the method's docstring — that is where it
|
|
98
|
-
stays attached through refactors.
|
|
99
|
-
|
|
100
|
-
### 2b. Add `__agent_notes__()` only for cross-method behavioral rules
|
|
101
|
-
|
|
102
|
-
When a rule does not fit in any single method's docstring — lifecycle order
|
|
103
|
-
across methods, pre-conditions, anti-patterns ("use `call()` for non-streaming,
|
|
104
|
-
`stream()` for streaming"), do/don't lists — put it on the **class** as a
|
|
105
|
-
`classmethod` named `__agent_notes__()`:
|
|
106
|
-
|
|
107
|
-
```python
|
|
108
|
-
class Sensor:
|
|
109
|
-
"""Reads a value from a hardware sensor."""
|
|
110
|
-
|
|
111
|
-
def __init__(self, pin: int, *, unit: str = "C"): ...
|
|
112
|
-
def calibrate(self, offset: float): ...
|
|
113
|
-
def read(self) -> float: ...
|
|
114
|
-
|
|
115
|
-
@classmethod
|
|
116
|
-
def __agent_notes__(cls) -> str:
|
|
117
|
-
return """
|
|
118
|
-
## Do
|
|
119
|
-
|
|
120
|
-
- Call `calibrate()` once during setup, before `read()`.
|
|
121
|
-
|
|
122
|
-
## Do not
|
|
123
|
-
|
|
124
|
-
- Do not call `read()` before `calibrate()` on first use.
|
|
125
|
-
"""
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Facts about `__agent_notes__()` worth knowing:
|
|
129
|
-
|
|
130
|
-
* **Accumulates across the MRO.** Every class in the inheritance chain that
|
|
131
|
-
defines its own `__agent_notes__()` contributes a section to `agent_help()`'s
|
|
132
|
-
output. The leaf class is marked as taking precedence on conflict.
|
|
133
|
-
* **Do not call `super().__agent_notes__()`** — collection is automatic.
|
|
134
|
-
* **No mixin required.** Defining `__agent_notes__` on any class is enough.
|
|
135
|
-
Monkey-patching also works for classes you do not own:
|
|
136
|
-
```python
|
|
137
|
-
ThirdPartyClass.__agent_notes__ = classmethod(lambda cls: "...")
|
|
138
|
-
```
|
|
139
|
-
* **Optional `AgentReadableMixin`.** Inheriting from
|
|
140
|
-
`agent_readable.AgentReadableMixin` is purely a convenience for type-checking
|
|
141
|
-
and IDE hints — `agent_help()` works on plain classes too.
|
|
142
|
-
|
|
143
|
-
### 2c. Almost never define a custom `__agent_help__()`
|
|
144
|
-
|
|
145
|
-
`__agent_help__()` **replaces** the entire auto-generated output. Use it only
|
|
146
|
-
when you have a hand-formatted help string you genuinely want to ship verbatim.
|
|
147
|
-
In every other case, let the auto-doc generate from your class + docstrings +
|
|
148
|
-
`__agent_notes__()`.
|
|
149
|
-
|
|
150
|
-
### 2d. Verify after annotating
|
|
151
|
-
|
|
152
|
-
After adding or changing notes, run:
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
python -c "from agent_readable import agent_help; from your_module import YourClass; print(agent_help(YourClass))"
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Check that signatures are correct (you may need to fix type hints), notes appear
|
|
159
|
-
in MRO order with the leaf class marked precedence-winning, and no private
|
|
160
|
-
members leaked into `## Public API`.
|
|
161
|
-
|
|
162
|
-
## Footguns
|
|
163
|
-
|
|
164
|
-
* **Do not define both a custom `__agent_help__()` and `__agent_notes__()` on
|
|
165
|
-
the same class.** A custom `__agent_help__()` owns the full output, so the
|
|
166
|
-
notes are silently dropped (a `UserWarning` is emitted). Fix it one of two
|
|
167
|
-
ways: fold the notes into your custom `__agent_help__()`, or remove the
|
|
168
|
-
custom `__agent_help__()` and let the auto-doc + notes path run.
|
|
169
|
-
* **Do not put per-method instructions in `__agent_notes__()`.** Single-method
|
|
170
|
-
behavior belongs in that method's docstring, where it stays attached through
|
|
171
|
-
refactors. `__agent_notes__()` is for rules that span methods.
|
|
172
|
-
* **Do not mix `agent_help()` with `help()`.** `help()` returns hundreds of
|
|
173
|
-
lines of inherited dunders and MRO detail; `agent_help()` returns a curated,
|
|
174
|
-
structured guide with behavioral rules attached. Use `agent_help()`.
|
|
175
|
-
|
|
176
|
-
## The one rule
|
|
177
|
-
|
|
178
|
-
> Before writing or modifying Python code that uses an external class, module,
|
|
179
|
-
> function, or method, call `agent_help(target)` and use its output as the
|
|
180
|
-
> canonical usage guide.
|
|
181
|
-
|
|
182
|
-
That call is cheap, runs in any coding-agent shell, and works on any Python
|
|
183
|
-
object — annotated or not.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|