基于鲲鹏openEuler的Agent Memory记忆管理系统设计与实践
2026/9/7 17:53:02 网站建设 项目流程

在 2026 中国国际大学生创新大赛的 openEuler 方向命题里,“基于鲲鹏平台的 Agent Memory 记忆管理系统”是一个把大模型应用和国产基础软件结合得非常紧密的题目。Agent 在大模型对话、智能客服、办公助手等场景中越来越常见,但模型本身没有记忆能力:每一次对话结束后,上下文就消失了。Agent 要连续完成任务、记住用户偏好、在后续会话中复用之前的结果,就必须有一套独立的记忆管理系统。这个系统承担的不只是“把聊天记录存下来”,而是要在合适的时机写入关键信息、在需要时快速召回、在记忆过时后更新或遗忘。这篇文章会沿着一条可复现的技术路线展开:先在鲲鹏平台的 openEuler 系统上搭建依赖环境,再实现一个包含持久化、检索和衰减策略的 Agent Memory 服务,最后用一组接口验证记忆闭环是否成立。文章也会把备赛过程中最容易卡住的环境问题整理成排查清单,帮助参赛队伍从“能演示”走向“能讲清楚”。

1. 理解命题:Agent Memory 到底要管理什么

1.1 先看懂 Agent 为什么需要记忆

大模型本身是无状态的。对于同一个模型,输入同样的问题,即使放在多轮对话中,它也无法自发记住五分钟前用户说过什么。现在的聊天应用之所以看起来有上下文,是因为应用层在每次请求前把历史消息拼进 prompt。但这种方式有两个明显问题:

  • 成本问题:上下文越长,token 消耗越高,响应越慢。
  • 检索精度问题:所有历史消息都塞进去,信息噪声会干扰模型输出。

Agent 场景比单轮问答更复杂。Agent 需要在多个步骤之间保留中间结果,需要记住用户长期的偏好,需要区分哪些信息是临时的、哪些是跨会话复用的。这类需求催生了 Agent Memory,也就是把记忆从模型外部独立出来管理。

记忆管理系统并不是一个数据库那么简单。它至少要能回答几个问题:

  • 什么值得记?
  • 怎么存才能方便后续快速找到?
  • 记忆什么时候应该更新?
  • 记忆什么时候应该被遗忘或合并?

1.2 一个合格的记忆系统要包含哪些能力

从工程实现角度看,Agent Memory 的核心能力可以拆成四条:

  • 写入:从对话或 Agent 执行结果中抽取值得保存的信息,结构化存储。
  • 读取:将记忆注入到 Agent 的 prompt 或工具上下文中。
  • 更新:当新信息和旧记忆冲突时,决定是覆盖、合并还是保留多个版本。
  • 遗忘:记忆不是永久不变的。当长时间未访问或重要度降低时,需要衰减或清理。

只做存储和读取,那和普通 KV 存储没有区别。大赛命题强调“管理系统”,重点往往在于:有没有写入策略、有没有检索排序、有没有衰减机制、有没有可视化和评估手段。

1.3 为什么命题要绑定鲲鹏平台和 openEuler

这个命题选择鲲鹏平台,而不是普通 x86 服务器,原因可以从三个层面看:

  • 基础软件适配:鲲鹏是 ARM 架构,底层指令集不同,很多软件需要 aarch64 版本。openEuler 作为开源操作系统,对鲲鹏有原生支持,两者结合能减少适配成本。
  • 产业需求:国产服务器和国产操作系统在不少政企场景已经落地,AI 应用跑在国产底座上,已经成为实际交付场景。
  • 大赛考察点:命题要求基于鲲鹏平台,本质上是希望参赛队伍能证明自己在国产计算平台上具备独立搭建、部署、调优 AI 系统的能力。

要注意,openEuler 同时支持 x86_64 和 aarch64,如果只在自己的 x86 笔记本上装虚拟机,也能写代码,但最终演示和运行最好放在鲲鹏环境上,这样能早一点发现架构差异带来的问题。

1.4 从命题到作品:应该交付什么

结合大赛通常的评审习惯,一个完整作品至少包括:

  • 可运行代码仓库,包含清晰的 README 和部署文档。
  • 一个能演示的最小闭环:Agent 对话后记忆被保存,下次对话可以复用。
  • 核心机制说明:写入、检索、更新、遗忘策略分别如何实现。
  • 测试数据和评估方式:比如一组模拟对话,能证明记忆系统确实提升了召回效果。
