news 2026/10/23 3:32:58

Agent记忆架构实战:情景记忆与语义记忆如何协同构建Memory Stack

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent记忆架构实战:情景记忆与语义记忆如何协同构建Memory Stack

做Agent开发也有一段时间了,从最早的“提示词+外部工具”的玩法,到后面开始碰记忆模块,中间踩了不少坑。最近在重构一个知识型Agent的底层架构,正好做到Episodic + Semantic Memory Stack这一层,很多朋友问我这两类记忆到底怎么组织、怎么落地、为什么要叠成一个Stack而不是塞一个向量库了事。这篇就把我实际梳理和实现的过程完整写出来,希望能帮你少走弯路。

这套东西不是什么理论模型,而是我在一个模拟项目X(一个服务C端用户的长期任务型Agent)上真实跑通的方案。它解决的核心问题是:如何让Agent在跨会话场景下,既记得“用户上次什么时候问了什么、当时心情怎么样、最后结果如何”,又能沉淀“用户长期以来形成了哪些稳定的偏好和知识结构”。只做RAG式的事实召回是远远不够的——你需要的是一个能同时支撑情景回放和知识积累的记忆体系。

如果你正在做AI助手、长期任务Agent、知识问答机器人,或者单纯对“记忆”这个概念在工程上怎么实现感兴趣,这篇内容会非常对胃口。

1. 为什么要把Memory单独拆出来讲

1.1 记忆不是缓存,而是Agent成长的关节

很多刚入门的朋友会觉得:Agent不就是一个大模型套工具调用吗?要什么记忆?把上下文窗口拉长一点不就行了?

但真到了生产环境你会发现,上下文窗口就像一个小白板,你今天写满、明天擦掉,用户昨天跟你说的“我下周要交一份市场分析报告”,今天你再问他“报告需要什么格式”,他只会觉得你是个健忘的AI。纯靠Context Window做不了跨会话的持续性,这是第一层瓶颈。

第二层瓶颈来自“体验预期”:用户对AI助手的期待,已经从“单次问答”变成了“越来越懂我”。他希望你第三次帮他的时候,能记得前两次他提过什么样的需求、偏好什么样的表达方式、曾经为什么事不满意。这种记忆需求不是缓存能解决的,它需要结构化沉淀。

我在做某跨平台系统的Agent模块时,一开始试图用一个简单的对话历史表顶住所有记忆需求,后来发现根本不够:用户问“我上次让你分析的那份数据”,你得能从几十条历史里定位“上次”是哪次、是哪个文件、当时分析出了什么结论——这就是典型的情景记忆(Episodic Memory)。而用户随后说“我一般喜欢看同比和环比两个维度”,这是稳定偏好——属于语义记忆(Semantic Memory)范畴。两类信息形态不一样,生命周期不一样,检索方式也不一样,硬塞在一张表里,要么查询巨慢,要么召回质量一塌糊涂。

所以我才把Memory单独拆出来,变成一个分层的Stack,而不是继续沿用“历史消息表 + 向量召回”这种简单粗暴的做法。

1.2 先理清记忆的几种类型,别混在一起谈

讨论Agent记忆的时候,很多资料会把Short-term和Long-term混着说,实际上逻辑上应该分成至少四个层次:

记忆类型核心特征Agent示例生命周期
工作记忆当前任务进行中的上下文正在撰写这份报告,已写到第三章会话内,分钟级
情景记忆具体时间、地点、事件、感受用户4月12日晚上反馈登录报错持久化,可回放
语义记忆提炼后的事实、概念、偏好用户习惯用表格看数据;客户公司主营电商持久化,可复用
程序性记忆怎么做一件事的流程/技能如何调用某个API完成报表导出持久化,按需加载

这篇文章聚焦的就是情景记忆和语义记忆这两层,以及它们如何在一个Stack中协同。工作记忆通常由上下文窗口承担,程序性记忆更多是Skill/Prompt层面的管理,这里不展开。

