做一个能跟自己聊天的“数字助理”,听起来像很遥远的事,但它其实特别适合拿来当入门级实战项目。这个标题里的“52”,大概暗示着它是一系列动手实验中的一环,我理解为一套从零搭建聊天机器人的完整路径。我照着这个思路把项目做了一遍,先把结论放在前面:不必追求多大的模型,先踏踏实实把一个最小闭环跑通,再逐步把它打磨成“有点聪明”的助手。这篇文章不涉及特定厂商的云服务,也不依赖任何网络代理,全部内容围绕本地运行的开源方案展开。
1. 为什么要自建AI助手:先想清楚目标和边界
1.1 “造轮子”之前先搞清楚要造什么样的轮子
现在市面上的聊天机器人方案很多,有直接调云端接口的,有基于开源框架二次开发的,也有自己从模型层开始训练的。我在最开始几乎被选项淹没了,后来才发现,项目失败往往不是因为没得选,而是因为想得太多。
做这个项目之前,我给自己提了几个问题:
- 这个AI助手的核心使用场景是什么?是陪我练习外语对话,还是当本地知识库问答窗口,或者只是纯粹想研究Prompt工程和对话流程?
- 部署环境是什么?有一台随便折腾的本地机器,还是只有普通办公电脑?
- 对语言质量和实时性要求有多高?是希望回答准确、结构化,还是只要“像个人在说话”就行?
- 预算和运维能力如何?能不能承担额外的硬件成本,有没有耐心折腾依赖库和CUDA环境?
想清楚这些之后,我把目标定成了“一个可以在普通电脑上跑的、能够记住上下文、可以接入本地知识库的Web聊天助手”。不追求云端那么大的参数量,不搞分布式训练,核心是稳、可解释、看得懂每一行代码。
1.2 和“直接调用现成方案”的对比
很多同学会问,既然有已经部署好的聊天API,为什么要自己在本地折腾一套?我的理解是,两类做法解决的问题不同。
直接使用现成方案,可以快速把业务功能跑起来,适合创业团队验证产品逻辑、做MVP;自己本地部署,则更适合学习、定制和离线保密场景。把两者放在一起对比:
| 维度 | 直接调用商业接口 | 本地开源方案 |
|---|---|---|
| 上手速度 | 很快,注册拿密钥即可调用 | 较慢,需要搭建环境和理解模型 |
| 数据隐私 | 数据经第三方处理,有外泄风险 | 全流程本地,数据不出内网 |
| 可控程度 | 受接口限制,Prompt和参数自由度低 | 模型、分词、采样参数、前后处理都可控 |
| 成本 | 按Token计费,长时间使用成本高 | 一次性硬件/电费成本,长期边际成本低 |
| 学习价值 | 侧重于产品功能开发 | 能学到模型调用、推理优化、对话状态管理等底层知识 |
这个项目最终选了本地开源方案,不是因为商业接口不好,而是因为我想要那种“每一层都能打开看”的掌控感。如果想快速做商业应用,直接用商业接口更省事,但想提升技术能力,本地部署这条路值得完整走一遍。
1.3 这个AI助手的完整技术路径预览
整个项目按照“数据处理 → 模型推理 → 对话管理 → 服务暴露 → 前端交互”的主线来做,后面每个部分都会展开。
我用一个三层结构来组织:
- 底层是模型推理服务:负责把用户输入变成模型输出,通过一个内部接口暴露。
- 中层是对话管理模块:处理上下文拼接、历史消息裁剪、角色设定,它也承担“记忆”的职责。
- 上层是Web前端和API接入层:让用户可以开网页聊天,也可以让其他程序调用同一个接口。
这个结构最大的好处是分层清晰,任何一层出问题都能单独排查。比如发现回答质量不高,基本是模型参数或Prompt问题,不用去动前端;发现响应太慢,重点看推理层的显存和并发配置,不用怀疑是浏览器渲染拖后腿。
2. 环境准备与模型选型:我踩过的几个具体坑
2.1 硬件和软件的底线要求
如果你也想在本地跑一个带对话记忆的聊天机器人,硬件上不必一步到位。我实际测试下来,8GB显存的显卡已经可以流畅跑7B级别模型,16GB显存则能比较舒服地跑13B级别并留出上下文空间。没有独显的话,用CPU推理也能跑,但速度会明显偏慢,适合测试逻辑不适合实际聊天。
软件环境方面,我推荐按以下路线准备:
- 操作系统:Linux首选,Windows通过WSL2也能跑,但路径坑比较多。
- Python版本:3.10以上。
- 深度学习框架:PyTorch的CPU/GPU版本,注意安装时选择对应的CUDA版本。
- 对话大模型:选择开源的中英文对话模型,这里不绑定具体某一家,而是建议按“基础能力、授权协议、硬件要求”三个维度筛。
创建开发环境时最好使用虚拟环境。我自己的做法是先建独立Python环境,再逐步安装组件,每一步都做一次导入验证,避免一次性安装一堆库之后分不清哪个环节有问题。
2.2 模型量化:同样的模型,差距可以很大
模型加载是新手最容易焦虑的环节。明明下载了模型,显存却放不下,或者推理速度像PPT翻页一样慢。这时候要考虑量化。
对于对话模型,常见的可选项有几种:FP16/BF16是全精度,通常不会掉多少质量,但显存占用高;8位整数量的INT8是把权重压缩到一半左右,速度略快,质量下降微弱;4位整数量的INT4压缩最狠,显存占用很低,但个别场景会明显感觉到文笔变差、逻辑变松散。
我在项目里先试了全精度,效果最好,但经常爆显存;后来切到4位,小模型速度快,但长文本回答经常出现上下文重复。最终选择折中的做法:核心对话链路控制在8位精度,知识库检索这种轻量环节不做量化。
| 量化精度 | 显存占用 | 推理速度 | 回答质量 | 我的使用场景 |
|---|---|---|---|---|
| FP16 | 高 | 较慢 | 最好 | 离线评测,对比基线 |
| INT8 | 中 | 中等 | 良好 | 正式接待用户对话 |
| INT4 | 低 | 较快 | 欠稳定 | 内部函数测试 |
这里有个小技巧:很多聊天项目其实并不需要完整的数学推理能力,如果应用场景是闲聊、写文案、问答,INT8完全够用;如果要做代码生成或复杂逻辑题,建议把推理精度调高,否则会看到模型一本正经地给出错误代码。
2.3 我踩过的模型加载细节
第一个坑是分词器和模型加载路径不一致。某个开源模型仓库里同时存在多个权重版本,我图省事直接下载了最大那个,结果发现它需要额外的配置词表参数。如果你遇到“模型输出全是乱码”或“加载后只能生成重复单词”,先检查权重目录下是否有tokenizer.json和tokenizer_config.json,没有的话补齐再跑。
第二个坑是忘关推理模式。在加载模型之后训练模式可能默认开着,导致每次生成都带上梯度计算,显存直接翻倍。正确做法是加载后立刻调用.eval()方法,并配合torch.no_grad()上下文管理器来禁用梯度计算。
第三个坑是“最大生成长度”设置不当。我把最大新Token设成了512,结果聊天时模型每次回答都长篇大论,回答问题像写作文,不仅慢而且质量低。后来改成128,同时加带温度采样参数,整体输出质量明显改善,聊天节奏也自然了。
3. 最小可运行的聊天机器人:从命令行到Web接口
3.1 先别做花哨界面,用终端聊起来再说
很多人做聊天机器人项目,一上来就折腾Web前端、流式输出、排版美化,结果后端逻辑还没跑通,前端倒写了好几屏。我的建议是先用Python脚本在终端里跑通“输入一句话 → 模型返回一句话”的最小闭环。
我把这个最小实现叫做“模拟项目X”的零号版本,代码逻辑非常直接:
import sys def chat_loop(model, tokenizer): history = [] user_input = input("你:") while user_input and user_input.strip() != "exit": history.append({"role": "user", "content": user_input}) prompt = tokenizer.apply_chat_template(history, tokenize=False) response = model_answer(model, tokenizer, prompt) # 这里封装模型推理 history.append({"role": "assistant", "content": response}) print(f"AI:{response}") user_input = input("你:") def model_answer(model, tokenizer, prompt): inputs = tokenizer(prompt, return_tensors="pt").to(model.device) outputs = model.generate( **inputs, max_new_tokens=128, do_sample=True, temperature=0.7, top_p=0.9, repetition_penalty=1.1, ) response = tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True) return response.strip()这个流程的核心在于把对话历史封装成模型要求的模板格式。不同模型的模板差异很大,有些需要特殊的“系统提示符”,有些需要“[INST]”开头。我建议在写业务代码之前,先调试模板本身是否能用,否则后续所有对话逻辑都建立在“错误的前提”上。
3.2 为什么我先坚持用终端而不是直接写网页
终端版本虽然丑,但它最大的优势是“能最快暴露出核心问题”。模型能否正常加载、模板是否匹配、采样参数是否合理、历史对话能不能拼接正确,这些问题在终端下用最小代码量就能定位。我见过不少同学同时写后端和前端,结果前端报错、后端也报错,查来查去花了一晚上发现只是模型路径写错了。
终端版本跑通之后,我做的第一件事不是写界面,而是把模型推理封装成独立的服务类,为后面Web接口做准备。封装的好处在于:终端和网页共用一套推理代码,修改模型参数只需要动一个文件,不必四处查找。
3.3 从一次“答非所问”定位到Prompt模板问题
在我的最小闭环测试里,第一次对话效果还可以,第二次就开始答非所问。排查后发现问题出在对话模板:模型的模板要求系统提示和用户输入之间插入特定的分隔标记,我漏掉了标记,导致模型把历史输入和当前问题混在一起。
这也是自建助手最值得关注的地方:模型生成质量,一半靠权重,另一半靠Prompt工程。模板不对,再好的模型也会答成“呓语”。建议每次修改模板后做一组固定用例回归测试,比如“你是谁”“1+1等于几”“写一首五言绝句”,方便快速定位变化来源。
4. 对话记忆机制:让AI记得你们半小时前聊过什么
4.1 记忆的真相:其实只是一段拼接起来的文本
聊过一阵子之后,AI好像“记得”你说过的话,实际原理很简单:我们把历史消息全部放进输入上下文,模型通过注意力机制“回顾”之前的内容。所谓记忆,本质上就是输入文本的重新拼接。
在代码里,管理记忆的任务由“对话历史列表”完成:
{MODEL_SYSTEM}这是一个友好的本地AI助手 {传统方式} [ {"role": "user", "content": "我最近在学做咖啡"}, {"role": "assistant", "content": "那你有喜欢的豆子品种吗?"}, {"role": "user", "content": "喜欢耶加雪菲"}, {"role": "assistant", "content": "这款豆子风味很清爽,适合做手冲。"}, ]每次新对话,只是把新的用户输入追加到这个列表最后,再整体作为提示词发给模型。
4.2 上下文窗口溢出:现在的内容太多,模型“看不过来”
但这里有一个绕不开的限制:模型有最大上下文窗口。8K、32K、128K听起来很大,可是一旦把系统设定、多轮对话、工具调用结果都装进去,很快就会到达上限。
我在持续对话测试中遇到过这样一个场景:用户问了二十多轮,中间还涉及几段粘贴的文章,结果模型开始重复说前面的话,甚至“忘记”了自己是AI助手。后来我加上了打印“实际送入的Token数”的日志,发现每次请求已经接近窗口上限,这直接导致模型输出质量失控。
解决溢出有几种典型方案:
- 截断:丢掉最老的对话,保留最近的。实现简单,但丢掉了早期可能重要的信息。
- 摘要:每次对话结束后,把早先的历史用一句话概括,再塞进上下文。记忆更紧凑,但需要额外调用一次模型,响应变慢。
- 滑动窗口+摘要混合:既保留最近几轮完整消息,又让模型为更早的内容做一个整体概括。这是我最终采用的方式,实测平衡性最好。
4.3 简单摘要记忆的实现思路
摘要方案的核心逻辑,可以看作“压缩对话历史”。关键代码如下:
class MemoryManager: def __init__(self, max_tokens=2048): self.history = [] self.max_tokens = max_tokens self.summary = "" def add_message(self, role, content): self.history.append({"role": role, "content": content}) def build_prompt(self): # 如果历史折算后的token超过max_tokens,就调用模型概括更早的内容 if estimate_tokens(self.history) > self.max_tokens: old_messages = self.history[:-8] self.summary = summarize_with_model(old_messages, self.summary) self.history = self.history[-8:] messages = [] if self.summary: messages.append({"role": "system", "content": f"这是之前的对话摘要,请参考:{self.summary}"}) messages.extend(self.history) return messages这里有个细节值得注意:摘要一旦生成就不会每条消息都更新,否则每次都让模型“回忆并总结”,既慢又可能打乱主线。我是每5轮对话或者上下文将溢出时才触发一次摘要,既控制了延迟,也不会丢失太多核心信息。
4.4 多轮对话中的“角色设定”应该放在哪个位置
很多聊天机器人的个性塑造依赖系统提示。我的建议是:把角色设定和长期偏好放在系统消息里,而不是每条用户消息都重复一遍。系统消息只传一次,既省Token又能保持稳定的“人设”。
如果用户希望AI记住自己的名字或者喜好,可以先让模型把这类信息抽取出来,写入一份持久化的记忆文件,下一次启动时自动载入。把这种机制叫“长期记忆”会比“长期摘要”更准确,因为摘要只是为了压缩上下文,而长期记忆服务于跨会话场景。
5. 服务化部署与Web交互:把机器人接到网页上
5.1 为什么我选择FastAPI作为应用入口
终端跑通之后,下一步是把这个机器人做成可以被Web页面调用的服务。我选择FastAPI作为后端框架,原因很直接:
- 异步支持好,模型推理虽然本质是同步阻塞操作,但可以通过线程池包装,避免阻塞事件循环。
- 自带交互式API文档,调试接口很方便。
- 用Pydantic做请求体校验,前端传参错误能快速得到提示。
需要说明的是,这里的“选型”不是唯一答案。如果团队更熟Flask,那用Flask也完全可以;关键是职责划分清晰,别把模型逻辑直接堆进Web路由函数里。
5.2 我写的Chat接口长这样
接口设计遵循一个原则:客户端只传“用户说的话”,服务端负责拼接历史、调用模型、返回回答。这样前端代码简单,后续接入其他应用也方便。
from fastapi import FastAPI from pydantic import BaseModel class ChatRequest(BaseModel): message: str session_id: str = "default" class ChatResponse(BaseModel): reply: str history_tokens: int app = FastAPI() chat_engines = {} def get_engine(session_id): if session_id not in chat_engines: chat_engines[session_id] = ChatEngineWithMemory() return chat_engines[session_id] @app.post("/chat", response_model=ChatResponse) async def chat(request: ChatRequest): engine = get_engine(request.session_id) reply, token_count = engine.process(request.message) return ChatResponse(reply=reply, history_tokens=token_count)session_id的作用很关键:它为不同用户维护独立的多轮会话状态。如果不区分session,那所有用户都共用一条对话历史,一个用户问“我家的猫叫什么”,另一个用户马上能看到答案,这显然不合理。
5.3 用Streamlit搭一个看起来体面的聊天界面
后端接口有了,我选择用Streamlit快速搭一个Web界面,主要看中它“用纯Python写前端逻辑”的开发效率。页面代码很短,核心是一个消息列表和两个按钮:
import streamlit as st import requests st.set_page_config(page_title="本地AI助手", page_icon=None, layout="centered") if "messages" not in st.session_state: st.session_state.messages = [] for msg in st.session_state.messages: role = "user" if msg["role"] == "user" else "assistant" st.chat_message(role).write(msg["content"]) user_input = st.chat_input("想和你的AI助手说点什么?") if user_input: st.session_state.messages.append({"role": "user", "content": user_input}) resp = requests.post("http://127.0.0.1:8000/chat", json={"message": user_input}) reply = resp.json()["reply"] st.session_state.messages.append({"role": "assistant", "content": reply}) st.rerun()这个界面的好处是降级友好:即使AI服务没启动,页面也能显示用户输入,只是拿不到回复而已。实际开发时,页面只保留最近30条消息展示即可,不必把整段历史都渲染出来,原生DOM节点太多会导致浏览器卡顿。
5.4 流式输出:让“等模型思考”的过程不再煎熬
本地模型推理通常需要几秒甚至几十秒,如果干等着转圈,体验非常不好。流式输出是解决这个问题的标配做法。实现了流式输出之后,用户体验会有本质提升:模型每生成一个字就立刻推到页面上,像打字机一样,用户知道“它正在动脑筋”,而不是觉得“系统挂死了”。
后端流式接口开发的关键,在于生成器逐块返回:
async def stream_chat(message: str, session_id: str): engine = get_engine(session_id) async for chunk in engine.process_stream(message): yield f"data: {json.dumps({'delta': chunk}, ensure_ascii=False)}\n\n"前端配合类似EventSource或fetch的流式读取方式,逐段渲染。实现过程中需要重点处理断线重连、缓存历史、异常结束补全等细节,工作量其实比想象中大,但这是聊天机器人“像助手”而不是“像命令行工具”的分水岭。
6. 知识库增强:让聊天助手了解你的私人文档
6.1 什么是“外挂知识库”
只靠模型内部知识,它会不知道你本地文档里写了什么。知识库增强(社区常说的RAG)是解决这个问题的常见路线:先把文档拆成小块、向量化、存进索引;用户提问时,先检索出相关内容,再把这些内容拼到Prompt里,让模型参考回答。
整个过程可以拆成三个环节:
- 文档加载与切分:把Markdown、PDF、Word文档读成纯文本,按固定块大小切分,保证每块语义相对完整。
- 向量化与索引:用嵌入模型把文本块转成向量,存入向量数据库。
- 检索与增强生成:用户问题转成向量后,检索最相似的几个文本块,拼进系统提示。
6.2 一个面向本地知识库的简单检索流程
我在项目里没有引入重量级向量库,而是先用一个轻巧的JSON索引:
import json import numpy as np class LocalKnowledgeBase: def __init__(self, store_path): self.blocks = [] self.embeddings = [] self.store_path = store_path def add_documents(self, doc_chunks, embed_func): for chunk in doc_chunks: self.blocks.append({"content": chunk}) self.embeddings.append(embed_func(chunk)) self._save() def query(self, question_embedding, top_k=3): scores = [np.dot(question_embedding, embed) for embed in self.embeddings] best_indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:top_k] return [self.blocks[i]["content"] for i in best_indices]这里不必一次就上复杂的检索策略,先把“用户问题 → 找最像的几个片段 → 模型结合片段回答”的链路走通,比一开始就优化检索算法重要得多。
6.3 我踩过的“上下文化截断”问题
文本切分不是简单按字符数切。一开始我用每512个字符切一块,结果不少块从段落中间断开,语义被割裂,检索命中后模型经常读到半句话。后来改成按段落切分并允许重叠部分重叠,比如每块600字,重叠100字,情况立刻改善。
另一个被忽视的点是:知识库回答时,模型可能会一本正经地编造文档里没有的内容。要缓解这一点,Prompt里要明确写清楚“只能基于给定内容回答,不要脑补;如果内容里没有,就直接说不知道”,同时可以把信源块编号一并传进去,让回答者说明依据。
7. 部署上线与安全合规:一次踩坑记录和自查清单
7.1 我在部署时遇到的三类问题
这个项目部署上线时,我连续踩了几个问题,分享出来希望你能跳过。
第一个是“并发请求导致模型进程崩了”。我的Web服务默认是并发处理请求,多个用户同时调AIGC接口时,模型显存被多个生成任务撑爆,服务直接OOM。解决办法是在模型层做一个全局锁(或者使用信号量),限制同一时间只允许一个生成任务,其余请求排队等待。对个人工具来说,这个“队”完全够用。
第二个是“生成内容出现不合规词汇”。本地模型并不天然具备内容审核能力,必须自己做前置检查后置过滤。我在输入输出两侧都加了过滤机制,同时在Prompt里约束模型对敏感话题拒绝回答。这个环节不是小题大做,生成内容失控才叫麻烦。
第三个是“API服务暴露到公网没有加任何认证”。我一开始只是为了自己在局域网里访问,结果随手映射到公网端口,没有任何鉴权,随后就被扫描工具盯上了。后来我把服务改成仅监听本地回环地址,需要远程访问时再套一层安全网关,并把接口加上访问令牌。
7.2 一张可复制的上线自查清单
上线前逐项核对,能少走很多弯路:
- 端口绑定:只监听127.0.0.1或内网IP,不直接暴露公网端口。
- 鉴权机制:接口启用访问令牌,Web界面登录后可调用。
- 模型路径权限:模型权重和知识库文件按最小权限设置,禁止Web应用直接读取任意路径。
- 输入长度限制:限制单条消息最大字符数,防止极端输入把上下文撑爆。
- 输出过滤:启用关键词过滤和内容安全模板,对特殊类型请求先拦截后放行。
- 知识库边界:禁止用户输入文件路径;只允许检索预置文档集合。
- 日志脱敏:不在日志里打印完整用户消息,可能含身份证、手机号等敏感信息。
- 并发控制:单实例生成任务加信号量,避免显存溢出。
- 异常兜底:模型生成超时或崩溃时,接口返回友好提示而不是直接把内部堆栈抛给前端。
7.3 数据隐私、物理隔离与合规意识
本地部署最大的卖点就是数据不出内网。但这不等于天然安全。用户的聊天内容仍然存在于日志文件、历史记录和模型上下文中,如果服务器被人拿去,隐私照样泄露。
我在项目中明确做了一件小事:历史记录只保留最近若干条在内存中,不落盘。这样即使服务崩溃,也不会有整段聊天记录进到磁盘。聊天机器人可以保存用户偏好,但保存之前必须考虑清楚“这个信息有多敏感”,以及“用户是否同意被记录”。
在内容合规方面,本地模型生成的文本应当遵守现行法律法规,尊重社会公序良俗,避免生成任何涉及不当题材或可能误导他人的内容。安全过滤是聊天机器人上线的硬门槛,不是可有可无的加分项。
8. 性能调优与质量评估:把“能聊”变成“聊得好”
8.1 响应延迟瓶颈在哪里
本地模型聊天,响应延迟主要来自两个环节:序列化/反序列化时间和模型生成时间。其中模型生成时间和生成Token数近似成正比,几乎占80%的耗时。
我做了一次简单压测:512个字符的答案,在显卡推理下耗时约4秒,CPU模式下约40秒。CPU模式下,光是注意力计算就占了六成开销。优化手段通常是三层:
- 换量化模型,让CPU也能跑。
- 缩短max_new_tokens,让模型别啰嗦。
- 用批处理或缓存KV Cache,提升连续对话效率。
8.2 模型采样参数:温度、Top-p和重复惩罚的调参思路
对话质量跟采样参数关系很大。我观察到的经验:
- 温度调高,模型更有创造性但也更容易胡扯;调低更稳定但有时显得呆板。
- Top-p和温度配合使用,限制随机样本的质量。0.8到0.95区间适合通用对话,太低容易变成复读机。
- 重复惩罚对中文很管用。中文模型一旦开启高重复惩罚,回答会变得凝练很多,但如果设太高又会显得生硬,像在跟机器说话。
不同场景推荐参数:
| 场景 | 温度 | Top-p | 重复惩罚 |
|---|---|---|---|
| 闲聊寒暄 | 0.8 | 0.90 | 1.05 |
| 知识问答 | 0.3 | 0.85 | 1.10 |
| 文案创作 | 0.95 | 0.95 | 1.02 |
| 代码生成 | 0.2 | 0.80 | 1.05 |
8.3 质量评估:不要只靠“感觉还可以”
最后评测阶段,如果完全靠人肉聊天看效果,很容易忽视回归问题。我为项目建立了一个固定的“问题集”,包含二十个典型问题,比如“自我介绍”“数学题”“常识判断”“中文文化题”“代码题”“拒绝回答题”。每次调整模型或Prompt后,跑一遍问题集,记录三类指标:
- 答案质量:是否完整、准确、有逻辑。
- 拒绝效果:对敏感/超纲问题是否干脆拒绝。
- 稳定程度:同样问题跑三次,是否每次输出相近。
这样调参就有依据,不像部分项目全靠“感觉这次比上次聪明了”。我把这个动作叫做“一轮基线评测”,虽然简陋,但在个人开发阶段足够了。
9. 回头总结一下我的实际操作体会
这个项目从头做完一遍,我最大的感受是:聊天机器人远不止“调用大模型”那么简单。模型推理只是其中一小块,更花心思的是对话记忆管理、Prompt模板组装、服务部署、并发控制、内容过滤和知识库增强。每一步单独拿出来都不算难,但它们组合在一起,复杂度是指数上升的。
如果有人要走同样的路,我的建议是先做减法。第一步只做命令行助手,聊十句话不断线就算成功;第二步接Web界面,能流畅聊二十轮不卡;第三步加知识库和记忆摘要,让助手开始变得“有用”。每做完一步,都跑一遍基线评测,确认没有回归问题再往前走。
我最后还给这个小项目加了一个很朴素的功能:把用户经常问的问题和答案用向量存起来,下次遇到完全相似的问题时,先过滤掉,不用再“麻烦”大模型思考一遍。这对于个人备忘、常用问答场景效果很好,也算是我在这次项目里觉得最实用的小技巧。接下来如果你也想深入优化这个方向,可以从“上下文压缩算法”“多会话隔离”“基于检索的对话路由”这几个角度继续挖,每一步都能单独水出好几篇文章。