feat(note): 添加标签系统和内容提炼功能
- 实现笔记标签功能,支持添加、编辑和按标签筛选 - 新增内容提炼功能,可自动生成摘要和关键词 - 添加批量提炼功能,支持一键处理所有笔记 - 在统计面板中增加标签统计信息 - 更新笔记数据结构和UI以支持新功能
This commit is contained in:
parent
4d36b7df93
commit
ea47c7ad09
@ -19,3 +19,8 @@
|
||||
3. 启动:`uv run streamlit run note_organizer.py`
|
||||
|
||||
## 四、开发心得
|
||||
|
||||
在开发这个学习笔记整理器的过程中,我们深刻体会到了技术与需求之间的平衡。这个项目的目的是为了帮助学生更好地管理和查询学习笔记,让学习变得更加高效有序。我们面向的主要是在校学生,他们需要记录大量的学习内容,我们希望能提供给他们一个有效的整理和检索工具。项目功能包括笔记的创建、编辑、删除、分类管理、标签系统、智能搜索、内容提炼以及导入导出等核心功能,力求为学生们提供一个简单易用的笔记管理平台。
|
||||
最初我们考虑使用Chainlit框架,因为它在AI交互方面表现突出,但在实际开发过程中发现,虽然Chainlit在对话式交互上有优势,但在笔记管理功能上显得不够完善,而且智能搜索的效果也没有达到预期。为了让搜索更加智能,我们尝试了各种prompt设计,包括语义搜索的prompt,要求AI根据用户查询理解意图而非简单关键词匹配;概括提取的prompt,让AI对搜索结果进行智能总结和关键点提取;意图识别的prompt,用于判断用户是想创建、编辑还是删除笔记;删除意图的prompt,区分删除单个笔记、删除所有笔记还是删除某个分类的笔记;以及笔记识别的prompt,让AI从多个笔记中找到用户指定的目标。这些prompt设计经过多次调整,包括改变temperature参数、调整max_tokens长度、优化system prompt的表述方式,但效果始终不尽如人意,AI经常误解用户意图或返回不相关的内容。
|
||||
最终我们意识到单纯依赖AI并不能解决所有问题,于是转向了更传统但更可靠的解决方案。在搜索功能上,我们结合了关键词匹配和简单的语义分析,虽然不如纯AI搜索那么智能,但准确性和稳定性大大提升。在笔记管理方面,我们采用了传统的CRUD操作配合Streamlit的交互组件,让用户能够通过直观的界面进行操作,而不是完全依赖自然语言交互。这种混合模式既保留了AI的辅助功能,又确保了核心功能的可靠性。经过反复比较和测试,最终选择了Streamlit框架,它在界面设计和功能实现上更加成熟,能够更好地满足我们的需求。
|
||||
这次开发经历让我们对自己的技术能力有了更清晰的认识。如果没有AI的辅助,我们可能根本无法独立完成这样一个项目,我们在编程基础、框架使用、算法设计等方面都存在很多不足。这次经历既让我们看到了AI工具的强大,也让我们明白了自己需要学习的地方还有很多,未来还需要更加努力地提升自己的技术能力。
|
||||
|
||||
@ -2,11 +2,50 @@ import streamlit as st
|
||||
from datetime import datetime
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from collections import Counter
|
||||
|
||||
# 1. 页面配置
|
||||
st.set_page_config(page_title="学习笔记整理器", page_icon="📚", layout="wide")
|
||||
st.title("📚 学习笔记整理器")
|
||||
|
||||
# 文本提炼函数
|
||||
def extract_key_content(content, max_sentences=3, max_keywords=5):
|
||||
"""提炼笔记的关键内容"""
|
||||
if not content or len(content.strip()) < 10:
|
||||
return {"summary": "内容太短,无法提炼", "keywords": []}
|
||||
|
||||
# 分句
|
||||
sentences = re.split(r'[。!?\n]', content)
|
||||
sentences = [s.strip() for s in sentences if s.strip()]
|
||||
|
||||
# 提取关键句子(基于句子长度和关键词密度)
|
||||
sentence_scores = []
|
||||
for sentence in sentences:
|
||||
score = len(sentence)
|
||||
# 检查是否包含常见关键词
|
||||
keywords = ['重要', '关键', '核心', '主要', '注意', '必须', '应该', '需要', '总结', '结论']
|
||||
for kw in keywords:
|
||||
if kw in sentence:
|
||||
score += 20
|
||||
sentence_scores.append((sentence, score))
|
||||
|
||||
# 按分数排序,取前几句
|
||||
sentence_scores.sort(key=lambda x: x[1], reverse=True)
|
||||
top_sentences = [s[0] for s in sentence_scores[:max_sentences]]
|
||||
|
||||
# 提取关键词
|
||||
words = re.findall(r'[\u4e00-\u9fa5]{2,}', content)
|
||||
word_freq = Counter(words)
|
||||
# 过滤常见词
|
||||
stop_words = ['的', '是', '在', '有', '和', '了', '不', '也', '都', '这', '那', '就', '会', '要', '可', '以', '我', '你', '他', '她', '它', '我们', '你们', '他们']
|
||||
keywords = [w for w, c in word_freq.most_common(max_keywords * 2) if w not in stop_words][:max_keywords]
|
||||
|
||||
return {
|
||||
"summary": "。".join(top_sentences) + "。" if top_sentences else "无法提取摘要",
|
||||
"keywords": keywords
|
||||
}
|
||||
|
||||
# 2. 数据持久化函数
|
||||
def save_notes():
|
||||
"""保存笔记到 JSON 文件"""
|
||||
@ -76,6 +115,25 @@ with st.sidebar:
|
||||
height=200
|
||||
)
|
||||
|
||||
# 标签输入
|
||||
tags_input = st.text_input(
|
||||
"标签",
|
||||
placeholder="输入标签,用逗号分隔 (例如: 重要, 复习, 考试)",
|
||||
help="为笔记添加多个标签,便于分类和搜索"
|
||||
)
|
||||
tags = [tag.strip() for tag in tags_input.split(",") if tag.strip()]
|
||||
|
||||
# 实时预览提炼结果
|
||||
if content and len(content.strip()) > 10:
|
||||
with st.expander("🔍 预览提炼结果", expanded=False):
|
||||
extracted = extract_key_content(content)
|
||||
st.markdown("**摘要预览:**")
|
||||
st.info(extracted['summary'])
|
||||
st.markdown("**关键词预览:**")
|
||||
if extracted['keywords']:
|
||||
keywords_html = " ".join([f'<span style="background-color: #fff3e0; color: #e65100; padding: 2px 8px; border-radius: 4px; margin-right: 5px; font-size: 12px;">{kw}</span>' for kw in extracted['keywords']])
|
||||
st.markdown(keywords_html, unsafe_allow_html=True)
|
||||
|
||||
# 添加按钮
|
||||
if st.button("添加笔记", type="primary"):
|
||||
if title and content:
|
||||
@ -84,6 +142,7 @@ with st.sidebar:
|
||||
"title": title,
|
||||
"category": category,
|
||||
"content": content,
|
||||
"tags": tags,
|
||||
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
@ -104,7 +163,7 @@ with st.sidebar:
|
||||
st.header("📖 我的笔记")
|
||||
|
||||
# 搜索和排序区域
|
||||
col1, col2, col3, col4 = st.columns([2, 1, 1, 1])
|
||||
col1, col2, col3, col4, col5 = st.columns([2, 1, 1, 1, 1])
|
||||
|
||||
with col1:
|
||||
# 搜索关键词
|
||||
@ -118,7 +177,7 @@ with col2:
|
||||
# 搜索范围选择
|
||||
search_scope = st.selectbox(
|
||||
"搜索范围",
|
||||
["全部", "仅标题", "仅内容", "仅类型"],
|
||||
["全部", "仅标题", "仅内容", "仅类型", "仅标签"],
|
||||
help="选择搜索的范围"
|
||||
)
|
||||
|
||||
@ -131,6 +190,15 @@ with col3:
|
||||
)
|
||||
|
||||
with col4:
|
||||
# 标签筛选
|
||||
all_tags = list(set(tag for note in st.session_state.notes for tag in note.get("tags", [])))
|
||||
filter_tag = st.selectbox(
|
||||
"筛选标签",
|
||||
["全部"] + sorted(all_tags),
|
||||
help="按标签筛选笔记"
|
||||
)
|
||||
|
||||
with col5:
|
||||
# 排序方式
|
||||
sort_by = st.selectbox(
|
||||
"排序方式",
|
||||
@ -138,6 +206,20 @@ with col4:
|
||||
help="选择笔记的排序方式"
|
||||
)
|
||||
|
||||
# 批量提炼功能
|
||||
st.divider()
|
||||
col_batch1, col_batch2 = st.columns([3, 1])
|
||||
with col_batch1:
|
||||
st.markdown("### ✨ 批量提炼")
|
||||
with col_batch2:
|
||||
if st.button("🔄 提炼所有笔记", key="batch_extract"):
|
||||
for note in st.session_state.notes:
|
||||
if len(note['content'].strip()) > 10:
|
||||
extracted = extract_key_content(note['content'])
|
||||
st.session_state[f'extracted_{note["id"]}'] = extracted
|
||||
st.success(f"已提炼 {len(st.session_state.notes)} 条笔记的关键内容!")
|
||||
st.rerun()
|
||||
|
||||
# 6. 显示笔记
|
||||
if st.session_state.notes:
|
||||
# 筛选和搜索逻辑
|
||||
@ -146,6 +228,9 @@ if st.session_state.notes:
|
||||
# 分类筛选
|
||||
category_match = filter_category == "全部" or note["category"] == filter_category
|
||||
|
||||
# 标签筛选
|
||||
tag_match = filter_tag == "全部" or filter_tag in note.get("tags", [])
|
||||
|
||||
# 关键词搜索(根据选择范围)
|
||||
search_match = True
|
||||
if search_query:
|
||||
@ -154,7 +239,8 @@ if st.session_state.notes:
|
||||
search_match = (
|
||||
query in note["title"].lower() or
|
||||
query in note["content"].lower() or
|
||||
query in note["category"].lower()
|
||||
query in note["category"].lower() or
|
||||
any(query in tag.lower() for tag in note.get("tags", []))
|
||||
)
|
||||
elif search_scope == "仅标题":
|
||||
search_match = query in note["title"].lower()
|
||||
@ -162,8 +248,10 @@ if st.session_state.notes:
|
||||
search_match = query in note["content"].lower()
|
||||
elif search_scope == "仅类型":
|
||||
search_match = query in note["category"].lower()
|
||||
elif search_scope == "仅标签":
|
||||
search_match = any(query in tag.lower() for tag in note.get("tags", []))
|
||||
|
||||
if category_match and search_match:
|
||||
if category_match and tag_match and search_match:
|
||||
filtered_notes.append(note)
|
||||
|
||||
# 排序逻辑
|
||||
@ -201,6 +289,16 @@ if st.session_state.notes:
|
||||
)
|
||||
edit_content = st.text_area("内容", value=note['content'], height=200, key=f"edit_content_{note['id']}")
|
||||
|
||||
# 编辑标签
|
||||
current_tags = ", ".join(note.get("tags", []))
|
||||
edit_tags = st.text_input(
|
||||
"标签",
|
||||
value=current_tags,
|
||||
key=f"edit_tags_{note['id']}",
|
||||
help="输入标签,用逗号分隔"
|
||||
)
|
||||
edit_tags_list = [tag.strip() for tag in edit_tags.split(",") if tag.strip()]
|
||||
|
||||
# 保存和取消按钮
|
||||
col_save, col_cancel = st.columns(2)
|
||||
with col_save:
|
||||
@ -208,6 +306,7 @@ if st.session_state.notes:
|
||||
note['title'] = edit_title
|
||||
note['category'] = edit_category
|
||||
note['content'] = edit_content
|
||||
note['tags'] = edit_tags_list
|
||||
note['updated_at'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
st.session_state.editing_note = None
|
||||
save_notes()
|
||||
@ -222,10 +321,35 @@ if st.session_state.notes:
|
||||
with st.expander(f"📌 {note['title']}", expanded=False):
|
||||
# 显示笔记元信息
|
||||
st.markdown(f"**分类:** {note['category']}")
|
||||
|
||||
# 显示标签
|
||||
if note.get("tags"):
|
||||
tags_html = " ".join([f'<span style="background-color: #e1f5fe; color: #01579b; padding: 2px 8px; border-radius: 4px; margin-right: 5px; font-size: 12px;">{tag}</span>' for tag in note["tags"]])
|
||||
st.markdown(f"**标签:** {tags_html}", unsafe_allow_html=True)
|
||||
|
||||
st.markdown(f"**创建时间:** {note['created_at']}")
|
||||
if 'updated_at' in note:
|
||||
st.markdown(f"**更新时间:** {note['updated_at']}")
|
||||
st.markdown("---")
|
||||
|
||||
# 提炼内容按钮
|
||||
if st.button("✨ 提炼内容", key=f"extract_{note['id']}"):
|
||||
extracted = extract_key_content(note['content'])
|
||||
st.session_state[f'extracted_{note["id"]}'] = extracted
|
||||
|
||||
# 显示提炼结果
|
||||
if f'extracted_{note["id"]}' in st.session_state:
|
||||
extracted = st.session_state[f'extracted_{note["id"]}']
|
||||
with st.expander("📝 提炼结果", expanded=True):
|
||||
st.markdown("**摘要:**")
|
||||
st.info(extracted['summary'])
|
||||
st.markdown("**关键词:**")
|
||||
if extracted['keywords']:
|
||||
keywords_html = " ".join([f'<span style="background-color: #fff3e0; color: #e65100; padding: 2px 8px; border-radius: 4px; margin-right: 5px; font-size: 12px;">{kw}</span>' for kw in extracted['keywords']])
|
||||
st.markdown(keywords_html, unsafe_allow_html=True)
|
||||
else:
|
||||
st.warning("未找到关键词")
|
||||
|
||||
# 显示笔记内容
|
||||
st.markdown(note['content'])
|
||||
|
||||
@ -250,17 +374,33 @@ else:
|
||||
st.divider()
|
||||
st.subheader("📊 统计信息")
|
||||
|
||||
# 动态生成统计列
|
||||
stat_cols = st.columns(min(5, len(st.session_state.categories) + 1))
|
||||
with stat_cols[0]:
|
||||
# 笔记和分类统计
|
||||
col_stats1, col_stats2 = st.columns(2)
|
||||
with col_stats1:
|
||||
st.metric("总笔记数", len(st.session_state.notes))
|
||||
with col_stats2:
|
||||
all_tags = list(set(tag for note in st.session_state.notes for tag in note.get("tags", [])))
|
||||
st.metric("总标签数", len(all_tags))
|
||||
|
||||
# 分类统计
|
||||
st.markdown("### 分类统计")
|
||||
cat_stat_cols = st.columns(min(5, len(st.session_state.categories)))
|
||||
for i, cat in enumerate(st.session_state.categories):
|
||||
if i + 1 < len(stat_cols):
|
||||
with stat_cols[i + 1]:
|
||||
if i < len(cat_stat_cols):
|
||||
with cat_stat_cols[i]:
|
||||
count = len([n for n in st.session_state.notes if n["category"] == cat])
|
||||
st.metric(cat, count)
|
||||
|
||||
# 标签统计
|
||||
if all_tags:
|
||||
st.markdown("### 标签统计")
|
||||
tag_stat_cols = st.columns(min(5, len(all_tags)))
|
||||
for i, tag in enumerate(sorted(all_tags)):
|
||||
if i < len(tag_stat_cols):
|
||||
with tag_stat_cols[i]:
|
||||
count = len([n for n in st.session_state.notes if tag in n.get("tags", [])])
|
||||
st.metric(tag, count)
|
||||
|
||||
# 8. 数据管理
|
||||
st.divider()
|
||||
st.subheader("💾 数据管理")
|
||||
|
||||
@ -6,7 +6,8 @@
|
||||
"category": "语文",
|
||||
"content": "李白的《静夜思》表达了思乡之情,意境深远。",
|
||||
"created_at": "",
|
||||
"updated_at": ""
|
||||
"updated_at": "2026-01-08 15:28:37",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
@ -95,6 +96,15 @@
|
||||
"content": "look forward to意为期待、盼望,后接名词或动名词。",
|
||||
"created_at": "",
|
||||
"updated_at": ""
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"title": "中国古典诗词艺术",
|
||||
"category": "语文",
|
||||
"content": "中国古典诗词是中华文化的瑰宝,承载着数千年的历史与智慧。从《诗经》的质朴纯真,到唐诗的辉煌灿烂,再到宋词的婉约豪放,诗词艺术在中国文学史上占据着极其重要的地位。\n\n诗歌的起源可以追溯到远古时期,先民们在劳动和生活中创造了最初的歌谣。这些歌谣经过长期的传承和演变,逐渐形成了具有固定格式的诗歌。《诗经》是中国最早的诗歌总集,收录了从西周初期到春秋中叶的305篇诗歌,分为风、雅、颂三个部分。《诗经》中的诗歌以四言为主,运用了赋、比、兴的表现手法,开创了中国诗歌的现实主义传统。\n\n汉代乐府诗继承了《诗经》的现实主义精神,反映了当时的社会生活和人民的思想感情。《古诗十九首》是汉代文人五言诗的代表作,语言质朴自然,情感真挚深沉,被誉为\"五言之冠冕\"。这些诗歌为后来诗歌的发展奠定了坚实的基础。\n\n唐代是中国诗歌发展的黄金时代,涌现出了李白、杜甫、白居易等一大批杰出的诗人。李白的诗歌豪放飘逸,想象奇特,充满了浪漫主义色彩。他的《将进酒》\"君不见黄河之水天上来,奔流到海不复回\"气势磅礴,展现了他豪迈不羁的性格。杜甫的诗歌沉郁顿挫,关注民生疾苦,被称为\"诗圣\"。他的《春望》\"国破山河在,城春草木深\"表达了对国家命运的忧虑。白居易的诗歌通俗易懂,关注社会现实,他的《长恨歌》\"在天愿作比翼鸟,在地愿为连理枝\"传唱千古。\n\n宋词是中国诗歌的另一种重要形式,分为豪放派和婉约派两大流派。苏轼是豪放派的代表人物,他的词作气势磅礴,意境开阔。他的《念奴娇·赤壁怀古》\"大江东去,浪淘尽,千古风流人物\"展现了豪迈的气概。李清照是婉约派的代表人物,她的词作细腻婉约,情感真挚。她的《声声慢》\"寻寻觅觅,冷冷清清,凄凄惨惨戚戚\"表达了她内心的愁苦。\n\n中国古典诗词的艺术特色主要体现在以下几个方面:一是意境深远,诗人通过生动的意象和精妙的语言,营造出深远的艺术境界;二是语言精炼,诗词要求字斟句酌,每一个字都要发挥最大的表现力;三是韵律优美,诗词讲究平仄和对仗,读起来朗朗上口;四是情感真挚,诗人将自己的真情实感融入诗中,使读者产生共鸣。\n\n诗歌的创作技巧丰富多彩,包括比喻、拟人、夸张、对偶、排比等多种修辞手法。比喻可以使抽象的事物变得具体形象,如\"问君能有几多愁,恰似一江春水向东流\"。拟人可以使事物具有人的情感和动作,如\"感时花溅泪,恨别鸟惊心\"。夸张可以突出事物的特征,如\"飞流直下三千尺,疑是银河落九天\"。对偶可以使诗歌更加工整优美,如\"两个黄鹂鸣翠柳,一行白鹭上青天\"。\n\n中国古典诗词蕴含着深刻的思想内涵。许多诗歌表达了诗人对国家和人民的关怀,如杜甫的\"安得广厦千万间,大庇天下寒士俱欢颜\"。有些诗歌表达了诗人对人生的感悟,如苏轼的\"人有悲欢离合,月有阴晴圆缺,此事古难全\"。还有些诗歌表达了诗人对自然的热爱,如王维的\"空山新雨后,天气晚来秋\"。\n\n学习古典诗词,不仅可以提高我们的文学素养,还可以陶冶我们的情操,丰富我们的精神世界。通过阅读和欣赏古典诗词,我们可以感受到古人的智慧和情感,体会到中华文化的博大精深。古典诗词是我们民族的精神财富,值得我们认真学习和传承。\n\n在现代社会,古典诗词依然具有重要的意义。它可以帮助我们更好地理解传统文化,增强文化自信。它可以提高我们的审美能力和语言表达能力。它还可以帮助我们缓解压力,陶冶情操。因此,我们应该重视古典诗词的学习和传承,让这一优秀的文化遗产在新时代焕发出新的光彩。",
|
||||
"created_at": "2026-01-08 16:00:00",
|
||||
"updated_at": "2026-01-08 16:00:00",
|
||||
"tags": ["诗词", "文学", "文化", "艺术", "经典"]
|
||||
}
|
||||
],
|
||||
"categories": [
|
||||
|
||||
Loading…
Reference in New Issue
Block a user