Skip to main content

Journeyman

Turn any agent into a self-learning system — tracing, graph memory, evals, and an eval-gated learning loop. Two lines of integration code.

pip install journeyman-agents        (local dev: pip install -e ".[dev]")
import journeyman as jm
jm.init()                                     # auto-instruments openai / anthropic

@jm.agent                                     # <- your agent. Any Python callable.
def my_agent(question: str, learned_context: str = "") -> str:
    # put learned_context into your prompt — that's the whole self-learning hook
    ...

my_agent("How do I reset my password?")       # traced
jm.feedback(1.0, "correct")                   # one-line learning signal
journeyman demo        # see the full flywheel on sample data (no API keys needed)
journeyman learn       # run one eval-gated learning cycle
journeyman up          # local API server (HTTP mode for other processes/languages)

What it does

Layer What you get Code required
Tracing Every run: inputs, outputs, tool calls, LLM calls, tokens, cost, latency, errors jm.init() + @jm.agent
Feedback Explicit signal bound to traces jm.feedback(score, comment)
Graph memory A knowledge graph built automatically from your agent's experience + your docs none
Insights Lessons distilled from failures/successes, injected back into your agent a learned_context: str = "" parameter
Evals Suites in YAML/DB, 7 scorer types incl. LLM-as-judge, full history a YAML file
The gate Learned changes only stay if the eval pass-rate holds — otherwise auto-rollback mark a suite gate: true
API server HTTP trace ingest + read/manage APIs, for other processes and languages journeyman up

The flywheel

     your agent runs                    users react
   ┌───────────────────┐            ┌─────────────────┐
   │  @jm.agent traces │──────────► │  jm.feedback()  │
   └───────────────────┘            └────────┬────────┘
             ▲                               │
             │                               ▼
   ┌─────────┴─────────┐            ┌─────────────────┐
   │ learned_context   │            │  learning cycle │  journeyman learn
   │ (insights + graph │ ◄──────────│  reflect→dedup→ │
   │  + few-shots)     │   applied  │  EVAL GATE      │──► rolled back if
   └───────────────────┘   only if  └─────────────────┘    pass-rate drops
                           evals hold

The learning cycle: collects new traces → folds them into the knowledge graph → reflects on traces that carry signal (bad feedback, corrections, errors) → extracts candidate insights → dedups/merges them against what's already known (delta updates only — never wholesale rewrites) → runs your gate suite before and after activating the batch → rolls back automatically on regression. Well-rated production answers are also mined into suggested eval cases you can accept into a suite with one API call.

Works with zero config — scales with config

  • No API key? Everything runs offline: deterministic extraction + hash embeddings. Add OPENAI_API_KEY / ANTHROPIC_API_KEY and the same pipeline upgrades itself to LLM extraction, LLM reflection, LLM-as-judge, and real embeddings. Your agent's own LLM stack is untouched either way.
  • No server? The SDK writes to an embedded SQLite db (.journeyman/). journeyman up reads the same file. Set JOURNEYMAN_SERVER=http://host:port to ship traces over HTTP instead (works from other processes/languages — POST /api/traces).
  • No config file? Fine. journeyman init writes an optional journeyman.yaml:
project: my-bot
entrypoint: app.main:my_agent      # lets the CLI/server run your agent for evals
learning:
  gate_suite: smoke                # the eval gate
  regression_threshold: 0.05
  auto_minutes: 0                  # >0: server runs learning cycles on a timer

Eval suites

# suite.yaml  ->  journeyman eval --load suite.yaml
name: smoke
gate: true
cases:
  - input: "How do I reset my password?"
    expect_contains: "portal.acme.com"
  - input: "VPN isn't connecting on my Mac"
    expect_contains: "6.2"
  - input: "Summarize our refund policy"
    judge: "Answer must state the 30-day window and the store-credit exception."

Checks: expect_exact, expect_contains, expect_contains_any, expect_not_contains, expect_regex, judge (binary LLM-as-judge), and scorer: module:fn for custom Python.

Safety guarantees of the SDK

  • Never raises into your code; internal failures degrade to no-ops (one warning).
  • Never blocks your hot path — persistence happens on a background thread with a bounded queue (drops, never blocks). jm.flush() for scripts/serverless.
  • KeyboardInterrupt/SystemExit always propagate; your exceptions are recorded and re-raised unchanged.
  • JOURNEYMAN_DISABLED=1 is a true kill switch.
  • jm.status() tells you exactly what's running and where data goes.

Other languages

The learning loop runs in the server, so other languages need only a thin client. Official SDKs, same safety contract as Python (never throw, never block, kill switch):

  • TypeScript / JavaScriptsdks/typescript (Node 18+, zero dependencies)
  • Gosdks/go (standard library only)
import * as jm from "journeyman-agents";
const myAgent = jm.agent(async (q, learnedContext) => { /* your agent */ });
await myAgent("How do I reset my password?");
jm.feedback(0.0, "wrong — the portal moved");

Anything else can speak the HTTP API directly: POST /api/traces, POST /api/feedback, GET /api/recall?q=... against journeyman up.

