fix(s08): support recursive glob and conditional micro compact

This commit is contained in:
Jared
2026-08-20 16:39:45 +08:00
parent f9e8b280f7
commit 1342a990a2
25 changed files with 248 additions and 128 deletions

View File

@@ -117,7 +117,7 @@ messages = [*messages[:head_end], marker, *messages[tail_start:]]
## ステップ 3micro_compact ## ステップ 3micro_compact
`micro_compact` は直近の assistant 応答より後に追加されたすべての `tool_result` を完全に保持し、モデルが各結果を少なくとも 1 回は完全な形で読めるようにします。モデルがすでに読んだ結果については最新 3 件を残し、それより古く 120 文字を超える結果を短くします。保存済みの結果にはファイルパスを残し、それ以外はプレースホルダーに置き換えます。 最初の 2 ステップの後、`prepare` は残りのコンテキストサイズを推定し、`CONTEXT_CHAR_LIMIT` を超えている場合にだけ `micro_compact` を実行します。`micro_compact` は直近の assistant 応答より後に追加されたすべての `tool_result` を完全に保持し、モデルが各結果を少なくとも 1 回は完全な形で読めるようにします。モデルがすでに読んだ結果については最新 3 件を残し、それより古く 120 文字を超える結果を短くします。保存済みの結果にはファイルパスを残し、それ以外はプレースホルダーに置き換えます。
![古い結果を置き換える](images/micro-compact.ja.svg) ![古い結果を置き換える](images/micro-compact.ja.svg)
@@ -142,12 +142,12 @@ for _, _, block in consumed[:-self.KEEP_RECENT_RESULTS]:
保存していない古い結果にはプレースホルダーだけが残ります。ステップ 1 で保存した結果には、完全な出力を読み直すためのパスが残ります。 保存していない古い結果にはプレースホルダーだけが残ります。ステップ 1 で保存した結果には、完全な出力を読み直すためのパスが残ります。
最初の 3 ステップは決定的なテキスト処理と構造操作です。追加の API 呼び出しは発生しません。 最初の 2 ステップは毎ラウンド実行され、ステップ 3 はコンテキストが上限を超えた場合にだけ実行されます。3 ステップとも決定的なテキスト処理と構造操作であり、追加の API 呼び出しは発生しません。
## ステップ 4compact_history ## ステップ 4compact_history
最初の 3 ステップの後、コードは `estimate_chars(messages)`現在のメッセージに含まれる文字数を数えます。 `micro_compact` の後、コードは `estimate_chars(messages)`コンテキストを再び推定します。
```python ```python
CONTEXT_CHAR_LIMIT = 50000 CONTEXT_CHAR_LIMIT = 50000
@@ -156,7 +156,7 @@ def estimate_chars(messages):
return len(json.dumps(messages, default=str, ensure_ascii=False)) return len(json.dumps(messages, default=str, ensure_ascii=False))
``` ```
文字数が `CONTEXT_CHAR_LIMIT` を超えると`compact_history` は 4 つの処理を行います。 文字数がまだ `CONTEXT_CHAR_LIMIT` を超えている場合`compact_history` は 4 つの処理を行います。
1. 完全なメッセージ履歴を `.transcripts/` に書き込みます。 1. 完全なメッセージ履歴を `.transcripts/` に書き込みます。
2. モデルに事実だけの状態要約を依頼します。 2. モデルに事実だけの状態要約を依頼します。
@@ -181,18 +181,20 @@ def compact_history(messages, active_request):
## 順序を固定する理由 ## 順序を固定する理由
パイプラインは常に次の順序で実行されます。 パイプラインは次の順序で処理し、必要な場合にだけ情報を失う圧縮へ進みます。
```text ```python
tool_result_budget messages = self.tool_result_budget(messages)
→ snip_compact messages = self.snip_compact(messages)
→ micro_compact if self.estimate_chars(messages) > self.CONTEXT_CHAR_LIMIT:
→ compact_history上限を超えた場合 messages = self.micro_compact(messages)
if self.estimate_chars(messages) > self.CONTEXT_CHAR_LIMIT:
messages = self.compact_history(messages, active_request)
``` ```
この順序には 2 つの条件があります。 この順序には 2 つの条件があります。
1. 最初の 3 ステップはモデルを呼び出しません。ステップ 4 だけが API リクエストを追加します。 1. ステップ 1 と 2 は毎ラウンド実行され、ステップ 3 は上限を超えた場合だけ実行されます。API リクエストを追加するのはステップ 4 だけです。
2. `tool_result_budget``micro_compact` より先に動く必要があります。古い結果をプレースホルダーにする前に、大きな結果をディスクへ保存します。 2. `tool_result_budget``micro_compact` より先に動く必要があります。古い結果をプレースホルダーにする前に、大きな結果をディスクへ保存します。
各ラウンドは、コストが低く情報を再取得しやすい処理から始まります。 各ラウンドは、コストが低く情報を再取得しやすい処理から始まります。
@@ -243,7 +245,7 @@ def agent_loop(messages, active_request):
raise raise
``` ```
すべてのモデル呼び出しが同じパイプラインを通ります。CLI は `query` を追加した後に `agent_loop(history, query)` を呼ぶため、圧縮を繰り返しても現在の要求は失われません。最初の 3 ステップ後も上限を超える場合、または API が拒否した場合にだけ、コードはモデルへ要約を依頼します。 すべてのモデル呼び出しが同じパイプラインを通ります。CLI は `query` を追加した後に `agent_loop(history, query)` を呼ぶため、圧縮を繰り返しても現在の要求は失われません。`micro_compact`後も上限を超える場合、または API が拒否した場合にだけ、コードはモデルへ要約を依頼します。
## compact ツール ## compact ツール

View File

@@ -117,7 +117,7 @@ This step controls the number of messages. Tool results inside the retained mess
## Step 3: micro_compact ## Step 3: micro_compact
`micro_compact` preserves every `tool_result` added after the most recent assistant response, so the model sees each new result in full once. Among results the model has already consumed, it keeps the latest 3 and shortens older results longer than 120 characters. Persisted results keep their file path; the rest become placeholders: After the first two steps, `prepare` estimates the remaining context size and runs `micro_compact` only when it is above `CONTEXT_CHAR_LIMIT`. `micro_compact` preserves every `tool_result` added after the most recent assistant response, so the model sees each new result in full once. Among results the model has already consumed, it keeps the latest 3 and shortens older results longer than 120 characters. Persisted results keep their file path; the rest become placeholders:
![Replacing old results](images/micro-compact.en.svg) ![Replacing old results](images/micro-compact.en.svg)
@@ -142,12 +142,12 @@ for _, _, block in consumed[:-self.KEEP_RECENT_RESULTS]:
An old result that was not persisted keeps only a placeholder. Results saved in Step 1 retain the path to their complete output. An old result that was not persisted keeps only a placeholder. Results saved in Step 1 retain the path to their complete output.
The first three steps are deterministic text and structure operations. They do not add API calls. The first two steps run every round. Step 3 runs only when the context is above the limit. All three are deterministic text and structure operations; they do not add API calls.
## Step 4: compact_history ## Step 4: compact_history
After the first three steps, the code counts the characters in the current messages with `estimate_chars(messages)`: After `micro_compact`, the code estimates the context again with `estimate_chars(messages)`:
```python ```python
CONTEXT_CHAR_LIMIT = 50000 CONTEXT_CHAR_LIMIT = 50000
@@ -156,7 +156,7 @@ def estimate_chars(messages):
return len(json.dumps(messages, default=str, ensure_ascii=False)) return len(json.dumps(messages, default=str, ensure_ascii=False))
``` ```
When the count exceeds `CONTEXT_CHAR_LIMIT`, `compact_history` does four things: When the count still exceeds `CONTEXT_CHAR_LIMIT`, `compact_history` does four things:
1. Writes the complete message history to `.transcripts/`. 1. Writes the complete message history to `.transcripts/`.
2. Asks the model for a factual state summary. 2. Asks the model for a factual state summary.
@@ -181,18 +181,20 @@ This lesson uses character count as its trigger, and all related thresholds use
## Why the Order Is Fixed ## Why the Order Is Fixed
The pipeline always runs in this order: The pipeline uses this order and only enters the lossy steps when necessary:
```text ```python
tool_result_budget messages = self.tool_result_budget(messages)
→ snip_compact messages = self.snip_compact(messages)
→ micro_compact if self.estimate_chars(messages) > self.CONTEXT_CHAR_LIMIT:
→ compact_history (only above the limit) messages = self.micro_compact(messages)
if self.estimate_chars(messages) > self.CONTEXT_CHAR_LIMIT:
messages = self.compact_history(messages, active_request)
``` ```
This order satisfies two constraints: This order satisfies two constraints:
1. The first three steps do not call the model. Only Step 4 adds an API request. 1. Steps 1 and 2 run every round. Step 3 runs only above the limit, and only Step 4 adds an API request.
2. `tool_result_budget` must run before `micro_compact`. Large results need to reach disk before older results can become placeholders. 2. `tool_result_budget` must run before `micro_compact`. Large results need to reach disk before older results can become placeholders.
Each round therefore starts with the lowest-cost operation whose information is easiest to recover. Each round therefore starts with the lowest-cost operation whose information is easiest to recover.
@@ -243,7 +245,7 @@ def agent_loop(messages, active_request):
raise raise
``` ```
Every model call enters through the same pipeline. After appending `query`, the CLI calls `agent_loop(history, query)`, so repeated compaction cannot lose the current request. The code asks for a summary only when the first three steps leave the context above the limit or when the API rejects it. Every model call enters through the same pipeline. After appending `query`, the CLI calls `agent_loop(history, query)`, so repeated compaction cannot lose the current request. The code asks for a summary only when `micro_compact` still leaves the context above the limit or when the API rejects it.
## The compact Tool ## The compact Tool

