Example: terraphim_agent shared learning end-to-end
This example shows how an AI coding agent captures a failed command, promotes
it to a SharedLearning, and lets a different agent retrieve it through the
hybrid scorer. The same code paths are used by the production terraphim-agent
binary and the terraphim_agent library.
Audience: developers integrating the
terraphim_agentlibrary into a Rust binary, and operators debugging why a particular learning surfaces (or does not) in aterraphim-agent suggestresponse.Source of truth:
terraphim-clients/crates/terraphim_agent/. The snippets below compile against the crate at commit8c245fd78.Background reading:
docs/architecture/terraphim-agent.mdexplains the data flow and hybrid scorer; this page is the working example to that architecture.
0. Build the crate
# Default build (no shared learning)
# Build with the cross-agent learning store and the hybrid scorer
The shared-learning feature pulls in terraphim_rolegraph and
terraphim_types; the cross-agent-injection feature adds the
LearningInjector.
1. Capture a failed command
The capture pipeline expects a PostToolUse event from an AI coding agent.
The simplest way to feed it from your own code is to construct a
HookInput and call capture_failed_command directly.
use ;
let config = default;
let hook = from_json_with_format?;
if hook.should_capture The capture pipeline reda secrets, filters test-runner commands, and writes
a markdown file to the resolved storage_location() (project dir if it
exists, otherwise global dir, both honouring TERRAPHIM_DEFAULT_DATA_PATH).
2. Bridge a capture to the cross-agent store
The per-agent CapturedLearning and the cross-agent SharedLearning are
deliberately different types. shared_learning_from_entry performs the
bridge when the shared-learning feature is on.
use shared_learning_from_entry;
use ;
async store_with_dedup is the entry point that decides whether the new learning
is a fresh entry (StoreResult::Created) or merges into an existing
near-duplicate above the configured similarity threshold
(StoreResult::Merged).
3. Wire a Terraphim RoleGraph for hybrid scoring
Hybrid scoring is opt-in. Without a graph the store falls back to pure
BM25. The graph is supplied as a terraphim_rolegraph::RoleGraph; the
store wraps it in a std::sync::RwLock internally.
use RoleGraph;
async set_role_graph performs an initial sync of the in-memory index into the
graph, so subsequent suggest and find_similar calls can use the hybrid
scorer without an explicit warm-up call. If the index or graph is locked
when set_role_graph runs, the graph is left empty (with a tracing::warn!)
and the next insert re-syncs that single learning.
4. Suggest learnings to a running agent
suggest is the per-agent lookup. It applies the applicable_agents
filter first, then scores with hybrid or BM25.
async Empty applicable_agents on a learning means "global" — every agent sees
it. Populated lists scope the learning to those agent names only.
5. Find similar learnings (corpus-wide)
find_similar does not filter by applicable_agents. Use it when the
caller already knows the corpus is relevant (e.g. the binary is running
its own internal audit and only cares about its own learnings).
async The returned Vec<(f64, SharedLearning)> is sorted by score descending
and the score is normalised against the trust-level weight, so a hit from
a L3 learning can outrank a more textually similar L1 hit.
6. Apply a learning and let it promote
When a learning is actually used and works, record the application. The
store increments the quality counters and, if auto_promote_l2 is on
and the L2 criteria (3+ applications across 2+ agents with positive
outcome) are met, automatically promotes the learning to L2.
async 7. Inspect the on-disk file
After the previous steps, the on-disk file looks like the one in the
architecture document. You can confirm the frontmatter with head -20:
Or list every learning the store currently knows about:
L2 and L3 learnings are the ones the wiki sync client will publish to
Gitea. Run terraphim-agent --help for the current subcommand list if you
want to drive the store from the CLI instead of from Rust.
8. Putting it together
The full happy path, in one sequence:
- Install the capture hook:
terraphim-agent install-hook --agent claude - A coding session fails a
git push -f; the hook captures it. shared_learning_from_entrypromotes the capture to aSharedLearning.store_with_dedupwrites it to the markdown backend.- A second agent starts up, calls
set_role_graph, and askssuggest("git push rejected", "code-review-agent", 5). - The hybrid scorer returns the new learning at L1 with a positive score.
- The second agent applies the correction and the store auto-promotes to L2 once criteria are met.
- The wiki sync client publishes the L2 learning to the Gitea wiki.
Each step is independently testable: see the unit tests in
markdown_store.rs and store.rs, and the integration test in
tests/kg_ranking_integration_test.rs.
9. Troubleshooting
- Learning not surfaced — first check that
applicable_agentsis empty or includes the calling agent's name. Then check whether the thesaurus loaded into the rolegraph contains the query terms. With no thesaurus node matching the query, the hybrid path returnsNoneand the store falls back to BM25. store_with_dedupalways merges into the same learning — lowerStoreConfig::similarity_thresholdor check theextract_searchable_textbody — the merge is driven by BM25 on that lowercased concatenation, not on the title alone.- Hybrid path silently disabled — set
RUST_LOG=terraphim_agent=debugand look for"rolegraph write lock poisoned"or"shared_learning_store index contended"warnings. The store does not fail the call; it falls back to BM25 and logs the reason. - Frontmatter will not parse — older learnings may use PascalCase
source:values (BashHookinstead ofbash_hook). Theparse_learning_sourcehelper accepts both, but a hand-edited file using an unknown source string will be silently downgraded toAutoExtract.