我当时选择先把Episodic和Semantic一起实现,是因为它们有一个很强的上下游关系:情景记忆是“原料”,语义记忆是“提炼后的成品”。Agent只有先记录下“发生了什么”,才有机会从中抽象出“用户是什么样的人、世界有哪些规律”。做成一个Stack,是为了让原料和成品之间有清晰的加工链路,而不是各存各的。

2. 两类记忆到底怎么区分,又怎么协作

2.1 一个具体案例讲清楚两类记忆

假设你的Agent是一个生活助手。用户今天下午六点说了一句:“帮我把明天上午十点的会议改成线上,我临时要出差去杭州。”

这句话里至少有两种记忆价值:

  • 情景记忆:用户在今天18:02提出了一个修改请求,原因是临时出差,原会议是明天上午十点的线下会议。这是一段带有时间锚点、事件因果的“情节”。
  • 语义记忆:用户在近期内频繁出差、倾向于把会议改为线上、出差目的地是杭州,这些是可以长期复用的用户偏好。

如果你只存语义记忆(“用户经常出差杭州”),下一次对话时Agent可能直接问“您要不要改成线上”,但你回忆不起这结论是怎么来的、是什么时候得出的、有多可靠。如果你只存情景记忆(“用户6月12日改了一次会议”),那Agent也无法从这些孤立事件中抽象出习惯和偏好。

Episodic Memory的定位是“回放”,Semantic Memory的定位是“理解”。前者帮助Agent回答“当时发生了什么”,后者帮助Agent回答“用户/世界长期是什么样的”。

2.2 从情节中提炼语义:记忆的加工链

架构上我很早就定了一个原则:Semantic Memory不能靠用户说一句就写一句,它必须来自对多条Episodic Memory的归纳。原因很简单,单条事件可能有偶然性,只有多条事件呈现一致性时,才值得沉淀为语义知识。

加工链路大概是这样的:

  1. Agent完成一段对话或一个任务后,把关键事件写入Episodic Memory。
  2. 定期或按触发条件(比如新增了N条情景记忆),对这些情节做一次“经验总结”。
  3. 总结时要求LLM区分两类产物:事实类知识(如“用户公司主营电商,年营收约8000万”)和偏好类知识(如“用户汇报时喜欢先看结论再看过程”)。
  4. 新产出的语义记忆与已有语义记忆做融合:一致的提升置信度,矛盾的打上待确认标记。
  5. 被融合的Episodic Memory打上“已消费”标记,后续不再重复参与提炼。

这个链路本质上模仿了人的记忆形成过程:我们不会因为一次迟到就断定某人时间观念差,但如果他接连五次迟到,“这个人容易迟到”就会成为你稳定的印象。Agent也一样,它的Semantic Memory不应该是一次性的判断,而应该是经过多情节验证的结论。

2.3 两类记忆在召回时的不同逻辑

回忆一下你平时用RAG做知识库问答时,检索逻辑是针对“文档片段”的语义相似度。但对Episodic Memory来说,只做语义召回是不够的。用户问“我上回报错那个事儿最后怎么解决的”,这条query的语义重心在“报错”和“解决”,但真正的关键约束是时间范围和事件归属——你得按时间倒序找回当时那串事件链,而不是找一个语义最像的知识碎片。

而Semantic Memory的召回则更接近“知识图谱/事实查询”混合模式:比如用户问“帮我按我平时的习惯生成报表”,Agent需要召回的是“用户平时习惯按周生成、关注同比环比、喜欢Excel格式”这些稳定事实,而不是某一时的场景流水。

所以它们的检索逻辑天然不同:一个偏时间线+事件属性过滤,一个偏语义+关系匹配。这也是为什么我用两个独立的Store,而不是把它们塞进同一个向量库里。

3. Memory Stack 的整体架构设计

3.1 分层设计:从会话上下文到长期知识

整个Memory Stack我按四个层次来组织:

