update
This commit is contained in:
@@ -0,0 +1,763 @@
|
||||
# AgentCore.jl - Session Management Deep Dive
|
||||
|
||||
## Session Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Session Layer │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Session = Tree of Entries │
|
||||
│ │
|
||||
│ Each entry represents a change in conversation state │
|
||||
│ │
|
||||
│ Branch Navigation: │
|
||||
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
|
||||
│ │ E1 │────▶│ E2 │────▶│ E3 │────▶│ E4 │────▶│ E5 │ (current leaf) │
|
||||
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
|
||||
│ │ │ │ │ │ │
|
||||
│ ▼ ▼ ▼ ▼ ▼ │
|
||||
│ Message Message Compaction Message BranchSummary │
|
||||
│ │
|
||||
│ To navigate to E2 (fork point): │
|
||||
│ Session.moveTo(E2) │
|
||||
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
|
||||
│ │ E1 │────▶│ E2 │────▶│ E3' │────▶│ E4' │────▶│ E5' │ (new branch) │
|
||||
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
|
||||
│ │ │ │
|
||||
│ │ ▼ create BranchSummary │
|
||||
│ │ ┌─────┐ │
|
||||
│ └──────│ E6 │ (branch summary) │
|
||||
│ └─────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Entry Types
|
||||
|
||||
```julia
|
||||
abstract type SessionTreeEntry end
|
||||
```
|
||||
|
||||
### 1. MessageEntry
|
||||
|
||||
```julia
|
||||
struct MessageEntry <: SessionTreeEntry
|
||||
type::String # "message"
|
||||
id::String # Unique entry ID
|
||||
parent_id::Union{String, Nothing}
|
||||
timestamp::String # ISO 8601 timestamp
|
||||
message::AgentMessage # The actual message
|
||||
end
|
||||
```
|
||||
|
||||
**Represents**: A user, assistant, or tool message
|
||||
|
||||
### 2. ThinkingLevelChangeEntry
|
||||
|
||||
```julia
|
||||
struct ThinkingLevelChangeEntry <: SessionTreeEntry
|
||||
type::String # "thinking_level_change"
|
||||
id::String
|
||||
parent_id::Union{String, Nothing}
|
||||
timestamp::String
|
||||
thinking_level::String # "off", "minimal", "low", "medium", etc.
|
||||
end
|
||||
```
|
||||
|
||||
**Represents**: Change in model thinking level
|
||||
|
||||
### 3. ModelChangeEntry
|
||||
|
||||
```julia
|
||||
struct ModelChangeEntry <: SessionTreeEntry
|
||||
type::String # "model_change"
|
||||
id::String
|
||||
parent_id::Union{String, Nothing}
|
||||
timestamp::String
|
||||
provider::String # "openai", "anthropic", etc.
|
||||
model_id::String # Model identifier
|
||||
end
|
||||
```
|
||||
|
||||
**Represents**: Change in model
|
||||
|
||||
### 4. ActiveToolsChangeEntry
|
||||
|
||||
```julia
|
||||
struct ActiveToolsChangeEntry <: SessionTreeEntry
|
||||
type::String # "active_tools_change"
|
||||
id::String
|
||||
parent_id::Union{String, Nothing}
|
||||
timestamp::String
|
||||
active_tool_names::Vector{String}
|
||||
end
|
||||
```
|
||||
|
||||
**Represents**: Change in active tools
|
||||
|
||||
### 5. CompactionEntry
|
||||
|
||||
```julia
|
||||
struct CompactionEntry <: SessionTreeEntry
|
||||
type::String # "compaction"
|
||||
id::String
|
||||
parent_id::Union{String, Nothing}
|
||||
timestamp::String
|
||||
summary::String # Summary of compacted history
|
||||
first_kept_entry_id::Union{String, Nothing}
|
||||
tokens_before::Int64 # Context size before compaction
|
||||
retained_tail::Union{Vector{AgentMessage}, Nothing}
|
||||
details::Union{Any, Nothing}
|
||||
usage::Union{Usage, Nothing}
|
||||
from_hook::Bool # Whether triggered by hook
|
||||
end
|
||||
```
|
||||
|
||||
**Represents**: Context window compression
|
||||
|
||||
**Key fields**:
|
||||
- `summary`: Summary of removed messages
|
||||
- `first_kept_entry_id`: First entry that was kept
|
||||
- `tokens_before`: Context size before compaction
|
||||
- `retained_tail`: Messages kept after compaction point
|
||||
|
||||
### 6. BranchSummaryEntry
|
||||
|
||||
```julia
|
||||
struct BranchSummaryEntry <: SessionTreeEntry
|
||||
type::String # "branch_summary"
|
||||
id::String
|
||||
parent_id::Union{String, Nothing}
|
||||
timestamp::String
|
||||
from_id::String # Branch point entry ID
|
||||
summary::String # Summary of branch history
|
||||
details::Union{Any, Nothing}
|
||||
usage::Union{Usage, Nothing}
|
||||
from_hook::Bool
|
||||
end
|
||||
```
|
||||
|
||||
**Represents**: Branch point with summary
|
||||
|
||||
### 7. CustomEntry
|
||||
|
||||
```julia
|
||||
struct CustomEntry <: SessionTreeEntry
|
||||
type::String # Custom type
|
||||
id::String
|
||||
parent_id::Union{String, Nothing}
|
||||
timestamp::String
|
||||
custom_type::String
|
||||
data::Union{Any, Nothing}
|
||||
end
|
||||
```
|
||||
|
||||
**Represents**: Custom application-specific data
|
||||
|
||||
### 8. CustomMessageEntry
|
||||
|
||||
```julia
|
||||
struct CustomMessageEntry <: SessionTreeEntry
|
||||
type::String
|
||||
id::String
|
||||
parent_id::Union{String, Nothing}
|
||||
timestamp::String
|
||||
custom_type::String
|
||||
content::String
|
||||
details::Union{Any, Nothing}
|
||||
display::Bool
|
||||
end
|
||||
```
|
||||
|
||||
**Represents**: Custom message to display to user
|
||||
|
||||
### 9. LabelEntry
|
||||
|
||||
```julia
|
||||
struct LabelEntry <: SessionTreeEntry
|
||||
type::String
|
||||
id::String
|
||||
parent_id::Union{String, Nothing}
|
||||
timestamp::String
|
||||
target_id::String # Entry being labeled
|
||||
label::Union{String, Nothing}
|
||||
end
|
||||
```
|
||||
|
||||
**Represents**: Label/note on an entry
|
||||
|
||||
### 10. SessionInfoEntry
|
||||
|
||||
```julia
|
||||
struct SessionInfoEntry <: SessionTreeEntry
|
||||
type::String
|
||||
id::String
|
||||
parent_id::Union{String, Nothing}
|
||||
timestamp::String
|
||||
name::Union{String, Nothing}
|
||||
end
|
||||
```
|
||||
|
||||
**Represents**: Session metadata (name, etc.)
|
||||
|
||||
### 11. LeafEntry
|
||||
|
||||
```julia
|
||||
struct LeafEntry <: SessionTreeEntry
|
||||
type::String
|
||||
id::String
|
||||
parent_id::Union{String, Nothing}
|
||||
timestamp::String
|
||||
target_id::Union{String, Nothing}
|
||||
end
|
||||
```
|
||||
|
||||
**Represents**: Change in current leaf (branch pointer)
|
||||
|
||||
## Session Storage Interface
|
||||
|
||||
```julia
|
||||
abstract type SessionStorage{T<:SessionMetadata} end
|
||||
```
|
||||
|
||||
### Storage Methods
|
||||
|
||||
```julia
|
||||
# Metadata
|
||||
getMetadata(storage::SessionStorage)::Promise{T}
|
||||
|
||||
# Leaf management
|
||||
getLeafId(storage::SessionStorage)::Promise{Union{String, Nothing}}
|
||||
setLeafId(storage::SessionStorage, leaf_id::String)::Promise{Nothing}
|
||||
|
||||
# Entry management
|
||||
createEntryId(storage::SessionStorage)::Promise{String}
|
||||
appendEntry(storage::SessionStorage, entry::SessionTreeEntry)::Promise{Nothing}
|
||||
getEntry(storage::SessionStorage, id::String)::Promise{Union{SessionTreeEntry, Nothing}}
|
||||
|
||||
# Query
|
||||
findEntries(storage::SessionStorage, type::String)::Promise{Vector{SessionTreeEntry}}
|
||||
getLabel(storage::SessionStorage, id::String)::Promise{Union{String, Nothing}}
|
||||
getSessionName(storage::SessionStorage)::Promise{Union{String, Nothing}}
|
||||
|
||||
# Branch navigation
|
||||
getPathToRootOrCompaction(
|
||||
storage::SessionStorage,
|
||||
leaf_id::String,
|
||||
)::Promise{Vector{SessionTreeEntry}}
|
||||
|
||||
getEntries(storage::SessionStorage, options::Dict{String, Any})::Promise{Vector{SessionTreeEntry}}
|
||||
|
||||
# Stats
|
||||
getSessionStats(storage::SessionStorage)::Promise{SessionStats}
|
||||
```
|
||||
|
||||
## JsonlSessionStorage
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ JSONL Storage Format │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
File: session.jsonl
|
||||
|
||||
Entry 1 (Metadata):
|
||||
{"type":"session","id":"meta_1","created_at":"2024-01-01T00:00:00Z","cwd":"/path","path":"/path/session.jsonl"}
|
||||
|
||||
Entry 2 (Message):
|
||||
{"type":"message","id":"msg_1","parent_id":null,"timestamp":"2024-01-01T00:00:01Z","message":{"role":"user","content":[{"type":"text","text":"Hello"}]}}
|
||||
|
||||
Entry 3 (Thinking Level):
|
||||
{"type":"thinking_level_change","id":"tl_1","parent_id":"msg_1","timestamp":"2024-01-01T00:00:02Z","thinking_level":"medium"}
|
||||
|
||||
Entry 4 (Model Change):
|
||||
{"type":"model_change","id":"mc_1","parent_id":"tl_1","timestamp":"2024-01-01T00:00:03Z","provider":"openai","model_id":"gpt-4"}
|
||||
|
||||
Entry 5 (Compaction):
|
||||
{"type":"compaction","id":"comp_1","parent_id":"mc_1","timestamp":"2024-01-01T00:00:04Z","summary":"Previous messages summarized...","first_kept_entry_id":"msg_3","tokens_before":100000,"tokens_after":50000}
|
||||
|
||||
Entry 6 (Branch Summary):
|
||||
{"type":"branch_summary","id":"branch_1","parent_id":"comp_1","timestamp":"2024-01-01T00:00:05Z","from_id":"msg_3","summary":"Branch from message 3"}
|
||||
|
||||
Entry 7 (Active Tools):
|
||||
{"type":"active_tools_change","id":"tools_1","parent_id":"branch_1","timestamp":"2024-01-01T00:00:06Z","active_tool_names":["bash","read"]}
|
||||
|
||||
Entry 8 (Leaf):
|
||||
{"type":"leaf","id":"leaf_1","parent_id":"tools_1","timestamp":"2024-01-01T00:00:07Z","target_id":"msg_5"}
|
||||
|
||||
Notes:
|
||||
- Each line is a JSON object (JSONL format)
|
||||
- parent_id references previous entry (linked list structure)
|
||||
- Leaf entry points to current position in tree
|
||||
- To fork, create new branch from any entry
|
||||
```
|
||||
|
||||
## InMemorySessionStorage
|
||||
|
||||
```julia
|
||||
mutable struct InMemorySessionStorage
|
||||
metadata::SessionMetadata
|
||||
leaf_id::Union{String, Nothing}
|
||||
entries::Dict{String, SessionTreeEntry}
|
||||
labels::Dict{String, String}
|
||||
end
|
||||
```
|
||||
|
||||
**Purpose**: Testing and temporary sessions
|
||||
|
||||
**Advantages**:
|
||||
- Fast (no I/O)
|
||||
- Easy to inspect
|
||||
- Perfect for tests
|
||||
|
||||
## Session Class
|
||||
|
||||
```julia
|
||||
mutable struct Session{T<:SessionMetadata}
|
||||
storage::SessionStorage{T}
|
||||
context_build_options::SessionContextBuildOptions
|
||||
end
|
||||
```
|
||||
|
||||
### Session Methods
|
||||
|
||||
#### appendMessage()
|
||||
|
||||
```julia
|
||||
function appendMessage(session::Session, message::AgentMessage)::String
|
||||
entry = MessageEntry(
|
||||
"message",
|
||||
createEntryId(session.storage),
|
||||
getLeafId(session.storage),
|
||||
create_timestamp(),
|
||||
message,
|
||||
)
|
||||
return appendTypedEntry(session, entry)
|
||||
end
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
```julia
|
||||
session = Session(storage)
|
||||
|
||||
# Add user message
|
||||
user_id = appendMessage(session, UserMessage("user", [TextContent("Hello")], timestamp))
|
||||
|
||||
# Add assistant message
|
||||
assistant_id = appendMessage(session, AssistantMessage(...))
|
||||
|
||||
# Add tool result
|
||||
tool_id = appendMessage(session, ToolResultMessage(...))
|
||||
```
|
||||
|
||||
#### appendThinkingLevelChange()
|
||||
|
||||
```julia
|
||||
function appendThinkingLevelChange(
|
||||
session::Session,
|
||||
thinking_level::String,
|
||||
)::String
|
||||
entry = ThinkingLevelChangeEntry(
|
||||
"thinking_level_change",
|
||||
createEntryId(session.storage),
|
||||
getLeafId(session.storage),
|
||||
create_timestamp(),
|
||||
thinking_level,
|
||||
)
|
||||
return appendTypedEntry(session, entry)
|
||||
end
|
||||
```
|
||||
|
||||
#### appendCompaction()
|
||||
|
||||
```julia
|
||||
function appendCompaction(
|
||||
session::Session,
|
||||
summary::String,
|
||||
first_kept_entry_id::Union{String, Nothing},
|
||||
tokens_before::Int64,
|
||||
details::Union{Any, Nothing}=nothing,
|
||||
from_hook::Bool=false,
|
||||
usage::Union{Usage, Nothing}=nothing,
|
||||
retained_tail::Union{Vector{AgentMessage}, Nothing}=nothing,
|
||||
)::String
|
||||
entry = CompactionEntry(
|
||||
"compaction",
|
||||
createEntryId(session.storage),
|
||||
getLeafId(session.storage),
|
||||
create_timestamp(),
|
||||
summary,
|
||||
first_kept_entry_id,
|
||||
tokens_before,
|
||||
retained_tail,
|
||||
details,
|
||||
usage,
|
||||
from_hook,
|
||||
)
|
||||
return appendTypedEntry(session, entry)
|
||||
end
|
||||
```
|
||||
|
||||
#### moveTo()
|
||||
|
||||
```julia
|
||||
function moveTo(
|
||||
session::Session,
|
||||
entry_id::Union{String, Nothing},
|
||||
summary::Union{Dict{String, Any}, Nothing}=nothing,
|
||||
)::Union{String, Nothing}
|
||||
# Set new leaf
|
||||
setLeafId(session.storage, entry_id)
|
||||
|
||||
# Optionally create branch summary
|
||||
if !isnothing(summary)
|
||||
return appendTypedEntry(session, BranchSummaryEntry(
|
||||
"branch_summary",
|
||||
createEntryId(session.storage),
|
||||
entry_id,
|
||||
create_timestamp(),
|
||||
entry_id,
|
||||
summary["summary"],
|
||||
get(summary, "details", nothing),
|
||||
get(summary, "usage", nothing),
|
||||
get(summary, "from_hook", false),
|
||||
))
|
||||
end
|
||||
|
||||
return nothing
|
||||
end
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
```julia
|
||||
# Fork from a specific point
|
||||
session.moveTo(msg_3_id)
|
||||
|
||||
# Branch with summary
|
||||
session.moveTo(
|
||||
msg_3_id,
|
||||
Dict(
|
||||
"summary" => "User wanted to focus on file operations",
|
||||
"details" => Dict("focus" => "files"),
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## Build Session Context
|
||||
|
||||
```julia
|
||||
function buildSessionContext(
|
||||
path_entries::Vector{SessionTreeEntry},
|
||||
options::SessionContextBuildOptions=SessionContextBuildOptions(),
|
||||
)::SessionContext
|
||||
state = deriveSessionContextState(path_entries)
|
||||
context_entries = buildContextEntries(path_entries, options)
|
||||
messages = SessionTreeEntry[]
|
||||
for (i, entry) in enumerate(context_entries)
|
||||
append!(messages, sessionEntryToContextMessages(entry, i, context_entries, options))
|
||||
end
|
||||
return SessionContext(messages, state.thinking_level, state.model, state.active_tool_names)
|
||||
end
|
||||
```
|
||||
|
||||
### Context Entry Transform
|
||||
|
||||
```julia
|
||||
function defaultContextEntryTransform(
|
||||
path_entries::Vector{SessionTreeEntry},
|
||||
)::Vector{SessionTreeEntry}
|
||||
compaction = nothing
|
||||
for entry in path_entries
|
||||
if entry isa CompactionEntry
|
||||
compaction = entry
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if isnothing(compaction)
|
||||
return copy(path_entries)
|
||||
end
|
||||
|
||||
# Include compaction entry
|
||||
entries = [compaction]
|
||||
|
||||
# Include retained tail if present
|
||||
if !isnothing(compaction.retained_tail)
|
||||
compaction_idx = findfirst(e -> e.id == compaction.id, path_entries)
|
||||
append!(entries, path_entries[compaction_idx+1:end])
|
||||
return entries
|
||||
end
|
||||
|
||||
# Otherwise include entries after first_kept_entry_id
|
||||
if !isnothing(compaction.first_kept_entry_id)
|
||||
found_first_kept = false
|
||||
compaction_idx = findfirst(e -> e.id == compaction.id, path_entries)
|
||||
for i in 1:compaction_idx-1
|
||||
entry = path_entries[i]
|
||||
if entry.id == compaction.first_kept_entry_id
|
||||
found_first_kept = true
|
||||
end
|
||||
if found_first_kept
|
||||
push!(entries, entry)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Include entries after compaction
|
||||
compaction_idx = findfirst(e -> e.id == compaction.id, path_entries)
|
||||
append!(entries, path_entries[compaction_idx+1:end])
|
||||
|
||||
return entries
|
||||
end
|
||||
```
|
||||
|
||||
### Session Entry to Context Messages
|
||||
|
||||
```julia
|
||||
function sessionEntryToContextMessages(
|
||||
entry::SessionTreeEntry,
|
||||
index::Int64,
|
||||
entries::Vector{SessionTreeEntry},
|
||||
options::SessionContextBuildOptions=SessionContextBuildOptions(),
|
||||
)::Vector{AgentMessage}
|
||||
if entry isa MessageEntry
|
||||
return [entry.message]
|
||||
end
|
||||
|
||||
if entry isa CustomMessageEntry
|
||||
return [createCustomMessage(
|
||||
entry.custom_type,
|
||||
entry.content,
|
||||
entry.display,
|
||||
entry.details,
|
||||
entry.timestamp,
|
||||
)]
|
||||
end
|
||||
|
||||
if entry isa CompactionEntry
|
||||
messages = [createCompactionSummaryMessage(
|
||||
entry.summary,
|
||||
entry.tokens_before,
|
||||
entry.timestamp,
|
||||
)]
|
||||
if !isnothing(entry.retained_tail)
|
||||
append!(messages, entry.retained_tail)
|
||||
end
|
||||
return messages
|
||||
end
|
||||
|
||||
if entry isa BranchSummaryEntry
|
||||
return [createBranchSummaryMessage(
|
||||
entry.summary,
|
||||
entry.from_id,
|
||||
entry.timestamp,
|
||||
)]
|
||||
end
|
||||
|
||||
if entry isa CustomEntry
|
||||
# Custom projectors can transform custom entries
|
||||
if !isnothing(options.entry_projectors) && haskey(options.entry_projectors, entry.custom_type)
|
||||
projector = options.entry_projectors[entry.custom_type]
|
||||
return projector(entry, index, entries)
|
||||
end
|
||||
return AgentMessage[]
|
||||
end
|
||||
|
||||
return AgentMessage[]
|
||||
end
|
||||
```
|
||||
|
||||
## Branch Navigation
|
||||
|
||||
```
|
||||
Scenario: User wants to explore a different path
|
||||
|
||||
Initial Branch (current path):
|
||||
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
|
||||
│ E1 │────▶│ E2 │────▶│ E3 │────▶│ E4 │ (leaf)
|
||||
└─────┘ └─────┘ └─────┘ └─────┘
|
||||
│ │ │ │
|
||||
Message Message Compaction Message
|
||||
|
||||
Step 1: Fork from E2
|
||||
┌─────┐ ┌─────┐ ┌─────┐
|
||||
│ E1 │────▶│ E2 │─────────────────┐
|
||||
└─────┘ └─────┘ │
|
||||
│ │ │
|
||||
│ ▼ create BranchSummary│
|
||||
│ ┌─────┐ │
|
||||
│ │ E5 │ (branch summary) │
|
||||
│ └─────┘ │
|
||||
└──────────────────────────────────┘
|
||||
(new branch from E2)
|
||||
|
||||
Step 2: Continue on new branch
|
||||
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
|
||||
│ E1 │────▶│ E2 │────▶│ E3' │────▶│ E4' │────▶│ E5' │ (new leaf)
|
||||
└─────┘ └─────┘ └─────┘ └─────┘ └─────┘
|
||||
|
||||
Current branch now is:
|
||||
[ E1, E2, E3', E4', E5' ]
|
||||
|
||||
Original branch is:
|
||||
[ E1, E2, E5 ] (E3, E4 are now separate branch)
|
||||
|
||||
Key Points:
|
||||
- Shared entries: E1, E2
|
||||
- Branch point: E2
|
||||
- Branch summary: E5 (points to E2)
|
||||
- Each branch has independent tail
|
||||
```
|
||||
|
||||
## Compaction Strategy
|
||||
|
||||
### Why Compaction?
|
||||
|
||||
LLM context windows have limits:
|
||||
- GPT-4: 128K tokens
|
||||
- Claude 2: 100K tokens
|
||||
- Llama 2: 4K tokens
|
||||
|
||||
**Problem**: Conversations grow unbounded
|
||||
**Solution**: Compaction - summarize old messages
|
||||
|
||||
### Compaction Process
|
||||
|
||||
```julia
|
||||
# 1. Identify messages to compact
|
||||
# - Keep recent N messages (e.g., last 2 turns)
|
||||
# - Summarize everything before
|
||||
|
||||
# 2. Generate summary
|
||||
# - Use LLM to summarize
|
||||
# - Include key facts, decisions, user preferences
|
||||
|
||||
# 3. Create CompactionEntry
|
||||
# - summary: The summary text
|
||||
# - first_kept_entry_id: First entry that was NOT compacted
|
||||
# - tokens_before: Context size before compaction
|
||||
# - retained_tail: Messages kept after compaction point
|
||||
|
||||
# 4. Update storage
|
||||
# - Append CompactionEntry
|
||||
# - Update leaf to CompactionEntry
|
||||
```
|
||||
|
||||
### Compaction Example
|
||||
|
||||
```julia
|
||||
# Before compaction (100K tokens):
|
||||
[
|
||||
msg_1, # User: "I need to set up a project"
|
||||
msg_2, # Assistant: "Sure, what language?"
|
||||
msg_3, # User: "Python"
|
||||
msg_4, # Assistant: "I'll create a Python project"
|
||||
msg_5, # User: "With FastAPI"
|
||||
msg_6, # Assistant: "Creating FastAPI project..."
|
||||
msg_7, # Tool: bash("mkdir myapp")
|
||||
msg_8, # Tool: write("myapp/main.py", ...)
|
||||
msg_9, # Assistant: "Project created!"
|
||||
msg_10, # User: "Can you add auth?"
|
||||
msg_11, # Assistant: "Adding auth..."
|
||||
msg_12, # User: "Use JWT"
|
||||
msg_13, # Assistant: "Implementing JWT..."
|
||||
msg_14, # Tool: bash("pip install jwt")
|
||||
msg_15, # Tool: write("myapp/auth.py", ...)
|
||||
msg_16, # Assistant: "Auth implemented!"
|
||||
]
|
||||
|
||||
# After compaction (20K tokens):
|
||||
[
|
||||
compaction_entry, # Summary of msg_1 to msg_10
|
||||
msg_11, # Keep recent messages
|
||||
msg_12,
|
||||
msg_13,
|
||||
msg_14,
|
||||
msg_15,
|
||||
msg_16,
|
||||
]
|
||||
|
||||
# Compaction summary:
|
||||
"""
|
||||
Previous conversation summary:
|
||||
- User wanted to create a Python project
|
||||
- Chose FastAPI framework
|
||||
- Assistant created project structure in myapp/
|
||||
- User requested authentication
|
||||
- Chose JWT for auth
|
||||
- Assistant implemented JWT auth in myapp/auth.py
|
||||
"""
|
||||
```
|
||||
|
||||
## Complete Session Example
|
||||
|
||||
```julia
|
||||
using AgentCore
|
||||
|
||||
# 1. Create storage
|
||||
storage = JsonlSessionStorage(
|
||||
SessionMetadata("session_1", "2024-01-01T00:00:00Z"),
|
||||
"/path/to/session.jsonl",
|
||||
)
|
||||
|
||||
# 2. Create session
|
||||
session = Session(storage)
|
||||
|
||||
# 3. Add messages
|
||||
msg1_id = appendMessage(session, UserMessage("user", [TextContent("Hello")], timestamp))
|
||||
msg2_id = appendMessage(session, AssistantMessage("assistant", [TextContent("Hi!")], ...))
|
||||
|
||||
# 4. Change thinking level
|
||||
tl_id = appendThinkingLevelChange(session, "medium")
|
||||
|
||||
# 5. Change model
|
||||
mc_id = appendModelChange(session, "openai", "gpt-4")
|
||||
|
||||
# 6. Add more messages
|
||||
msg3_id = appendMessage(session, UserMessage("user", [TextContent("What can you do?")], timestamp))
|
||||
msg4_id = appendMessage(session, AssistantMessage("assistant", [TextContent("I can...")], ...))
|
||||
|
||||
# 7. Compact context (100K tokens → 20K)
|
||||
compact_id = appendCompaction(
|
||||
session,
|
||||
"User asked about capabilities and assistant explained",
|
||||
msg2_id,
|
||||
100000,
|
||||
Dict("summary_length" => 50),
|
||||
false,
|
||||
usage,
|
||||
[msg3, msg4], # Retained tail
|
||||
)
|
||||
|
||||
# 8. Fork and branch
|
||||
session.moveTo(msg2_id) # Go back to msg2
|
||||
|
||||
# 9. Create new branch
|
||||
branch_id = appendBranchSummary(
|
||||
session,
|
||||
"User changed direction to focus on file operations",
|
||||
msg2_id,
|
||||
Dict("focus" => "files"),
|
||||
)
|
||||
|
||||
# 10. Continue on new branch
|
||||
msg5_id = appendMessage(session, UserMessage("user", [TextContent("Let's work with files")], timestamp))
|
||||
|
||||
# 11. Query session context
|
||||
context = buildSessionContext(session)
|
||||
|
||||
# 12. Get stats
|
||||
stats = getSessionStats(session)
|
||||
println("Messages: $(stats.message_count)")
|
||||
println("Total tokens: $(stats.total_tokens)")
|
||||
println("Cost: $$(stats.cost_total)")
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use compaction** for long conversations to stay within context limits
|
||||
2. **Create branch summaries** when forking to document divergent paths
|
||||
3. **Retain tail messages** after compaction for context
|
||||
4. **Track token usage** to optimize compaction timing
|
||||
5. **Use InMemorySessionStorage** for testing
|
||||
Reference in New Issue
Block a user