feat: Add compaction hook - #12176
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
e902bec to
1281a8a
Compare
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
anakin87
left a comment
There was a problem hiding this comment.
I left a few initial comments
| from haystack.hooks.compaction import ContextCompactionHook, SlidingWindowCompactor | ||
|
|
||
| hook = ContextCompactionHook( | ||
| compactor=SlidingWindowCompactor(keep_last_n_messages=20), threshold_tokens=100_000 |
There was a problem hiding this comment.
In this design
- the hook knows when to run the compactor (based on tokens)
- the compactor knows how many messages to keep
Have you also thought of other design options?
Like a single context_budget parameter?
There was a problem hiding this comment.
@anakin87 could you explain a bit more what the context_budget parameter would be exactly? Some constraints we should be aware of:
- We don't have an exact token count at compaction time since we haven't counted the tokens yet from the tool results picked up from the last turn. So we are definitely under counting when compaction is triggered. --> We could try an approximate this using a provided tokenizer like tiktoken.
- We don't know what the context window size is of the model that is currently driving the Agent. --> This could be solved by asking for this info at init time in the Compactor.
There was a problem hiding this comment.
Yes, I understand the limitations.
My main concern is that we trigger on a certain token threshold and then we cut based on messages; the Compactor also does not know if it reached a number below the threshold. For these reasons, I'm not sure that the current protocol will be correct in the future (maybe the Compactor needs to know about the threshold).
Maybe we should flag this feature as experimental?
LC SummarizationMiddleware: interesting to take a look, not perfect
There was a problem hiding this comment.
Yeah that's a fair point. I'm looking into potential solutions now. It seems like we will need to introduce some sort of approximate token counter for this to be more robust. I'll see if I can rework this PR to do something like this and then I'd be happy to split it up into smaller ones to help keep the review size manageable.
Could you explain tool result pruning?
What's the use case for having a separate Compaction Tool? |
Sure! Tool result pruning is the idea that to shorten context we could simply prune older tool results especially long ones. The idea being in a long running Agent session think 100s of tool calls the earlier ones in the history may no longer be relevant and just pollute the context window. So this compactor would go in and replace old tool results with a placeholder message like There are some blog posts that mention this concept either under the name pruning or trimming.
The point of having a compaction tool is to let the Agent decide when to compact. Mostly motivated by this blog post https://www.langchain.com/blog/autonomous-context-compression allowing users to follow the philosophy of giving more power to the Agent in making decisions. E.g. a quote from the blog post
|
Related Issues
Proposed Changes:
Added context compaction for the
Agent: aContextCompactionHookthat shortens the conversation once itgrows past a threshold, and a
Compactorprotocol defining how the shortening is done. The first built-incompactor is
SlidingWindowCompactor, which keeps the leading system messages and the most recent messages and drops what is in between, leaving a short note in their place. Register the hook under thebefore_llmhook point:The trigger is the
context_tokensstate key, which the Agent refreshes after each chat-generator call from thereply's reported token usage. The Agent's Chat Generator must report usage in
meta["usage"], otherwisecontext_tokensstays at0, the threshold is never reached, and the hook never compacts. The hook logs awarning once per run when it detects this. Leave headroom between the threshold and the model's context window:
context_tokenswas measured at the previous call, so the tool results appended since and the upcoming reply must still fit within the window.Compaction never leaves a tool result whose originating tool call was removed, which chat-completion APIs reject: the boundary of the retained window is moved backwards onto the assistant message holding the call, so slightly more may be kept than
keep_last_n_messagesasks for. Implement theCompactorprotocol to define your own strategy. ReturnNoneunless the conversation actually gets smaller, and never drop the final message, because the Agent may be mid-step with a pending tool call whose result is appended right after compaction.Note that compaction is lossy. After it runs the Agent works from a shorter record of the run, and with
SlidingWindowCompactoranything outside the retained window is gone rather than summarized.How did you test it?
Added new tests.
Also tried it out locally with
compaction_live_check.py
Notes for the reviewer
This one a few PRs.
Checklist
fix:,feat:,build:,chore:,ci:,docs:,style:,refactor:,perf:,test:and added!in case the PR includes breaking changes.