层级职责存储形式生命周期
L0 会话缓冲保存当前会话最近的原始对话内存 / Redis会话期间
L1 情景记忆保存结构化的事件记录关系型数据库 + 向量索引长期
L2 语义记忆保存提炼后的稳定知识向量库 / 图存储长期
L3 工作空间组装当前Agent可用的记忆上下文动态生成单次推理

L0负责短期对话流畅性,L1和L2是本篇的核心,L3是真正让模型在推理时使用的“压缩记忆包”。

之所以要把L3单列出来,是因为我实践中发现:直接L1、L2一起丢给LLM,上下文里经常出现“事件流”和“知识碎片”互相打架的情况。L3的作用是把检索回来的情节和语义知识,按照当前任务重组成一段有序的“记忆简报”,让模型看到的是一个逻辑自洽的背景,而不是一锅乱炖。

3.2 为什么是Stack,不是一个记忆库

有些开发者会问:能不能用一个带metadata的向量库搞定所有记忆?我的答案是:可以,但你会在大约三周后开始后悔。

原因有三个:

第一,数据形态完全不同。Episodic Memory本质上是带有时间、角色、事件类型、因果关系的结构化事件流,它适合用关系型查询(“用户上周所有关于报错的事件”);Semantic Memory本质上是实体-属性-值,或者实体-关系-实体,适合按语义或关系匹配。你把它们塞进同一个向量库,查询接口会被迫降级成“向量相似度+metadata过滤”,丧失掉很多结构化的表达能力。

第二,写入模式不同。Episodic几乎是实时追加的——每次交互都可能产生新事件;Semantic是低频合并的——它需要经过提炼和验证。混合在一个库里,写入路径很难做差异化优化。

第三,丢失成本不同。Semantic Memory如果混在大量Episodic流水里,一旦误删或污染,Agent的长期行为会发生不可控的偏移;分开管理之后,至少可以独立备份、独立回滚。

用一句话总结:Stack的本质,是为不同生命周期的记忆提供不同的存取通道,并在通道之上建立一条“事件→知识”的加工流水线。

4. 数据模型与存储方案

4.1 Episodic Memory 的数据模型

我的Episodic Memory表在设计时,参考了事件溯源(Event Sourcing)的思路。一张核心表长这样:

字段类型说明
memory_idstring全局唯一ID
agent_idstring记忆归属的Agent实例
user_idstring关联用户
session_idstring产生记忆的会话ID
event_typestring事件类型(如task_create, feedback, query_log)
contentjsonb事件的结构化内容
summarytext便于检索的事件摘要
start_timedatetime事件发生时间
end_timedatetime事件结束时间(如任务完成)
importance_scorefloat重要程度,影响保留优先级
embeddingvector用于语义召回的事件向量
consumedbool是否已被提炼到语义层

为什么一定要有start_time和end_time?因为情节往往不是一个瞬间,而是一个过程。比如用户提交了一份数据分析请求,三个小时后反馈“结果不错”,这是一个跨时间段的事件链。只有把它们组织为带起止的事件,Agent才能回答“那件事最后怎么样了”。

importance_score是我推荐大家一定要加的字段。它的初始值可以由两部分合成:一部分来自规则(比如涉及付款、投诉、明确的时间约定,分数就高),一部分来自LLM对事件重要性的评估。有了这个分数,之后的记忆清理和衰减才有依据。

4.2 Semantic Memory 的数据模型

语义记忆我采取的模型是“三元组 + 置信度 + 溯源”,形式上接近精简版知识图谱:

字段类型说明
fact_idstring全局唯一ID
subjectstring主体(如用户ID、公司名、产品名)
predicatestring属性/关系(如prefers_format, located_in)
objectstring值(如Excel, 上海)
confidencefloat置信度
source_event_idstext[]佐证这条事实的事件ID列表
last_updateddatetime最近更新时间
embeddingvector用于语义召回的向量

这里最关键的字段是source_event_ids。它保证了语义记忆不是无源之水——一旦后来出现矛盾信息,你可以回溯到原始事件去核实。

