agent-learning 0.4.2__tar.gz → 0.4.3__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_learning-0.4.2/src/agent_learning.egg-info → agent_learning-0.4.3}/PKG-INFO +5 -5
- agent_learning-0.4.3/PYPI.md +15 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/README.md +9 -9
- {agent_learning-0.4.2 → agent_learning-0.4.3}/pyproject.toml +1 -1
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/_version.py +1 -1
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/cli.py +33 -2
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/storage/base.py +3 -1
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/storage/cosmos.py +8 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/storage/local.py +4 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/storage/memory.py +4 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3/src/agent_learning.egg-info}/PKG-INFO +5 -5
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_cli.py +90 -1
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_storage_local.py +3 -0
- agent_learning-0.4.2/PYPI.md +0 -15
- {agent_learning-0.4.2 → agent_learning-0.4.3}/LICENSE +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/setup.cfg +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/capture.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/base.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/router.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/_base.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/adherence.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/completion.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/intent.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/config.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/learners/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/learners/base.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/learners/reinforce.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/base.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/intent_resolution.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/local.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/registry.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/task_adherence.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/task_completion.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/policy/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/policy/base.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/policy/contextual_softmax.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/policy/softmax_bandit.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/py.typed +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/rewards/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/rewards/shaping.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/rewards/writer.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/base.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/llm/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/llm/_base.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/llm/adherence.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/llm/completion.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/llm/intent.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp/_base.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp/adherence.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp/completion.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp/intent.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp_text/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp_text/_base.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp_text/adherence.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp_text/completion.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp_text/intent.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/slm/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/slm/_base.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/slm/adherence.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/slm/completion.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/slm/intent.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/stdlib/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/stdlib/_text.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/stdlib/adherence.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/stdlib/completion.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/stdlib/intent.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/storage/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/training/__init__.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/training/runner.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/types.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning.egg-info/SOURCES.txt +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning.egg-info/dependency_links.txt +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning.egg-info/entry_points.txt +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning.egg-info/requires.txt +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning.egg-info/top_level.txt +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_capture.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_contextual_policy.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_default_store.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_end_to_end.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_learner.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_metrics_local.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_policy.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_scorers.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_scorers_llm.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_scorers_nlp_text.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_scorers_slm.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_scorers_stdlib.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_shaping.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_storage_memory.py +0 -0
- {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-learning
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Native, in-process reinforcement learning SDK for AI agents — in-memory and local-file storage by default.
|
|
5
5
|
Author: Chris Tava
|
|
6
6
|
License: MIT License
|
|
@@ -67,16 +67,16 @@ Dynamic: license-file
|
|
|
67
67
|
|
|
68
68
|
# agent-learning
|
|
69
69
|
|
|
70
|
-
Native reinforcement learning SDK for AI agents. An in-process
|
|
70
|
+
Native reinforcement learning SDK for AI agents. An in-process Learner optimizes a small, interpretable TaskPolicy over discrete agent choices (understand intent and complete task by choosing the right outcome).
|
|
71
71
|
|
|
72
72
|
## How it works
|
|
73
73
|
|
|
74
74
|
The SDK improves agents without LLM weight fine-tuning. There are no GPU fine-tune jobs and no opaque update cycles — just three pieces that run in your existing Python process:
|
|
75
75
|
|
|
76
|
-
1.
|
|
76
|
+
1. **TaskPolicy** is a softmax distribution over `N` discrete actions (e.g., "take action A", "take action B", "take action C"). It lives in Python and updates in milliseconds.
|
|
77
77
|
|
|
78
|
-
2.
|
|
78
|
+
2. **Score** evaluates each episode on-device with three stdlib scorers for intent resolution, task adherence, and task completion. Their scores are combined into a single scalar reward with no scoring endpoint or environment variables required. Azure AI evaluators remain available as an opt-in.
|
|
79
79
|
|
|
80
|
-
3.
|
|
80
|
+
3. **Learner** applies REINFORCE-with-baseline to update TaskPolicy logits directly from logged episodes. Updates are tiny gradient steps that run on local compute and persist through a pluggable store — in-memory or local files by default, with Azure Cosmos DB optional.
|
|
81
81
|
|
|
82
82
|
Every episode, reward, run, and deployment is captured by the configured store — in-memory or local files by default, or Azure Cosmos DB — giving you a complete lineage and audit trail of how the policy evolved over time.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# agent-learning
|
|
2
|
+
|
|
3
|
+
Native reinforcement learning SDK for AI agents. An in-process Learner optimizes a small, interpretable TaskPolicy over discrete agent choices (understand intent and complete task by choosing the right outcome).
|
|
4
|
+
|
|
5
|
+
## How it works
|
|
6
|
+
|
|
7
|
+
The SDK improves agents without LLM weight fine-tuning. There are no GPU fine-tune jobs and no opaque update cycles — just three pieces that run in your existing Python process:
|
|
8
|
+
|
|
9
|
+
1. **TaskPolicy** is a softmax distribution over `N` discrete actions (e.g., "take action A", "take action B", "take action C"). It lives in Python and updates in milliseconds.
|
|
10
|
+
|
|
11
|
+
2. **Score** evaluates each episode on-device with three stdlib scorers for intent resolution, task adherence, and task completion. Their scores are combined into a single scalar reward with no scoring endpoint or environment variables required. Azure AI evaluators remain available as an opt-in.
|
|
12
|
+
|
|
13
|
+
3. **Learner** applies REINFORCE-with-baseline to update TaskPolicy logits directly from logged episodes. Updates are tiny gradient steps that run on local compute and persist through a pluggable store — in-memory or local files by default, with Azure Cosmos DB optional.
|
|
14
|
+
|
|
15
|
+
Every episode, reward, run, and deployment is captured by the configured store — in-memory or local files by default, or Azure Cosmos DB — giving you a complete lineage and audit trail of how the policy evolved over time.
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
# agent-learning
|
|
2
2
|
|
|
3
3
|
Native reinforcement learning SDK for AI agents. An in-process
|
|
4
|
-
|
|
4
|
+
Learner optimizes a small, interpretable TaskPolicy over discrete agent choices (e.g., "take action A", "take action B", "take action C") using on-device evaluation scores as the reward
|
|
5
5
|
signal by default.
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<img src="images/agent-learning-loop.svg" alt="Animated loop:
|
|
8
|
+
<img src="images/agent-learning-loop.svg" alt="Animated TaskPolicy Score Learner loop: TaskPolicy chooses a task action, Score evaluates the episode, and Learner updates TaskPolicy" width="960" style="max-width:100%; height:auto;" />
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
## How it works
|
|
12
12
|
|
|
13
13
|
The SDK improves agents without LLM weight fine-tuning. There are no GPU fine-tune jobs and no opaque update cycles — just three pieces that run in your existing Python process:
|
|
14
14
|
|
|
15
|
-
1.
|
|
15
|
+
1. **TaskPolicy** is a softmax distribution over `N` discrete
|
|
16
16
|
actions (e.g., "take action A", "take action B", "take action C"). It lives in Python and updates in milliseconds.
|
|
17
17
|
|
|
18
|
-
<img src="images/0f85e08d0c47cd01.png" alt="
|
|
18
|
+
<img src="images/0f85e08d0c47cd01.png" alt="TaskPolicy selects one of N discrete actions" width="360" style="max-width:100%; height:auto;" />
|
|
19
19
|
|
|
20
|
-
2.
|
|
20
|
+
2. **Score** evaluates each episode locally with three stdlib scorers for intent
|
|
21
21
|
resolution, task adherence, and task completion. Their scores are combined
|
|
22
22
|
into one scalar reward. No scoring endpoint or environment variable is
|
|
23
23
|
required. Configured Azure AI evaluators remain available as an opt-in.
|
|
24
24
|
|
|
25
25
|
<img src="images/246d112f995b785a.png" alt="Three evaluator scores feed a single scalar reward" width="360" style="max-width:100%; height:auto;" />
|
|
26
26
|
|
|
27
|
-
3.
|
|
27
|
+
3. **Learner** applies REINFORCE-with-baseline to update TaskPolicy logits
|
|
28
28
|
directly from stored episodes. Updates are tiny gradient steps
|
|
29
29
|
that run on local compute and persist through a pluggable store — in-memory
|
|
30
30
|
or local files by default, with Azure Cosmos DB optional.
|
|
@@ -72,11 +72,11 @@ The `agent-learn` CLI provides the current task-learning-loop operations:
|
|
|
72
72
|
```text
|
|
73
73
|
agent-learn list
|
|
74
74
|
agent-learn tasks-list <agent_id>
|
|
75
|
-
agent-learn task-episodes-count <agent_id> [--task-id <task_id>]
|
|
76
|
-
agent-learn task-episodes-list <agent_id> [--task-id <task_id>] [--limit <1-500>] [--include-incomplete]
|
|
75
|
+
agent-learn task-episodes-count <agent_id> [--task-id <task_id>] [--start-date <date>] [--end-date <date>]
|
|
76
|
+
agent-learn task-episodes-list <agent_id> [--task-id <task_id>] [--limit <1-500>] [--include-incomplete] [--start-date <date>] [--end-date <date>]
|
|
77
77
|
agent-learn task-policy-init --agent-id <agent_id> --task-id <task_id> --actions ./actions.json
|
|
78
78
|
agent-learn task-episode-register --agent-id <agent_id> --task-id <task_id> --episode ./episode.json
|
|
79
79
|
agent-learn score --agent-id <agent_id> [--task-id <task_id>] [--limit <1-500>]
|
|
80
|
-
agent-learn train --agent-id <agent_id> [--task-id <task_id>] [--limit <1-500>] [--start-date <date>] [--end-date <date>] [--skip-scoring]
|
|
80
|
+
agent-learn train --agent-id <agent_id> [--task-id <task_id>] [--limit <1-500>] [--min-episodes <1-500>] [--start-date <date>] [--end-date <date>] [--skip-scoring]
|
|
81
81
|
agent-learn task-policy --agent-id <agent_id> --task-id <task_id>
|
|
82
82
|
```
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agent-learning"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.3"
|
|
8
8
|
description = "Native, in-process reinforcement learning SDK for AI agents — in-memory and local-file storage by default."
|
|
9
9
|
readme = "PYPI.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -8,6 +8,7 @@ import logging
|
|
|
8
8
|
import sys
|
|
9
9
|
import uuid
|
|
10
10
|
from collections import Counter
|
|
11
|
+
from datetime import datetime, timezone
|
|
11
12
|
from typing import Any
|
|
12
13
|
|
|
13
14
|
from .policy.softmax_bandit import SoftmaxPolicy
|
|
@@ -27,6 +28,16 @@ def _episode_limit(value: str) -> int:
|
|
|
27
28
|
return limit
|
|
28
29
|
|
|
29
30
|
|
|
31
|
+
def _iso_date(value: str) -> str:
|
|
32
|
+
try:
|
|
33
|
+
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
34
|
+
except ValueError as exc:
|
|
35
|
+
raise argparse.ArgumentTypeError(f"invalid ISO 8601 date: {value!r}") from exc
|
|
36
|
+
if parsed.tzinfo is None:
|
|
37
|
+
parsed = parsed.replace(tzinfo=timezone.utc)
|
|
38
|
+
return parsed.astimezone(timezone.utc).isoformat()
|
|
39
|
+
|
|
40
|
+
|
|
30
41
|
def _build_arg_parser() -> argparse.ArgumentParser:
|
|
31
42
|
parser = argparse.ArgumentParser(prog="agent-learn", description="Native RL CLI for AI agents.")
|
|
32
43
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
@@ -42,6 +53,8 @@ def _build_arg_parser() -> argparse.ArgumentParser:
|
|
|
42
53
|
)
|
|
43
54
|
count.add_argument("agent_id")
|
|
44
55
|
count.add_argument("--task-id")
|
|
56
|
+
count.add_argument("--start-date", type=_iso_date)
|
|
57
|
+
count.add_argument("--end-date", type=_iso_date)
|
|
45
58
|
|
|
46
59
|
episodes = sub.add_parser(
|
|
47
60
|
"task-episodes-list",
|
|
@@ -51,13 +64,16 @@ def _build_arg_parser() -> argparse.ArgumentParser:
|
|
|
51
64
|
episodes.add_argument("--task-id")
|
|
52
65
|
episodes.add_argument("--limit", type=_episode_limit, default=_MAX_EPISODES)
|
|
53
66
|
episodes.add_argument("--include-incomplete", action="store_true")
|
|
67
|
+
episodes.add_argument("--start-date", type=_iso_date)
|
|
68
|
+
episodes.add_argument("--end-date", type=_iso_date)
|
|
54
69
|
|
|
55
70
|
train = sub.add_parser("train", help="Run one offline learning batch.")
|
|
56
71
|
train.add_argument("--agent-id", required=True)
|
|
57
72
|
train.add_argument("--task-id")
|
|
58
73
|
train.add_argument("--limit", type=_episode_limit, default=200)
|
|
59
|
-
train.add_argument("--
|
|
60
|
-
train.add_argument("--
|
|
74
|
+
train.add_argument("--min-episodes", type=_episode_limit, default=1)
|
|
75
|
+
train.add_argument("--start-date", type=_iso_date)
|
|
76
|
+
train.add_argument("--end-date", type=_iso_date)
|
|
61
77
|
train.add_argument(
|
|
62
78
|
"--skip-scoring",
|
|
63
79
|
action="store_true",
|
|
@@ -118,6 +134,8 @@ def _cmd_agents_episodes_count(args: argparse.Namespace) -> int:
|
|
|
118
134
|
args.agent_id,
|
|
119
135
|
task_id=args.task_id,
|
|
120
136
|
full_only=True,
|
|
137
|
+
start_date=args.start_date,
|
|
138
|
+
end_date=args.end_date,
|
|
121
139
|
)
|
|
122
140
|
print(count)
|
|
123
141
|
return 0
|
|
@@ -129,6 +147,8 @@ def _cmd_agents_episodes_list(args: argparse.Namespace) -> int:
|
|
|
129
147
|
args.agent_id,
|
|
130
148
|
task_id=args.task_id,
|
|
131
149
|
limit=_MAX_EPISODES,
|
|
150
|
+
start_date=args.start_date,
|
|
151
|
+
end_date=args.end_date,
|
|
132
152
|
)
|
|
133
153
|
if not args.include_incomplete:
|
|
134
154
|
episodes = [episode for episode in episodes if episode.is_full]
|
|
@@ -183,6 +203,17 @@ def _cmd_train(args: argparse.Namespace) -> int:
|
|
|
183
203
|
if episode_limit == 0:
|
|
184
204
|
skipped.append({"task_id": task_id, "reason": "no episodes in selected batch"})
|
|
185
205
|
continue
|
|
206
|
+
if episode_limit < args.min_episodes:
|
|
207
|
+
skipped.append(
|
|
208
|
+
{
|
|
209
|
+
"task_id": task_id,
|
|
210
|
+
"reason": (
|
|
211
|
+
f"selected batch has {episode_limit} episodes; "
|
|
212
|
+
f"minimum is {args.min_episodes}"
|
|
213
|
+
),
|
|
214
|
+
}
|
|
215
|
+
)
|
|
216
|
+
continue
|
|
186
217
|
policy = SoftmaxPolicy.from_snapshot(snapshot)
|
|
187
218
|
runner = LearningRunner(store=store, policy=policy)
|
|
188
219
|
run = runner.run_offline_batch(
|
|
@@ -66,8 +66,10 @@ class LearningStore(ABC):
|
|
|
66
66
|
*,
|
|
67
67
|
task_id: Optional[str] = None,
|
|
68
68
|
full_only: bool = False,
|
|
69
|
+
start_date: Optional[str] = None,
|
|
70
|
+
end_date: Optional[str] = None,
|
|
69
71
|
) -> int:
|
|
70
|
-
"""Count episodes
|
|
72
|
+
"""Count episodes filtered by task, completeness, or time window."""
|
|
71
73
|
|
|
72
74
|
# ---- Metric results -------------------------------------------
|
|
73
75
|
|
|
@@ -272,6 +272,8 @@ class CosmosStore(LearningStore):
|
|
|
272
272
|
*,
|
|
273
273
|
task_id: Optional[str] = None,
|
|
274
274
|
full_only: bool = False,
|
|
275
|
+
start_date: Optional[str] = None,
|
|
276
|
+
end_date: Optional[str] = None,
|
|
275
277
|
) -> int:
|
|
276
278
|
clauses = ["c.agent_id = @agent_id"]
|
|
277
279
|
params: List[Dict[str, Any]] = [{"name": "@agent_id", "value": agent_id}]
|
|
@@ -281,6 +283,12 @@ class CosmosStore(LearningStore):
|
|
|
281
283
|
else:
|
|
282
284
|
clauses.append("c.task_id = @task_id")
|
|
283
285
|
params.append({"name": "@task_id", "value": task_id})
|
|
286
|
+
if start_date:
|
|
287
|
+
clauses.append("c.created_at >= @start_date")
|
|
288
|
+
params.append({"name": "@start_date", "value": start_date})
|
|
289
|
+
if end_date:
|
|
290
|
+
clauses.append("c.created_at <= @end_date")
|
|
291
|
+
params.append({"name": "@end_date", "value": end_date})
|
|
284
292
|
if full_only:
|
|
285
293
|
clauses.extend(
|
|
286
294
|
[
|
|
@@ -212,12 +212,16 @@ class LocalFileStore(LearningStore):
|
|
|
212
212
|
*,
|
|
213
213
|
task_id: Optional[str] = None,
|
|
214
214
|
full_only: bool = False,
|
|
215
|
+
start_date: Optional[str] = None,
|
|
216
|
+
end_date: Optional[str] = None,
|
|
215
217
|
) -> int:
|
|
216
218
|
return sum(
|
|
217
219
|
1
|
|
218
220
|
for doc in self._read_dir_docs("episodes", agent_id)
|
|
219
221
|
if (task_id is None or doc.get("task_id", "default") == task_id)
|
|
220
222
|
and (not full_only or Episode.from_dict(doc).is_full)
|
|
223
|
+
and (start_date is None or doc.get("created_at", "") >= start_date)
|
|
224
|
+
and (end_date is None or doc.get("created_at", "") <= end_date)
|
|
221
225
|
)
|
|
222
226
|
|
|
223
227
|
# ------------------------------------------------------------------
|
|
@@ -93,6 +93,8 @@ class InMemoryStore(LearningStore):
|
|
|
93
93
|
*,
|
|
94
94
|
task_id: Optional[str] = None,
|
|
95
95
|
full_only: bool = False,
|
|
96
|
+
start_date: Optional[str] = None,
|
|
97
|
+
end_date: Optional[str] = None,
|
|
96
98
|
) -> int:
|
|
97
99
|
return sum(
|
|
98
100
|
1
|
|
@@ -100,6 +102,8 @@ class InMemoryStore(LearningStore):
|
|
|
100
102
|
if episode.agent_id == agent_id
|
|
101
103
|
and (task_id is None or episode.task_id == task_id)
|
|
102
104
|
and (not full_only or episode.is_full)
|
|
105
|
+
and (start_date is None or episode.created_at >= start_date)
|
|
106
|
+
and (end_date is None or episode.created_at <= end_date)
|
|
103
107
|
)
|
|
104
108
|
|
|
105
109
|
# ---- Metric results -------------------------------------------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-learning
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Native, in-process reinforcement learning SDK for AI agents — in-memory and local-file storage by default.
|
|
5
5
|
Author: Chris Tava
|
|
6
6
|
License: MIT License
|
|
@@ -67,16 +67,16 @@ Dynamic: license-file
|
|
|
67
67
|
|
|
68
68
|
# agent-learning
|
|
69
69
|
|
|
70
|
-
Native reinforcement learning SDK for AI agents. An in-process
|
|
70
|
+
Native reinforcement learning SDK for AI agents. An in-process Learner optimizes a small, interpretable TaskPolicy over discrete agent choices (understand intent and complete task by choosing the right outcome).
|
|
71
71
|
|
|
72
72
|
## How it works
|
|
73
73
|
|
|
74
74
|
The SDK improves agents without LLM weight fine-tuning. There are no GPU fine-tune jobs and no opaque update cycles — just three pieces that run in your existing Python process:
|
|
75
75
|
|
|
76
|
-
1.
|
|
76
|
+
1. **TaskPolicy** is a softmax distribution over `N` discrete actions (e.g., "take action A", "take action B", "take action C"). It lives in Python and updates in milliseconds.
|
|
77
77
|
|
|
78
|
-
2.
|
|
78
|
+
2. **Score** evaluates each episode on-device with three stdlib scorers for intent resolution, task adherence, and task completion. Their scores are combined into a single scalar reward with no scoring endpoint or environment variables required. Azure AI evaluators remain available as an opt-in.
|
|
79
79
|
|
|
80
|
-
3.
|
|
80
|
+
3. **Learner** applies REINFORCE-with-baseline to update TaskPolicy logits directly from logged episodes. Updates are tiny gradient steps that run on local compute and persist through a pluggable store — in-memory or local files by default, with Azure Cosmos DB optional.
|
|
81
81
|
|
|
82
82
|
Every episode, reward, run, and deployment is captured by the configured store — in-memory or local files by default, or Azure Cosmos DB — giving you a complete lineage and audit trail of how the policy evolved over time.
|
|
@@ -54,6 +54,48 @@ def test_discovery_and_full_episode_count(monkeypatch, capsys) -> None:
|
|
|
54
54
|
assert capsys.readouterr().out.strip() == "1"
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
def test_episode_count_and_list_use_the_training_date_window(
|
|
58
|
+
monkeypatch, capsys
|
|
59
|
+
) -> None:
|
|
60
|
+
store = InMemoryStore()
|
|
61
|
+
monkeypatch.setattr(cli, "get_default_store", lambda: store)
|
|
62
|
+
for index, created_at in enumerate(
|
|
63
|
+
(
|
|
64
|
+
"2026-08-09T05:40:00+00:00",
|
|
65
|
+
"2026-08-09T06:07:25+00:00",
|
|
66
|
+
"2026-08-09T06:12:23+00:00",
|
|
67
|
+
"2026-08-09T06:13:10+00:00",
|
|
68
|
+
"2026-08-09T06:13:55+00:00",
|
|
69
|
+
"2026-08-09T06:14:37+00:00",
|
|
70
|
+
"2026-08-09T06:20:00+00:00",
|
|
71
|
+
)
|
|
72
|
+
):
|
|
73
|
+
episode = _full_episode()
|
|
74
|
+
episode.id = f"episode-{index}"
|
|
75
|
+
episode.created_at = created_at
|
|
76
|
+
store.store_episode(episode)
|
|
77
|
+
|
|
78
|
+
window = [
|
|
79
|
+
"--task-id",
|
|
80
|
+
"chat",
|
|
81
|
+
"--start-date",
|
|
82
|
+
"2026-08-09T05:42:45.258Z",
|
|
83
|
+
"--end-date",
|
|
84
|
+
"2026-08-09T06:16:07.333Z",
|
|
85
|
+
]
|
|
86
|
+
assert cli.main(["task-episodes-count", "agent-1", *window]) == 0
|
|
87
|
+
assert capsys.readouterr().out.strip() == "5"
|
|
88
|
+
assert cli.main(["task-episodes-list", "agent-1", *window]) == 0
|
|
89
|
+
listed = json.loads(capsys.readouterr().out)
|
|
90
|
+
assert len(listed) == 5
|
|
91
|
+
assert all(
|
|
92
|
+
"2026-08-09T05:42:45.258+00:00"
|
|
93
|
+
<= item["episode"]["created_at"]
|
|
94
|
+
<= "2026-08-09T06:16:07.333+00:00"
|
|
95
|
+
for item in listed
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
57
99
|
def test_episode_inspection_includes_scores_and_final_reward(monkeypatch, capsys) -> None:
|
|
58
100
|
store = InMemoryStore()
|
|
59
101
|
episode = _full_episode()
|
|
@@ -317,4 +359,51 @@ def test_agent_training_uses_one_limit_and_preserves_task_policy_history(
|
|
|
317
359
|
|
|
318
360
|
def test_episode_limit_is_capped_at_500() -> None:
|
|
319
361
|
with pytest.raises(SystemExit):
|
|
320
|
-
cli.main(["train", "--agent-id", "agent-1", "--limit", "501"])
|
|
362
|
+
cli.main(["train", "--agent-id", "agent-1", "--limit", "501"])
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def test_train_enforces_minimum_selected_episode_count(
|
|
366
|
+
monkeypatch, capsys
|
|
367
|
+
) -> None:
|
|
368
|
+
store = InMemoryStore()
|
|
369
|
+
monkeypatch.setattr(cli, "get_default_store", lambda: store)
|
|
370
|
+
policy = SoftmaxPolicy.from_actions(
|
|
371
|
+
[Action(id="respond")], agent_id="agent-1", task_id="chat"
|
|
372
|
+
)
|
|
373
|
+
store.store_policy(policy.snapshot())
|
|
374
|
+
for index in range(3):
|
|
375
|
+
episode = _full_episode()
|
|
376
|
+
episode.id = f"episode-{index}"
|
|
377
|
+
store.store_episode(episode)
|
|
378
|
+
store.store_reward(
|
|
379
|
+
Reward(
|
|
380
|
+
episode_id=episode.id,
|
|
381
|
+
agent_id=episode.agent_id,
|
|
382
|
+
source=RewardSource.AGGREGATE,
|
|
383
|
+
value=0.8,
|
|
384
|
+
)
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
result = cli.main(
|
|
388
|
+
[
|
|
389
|
+
"train",
|
|
390
|
+
"--agent-id",
|
|
391
|
+
"agent-1",
|
|
392
|
+
"--task-id",
|
|
393
|
+
"chat",
|
|
394
|
+
"--min-episodes",
|
|
395
|
+
"5",
|
|
396
|
+
"--skip-scoring",
|
|
397
|
+
]
|
|
398
|
+
)
|
|
399
|
+
output = json.loads(capsys.readouterr().out)
|
|
400
|
+
|
|
401
|
+
assert result == 2
|
|
402
|
+
assert output["runs"] == []
|
|
403
|
+
assert output["skipped"] == [
|
|
404
|
+
{
|
|
405
|
+
"task_id": "chat",
|
|
406
|
+
"reason": "selected batch has 3 episodes; minimum is 5",
|
|
407
|
+
}
|
|
408
|
+
]
|
|
409
|
+
assert store.get_active_policy("agent-1", "chat").version == 0
|
|
@@ -135,6 +135,9 @@ def test_query_filters_and_limit(tmp_path: Path) -> None:
|
|
|
135
135
|
# Filter by window
|
|
136
136
|
windowed = store.query_episodes("dq", start_date="2024-05-01", end_date="2024-07-01")
|
|
137
137
|
assert len(windowed) == 1
|
|
138
|
+
assert store.count_episodes(
|
|
139
|
+
"dq", start_date="2024-05-01", end_date="2024-07-01"
|
|
140
|
+
) == 1
|
|
138
141
|
# Limit
|
|
139
142
|
assert len(store.query_episodes("dq", limit=1)) == 1
|
|
140
143
|
|
agent_learning-0.4.2/PYPI.md
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# agent-learning
|
|
2
|
-
|
|
3
|
-
Native reinforcement learning SDK for AI agents. An in-process learner optimizes a small, interpretable policy over discrete agent choices (understand intent and complete task by choosing the right outcome).
|
|
4
|
-
|
|
5
|
-
## How it works
|
|
6
|
-
|
|
7
|
-
The SDK improves agents without LLM weight fine-tuning. There are no GPU fine-tune jobs and no opaque update cycles — just three pieces that run in your existing Python process:
|
|
8
|
-
|
|
9
|
-
1. The **policy** is a softmax distribution over `N` discrete actions (e.g., "take action A", "take action B", "take action C"). It lives in Python and updates in milliseconds.
|
|
10
|
-
|
|
11
|
-
2. Each episode is **evaluated** on-device by three stdlib scorers for intent resolution, task adherence, and task completion. Their scores are combined into a single scalar reward with no scoring endpoint or environment variables required. Azure AI evaluators remain available as an opt-in.
|
|
12
|
-
|
|
13
|
-
3. A **REINFORCE-with-baseline** learner updates the policy logits directly from logged episodes. Updates are tiny gradient steps that run on local compute and persist through a pluggable store — in-memory or local files by default, with Azure Cosmos DB optional.
|
|
14
|
-
|
|
15
|
-
Every episode, reward, run, and deployment is captured by the configured store — in-memory or local files by default, or Azure Cosmos DB — giving you a complete lineage and audit trail of how the policy evolved over time.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/__init__.py
RENAMED
|
File without changes
|
{agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/_base.py
RENAMED
|
File without changes
|
{agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/adherence.py
RENAMED
|
File without changes
|
{agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/completion.py
RENAMED
|
File without changes
|
{agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/intent.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/intent_resolution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/policy/contextual_softmax.py
RENAMED
|
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
|
{agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp_text/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp_text/adherence.py
RENAMED
|
File without changes
|
{agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp_text/completion.py
RENAMED
|
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
|
{agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/stdlib/adherence.py
RENAMED
|
File without changes
|
{agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/stdlib/completion.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning.egg-info/dependency_links.txt
RENAMED
|
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
|