Learning.drop_journey
def drop_journey(
jid:str
):Remove a journey. Its concepts and their state stay.
A learner often wants to learn more than one concept. “I want to learn how LLMs work” needs tokenizers, embeddings, attention, transformers and more, in a given order. A journey keeps that goal and that order on the learning shelf.
The planner (one model call) proposes the steps. It uses the concepts on the shelf when they fit. It makes new concepts for the topics that the shelf does not have. Then the learner can change the path: move, skip, mark as known, add and split steps.
The journey does not keep progress. Progress comes from the learner model each time. A step is learned when the floor of its concept reaches the goal depth. So a concept that the learner learns anywhere counts in each journey that has it.
Import the modules that this notebook uses.
A journey is one vault document of kind journey. Its meta has the goal, a title, a subject, and the steps. Each step has a slug, a status (todo, known or skip) and the reason that it is in the path.
Remove a journey. Its concepts and their state stay.
All journeys, the most recently changed first.
Save journey j, and replace an earlier one with the same id.
step_state names the state of one step from its status and its concept. The states learned, known, skipped and missing (the concept is gone) are done. The states new and started are not done.
Each step with its concept and state, the count done, the count that counts, and the current step.
One of new, started, learned, known, skipped or missing.
current gives the slug of the step to learn now. A step in skip waits, unless no other step is left.
The slug of the first step that is not done and not in skip; else the first step that is not done.
journey_step makes one change to one step. journey_add adds a step at the end, or before another step.
Add concept slug as a step, at the end or before step before. A step that is there already stays where it is.
Do act on step slug: up, down, skip, known, todo or remove.
The planner gets the goal and the learner profile as text. It also gets the concepts on the shelf that are near the goal or that the learner started. It returns the steps in order.
(slug, name, floor) for the concepts that search finds for goal, and the ones the learner started.
The prompt that plans a path to goal.
plan saves the journey. A step can name a known slug, or a known name or alias. Then it uses that concept and keeps its state. Each other step becomes a new concept, with the steps it needs as its prereqs. With sources, plan links each new concept to the passages that name it in the learner’s own sources.
Plan a journey to goal, make the concepts that the shelf does not have, and save it.
add_topic adds a topic by name. When the shelf knows the name, add_topic uses that concept. For a new name, the model writes a short statement.
Add topic name to journey jid, as a concept on the shelf. Return the journey.
name_gaps keeps each idea that you say you do not know as a gap of the concept you learn. It finds the idea on the shelf by name or alias. Or it adds the idea with a short statement. Next time the gap comes first, but it does not block the concept.
Find or add a concept for each idea in terms, and keep it as a gap of slug. Return the gap slugs.
A new idea becomes a concept with a statement and the gap provenance. name_gaps finds an idea that is already on the shelf by its alias. It does not add that idea again.
from abhyasa.store import temp_learning
from abhyasa.llm import Fake
ngl = temp_learning(); ngl.put('Functions and the neuron', 'z = w . x + b.', 'ml'); ngl.put('Bias', 'A constant added to z.', 'ml', aliases=['the bias'])
gask = Fake({'The idea: the loop': dict(name='Weighted sum as a loop', statement='A loop adds each weight times its input.')})
test_eq(name_gaps(ngl, gask, 'functions-and-the-neuron', ['the loop', 'the bias', 'the loop']), ['weighted-sum-as-a-loop', 'bias'])
g = ngl.concept('weighted-sum-as-a-loop')
test_eq((g.aliases, g.provenance[0]['src'], g.provenance[0]['of']), (['the loop'], 'gap', 'functions-and-the-neuron'))
test_eq(ngl.concept('functions-and-the-neuron').gaps, ['weighted-sum-as-a-loop', 'bias'])split divides a big step into smaller steps. It runs extract over the statement of the step and the passages from the learner’s sources. The concepts that it finds go before the step, as its prereqs.
Find the parts of step slug, add them before it as steps and prereqs, and return their slugs.
related gives the concepts next to slug in the graph. First come the concepts that build on it, then the related ones, then its prereqs. The concepts that are not learned come first. with_step finds the journeys that have a concept. beyond gives what comes after a journey: the concepts that build on its steps and are not in it.
Up to k concepts that build on the steps of journey j and are not in it.
(journey, index) for each journey that has step slug.
Up to k concepts next to slug: what builds on it, what relates to it, then what it needs.
A fake model plans a path to LLMs. The shelf has attention already, at depth 3.
from abhyasa.store import temp_learning
from abhyasa.llm import Fake
lr = temp_learning()
lr.put('Attention', 'Weights from query-key dot products.', 'ml')
lr.update('attention', floor=3, seen=1)
fake = Fake({'You plan a learning path': dict(title='How LLMs work', subject='ml', steps=[
dict(name='Tokenizers', statement='A tokenizer cuts text into tokens.', slug='', why='The model reads tokens.', needs=[]),
dict(name='Embeddings', statement='An embedding maps a token to a vector.', slug='', why='Tokens become vectors.', needs=['Tokenizers']),
dict(name='Attention', statement='', slug='attention', why='The core of a transformer.', needs=['Embeddings']),
dict(name='Mamba', statement='Mamba is a selective state-space model.', slug='', why='Another way to mix tokens.', needs=['Attention'])])})
j = plan(lr, fake, 'I want to learn how LLMs work', who='Background: codes in Python')plan keeps the order, uses the known concept, and makes the new ones with their prereqs.
test_eq([s['slug'] for s in j.steps], ['tokenizers', 'embeddings', 'attention', 'mamba'])
test_eq((lr.concept('embeddings').prereqs, lr.concept('mamba').prereqs, lr.concept('mamba').subject), (['tokenizers'], ['attention'], 'ml'))
test_eq(lr.concept('attention').floor, 3) # a known concept keeps its state
assert 'codes in Python' in fake.calls[-1][0] and 'attention: Attention (depth 3)' in fake.calls[-1][0]progress names the state of each step. The current step is the first that is not done.
A step that the learner learns anywhere is learned in this journey too. A step that is marked known or skipped is done. Then current moves to the next step.
lr.update('tokenizers', floor=4, seen=2)
lr.journey_step(j.id, 'embeddings', 'known')
pg = progress(lr, lr.journey(j.id))
test_eq([s.state for s in pg.steps], ['learned', 'known', 'started', 'new'])
test_eq((pg.done, current(lr, lr.journey(j.id)), current(lr, lr.journey(j.id), skip={'attention'})), (2, 'attention', 'mamba'))Steps move up and down. A removed step leaves the path. A skipped step does not count in the total.
lr.journey_step(j.id, 'mamba', 'up')
test_eq([s['slug'] for s in lr.journey(j.id).steps], ['tokenizers', 'embeddings', 'mamba', 'attention'])
lr.journey_step(j.id, 'mamba', 'skip')
test_eq(progress(lr, lr.journey(j.id)).total, 3)
lr.journey_step(j.id, 'mamba', 'remove')
test_eq(len(lr.journey(j.id).steps), 3)add_topic uses a known name, and asks the model only for a new one.
fake.rules['The idea: '] = dict(name='PyTorch', statement='PyTorch is a Python library for tensors and autograd.')
add_topic(lr, fake, j.id, 'PyTorch')
add_topic(lr, fake, j.id, 'tokenizers') # known, and in the path already: no change
test_eq([s['slug'] for s in lr.journey(j.id).steps], ['tokenizers', 'embeddings', 'attention', 'pytorch'])split adds the parts of a step before it, as its prereqs.
fake.rules['Return JSON: {"concepts"'] = dict(concepts=[dict(name='Query and key', statement='Each token has a query and a key.', aliases=[], prereqs=[])])
new = split(lr, fake, j.id, 'attention')
test_eq(list(new), ['query-and-key'])
test_eq([s['slug'] for s in lr.journey(j.id).steps], ['tokenizers', 'embeddings', 'query-and-key', 'attention', 'pytorch'])
assert 'query-and-key' in lr.concept('attention').prereqsrelated puts what builds on a concept first. with_step finds the journeys of a concept. beyond gives what comes after.
lr.put('Transformer block', 'Attention, then a feed-forward layer.', 'ml', prereqs=['attention'])
test_eq(related(lr, 'attention').attrgot('slug'), ['mamba', 'transformer-block', 'query-and-key']) # mamba left the path, but it builds on attention
test_eq([(x.id, i) for x, i in with_step(lr, 'attention')], [(j.id, 3)])
test_eq(beyond(lr, lr.journey(j.id)).attrgot('slug'), ['mamba', 'transformer-block'])journeys lists the journeys with the newest change first. drop_journey removes one journey.