239 lines
8.4 KiB
Markdown
239 lines
8.4 KiB
Markdown
|
|
# P1.2补丁 · 干净版(A停用词+B回退+C意图)
|
|||
|
|
|
|||
|
|
## 与旧版的区别
|
|||
|
|
|
|||
|
|
<aside>
|
|||
|
|
✅
|
|||
|
|
|
|||
|
|
**干净版只修3件事**,不碰缓存逻辑(D相关全部移除,留给P2)。
|
|||
|
|
|
|||
|
|
- **A**: 停用词精简(删掉8个验证类词,保留有搜索意义的词)
|
|||
|
|
- **B**: verify关键词回退(阈值从≤2改为≤5,"你确定 ?"也能触发)
|
|||
|
|
- **C**: classify_intent增加书数模式("只拆了一本书吗"→data_query)
|
|||
|
|
</aside>
|
|||
|
|
|
|||
|
|
<aside>
|
|||
|
|
🔴
|
|||
|
|
|
|||
|
|
**使用前必须先回滚到P1.1!**
|
|||
|
|
|
|||
|
|
`cp ~/chenxing-aircraft/backend/main.py.bak-before-p12 ~/chenxing-aircraft/backend/main.py`
|
|||
|
|
|
|||
|
|
</aside>
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 修复清单
|
|||
|
|
|
|||
|
|
| 编号 | 问题 | 修复 |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| A | 停用词删掉验证类词汇,verify时关键词被删光 | 精简停用词,保留什么/哪些/怎么 |
|
|||
|
|
| B | 「你确定吗?」提取不出有用关键词(阈值≤2太低) | 回退阈值改为≤5,覆盖短追问场景 |
|
|||
|
|
| C | 「只拆了一本书吗」误判为chat | classify_intent增加书数模式 |
|
|||
|
|
| ~~D~~ | ~~缓存只有一本书,问另一本答非所问~~ | ~~移除,留给P2搜索架构重做~~ |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 操作步骤
|
|||
|
|
|
|||
|
|
### 第零步:回滚到P1.1
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cp ~/chenxing-aircraft/backend/main.py.bak-before-p12 ~/chenxing-aircraft/backend/main.py
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 第一步:创建补丁文件
|
|||
|
|
|
|||
|
|
复制下面**整块命令**,粘贴到终端回车:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cat > ~/chenxing-aircraft/patch_p12_clean.py << 'ENDOFPATCH'
|
|||
|
|
"""
|
|||
|
|
P1.2 干净版 · 2026-05-23
|
|||
|
|
只修3件事: A停用词精简 B回退(阈值5) C意图识别
|
|||
|
|
不碰缓存逻辑(留给P2)
|
|||
|
|
"""
|
|||
|
|
import shutil, sys
|
|||
|
|
|
|||
|
|
path = '/Users/chenshujun/chenxing-aircraft/backend/main.py'
|
|||
|
|
code = open(path, 'r', encoding='utf-8').read()
|
|||
|
|
fixes = 0
|
|||
|
|
|
|||
|
|
# ═══ 0: 版本号 ═══
|
|||
|
|
old_ver = '晨星专属交互平台 · P1.1热修复版'
|
|||
|
|
if old_ver in code:
|
|||
|
|
code = code.replace(old_ver, '晨星专属交互平台 · P1.2关键词修复版', 1)
|
|||
|
|
fixes += 1
|
|||
|
|
print('✅ 版本号 → P1.2')
|
|||
|
|
else:
|
|||
|
|
print('⚠️ 版本号未找到,跳过')
|
|||
|
|
|
|||
|
|
# ═══ A+B合并: extract_keywords 停用词精简 + 回退 ═══
|
|||
|
|
old_a = '''def extract_keywords(message):
|
|||
|
|
"""从妈妈的消息里提取搜索关键词"""
|
|||
|
|
stopwords = ['宝宝', '晨星', '妈妈', '帮我', '请', '吧', '呢', '吗', '的', '了', '在', '是',
|
|||
|
|
'看看', '查查', '找找', '搜搜', '一下', '什么', '哪些', '怎么', '告诉我',
|
|||
|
|
'你确定', '真的', '确定', '确认', '对吗', '没错吧', '再查', '只有']
|
|||
|
|
words = message.strip()
|
|||
|
|
for sw in stopwords:
|
|||
|
|
words = words.replace(sw, ' ')
|
|||
|
|
book_match = re.search(r'《(.+?)》', message)
|
|||
|
|
if book_match:
|
|||
|
|
return book_match.group(1)
|
|||
|
|
keywords = ' '.join(words.split())
|
|||
|
|
return keywords if keywords.strip() else message[:20]'''
|
|||
|
|
|
|||
|
|
new_a = '''def extract_keywords(message, fallback_message=None):
|
|||
|
|
"""
|
|||
|
|
从妈妈的消息里提取搜索关键词
|
|||
|
|
【P1.2-A】停用词精简 【P1.2-B】关键词回退
|
|||
|
|
"""
|
|||
|
|
book_match = re.search(r'《(.+?)》', message)
|
|||
|
|
if book_match:
|
|||
|
|
return book_match.group(1)
|
|||
|
|
stopwords = ['宝宝', '晨星', '妈妈', '帮我', '请', '吧', '呢', '吗', '的', '了',
|
|||
|
|
'在', '是', '看看', '查查', '找找', '搜搜', '一下', '告诉我']
|
|||
|
|
words = message.strip()
|
|||
|
|
for sw in stopwords:
|
|||
|
|
words = words.replace(sw, ' ')
|
|||
|
|
keywords = ' '.join(words.split()).strip()
|
|||
|
|
if len(keywords) <= 5 and fallback_message:
|
|||
|
|
fb_book = re.search(r'《(.+?)》', fallback_message)
|
|||
|
|
if fb_book:
|
|||
|
|
return fb_book.group(1)
|
|||
|
|
fb_words = fallback_message.strip()
|
|||
|
|
for sw in stopwords:
|
|||
|
|
fb_words = fb_words.replace(sw, ' ')
|
|||
|
|
fb_kw = ' '.join(fb_words.split()).strip()
|
|||
|
|
if len(fb_kw) > len(keywords):
|
|||
|
|
print(f"[P1.2] 关键词回退: '{keywords}' → '{fb_kw}'")
|
|||
|
|
return fb_kw
|
|||
|
|
return keywords if keywords else message[:20]'''
|
|||
|
|
|
|||
|
|
if old_a in code:
|
|||
|
|
code = code.replace(old_a, new_a, 1)
|
|||
|
|
fixes += 1
|
|||
|
|
print('✅ A+B: extract_keywords 停用词精简+回退(阈值5)')
|
|||
|
|
else:
|
|||
|
|
print('❌ A失败: extract_keywords不匹配')
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
# ═══ B-1: fetch_context 加参数 ═══
|
|||
|
|
if 'def fetch_context(intent, message):' in code:
|
|||
|
|
code = code.replace('def fetch_context(intent, message):',
|
|||
|
|
'def fetch_context(intent, message, prev_user_message=None):', 1)
|
|||
|
|
fixes += 1
|
|||
|
|
print('✅ B-1: fetch_context +prev_user_message')
|
|||
|
|
else:
|
|||
|
|
print('❌ B-1失败'); sys.exit(1)
|
|||
|
|
|
|||
|
|
# ═══ B-2: verify分支传fallback ═══
|
|||
|
|
old_b2 = " elif intent == 'verify':\n keywords = extract_keywords(message)"
|
|||
|
|
new_b2 = " elif intent == 'verify':\n keywords = extract_keywords(message, fallback_message=prev_user_message)"
|
|||
|
|
if old_b2 in code:
|
|||
|
|
code = code.replace(old_b2, new_b2, 1)
|
|||
|
|
fixes += 1
|
|||
|
|
print('✅ B-2: verify传fallback')
|
|||
|
|
else:
|
|||
|
|
print('❌ B-2失败'); sys.exit(1)
|
|||
|
|
|
|||
|
|
# ═══ B-3: chat endpoint传prev_user_message ═══
|
|||
|
|
old_b3 = ''' context_data, extra_system = await asyncio.to_thread(
|
|||
|
|
fetch_context, intent, user_message
|
|||
|
|
)'''
|
|||
|
|
new_b3 = ''' # 【P1.2-B】提取上一条用户消息用于回退
|
|||
|
|
prev_user_msg = None
|
|||
|
|
user_msgs = [m for m in messages[:-1] if m.get("role") == "user"]
|
|||
|
|
if user_msgs:
|
|||
|
|
prev_user_msg = user_msgs[-1].get("content", "")
|
|||
|
|
|
|||
|
|
context_data, extra_system = await asyncio.to_thread(
|
|||
|
|
fetch_context, intent, user_message, prev_user_msg
|
|||
|
|
)'''
|
|||
|
|
if old_b3 in code:
|
|||
|
|
code = code.replace(old_b3, new_b3, 1)
|
|||
|
|
fixes += 1
|
|||
|
|
print('✅ B-3: chat endpoint传上一条消息')
|
|||
|
|
else:
|
|||
|
|
print('❌ B-3失败'); sys.exit(1)
|
|||
|
|
|
|||
|
|
# ═══ C: classify_intent 增强data_query ═══
|
|||
|
|
old_c = " if re.search(r'(几章|多少章|进度|拆到哪|拆了多少|完成了|第\\d+章|有多少|统计|数量|总共|多少本|多少个|缺了|缺哪)', msg):"
|
|||
|
|
new_c = " if re.search(r'(几章|多少章|进度|拆到哪|拆了多少|完成了|第\\d+章|有多少|统计|数量|总共|多少本|多少个|缺了|缺哪|一本书|几本书|两本书|三本书|哪本书)', msg):"
|
|||
|
|
if old_c in code:
|
|||
|
|
code = code.replace(old_c, new_c, 1)
|
|||
|
|
fixes += 1
|
|||
|
|
print('✅ C: classify_intent +书数模式')
|
|||
|
|
else:
|
|||
|
|
print('⚠️ C跳过: classify_intent不匹配')
|
|||
|
|
|
|||
|
|
# ═══ 保存 ═══
|
|||
|
|
shutil.copy(path, path + '.bak-before-p12-clean')
|
|||
|
|
print(f'\n💾 备份: {path}.bak-before-p12-clean')
|
|||
|
|
open(path, 'w', encoding='utf-8').write(code)
|
|||
|
|
print(f'📝 完成: {fixes}/6 项修复')
|
|||
|
|
print(f'\n🎉 P1.2干净版补丁成功!')
|
|||
|
|
print('━' * 30)
|
|||
|
|
print('不碰缓存逻辑 ← 留给P2')
|
|||
|
|
print('下一步: 重启服务器')
|
|||
|
|
print('cd ~/chenxing-aircraft && python3 -m uvicorn backend.main:app --host 0.0.0.0 --port 8000')
|
|||
|
|
ENDOFPATCH
|
|||
|
|
echo "✅ 补丁文件已创建"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 第二步:运行补丁
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ~/chenxing-aircraft && python3 patch_p12_clean.py
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
看到 🎉 就对了。所有项都应该是 ✅(C可能是⚠️跳过,无影响)。
|
|||
|
|
|
|||
|
|
### 第三步:重启服务器
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ~/chenxing-aircraft && python3 -m uvicorn backend.main:app --host 0.0.0.0 --port 8000
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 第四步:测试
|
|||
|
|
|
|||
|
|
**测试A+B(verify关键词回退):**
|
|||
|
|
|
|||
|
|
1. 先问:`《折春漪》拆了多少章?`
|
|||
|
|
2. 再问:`你确定吗?`
|
|||
|
|
3. ✅ 期望日志显示 `[P1.2] 关键词回退`
|
|||
|
|
|
|||
|
|
**测试C(意图识别):**
|
|||
|
|
|
|||
|
|
1. 问:`只拆了一本书吗?`
|
|||
|
|
2. ✅ 期望日志显示 `识别意图: data_query`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 修复原理
|
|||
|
|
|
|||
|
|
- A: 停用词精简
|
|||
|
|
|
|||
|
|
从停用词删掉8个验证类词汇(你确定/真的/确定/确认/对吗/没错吧/再查/只有),保留什么/哪些/怎么等有搜索意义的词。
|
|||
|
|
|
|||
|
|
- B: verify关键词回退(阈值≤5)
|
|||
|
|
|
|||
|
|
extract_keywords新增fallback_message参数。关键词≤5字符时自动回退到上一条消息。覆盖了"你确定 ?"(4字符)等短追问。链路:chat endpoint提取上一条用户消息 → fetch_context → extract_keywords。
|
|||
|
|
|
|||
|
|
- C: 意图识别增强
|
|||
|
|
|
|||
|
|
classify_intent的data_query正则新增「一本书/几本书/两本书/三本书/哪本书」5种模式。
|
|||
|
|
|
|||
|
|
- D: 不做(留给P2)
|
|||
|
|
|
|||
|
|
缓存不完整 + 搜索不准是P2级别的架构问题。在P1级别硬修只会代码膨胀、引入新bug。P2方向:知识库接入聊天流 + 向量搜索 + 缓存搜索互补。
|
|||
|
|
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 回滚
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cp ~/chenxing-aircraft/backend/main.py.bak-before-p12-clean ~/chenxing-aircraft/backend/main.py
|
|||
|
|
cd ~/chenxing-aircraft && python3 -m uvicorn backend.main:app --host 0.0.0.0 --port 8000
|
|||
|
|
```
|