View File

@@ -117,7 +117,7 @@ messages = [*messages[:head_end], marker, *messages[tail_start:]]
## 第三步micro_compact ## 第三步micro_compact
`micro_compact` 会完整保留最近一次 assistant 响应之后新增的所有 `tool_result`,确保模型至少完整读取每条新结果一次。对于模型已经读取过的结果,它保留最近 3 条,并缩短其余超过 120 个字符的旧结果。已经转存的结果保留文件路径,其他结果只留下占位符: 前两步完成后,`prepare` 会估算剩余上下文的大小,只有超过 `CONTEXT_CHAR_LIMIT` 时才执行 `micro_compact``micro_compact` 会完整保留最近一次 assistant 响应之后新增的所有 `tool_result`,确保模型至少完整读取每条新结果一次。对于模型已经读取过的结果,它保留最近 3 条,并缩短其余超过 120 个字符的旧结果。已经转存的结果保留文件路径,其他结果只留下占位符:
![旧结果替换为占位符](images/micro-compact.svg) ![旧结果替换为占位符](images/micro-compact.svg)
@@ -142,12 +142,12 @@ for _, _, block in consumed[:-self.KEEP_RECENT_RESULTS]:
未转存的旧结果只保留占位符。第一步保存过的完整结果仍能通过路径读取,不会在第三步丢失位置。 未转存的旧结果只保留占位符。第一步保存过的完整结果仍能通过路径读取,不会在第三步丢失位置。
前三步都是确定性的结构和文本操作,不产生额外 API 调用。 两步每轮都会执行,第三步只在上下文超限时执行。三步都是确定性的结构和文本操作,不产生额外 API 调用。
## 第四步compact_history ## 第四步compact_history
前三步执行后,代码用 `estimate_chars(messages)` 计算当前消息的字符数 `micro_compact` 执行后,代码会再次`estimate_chars(messages)` 估算上下文
```python ```python
CONTEXT_CHAR_LIMIT = 50000 CONTEXT_CHAR_LIMIT = 50000
@@ -156,7 +156,7 @@ def estimate_chars(messages):
return len(json.dumps(messages, default=str, ensure_ascii=False)) return len(json.dumps(messages, default=str, ensure_ascii=False))
``` ```
字符数超过 `CONTEXT_CHAR_LIMIT` 时,`compact_history` 完成四件事: 字符数仍然超过 `CONTEXT_CHAR_LIMIT` 时,`compact_history` 完成四件事:
1. 将完整消息历史写入 `.transcripts/` 1. 将完整消息历史写入 `.transcripts/`
2. 请求模型生成只包含事实的状态摘要。 2. 请求模型生成只包含事实的状态摘要。
@@ -181,18 +181,20 @@ def compact_history(messages, active_request):
## 为什么顺序固定 ## 为什么顺序固定
四步管线的执行顺序是 管线按以下顺序执行,并且只在必要时进入有损压缩步骤
```text ```python
tool_result_budget messages = self.tool_result_budget(messages)
→ snip_compact messages = self.snip_compact(messages)
→ micro_compact if self.estimate_chars(messages) > self.CONTEXT_CHAR_LIMIT:
→ compact_history超过阈值时 messages = self.micro_compact(messages)
if self.estimate_chars(messages) > self.CONTEXT_CHAR_LIMIT:
messages = self.compact_history(messages, active_request)
``` ```
这个顺序同时满足两个条件: 这个顺序同时满足两个条件:
1. 前三步不调用模型,第四步才产生额外 API 请求。 1. 第一步和第二步每轮执行,第三步只在超限时执行,只有第四步会增加 API 请求。
2. `tool_result_budget` 必须早于 `micro_compact`。大结果先落盘,之后才允许旧结果变成占位符。 2. `tool_result_budget` 必须早于 `micro_compact`。大结果先落盘,之后才允许旧结果变成占位符。
顺序固定后,每一轮都从成本更低、信息更容易恢复的操作开始。 顺序固定后,每一轮都从成本更低、信息更容易恢复的操作开始。
@@ -243,7 +245,7 @@ def agent_loop(messages, active_request):
raise raise
``` ```
每次调用模型前都会经过同一条管线。CLI 在追加 `query` 后调用 `agent_loop(history, query)`,所以压缩多少次都不会丢失本轮请求。前三步处理后仍超过阈值,或者 API 明确拒绝上下文时,代码才会请求模型生成摘要。 每次调用模型前都会经过同一条管线。CLI 在追加 `query` 后调用 `agent_loop(history, query)`,所以压缩多少次都不会丢失本轮请求。只有 `micro_compact` 处理后仍超过阈值,或者 API 明确拒绝上下文时,代码才会请求模型生成摘要。
## compact 工具 ## compact 工具

View File

@@ -11,18 +11,21 @@ s08_context_compact.py - Context Compact
v v
+--------------------+ +--------------------+
| snip_compact | archive the old middle -> .transcripts/ | snip_compact | archive the old middle -> .transcripts/
+--------------------+
|
v
+--------------------+
| micro_compact | shorten old tool results
+--------------------+ +--------------------+
| |
v v
context over limit? context over limit?
| no | yes | no | yes
v v | v
model call compact_history -> model call | +--------------------+
| | micro_compact | shorten old tool results
| +--------------------+
| |
| v
| still over limit?
| | no | yes
v v v
model call compact_history -> model call
Other entry points: Other entry points:
@@ -116,7 +119,7 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
def run_glob(pattern: str) -> str: def run_glob(pattern: str) -> str:
try: try:
matches = [ matches = [
match for match in glob.glob(pattern, root_dir=WORKDIR) match for match in glob.glob(pattern, root_dir=WORKDIR, recursive=True)
if (WORKDIR / match).resolve().is_relative_to(WORKDIR) if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
] ]
return "\n".join(matches) if matches else "(no matches)" return "\n".join(matches) if matches else "(no matches)"
@@ -419,10 +422,11 @@ class ContextCompactor:
def prepare(self, messages: list, active_request: str) -> list: def prepare(self, messages: list, active_request: str) -> list:
messages = self.tool_result_budget(messages) messages = self.tool_result_budget(messages)
messages = self.snip_compact(messages) messages = self.snip_compact(messages)
messages = self.micro_compact(messages)
if self.estimate_chars(messages) > self.CONTEXT_CHAR_LIMIT: if self.estimate_chars(messages) > self.CONTEXT_CHAR_LIMIT:
print("[auto compact]") messages = self.micro_compact(messages)
messages = self.compact_history(messages, active_request) if self.estimate_chars(messages) > self.CONTEXT_CHAR_LIMIT:
print("[auto compact]")
messages = self.compact_history(messages, active_request)
return messages return messages

View File