能力需要回答的问题设计要点
写入哪些内容要进入记忆库抽取规则、重要度评分、去重
存储用什么数据结构保存结构化字段、向量索引、版本管理
读取召回哪些记忆交给模型检索排序、相关性过滤、上下文截断
更新新旧记忆冲突怎么处理覆盖、合并、置信度比较
遗忘记忆什么时候失效时间衰减、LRU、容量上限

2. 系统设计:从需求到模块拆分

2.1 整体架构:分四层

参赛作品在架构上不需要一开始就追求复杂,但要边界清晰。系统可以分成四个层次:

  • 接入层:HTTP API,给 Agent 提供写入、查询、更新、删除接口。
  • 记忆管理引擎:负责记忆的抽取、评分、去重、更新、衰减。
  • 存储层:Redis 做缓存和快速访问,SQLite 做持久化,可选向量库做语义检索。
  • 基础环境:openEuler + Python 3.9+ + Redis + Docker。

这样的分层好处是:每一层都能单独测试。答辩时可以讲清楚“接口层如何对接 Agent”“策略层如何决定记忆去留”“存储层如何保证重启不丢失”。

2.2 技术选型:为什么不用一个数据库解决所有问题

设计记忆系统时常见的误区是“用一个向量数据库搞定”。实际上,记忆可以分为显式记忆和语义记忆:

  • 显式记忆:用户姓名、偏好、任务状态,这类是结构化数据,用 Redis 或关系型数据库更合适。
  • 语义记忆:一段话的具体内容,适合向量化后检索。

用 Redis 存储结构化字段,用向量存储做语义召回,用 SQLite 或 MySQL 做长期历史归档。对参赛作品来说,Redis + SQLite 足够支撑演示;如果时间充裕,可以再接入向量库。

组件作用推荐软件说明
系统运行底座openEuler 22.03 LTS SP4对鲲鹏适配好
运行时应用开发Python 3.9+AI 生态丰富
缓存/索引快速读写Redis 7.x支持过期策略
持久化归档备份SQLite 3.x零配置,适合原型
API接口框架FastAPI 或 Flask便于快速开发
向量检索语义召回faiss / hnswlib可选组件

2.3 记忆数据模型:每条记忆保存哪些字段

记忆数据模型需要兼顾“检索效率”和“表达力”。下面这个 JSON 结构可以作为一条记忆的最小单位:

{ "memory_id": "uuid", "content": "用户李明的偏好是希望回答尽量简洁", "memory_type": "user_profile", "importance": 0.85, "access_count": 3, "created_at": "2026-05-01T10:00:00Z", "updated_at": "2026-05-01T11:30:00Z", "metadata": { "source": "session_001", "agent_id": "customer_service", "expire_policy": "sliding" } }

字段含义说明:

  • memory_id:全局唯一,用 UUID 生成。
  • content:记忆正文,通常是抽取出的事实或偏好。
  • memory_type:记忆类型,例如 user_profile、conversation_fact、task_result。
  • importance:重要度评分,控制记忆在召回时的权重。
  • access_count:访问次数,用于热度排序。
  • created_at 和 updated_at:创建和更新时间,用于衰减策略。
  • metadata:扩展字段,保存来源会话、Agent 编号等上下文信息。

2.4 记忆生命周期流程

一条记忆不是写入数据库后永远不变的,它会经历几个阶段:

  1. 写入:抽取出事实,计算重要度,写入 Redis 和 SQLite。
  2. 活跃:被检索命中后,访问次数增加,updated_at 刷新。
  3. 衰减:长时间没有被命中,热度降低,在结果中的排序下降。
  4. 遗忘:超过 TTL 或容量上限后,Redis 中的缓存被清理,SQLite 中可保留审计记录。

在实现时,建议把“缓存层遗忘”和“历史归档层清理”分开。Redis 的 TTL 可以快速模拟短时遗忘,SQLite 留作历史审计,二者不冲突。

3. 环境准备:在鲲鹏 openEuler 上把基础服务跑起来

3.1 选择 openEuler 版本

很多队伍在备赛时会纠结“CentOS 7.5 应该用 openEuler 哪个版本”。从实际使用看,建议避开老旧的 CentOS 习惯,直接使用 openEuler 当前的主流 LTS 版本。

