05 - 会话与持久化:让 Agent 有记性
一句话总结:用 session_id 管理对话上下文,用 SqliteDb 把对话存下来,Agent 重启也不会失忆。
问题:Agent 天生失忆
默认情况下,Agent 的每次 run() 或 print_response() 都是独立的。它不记得你上一句说了什么,更不记得昨天聊过什么。就像跟一个失忆症患者对话 – 每次都是初次见面。
from agno.agent import Agent
from agno.models.openai import OpenAIChat
agent = Agent(model=OpenAIChat(id="gpt-4o-mini"))
agent.print_response("我叫小明")
agent.print_response("我叫什么名字?") # 它不知道,因为两次调用完全独立
这在某些场景下没问题 – 比如一次性的翻译、摘要任务。但只要涉及多轮对话,你就需要两样东西:会话管理 和 持久化存储。
会话和 session_id
一个 session(会话)就是一串连续的对话,你可以把它理解成微信里的一个聊天窗口。Agno 用 session_id 来标识不同的会话:
- 相同的
session_id= 同一个对话上下文 - 不同的
session_id= 全新的对话,互不干扰
光有 session_id 还不够,你还得告诉 Agent “请把历史对话带上”。这就是 add_history_to_context 的作用:
from agno.agent import Agent
from agno.models.openai import OpenAIChat
agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
instructions="你是一个友好的助手,请记住我们的对话内容",
add_history_to_context=True, # 把历史对话塞进上下文
num_history_runs=5, # 保留最近 5 轮对话
)
# 同一个 session -- Agent 记得住
agent.print_response("我叫小明", session_id="chat-001")
agent.print_response("我叫什么名字?", session_id="chat-001") # 它知道你叫小明
# 换一个 session -- Agent 又失忆了
agent.print_response("我叫什么名字?", session_id="chat-002") # 它不知道
原理很简单:当你指定 session_id="chat-001" 时,Agent 会把这个 session 里之前的对话记录拿出来,拼到发给模型的 messages 里。模型看到了上下文,自然就”记得”了。
如果你不指定 session_id,Agno 会自动生成一个。这意味着同一个 Agent 实例的多次调用默认在同一个 session 里。
用 SQLite 做持久化存储
上面的方案有个致命问题:对话历史只存在于内存中。脚本一结束,什么都没了。下次启动,Agent 照样失忆。
解决方案是给 Agent 接上一个数据库。Agno 内置了 SQLite 支持,一行搞定:
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.db.sqlite import SqliteDb
db = SqliteDb(db_file="tmp/agents.db")
agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
instructions="你是一个友好的助手",
db=db, # 接上数据库
add_history_to_context=True, # 带上历史
num_history_runs=10, # 保留最近 10 轮
)
# 第一次运行脚本:告诉 Agent 一些事情
agent.print_response("我最喜欢的编程语言是Python", session_id="my-session")
现在关闭脚本,重新运行:
# 第二次运行脚本:Agent 还记得
agent.print_response("我最喜欢什么编程语言?", session_id="my-session")
# 它会告诉你:Python。因为对话历史已经存在 SQLite 里了
SqliteDb 会自动在 tmp/agents.db 创建数据库文件和表结构,你不用操心 schema。对话记录、session 状态全都帮你存好了。
Session State:结构化的会话数据
有时候你不光要记住对话历史,还想存一些结构化的数据 – 比如用户的偏好、已讨论的话题、购物车内容等。这就是 session_state 的用武之地。
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.db.sqlite import SqliteDb
from agno.run import RunContext
def update_user_name(run_context: RunContext, name: str) -> str:
"""记录用户的名字。"""
if run_context.session_state is not None:
run_context.session_state["user_name"] = name
return f"好的,我记住了,你叫{name}"
agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
db=SqliteDb(db_file="tmp/agents.db"),
# 初始化结构化状态
session_state={"user_name": "", "topics_discussed": []},
# 在指令中引用 session_state 的变量
instructions="""\
你是一个助手。当前用户名: {user_name}。
当用户告诉你名字时,调用 update_user_name 工具记录。
""",
tools=[update_user_name],
markdown=True,
)
agent.print_response("我叫小红", session_id="stateful-001")
print(f"当前状态: {agent.get_session_state()}")
几个关键点:
session_state是一个字典,你定义初始结构,Agent 运行时可以通过工具修改它- 指令里用
{user_name}这样的占位符可以直接引用 state 中的值 session_state跟着 session 走,存在数据库里,重启不丢失- 用
agent.get_session_state()或response.session_state随时查看当前状态
session_state 和对话历史的区别在于:历史是原始的聊天记录,state 是你提炼出来的结构化信息。两者配合使用效果最好。
控制历史大小
对话历史越长,发给模型的 token 就越多,既费钱又可能撑爆上下文窗口。num_history_runs 就是用来控制这个的:
agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
db=SqliteDb(db_file="tmp/agents.db"),
add_history_to_context=True,
num_history_runs=5, # 只带最近 5 轮对话,省 token
)
如果不设置 num_history_runs,默认值是 3。对于大多数场景,3 到 10 轮就够了。设太大一是费钱,二是早期对话往往已经不重要了。
一个实用技巧:如果你的对话轮次很多但关键信息集中在最近几轮,保持 num_history_runs 较小就行。如果关键信息分散在整个对话中,考虑用 session_state 把重要信息提取出来,而不是无限扩大历史窗口。
Storage vs Memory:一个重要的区分
到这里你可能会想:这不就是”记忆”吗?是,但只是记忆的一半。
Agno 把”记性”分成了两个层次:
| 概念 | 作用 | 范围 |
|---|---|---|
| Storage(存储) | 保存对话历史和 session 状态 | 单个 session 内 |
| Memory(记忆) | 保存关于用户的长期信息 | 跨所有 session |
打个比方:
- Storage 就像你的聊天记录 – 你可以翻看和某个人的某次对话说了什么,但每个聊天窗口是独立的
- Memory 就像你大脑中对这个人的印象 – 不管在哪个聊天窗口,你都知道他叫什么、喜欢什么
今天讲的 db、add_history_to_context、session_state 都属于 Storage 的范畴。Agent 能在同一个 session 内记住上下文,但换一个 session 就什么都不知道了。
Memory 解决的是跨 session 的问题:即使开了一个全新的对话,Agent 依然”认识”这个用户。这个下一篇专门讲。
今天学了什么
回顾一下关键概念:
- session_id 标识一个对话会话,相同 id 的调用共享上下文
- add_history_to_context=True 让 Agent 把历史对话带进上下文,这是”记住”的前提
- num_history_runs 控制带多少轮历史,默认 3 轮,按需调整
- db=SqliteDb(…) 接上数据库,对话历史持久化存储,脚本重启也不丢
- session_state 用于存储结构化的会话数据,比如用户偏好、计数器等
- Storage 是 session 级别的,跨 session 的”记忆”需要 Memory 系统
一个快速对照表,帮你选择用什么:
| 你想做的事 | 用什么 |
|---|---|
| 多轮对话中记住上下文 | add_history_to_context=True |
| 对话重启后还能继续 | db=SqliteDb(...) |
| 存储结构化数据 | session_state={...} |
| 跨对话记住用户信息 | Memory(下一篇讲) |
生产环境建议用 PostgreSQL 替代 SQLite – Agno 同样内置了 PostgresDb,用法几乎一样,换个连接字符串就行。
下一篇预告
06 - 记忆系统:让 Agent 真正”认识”你
Storage 解决了单个会话内的记忆问题,但用户一开新对话就回到陌生人状态,这体验不行。下一篇我们聊 Memory – 让 Agent 跨越所有会话,真正”认识”每一个用户,记住他们的偏好、习惯和背景信息。从”有记性”进化到”有记忆”。