pyagent-context 0.1.0__tar.gz → 0.2.4__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.
- pyagent_context-0.2.4/PKG-INFO +518 -0
- pyagent_context-0.2.4/README.md +484 -0
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/pyproject.toml +5 -2
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/src/pyagent_context/compression.py +3 -7
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/src/pyagent_context/item.py +10 -5
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/src/pyagent_context/ledger.py +6 -6
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/src/pyagent_context/lifecycle.py +5 -2
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/src/pyagent_context/memory/__init__.py +2 -2
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/src/pyagent_context/memory/semantic.py +106 -19
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/src/pyagent_context/memory/session.py +0 -1
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/src/pyagent_context/memory/working.py +3 -1
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/src/pyagent_context/redaction.py +1 -1
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/src/pyagent_context/retrieval.py +8 -7
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/tests/test_compression.py +37 -27
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/tests/test_ledger.py +4 -2
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/tests/test_lifecycle.py +36 -26
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/tests/test_memory.py +10 -6
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/tests/test_retrieval.py +23 -17
- pyagent_context-0.1.0/PKG-INFO +0 -286
- pyagent_context-0.1.0/README.md +0 -255
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/.gitignore +0 -0
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/src/pyagent_context/__init__.py +0 -0
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/src/pyagent_context/py.typed +0 -0
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/tests/__init__.py +0 -0
- {pyagent_context-0.1.0 → pyagent_context-0.2.4}/tests/test_item.py +0 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyagent-context
|
|
3
|
+
Version: 0.2.4
|
|
4
|
+
Summary: Three-tier memory with trust-aware context ledger for multi-agent LLM systems
|
|
5
|
+
Project-URL: Homepage, https://pyagent.org
|
|
6
|
+
Project-URL: Repository, https://github.com/pyagent-core/pyagent
|
|
7
|
+
Project-URL: Documentation, https://pyagent.org
|
|
8
|
+
Project-URL: Agent Orchestration Patterns, https://pyagent.org/patterns/orchestrator-worker/
|
|
9
|
+
Project-URL: Agent Harness Architecture, https://pyagent.org/architecture/multi-agent-harness/
|
|
10
|
+
Project-URL: Agent Experience Optimization, https://pyagent.org/concepts/agent-experience-optimization/
|
|
11
|
+
Author-email: PyAgent Team <team@pyagent.org>
|
|
12
|
+
License: MIT
|
|
13
|
+
Keywords: AXO,LLM,agent memory,agents,context,memory,retrieval,stateful multi-actor workflows,trust
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: pyagent-patterns>=0.1.0
|
|
24
|
+
Provides-Extra: chromadb
|
|
25
|
+
Requires-Dist: chromadb>=0.5; extra == 'chromadb'
|
|
26
|
+
Provides-Extra: compress
|
|
27
|
+
Requires-Dist: pyagent-compress>=0.1.0; extra == 'compress'
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# pyagent-context
|
|
36
|
+
|
|
37
|
+
**Pillar 3 of the PyAgent production stack for multi-agent LLM systems** — three-tier memory with trust-aware context ledger. Trust levels, sensitivity classification, compression policies, and PII redaction built in.
|
|
38
|
+
|
|
39
|
+
[](../../LICENSE)
|
|
40
|
+
[](https://www.python.org/downloads/)
|
|
41
|
+
|
|
42
|
+
> **Architecture Pillar: 🧠 Context & Memory**
|
|
43
|
+
> The sole Context & Memory pillar package — a structured `ContextLedger` with three-tier memory (working, session, semantic), trust and sensitivity metadata, compression, and PII redaction for agents that need to remember across turns.
|
|
44
|
+
> Part of the full stack: install `pyagent-all` for all pillars.
|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install pyagent-context # Core (working + session + semantic memory)
|
|
50
|
+
pip install pyagent-context[compress] # + ContextCompressor with pyagent-compress
|
|
51
|
+
pip install pyagent-context[chromadb] # + ChromaDB semantic memory backend
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Depends on: `pyagent-patterns`.
|
|
55
|
+
|
|
56
|
+
## Why Structured Context?
|
|
57
|
+
|
|
58
|
+
Without `pyagent-context`, multi-agent workflows pass a flat `list[Message]` that grows unbounded. You lose track of *who* said *what*, *when*, and *how much to trust it*. This package adds trust levels, sensitivity tiers, expiry, compression, and three memory tiers — so your agents always work with the right context.
|
|
59
|
+
|
|
60
|
+
## ContextItem — Everything Has Metadata
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import time
|
|
64
|
+
from pyagent_context import ContextItem, TrustLevel, Sensitivity
|
|
65
|
+
|
|
66
|
+
item = ContextItem(
|
|
67
|
+
content="Revenue grew 15% YoY to $25.2B",
|
|
68
|
+
source="analyst",
|
|
69
|
+
trust_level=TrustLevel.VERIFIED, # verified | inferred | user | external
|
|
70
|
+
sensitivity=Sensitivity.INTERNAL, # public | internal | confidential | restricted
|
|
71
|
+
expires_at=time.time() + 3600, # auto-expire in 1 hour
|
|
72
|
+
derived_from="abc123", # parent item ID
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
print(item.id) # unique 12-char hex
|
|
76
|
+
print(item.token_estimate) # auto-calculated: len(content) // 4
|
|
77
|
+
print(item.is_expired) # False (still within TTL)
|
|
78
|
+
print(item.age_seconds) # seconds since creation
|
|
79
|
+
|
|
80
|
+
# Serialize / deserialize
|
|
81
|
+
data = item.to_dict()
|
|
82
|
+
restored = ContextItem.from_dict(data)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## ContextLedger — Append-Only Context Log
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from pyagent_context import ContextLedger, TrustLevel
|
|
89
|
+
|
|
90
|
+
ledger = ContextLedger()
|
|
91
|
+
|
|
92
|
+
# Add items
|
|
93
|
+
ledger.add("User asked about Q3 earnings", "user", TrustLevel.USER_PROVIDED)
|
|
94
|
+
ledger.add("Revenue: $25.2B (+8% YoY)", "analyst", TrustLevel.VERIFIED)
|
|
95
|
+
ledger.add("I think margins will improve", "forecaster", TrustLevel.INFERRED)
|
|
96
|
+
|
|
97
|
+
# Query by trust
|
|
98
|
+
verified = ledger.query(min_trust=TrustLevel.VERIFIED)
|
|
99
|
+
print(len(verified)) # 1
|
|
100
|
+
|
|
101
|
+
# Query by age (last 5 minutes)
|
|
102
|
+
recent = ledger.query(max_age_seconds=300)
|
|
103
|
+
|
|
104
|
+
# Query by source
|
|
105
|
+
from_analyst = ledger.query(source="analyst")
|
|
106
|
+
|
|
107
|
+
# Convert to Messages for pattern consumption
|
|
108
|
+
messages = ledger.to_messages() # all items
|
|
109
|
+
messages = ledger.to_messages(max_tokens=500) # budget-constrained (most recent first)
|
|
110
|
+
|
|
111
|
+
# Snapshot for persistence
|
|
112
|
+
snap = ledger.snapshot() # JSON-serializable dict
|
|
113
|
+
restored = ContextLedger.from_snapshot(snap)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Three-Tier Memory
|
|
117
|
+
|
|
118
|
+
### WorkingMemory — Bounded In-Flight Context
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from pyagent_context import WorkingMemory, ContextItem
|
|
122
|
+
|
|
123
|
+
wm = WorkingMemory(max_items=50, max_tokens=10_000)
|
|
124
|
+
|
|
125
|
+
item = ContextItem(content="New observation", source="agent_1")
|
|
126
|
+
evicted = wm.add(item) # returns list of evicted items if capacity exceeded
|
|
127
|
+
|
|
128
|
+
print(len(wm)) # current item count
|
|
129
|
+
print(wm.total_tokens) # current token usage
|
|
130
|
+
print(f"{wm.utilization:.0%}") # e.g. "42%"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### SessionMemory — Persist Across Turns
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from pyagent_context import SessionMemory, ContextItem
|
|
137
|
+
|
|
138
|
+
# JSON backend (simple, human-readable)
|
|
139
|
+
session = SessionMemory("user-123-session", backend="json", storage_path=".sessions")
|
|
140
|
+
session.add(ContextItem(content="User prefers concise answers", source="user"))
|
|
141
|
+
session.save()
|
|
142
|
+
|
|
143
|
+
# Later: reload
|
|
144
|
+
session2 = SessionMemory("user-123-session", backend="json", storage_path=".sessions")
|
|
145
|
+
session2.load()
|
|
146
|
+
items = session2.get_all()
|
|
147
|
+
|
|
148
|
+
# SQLite backend (concurrent-safe)
|
|
149
|
+
session = SessionMemory("user-123-session", backend="sqlite", storage_path=".sessions")
|
|
150
|
+
session.add(ContextItem(content="Important context", source="system"))
|
|
151
|
+
session.save()
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### SemanticMemory — Vector-Indexed Long-Term Store
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from pyagent_context import InMemorySemanticStore, ContextItem
|
|
158
|
+
|
|
159
|
+
store = InMemorySemanticStore()
|
|
160
|
+
|
|
161
|
+
# Index items
|
|
162
|
+
store.add(ContextItem(content="Python asyncio event loop concurrency patterns", source="docs"))
|
|
163
|
+
store.add(ContextItem(content="JavaScript React component lifecycle hooks", source="docs"))
|
|
164
|
+
store.add(ContextItem(content="Python FastAPI async web framework REST API design", source="docs"))
|
|
165
|
+
|
|
166
|
+
# Semantic search (TF-IDF cosine similarity)
|
|
167
|
+
results = store.search("Python async web", top_k=3)
|
|
168
|
+
for r in results:
|
|
169
|
+
print(f" [{r.score:.2f}] {r.item.content[:60]}...")
|
|
170
|
+
|
|
171
|
+
# Remove / clear
|
|
172
|
+
store.remove(item_id)
|
|
173
|
+
store.clear()
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## ContextCompressor — Manage Token Growth
|
|
177
|
+
|
|
178
|
+
Four policies: `NONE`, `FIFO`, `SEMANTIC_LOSSLESS`, `SAWTOOTH`.
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from pyagent_context import ContextCompressor, CompressionPolicy, ContextLedger
|
|
182
|
+
|
|
183
|
+
# FIFO: drop oldest items until under floor
|
|
184
|
+
compressor = ContextCompressor(
|
|
185
|
+
policy=CompressionPolicy.FIFO,
|
|
186
|
+
threshold_tokens=10_000, # trigger compression at 10k tokens
|
|
187
|
+
floor_tokens=5_000, # compress down to 5k
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if compressor.should_compress(ledger):
|
|
191
|
+
compressed = compressor.compress(ledger)
|
|
192
|
+
print(f"Compressed: {ledger.total_tokens} → {compressed.total_tokens} tokens")
|
|
193
|
+
|
|
194
|
+
# SAWTOOTH: compress to floor, then allow growth again
|
|
195
|
+
compressor = ContextCompressor(
|
|
196
|
+
policy=CompressionPolicy.SAWTOOTH,
|
|
197
|
+
threshold_tokens=10_000,
|
|
198
|
+
floor_tokens=3_000,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# SEMANTIC_LOSSLESS: compress text but preserve verified items unchanged
|
|
202
|
+
compressor = ContextCompressor(
|
|
203
|
+
policy=CompressionPolicy.SEMANTIC_LOSSLESS,
|
|
204
|
+
threshold_tokens=8_000,
|
|
205
|
+
floor_tokens=4_000,
|
|
206
|
+
)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## TrustAwareRetriever — Smart Context Selection
|
|
210
|
+
|
|
211
|
+
Scores items by `trust × recency × relevance`:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from pyagent_context import TrustAwareRetriever
|
|
215
|
+
|
|
216
|
+
retriever = TrustAwareRetriever(
|
|
217
|
+
weight_trust=0.3,
|
|
218
|
+
weight_recency=0.3,
|
|
219
|
+
weight_relevance=0.4,
|
|
220
|
+
recency_half_life=3600.0, # 1 hour half-life
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
results = retriever.retrieve(ledger, "Q3 earnings revenue growth", top_k=5)
|
|
224
|
+
for r in results:
|
|
225
|
+
print(f" [{r.score:.2f}] trust={r.trust_score:.2f} "
|
|
226
|
+
f"recency={r.recency_score:.2f} relevance={r.relevance_score:.2f}")
|
|
227
|
+
print(f" {r.item.content[:80]}...")
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## ContextLifecycle — Expiry, Decay, Consolidation
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
from pyagent_context import ContextLifecycle
|
|
234
|
+
|
|
235
|
+
lifecycle = ContextLifecycle(consolidation_threshold=0.6)
|
|
236
|
+
|
|
237
|
+
# Remove expired items
|
|
238
|
+
new_ledger, expired = lifecycle.sweep_expired(ledger)
|
|
239
|
+
print(f"Removed {len(expired)} expired items")
|
|
240
|
+
|
|
241
|
+
# Apply freshness decay (old items get smaller token budgets)
|
|
242
|
+
decayed = lifecycle.apply_freshness_decay(ledger, half_life_seconds=3600)
|
|
243
|
+
|
|
244
|
+
# Merge similar items from the same source
|
|
245
|
+
consolidated = lifecycle.consolidate(ledger)
|
|
246
|
+
print(f"Consolidated: {len(ledger)} → {len(consolidated)} items")
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## ContextRedactor — Sensitivity-Based Redaction
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from pyagent_context import ContextRedactor
|
|
253
|
+
from pyagent_context.item import Sensitivity
|
|
254
|
+
|
|
255
|
+
# Redact items above INTERNAL sensitivity
|
|
256
|
+
redactor = ContextRedactor(
|
|
257
|
+
max_sensitivity=Sensitivity.INTERNAL,
|
|
258
|
+
redaction_text="[REDACTED — CONFIDENTIAL]",
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
redacted_ledger = redactor.redact_ledger(ledger)
|
|
262
|
+
|
|
263
|
+
# Or exclude entirely instead of redacting
|
|
264
|
+
redactor = ContextRedactor(
|
|
265
|
+
max_sensitivity=Sensitivity.INTERNAL,
|
|
266
|
+
exclude_above=True,
|
|
267
|
+
)
|
|
268
|
+
filtered_ledger = redactor.redact_ledger(ledger)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Integration with pyagent-patterns
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
from pyagent_patterns.base import Agent, MockLLM
|
|
275
|
+
from pyagent_patterns.orchestration import Pipeline
|
|
276
|
+
from pyagent_context import ContextLedger, TrustLevel, WorkingMemory
|
|
277
|
+
|
|
278
|
+
ledger = ContextLedger()
|
|
279
|
+
|
|
280
|
+
# Before pattern run: seed with trusted context
|
|
281
|
+
ledger.add("User is asking about Q3 2025 earnings", "user", TrustLevel.USER_PROVIDED)
|
|
282
|
+
ledger.add("Tesla Q3 revenue was $25.2B", "database", TrustLevel.VERIFIED)
|
|
283
|
+
|
|
284
|
+
# Convert to messages and prepend to pattern input
|
|
285
|
+
context_messages = ledger.to_messages(max_tokens=2000)
|
|
286
|
+
|
|
287
|
+
# After pattern run: store results
|
|
288
|
+
ledger.add(result.output, "pipeline", TrustLevel.INFERRED)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Architecture — Three-Tier Memory Model
|
|
292
|
+
|
|
293
|
+
```mermaid
|
|
294
|
+
flowchart TD
|
|
295
|
+
subgraph Agent Interaction
|
|
296
|
+
A1[Agent 1] -->|append output| CL[ContextLedger]
|
|
297
|
+
CL -->|to_messages budget| A2[Agent 2 input]
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
subgraph Three-Tier Memory
|
|
301
|
+
CL --> WM[WorkingMemory — bounded deque, current turn]
|
|
302
|
+
CL --> SM[SessionMemory — JSON/SQLite, cross-turn persistence]
|
|
303
|
+
CL --> SEM[SemanticMemory — TF-IDF similarity, long-term recall]
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
subgraph Processing
|
|
307
|
+
CL --> CC[ContextCompressor — policy-based trimming]
|
|
308
|
+
CL --> TR[TrustAwareRetriever — ranked retrieval]
|
|
309
|
+
CL --> CR[ContextRedactor — sensitivity filtering]
|
|
310
|
+
CL --> LC[ContextLifecycle — expiry, decay, consolidation]
|
|
311
|
+
end
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Memory Tier Details
|
|
315
|
+
|
|
316
|
+
| Tier | Class | Storage | Capacity | Eviction | Use Case |
|
|
317
|
+
|------|-------|---------|----------|----------|----------|
|
|
318
|
+
| **Working** | `WorkingMemory` | In-memory deque | `max_items` or `max_tokens` | Oldest-first when full | Current conversation turn |
|
|
319
|
+
| **Session** | `SessionMemory` | JSON file or SQLite | Unlimited | Manual clear | Cross-turn persistence (multi-step workflows) |
|
|
320
|
+
| **Semantic** | `InMemorySemanticStore` | In-memory TF-IDF index | Unlimited | Manual | Long-term recall via similarity search |
|
|
321
|
+
|
|
322
|
+
### WorkingMemory Eviction
|
|
323
|
+
|
|
324
|
+
When `WorkingMemory` reaches its `max_items` or `max_tokens` limit, the oldest items are evicted first. Utilization metrics are available:
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
from pyagent_context import WorkingMemory
|
|
328
|
+
|
|
329
|
+
wm = WorkingMemory(max_items=20, max_tokens=8000)
|
|
330
|
+
wm.add(item)
|
|
331
|
+
|
|
332
|
+
print(wm.utilization) # 0.05 → 5% of max_items used
|
|
333
|
+
print(wm.token_usage) # current token estimate total
|
|
334
|
+
print(len(wm)) # number of items in memory
|
|
335
|
+
wm.clear() # flush all items
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### SessionMemory Backends
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
from pyagent_context import SessionMemory
|
|
342
|
+
|
|
343
|
+
# JSON backend — simple file-based persistence
|
|
344
|
+
sm_json = SessionMemory(backend="json", path="session.json")
|
|
345
|
+
|
|
346
|
+
# SQLite backend — more robust, concurrent-safe
|
|
347
|
+
sm_sqlite = SessionMemory(backend="sqlite", path="session.db")
|
|
348
|
+
|
|
349
|
+
sm_json.add(item)
|
|
350
|
+
sm_json.save() # persist to disk
|
|
351
|
+
sm_json.load() # reload from disk
|
|
352
|
+
items = sm_json.retrieve(query="billing", top_k=5)
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### SemanticMemory — TF-IDF Similarity Search
|
|
356
|
+
|
|
357
|
+
The `InMemorySemanticStore` uses TF-IDF vectorization for similarity-based retrieval across the full context history:
|
|
358
|
+
|
|
359
|
+
```python
|
|
360
|
+
from pyagent_context import InMemorySemanticStore
|
|
361
|
+
|
|
362
|
+
store = InMemorySemanticStore()
|
|
363
|
+
store.add(item1)
|
|
364
|
+
store.add(item2)
|
|
365
|
+
store.add(item3)
|
|
366
|
+
|
|
367
|
+
# Retrieve items most similar to the query
|
|
368
|
+
results = store.search("billing question", top_k=3)
|
|
369
|
+
for item, score in results:
|
|
370
|
+
print(f"[{score:.2f}] {item.content[:80]}...")
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## ContextLedger — Token-Budgeted Message Conversion
|
|
374
|
+
|
|
375
|
+
The `ContextLedger` is an append-only log that converts stored `ContextItem` objects into LLM-compatible messages with automatic token budgeting:
|
|
376
|
+
|
|
377
|
+
```python
|
|
378
|
+
from pyagent_context import ContextLedger, ContextItem, TrustLevel
|
|
379
|
+
|
|
380
|
+
ledger = ContextLedger()
|
|
381
|
+
ledger.append(ContextItem(content="Revenue was $25.2B", source="database", trust=TrustLevel.VERIFIED))
|
|
382
|
+
ledger.append(ContextItem(content="Margin expanded to 17%", source="agent_1", trust=TrustLevel.INFERRED))
|
|
383
|
+
|
|
384
|
+
# Convert to messages with a token budget
|
|
385
|
+
# Higher-trust items are prioritized when budget is tight
|
|
386
|
+
messages = ledger.to_messages(budget=4000)
|
|
387
|
+
print(ledger.total_tokens()) # total token estimate across all items
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
## TrustAwareRetriever — Composite Scoring
|
|
391
|
+
|
|
392
|
+
The retriever ranks items using a composite score of three signals:
|
|
393
|
+
|
|
394
|
+
| Signal | Weight | Description |
|
|
395
|
+
|--------|--------|-------------|
|
|
396
|
+
| **Trust** | Configurable | `VERIFIED` > `INFERRED` > `USER_PROVIDED` > `UNVERIFIED` |
|
|
397
|
+
| **Recency** | Half-life decay | Newer items score higher; decay rate is configurable |
|
|
398
|
+
| **Relevance** | Keyword overlap | TF-IDF similarity between query and item content |
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
from pyagent_context import TrustAwareRetriever
|
|
402
|
+
|
|
403
|
+
retriever = TrustAwareRetriever(
|
|
404
|
+
trust_weight=0.4,
|
|
405
|
+
recency_weight=0.3,
|
|
406
|
+
relevance_weight=0.3,
|
|
407
|
+
half_life_hours=24.0,
|
|
408
|
+
)
|
|
409
|
+
results = retriever.retrieve(items, query="billing issue", top_k=5)
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## Integration with pyagent-patterns
|
|
413
|
+
|
|
414
|
+
Context flows between agents via the `ContextLedger`:
|
|
415
|
+
|
|
416
|
+
```python
|
|
417
|
+
from pyagent_patterns.base import Agent, MockLLM
|
|
418
|
+
from pyagent_patterns.orchestration import Pipeline
|
|
419
|
+
from pyagent_context import ContextLedger, ContextItem, TrustLevel
|
|
420
|
+
|
|
421
|
+
ledger = ContextLedger()
|
|
422
|
+
|
|
423
|
+
# Before agent execution: read context to prepend as system/user messages
|
|
424
|
+
context_messages = ledger.to_messages(budget=4000)
|
|
425
|
+
|
|
426
|
+
# After agent execution: write output as a new context item
|
|
427
|
+
ledger.append(ContextItem(
|
|
428
|
+
content=result.output,
|
|
429
|
+
source="analyst",
|
|
430
|
+
trust=TrustLevel.INFERRED,
|
|
431
|
+
))
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
In the hook-based integration model, agents automatically read from and write to the ledger when one is attached via `set_context()`.
|
|
435
|
+
|
|
436
|
+
## Integration with pyagent-compress
|
|
437
|
+
|
|
438
|
+
Two levels of compression work together:
|
|
439
|
+
|
|
440
|
+
| Layer | Package | What It Compresses | How |
|
|
441
|
+
|-------|---------|-------------------|-----|
|
|
442
|
+
| **Message-level** | `pyagent-compress` | Individual agent outputs | Extractive: remove filler, rank sentences, keep top-N |
|
|
443
|
+
| **Context-level** | `pyagent-context` | Accumulated context items | Policy-based: FIFO, semantic lossless, sawtooth |
|
|
444
|
+
|
|
445
|
+
```python
|
|
446
|
+
from pyagent_context import ContextCompressor, ContextLedger
|
|
447
|
+
from pyagent_compress import MessageCompressor
|
|
448
|
+
|
|
449
|
+
# Context compression: decide which items to keep
|
|
450
|
+
compressor = ContextCompressor(policy="semantic_lossless")
|
|
451
|
+
trimmed = compressor.compress(ledger.items(), target_tokens=4000)
|
|
452
|
+
|
|
453
|
+
# Message compression: reduce verbosity of individual outputs
|
|
454
|
+
msg_compressor = MessageCompressor(target_ratio=0.5)
|
|
455
|
+
compressed = msg_compressor.compress(agent_output)
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Integration with pyagent-trace
|
|
459
|
+
|
|
460
|
+
Context operations can be tracked via the `TraceEventBus`:
|
|
461
|
+
|
|
462
|
+
- **Ledger writes** — When agents append items, trace events capture the source, trust level, and token count
|
|
463
|
+
- **Memory tier transitions** — Working → session → semantic migrations emit trace events
|
|
464
|
+
- **Retrieval** — Trust-aware retrieval results (scores, items selected) are logged for debugging
|
|
465
|
+
- **Compression** — Context compression events show which items were kept/dropped and the token savings
|
|
466
|
+
|
|
467
|
+
## Integration with pyagent-blueprint
|
|
468
|
+
|
|
469
|
+
The `context` section of a blueprint YAML maps directly to context package configuration:
|
|
470
|
+
|
|
471
|
+
```yaml
|
|
472
|
+
context:
|
|
473
|
+
memory:
|
|
474
|
+
backend: sqlite
|
|
475
|
+
working_max_tokens: 128000
|
|
476
|
+
compression:
|
|
477
|
+
policy: semantic_lossless
|
|
478
|
+
target_ratio: 0.6
|
|
479
|
+
redaction:
|
|
480
|
+
max_sensitivity: internal
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
After `BlueprintCompiler.compile()`, these settings are available on the `RuntimeGraph` for the consumer to wire into agents.
|
|
484
|
+
|
|
485
|
+
## Full stack
|
|
486
|
+
|
|
487
|
+
Install all pillars at once: `pip install pyagent-all`
|
|
488
|
+
|
|
489
|
+
→ [pyagent.org](https://pyagent.org) for full documentation.
|
|
490
|
+
|
|
491
|
+
## Full Documentation
|
|
492
|
+
|
|
493
|
+
See [pyagent.org](https://pyagent.org) for full API reference and integration guides.
|
|
494
|
+
|
|
495
|
+
<!-- pyagent-ecosystem-footer:start -->
|
|
496
|
+
|
|
497
|
+
## The PyAgent ecosystem
|
|
498
|
+
|
|
499
|
+
PyAgent is a production stack for multi-agent LLM systems. Each package is independent — install
|
|
500
|
+
only what you need, or get everything with [`pip install pyagent-all`](https://pyagent.org/getting-started/).
|
|
501
|
+
|
|
502
|
+
| Package | What it gives you |
|
|
503
|
+
|---------|-------------------|
|
|
504
|
+
| `pyagent-blueprint` | [Declarative multi-agent blueprints](https://pyagent.org/guides/blueprint/) — compile, validate, and diff agent systems from YAML |
|
|
505
|
+
| `pyagent-patterns` | [Reusable multi-agent design patterns](https://pyagent.org/packages/patterns/) — Supervisor, Pipeline, ReAct, and 15 more |
|
|
506
|
+
| `pyagent-router` | [Difficulty-aware model routing](https://pyagent.org/guides/router/) — cost-efficient model selection per task |
|
|
507
|
+
| `pyagent-compress` | [Token-efficient agent compression](https://pyagent.org/guides/compression/) — inter-agent token budgets |
|
|
508
|
+
| `pyagent-providers` | [Multi-provider orchestration](https://pyagent.org/guides/providers/) — fallback chains and capability negotiation |
|
|
509
|
+
| `pyagent-context` | [Stateful agent memory](https://pyagent.org/guides/context/) — trust-aware, three-tier context ledger |
|
|
510
|
+
| `pyagent-trace` | [Multi-agent observability & tracing](https://pyagent.org/guides/tracing/) — pattern-aware OpenTelemetry spans |
|
|
511
|
+
| `pyagent-studio` | [Agent control plane dashboard](https://pyagent.org/guides/studio/) — live traces, cost, and governance |
|
|
512
|
+
|
|
513
|
+
**Learn the concepts:**
|
|
514
|
+
[The Orchestrator-Worker pattern](https://pyagent.org/patterns/orchestrator-worker/) ·
|
|
515
|
+
[Engineering a resilient multi-agent harness](https://pyagent.org/architecture/multi-agent-harness/) ·
|
|
516
|
+
[Agent Experience Optimization (AXO)](https://pyagent.org/concepts/agent-experience-optimization/)
|
|
517
|
+
|
|
518
|
+
<!-- pyagent-ecosystem-footer:end -->
|