置信度的维护有一套规则:当一条新的情节与已有语义记忆一致时,confidence上调;矛盾时,confidence下调,并且把新事件作为疑点记入source_event_ids。只有confidence高于某个阈值(比如0.75)时,语义记忆才对外提供服务。

4.3 存储选型的实际取舍

我在模拟项目X中先后试过三种方案:

  1. 纯关系型存储:适合Episodic,因为结构化查询和分表分页很方便;但Semantic层做语义召回时性能很差。
  2. 纯向量库:语义召回很好用,但复杂事件过滤和时间倒序查询很吃瘪。
  3. 混合方案(最终采用):Episodic存PgSQL(含pgvector),Semantic存单独的向量库或PgSQL加向量列。两张表共用同一个embedding模型,保证向量空间一致。

如果你项目不大,直接一张表加vector索引也可以起步,但务必在数据模型层面保留event和fact的语义区分,不要为了省事把两类记忆混成一个模型。否则后面做提炼、做衰减、做回放时会非常拧巴。

5. 实战:从零实现一个Memory Stack

5.1 第一步:定义记忆写入入口

我采取的做法是:不直接让业务代码到处调用“记忆接口”,而是通过一个MemoryManager统一收口。业务层只需要抛出一个“事件对象”,MemoryManager会完成结构化、向量化、落库、触发提炼判断。

# memory_stack/memory_manager.py from dataclasses import dataclass from typing import Optional import uuid from datetime import datetime @dataclass class Episode: user_id: str event_type: str content: dict summary: str importance: float = 0.5 session_id: Optional[str] = None start_time: Optional[datetime] = None end_time: Optional[datetime] = None class MemoryManager: def __init__(self, episode_store, semantic_store, embed_fn): self.episodes = episode_store self.semantics = semantic_store self.embed_fn = embed_fn def record_episode(self, ep: Episode) -> str: ep_id = str(uuid.uuid4()) ep.start_time = ep.start_time or datetime.now() ep.end_time = ep.end_time or ep.start_time embedding = self.embed_fn(ep.summary + " " + str(ep.content)) self.episodes.insert( memory_id=ep_id, user_id=ep.user_id, event_type=ep.event_type, content=ep.content, summary=ep.summary, start_time=ep.start_time, end_time=ep.end_time, importance=ep.importance, embedding=embedding, ) # 每次写入后,尝试触发提炼 self.try_consolidate(ep.user_id, window_size=10) return ep_id

为什么要把start_time默认设成当前时间而不是让调用方传?因为很多业务方没时间意识,你让他在业务代码里维护时间,他会漏传或传错。统一在记忆层处理,数据质量会稳定很多。

5.2 第二步:从情节中提炼语义

提炼不是每次对话后都做的,那样成本太高。我用的策略是:当某个用户新增的、未被消费的Episodic Memory达到N条(窗口大小通常设10),就触发一次批量提炼。

提炼过程分三步:先按主题聚类,再做信息抽取,最后做语义融合。代码示意如下:

# memory_stack/consolidator.py class Consolidator: def __init__(self, llm, semantic_store, episode_store): self.llm = llm self.semantics = semantic_store self.episodes = episode_store def consolidate(self, user_id: str, recent_episodes: list) -> list[dict]: # 1. 让LLM从多条情节中抽取候选事实 prompt = self._build_consolidation_prompt(recent_episodes) candidates = self.llm.extract_facts(prompt) # candidates: [{subject, predicate, object}] merged = [] for cand in candidates: # 2. 检查是否与已有语义记忆冲突 existing = self.semantics.find_match( subject=cand["subject"], predicate=cand["predicate"] ) if existing is None: self.semantics.insert( fact_id=str(uuid.uuid4()), subject=cand["subject"], predicate=cand["predicate"], object=cand["object"], confidence=0.6, # 初始置信度 source_event_ids=[ep["memory_id"] for ep in recent_episodes], ) merged.append(cand) elif existing.object == cand["object"]: self.semantics.bump_confidence(existing.fact_id, delta=0.05) else: self.semantics.lower_confidence(existing.fact_id, delta=0.2) # 记录矛盾,待人工/后续事件验证 self.semantics.add_conflict(existing.fact_id, cand) # 3. 批量标记已消费 for ep in recent_episodes: self.episodes.mark_consumed(ep["memory_id"]) return merged