@@ -16,7 +16,7 @@
<!-- Trigger Condition --> <!-- Trigger Condition -->
<rect x="20" y="54" width="680" height="44" rx="6" fill="#fef2f2" stroke="#fca5a5" stroke-width="1"/> <rect x="20" y="54" width="680" height="44" rx="6" fill="#fef2f2" stroke="#fca5a5" stroke-width="1"/>
<text x="35" y="70" fill="#991b1b" font-size="11" font-weight="600">Trigger Condition</text> <text x="35" y="70" fill="#991b1b" font-size="11" font-weight="600">Trigger Condition</text>
<text x="140" y="70" fill="#991b1b" font-size="11">After Steps 13, estimate_chars(messages) &gt; CONTEXT_CHAR_LIMIT.</text> <text x="140" y="70" fill="#991b1b" font-size="11">After micro_compact, estimate_chars(messages) &gt; CONTEXT_CHAR_LIMIT.</text>
<text x="140" y="86" fill="#991b1b" font-size="10">The current CONTEXT_CHAR_LIMIT is 50,000 characters.</text> <text x="140" y="86" fill="#991b1b" font-size="10">The current CONTEXT_CHAR_LIMIT is 50,000 characters.</text>
<!-- Steps --> <!-- Steps -->

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -16,7 +16,7 @@
<!-- トリガー条件 --> <!-- トリガー条件 -->
<rect x="20" y="54" width="680" height="44" rx="6" fill="#fef2f2" stroke="#fca5a5" stroke-width="1"/> <rect x="20" y="54" width="680" height="44" rx="6" fill="#fef2f2" stroke="#fca5a5" stroke-width="1"/>
<text x="35" y="70" fill="#991b1b" font-size="11" font-weight="600">トリガー条件</text> <text x="35" y="70" fill="#991b1b" font-size="11" font-weight="600">トリガー条件</text>
<text x="115" y="70" fill="#991b1b" font-size="11">Step 13 の後、estimate_chars(messages) &gt; CONTEXT_CHAR_LIMIT。</text> <text x="115" y="70" fill="#991b1b" font-size="11">micro_compact の後、estimate_chars(messages) &gt; CONTEXT_CHAR_LIMIT。</text>
<text x="115" y="86" fill="#991b1b" font-size="10">現在の CONTEXT_CHAR_LIMIT は 50,000 文字。</text> <text x="115" y="86" fill="#991b1b" font-size="10">現在の CONTEXT_CHAR_LIMIT は 50,000 文字。</text>
<!-- ステップ --> <!-- ステップ -->

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -16,7 +16,7 @@
<!-- 触发条件 --> <!-- 触发条件 -->
<rect x="20" y="54" width="680" height="44" rx="6" fill="#fef2f2" stroke="#fca5a5" stroke-width="1"/> <rect x="20" y="54" width="680" height="44" rx="6" fill="#fef2f2" stroke="#fca5a5" stroke-width="1"/>
<text x="35" y="70" fill="#991b1b" font-size="11" font-weight="600">触发条件</text> <text x="35" y="70" fill="#991b1b" font-size="11" font-weight="600">触发条件</text>
<text x="105" y="70" fill="#991b1b" font-size="11">前三步执行estimate_chars(messages) &gt; CONTEXT_CHAR_LIMIT。</text> <text x="105" y="70" fill="#991b1b" font-size="11">micro_compact estimate_chars(messages) &gt; CONTEXT_CHAR_LIMIT。</text>
<text x="105" y="86" fill="#991b1b" font-size="10">当前实现的 CONTEXT_CHAR_LIMIT 为 50,000 个字符。</text> <text x="105" y="86" fill="#991b1b" font-size="10">当前实现的 CONTEXT_CHAR_LIMIT 为 50,000 个字符。</text>
<!-- 步骤 --> <!-- 步骤 -->

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -45,9 +45,9 @@
<rect x="170" y="82" width="200" height="252" rx="10" fill="#fffbeb" stroke="#d97706" stroke-width="2"/> <rect x="170" y="82" width="200" height="252" rx="10" fill="#fffbeb" stroke="#d97706" stroke-width="2"/>
<text x="270" y="102" fill="#92400e" font-size="11" font-weight="700" text-anchor="middle">Compression Pipeline</text> <text x="270" y="102" fill="#92400e" font-size="11" font-weight="700" text-anchor="middle">Compression Pipeline</text>
<!-- ── ① Every Turn Auto ── --> <!-- ── ① Pre-processing ── -->
<rect x="186" y="110" width="168" height="16" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="0.8"/> <rect x="186" y="110" width="168" height="16" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="0.8"/>
<text x="270" y="122" fill="#92400e" font-size="8" font-weight="700" text-anchor="middle">① Every Turn · Unconditional · 0 API</text> <text x="270" y="122" fill="#92400e" font-size="8" font-weight="700" text-anchor="middle">Steps 12 Every Turn · 0 API</text>
<rect x="186" y="130" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/> <rect x="186" y="130" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="270" y="146" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 1 tool_result_budget</text> <text x="270" y="146" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 1 tool_result_budget</text>
@@ -56,14 +56,14 @@
<text x="270" y="174" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 2 snip_compact</text> <text x="270" y="174" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 2 snip_compact</text>
<rect x="186" y="186" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/> <rect x="186" y="186" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="270" y="202" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 3 micro_compact</text> <text x="270" y="202" fill="#92400e" font-size="9" font-weight="600" text-anchor="middle">Step 3 micro_compact (over limit)</text>
<!-- ↓ → ◇ --> <!-- ↓ → ◇ -->
<line x1="270" y1="210" x2="270" y2="222" stroke="#555" stroke-width="1.2" marker-end="url(#arrow)"/> <line x1="270" y1="210" x2="270" y2="222" stroke="#555" stroke-width="1.2" marker-end="url(#arrow)"/>
<!-- ◇ Decision Diamond --> <!-- ◇ Decision Diamond -->
<polygon points="270,226 300,244 270,262 240,244" fill="#f0f4ff" stroke="#ea580c" stroke-width="1.5"/> <polygon points="270,226 300,244 270,262 240,244" fill="#f0f4ff" stroke="#ea580c" stroke-width="1.5"/>
<text x="270" y="247" fill="#9a3412" font-size="7" font-weight="600" text-anchor="middle">Over threshold?</text> <text x="270" y="247" fill="#9a3412" font-size="7" font-weight="600" text-anchor="middle">Still over?</text>
<!-- No: right annotation --> <!-- No: right annotation -->
<text x="306" y="240" fill="#16a34a" font-size="9" font-weight="700">No → Pass</text> <text x="306" y="240" fill="#16a34a" font-size="9" font-weight="700">No → Pass</text>
@@ -126,10 +126,10 @@
<text x="94" y="414" fill="#334155" font-size="10">Shared: loop, hooks, permissions, five base tools</text> <text x="94" y="414" fill="#334155" font-size="10">Shared: loop, hooks, permissions, five base tools</text>
<rect x="70" y="426" width="16" height="12" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="1"/> <rect x="70" y="426" width="16" height="12" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="1"/>
<text x="94" y="436" fill="#334155" font-size="10">Every Turn: Steps 1→2→3 run before each LLM call, 0 API</text> <text x="94" y="436" fill="#334155" font-size="10">Pre-process: Steps 1→2 every turn; Step 3 only over the limit, 0 API</text>
<rect x="70" y="448" width="16" height="12" rx="3" fill="#fed7aa" stroke="#ea580c" stroke-width="1"/> <rect x="70" y="448" width="16" height="12" rx="3" fill="#fed7aa" stroke="#ea580c" stroke-width="1"/>
<text x="94" y="458" fill="#334155" font-size="10">② Conditional: size remains over the limit after Step 3 → compact_history, 1 API</text> <text x="94" y="458" fill="#334155" font-size="10">② Conditional: still over the limit after Step 3 → compact_history, 1 API</text>
<rect x="70" y="470" width="16" height="12" rx="3" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="3,2"/> <rect x="70" y="470" width="16" height="12" rx="3" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="3,2"/>
<text x="94" y="480" fill="#334155" font-size="10">③ Recovery: API returns prompt_too_long → reactive_compact → retry once</text> <text x="94" y="480" fill="#334155" font-size="10">③ Recovery: API returns prompt_too_long → reactive_compact → retry once</text>

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@@ -45,9 +45,9 @@
<rect x="170" y="82" width="200" height="252" rx="10" fill="#fffbeb" stroke="#d97706" stroke-width="2"/> <rect x="170" y="82" width="200" height="252" rx="10" fill="#fffbeb" stroke="#d97706" stroke-width="2"/>
<text x="270" y="102" fill="#92400e" font-size="11" font-weight="700" text-anchor="middle">圧縮パイプライン</text> <text x="270" y="102" fill="#92400e" font-size="11" font-weight="700" text-anchor="middle">圧縮パイプライン</text>
<!-- ── ① 毎ターン自動 ── --> <!-- ── ① 前処理 ── -->
<rect x="186" y="110" width="168" height="16" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="0.8"/> <rect x="186" y="110" width="168" height="16" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="0.8"/>
<text x="270" y="122" fill="#92400e" font-size="8" font-weight="700" text-anchor="middle">① 毎ターン自動 · 無条件 · 0 API</text> <text x="270" y="122" fill="#92400e" font-size="8" font-weight="700" text-anchor="middle">Step 12 は毎ターン · 0 API</text>
<rect x="186" y="130" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/> <rect x="186" y="130" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="270" y="146" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 1 tool_result_budget</text> <text x="270" y="146" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 1 tool_result_budget</text>
@@ -56,14 +56,14 @@
<text x="270" y="174" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 2 snip_compact</text> <text x="270" y="174" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 2 snip_compact</text>
<rect x="186" y="186" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/> <rect x="186" y="186" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="270" y="202" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 3 micro_compact</text> <text x="270" y="202" fill="#92400e" font-size="9" font-weight="600" text-anchor="middle">Step 3 micro_compact(上限超過時)</text>
<!-- ↓ → ◇ --> <!-- ↓ → ◇ -->
<line x1="270" y1="210" x2="270" y2="222" stroke="#555" stroke-width="1.2" marker-end="url(#arrow)"/> <line x1="270" y1="210" x2="270" y2="222" stroke="#555" stroke-width="1.2" marker-end="url(#arrow)"/>
<!-- ◇ 判定ダイヤモンド --> <!-- ◇ 判定ダイヤモンド -->
<polygon points="270,226 300,244 270,262 240,244" fill="#f0f4ff" stroke="#ea580c" stroke-width="1.5"/> <polygon points="270,226 300,244 270,262 240,244" fill="#f0f4ff" stroke="#ea580c" stroke-width="1.5"/>
<text x="270" y="247" fill="#9a3412" font-size="7" font-weight="600" text-anchor="middle">推定値超過?</text> <text x="270" y="247" fill="#9a3412" font-size="7" font-weight="600" text-anchor="middle">まだ超過?</text>
<!-- いいえ:右側注釈 --> <!-- いいえ:右側注釈 -->
<text x="306" y="240" fill="#16a34a" font-size="9" font-weight="700">No → 通過</text> <text x="306" y="240" fill="#16a34a" font-size="9" font-weight="700">No → 通過</text>
@@ -126,10 +126,10 @@
<text x="94" y="414" fill="#334155" font-size="10">共通ループ、フック、権限確認、5 個の基本ツール</text> <text x="94" y="414" fill="#334155" font-size="10">共通ループ、フック、権限確認、5 個の基本ツール</text>
<rect x="70" y="426" width="16" height="12" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="1"/> <rect x="70" y="426" width="16" height="12" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="1"/>
<text x="94" y="436" fill="#334155" font-size="10">毎ターンStep 1→2→3 を各 LLM 呼び出し前に実行、0 API</text> <text x="94" y="436" fill="#334155" font-size="10">前処理Step 1→2 は毎ターン、Step 3 は上限超過時のみ、0 API</text>
<rect x="70" y="448" width="16" height="12" rx="3" fill="#fed7aa" stroke="#ea580c" stroke-width="1"/> <rect x="70" y="448" width="16" height="12" rx="3" fill="#fed7aa" stroke="#ea580c" stroke-width="1"/>
<text x="94" y="458" fill="#334155" font-size="10">② 条件Step 3 後もサイズ上限超過 → compact_history、1 API</text> <text x="94" y="458" fill="#334155" font-size="10">② 条件Step 3 後も上限超過 → compact_history、1 API</text>
<rect x="70" y="470" width="16" height="12" rx="3" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="3,2"/> <rect x="70" y="470" width="16" height="12" rx="3" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="3,2"/>
<text x="94" y="480" fill="#334155" font-size="10">③ 回復API が prompt_too_long を返す → reactive_compact → 1 回リトライ</text> <text x="94" y="480" fill="#334155" font-size="10">③ 回復API が prompt_too_long を返す → reactive_compact → 1 回リトライ</text>

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -45,9 +45,9 @@
<rect x="170" y="82" width="200" height="252" rx="10" fill="#fffbeb" stroke="#d97706" stroke-width="2"/> <rect x="170" y="82" width="200" height="252" rx="10" fill="#fffbeb" stroke="#d97706" stroke-width="2"/>
<text x="270" y="102" fill="#92400e" font-size="11" font-weight="700" text-anchor="middle">压缩管线</text> <text x="270" y="102" fill="#92400e" font-size="11" font-weight="700" text-anchor="middle">压缩管线</text>
<!-- ── ① 每轮自动 ── --> <!-- ── ① 预处理 ── -->
<rect x="186" y="110" width="168" height="16" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="0.8"/> <rect x="186" y="110" width="168" height="16" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="0.8"/>
<text x="270" y="122" fill="#92400e" font-size="8" font-weight="700" text-anchor="middle">① 每轮自动 · 无条件 · 0 API</text> <text x="270" y="122" fill="#92400e" font-size="8" font-weight="700" text-anchor="middle">Step 12 每轮 · 0 API</text>
<rect x="186" y="130" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/> <rect x="186" y="130" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="270" y="146" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 1 tool_result_budget</text> <text x="270" y="146" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 1 tool_result_budget</text>
@@ -56,14 +56,14 @@
<text x="270" y="174" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 2 snip_compact</text> <text x="270" y="174" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 2 snip_compact</text>
<rect x="186" y="186" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/> <rect x="186" y="186" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="270" y="202" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 3 micro_compact</text> <text x="270" y="202" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 3 micro_compact(超限时)</text>
<!-- ↓ → ◇ --> <!-- ↓ → ◇ -->
<line x1="270" y1="210" x2="270" y2="222" stroke="#555" stroke-width="1.2" marker-end="url(#arrow)"/> <line x1="270" y1="210" x2="270" y2="222" stroke="#555" stroke-width="1.2" marker-end="url(#arrow)"/>
<!-- ◇ 判断菱形(紧凑) --> <!-- ◇ 判断菱形(紧凑) -->
<polygon points="270,226 300,244 270,262 240,244" fill="#f0f4ff" stroke="#ea580c" stroke-width="1.5"/> <polygon points="270,226 300,244 270,262 240,244" fill="#f0f4ff" stroke="#ea580c" stroke-width="1.5"/>
<text x="270" y="247" fill="#9a3412" font-size="7" font-weight="600" text-anchor="middle">估算超限?</text> <text x="270" y="247" fill="#9a3412" font-size="7" font-weight="600" text-anchor="middle">超限?</text>
<!-- 否:右侧文字标注 --> <!-- 否:右侧文字标注 -->
<text x="306" y="240" fill="#16a34a" font-size="9" font-weight="700">否 → 通过</text> <text x="306" y="240" fill="#16a34a" font-size="9" font-weight="700">否 → 通过</text>
@@ -126,10 +126,10 @@
<text x="94" y="414" fill="#334155" font-size="10">共同骨架循环、hook、权限检查、5 个基础工具</text> <text x="94" y="414" fill="#334155" font-size="10">共同骨架循环、hook、权限检查、5 个基础工具</text>
<rect x="70" y="426" width="16" height="12" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="1"/> <rect x="70" y="426" width="16" height="12" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="1"/>
<text x="94" y="436" fill="#334155" font-size="10">每轮自动Step 1→2→3 在每次 LLM 调用前执行0 API</text> <text x="94" y="436" fill="#334155" font-size="10">预处理Step 1→2 每轮执行;超限时再执行 Step 30 API</text>
<rect x="70" y="448" width="16" height="12" rx="3" fill="#fed7aa" stroke="#ea580c" stroke-width="1"/> <rect x="70" y="448" width="16" height="12" rx="3" fill="#fed7aa" stroke="#ea580c" stroke-width="1"/>
<text x="94" y="458" fill="#334155" font-size="10">② 条件触发:前三步后 size 仍超阈值 → compact_history1 API</text> <text x="94" y="458" fill="#334155" font-size="10">② 条件触发:Step 3 后 size 仍超阈值 → compact_history1 API</text>
<rect x="70" y="470" width="16" height="12" rx="3" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="3,2"/> <rect x="70" y="470" width="16" height="12" rx="3" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="3,2"/>
<text x="94" y="480" fill="#334155" font-size="10">③ 异常触发API 返回 prompt_too_long → reactive_compact → 重试一次</text> <text x="94" y="480" fill="#334155" font-size="10">③ 异常触发API 返回 prompt_too_long → reactive_compact → 重试一次</text>

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -39,7 +39,7 @@
<!-- ===== Pre-processing pipeline title ===== --> <!-- ===== Pre-processing pipeline title ===== -->
<rect x="20" y="146" width="720" height="24" rx="4" fill="#f1f5f9"/> <rect x="20" y="146" width="720" height="24" rx="4" fill="#f1f5f9"/>
<text x="55" y="163" fill="#64748b" font-size="11" font-weight="600">Pre-processing (Step 1 → Step 2 → Step 3 before every LLM call, 0 API)</text> <text x="55" y="163" fill="#64748b" font-size="11" font-weight="600">Pre-processing (Steps 1 → 2 every turn; Step 3 only over the limit, 0 API)</text>
<!-- Step 1: tool_result_budget --> <!-- Step 1: tool_result_budget -->
<rect x="80" y="180" width="600" height="46" rx="7" fill="url(#pre)" stroke="#2563eb" stroke-width="1.5"/> <rect x="80" y="180" width="600" height="46" rx="7" fill="url(#pre)" stroke="#2563eb" stroke-width="1.5"/>
@@ -69,11 +69,11 @@
<text x="155" y="320" fill="#1e40af" font-size="13" font-weight="700">micro_compact</text> <text x="155" y="320" fill="#1e40af" font-size="13" font-weight="700">micro_compact</text>
<text x="260" y="320" fill="#1e40af" font-size="11">old tool_result → placeholder (keep latest 3)</text> <text x="260" y="320" fill="#1e40af" font-size="11">old tool_result → placeholder (keep latest 3)</text>
<text x="650" y="320" fill="#1e40af" font-size="10" text-anchor="end">compact old</text> <text x="650" y="320" fill="#1e40af" font-size="10" text-anchor="end">compact old</text>
<text x="155" y="338" fill="#2563eb" font-size="9">Runs every turn and keeps the latest 3 results complete</text> <text x="155" y="338" fill="#2563eb" font-size="9">Runs over the context limit and keeps the latest 3 results complete</text>
<!-- ===== Auto-compact title ===== --> <!-- ===== Auto-compact title ===== -->
<rect x="20" y="358" width="720" height="24" rx="4" fill="#f1f5f9"/> <rect x="20" y="358" width="720" height="24" rx="4" fill="#f1f5f9"/>
<text x="70" y="375" fill="#64748b" font-size="11" font-weight="600">Auto-compact Decision (triggered when pre-processing is insufficient, 1 API call)</text> <text x="70" y="375" fill="#64748b" font-size="11" font-weight="600">Auto-compact Decision (triggered when still over after Step 3, 1 API call)</text>
<!-- Step 4: compact_history --> <!-- Step 4: compact_history -->
<rect x="80" y="390" width="600" height="58" rx="7" fill="url(#auto)" stroke="#dc2626" stroke-width="2"/> <rect x="80" y="390" width="600" height="58" rx="7" fill="url(#auto)" stroke="#dc2626" stroke-width="2"/>

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -39,7 +39,7 @@
<!-- ===== 前処理パイプラインタイトル ===== --> <!-- ===== 前処理パイプラインタイトル ===== -->
<rect x="20" y="146" width="720" height="24" rx="4" fill="#f1f5f9"/> <rect x="20" y="146" width="720" height="24" rx="4" fill="#f1f5f9"/>
<text x="55" y="163" fill="#64748b" font-size="11" font-weight="600">前処理Step 1 → Step 2 → Step 3、各 LLM 呼び出し前、0 API</text> <text x="55" y="163" fill="#64748b" font-size="11" font-weight="600">前処理Step 1 → 2 は毎ターン、Step 3 は上限超過時のみ、0 API</text>
<!-- Step 1: tool_result_budget --> <!-- Step 1: tool_result_budget -->
<rect x="80" y="180" width="600" height="46" rx="7" fill="url(#pre)" stroke="#2563eb" stroke-width="1.5"/> <rect x="80" y="180" width="600" height="46" rx="7" fill="url(#pre)" stroke="#2563eb" stroke-width="1.5"/>
@@ -69,11 +69,11 @@
<text x="155" y="320" fill="#1e40af" font-size="13" font-weight="700">micro_compact</text> <text x="155" y="320" fill="#1e40af" font-size="13" font-weight="700">micro_compact</text>
<text x="260" y="320" fill="#1e40af" font-size="11">古い tool_result → プレースホルダー(最新 3 件保持)</text> <text x="260" y="320" fill="#1e40af" font-size="11">古い tool_result → プレースホルダー(最新 3 件保持)</text>
<text x="650" y="320" fill="#1e40af" font-size="10" text-anchor="end">旧結果を圧縮</text> <text x="650" y="320" fill="#1e40af" font-size="10" text-anchor="end">旧結果を圧縮</text>
<text x="155" y="338" fill="#2563eb" font-size="9">毎ターン実行し、最新 3 件は完全に保持</text> <text x="155" y="338" fill="#2563eb" font-size="9">上限超過時に実行し、最新 3 件は完全に保持</text>
<!-- ===== 自動圧縮タイトル ===== --> <!-- ===== 自動圧縮タイトル ===== -->
<rect x="20" y="358" width="720" height="24" rx="4" fill="#f1f5f9"/> <rect x="20" y="358" width="720" height="24" rx="4" fill="#f1f5f9"/>
<text x="70" y="375" fill="#64748b" font-size="11" font-weight="600">自動圧縮判定(前処理で不足時にトリガー、1 API 呼び出し)</text> <text x="70" y="375" fill="#64748b" font-size="11" font-weight="600">自動圧縮判定(Step 3 後も上限超過時にトリガー、1 API 呼び出し)</text>
<!-- Step 4: compact_history --> <!-- Step 4: compact_history -->
<rect x="80" y="390" width="600" height="58" rx="7" fill="url(#auto)" stroke="#dc2626" stroke-width="2"/> <rect x="80" y="390" width="600" height="58" rx="7" fill="url(#auto)" stroke="#dc2626" stroke-width="2"/>

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -39,7 +39,7 @@
<!-- ===== 预处理管线标题 ===== --> <!-- ===== 预处理管线标题 ===== -->
<rect x="20" y="146" width="720" height="24" rx="4" fill="#f1f5f9"/> <rect x="20" y="146" width="720" height="24" rx="4" fill="#f1f5f9"/>
<text x="55" y="163" fill="#64748b" font-size="11" font-weight="600">预处理管线(执行顺序:Step 1 → Step 2 → Step 3每轮调用前执行0 API</text> <text x="55" y="163" fill="#64748b" font-size="11" font-weight="600">预处理管线Step 1 → Step 2 每轮执行;超限时执行 Step 30 API</text>
<!-- Step 1: tool_result_budget --> <!-- Step 1: tool_result_budget -->
<rect x="80" y="180" width="600" height="46" rx="7" fill="url(#pre)" stroke="#2563eb" stroke-width="1.5"/> <rect x="80" y="180" width="600" height="46" rx="7" fill="url(#pre)" stroke="#2563eb" stroke-width="1.5"/>
@@ -69,11 +69,11 @@
<text x="155" y="320" fill="#1e40af" font-size="13" font-weight="700">micro_compact</text> <text x="155" y="320" fill="#1e40af" font-size="13" font-weight="700">micro_compact</text>
<text x="260" y="320" fill="#1e40af" font-size="11">旧 tool_result → 占位符(保留最近 3 条)</text> <text x="260" y="320" fill="#1e40af" font-size="11">旧 tool_result → 占位符(保留最近 3 条)</text>
<text x="650" y="320" fill="#1e40af" font-size="10" text-anchor="end">压旧结果</text> <text x="650" y="320" fill="#1e40af" font-size="10" text-anchor="end">压旧结果</text>
<text x="155" y="338" fill="#2563eb" font-size="9">每轮执行,最近 3 条结果保持完整</text> <text x="155" y="338" fill="#2563eb" font-size="9">上下文超限时执行,最近 3 条结果保持完整</text>
<!-- ===== 自动压缩标题 ===== --> <!-- ===== 自动压缩标题 ===== -->
<rect x="20" y="358" width="720" height="24" rx="4" fill="#f1f5f9"/> <rect x="20" y="358" width="720" height="24" rx="4" fill="#f1f5f9"/>
<text x="70" y="375" fill="#64748b" font-size="11" font-weight="600">自动压缩决策(预处理不够时触发1 API 调用)</text> <text x="70" y="375" fill="#64748b" font-size="11" font-weight="600">自动压缩决策(Step 3 后仍超限时触发1 API 调用)</text>
<!-- Step 4: compact_history --> <!-- Step 4: compact_history -->
<rect x="80" y="390" width="600" height="58" rx="7" fill="url(#auto)" stroke="#dc2626" stroke-width="2"/> <rect x="80" y="390" width="600" height="58" rx="7" fill="url(#auto)" stroke="#dc2626" stroke-width="2"/>

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -0,0 +1,110 @@
import runpy
import sys
import types
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
LESSON = ROOT / "s08_context_compact" / "code.py"
def load_lesson(monkeypatch, workdir: Path):
fake_anthropic = types.ModuleType("anthropic")
fake_dotenv = types.ModuleType("dotenv")
class FakeAnthropic:
def __init__(self, *args, **kwargs):
self.messages = types.SimpleNamespace(create=None)
fake_anthropic.Anthropic = FakeAnthropic
fake_dotenv.load_dotenv = lambda override=True: None
monkeypatch.setitem(sys.modules, "anthropic", fake_anthropic)
monkeypatch.setitem(sys.modules, "dotenv", fake_dotenv)
monkeypatch.setenv("MODEL_ID", "test-model")
monkeypatch.setenv("ANTHROPIC_API_KEY", "test-key")
monkeypatch.chdir(workdir)
return runpy.run_path(str(LESSON))
def test_glob_double_star_matches_files_at_any_depth(tmp_path, monkeypatch):
(tmp_path / "root.py").write_text("")
(tmp_path / "one").mkdir()
(tmp_path / "one" / "one.py").write_text("")
(tmp_path / "one" / "two").mkdir()
(tmp_path / "one" / "two" / "deep.py").write_text("")
lesson = load_lesson(monkeypatch, tmp_path)
matches = set(lesson["run_glob"]("**/*.py").splitlines())
assert matches == {"root.py", "one/one.py", "one/two/deep.py"}
def test_prepare_preserves_tool_results_while_context_is_within_limit(
tmp_path, monkeypatch):
lesson = load_lesson(monkeypatch, tmp_path)
messages = []
expected_results = []
for index in range(5):
tool_id = f"tool-{index}"
result = f"result-{index}:" + "x" * 200
expected_results.append(result)
messages.extend([
{"role": "assistant", "content": [
{"type": "tool_use", "id": tool_id, "name": "bash", "input": {}}
]},
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": tool_id, "content": result}
]},
])
messages.append({"role": "assistant", "content": [
{"type": "text", "text": "continue"}
]})
prepared = lesson["COMPACTOR"].prepare(messages, "inspect the repository")
actual_results = [
block["content"]
for message in prepared
if message["role"] == "user"
for block in message["content"]
if block["type"] == "tool_result"
]
assert actual_results == expected_results
def test_prepare_micro_compacts_tool_results_after_context_exceeds_limit(
tmp_path, monkeypatch):
lesson = load_lesson(monkeypatch, tmp_path)
messages = []
for index in range(5):
tool_id = f"tool-{index}"
messages.extend([
{"role": "assistant", "content": [
{"type": "tool_use", "id": tool_id, "name": "bash", "input": {}}
]},
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": tool_id,
"content": f"result-{index}:" + "x" * 1000}
]},
])
messages.append({"role": "assistant", "content": [
{"type": "text", "text": "continue"}
]})
compactor = lesson["COMPACTOR"]
compactor.CONTEXT_CHAR_LIMIT = 4500
prepared = compactor.prepare(messages, "inspect the repository")
actual_results = [
block["content"]
for message in prepared
if message["role"] == "user"
for block in message["content"]
if block["type"] == "tool_result"
]
assert actual_results[:2] == [
"[Earlier tool result omitted.]",
"[Earlier tool result omitted.]",
]
assert all(result.startswith(f"result-{index}:")
for index, result in enumerate(actual_results[2:], start=2))