使用场景建议版本架构备注
鲲鹏服务器openEuler 22.03 LTS SP4aarch64稳定,适配仓库全
本地虚拟机openEuler 22.03 LTS SP4x86_64开发调试
生产部署openEuler 最新 LTSaarch64按官方生命周期维护

下载镜像时,鲲鹏服务器要选择 aarch64 版本,本地虚拟机如果不想模拟 ARM,可以先用 x86_64 版本做开发。但最终必须有一份在鲲鹏 aarch64 上完整跑通的部署文档。

3.2 系统初始化:网络、SSH、yum 源

在正式安装依赖前,必须确认 yum 源可用。openEuler 安装完成后,默认可能使用官方源,也可能因为网络原因很慢。此时建议换成镜像源,下面的示例使用阿里云镜像:

# 备份默认源配置 mkdir -p /etc/yum.repos.d/backup cp /etc/yum.repos.d/openEuler.repo /etc/yum.repos.d/backup/ # 写入镜像源配置 cat > /etc/yum.repos.d/openEuler.repo <<'EOF' [openEuler] name=openEuler baseurl=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP4/OS/$basearch/ enabled=1 gpgcheck=0 EOF yum clean all yum makecache

$basearch 变量会自动识别为 aarch64 或 x86_64,不同架构下都能使用。

开启 SSH 服务,便于后续远程开发和部署:

dnf install -y openssh-server systemctl enable --now sshd systemctl status sshd

3.3 安装 Python、Redis、Docker

openEuler 自带 Python 3,但版本不一定满足项目要求。为了不破坏系统 Python,建议用 conda 或 pyenv 建独立环境。

# 更新系统 dnf update -y # 安装基础编译工具 dnf install -y gcc gcc-c++ make cmake git wget tar # 安装 Redis dnf install -y redis systemctl enable --now redis redis-cli ping

如果要用 Docker,可以在 openEuler 上直接尝试通过 dnf 安装:

dnf install -y docker systemctl enable --now docker

不同版本、不同架构下的 Docker 包名可能稍有差异,落地前要先确认当前仓库是否提供 docker 包。如果仓库没有对应包,可以使用 Docker 官方提供的静态二进制或容器镜像仓库,但要注意选择 linux/arm64 版本。

安装 conda 时,一定要选择 aarch64 版本:

wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-py39_4.12.0-Linux-aarch64.sh bash Miniconda3-py39_4.12.0-Linux-aarch64.sh

如果是在本地 x86_64 虚拟机,需要换成 x86_64 对应文件名。

3.4 环境验证清单

安装完成后,建议用下面表格逐项确认:

检查项命令预期结果
架构uname -maarch64
系统版本cat /etc/openEuler-releaseopenEuler 22.03 LTS
SSH 状态systemctl status sshdactive
Redis 状态redis-cli pingPONG
Python 版本python3 --version3.9+
Docker 状态docker infoServer Version 存在

这些检查项要写进项目 README,方便评委按文档复现,也方便队友在不同机器上快速对齐。

4. 核心实现:构建一个可运行的最小 Agent Memory 服务

4.1 项目结构

创建一个项目目录:

mkdir -p /opt/agent-memory/{memcore,api,data,scripts} cd /opt/agent-memory

推荐的项目结构如下:

agent-memory/ ├── api.py ├── memcore/ │ ├── __init__.py │ ├── model.py │ ├── store.py │ └── manager.py ├── data/ │ └── memory.db ├── requirements.txt └── scripts/ └── demo_session.py

这个结构把数据模型、存储层、策略层、API 层分开,后续扩展向量检索时只需要增加一个 retriever 模块。

requirements.txt 内容:

fastapi==0.104.1 uvicorn==0.24.0 redis==5.0.1 pydantic==2.5.0

4.2 记忆数据模型实现

在 memcore/model.py 中定义 MemoryItem 数据类:

# memcore/model.py from dataclasses import dataclass, field from datetime import datetime, timezone from typing import Any, Dict def _now_iso() -> str: return datetime.now(timezone.utc).isoformat() @dataclass class MemoryItem: memory_id: str content: str memory_type: str = "conversation_fact" importance: float = 1.0 access_count: int = 0 created_at: str = field(default_factory=_now_iso) updated_at: str = field(default_factory=_now_iso) metadata: Dict[str, Any] = field(default_factory=dict)