这里一个容易踩的坑:LLM抽取事实时,如果一次性给太多条情节,它容易“编造”出看似合理但其实没有依据的事实。建议每条prompt控制在4-8条情节,同时要求LLM输出时必须标注依据了哪条memory_id,做不到就丢弃。

5.3 第三步:双通道检索与合并

检索是整个Stack里最体现工程复杂度的一环。我的实现是双通道召回,再做一次轻量融合排序:

  • 通道A(Episodic):接收自然语言query,提取时间约束、事件类型约束,利用结构化条件+向量相似度做召回。
  • 通道B(Semantic):接收自然语言query,直接做语义召回。
  • 融合层:把两类结果分别打成“事件简报”和“知识简报”,再汇总成一条记忆上下文。
# memory_stack/retriever.py class MemoryRetriever: def __init__(self, episode_store, semantic_store, embed_fn): self.episodes = episode_store self.semantics = semantic_store self.embed_fn = embed_fn def retrieve(self, user_id: str, query: str, top_k: int = 5) -> dict: q_emb = self.embed_fn(query) # 通道A:情节记忆 episodes = self.episodes.search( user_id=user_id, query_embedding=q_emb, top_k=top_k, # 这里可以用 query 中小模型提取出的时间/类型条件做过滤 # 比如 query 中出现"上周"就限定 start_time 范围 ) # 通道B:语义记忆 facts = self.semantics.search( user_id=user_id, query_embedding=q_emb, top_k=top_k, min_confidence=0.75, ) return { "episode_brief": self._format_episodes(episodes), "semantic_brief": self._format_facts(facts), } def _format_episodes(self, episodes): # 按时间倒序拼接:事件摘要 + 结果 lines = [] for e in episodes: lines.append(f"[{e.start_time}] {e.summary}") if e.content.get("result"): lines.append(f"结果: {e.content['result']}") return "\n".join(lines) def _format_facts(self, facts): return "\n".join( f"{f.subject} {f.predicate} {f.object}(置信度 {f.confidence:.2f})" for f in facts )

检索阶段最大的心得是:query里如果有关键的时间表达,尽量在小模型层就把它抽出来,不要指望大模型在检索后再去理解。比如“上次”、“上周三”、“6月2号”这类表达式,应该作为结构化过滤条件提前释放,否则向量召回很容易把时间维度搞乱。

5.4 第四步:记忆的遗忘与更新

没有遗忘机制的记忆系统,迟早变成垃圾场。语义还好,毕竟是提炼过的;但情节记忆的增长速度非常快,一个高频用户一星期就能产生几千条事件。我采用的策略是:

  1. 基于时间的指数衰减:importance_score随时间逐步下调,访问频率可以减缓衰减速。
  2. 定期清理:低于阈值且超过保留期的情节,归档到冷存储或直接删除。
  3. 摘要压缩:对很旧但被多次访问的一批情节,做一次“情节摘要”,把整批事件压缩成一条概要事件。
# memory_stack/decay.py import math from datetime import datetime, timedelta def calc_current_importance(base_score, last_access_time, now, decay_days=45): # 指数衰减,访问一次相当于把“最后访问”刷新到现在 age_days = max(0, (now - last_access_time).total_seconds() / 86400) decayed = base_score * math.exp(-age_days / decay_days) # 保留基础下限,防止突降 return round(max(0.05, min(1.0, decayed)), 4) # 批量任务:每天扫一次,把低于必要阈值的记忆标记为可清理 def daily_decay_sweep(episode_store, threshold=0.15): now = datetime.now() stale_ids = episode_store.scan_all( filter=lambda row: calc_current_importance( row.importance, row.last_access_time, now ) < threshold ) episode_store.archive(stale_ids)

