feat(mcp): the bundle's map, and a working method that reads it first
C5. `bundlemap.build_map` lists a bundle in its own words: one line per source document -- its name, then the titles of its concepts in document order -- and documents whose names differ only in their numbers (a changelog per release, a note per week) as ONE line: the name with every number as `#`, the count, the first and last by natural order, and the titles across the series that are words. `SERIES_MIN` = 5, at most `TITLES_PER_LINE` = 24 titles a line, the lines capped at `MAP_MAX_BYTES` = 48 000 together with `lines_truncated` counting the rest. Derived on every call, never stored. The card (`okf card`, `okf_describe`) carries it as `map` and no longer carries `source_files`: that list named every document a second time with no series collapsed, a quarter of the reply on a large bundle, for names the map already carries. Chose removal over keeping both because the describe reply has to fit a client's tool-reply limit and the map says more. The working method now reads: take the map first (`okf card`, or `okf_describe`), write two to four sub-questions in its words, and send them in ONE call (`--question` repeated, or `okf_ask` `questions`). Changed in the skill template, the generated `skills/okf-consume`, and the server instructions (held under the 2 KB a client keeps). The regeneration recipe for `skills/okf-consume` gains `--for-bundle`: since v1.1 the generator writes the generic skill by default, so the recipe as published produced the other file. A test holds a four-sub-question `okf_ask` over concepts far over the passage size under 50 000 bytes of reply text (25 000 tokens at a pessimistic two bytes a token). The real-collection measurements are kept in local state. Suite on a clean tree after `git add`: 2429 passed, 2 skipped, 4 xfailed. ruff, ruff format, mypy --strict clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
f7cd84c5e6
commit
da6faf8776
9 changed files with 450 additions and 64 deletions
|
|
@ -57,28 +57,31 @@ and the cut; it decides nothing about the question.
|
|||
Five steps, in this order. The pre-pass is step three, not step one: a question
|
||||
asked in the wrong words reaches the wrong concepts however good the ranking is.
|
||||
|
||||
**1. Understand the question first.** Read the bundle's map before you search
|
||||
it — what it holds, how many concepts, what the documents are called. Then put
|
||||
the question into the bundle's own words: a bundle written in one language and
|
||||
a question asked in another share few tokens, and the pre-pass matches tokens.
|
||||
Take the terms from the bundle's own titles, not from your vocabulary.
|
||||
**1. Understand the question first.** Read the bundle's `map` before you search
|
||||
it — `okf card <BUNDLE_ROOT>` prints it: one line per document with its section
|
||||
titles, a series of like-named documents as one line. Then put the question
|
||||
into the bundle's own words: a bundle written in one language and a question
|
||||
asked in another share few tokens, and the pre-pass matches tokens. Take the
|
||||
terms from the map's titles, not from your vocabulary.
|
||||
|
||||
**2. Split a broad question into 2–4 sub-questions.** One search for a question
|
||||
with four parts returns the best eight concepts for the average of the four,
|
||||
which is often the best eight for none of them. Write the sub-questions down;
|
||||
they are also the shape of the answer.
|
||||
|
||||
**3. Search per sub-question. Several searches are normal, and searching again
|
||||
is allowed and expected.** Run the pre-pass once per sub-question:
|
||||
**3. Search all sub-questions in ONE run. Several searches are normal, and
|
||||
searching again is allowed and expected.** Give the pre-pass every sub-question
|
||||
at once:
|
||||
|
||||
```sh
|
||||
okf consume <BUNDLE_ROOT> --question "one sub-question" --out /tmp/p1.json
|
||||
okf consume <BUNDLE_ROOT> --question "first sub-question" --question "second sub-question" --out /tmp/p1.json
|
||||
```
|
||||
|
||||
After each run, read two things: what came back, and what lay just outside the
|
||||
cut. `withheld.nearest` names the best-ranked concepts that missed, with their
|
||||
titles — if one of them is what you were after, that is a signal about the
|
||||
WORDS, not a closed door. Search again with the words that concept uses, or
|
||||
Each sub-question is ranked alone and the answers are interleaved; every
|
||||
excerpt names the `subquestions` it answered. After each run, read two things:
|
||||
what came back, and what lay just outside the cut. `withheld.nearest` names
|
||||
the best-ranked concepts that missed, with their titles — if one of them is
|
||||
what you were after, that is a signal about the WORDS, not a closed door. Search again with the words that concept uses, or
|
||||
ask for it by name. There is no limit on runs and no penalty for a run that
|
||||
found nothing; a run that found nothing is a measurement, and its denominator
|
||||
is worth carrying. When `coverage.weak` is true — a word of yours the bundle
|
||||
|
|
|
|||
|
|
@ -68,28 +68,31 @@ and the cut; it decides nothing about the question.
|
|||
Five steps, in this order. The pre-pass is step three, not step one: a question
|
||||
asked in the wrong words reaches the wrong concepts however good the ranking is.
|
||||
|
||||
**1. Understand the question first.** Read the bundle's map before you search
|
||||
it — what it holds, how many concepts, what the documents are called. Then put
|
||||
the question into the bundle's own words: a bundle written in one language and
|
||||
a question asked in another share few tokens, and the pre-pass matches tokens.
|
||||
Take the terms from the bundle's own titles, not from your vocabulary.
|
||||
**1. Understand the question first.** Read the bundle's `map` before you search
|
||||
it — `okf card examples/ingest-golden-segmented-okf-v0-2/expected-bundle` prints it: one line per document with its section
|
||||
titles, a series of like-named documents as one line. Then put the question
|
||||
into the bundle's own words: a bundle written in one language and a question
|
||||
asked in another share few tokens, and the pre-pass matches tokens. Take the
|
||||
terms from the map's titles, not from your vocabulary.
|
||||
|
||||
**2. Split a broad question into 2–4 sub-questions.** One search for a question
|
||||
with four parts returns the best eight concepts for the average of the four,
|
||||
which is often the best eight for none of them. Write the sub-questions down;
|
||||
they are also the shape of the answer.
|
||||
|
||||
**3. Search per sub-question. Several searches are normal, and searching again
|
||||
is allowed and expected.** Run the pre-pass once per sub-question:
|
||||
**3. Search all sub-questions in ONE run. Several searches are normal, and
|
||||
searching again is allowed and expected.** Give the pre-pass every sub-question
|
||||
at once:
|
||||
|
||||
```sh
|
||||
okf consume examples/ingest-golden-segmented-okf-v0-2/expected-bundle --question "one sub-question" --out /tmp/p1.json
|
||||
okf consume examples/ingest-golden-segmented-okf-v0-2/expected-bundle --question "first sub-question" --question "second sub-question" --out /tmp/p1.json
|
||||
```
|
||||
|
||||
After each run, read two things: what came back, and what lay just outside the
|
||||
cut. `withheld.nearest` names the best-ranked concepts that missed, with their
|
||||
titles — if one of them is what you were after, that is a signal about the
|
||||
WORDS, not a closed door. Search again with the words that concept uses, or
|
||||
Each sub-question is ranked alone and the answers are interleaved; every
|
||||
excerpt names the `subquestions` it answered. After each run, read two things:
|
||||
what came back, and what lay just outside the cut. `withheld.nearest` names
|
||||
the best-ranked concepts that missed, with their titles — if one of them is
|
||||
what you were after, that is a signal about the WORDS, not a closed door. Search again with the words that concept uses, or
|
||||
ask for it by name. There is no limit on runs and no penalty for a run that
|
||||
found nothing; a run that found nothing is a measurement, and its denominator
|
||||
is worth carrying. When `coverage.weak` is true — a word of yours the bundle
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ Regenerate them from the repository root rather than editing either file, with
|
|||
|
||||
```sh
|
||||
okf skill examples/ingest-golden-segmented-okf-v0-2/expected-bundle \
|
||||
--out skills/okf-consume --force --example-question "Hva sier veiledningen om krav?"
|
||||
--out skills/okf-consume --force --for-bundle \
|
||||
--example-question "Hva sier veiledningen om krav?"
|
||||
python3 -c 'import os, pathlib; p = pathlib.Path("skills/okf-consume/SKILL.md"); p.write_text(p.read_text(encoding="utf-8").replace(os.path.realpath(".") + "/", ""), encoding="utf-8")'
|
||||
okf check --skill skills/okf-consume/SKILL.md \
|
||||
--payload skills/okf-consume/references/example-payload.json
|
||||
|
|
@ -20,6 +21,8 @@ okf check --skill skills/okf-consume/SKILL.md \
|
|||
|
||||
Why each part is there:
|
||||
|
||||
- **`--for-bundle`**: since v1.1 the generator writes the GENERIC skill by
|
||||
default; this copy is the instantiated one, for this bundle.
|
||||
- **`--force`**: the generator refuses to replace an existing `SKILL.md`
|
||||
(`refused (target_occupied)`), because a silent overwrite would destroy a
|
||||
hand-edited copy.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue