1212
1313if TYPE_CHECKING :
1414 from dare_framework .compression .moving_compression import MovingCompressor
15+ from dare_framework .guidance .guidance_queue import GuidanceQueue
1516 from dare_framework .model .types import Prompt
1617 from dare_framework .skill .types import Skill
1718
1819from dare_framework .context .kernel import IContext , IRetrievalContext , IAssembleContext
19- from dare_framework .context .types import AssembledContext , Budget , Message , MessageRole
20+ from dare_framework .context .types import AssembledContext , Budget , Message , MessageKind , MessageRole
2021
2122
2223# ============================================================
@@ -45,6 +46,7 @@ def __init__(
4546 assemble_context : IAssembleContext | None = None ,
4647 context_window_tokens : int | None = None ,
4748 moving_compressor : MovingCompressor | None = None ,
49+ guidance_queue : GuidanceQueue | None = None ,
4850 ) -> None :
4951 if config is None :
5052 raise ValueError ("Context requires a non-null Config" )
@@ -69,6 +71,9 @@ def __init__(
6971 # 可选:移动窗口压缩器,通过 moving_compressor.prune(self) 使用
7072 self ._moving_compressor = moving_compressor
7173
74+ # 可选:用户引导队列,在 assemble() 时自动排空注入到 STM
75+ self ._guidance_queue : GuidanceQueue | None = guidance_queue
76+
7277 # Initialize default short-term memory if not provided
7378 if self ._short_term_memory is None :
7479 from dare_framework .memory .in_memory_stm import InMemorySTM
@@ -198,7 +203,26 @@ def list_tools(self) -> list[CapabilityDescriptor]:
198203
199204 # ========== Assembly Methods (Core) ==========
200205
206+ def _drain_guidance (self ) -> None :
207+ """Drain pending guidance items into STM.
208+
209+ Called before compression and assembly so that guidance messages
210+ participate in token-budget pruning and appear in the assembled
211+ context exactly once.
212+ """
213+ if self ._guidance_queue is None :
214+ return
215+ items = self ._guidance_queue .drain_all_sync ()
216+ for item in items :
217+ self .stm_add (Message (
218+ role = MessageRole .USER ,
219+ kind = MessageKind .CHAT ,
220+ text = item .content ,
221+ metadata = {"guidance_id" : item .id , "source" : "user_guidance" },
222+ ))
223+
201224 def assemble (self ) -> AssembledContext :
225+ self ._drain_guidance ()
202226 return self ._assemble_context .assemble (self )
203227
204228 async def compress (self , ** options : Any ) -> None :
@@ -214,7 +238,13 @@ async def compress(self, **options: Any) -> None:
214238 await self ._moving_compressor .prune (self , ** prune_opts )
215239
216240 async def assemble_for_model (self , ** options : Any ) -> AssembledContext :
217- """供模型调用的装配入口:在内部静默触发压缩,然后返回 AssembledContext。"""
241+ """供模型调用的装配入口:先排空引导队列,再压缩,最后装配。
242+
243+ Guidance is drained **before** compression so that guidance messages
244+ participate in token-budget pruning and do not silently push the
245+ final prompt over the configured context window.
246+ """
247+ self ._drain_guidance ()
218248 await self .compress (** options )
219249 return self .assemble ()
220250
0 commit comments