使用 dataclass 可以省去大量样板代码,字段顺序和 JSON 结构也容易对齐。created_at 使用 UTC 时间,避免不同服务器时区不一致导致衰减计算混乱。

4.3 存储层实现:Redis + SQLite 双层设计

存储层是记忆系统的基础。Redis 负责快速读写和 TTL 遗忘,SQLite 负责持久化。

# memcore/store.py import json import sqlite3 import redis from .model import MemoryItem class MemoryStore: def __init__(self, redis_url="redis://127.0.0.1:6379/0", sqlite_path="data/memory.db"): self.redis_client = redis.Redis.from_url(redis_url, decode_responses=True) self.sqlite_path = sqlite_path self._init_sqlite() def _init_sqlite(self): conn = sqlite3.connect(self.sqlite_path) conn.execute( """ CREATE TABLE IF NOT EXISTS memories ( memory_id TEXT PRIMARY KEY, content TEXT, memory_type TEXT, importance REAL, access_count INTEGER, created_at TEXT, updated_at TEXT, metadata TEXT ) """ ) conn.commit() conn.close() def _key(self, memory_id: str) -> str: return f"mem:{memory_id}" def write(self, item: MemoryItem, ttl: int = 7 * 24 * 3600): key = self._key(item.memory_id) self.redis_client.hset( key, mapping={ "content": item.content, "memory_type": item.memory_type, "importance": item.importance, "access_count": item.access_count, "created_at": item.created_at, "updated_at": item.updated_at, "metadata": json.dumps(item.metadata, ensure_ascii=False), }, ) self.redis_client.expire(key, ttl) conn = sqlite3.connect(self.sqlite_path) conn.execute( """ INSERT INTO memories (memory_id, content, memory_type, importance, access_count, created_at, updated_at, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(memory_id) DO UPDATE SET content = excluded.content, memory_type = excluded.memory_type, importance = excluded.importance, access_count = excluded.access_count, updated_at = excluded.updated_at, metadata = excluded.metadata """, ( item.memory_id, item.content, item.memory_type, item.importance, item.access_count, item.created_at, item.updated_at, json.dumps(item.metadata, ensure_ascii=False), ), ) conn.commit() conn.close() def get(self, memory_id: str): key = self._key(memory_id) data = self.redis_client.hgetall(key) if data: return self._from_redis_dict(memory_id, data) return self._get_from_sqlite(memory_id) def delete(self, memory_id: str): self.redis_client.delete(self._key(memory_id)) conn = sqlite3.connect(self.sqlite_path) conn.execute("DELETE FROM memories WHERE memory_id = ?", (memory_id,)) conn.commit() conn.close() def list_all(self): conn = sqlite3.connect(self.sqlite_path) rows = conn.execute("SELECT * FROM memories").fetchall() conn.close() return [self._from_row(row) for row in rows] def _get_from_sqlite(self, memory_id: str): conn = sqlite3.connect(self.sqlite_path) row = conn.execute( "SELECT * FROM memories WHERE memory_id = ?", (memory_id,) ).fetchone() conn.close() if row: return self._from_row(row) return None def _from_redis_dict(self, memory_id, data): return MemoryItem( memory_id=memory_id, content=data.get("content", ""), memory_type=data.get("memory_type", "conversation_fact"), importance=float(data.get("importance", 1.0)), access_count=int(data.get("access_count", 0)), created_at=data.get("created_at", ""), updated_at=data.get("updated_at", ""), metadata=json.loads(data.get("metadata", "{}")), ) def _from_row(self, row): return MemoryItem( memory_id=row[0], content=row[1], memory_type=row[2], importance=row[3], access_count=row[4], created_at=row[5], updated_at=row[6], metadata=json.loads(row[7]) if row[7] else {}, )

这里的关键点是:写入时同时操作 Redis 和 SQLite。Redis 提供高速访问,SQLite 保证进程重启后记忆不丢失。如果 Redis 中没有命中,就回源到 SQLite 读取,这非常像缓存与数据库的双写模式。

4.4 记忆管理策略:评分、去重、检索、访问计数

光有存储还不够,还需要一个 manager 层承载策略逻辑。

