feat(mcp): 加 _pagination_hint 帮 LLM 决定是否续翻 (#103)

## 问题

LLM 调用 \`get_chat_history(limit=50)\` 拿到 50 条消息后, 无法判断
"是真只有 50 条" 还是 "还有 150 条没拿"。LLM 缺少续翻信号, 容易
基于不完整数据回答。

类似问题影响所有分页工具: \`search_messages\` / \`get_chat_images\` /
\`get_voice_messages\` / \`get_contacts\`。

## 修复

加 \`_pagination_hint(count, limit, offset)\` helper:
- \`count >= limit\` 时返回 \`(可能还有更多结果,可设 offset=N 继续查询)\`
- \`count < limit\` 时返回空 (表示已读完当前条件全部结果)
- \`limit == 0\` (理论非法, 上游有 \`_validate_pagination\` 兜底) 防御
  性返回空

应用到 5 个工具返回字符串末尾:
- \`get_chat_history\` (1 处)
- \`search_messages\` 三个内部分发 \`_search_single_chat\` /
  \`_search_multiple_chats\` / \`_search_all_messages\` (3 处)
- \`get_chat_images\` / \`get_voice_messages\` (各 1 处, 当前两者无 offset
  参数, 使用 \`offset=0\` 占位; 后续接口对齐 PR 会把 \`offset\` 加进来)
- \`get_contacts\` 单独用 \`total > limit\` 模式提示 "共 N 个匹配, 当前
  仅显示前 limit 个, 可增大 limit" — 因为 \`get_contacts\` 当前无
  pagination 语义, 仅有 limit, 文案语义不同

## 测试

\`tests/test_pagination_hint.py\` 5 个 case 覆盖:
- count < limit 不提示
- count == limit 提示且 offset 累加正确
- 连续翻页 offset 推进 (offset=100, limit=20 → 提示 offset=120)
- limit=0 防御
- count > limit 边界 (理论不该发生)

全量 205/205 通过。

## 范围

纯返回字符串末尾追加, 不改任何查询逻辑、不改函数签名、不改数据库
读路径。零破坏性, 调用方 100% 向后兼容。

提示文案如不合适可直接改, 不影响行为。
This commit is contained in:
Belugary
2026-05-14 15:30:16 +08:00
committed by GitHub
parent 70d44ef61f
commit 9450e46ca5
2 changed files with 58 additions and 7 deletions

View File

@@ -0,0 +1,36 @@
"""测试分页提示语 _pagination_hint() 的边界行为。"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import mcp_server
def test_no_hint_when_count_less_than_limit():
"""count < limit 表示已读完当前条件下全部结果,不提示。"""
assert mcp_server._pagination_hint(count=10, limit=50, offset=0) == ""
def test_hint_when_count_equals_limit():
"""count == limit 时无法判断是否还有更多,提示下一页 offset。"""
hint = mcp_server._pagination_hint(count=50, limit=50, offset=0)
assert "可能还有更多" in hint
assert "offset=50" in hint
def test_hint_advances_offset_by_limit():
"""连续翻页时 offset 累加。"""
hint = mcp_server._pagination_hint(count=20, limit=20, offset=100)
assert "offset=120" in hint
def test_no_hint_when_limit_zero():
"""limit=0 是非法分页 (上游有 _validate_pagination 兜底);防御性返回空。"""
assert mcp_server._pagination_hint(count=0, limit=0, offset=0) == ""
def test_no_hint_when_count_exceeds_limit():
"""理论上 count > limit 不该发生 (调用方已 limit), 但若发生仍要提示。"""
hint = mcp_server._pagination_hint(count=51, limit=50, offset=0)
assert "可能还有更多" in hint