View File

@@ -16,7 +16,7 @@
<!-- Trigger Condition --> <!-- Trigger Condition -->
<rect x="20" y="54" width="680" height="44" rx="6" fill="#fef2f2" stroke="#fca5a5" stroke-width="1"/> <rect x="20" y="54" width="680" height="44" rx="6" fill="#fef2f2" stroke="#fca5a5" stroke-width="1"/>
<text x="35" y="70" fill="#991b1b" font-size="11" font-weight="600">Trigger Condition</text> <text x="35" y="70" fill="#991b1b" font-size="11" font-weight="600">Trigger Condition</text>
<text x="140" y="70" fill="#991b1b" font-size="11">After Steps 13, estimate_chars(messages) &gt; CONTEXT_CHAR_LIMIT.</text> <text x="140" y="70" fill="#991b1b" font-size="11">After micro_compact, estimate_chars(messages) &gt; CONTEXT_CHAR_LIMIT.</text>
<text x="140" y="86" fill="#991b1b" font-size="10">The current CONTEXT_CHAR_LIMIT is 50,000 characters.</text> <text x="140" y="86" fill="#991b1b" font-size="10">The current CONTEXT_CHAR_LIMIT is 50,000 characters.</text>
<!-- Steps --> <!-- Steps -->

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -16,7 +16,7 @@
<!-- トリガー条件 --> <!-- トリガー条件 -->
<rect x="20" y="54" width="680" height="44" rx="6" fill="#fef2f2" stroke="#fca5a5" stroke-width="1"/> <rect x="20" y="54" width="680" height="44" rx="6" fill="#fef2f2" stroke="#fca5a5" stroke-width="1"/>
<text x="35" y="70" fill="#991b1b" font-size="11" font-weight="600">トリガー条件</text> <text x="35" y="70" fill="#991b1b" font-size="11" font-weight="600">トリガー条件</text>
<text x="115" y="70" fill="#991b1b" font-size="11">Step 13 の後、estimate_chars(messages) &gt; CONTEXT_CHAR_LIMIT。</text> <text x="115" y="70" fill="#991b1b" font-size="11">micro_compact の後、estimate_chars(messages) &gt; CONTEXT_CHAR_LIMIT。</text>
<text x="115" y="86" fill="#991b1b" font-size="10">現在の CONTEXT_CHAR_LIMIT は 50,000 文字。</text> <text x="115" y="86" fill="#991b1b" font-size="10">現在の CONTEXT_CHAR_LIMIT は 50,000 文字。</text>
<!-- ステップ --> <!-- ステップ -->

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -16,7 +16,7 @@
<!-- 触发条件 --> <!-- 触发条件 -->
<rect x="20" y="54" width="680" height="44" rx="6" fill="#fef2f2" stroke="#fca5a5" stroke-width="1"/> <rect x="20" y="54" width="680" height="44" rx="6" fill="#fef2f2" stroke="#fca5a5" stroke-width="1"/>
<text x="35" y="70" fill="#991b1b" font-size="11" font-weight="600">触发条件</text> <text x="35" y="70" fill="#991b1b" font-size="11" font-weight="600">触发条件</text>
<text x="105" y="70" fill="#991b1b" font-size="11">前三步执行estimate_chars(messages) &gt; CONTEXT_CHAR_LIMIT。</text> <text x="105" y="70" fill="#991b1b" font-size="11">micro_compact estimate_chars(messages) &gt; CONTEXT_CHAR_LIMIT。</text>
<text x="105" y="86" fill="#991b1b" font-size="10">当前实现的 CONTEXT_CHAR_LIMIT 为 50,000 个字符。</text> <text x="105" y="86" fill="#991b1b" font-size="10">当前实现的 CONTEXT_CHAR_LIMIT 为 50,000 个字符。</text>
<!-- 步骤 --> <!-- 步骤 -->

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -45,9 +45,9 @@
<rect x="170" y="82" width="200" height="252" rx="10" fill="#fffbeb" stroke="#d97706" stroke-width="2"/> <rect x="170" y="82" width="200" height="252" rx="10" fill="#fffbeb" stroke="#d97706" stroke-width="2"/>
<text x="270" y="102" fill="#92400e" font-size="11" font-weight="700" text-anchor="middle">Compression Pipeline</text> <text x="270" y="102" fill="#92400e" font-size="11" font-weight="700" text-anchor="middle">Compression Pipeline</text>
<!-- ── ① Every Turn Auto ── --> <!-- ── ① Pre-processing ── -->
<rect x="186" y="110" width="168" height="16" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="0.8"/> <rect x="186" y="110" width="168" height="16" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="0.8"/>
<text x="270" y="122" fill="#92400e" font-size="8" font-weight="700" text-anchor="middle">① Every Turn · Unconditional · 0 API</text> <text x="270" y="122" fill="#92400e" font-size="8" font-weight="700" text-anchor="middle">Steps 12 Every Turn · 0 API</text>
<rect x="186" y="130" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/> <rect x="186" y="130" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="270" y="146" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 1 tool_result_budget</text> <text x="270" y="146" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 1 tool_result_budget</text>
@@ -56,14 +56,14 @@
<text x="270" y="174" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 2 snip_compact</text> <text x="270" y="174" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 2 snip_compact</text>
<rect x="186" y="186" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/> <rect x="186" y="186" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="270" y="202" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 3 micro_compact</text> <text x="270" y="202" fill="#92400e" font-size="9" font-weight="600" text-anchor="middle">Step 3 micro_compact (over limit)</text>
<!-- ↓ → ◇ --> <!-- ↓ → ◇ -->
<line x1="270" y1="210" x2="270" y2="222" stroke="#555" stroke-width="1.2" marker-end="url(#arrow)"/> <line x1="270" y1="210" x2="270" y2="222" stroke="#555" stroke-width="1.2" marker-end="url(#arrow)"/>
<!-- ◇ Decision Diamond --> <!-- ◇ Decision Diamond -->
<polygon points="270,226 300,244 270,262 240,244" fill="#f0f4ff" stroke="#ea580c" stroke-width="1.5"/> <polygon points="270,226 300,244 270,262 240,244" fill="#f0f4ff" stroke="#ea580c" stroke-width="1.5"/>
<text x="270" y="247" fill="#9a3412" font-size="7" font-weight="600" text-anchor="middle">Over threshold?</text> <text x="270" y="247" fill="#9a3412" font-size="7" font-weight="600" text-anchor="middle">Still over?</text>
<!-- No: right annotation --> <!-- No: right annotation -->
<text x="306" y="240" fill="#16a34a" font-size="9" font-weight="700">No → Pass</text> <text x="306" y="240" fill="#16a34a" font-size="9" font-weight="700">No → Pass</text>
@@ -126,10 +126,10 @@
<text x="94" y="414" fill="#334155" font-size="10">Shared: loop, hooks, permissions, five base tools</text> <text x="94" y="414" fill="#334155" font-size="10">Shared: loop, hooks, permissions, five base tools</text>
<rect x="70" y="426" width="16" height="12" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="1"/> <rect x="70" y="426" width="16" height="12" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="1"/>
<text x="94" y="436" fill="#334155" font-size="10">Every Turn: Steps 1→2→3 run before each LLM call, 0 API</text> <text x="94" y="436" fill="#334155" font-size="10">Pre-process: Steps 1→2 every turn; Step 3 only over the limit, 0 API</text>
<rect x="70" y="448" width="16" height="12" rx="3" fill="#fed7aa" stroke="#ea580c" stroke-width="1"/> <rect x="70" y="448" width="16" height="12" rx="3" fill="#fed7aa" stroke="#ea580c" stroke-width="1"/>
<text x="94" y="458" fill="#334155" font-size="10">② Conditional: size remains over the limit after Step 3 → compact_history, 1 API</text> <text x="94" y="458" fill="#334155" font-size="10">② Conditional: still over the limit after Step 3 → compact_history, 1 API</text>
<rect x="70" y="470" width="16" height="12" rx="3" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="3,2"/> <rect x="70" y="470" width="16" height="12" rx="3" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="3,2"/>
<text x="94" y="480" fill="#334155" font-size="10">③ Recovery: API returns prompt_too_long → reactive_compact → retry once</text> <text x="94" y="480" fill="#334155" font-size="10">③ Recovery: API returns prompt_too_long → reactive_compact → retry once</text>

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@@ -45,9 +45,9 @@
<rect x="170" y="82" width="200" height="252" rx="10" fill="#fffbeb" stroke="#d97706" stroke-width="2"/> <rect x="170" y="82" width="200" height="252" rx="10" fill="#fffbeb" stroke="#d97706" stroke-width="2"/>
<text x="270" y="102" fill="#92400e" font-size="11" font-weight="700" text-anchor="middle">圧縮パイプライン</text> <text x="270" y="102" fill="#92400e" font-size="11" font-weight="700" text-anchor="middle">圧縮パイプライン</text>
<!-- ── ① 毎ターン自動 ── --> <!-- ── ① 前処理 ── -->
<rect x="186" y="110" width="168" height="16" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="0.8"/> <rect x="186" y="110" width="168" height="16" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="0.8"/>
<text x="270" y="122" fill="#92400e" font-size="8" font-weight="700" text-anchor="middle">① 毎ターン自動 · 無条件 · 0 API</text> <text x="270" y="122" fill="#92400e" font-size="8" font-weight="700" text-anchor="middle">Step 12 は毎ターン · 0 API</text>
<rect x="186" y="130" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/> <rect x="186" y="130" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="270" y="146" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 1 tool_result_budget</text> <text x="270" y="146" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 1 tool_result_budget</text>
@@ -56,14 +56,14 @@
<text x="270" y="174" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 2 snip_compact</text> <text x="270" y="174" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 2 snip_compact</text>
<rect x="186" y="186" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/> <rect x="186" y="186" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="270" y="202" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 3 micro_compact</text> <text x="270" y="202" fill="#92400e" font-size="9" font-weight="600" text-anchor="middle">Step 3 micro_compact(上限超過時)</text>
<!-- ↓ → ◇ --> <!-- ↓ → ◇ -->
<line x1="270" y1="210" x2="270" y2="222" stroke="#555" stroke-width="1.2" marker-end="url(#arrow)"/> <line x1="270" y1="210" x2="270" y2="222" stroke="#555" stroke-width="1.2" marker-end="url(#arrow)"/>
<!-- ◇ 判定ダイヤモンド --> <!-- ◇ 判定ダイヤモンド -->
<polygon points="270,226 300,244 270,262 240,244" fill="#f0f4ff" stroke="#ea580c" stroke-width="1.5"/> <polygon points="270,226 300,244 270,262 240,244" fill="#f0f4ff" stroke="#ea580c" stroke-width="1.5"/>
<text x="270" y="247" fill="#9a3412" font-size="7" font-weight="600" text-anchor="middle">推定値超過?</text> <text x="270" y="247" fill="#9a3412" font-size="7" font-weight="600" text-anchor="middle">まだ超過?</text>
<!-- いいえ:右側注釈 --> <!-- いいえ:右側注釈 -->
<text x="306" y="240" fill="#16a34a" font-size="9" font-weight="700">No → 通過</text> <text x="306" y="240" fill="#16a34a" font-size="9" font-weight="700">No → 通過</text>
@@ -126,10 +126,10 @@
<text x="94" y="414" fill="#334155" font-size="10">共通ループ、フック、権限確認、5 個の基本ツール</text> <text x="94" y="414" fill="#334155" font-size="10">共通ループ、フック、権限確認、5 個の基本ツール</text>
<rect x="70" y="426" width="16" height="12" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="1"/> <rect x="70" y="426" width="16" height="12" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="1"/>
<text x="94" y="436" fill="#334155" font-size="10">毎ターンStep 1→2→3 を各 LLM 呼び出し前に実行、0 API</text> <text x="94" y="436" fill="#334155" font-size="10">前処理Step 1→2 は毎ターン、Step 3 は上限超過時のみ、0 API</text>
<rect x="70" y="448" width="16" height="12" rx="3" fill="#fed7aa" stroke="#ea580c" stroke-width="1"/> <rect x="70" y="448" width="16" height="12" rx="3" fill="#fed7aa" stroke="#ea580c" stroke-width="1"/>
<text x="94" y="458" fill="#334155" font-size="10">② 条件Step 3 後もサイズ上限超過 → compact_history、1 API</text> <text x="94" y="458" fill="#334155" font-size="10">② 条件Step 3 後も上限超過 → compact_history、1 API</text>
<rect x="70" y="470" width="16" height="12" rx="3" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="3,2"/> <rect x="70" y="470" width="16" height="12" rx="3" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="3,2"/>
<text x="94" y="480" fill="#334155" font-size="10">③ 回復API が prompt_too_long を返す → reactive_compact → 1 回リトライ</text> <text x="94" y="480" fill="#334155" font-size="10">③ 回復API が prompt_too_long を返す → reactive_compact → 1 回リトライ</text>

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -45,9 +45,9 @@
<rect x="170" y="82" width="200" height="252" rx="10" fill="#fffbeb" stroke="#d97706" stroke-width="2"/> <rect x="170" y="82" width="200" height="252" rx="10" fill="#fffbeb" stroke="#d97706" stroke-width="2"/>
<text x="270" y="102" fill="#92400e" font-size="11" font-weight="700" text-anchor="middle">压缩管线</text> <text x="270" y="102" fill="#92400e" font-size="11" font-weight="700" text-anchor="middle">压缩管线</text>
<!-- ── ① 每轮自动 ── --> <!-- ── ① 预处理 ── -->
<rect x="186" y="110" width="168" height="16" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="0.8"/> <rect x="186" y="110" width="168" height="16" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="0.8"/>
<text x="270" y="122" fill="#92400e" font-size="8" font-weight="700" text-anchor="middle">① 每轮自动 · 无条件 · 0 API</text> <text x="270" y="122" fill="#92400e" font-size="8" font-weight="700" text-anchor="middle">Step 12 每轮 · 0 API</text>
<rect x="186" y="130" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/> <rect x="186" y="130" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="270" y="146" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 1 tool_result_budget</text> <text x="270" y="146" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 1 tool_result_budget</text>
@@ -56,14 +56,14 @@
<text x="270" y="174" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 2 snip_compact</text> <text x="270" y="174" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 2 snip_compact</text>
<rect x="186" y="186" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/> <rect x="186" y="186" width="168" height="24" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="270" y="202" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 3 micro_compact</text> <text x="270" y="202" fill="#92400e" font-size="10" font-weight="600" text-anchor="middle">Step 3 micro_compact(超限时)</text>
<!-- ↓ → ◇ --> <!-- ↓ → ◇ -->
<line x1="270" y1="210" x2="270" y2="222" stroke="#555" stroke-width="1.2" marker-end="url(#arrow)"/> <line x1="270" y1="210" x2="270" y2="222" stroke="#555" stroke-width="1.2" marker-end="url(#arrow)"/>
<!-- ◇ 判断菱形(紧凑) --> <!-- ◇ 判断菱形(紧凑) -->
<polygon points="270,226 300,244 270,262 240,244" fill="#f0f4ff" stroke="#ea580c" stroke-width="1.5"/> <polygon points="270,226 300,244 270,262 240,244" fill="#f0f4ff" stroke="#ea580c" stroke-width="1.5"/>
<text x="270" y="247" fill="#9a3412" font-size="7" font-weight="600" text-anchor="middle">估算超限?</text> <text x="270" y="247" fill="#9a3412" font-size="7" font-weight="600" text-anchor="middle">超限?</text>
<!-- 否:右侧文字标注 --> <!-- 否:右侧文字标注 -->
<text x="306" y="240" fill="#16a34a" font-size="9" font-weight="700">否 → 通过</text> <text x="306" y="240" fill="#16a34a" font-size="9" font-weight="700">否 → 通过</text>
@@ -126,10 +126,10 @@
<text x="94" y="414" fill="#334155" font-size="10">共同骨架循环、hook、权限检查、5 个基础工具</text> <text x="94" y="414" fill="#334155" font-size="10">共同骨架循环、hook、权限检查、5 个基础工具</text>
<rect x="70" y="426" width="16" height="12" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="1"/> <rect x="70" y="426" width="16" height="12" rx="3" fill="#fde68a" stroke="#d97706" stroke-width="1"/>
<text x="94" y="436" fill="#334155" font-size="10">每轮自动Step 1→2→3 在每次 LLM 调用前执行0 API</text> <text x="94" y="436" fill="#334155" font-size="10">预处理Step 1→2 每轮执行;超限时再执行 Step 30 API</text>
<rect x="70" y="448" width="16" height="12" rx="3" fill="#fed7aa" stroke="#ea580c" stroke-width="1"/> <rect x="70" y="448" width="16" height="12" rx="3" fill="#fed7aa" stroke="#ea580c" stroke-width="1"/>
<text x="94" y="458" fill="#334155" font-size="10">② 条件触发:前三步后 size 仍超阈值 → compact_history1 API</text> <text x="94" y="458" fill="#334155" font-size="10">② 条件触发:Step 3 后 size 仍超阈值 → compact_history1 API</text>
<rect x="70" y="470" width="16" height="12" rx="3" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="3,2"/> <rect x="70" y="470" width="16" height="12" rx="3" fill="#fef2f2" stroke="#dc2626" stroke-width="1" stroke-dasharray="3,2"/>
<text x="94" y="480" fill="#334155" font-size="10">③ 异常触发API 返回 prompt_too_long → reactive_compact → 重试一次</text> <text x="94" y="480" fill="#334155" font-size="10">③ 异常触发API 返回 prompt_too_long → reactive_compact → 重试一次</text>

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -39,7 +39,7 @@
<!-- ===== Pre-processing pipeline title ===== --> <!-- ===== Pre-processing pipeline title ===== -->
<rect x="20" y="146" width="720" height="24" rx="4" fill="#f1f5f9"/> <rect x="20" y="146" width="720" height="24" rx="4" fill="#f1f5f9"/>
<text x="55" y="163" fill="#64748b" font-size="11" font-weight="600">Pre-processing (Step 1 → Step 2 → Step 3 before every LLM call, 0 API)</text> <text x="55" y="163" fill="#64748b" font-size="11" font-weight="600">Pre-processing (Steps 1 → 2 every turn; Step 3 only over the limit, 0 API)</text>
<!-- Step 1: tool_result_budget --> <!-- Step 1: tool_result_budget -->
<rect x="80" y="180" width="600" height="46" rx="7" fill="url(#pre)" stroke="#2563eb" stroke-width="1.5"/> <rect x="80" y="180" width="600" height="46" rx="7" fill="url(#pre)" stroke="#2563eb" stroke-width="1.5"/>
@@ -69,11 +69,11 @@
<text x="155" y="320" fill="#1e40af" font-size="13" font-weight="700">micro_compact</text> <text x="155" y="320" fill="#1e40af" font-size="13" font-weight="700">micro_compact</text>
<text x="260" y="320" fill="#1e40af" font-size="11">old tool_result → placeholder (keep latest 3)</text> <text x="260" y="320" fill="#1e40af" font-size="11">old tool_result → placeholder (keep latest 3)</text>
<text x="650" y="320" fill="#1e40af" font-size="10" text-anchor="end">compact old</text> <text x="650" y="320" fill="#1e40af" font-size="10" text-anchor="end">compact old</text>
<text x="155" y="338" fill="#2563eb" font-size="9">Runs every turn and keeps the latest 3 results complete</text> <text x="155" y="338" fill="#2563eb" font-size="9">Runs over the context limit and keeps the latest 3 results complete</text>
<!-- ===== Auto-compact title ===== --> <!-- ===== Auto-compact title ===== -->
<rect x="20" y="358" width="720" height="24" rx="4" fill="#f1f5f9"/> <rect x="20" y="358" width="720" height="24" rx="4" fill="#f1f5f9"/>
<text x="70" y="375" fill="#64748b" font-size="11" font-weight="600">Auto-compact Decision (triggered when pre-processing is insufficient, 1 API call)</text> <text x="70" y="375" fill="#64748b" font-size="11" font-weight="600">Auto-compact Decision (triggered when still over after Step 3, 1 API call)</text>
<!-- Step 4: compact_history --> <!-- Step 4: compact_history -->
<rect x="80" y="390" width="600" height="58" rx="7" fill="url(#auto)" stroke="#dc2626" stroke-width="2"/> <rect x="80" y="390" width="600" height="58" rx="7" fill="url(#auto)" stroke="#dc2626" stroke-width="2"/>

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -39,7 +39,7 @@
<!-- ===== 前処理パイプラインタイトル ===== --> <!-- ===== 前処理パイプラインタイトル ===== -->
<rect x="20" y="146" width="720" height="24" rx="4" fill="#f1f5f9"/> <rect x="20" y="146" width="720" height="24" rx="4" fill="#f1f5f9"/>
<text x="55" y="163" fill="#64748b" font-size="11" font-weight="600">前処理Step 1 → Step 2 → Step 3、各 LLM 呼び出し前、0 API</text> <text x="55" y="163" fill="#64748b" font-size="11" font-weight="600">前処理Step 1 → 2 は毎ターン、Step 3 は上限超過時のみ、0 API</text>
<!-- Step 1: tool_result_budget --> <!-- Step 1: tool_result_budget -->
<rect x="80" y="180" width="600" height="46" rx="7" fill="url(#pre)" stroke="#2563eb" stroke-width="1.5"/> <rect x="80" y="180" width="600" height="46" rx="7" fill="url(#pre)" stroke="#2563eb" stroke-width="1.5"/>
@@ -69,11 +69,11 @@
<text x="155" y="320" fill="#1e40af" font-size="13" font-weight="700">micro_compact</text> <text x="155" y="320" fill="#1e40af" font-size="13" font-weight="700">micro_compact</text>
<text x="260" y="320" fill="#1e40af" font-size="11">古い tool_result → プレースホルダー(最新 3 件保持)</text> <text x="260" y="320" fill="#1e40af" font-size="11">古い tool_result → プレースホルダー(最新 3 件保持)</text>
<text x="650" y="320" fill="#1e40af" font-size="10" text-anchor="end">旧結果を圧縮</text> <text x="650" y="320" fill="#1e40af" font-size="10" text-anchor="end">旧結果を圧縮</text>
<text x="155" y="338" fill="#2563eb" font-size="9">毎ターン実行し、最新 3 件は完全に保持</text> <text x="155" y="338" fill="#2563eb" font-size="9">上限超過時に実行し、最新 3 件は完全に保持</text>
<!-- ===== 自動圧縮タイトル ===== --> <!-- ===== 自動圧縮タイトル ===== -->
<rect x="20" y="358" width="720" height="24" rx="4" fill="#f1f5f9"/> <rect x="20" y="358" width="720" height="24" rx="4" fill="#f1f5f9"/>
<text x="70" y="375" fill="#64748b" font-size="11" font-weight="600">自動圧縮判定(前処理で不足時にトリガー、1 API 呼び出し)</text> <text x="70" y="375" fill="#64748b" font-size="11" font-weight="600">自動圧縮判定(Step 3 後も上限超過時にトリガー、1 API 呼び出し)</text>
<!-- Step 4: compact_history --> <!-- Step 4: compact_history -->
<rect x="80" y="390" width="600" height="58" rx="7" fill="url(#auto)" stroke="#dc2626" stroke-width="2"/> <rect x="80" y="390" width="600" height="58" rx="7" fill="url(#auto)" stroke="#dc2626" stroke-width="2"/>

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -39,7 +39,7 @@
<!-- ===== 预处理管线标题 ===== --> <!-- ===== 预处理管线标题 ===== -->
<rect x="20" y="146" width="720" height="24" rx="4" fill="#f1f5f9"/> <rect x="20" y="146" width="720" height="24" rx="4" fill="#f1f5f9"/>
<text x="55" y="163" fill="#64748b" font-size="11" font-weight="600">预处理管线(执行顺序:Step 1 → Step 2 → Step 3每轮调用前执行0 API</text> <text x="55" y="163" fill="#64748b" font-size="11" font-weight="600">预处理管线Step 1 → Step 2 每轮执行;超限时执行 Step 30 API</text>
<!-- Step 1: tool_result_budget --> <!-- Step 1: tool_result_budget -->
<rect x="80" y="180" width="600" height="46" rx="7" fill="url(#pre)" stroke="#2563eb" stroke-width="1.5"/> <rect x="80" y="180" width="600" height="46" rx="7" fill="url(#pre)" stroke="#2563eb" stroke-width="1.5"/>
@@ -69,11 +69,11 @@
<text x="155" y="320" fill="#1e40af" font-size="13" font-weight="700">micro_compact</text> <text x="155" y="320" fill="#1e40af" font-size="13" font-weight="700">micro_compact</text>
<text x="260" y="320" fill="#1e40af" font-size="11">旧 tool_result → 占位符(保留最近 3 条)</text> <text x="260" y="320" fill="#1e40af" font-size="11">旧 tool_result → 占位符(保留最近 3 条)</text>
<text x="650" y="320" fill="#1e40af" font-size="10" text-anchor="end">压旧结果</text> <text x="650" y="320" fill="#1e40af" font-size="10" text-anchor="end">压旧结果</text>
<text x="155" y="338" fill="#2563eb" font-size="9">每轮执行,最近 3 条结果保持完整</text> <text x="155" y="338" fill="#2563eb" font-size="9">上下文超限时执行,最近 3 条结果保持完整</text>
<!-- ===== 自动压缩标题 ===== --> <!-- ===== 自动压缩标题 ===== -->
<rect x="20" y="358" width="720" height="24" rx="4" fill="#f1f5f9"/> <rect x="20" y="358" width="720" height="24" rx="4" fill="#f1f5f9"/>
<text x="70" y="375" fill="#64748b" font-size="11" font-weight="600">自动压缩决策(预处理不够时触发1 API 调用)</text> <text x="70" y="375" fill="#64748b" font-size="11" font-weight="600">自动压缩决策(Step 3 后仍超限时触发1 API 调用)</text>
<!-- Step 4: compact_history --> <!-- Step 4: compact_history -->
<rect x="80" y="390" width="600" height="58" rx="7" fill="url(#auto)" stroke="#dc2626" stroke-width="2"/> <rect x="80" y="390" width="600" height="58" rx="7" fill="url(#auto)" stroke="#dc2626" stroke-width="2"/>

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long