# memcore/manager.py import re import uuid from datetime import datetime, timezone from typing import Optional from .model import MemoryItem from .store import MemoryStore def _now_ts(): return datetime.now(timezone.utc).timestamp() class MemoryManager: def __init__(self, store: MemoryStore): self.store = store def _estimate_importance(self, content: str) -> float: score = 1.0 if re.search(r"偏好|喜欢|需要|希望|不同意|不要", content): score += 0.2 if re.search(r"姓名|用户|任务|订单|退款|错误|修复", content): score += 0.1 return min(score, 1.0) def write_from_agent( self, content: str, memory_type: str = "conversation_fact", metadata: Optional[dict] = None, ttl: int = 7 * 24 * 3600, ) -> MemoryItem: metadata = metadata or {} importance = self._estimate_importance(content) # 去重:同类型同内容不重复写入 for old in self.store.list_all(): if old.content == content and old.memory_type == memory_type: return old item = MemoryItem( memory_id=str(uuid.uuid4()), content=content, memory_type=memory_type, importance=importance, metadata=metadata, ) self.store.write(item, ttl=ttl) return item def search(self, query: str, limit: int = 5): words = [w for w in re.split(r"\s+", query.strip()) if w] results = [] for item in self.store.list_all(): score = 0.0 for w in words: if w in item.content: score += 1.0 if score > 0: recency = item.importance / (1 + item.access_count) results.append((score + recency, item)) results.sort(key=lambda x: x[0], reverse=True) return [item for _, item in results[:limit]] def touch(self, memory_id: str): item = self.store.get(memory_id) if item: item.access_count += 1 item.updated_at = datetime.now(timezone.utc).isoformat() self.store.write(item) return item

这个实现有几个值得在答辩时讲清楚的点:

  • 重要度评分是规则式的,不用模型,适合最小闭环。后续可以替换为模型打分。
  • 去重策略通过“同类型 + 同内容”判断,避免同一事实反复写入。
  • 排序综合了关键词匹配数量、重要度和访问次数,体现“热点记忆优先召回”。

4.5 对外 API 封装

使用 FastAPI 把记忆服务暴露成 HTTP 接口,方便 Agent 或者演示前端调用。

# api.py from typing import Optional from fastapi import FastAPI from pydantic import BaseModel from memcore.manager import MemoryManager from memcore.store import MemoryStore app = FastAPI() store = MemoryStore() manager = MemoryManager(store) class MemoryWriteRequest(BaseModel): content: str memory_type: str = "conversation_fact" metadata: Optional[dict] = None @app.post("/memories") def create_memory(req: MemoryWriteRequest): item = manager.write_from_agent( content=req.content, memory_type=req.memory_type, metadata=req.metadata or {}, ) return {"memory_id": item.memory_id, "importance": item.importance} @app.get("/memories/search") def search_memories(query: str, limit: int = 5): items = manager.search(query, limit) return [ { "memory_id": item.memory_id, "content": item.content, "memory_type": item.memory_type, "importance": item.importance, } for item in items ] @app.get("/memories/{memory_id}") def get_memory(memory_id: str): item = store.get(memory_id) if not item: return {"error": "not found"} return item.__dict__ @app.delete("/memories/{memory_id}") def delete_memory(memory_id: str): store.delete(memory_id) return {"ok": True} @app.get("/memories") def list_memories(): items = store.list_all() return [item.__dict__ for item in items]

Pydantic 的 BaseModel 负责请求参数校验,内容为空时会被 FastAPI 直接拒绝。这里没有做鉴权,是因为参赛 Demo 面向本地和服务器内网演示,后续如果要开放公网访问,需要加上 API Key 或认证中间件。

4.6 Agent 如何接入记忆系统

Agent 接入记忆系统时,推荐按以下顺序处理:

  1. 用户发起新请求。
  2. 调用/memories/search?query=用户问题获取相关记忆。
  3. 将记忆拼接进系统 prompt。
  4. 调用大模型生成回复。
  5. 从本轮对话中抽取值得保存的事实,调用/memories写入记忆。

示例 prompt 结构:

你是一个智能客服助手。 以下是该用户的已知偏好: - 用户希望回答简洁,控制在300字以内。 - 用户上次问过订单退款流程。 请基于这些信息回答当前问题:...

这种接入方式对现有 Agent 代码的侵入很小,只需要在调用大模型前后增加两个 HTTP 请求。

5. 运行验证:用最小案例跑通记忆闭环

5.1 启动服务

安装依赖并启动:

cd /opt/agent-memory pip install -r requirements.txt uvicorn api:app --host 0.0.0.0 --port 8000