你可能担心遗忘会不会把有用的信息给弄丢。实践上,我建议“归档”而不是“删除”,也就是把低价值情节挪到冷存储,代价只是存储成本,换来的是万一Agent需要回溯时还能找到。对于绝大多数To B场景,这个性价比是很高的。

6. 实操中遇到的坑和排查方法

6.1 情节记忆太多,检索越来越慢

向量检索的单表数据量一大,延迟会显著上升。我们在模拟项目X中,单个用户的情节数超过3万条之后,召回延迟从20ms涨到了300ms+。

我的排查和解决路径是这样的:

  • 第一步:确认是不是embedding向量维度太高,降维到256以下。
  • 第二步:利用user_id + 时间区间做pre-filter,让向量检索只在一个小候选集里进行。实测下来这是最有效的手段。
  • 第三步:如果某个用户的历史数据特别长,按季度做分库分表,或者做情节摘要压缩,用概要事件代替大量原始事件参与检索。

有一个坑必须提醒:把时间过滤写在SQL里和写在向量检索的filter里,性能差距巨大。尽量让支持metadata过滤的向量检索服务来做pre-filter,不要先查全量再过滤。

6.2 语义记忆之间互相矛盾

矛盾通常出现在语义记忆提炼了一次,之后又出现新的情节与它冲突的时候。比如之前归纳出“用户偏好Excel格式”,后来又发现用户有一次明确说“不要Excel,要PDF”。

这时候你如果直接删掉旧知识再写入新知识,很容易造成震荡,Agent的行为一会儿一个样。我的处理是保留旧事实,但把它标记为“待验证”,同时降低置信度。只有当同方向的新事实出现至少两次后,才把旧的替换掉。

这套机制背后是我之前提到的source_event_ids字段在起作用:它让你能回到具体情节去判断,到底哪一轮的归纳更可靠。不要嫌设计重,这个字段在关键时刻救过我好几次。

6.3 “该记什么、不该记什么”很难拿捏

最初我把所有对话都一股脑记成情节,三天后整个库变成噪音大会。后来我对事件类型做了非常严格的分类治理。

重要度分级参考:

重要度事件类型示例处理策略
高用户明确偏好、项目里程碑、投诉、付款相关完整记录,入长期记忆
中任务完成过程、状态变化、关键决策记录摘要,可参与提炼
低普通寒暄、临时提问只保留在当前会话,不入库

这个分级可以配合LLM做重要性判断,但规则兜底是必须的。比如凡是涉及“投诉”“取消”“延期”等关键词的,不管LLM怎么打分,至少给到0.8以上的初始importance,否则风险很大。

6.4 离线评估怎么做

很多同学做完Memory Stack不知道效果好不好。我的做法是构造三类测试:

  1. 回放测试:给Agent一个情景,比如“上个月用户在哪天反馈过登录问题”,看能否准确召回对应情节。
  2. 偏好理解测试:给Agent一个决策场景,例如“为用户生成报表”,看它是否自动使用用户偏好的格式和口径。
  3. 冲突处理测试:故意注入与已有语义记忆矛盾的事件,看Agent是否表现出“合理怀疑”而不是盲目切换。

这三类测试可以在离线数据集上批量跑,也可以做成回归用例,每次修改记忆模块后自动触发。

6.5 一个容易忽视的问题:隐私与数据边界

记忆模块存储了大量用户相关数据,任何Agent项目都要考虑数据的最小化保留和访问控制。即便你在做内部Demo,也应该给Episodic Memory增加“数据删除”API,并且对敏感字段脱敏。这里不展开讲合规,但强烈建议不要在记忆库中明文保存口令、完整身份证号等信息。我在早期版本里吃过一次亏,后来加了脱敏层才安心。

7. 个人实操体会与关键建议

整套Memory Stack跑通之后,我最深刻的感受是:记忆设计不能一开始就奔着“大而全”去,否则大概率会在成本和复杂度上翻车。

