基于Groq API与Streamlit构建AI会议记忆助手:从原理到实践
1. 项目概述为什么我们需要一个“会议记忆体”在团队协作的日常里会议是决策和同步的核心场景。但开完会之后呢那些散落在不同人笔记里的关键结论、临时分配的任务、以及一闪而过的灵感火花往往就像沙滩上的字迹被后续涌来的信息潮水迅速抹平。你肯定经历过这样的场景一周后有人问“上次会上说这个功能谁负责来着”大家面面相觑然后不得不花时间重新翻聊天记录、查邮件甚至再开个小会来“回忆”。信息不是没有被记录而是没有被有效地“记住”和“关联”。这正是我动手构建“AI会议记忆助手”的初衷。它不是一个简单的笔记应用而是一个具备持久化记忆和上下文理解能力的智能体。核心想法很简单让AI成为团队的“集体记忆中枢”。每次会议的要点、讨论的决策、分配的责任人都以结构化的方式喂给它。之后无论是新成员想了解项目背景还是老成员忘记某个细节都可以直接向它提问它能像一位始终在场的资深同事一样从历史记忆中提取相关信息给出准确的答案。这个项目的核心价值在于“记忆智能”的耦合。传统笔记工具只是被动的存储检索依赖关键词匹配往往不够灵活。而这里我们利用大语言模型的理解能力让系统能“读懂”笔记内容并“理解”用户问题的意图从而实现基于语义的智能问答。比如你输入“小明负责用户登录模块的后端开发”之后无论是问“谁在做登录后端”还是“用户认证部分是谁在搞”系统都能准确指向小明。这背后是本地化的记忆存储用JSON文件实现简单可控与云端高速LLM推理能力这里选用Groq API因其响应速度极快适合交互式应用的结合。整个技术栈保持轻量和实用主义Python作为粘合剂Streamlit快速搭建交互界面Groq提供大脑本地JSON文件作为记忆仓库。它适合任何规模的团队尤其是那些频繁开会、项目信息迭代快、且不希望将敏感会议记录上传至第三方SaaS平台的场景。接下来我将从设计思路到每一行代码拆解这个如何让AI真正“记住”会议内容的实现过程。2. 核心设计思路与架构选型2.1 从需求到方案为何选择“智能体”范式面对“会议信息易丢失”这个问题最直接的解决方案可能是建立一个共享的在线文档模板要求所有人会后同步更新。但这依赖于人的自觉和执行力往往难以持续。另一种方案是使用专业的项目管理或知识库软件但这类工具通常较重学习成本和迁移成本较高。我选择“AI智能体”这条路径是基于以下几个考量主动性智能体可以被动接收信息存储笔记也能主动响应查询回答问题扮演了一个交互式知识库的角色。语义化不同于数据库的精确查询SELECT * FROM notes WHERE content LIKE ‘%后端%’LLM驱动的智能体能够理解自然语言中的同义词、模糊指代和上下文关联。例如“后端”和“服务器逻辑”在查询时应该被关联起来。低侵入性理想的工作流是会议记录者只需将整理好的文本甚至可以是语音转文字的初稿粘贴进一个输入框剩下的记忆和关联工作由智能体完成。这比要求大家严格按照固定格式填写多个字段的阻力要小得多。渐进式增强这个助手可以从简单的“问答机”开始未来可以很容易地扩展功能比如自动从会议录音中提取摘要、关联任务与责任人生成待办清单、甚至基于历史决策趋势给出建议等。因此系统的核心设计目标确定为构建一个具备持久化、可检索记忆并能通过自然语言与之交互的智能体。2.2 技术栈决策背后的“为什么”Python: 几乎是AI原型开发的事实标准。其丰富的生态如requests, json, os和针对LLM应用的高级框架如LangChain让开发效率极高。虽然最终部署可能考虑其他语言但在验证概念阶段Python是最佳选择。Streamlit: 选择Streamlit而非Django或Flask是为了极致化开发速度。我们的目标是快速验证“记忆-问答”这个核心循环是否有效、体验是否顺畅。Streamlit允许我们用纯Python脚本快速创建出一个带有输入框、按钮、聊天历史展示区的Web应用无需处理前后端分离、路由、HTML/CSS等繁琐工作。这对于一个工具类、内部使用的原型来说性价比最高。Groq API: 这是本项目的一个关键决策点。市面上LLM API众多如OpenAI, Anthropic, 国内各大平台。选择Groq的主要原因在于其惊人的推理速度。在会议问答场景下用户期待的是近乎即时的反馈如果每次回答都需要等待数秒交互体验会大打折扣。Groq凭借其LPU语言处理单元硬件架构在文本生成速度上具有显著优势。此外其API设计简洁成本透明适合中小流量应用。注意Groq API的速度优势明显但也要注意其模型可能在某些复杂推理或创意写作任务上与其他顶级模型有细微差距。对于本项目的“信息提取与重组”核心任务它完全胜任。本地JSON文件存储记忆: 为什么不直接用数据库如SQLite这基于两点考虑一是简化JSON文件对于Python来说操作极其方便无需额外服务二是隐私与可控性所有会议记录都保存在团队自己的服务器或电脑上没有数据出域的风险。JSON的缺点是不适合海量数据的高效检索但考虑到一个团队的会议记录在初期可能只有几百上千条完全在可接受范围内。未来如果数据量激增可以平滑地将存储层迁移到向量数据库如Chroma, Weaviate以实现更高效的语义搜索。2.3 系统架构全景图与数据流整个系统的运行遵循一个清晰的单向数据流我们可以将其理解为一次“交互生命周期”用户输入 │ ▼ [Streamlit UI 前端] │ (获取输入文本) ▼ [Python 后端逻辑] │ ├── 路径判断是“笔记”还是“问题” │ │ │ ├── 如果是“笔记” │ │ │ │ │ ▼ │ │ [记忆存储模块] │ │ │ (添加时间戳格式化) │ │ ▼ │ │ [本地JSON文件] │ │ │ (持久化保存) │ │ ▼ │ │ 返回确认信息“已记录” │ │ │ └── 如果是“问题” │ │ │ ▼ │ [记忆检索模块] │ │ (读取整个JSON记忆库) │ ▼ │ [上下文组装模块] │ │ (将相关记忆问题组合成LLM提示词) │ ▼ │ [Groq API 调用模块] │ │ (发送提示词获取生成结果) │ ▼ │ 返回AI生成的答案 │ ▼ [Streamlit UI 前端] │ (展示结果确认信息 或 AI答案) ▼ 用户获得反馈这个架构的关键在于“路径判断”和“上下文组装”。如何判断一段输入是笔记还是问题一个简单而有效的启发式规则是检查输入是否以问号结尾或者是否包含明显的疑问词如“谁”、“什么”、“何时”、“哪里”、“为什么”、“如何”。当然这并非完美但作为初版它足够工作。更复杂的方案可以训练一个简单的文本分类模型但对于MVP最小可行产品而言过度工程化并不可取。“上下文组装”是智能的源泉。我们不能简单地把所有记忆都塞给LLM那会超出上下文窗口且包含噪音。因此检索模块虽然目前是读取全部记忆但在未来优化时应升级为基于嵌入向量的语义检索只提取与当前问题最相关的几条记忆从而生成更精准、成本更低的提示词。3. 分步实现详解从零搭建记忆助手3.1 环境准备与依赖安装首先确保你的开发环境是Python 3.8或更高版本。我强烈建议使用虚拟环境来管理项目依赖避免污染全局环境。# 创建项目目录并进入 mkdir ai-meeting-memory cd ai-meeting-memory # 创建虚拟环境以venv为例 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate # 安装核心依赖 pip install streamlit groq python-dotenv这里解释一下每个包的作用streamlit: 用于构建Web应用界面。groq: Groq官方提供的Python SDK封装了API调用比直接用requests库更便捷。python-dotenv: 用于从.env文件加载环境变量如API密钥避免将敏感信息硬编码在代码中。接下来创建项目文件结构ai-meeting-memory/ ├── app.py # Streamlit主应用文件 ├── memory_agent.py # 核心逻辑记忆存储、检索、AI调用 ├── memory.json # 存储会议记忆的JSON文件运行后自动生成 ├── .env # 存储环境变量如GROQ_API_KEY └── .gitignore # 忽略venv、.env等文件3.2 核心逻辑层 (memory_agent.py) 实现这是项目的大脑我们将其功能封装在一个类中以提高代码的可维护性和可测试性。import json import os from datetime import datetime from groq import Groq from dotenv import load_dotenv # 加载.env文件中的环境变量 load_dotenv() class MeetingMemoryAgent: def __init__(self, memory_filememory.json): 初始化记忆代理。 :param memory_file: 存储记忆的JSON文件路径。 self.memory_file memory_file self.client Groq(api_keyos.getenv(GROQ_API_KEY)) # 初始化记忆文件如果不存在则创建一个空列表 if not os.path.exists(self.memory_file): with open(self.memory_file, w, encodingutf-8) as f: json.dump([], f) def _load_memory(self): 从JSON文件加载所有记忆。 try: with open(self.memory_file, r, encodingutf-8) as f: return json.load(f) except (FileNotFoundError, json.JSONDecodeError): # 如果文件损坏或不存在返回空列表并重置文件 with open(self.memory_file, w, encodingutf-8) as f: json.dump([], f) return [] def _save_memory(self, memory_list): 将记忆列表保存回JSON文件。 with open(self.memory_file, w, encodingutf-8) as f: # ensure_asciiFalse 确保中文等非ASCII字符正确存储 # indent2 使文件内容格式化便于人工阅读 json.dump(memory_list, f, ensure_asciiFalse, indent2) def add_memory(self, content): 添加一条新的会议记忆。 :param content: 记忆内容字符串。 memory_list self._load_memory() new_entry { id: len(memory_list) 1, # 简单的自增ID timestamp: datetime.now().isoformat(), # ISO格式时间戳 content: content.strip() # 存储前去除首尾空格 } memory_list.append(new_entry) self._save_memory(memory_list) return new_entry def get_all_memories(self): 获取所有记忆按时间倒序排列最新的在前。 memory_list self._load_memory() # 按时间戳倒序排列 return sorted(memory_list, keylambda x: x[timestamp], reverseTrue) def _is_question(self, text): 判断输入文本是否是一个问题。 这是一个简单的启发式方法可以根据需要增强。 :param text: 输入的文本。 :return: 布尔值True表示是问题。 text_lower text.strip().lower() question_words [谁, 什么, 何时, 哪里, 为什么, 如何, 怎么, 是否, 吗, , ?] # 检查是否以问号结尾或包含疑问词 return text_lower.endswith((, ?)) or any(word in text_lower for word in question_words) def _build_prompt(self, question, relevant_memories): 构建发送给LLM的提示词。 这是决定回答质量的关键环节。 :param question: 用户的问题。 :param relevant_memories: 相关的记忆列表。 :return: 组装好的提示词字符串。 # 将相关记忆格式化为上下文 context for mem in relevant_memories[-5:]: # 最多使用最近的5条相关记忆防止上下文过长 # 可以在这里格式化显示例如加上时间 date datetime.fromisoformat(mem[timestamp]).strftime(%Y-%m-%d %H:%M) context f[记录于 {date}] {mem[content]}\n prompt f你是一个专业的会议记忆助手负责根据团队的历史会议记录回答问题。 以下是相关的历史会议记录按时间倒序排列 {context} --- 请严格根据以上记录回答下面的问题。如果记录中没有明确信息请直接说“根据现有记录无法回答此问题”不要编造信息。 问题{question} 答案 return prompt def ask(self, question): 向助手提问。 :param question: 用户的问题。 :return: AI生成的答案字符串。 if not self._is_question(question): # 如果不是问题可以将其作为记忆存储或者返回提示。 # 这里我们选择将其存储为记忆因为用户可能是在陈述事实。 self.add_memory(question) return 您输入的内容似乎不是一个明确的问题。我已将其作为新的会议记录保存。 # 1. 检索记忆当前版本检索全部 all_memories self.get_all_memories() # 2. 构建提示词 prompt self._build_prompt(question, all_memories) # 目前传入全部记忆 # 3. 调用Groq API try: chat_completion self.client.chat.completions.create( messages[ { role: user, content: prompt, } ], # 模型选择mixtral-8x7b-32768 是一个性能与速度平衡的选择 # 也可以尝试 llama3-70b-8192 或 llama3-8b-8192 modelmixtral-8x7b-32768, temperature0.1, # 低温度使输出更确定、更基于事实 max_tokens500, ) answer chat_completion.choices[0].message.content return answer.strip() except Exception as e: # 友好的错误提示 return f抱歉处理您的问题时出现了错误{str(e)}。请检查网络连接或API密钥。代码解析与实操要点记忆存储格式每条记忆包含id、timestamp和content。timestamp使用ISO格式便于排序和解析。这种结构简单且易于扩展未来可以轻松添加tags、speaker等字段。问题判断逻辑 (_is_question)这是一个基础版本。在实际使用中你可能会发现一些误判。例如“通知下周一下午三点开会”会被判定为问题因为包含“吗”字。你可以通过更复杂的规则如结合句子结构或引入一个轻量级ML模型来改进它。但对于MVP简单规则已能覆盖大部分场景。提示词工程 (_build_prompt)这是连接记忆与AI的桥梁。提示词清晰界定了AI的角色、任务和约束。关键指令“严格根据以上记录回答”和“不要编造信息”对于减少AI“幻觉”至关重要。我们限制了使用的记忆条数最近5条这是为了防止上下文窗口被占满。在更复杂的版本中这里应该替换为语义检索只选取与问题最相关的记忆。Groq API调用参数model:mixtral-8x7b-32768是一个混合专家模型在理解能力和速度之间取得了很好的平衡且上下文窗口大32768 tokens。temperature: 设置为较低的0.1因为会议问答需要确定性和事实准确性而不是创造性。max_tokens: 限制生成答案的长度避免冗长回复。错误处理在ask方法中我们用try-except包裹API调用。网络超时、API密钥无效、额度不足等问题都会被捕获并向用户返回友好的错误信息而不是让整个程序崩溃。3.3 用户界面层 (app.py) 实现使用Streamlit我们可以用极少的代码构建一个直观的界面。import streamlit as st from memory_agent import MeetingMemoryAgent # 设置页面标题和图标 st.set_page_config(page_titleAI会议记忆助手, page_icon, layoutwide) # 初始化记忆助手单例模式利用Streamlit的缓存机制 st.cache_resource def get_agent(): return MeetingMemoryAgent() agent get_agent() # 应用标题和描述 st.title( AI会议记忆助手) st.markdown( 这是一个能**记住**你们团队所有会议内容的智能助手。 - **记录**将会议讨论的决策、任务分配等信息粘贴到下框。 - **询问**随时向它提问它会基于历史记忆给出答案。 - **迭代**用得越多它就越了解你们的项目。 ) # 创建两列布局左侧用于新增记忆右侧用于问答和查看历史 col1, col2 st.columns([1, 2]) with col1: st.header( 新增会议记录) new_memory st.text_area( 在此输入会议讨论要点、决策或任务分配例如张三负责UI设计李四本周五前完成API初稿, height150, keynew_memory_input ) if st.button(保存记录, typeprimary): if new_memory: with st.spinner(正在保存...): agent.add_memory(new_memory) st.success(✅ 记录已保存) # 清空输入框 st.rerun() # 使用rerun来刷新界面清空输入框 else: st.warning(请输入一些内容再保存。) # 显示最近的几条记忆 st.subheader(近期记忆预览) all_memories agent.get_all_memories() if all_memories: # 只显示最近5条 for mem in all_memories[:5]: # 格式化时间显示 from datetime import datetime time_str datetime.fromisoformat(mem[timestamp]).strftime(%m-%d %H:%M) st.caption(fID:{mem[id]} | {time_str}) st.text(mem[content][:100] (... if len(mem[content]) 100 else )) else: st.info(暂无会议记录。) with col2: st.header(❓ 向助手提问) question st.text_input(输入你的问题例如谁负责后端开发我们上次关于登录的决策是什么, keyquestion_input) if st.button(获取答案, keyask_button): if question: with st.spinner( 助手正在思考...): answer agent.ask(question) st.markdown(### 助手回答) st.write(answer) # 将问答历史存入session state以便展示 if qa_history not in st.session_state: st.session_state.qa_history [] st.session_state.qa_history.insert(0, {question: question, answer: answer}) else: st.warning(请输入一个问题。) # 显示问答历史 if qa_history in st.session_state and st.session_state.qa_history: st.subheader( 问答历史) for qa in st.session_state.qa_history[:5]: # 显示最近5条 with st.expander(fQ: {qa[question][:50]}...): st.markdown(f**问题**{qa[question]}) st.markdown(f**回答**{qa[answer]}) # 侧边栏高级选项和记忆管理 with st.sidebar: st.header(⚙️ 设置与管理) if st.button(查看完整记忆库, keyview_all): st.subheader(完整记忆库) if all_memories: for mem in all_memories: time_str datetime.fromisoformat(mem[timestamp]).strftime(%Y-%m-%d %H:%M:%S) st.markdown(f**ID {mem[id]}** ({time_str})) st.text(mem[content]) st.divider() else: st.info(记忆库为空。) # 警告清空记忆库的操作 if st.button( 清空所有记忆, keyclear_mem): st.warning(此操作将永久删除所有会议记录不可恢复) confirm st.checkbox(我确认要清空所有记忆) if confirm: try: with open(agent.memory_file, w) as f: json.dump([], f) st.success(记忆库已清空。) st.cache_resource.clear() # 清除缓存重新初始化agent st.rerun() except Exception as e: st.error(f清空失败{e})界面设计心得分栏布局使用st.columns将新增记录和问答两个主要功能并排符合用户“先记录后查询”或同时操作的自然流程。状态反馈每个操作保存、提问都配以st.spinner加载动画和st.success/st.warning提示给用户即时的操作反馈提升体验。会话状态 (st.session_state)用于临时存储问答历史这样用户在页面交互时比如切Tab历史不会丢失。这是Streamlit构建交互式应用的关键技巧。侧边栏利用将不常用的管理功能查看全部、清空放在侧边栏保持主界面整洁。数据预览在左侧栏显示最近的记忆片段让用户对已存储的内容有感知增强信任感。3.4 配置与运行获取并配置Groq API密钥访问 Groq 官网注册账号并进入控制台。在API Keys部分创建一个新的密钥。在项目根目录创建.env文件内容如下GROQ_API_KEY你的实际API密钥重要安全提示务必在.gitignore文件中添加.env切勿将包含密钥的.env文件提交到Git等版本控制系统。运行应用 在终端确保位于项目目录且虚拟环境已激活执行streamlit run app.pyStreamlit会自动在浏览器中打开应用通常是http://localhost:8501。4. 效果评估、优化方向与踩坑实录4.1 实际测试与效果展示按照上述代码部署后我进行了多轮测试。以下是一个模拟的团队会议场景记忆录入阶段输入“2023年10月26日项目启动会。决定采用微服务架构。前端由Alice负责使用React框架。后端由Bob负责主要语言是Go。数据库选用PostgreSQL。”输入“2023年10月30日技术评审。Bob提出后端API鉴权方案采用JWTAlice对此无异议。项目第一期deadline定在12月15日。”输入“2023年11月5日晨会。Alice反馈前端组件库已选定Ant Design。Bob说Go的ORM层决定用Gorm。”问答测试阶段提问“谁负责前端”助手回答“根据记录前端由Alice负责使用React框架。”提问“我们用什么数据库”助手回答“根据记录数据库选用PostgreSQL。”提问“关于API鉴权是怎么定的”助手回答“根据记录在2023年10月30日的技术评审中Bob提出后端API鉴权方案采用JWTAlice对此无异议。”提问“项目什么时候截止”助手回答“根据记录项目第一期deadline定在12月15日。”测试结论对于明确存在于记忆中的事实性信息助手能够准确提取并组织语言回答。它成功地将分散在多次会议记录中的信息关联了起来证明了“记忆”的有效性。4.2 遇到的典型问题与解决方案在开发和测试过程中我遇到了几个具有代表性的问题问题1AI的“幻觉”与过度发挥现象当提出的问题在记忆库中没有明确答案时早期的提示词下AI有时会根据自己的“知识”编造一个听起来合理的答案而不是承认不知道。解决方案这需要通过提示词工程来严格约束。最终版的提示词中我明确加入了指令“请严格根据以上记录回答下面的问题。如果记录中没有明确信息请直接说‘根据现有记录无法回答此问题’不要编造信息。” 同时将temperature参数调低0.1进一步降低随机性。实测后幻觉现象大幅减少。问题2记忆检索效率与相关性现象当前版本是每次问答都加载全部记忆。当记忆条数超过几百条时会有两个问题一是加载变慢二是无关记忆会作为噪声干扰AI可能导致答案不精准或API调用成本增加因为提示词更长。解决方案这是本项目最核心的优化点。需要引入向量化检索。流程改造在存储每条记忆时不仅保存文本还使用一个嵌入模型如text-embedding-3-small将其转换为一个向量一组数字并存储到向量数据库如ChromaDB中。智能检索当用户提问时同样将问题转换为向量然后在向量数据库中搜索与问题向量“最相似”通常用余弦相似度衡量的Top K条记忆。优势这种方式是基于语义的检索。即使问题中的用词和记忆中的用词不完全相同如“登录”和“用户认证”只要语义相近也能被检索出来。这大大提升了回答的准确性和效率。问题3简单问题判断规则的误伤现象_is_question函数会将“通知明天下午三点开会”判定为问题因为包含“吗”字导致系统将其作为问题去记忆库中搜索可能返回一个无关的答案而不是将其存储为新记忆。解决方案短期优化规则例如要求疑问词必须出现在句首或特定位置或者结合标点综合判断。长期提供一个明确的UI选择。在输入框上方增加一个单选按钮让用户主动选择当前输入是“保存新记录”还是“提出问题”。这样逻辑最清晰用户体验也最可控。我最终在思考后认为对于工具类产品明确的用户意图选择比复杂的自动判断更可靠。问题4记忆的“信息过载”与结构化现象用户可能一次性粘贴一大段包含多个要点的会议纪要。系统将其作为一条记忆存储。当后续提问涉及其中某个具体要点时这条长记忆会被整体检索出来导致提示词冗长且AI可能需要从长文本中定位信息。解决方案可以在存储前对长文本进行预处理。一种方法是使用LLM或简单的规则如按句号、分号分割将其拆分成多个独立的、语义完整的短句或要点再分别存储。这样每条记忆的信息粒度更细检索和回答会更精准。这属于记忆预处理的优化。4.3 性能优化与扩展思路引入缓存机制对于get_all_memories()这类频繁调用且数据变化不快的操作可以使用functools.lru_cache或Streamlit的st.cache_data进行缓存减少不必要的文件I/O。支持文件导入除了手动输入可以增加功能允许用户上传.txt、.md或会议转录文本文件系统自动解析并存储关键内容。记忆分类与打标为记忆增加标签如“前端”、“后端”、“决策”、“任务”用户提问时可以指定范围如“关于前端我们做过哪些决策”多轮对话与上下文记忆当前的每次问答是独立的。可以升级为支持多轮对话让助手能记住同一会话中之前的问题和答案实现更连贯的讨论。部署与共享使用Streamlit Cloud、Hugging Face Spaces或自己的服务器部署应用生成一个链接团队所有成员即可通过浏览器访问使用。4.4 安全与隐私提醒重要本项目将数据存储在本地memory.json文件中这提供了基本的隐私控制。但在实际部署时仍需注意API密钥安全确保.env文件不被泄露。在部署平台如Streamlit Cloud上应通过其Secrets管理功能设置环境变量。Groq API使用发送到Groq API的提示词中包含你们的会议记录。请确保你们团队的信息不涉及高度机密并且你已了解Groq的数据使用政策通常主流API提供商不会用请求数据训练模型但最好确认。访问控制当前版本的Streamlit应用没有登录功能任何知道地址的人都可以访问和操作。如果记录敏感信息务必通过反向代理、Streamlit内置的密码保护或集成第三方认证来增加访问控制。构建这个AI会议记忆助手的过程是一个典型的“用简单工具解决实际痛点”的实践。它没有追求大而全的功能而是聚焦于“记忆”和“问答”这个核心闭环并用最小的代价实现了可用版本。从最初的几十行脚本到如今这个具备基础UI和健壮逻辑的应用最大的收获不是代码本身而是如何围绕一个真实需求进行技术选型、迭代设计和问题拆解。这个项目就像一个乐高底座你可以很容易地往上添加“向量检索”、“自动摘要”、“任务提取”等更高级的模块让它真正成长为团队不可或缺的智能工作伙伴。