ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从零搭建越用越懂你的个人智能体:FastAPI+DeepSeek四层架构实战

从零搭建越用越懂你的个人智能体:FastAPI+DeepSeek四层架构实战 1. 为什么我要自己造一个“越用越懂你”的智能体先说结论市面上能用的智能体平台我几乎都试过一遍从拖拽式的到纯代码的最后让我下定决心自己动手的原因只有一个——它们记不住我。每次开新会话我都得像第一次见面一样重新交代背景、偏好、工作习惯这种体验就像你雇了个助理结果他每天上班前都要失忆一次。所以这个系列的第一篇我不聊虚的直接把一个能长期运行、能积累记忆、能调用工具的个人智能体架构拆开给你看。这个智能体我内部叫它“超体”核心目标很朴素越用越懂你。它不是那种问一句答一句的聊天机器人而是一个有记忆层、有工具层、有调度层的完整系统。你用得越多它对你了解越深能替你做的事也越多。适合谁来参考如果你已经会用 Python 写点脚本对 FastAPI 不陌生想从“调 API 玩一玩”进阶到“搭一个真正能用的 Agent”那这篇就是写给你的。如果你完全没碰过代码也没关系我会把每个模块为什么这么设计讲清楚你至少能看懂一个智能体到底由哪些零件组成。我选择 FastAPI 作为整个系统的骨架不是因为它火而是因为它够轻、够快、异步支持好而且和 Python 生态里的 AI 库配合起来几乎没有摩擦。DeepSeek 作为推理内核负责理解意图和生成决策它的 API 调用成本可控响应速度在可接受范围内。整个架构我拆成了四层接入层、调度层、记忆层、工具层。这四层各司其职后面我会一层一层拆开讲包括每一层我踩过的坑和最后定下来的方案。2. 整体架构设计与分层思路2.1 四层架构的职责划分一个能长期运行的智能体最怕的就是把所有逻辑塞在一个文件里。我见过太多项目一开始就是一个main.py里写几百行后面想加个记忆功能就得重构半天。所以我在动手之前先把职责边界划清楚。接入层负责和外界打交道包括 HTTP 接口、WebSocket 长连接、以及未来可能接入的桌面客户端。这一层不处理任何业务逻辑只做请求解析和响应封装。调度层是大脑接收用户输入后决定走哪条路径是直接回答还是调用工具还是先查记忆再回答。记忆层负责存储和检索包括短期对话上下文和长期用户画像。工具层是手脚封装了搜索、计算、文件操作、外部 API 调用等具体能力。这样分的好处是每一层都可以独立替换。比如我后来把记忆层从简单的 JSON 文件换成了向量数据库调度层完全不用改。工具层加一个新工具也只需要注册进去不影响其他部分。2.2 为什么选 FastAPI 而不是 Flask 或 DjangoFlask 太轻轻到很多异步场景要自己造轮子Django 太重自带的那套 ORM 和 Admin 对一个智能体后端来说完全是负担。FastAPI 刚好卡在中间原生支持async/awaitPydantic 做数据校验省心自动生成 OpenAPI 文档方便调试而且性能实测下来比 Flask 高出一截。更重要的是FastAPI 的依赖注入系统非常适合做工具注册。我可以把每个工具写成一个独立的函数通过依赖注入挂到路由上调度层只需要根据意图去查表调用就行。这种设计让工具扩展变得非常干净。2.3 DeepSeek 在架构中的角色定位DeepSeek 在这个架构里不是“全部”而是“决策核心”。它负责三件事理解用户意图、生成工具调用参数、以及在没有工具可用时生成自然语言回复。但记忆的存储和检索、工具的注册和执行、会话的管理这些都不归它管。我见过一些项目把什么都丢给模型结果就是每次请求都要把全部历史塞进 prompttoken 消耗巨大响应还慢。我的做法是模型只拿它需要的那部分上下文。调度层会先判断这次请求需不需要查长期记忆需要的话只检索最相关的几条再拼进 prompt。这样既省 token又提高了回答的精准度。3. 核心模块拆解与关键实现细节3.1 记忆层让智能体真正“记住”你记忆层是整个“越用越懂你”的核心。我把它分成两块短期记忆和长期记忆。短期记忆就是当前会话的对话历史我用一个滑动窗口来管理默认保留最近 20 轮对话。超过的部分会被压缩成摘要存进长期记忆。这里有个细节压缩不是简单截断而是让 DeepSeek 生成一段简短摘要保留关键信息。这样即使对话很长早期的重要信息也不会丢。长期记忆我一开始用 JSON 文件存后来数据量大了检索太慢换成了 SQLite 加向量索引。具体做法是每条长期记忆存三样东西——原始文本、向量表示、以及元数据时间戳、标签、重要程度。检索的时候先用向量相似度找候选再按重要程度和时间衰减排序。重要程度是让模型自己打的标签比如用户说“我以后都用中文回复”这条就会被标记为高重要度。注意长期记忆的写入不要每轮都做那样会产生大量冗余。我的策略是每 5 轮对话触发一次记忆提取或者当用户明确表达偏好时立即触发。3.2 调度层意图识别与路由决策调度层的工作流程是这样的收到用户输入后先做意图分类。我把意图分成三类直接回答、工具调用、记忆操作。分类不是单独训一个模型而是用 DeepSeek 的 function calling 能力在 prompt 里定义好可用的工具和记忆操作让模型自己选。这里有个坑我踩过如果工具描述写得太模糊模型会频繁误调用。比如我早期把“搜索”工具描述成“查找信息”结果模型连“今天天气怎么样”都要去调搜索。后来我把描述改具体“当用户询问实时信息、新闻、或你不确定的事实性内容时使用”误调用率立刻降下来了。路由决策还有一个关键点是兜底策略。如果模型返回的调用参数不合法或者工具执行超时调度层要能捕获异常并降级到直接回答。我实测下来加上兜底之后整个系统的可用性从 85% 提升到了 99% 以上。3.3 工具层注册机制与执行隔离工具层我设计了一个装饰器来注册工具用起来大概是这样tool(namesearch, description当用户询问实时信息或你不确定的事实时使用) async def search(query: str) - str: # 具体实现 ...装饰器会把工具的名称、描述、参数 schema 自动注册到一个全局 registry 里。调度层需要工具列表时直接从 registry 拿拼进 prompt 的 function 定义部分。执行隔离这块我做了两层保护一是超时控制每个工具调用默认 10 秒超时超时直接返回错误信息让模型重新决策二是异常捕获工具内部抛出的任何异常都会被包装成结构化错误返回不会导致整个请求崩溃。实操心得工具的描述文字直接决定了模型的调用准确率。我建议每个工具描述都包含“什么时候用”和“什么时候不用”两部分实测能减少 40% 以上的误调用。4. 从零搭建的完整实操流程4.1 项目目录结构设计我最终定下来的目录结构是这样的super-agent/ ├── app/ │ ├── main.py # FastAPI 入口 │ ├── config.py # 配置管理 │ ├── api/ │ │ ├── routes.py # 路由定义 │ │ └── schemas.py # Pydantic 模型 │ ├── core/ │ │ ├── dispatcher.py # 调度层 │ │ ├── memory.py # 记忆层 │ │ └── tools.py # 工具注册与执行 │ ├── tools/ │ │ ├── search.py │ │ ├── calculator.py │ │ └── file_ops.py │ └── models/ │ └── deepseek.py # DeepSeek API 封装 ├── data/ │ ├── memory.db # SQLite 记忆库 │ └── vectors/ # 向量索引文件 ├── tests/ ├── requirements.txt └── README.md这个结构的好处是每一层都有明确的归属新人接手也能快速定位。core目录放核心逻辑tools目录放具体工具实现api目录只做接口定义。4.2 环境准备与依赖安装Python 版本我用的 3.11低于 3.10 的话有些异步语法会报错。依赖清单如下pip install fastapi uvicorn httpx pydantic python-dotenv pip install numpy sqlite-vec # 向量检索相关DeepSeek 的 API key 放在.env文件里不要硬编码进代码。我见过有人把 key 直接写在main.py里然后推到公开仓库结果被人刷了几百块。用python-dotenv加载配合.gitignore排除.env文件这是基本操作。4.3 DeepSeek API 封装与调用参数调优封装层我做了三件事统一错误处理、自动重试、以及 token 计数。调用参数上temperature我设成 0.3因为智能体需要稳定决策不需要太多创造性。max_tokens根据场景动态调整意图分类时设 256 就够生成回复时设 2048。重试策略是遇到 429 或 5xx 错误时等待 1 秒后重试最多重试 3 次。实测下来加上重试之后因网络抖动导致的失败基本消失了。4.4 记忆库初始化与向量检索配置SQLite 建表语句如下CREATE TABLE memories ( id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT NOT NULL, embedding BLOB, importance INTEGER DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );向量检索我用sqlite-vec扩展它可以直接在 SQLite 里做相似度查询不需要额外部署向量数据库。对于个人智能体这个量级完全够用。检索时先按向量相似度取 top 20再按重要程度和时间衰减重排最后取 top 5 拼进 prompt。注意向量维度要和 DeepSeek 的 embedding 输出维度对齐我用的 1024 维。维度不匹配的话检索结果会完全乱掉。5. 常见问题与排查技巧实录5.1 模型不调用工具怎么办这是最常见的问题。排查顺序是先看工具描述是否清晰再看 prompt 里有没有明确指示“需要时调用工具”最后检查 function calling 的 schema 是否合法。我遇到过一次是参数类型写错了把string写成了str模型直接忽略了这个工具。5.2 记忆检索结果不相关通常是向量模型选得不对或者检索时没有做重排。我的经验是向量相似度只做粗筛一定要加一层基于重要程度和时间衰减的重排。另外记忆写入时的文本质量也很关键如果存进去的就是一堆碎片检索出来自然没用。5.3 并发请求下响应变慢FastAPI 本身是异步的但如果工具层里有同步阻塞操作整个事件循环都会被卡住。我的做法是所有工具都写成async函数内部用httpx.AsyncClient做网络请求文件操作也尽量用异步库。实测下来单机并发 50 个请求平均响应时间能控制在 2 秒以内。5.4 常见问题速查表问题现象可能原因排查方向模型不调用工具工具描述模糊或 schema 错误检查 description 和参数类型记忆检索不准向量维度不匹配或缺少重排核对维度加重要度排序响应超时工具阻塞事件循环改异步实现加超时控制重复调用同一工具缺少调用次数限制在调度层加最大调用轮数长期记忆膨胀写入频率过高降低触发频率加去重逻辑5.5 我踩过的三个坑第一个坑是把记忆写入放在了请求主流程里导致每次对话都要等记忆写完才返回响应慢了一倍。后来改成后台任务异步写入体验立刻上来了。第二个坑是工具超时没设有一次搜索工具卡住整个请求挂了 30 秒。加上 10 秒超时之后最坏情况也能在 10 秒内降级返回。第三个坑是prompt 里塞了太多历史token 消耗飞快。后来改成只检索相关记忆token 用量降了 60%回答质量反而更好了。这个架构我跑了三个月迭代了十几个版本目前稳定支撑我日常的写作辅助、信息检索和日程管理。后面几篇我会分别拆解记忆层的向量检索优化、工具层的动态加载、以及怎么把这个智能体打包成桌面应用。如果你也在搭自己的智能体希望这篇能帮你少走点弯路。
返回列表