你有没有遇到过这种场景?
你问 AI:"Kafka 消费延迟怎么处理?"
AI 给你一个泛泛的回答——调参数、加分区、检查 consumer lag。
但如果你之前学过 Kafka,你的笔记里可能写着:你所在的项目用 RocketMQ 做过类似的事,consumer lag 的根因其实是 rebalance 太频繁,跟 partition 数量有关。
AI 不知道这些。每次对话,它都从零开始。

我现在的工作流是这样的:我在 Claude Code 里输入"Kafka 消费延迟",AI 的回答里直接引用了我之前学过的 Kafka 知识和 RocketMQ 的对比经验。
我不需要搜索,不需要复制粘贴,不需要告诉 AI 我的背景。知识自动出现在它需要的地方。
这篇文章讲我是怎么做到的。
核心思路一句话:在用户消息到达 AI 之前,用 Hook 拦截,自动匹配并注入相关知识。
你输入: "Kafka 消费延迟怎么处理?"
↓
Hook 拦截,检测到关键词: Kafka、消费延迟
↓
从知识库中匹配到相关页面,直接注入到消息中
↓
AI 收到的内容 = 你的问题 + 你之前学过的 Kafka 知识
↓
AI 的回答已经基于你的经验,不是泛泛而谈关键点:不是让 AI 自己决定要不要查知识库,而是知识已经在它面前了。 这比 MCP 工具调用快一个数量级,因为不需要额外的 API 往返。
Claude Code 有一个 UserPromptSubmit Hook:用户每次输入消息时,先执行一个脚本,脚本的输出会直接注入到上下文中。
配置方式(~/.claude/settings.json):
{
"hooks": {
"UserPromptSubmit": [{
"type": "command",
"command": "~/.claude/scripts/exomind-context.sh"
}]
}
}就这么简单。Hook 就是标准输入 + 标准输出。
# stdin = 用户消息
MSG=$(cat)
# 处理匹配逻辑...
# stdout = 注入到上下文的内容
echo "[ExoMind] 以下是相关知识:..."整个脚本分两个阶段。
MSG=$(cat) # 从 stdin 读取用户消息
# 短消息跳过(减少噪音)
[ ${#MSG} -lt 8 ] && exit 0
# 无关指令跳过
case "$MSG" in
"好"|"继续"|"是"|"ok"|"yes"|"no")
exit 0 ;;
esac关键词匹配用 Python(bash 处理中文太痛苦):
import re
msg = sys.stdin.read().strip()
# 英文:精确匹配(3字符以上)
en_words = set(re.findall(r'[A-Za-z][A-Za-z0-9_+-]{2,}', msg))
# 中文:滑动窗口匹配(2-6字符子串)
cn_words = set(re.findall(r'[\u4e00-\u9fff]{2,6}', msg))
# 在知识库索引中查找匹配
hits = []
for w in en_words:
if w in keyword_index:
hits.append(w)
for w in cn_words:
if w in keyword_index:
hits.append(w)
else:
# 滑动窗口:从长到短尝试匹配
for length in range(min(len(w), 6), 1, -1):
for i in range(len(w) - length + 1):
sub = w[i:i+length]
if sub in keyword_index:
hits.append(sub)
break中文匹配是个有意思的工程问题。 英文天然有空格分词,但中文没有。"消费延迟"作为一个整体命中了关键词索引,但"消息消费延迟"可能匹配不到。所以用滑动窗口从长到短尝试子串匹配。
匹配到关键词后,读取对应的 Markdown 文件,截取前 800 字符注入:
for keyword in hits[:3]: # 最多注入 3 个关键词
for subdir in ['entities', 'concepts', 'summaries']:
filepath = os.path.join(wiki_path, subdir, keyword + '.md')
if os.path.exists(filepath):
content = open(filepath, 'r').read()
body = extract_body(content) # 去掉 frontmatter
if len(body) > 800:
body = body[:800] + '...'
output_parts.append(f'### {keyword}\n{body}')
break
print('[ExoMind 知识库上下文] 以下是与当前话题相关的已有知识:')
print('\n'.join(output_parts))这里有一个容易忽略的问题:同一个会话里,如果你三次提到 Redis,不应该注入三次相同的内容。
我用了一个会话级去重机制:
# 会话级去重:30 分钟内不重复注入
dedup_file = '/tmp/.exomind-dedup-' + session_hash + '.json'
dedup = load_dedup(dedup_file)
dedup = {k: v for k, v in dedup.items() if now - v < 1800} # 清理过期
# 已注入的关键词再次被引用 = 消费(不是重复注入)
consumed = [h for h in hits if h in dedup]
hits = [h for h in hits if h not in dedup]
# 记录消费事件(影响知识质量评分)
for kw in consumed:
update_quality_score(kw, 'consumptions', +1)这个"消费追踪"是知识飞轮的关键一环:
第一次提到 Redis → 注入 Redis 知识(记录 click)
第二次提到 Redis → 检测到消费(记录 consumption,不重复注入)消费次数越多的知识,在搜索中排名越高。 这就是知识复利:被用得越多,越容易被发现。
这是最后加的一个阶段。问题是:在 Claude Code 对话中产生的经验总结,怎么自动摄入知识库?
靠 CLAUDE.md 里写"请你自动摄入"是不靠谱的——我在一个长会话中反复产生经验总结,LLM 根本没执行。
所以我把检测逻辑放到了 Hook 里:
# 经验/洞察关键词检测
experience_patterns = [
r'关键经验', r'踩坑', r'最佳实践', r'架构决策',
r'根因分析', r'优化效果', r'心得', r'避坑',
r'lesson learned', r'best practice', r'gotcha',
]
# 或者结构化特征:3个以上 bullet point + 技术关键词
bullet_count = len(re.findall(r'^\s*[-*•]\s+', msg, re.MULTILINE))
has_tech_terms = bool(re.search(r'(API|框架|架构|缓存|数据库)', msg))
if insight_count >= 1 or (bullet_count >= 3 and has_tech_terms):
print('[ExoMind 自动摄入指令] 检测到经验/洞察内容。')
print('请立即调用 mcp__exomind__ingest 将以上内容摄入知识库。')这比 CLAUDE.md 指令可靠得多,因为 Hook 的输出是直接注入到上下文的,LLM 必须看到它。
Hook 跑了一周:
指标 | 数值 |
|---|---|
总注入次数 | 23 次 |
日均注入 | 3.3 次 |
触发关键词数 | 15 个 |
高频关键词 | wechat-publish-service(9), Redis(3), RocketMQ(3) |
每次注入延迟 | <100ms(本地文件读取) |
每日注入分布:
05-10: 1 次 █
05-11: 9 次 ████████████████████ (批量开发日,触发最多)
05-12: 7 次 ██████████████
05-13: 2 次 ████
05-14: 2 次 ████
05-15: 1 次 ██
05-16: 1 次 ██我输入: "Redis 分布式锁怎么实现?"
Hook 自动注入:
- Redis(实体页面,64条关系)
- Redis分布式锁(概念页面)
- 持久化(关联概念)
Claude 的回答:
直接引用了我之前学过的 RDB/AOF 对锁可靠性的影响,
而不是泛泛介绍 SETNX + EXPIRE。我输入: "微信 access_token 过期怎么处理?"
Hook 自动注入:
- 微信公众号 access_token 机制
- OAuth 2.0 token 刷新模式
- 之前项目中踩过的坑
Claude 的回答:
基于我自己的项目经验回答,提到了我之前遇到过的
token 刷新竞争条件问题。这就是知识复利的效果:你过去学的东西,在你需要的时候自动出现。
最初我让 Hook 输出"建议你查询 xxx",期望 AI 自己决定要不要查。结果 AI 大部分时候忽略了。
解决:直接输出知识内容,不是建议。把"你可以查看 Redis 的笔记"改成直接把 Redis 笔记的内容贴出来。AI 没法忽略已经出现在上下文中的信息。
早期版本不限制注入长度,一次注入 5 个关键词 × 2000 字 = 10000 字,把上下文塞满了。
解决:每个关键词最多注入 800 字符,最多注入 3 个关键词。总注入量控制在 2400 字以内。
在 CLAUDE.md 中写"检测到经验总结时请自动摄入"——实测在长会话中基本不执行。LLM 的注意力在长上下文中会衰减,写在系统提示里的指令优先级不够。
解决:把检测逻辑移到 Hook 里。Hook 的输出在用户消息之后、AI 处理之前,位置最显眼,不可能被忽略。
三个模式,适用于任何 AI 助手的个性化场景:
模式 1:Hook 注入 > 工具调用 > 手动查询
让 AI 自己决定要不要查知识库(工具调用),不如直接把知识塞进上下文(Hook 注入)。省了一次 API 调用,减少了 AI "忘记查询"的风险。
模式 2:消费追踪是反馈飞轮的起点
知识被注入后,如果用户再次提到同一个关键词,说明知识被"消费"了。消费次数驱动质量评分,质量评分驱动搜索排名。这是让知识越用越好的正循环。
模式 3:不要指望 LLM 执行系统提示中的指令
CLAUDE.md / System Prompt 里的"请自动做 X"在短会话中可能有效,长会话中基本失效。如果某个行为很关键,把它放到 Hook 或工具中强制执行,不要依赖 LLM 的"自觉"。
最好的知识管理不是你去找知识,是知识在你需要的时候自己出现。
一行 Hook 配置,让你的 AI 助手从"通用工具"变成"知道你一切的私人顾问"。