启动后,先用 curl 验证写入接口:

curl -X POST http://127.0.0.1:8000/memories \ -H "Content-Type: application/json" \ -d '{"content":"用户偏好简洁回答","memory_type":"user_profile"}'

预期返回:

{"memory_id": "32fd6f1e-2e8b-4e19-aec4-9a2d1b0f2b0b", "importance": 1.2}

importance 大于 1.0,说明“偏好”关键词触发了加分规则。

5.2 验证记忆检索

搜索接口用于召回与当前问题相关的记忆:

curl "http://127.0.0.1:8000/memories/search?query=用户偏好&limit=3"

预期能召回刚才写入的记录。如果搜索“天气怎么样”,则不会召回任何记忆,因为关键词没有命中。

5.3 验证记忆持久化

重启 Redis 和 uvicorn,再查询列表:

systemctl restart redis # 重新启动 uvicorn uvicorn api:app --host 0.0.0.0 --port 8000 & curl http://127.0.0.1:8000/memories

只要 Redis 重启前 TTL 没有过期,list 接口会先走 SQLite 还原数据,并重新写回 Redis。这一步骤是证明“记忆系统不是内存聊天记录”的关键演示。

5.4 验证遗忘策略

为了体现“遗忘”,可以把写入时的 TTL 调小。当前代码中 write_from_agent 默认 TTL 是 7 天,可以临时改为 10 秒,或者通过 metadata 传入更短的 ttl 参数。写入后等待 15 秒,再调用:

curl http://127.0.0.1:8000/memories/{memory_id}

如果 Redis 中的缓存已过期,而 SQLite 历史仍在,说明系统具备“缓存层遗忘 + 历史归档”的能力。正式答辩时,可以把这种两级设计讲清楚,而不是简单说“删除记录”。

5.5 模拟多轮对话脚本

在 scripts/demo_session.py 中放一个演示脚本,可以一键展示“第一轮写入、第二轮召回”:

# scripts/demo_session.py import requests BASE = "http://127.0.0.1:8000" # 第一轮:用户告诉 Agent 自己的姓名和偏好 requests.post( f"{BASE}/memories", json={ "content": "用户叫张三,希望回答尽量简洁", "memory_type": "user_profile", "metadata": {"source": "session_001"}, }, ) # 第二轮:新的会话,Agent 先搜索用户信息 resp = requests.get( f"{BASE}/memories/search", params={"query": "用户是谁", "limit": 3}, ).json() for item in resp: print(item["content"])

运行后输出应包含“用户叫张三,希望回答尽量简洁”。这个脚本可以作为演示视频的基础素材。

6. 常见问题排查:鲲鹏 openEuler 上最容易踩的坑

6.1 yum 源和仓库问题

现象:执行 dnf install 时出现 404、超时,或者找不到软件包。

原因:仓库地址与系统版本、架构不匹配。

检查方式:

cat /etc/yum.repos.d/openEuler.repo dnf repolist

解决方案:确认 openEuler 版本后,替换为对应版本的镜像源。重点检查 baseurl 中的版本号路径,例如 openEuler-22.03-LTS-SP4。

6.2 aarch64 架构下 Python 包安装失败

现象:pip install faiss-cpu 或类似包时提示找不到匹配分发,或编译过程中报错。

原因:部分 Python 包没有发布 aarch64 的 wheel,源码编译又缺少依赖。

检查方式:

pip debug --verbose | grep -A 5 "Compatible tags"

解决方案:优先选择已有 aarch64 wheel 的包;如果确实需要 faiss,可以尝试安装faiss-cpu的特定版本或改用hnswlib。在参赛 Demo 中,可以先用简单的关键词检索实现最小闭环,向量检索作为加分项再接入。

6.3 Redis 连接失败

现象:应用启动后,写入或读取时报 Redis 连接异常。

原因:Redis 未启动、绑定地址受限,或防火墙拦截。

检查方式:

redis-cli -h 127.0.0.1 ping ss -lntp | grep 6379

解决方案:启动 Redis 并设置为开机自启;如果 Redis 只允许本机访问,应用和 Redis 在同一台机器时连接串保持127.0.0.1即可。跨机器访问时需要修改 bind 配置并开放防火墙端口。

6.4 SSH 无法远程登录

现象:从本机 ssh 到鲲鹏服务器超时或被拒绝。

