summaryrefslogtreecommitdiff
path: root/docs/tools-schema.md
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-08-02 16:18:29 +0900
committertanaka takashi <isazaka@gmail.com>2026-08-02 16:18:29 +0900
commitaf1da765acafec4d7468a79d462427f4245b44db (patch)
tree994c95c506f91d9dd23fd39ed2be69a4d2d79b4e /docs/tools-schema.md
parent515f96a7779f117a7e37f854de2ded99160d0c76 (diff)
fix(tools): search_and_fetch のページングトークン破損バグを修正
## 背景 search_and_fetch の next_token / continue_from によるページング継続が PDF 形式で間欠的に失敗するバグが報告された。発生エラー: 1. payload is not valid JSON: Expecting ':' delimiter 2. 'utf-8' codec can't decode byte 0xda in position 278 1ページ目の取得自体は成功し、next_token 返却まで正常だが、 next_token を continue_from に渡した2ページ目でエラーが発生。 txt/md/csv/json では安定動作し、PDF で間欠的・再現性なし。 ## 根本原因 コード精査の結果、報告の「非UTF-8バイト混入」仮説に対し、より有力な 主因として「トークン巨大化による末尾欠損」を特定: - _render_file_page() は matched 配列(title/href/body 全部入り)を トークンに埋め込んでいたが、レンダリングで使うのは title と href のみ。body(DuckDuckGo のスニペット、PDF 論文要約で数KB)は未使用 - 候補複数件でトークンが数KB〜十数KBに膨張 - LLM が continue_from にコピーする際、コンテキスト長や _trim_tool_result の切り詰めでトークン末尾が欠損 - base64 デコード結果が不正になり JSONDecodeError(JSON途中切断) と UnicodeDecodeError(マルチバイト境界切断)の両エラーが発生 - PDF で間欠的・再現性なしは、検索結果セットごとにスニペット長が 変動しトークンサイズが破損閾値を前後するためと説明がつく 補助要因: make_token() の ensure_ascii=False が、末尾欠損時に マルチバイト文字境界で切れる確率を上げていた。 ## 修正内容(案A: ユーザー承認済み) 1. pagination.py の make_token() を ensure_ascii=True 化 - トークンを純ASCII化し、末尾欠損でも UnicodeDecodeError を防止 - 非ASCII文字は \uXXXX エスケープ(6バイト固定)で安定化 2. fetch.py に _slim_matched() ヘルパーを追加 - matched から body を除外し title+href のみ埋め込み - 3箇所の make_token 呼び出し(fetch失敗時・ファイル内継続時・ 次ファイル移行時)を matched=_slim_matched(matched) に変更 - トークンサイズを数KB〜十数KB → 数百バイトに削減 3. トークン形式バージョン v は 1維持(構造不変・後方互換) - 旧トークン(body入り・ensure_ascii=False)も parse_token で 正常デコード可能 - 再検索回避機能も維持 ## テスト tests/test_tools.py に新規テスト5件を追加: - test_make_token_ensure_ascii_true - test_search_and_fetch_token_excludes_body - test_search_and_fetch_token_size_reduced - test_search_and_fetch_continue_with_slim_token - test_search_and_fetch_pagination_with_nonascii_title ## 検証結果 - pytest: 113 passed (新規5件含む、既存回帰なし) - ruff check src/ tests/: All checks passed - mypy src/: Success: no issues found in 30 source files ## ドキュメント docs/tools-schema.md に実装ベースの差分を反映: - make_token() の説明に ensure_ascii=True 化の旨を追記 - search_and_fetch 処理フローの注記に _slim_matched() による body 除外とトークンサイズ削減の旨を追記 - トークンペイロード構造の matched 例から body を削除
Diffstat (limited to 'docs/tools-schema.md')
-rw-r--r--docs/tools-schema.md8
1 files changed, 4 insertions, 4 deletions
diff --git a/docs/tools-schema.md b/docs/tools-schema.md
index ea438d3..b9cf4b9 100644
--- a/docs/tools-schema.md
+++ b/docs/tools-schema.md
@@ -190,7 +190,7 @@ URL: {url}
- ファイルが完了したら次のファイルへ
4. 残り候補 or 残りファイル内容があれば `has_more: true` + `next_token` を付与
-> トークンペイロードは `search_and_fetch` 用に拡張され、`matched`(候補URLリスト)を埋め込むことで再検索なしで続きを取得(API呼び出し増加を防止)。
+> トークンペイロードは `search_and_fetch` 用に拡張され、`matched`(候補リスト)を埋め込むことで再検索なしで続きを取得(API呼び出し増加を防止)。`matched` には [`_slim_matched()`](src/ollama_web/tools/fetch.py:42) で `body`(検索スニペット)を除外した `title` / `href` のみを格納し、トークンサイズの肥大化と末尾欠損による破損を防止する。
### 設計上の注意点
@@ -223,7 +223,7 @@ URL: {url}
### helper/pagination.py
- **実装**: [`pagination.py`](src/ollama_web/tools/helper/pagination.py:1)
-- [`make_token()`](src/ollama_web/tools/helper/pagination.py:1): 継続トークン生成(`base64url(JSON)`)
+- [`make_token()`](src/ollama_web/tools/helper/pagination.py:1): 継続トークン生成(`base64url(JSON)`、`ensure_ascii=True` で純ASCII化し末尾欠損時の `UnicodeDecodeError` を防止)
- [`parse_token()`](src/ollama_web/tools/helper/pagination.py:1): トークン検証・デコード。`tool` 一致・`v` バージョン・必須フィールドを検証し、不正時は `InvalidTokenError`
- [`format_paged_result()`](src/ollama_web/tools/helper/pagination.py:1): 戻り値ラッピング。`---page-info---` マーカーで本文とフッターを分離
- [`parse_paged_result()`](src/ollama_web/tools/helper/pagination.py:1): 戻り値分割(テスト・チェーニング用)
@@ -303,7 +303,7 @@ flowchart TD
}
```
-`search_and_fetch` は追加フィールドを埋め込む:
+`search_and_fetch` は追加フィールドを埋め込む。`matched` には検索スニペット(`body`)を含めず `title` / `href` のみを格納する([`_slim_matched()`](src/ollama_web/tools/fetch.py:42) で生成)。これによりトークンサイズを数百バイトに抑え、LLM が `continue_from` にコピーする際の末尾欠損による破損を防止する:
```json
{
@@ -311,7 +311,7 @@ flowchart TD
"tool": "search_and_fetch",
"url": "https://example.com/file.pdf",
"offset": 8000,
- "matched": [{"title": "...", "href": "...", "body": "..."}],
+ "matched": [{"title": "...", "href": "..."}],
"file_idx": 1,
"file_offset": 8000
}