makecindy / makecindy/cindy

cindy_memory 的 memory_search 对中文查询几乎全部失效(FTS5 unicode61 不分词 CJK)

Open
#1,537 1 comment 1 reaction 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
2.7k
Forks
395
Avg merge
21h 48m
Merged PRs (30d)
776

Description

**提交人**: 匿名
**客户端版本**: 0.1.27

---

## 现象

`cindy_memory` MCP 的 `memory_search` 工具,对中文关键词查询几乎总是返回空结果,即使目标内容确实存在于 memory 正文中;对英文关键词查询正常。

## 复现步骤

1. 通过 `memory_write` 写入一条包含中文正文的 memory(例如正文含“边界”“索引”等词)。
2. 调用 `memory_search({query: "边界"})` → 返回 `count: 0`,即使该词确实存在于某条 memory 的 body 中(可用 `memory_read` 核实)。
3. 调用 `memory_search({query: "边界*"})`(末尾加通配符)→ 能命中。
4. 调用 `memory_search({query: "MaxCompute"})` 或其他英文词 → 正常命中。

直接对底层 SQLite FTS5 表验证(memory 存储在 `~/Library/Application Support/Cindy/owners//maker-memory//fts.db`):

```sql
-- 精确匹配中文词,0 命中(词确实存在于正文)
SELECT count(*) FROM memory_fts WHERE memory_fts MATCH '边界'; -- 0

-- 加前缀通配才能命中
SELECT count(*) FROM memory_fts WHERE memory_fts MATCH '边界*'; -- 1

-- 英文词精确匹配正常
SELECT count(*) FROM memory_fts WHERE memory_fts MATCH 'README'; -- 5

-- tokenizer 配置:只有 version|4,未指定 tokenize,即默认 unicode61
SELECT * FROM memory_fts_config;

-- 用 fts5vocab 查看实际切词结果,高频 term 全是英文/数字,没有任何中文 token
CREATE VIRTUAL TABLE temp.v USING fts5vocab(main, memory_fts, row);
SELECT term, cnt FROM temp.v ORDER BY cnt DESC LIMIT 15;
-- 结果:00, 2026, arena, 07, codex, skill, id, dwd, uid, agent, item, cindi, md, 5, token(全部英数字)
```

## 根因

FTS5 建表未指定 `tokenize` 参数,使用默认 `unicode61` tokenizer。该 tokenizer 按 Unicode 分类切词,把连续的 CJK 字符当作一个整体的 alphanumeric token(因为它们之间没有 ASCII 空格/标点这类分隔符),导致:

- 一段中文正文被切成极少数几个超长 token,几乎不可能被短查询词精确 MATCH 命中。
- 只有查询词恰好是某个被切出的完整超长 token 的前缀(配合 `*` 通配)时才能命中,这在实际使用中几乎不可控。
- 英文因为天然有空格分隔,不受影响,所以问题只在中文场景暴露。

## 影响

这个工作区(游戏数据分析场景)的 memory 内容几乎全是中文。agent 写入新 memory 前,按 maker memory 使用规范应该先 `memory_search` 检查是否已有相关条目、避免重复;但由于中文检索基本不可用,搜索总是返回空,导致 agent 反复把已存在的知识当作新知识写入,实际观察到同一工作区产生了内容与已有条目高度重复的新 memory 分片。`memory_list` + `memory_read` 可以绕过这个问题手动定位,但那违背了 `memory_search` 工具本身存在的意义(语义/关键词检索,而非人工逐条翻阅)。

## 期望行为

`memory_search` 对中文(及其他 CJK 语言)关键词应能像英文一样正常做子串级检索命中,不需要用户或调用方手动加通配符。

## 可能的修复方向

以下任选其一即可解决,具体取舍留给维护者判断:

1. 建 FTS5 表时改用支持 CJK 的 tokenizer,例如 `tokenize='trigram'`(SQLite 3.34+ 内置,对任意语言按字符 n-gram 切分,天然支持中文字串匹配)。
2. 或在写入前对 CJK 文本做 bigram/unigram 预处理后再入库。
3. 或在查询层:检测到查询词包含 CJK 字符时,自动在词尾(必要时词首)补 `*` 做前缀匹配,作为不改表结构的低成本 workaround。

## 复现环境

- 平台:macOS(Darwin 25.5.0)
- 触发场景:本地 Claude Code / Cindy 内置 agent 会话,工作区为纯中文游戏数据分析项目
- 复现环境:memory 数据库路径形如 `~/Library/Application Support/Cindy/owners//maker-memory//fts.db`,表名 `memory_fts`
---
**版本区域**: CN
**OS**: darwin arm64 (25.5.0)
**界面语言**: zh-CN

Contributor guide

Open the contributing guide

Research direction

Start at the memory_search implementation and the FTS5 schema creation for memory_fts, then reproduce the behavior with the SQL queries and Chinese examples in the issue. Compare the tokenizer options described in the report and verify that the chosen fix lets an exact Chinese query match without a wildcard while preserving English search behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
sqlite, typescript
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.