原因:openssh-server 未安装、sshd 未启动,或安全组没有放行 22 端口。

检查方式:

systemctl status sshd

解决方案:安装并启动 sshd,然后检查云服务器的安全组规则。openEuler 默认可能没有完全复制 CentOS 的 SSH 配置,需要手动启用。

6.5 Docker 镜像架构不匹配

现象:在鲲鹏上拉取 Redis、Python 等镜像后,运行报 exec format error。

原因:镜像不是 linux/arm64 架构。

检查方式:

docker inspect <镜像名> | grep Architecture

解决方案:拉取镜像时优先选择官方镜像,官方镜像一般支持多架构。如果使用自定义镜像,构建时要在 Dockerfile 中明确FROM --platform=linux/arm64

6.6 桌面环境与音频问题

在 openEuler 上安装桌面版后,可能会遇到没有声音、显示卡顿等问题。如果是参赛开发,建议主服务器不安装桌面,只保留最小化系统 + SSH,这样能减少不必要的依赖冲突。本机和远程开发环境使用 VSCode Remote SSH 即可,不需要桌面。

问题现象常见原因检查方式处理建议
dnf 安装超时yum 源不可用dnf repolist换镜像源
pip 安装失败aarch64 wheel 缺失pip debug改替代包
Redis 连不上未启动/未放行redis-cli ping启动并检查端口
SSH 登不上sshd 未启动systemctl status sshd启用 sshd
Docker 运行报错架构不匹配docker inspect拉 arm64 镜像

7. 参赛建议:从 Demo 到完整作品

7.1 建议增加记忆可视化界面

答辩时,只看命令行接口不够直观。建议用 Flask 或 Gradio 做一个简单的记忆管理面板,展示:

  • 当前记忆列表。
  • 每条记忆的重要度、访问次数、创建时间。
  • 搜索命中情况。
  • 遗忘清理记录。

可视化不是核心功能,但能让评委在几分钟内理解系统的记忆生命周期。

7.2 评估指标要提前设计

项目 README 里建议写清楚评估维度,例如:

指标说明
写入成功率合法记忆是否能成功持久化
召回率构造 N 个查询,能召回多少相关记忆
精确率召回的记录中多少真正相关
重复写入率同一条事实是否被重复保存
遗忘有效性过期记忆是否被清理
响应时延写入和查询的平均耗时

使用模拟对话数据时,要区分训练集和测试集,避免用同一条对话既构造写入又构造问题。

7.3 答辩前检查清单

类别检查项状态
环境鲲鹏 aarch64 能一键复现部署是/否
功能写入、查询、更新、删除全部可用是/否
策略重要度、去重、衰减机制有代码实现是/否
接口Agent 可以通过 HTTP API 接入是/否
文档README 包含环境要求和部署命令是/否
演示scripts/demo_session.py 可以一键演示是/否
排障已知坑记录在项目文档中是/否

7.4 可继续扩展的方向

如果基础版本已经稳定,可以考虑以下扩展:

  • 向量检索:使用 embedding 模型 + 向量库实现语义召回。
  • 记忆合并:相似记忆自动合并,减少冗余。
  • 冲突消解:新记忆与旧记忆矛盾时,根据时间戳和重要度决策。
  • 多 Agent 共享记忆:通过命名空间隔离不同 Agent 的记忆。
  • 分布式存储:将 SQLite 替换为数据库,支撑更大规模并发。

这些方向每一个都可以作为答辩的“创新点”,但要确保在正式演示前已经稳定运行,而不是只画架构图。

Agent Memory 是一个典型的交叉方向题目,它要求参赛队伍同时理解 AI 应用、存储系统、基础软件适配三块内容。openEuler 和鲲鹏平台并不是单纯的运行环境,它们会在依赖安装、包管理、架构兼容等细节上影响整个开发过程。备赛时,尽早把环境固定在鲲鹏 aarch64 上,可以省掉大量临场调试时间。对于新手团队,先跑通一个不依赖向量库的 Redis + SQLite 版本,把记忆闭环和遗忘策略讲清楚,再逐步加入向量检索和可视化,是比较稳妥的递进路线。最终评审关注的不是一个“聊天记录查询系统”,而是记忆系统是否有明确的写入决策、检索排序、冲突更新和遗忘机制,以及这些机制能否在国产平台上稳定运行。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询