我建议的落地顺序是:先做一个可靠的Episodic Memory存储,确保Agent能回答“上次发生了什么”;然后等积累到一定量,再加Semantic提炼层,让Agent越用越懂用户。第二步别急着做,稍微晚一点反而好,因为提炼质量依赖足够的事件样本。

还有一个非常实用的小技巧:定期对单个用户的Episodic Memory做一次“复盘式提炼”——不是抽取事实,而是让LLM生成一段类似“用户连续几周在周五下午做数据复盘,且偏好用移动端查看”这种综合判断。这类判断往往比零散的三元组更有智能感,也更贴近人的记忆组织方式。

最后分享一个过来人的经验:Memory Stack不是一次性建设完成的,它需要一个“记忆审计”的长期习惯。每周抽时间看一眼哪些记忆被高频召回、哪些语义记忆长期没有被使用,你才能持续优化这个记忆系统的质量。我自己现在每隔几周就会调整一次提炼阈值,这套系统才慢慢变得真正“像人一样记忆”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/23 3:32:51

ISO 21448-2022 SOTIF 预期功能安全标准解读与工程落地实践

简介&#xff1a;ISO 21448-2022《道路车辆——预期功能的安全性》是国际标准化组织发布的SOTIF领域核心标准&#xff0c;面向汽车制造商、零部件供应商、软件开发商及道路安全监管机构&#xff0c;用于评估和缓解自动驾驶与辅助驾驶系统在正常运行条件下因功能局限、传感器失效…

作者头像 李华
网站建设 2026/10/23 3:32:39

Qlib量化投资实战:港股因子选股与LightGBM模型全流程

简介&#xff1a;这份PDF文档是华泰证券金工研究团队出品的深度研究报告《微软AI量化投资平台Qlib体验》&#xff0c;面向量化投资研究者、金融工程从业者及希望入门AI选股的学生群体。报告系统梳理了Qlib的基础与进阶功能&#xff0c;对比传统量化策略开发流程&#xff0c;提炼…

作者头像 李华
网站建设 2026/10/23 3:31:51

基于ONNX Runtime的PP-OCR模型部署实战:从导出到调优

1. 为什么最终选 ONNX Runtime 这条路线&#xff0c;而不是继续扛着 Paddle 全家桶做 OCR 部署的人&#xff0c;十有八九都经历过这种时刻&#xff1a;模型在 Paddle 里跑得飞起&#xff0c;一换到业务方的服务器上就各种闹脾气——环境装不上、依赖冲突、推理速度拉胯。我自己…

作者头像 李华
网站建设 2026/10/23 3:31:13

大模型训练全链路:预训练、微调与强化学习实战指南

大模型这几年从一个学术圈里的概念&#xff0c;变成了几乎人人都在讨论的东西。但从“有个模型”到“能用起来”之间&#xff0c;其实隔着一条相当长的流水线。很多朋友拿着开源权重&#xff0c;跑通推理&#xff0c;觉得“模型就是这么回事”&#xff0c;真正动手试过一次训练…

作者头像 李华
网站建设 2026/10/23 3:31:05

MATLAB实现BP神经网络气象预测与天气分析实战

做气象数据分析这段时间&#xff0c;我最大的感受是&#xff1a;天气预报看着简单&#xff0c;实际上是一个典型的非线性、高维度、强耦合的映射问题。这次用MATLAB搭建BP神经网络做气象预测与天气分析&#xff0c;目标很明确——一方面通过多维气象要素的映射关系&#xff0c;…

作者头像 李华
网站建设 2026/10/23 3:30:15

Agent长期记忆系统设计:分层、TTL与数据治理实战

做了几年大模型应用&#xff0c;我越来越确定一件事&#xff1a;Agent落地最难的部分往往不是模型能力不够&#xff0c;而是记忆系统没设计好。用户跟Agent聊到第十分钟&#xff0c;Agent突然忘了五分钟前刚说过的关键条件&#xff0c;这种体验基本等于劝退。但反过来说&#x…

作者头像 李华