Python API

jm.init(project=..., autolog=True, server=None, db=None)   # all optional
@jm.agent / @jm.tool / @jm.step        # decorators (bare or with args; sync or async)
jm.wrap(fn)                            # functional form
with jm.trace("name", input=...):      # for code you can't decorate
jm.feedback(score, comment, trace_id=None)
jm.recall("query")                     # the learned-context block, on demand
jm.evaluate(fn=None, suite=None)       # run a suite from Python
jm.learn()                             # run a learning cycle from Python
jm.record_llm(model, input, output, tokens_in, tokens_out)  # custom LLM clients
jm.flush(); jm.status()

How much better does it get?

A working retrieval agent — it reads documents, extracts answers, picks options — answers four public benchmarks. It gets most of them wrong, which is the point: that's the starting line. Users correct its mistakes on the original wording, one learning cycle runs, and then it faces the same questions reworded, so it can't have memorised anything.

Benchmark Scored by Before After
SQuAD (40-document corpus) F1 18% 78%
HotpotQA (two-hop, 8 decoys per question) F1 8% 100%
MMLU (college & professional exams) exact match 18% 75%
ARC-Challenge (science reasoning) exact match 20% 88%
Average — 200 questions 16% 85%

16% → 85%, a 5.4× improvement, from 174 corrections and one learning cycle that takes 12 seconds. Both columns are the same agent on the same reworded questions; the only difference is whether it has the lessons. Reproduce: python bench/improve.py.

What happens when the feedback is wrong?

This is the question every "learns from your corrections" tool skips, and it's the one that costs money. Take an agent that already works, hand it 10 confidently wrong corrections per benchmark, run a learning cycle, and measure how much of it still answers correctly:

Answers still correct after 70 wrong corrections Keyword log Journeyman
Across all questions 74% 97%
On the questions the bad feedback targeted 0% 91%
On untouched questions 99% 99%

Every batch is tested against your eval suite before it goes live and kept only if the pass rate holds. A lookup table has no equivalent — it applies whatever it's told, immediately. Reproduce: python bench/robustness.py.

Weakest benchmark: MBPP at 82%. The gate is exactly as good as the evals you give it, and 20 cases didn't cover enough of the poisoned tasks. Full detail in docs/BENCHMARKS.md.

Do the corrections stick?

Seven public benchmarks, one test each: the agent answers with no memory, every miss gets one correction on the original wording, one learning cycle runs, then it faces 40 rewordings it has never seen. Asking the same question again would only prove it memorized the test.

Three systems get the identical corrections: nothing, a keyword log (what you'd build yourself — store every correction, look it up by word overlap), and Journeyman.

Benchmark No memory Keyword log Journeyman
TriviaQA 0% 98% 98%
Natural Questions 0% 95% 88%
SQuAD 0% 92% 88%
MMLU 0% 92% 90%
ARC-Challenge 0% 98% 98%
GSM8K 0% 100% 98%
MBPP (code, run against its real unit tests) 0% 75% 68%
Average — 280 questions 0% 93% 89%

The keyword log wins on raw recall, and we publish that. Looking up a correction you already have is hard to beat with plain word matching. What it can't do: it applies every correction with no gate, so one bad answer poisons the rest; it never deduplicates; and asked something nobody taught it, it returns a confident wrong answer 100% of the time (Journeyman: 69%). These are also Journeyman's floor numbers — no API key, hash embeddings.

Reproduce it in about a minute with no API keys: python bench/suite.py. Method, the rewordings, what we changed after seeing these results and what we refused to change: docs/BENCHMARKS.md.

Development

python -m venv .venv
.venv/Scripts/python -m pip install -e ".[dev]"
.venv/Scripts/python -m pytest tests

Docs: docs/DESIGN.md (architecture), docs/BENCHMARKS.md (what's measured and how), docs/RESEARCH.md (the research this design is grounded in), CONTRIBUTING.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

journeyman_agents-0.1.0.tar.gz (1.0 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

journeyman_agents-0.1.0-py3-none-any.whl (54.4 kB view details)

Uploaded Python 3

File details

Details for the file journeyman_agents-0.1.0.tar.gz.

File metadata

  • Download URL: journeyman_agents-0.1.0.tar.gz
  • Upload date:
  • Size: 1.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for journeyman_agents-0.1.0.tar.gz
Algorithm Hash digest
SHA256 22a0b03642a01cecad377d6bc46a923934e12cc70879c69415e3961fdc825bb0
MD5 7e30a850f8957f98263bc95561d35f05
BLAKE2b-256 a926e8670f1f074a9904c27a367ff178a7b8a3725f7f70f54649ef379c62cecf

See more details on using hashes here.

File details

Details for the file journeyman_agents-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for journeyman_agents-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 50abc93fde88313f7d2e007ee0247aad9e0a2b042058d35d30e33affb10bb2e3
MD5 3d937f969a83a8571fac7fc68dc60c8a
BLAKE2b-256 1a5095b960c8298a9430a24b4775aa4609c362162f692d3a8312c2ac18b83936

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page