Acontext:为AI智能体构建可解释、可编辑的技能记忆层
1. 项目概述:Acontext,为AI智能体构建“技能记忆层”
如果你正在构建AI智能体,无论是基于OpenAI的GPTs、Anthropic的Claude,还是LangChain、LlamaIndex这类框架,一个核心的痛点很快就会浮现:智能体如何记住它做过什么?更具体地说,如何让它从每一次交互中学习,避免重复犯错,并积累可复用的经验?传统的解决方案,比如在对话历史中附加冗长的上下文,或者使用向量数据库进行语义搜索,往往带来新的问题——上下文窗口迅速被填满,成本飙升,而存储在向量数据库中的“记忆”又像是一个黑盒,难以理解、调试和精确控制。
Acontext正是为了解决这个问题而生。它不是一个简单的记忆存储库,而是一个开源的智能体技能记忆层。它的核心理念非常清晰:将智能体的记忆,转化为可读、可编辑、可共享的Markdown技能文件。想象一下,你的智能体每完成一次成功的客户服务,或解决一个棘手的代码问题,它都能将这次经历的核心“诀窍”提炼成一个.md文件。这个文件就像一份标准操作程序,可以被其他智能体直接读取、遵循,甚至由你手动优化。这就是Acontext在做的事情——它让智能体的学习过程变得透明、可管理,并且完全摆脱了对特定供应商或复杂嵌入模型的依赖。
2. 核心理念:为什么“技能即记忆”是更优解?
在深入技术细节前,我们必须先理解Acontext背后的设计哲学。当前主流的智能体记忆方案,无论是基于向量检索的RAG,还是基于键值对的简单存储,都存在几个根本性的局限。
2.1 传统记忆方案的困境
首先,可解释性差。向量数据库里存储的“记忆片段”,经过嵌入模型编码后,对人类来说是不可读的。你无法直观地知道智能体“记住”了什么,更难以纠正其中的错误或偏见。当智能体行为异常时,排查记忆问题如同大海捞针。
其次,控制粒度粗。基于相似度的检索是“模糊匹配”,它可能返回相关但不精确的记忆,或者遗漏掉那些表述不同但本质相同的关键经验。你很难命令智能体“去调用上周二处理用户‘张三’退款请求时总结的那个流程”。
最后,存在供应商锁定风险。你的记忆系统深度依赖特定的嵌入模型API和向量数据库服务。一旦想要迁移或进行本地部署,整个记忆层可能都需要重构。
2.2 Acontext的“技能文件”范式
Acontext提出了一个截然不同的思路:如果智能体的一切能力都可以用技能文件来定义和描述,那么它的记忆为什么不能也用同样的格式?
- 记忆即技能:一次成功的操作、一个避坑经验、一个用户偏好,都被视为一个“技能”。这个技能被结构化的记录在一个Markdown文件里。文件内容清晰明了,包含了何时(触发条件)、何故(背景与目标)、如何(具体步骤与逻辑)、何果(效果与注意事项)。
- 纯文本,全兼容:Markdown是通用格式。这意味着这份记忆可以被任何能读取文本的框架使用——LangGraph、Claude API、Vercel AI SDK,甚至是你自己写的脚本。你可以用
git管理它的版本,用grep搜索特定内容,或者直接把它挂载到智能体的沙箱环境中。 - 设计权在你手:记忆的组织结构不是固定的。你可以通过上传一个“上下文技能”文件来定义记忆的schema。比如,你可以规定“每个联系人创建一个独立的技能文件”,或者“每个项目建立一个文件夹,里面存放相关的需求、代码片段和沟通纪要技能”。记忆系统完全遵循你设计的蓝图。
- 渐进式披露,而非暴力搜索:智能体不是通过语义搜索一次性获取一堆可能相关的记忆。相反,它通过调用
get_skill、list_skills等工具,像使用函数库一样,基于当前任务进行推理,主动请求它认为需要的特定技能文件。这模仿了人类“按需回忆”的思考过程,更精准,也更节省上下文。
实操心得:从“黑盒记忆”到“白盒技能库”的转变使用Acontext后,最深刻的体会是调试效率的质变。以前智能体犯错,你需要猜测是不是某段“记忆”在作祟,过程很痛苦。现在,你直接打开项目里的
skills/目录,里面整整齐齐地躺着handling_customer_complaint.md、generate_monthly_report_flow.md这样的文件。你可以像审查代码一样审查智能体的“经验”,发现它总结的步骤有误,直接编辑文件修正即可。下次运行时,智能体读取的就是更新后的、更优的版本。这种掌控感是传统方案无法提供的。
3. 核心架构与工作流程拆解
Acontext的架构清晰地区分了“存储”和“召回”两个核心过程,下面我们拆开来看。
3.1 存储流程:会话如何转化为技能文件?
整个学习过程是自动化的,但每一步都设计得可理解、可干预。其核心流程如下图所示(我们用文字描述替代图表):
- 会话消息流输入:这是学习的原材料。Acontext会接收智能体与用户(或环境)的完整对话消息流。你还可以选择性地传入工具调用记录、生成的产物等,作为更丰富的上下文。
- 任务完成/失败标记:这是学习的触发点。系统需要知道一段对话流对应哪个任务的结束。这可以通过两种方式实现:
- 显式报告:在你的智能体代码中,当任务完成时,明确调用Acontext SDK的相应方法(如
report_task_outcome)来标记。 - 自动推断:Acontext内置的LLM可以分析消息流,自动推断出任务的边界和最终状态(成功/失败)。这对于简化集成很有帮助。
- 显式报告:在你的智能体代码中,当任务完成时,明确调用Acontext SDK的相应方法(如
- 提炼:这是将“经历”转化为“知识”的关键一步。一个LLM(默认为GPT-4)会分析该任务相关的完整会话和轨迹,并回答一系列核心问题:这个任务的目标是什么?最终是如何达成或失败的?过程中有哪些关键步骤和决策点?用户表现出了哪些偏好或禁忌?这个步骤的输出是一份结构化的“经验总结”。
- 技能智能体决策:另一个LLM(技能智能体)会接收上一步的总结,并结合你预先定义的
SKILL.md架构规则,做出两个决策:- 路由:这条新知识应该归属到哪个现有的技能文件?还是需要创建一个新的?
- 整合:如何将新知识写入目标技能文件?是新增段落、修改现有内容,还是合并信息?
- 更新技能文件:根据技能智能体的决策,系统会以编程方式创建或修改对应的Markdown技能文件。所有这些文件都存储在你指定的学习空间中,结构完全遵循你的设计。
3.2 召回流程:智能体如何运用记忆?
当智能体在新的会话中运行时,它使用记忆的方式非常直观:
- 工具集成:你为智能体配备Acontext提供的“技能内容工具”,主要是
get_skill(按名称或ID获取特定技能内容)和list_skills(列出所有可用技能)。 - 智能体驱动召回:智能体根据当前对话的进展和任务需求,自主决定需要查阅哪些“经验”。例如,当用户说“帮我导出上个月的销售数据”时,智能体可能会推理:“我需要先调用
list_skills看看有没有关于‘数据导出’或‘月度报告’的技能”,然后调用get_skill(“generate_monthly_report_flow”)获取具体的操作步骤。 - 内容注入上下文:工具调用的结果——即技能文件的Markdown内容——会被作为上下文的一部分返回给智能体。智能体便能基于这些过往的成功经验来指导当前行动。
这个过程的核心是“渐进式披露”和“智能体在环”。记忆的获取不再是后台一个不可控的检索动作,而是变成了智能体主动、透明、可推理的工具使用行为。这极大地提升了行为的可预测性和可解释性。
4. 从零开始:实战部署与集成指南
理论讲完了,我们动手把它用起来。Acontext提供了云服务和自托管两种方式,我们将分别介绍。
4.1 方案一:使用托管云服务(最快上手)
对于大多数开发者和团队,直接从官方云服务开始是最便捷的选择。
注册与获取密钥:
- 访问 Acontext.io ,使用GitHub或邮箱注册。
- 完成一键式引导,系统会赠送免费额度,并为你生成一个唯一的API Key(格式为
sk-ac-...)。这个Key是调用所有API的凭证,务必妥善保管。
安装SDK: 根据你的开发语言选择安装对应的SDK。这里以Python为例:
pip install acontext如果你使用TypeScript/JavaScript,可以执行
npm install @acontext/acontext。初始化客户端: 在你的项目代码中,引入SDK并初始化客户端。
import os from acontext import AcontextClient # 使用云服务,传入你的API Key client = AcontextClient( api_key=os.getenv("ACONTEXT_API_KEY") # 建议从环境变量读取 )
4.2 方案二:本地自托管(追求数据可控)
如果你对数据隐私有更高要求,或者希望进行深度定制,Acontext提供了完整的自托管方案。它依赖Docker,可以快速在本地或私有服务器上拉起全套服务。
前置条件准备:
- 确保系统已安装 Docker 和 Docker Compose。
- 准备一个可用的OpenAI API Key(或其他兼容OpenAI API的LLM服务密钥),因为Acontext的学习和决策过程需要LLM驱动。
安装与启动CLI工具: Acontext提供了一个命令行工具
acontext-cli来简化部署。# 下载并安装CLI curl -fsSL https://install.acontext.io | sh # 创建一个目录用于存放服务数据 mkdir acontext_server && cd acontext_server # 启动服务 acontext server up执行
up命令后,CLI会引导你进行配置:- 它会创建或使用当前目录下的
.env文件,你需要在此文件中设置OPENAI_API_KEY等环境变量。 - 它会生成
config.yaml配置文件,你可以在这里调整LLM模型(默认gpt-4)、端口号等设置。 - 它会在本地启动多个Docker容器,包括API后端、数据库(PostgreSQL)、对象存储(MinIO)、消息队列(RabbitMQ)和Web管理界面。
- 它会创建或使用当前目录下的
验证与连接: 服务启动成功后,你可以访问以下地址:
- API 基础地址:
http://localhost:8029/api/v1 - 管理仪表盘:
http://localhost:3000/初始化自托管客户端的代码如下:
from acontext import AcontextClient client = AcontextClient( base_url="http://localhost:8029/api/v1", # 指向你的本地API api_key="sk-ac-your-root-api-bearer-token", # 初始token通常在启动日志或仪表盘中找到 )- API 基础地址:
注意事项:自托管的环境与配置
- 资源消耗:自托管会启动多个容器,建议预留至少4GB内存和2核CPU。
- 数据持久化:CLI默认会在当前目录下创建
db/、storage/等子目录来持久化数据。定期备份这些目录即可。- LLM配置:在
config.yaml中,你可以将llm.provider从openai改为azure_openai、anthropic或local(连接本地Ollama等),并配置相应的参数。这是实现完全私有化部署的关键。- 生产部署:
acontext server up适合开发和测试。对于生产环境,建议参考官方文档,使用更成熟的编排工具(如Kubernetes Helm Chart)进行部署。
5. 核心功能实战:让智能体开始学习与回忆
现在,我们通过一个完整的代码示例,看看如何将Acontext集成到你的智能体工作流中。
5.1 创建学习空间与会话
学习空间可以理解为一个独立的“项目”或“知识库”,所有相关的技能文件都会存储在这里。会话则代表一次具体的对话或任务执行过程。
from acontext import AcontextClient import time client = AcontextClient(api_key="your_api_key_here") # 1. 创建一个学习空间,可以给它起个名字,比如“CustomerSupportBot” space = client.learning_spaces.create(name="CustomerSupportBot") print(f"学习空间创建成功,ID: {space.id}") # 2. 创建一个会话,代表一次独立的客服对话 session = client.sessions.create() print(f"会话创建成功,ID: {session.id}") # 3. 将这个会话关联到学习空间,意味着这个会话中的经验将沉淀到该空间 client.learning_spaces.learn(space_id=space.id, session_id=session.id)5.2 模拟智能体运行并存储消息
接下来,我们模拟智能体与用户的交互过程。你需要将Acontext的store_message方法插入到你智能体框架的消息处理循环中。
# 模拟用户发起对话 client.sessions.store_message( session_id=session.id, blob={ "role": "user", "content": "你好,我的订单#12345显示已发货,但一周了还没收到物流更新,能帮我查一下吗?" } ) # 模拟智能体回复并调用内部工具(例如查询订单系统) # 假设智能体调用了一个 `query_order_system` 的工具并成功获取了信息 client.sessions.store_message( session_id=session.id, blob={ "role": "assistant", "content": "好的,已为您查询。订单#12345由XX快递承运,单号是YT123456789。当前物流信息显示【运输中】,最新节点是【已到达上海中转中心】。预计明天会有派送更新。这是详细的物流截图。", # 你可以选择性地存储工具调用结果或生成的产物(如图片URL) "metadata": { "tool_calls": [{"name": "query_order_system", "input": {"order_id": "12345"}}], "artifacts": ["https://example.com/tracking_screenshot.png"] } } ) # 模拟用户确认问题解决 client.sessions.store_message( session_id=session.id, blob={"role": "user", "content": "看到了,谢谢!原来已经到上海了,那我再等等。"} ) # 4. 关键步骤:标记任务完成 # 当对话结束或任务达成时,你需要显式报告结果,以触发学习流程。 # 这里我们标记为“completed”(成功完成)。 client.sessions.report_task_outcome( session_id=session.id, outcome="completed", # 也可以是 "failed" summary="用户查询物流停滞订单,助理成功提供快递单号和当前物流节点,用户满意。" # 可选,帮助提炼 )5.3 触发学习与获取技能
报告任务结果后,Acontext后端会异步启动我们之前描述的“存储流程”。在演示中,我们可以使用一个辅助方法等待学习完成。
# 等待该会话的学习过程完成(生产环境是异步的,无需等待) client.learning_spaces.wait_for_learning(space_id=space.id, session_id=session.id) # 列出学习空间内所有已生成的技能 skills = client.learning_spaces.list_skills(space_id=space.id) print(f"当前空间共有 {len(skills)} 个技能文件:") for skill in skills: print(f" - {skill.name} (ID: {skill.id})") # 获取并查看某个技能的具体内容 if skills: skill_content = client.skills.get_content(skill_id=skills[0].id) print(f"\n技能 '{skills[0].name}' 的内容预览:") print(skill_content[:500]) # 打印前500字符5.4 在后续会话中召回技能
当新的用户提出类似问题时,你的智能体就可以利用之前积累的技能了。
# 在新的会话中 new_session = client.sessions.create() client.learning_spaces.learn(space_id=space.id, session_id=new_session.id) # 假设智能体通过推理,认为需要“查询物流”相关的技能 # 它首先可以列出所有技能寻找相关项 available_skills = client.learning_spaces.list_skills(space_id=space.id) # 智能体逻辑:过滤或选择技能(这里简化为搜索名称含“物流”的技能) logistics_skills = [s for s in available_skills if "物流" in s.name] if logistics_skills: # 获取第一个相关技能的内容 skill_content = client.skills.get_content(skill_id=logistics_skills[0].id) # 将这个技能内容作为系统提示或上下文的一部分,提供给LLM enhanced_prompt = f""" 根据我们以往的经验,处理物流查询的步骤如下: {skill_content} 现在,用户的新问题是:我的订单#67890怎么没动静? 请根据以上经验进行回复。 """ # 将 enhanced_prompt 发送给你的LLM/智能体... print("已注入历史技能到上下文。")实操心得:集成模式的选择上述示例展示了最基础的API调用集成。在实际项目中,根据你使用的智能体框架,有更优雅的集成方式:
- LangChain / LlamaIndex:可以开发一个自定义的
AcontextMemory或Retriever组件,将其无缝嵌入到Chain或Agent的初始化中。- OpenAI Assistants API / Anthropic Messages API:你可以将
get_skill和list_skills封装为Function Calling工具,让助手在需要时自行调用。- Acontext SDK 高级封装:Acontext的Python/TS SDK本身就提供了与流行框架(如OpenAI Agent SDK, Claude Agent SDK)的存储层集成,可以自动处理消息存储和任务结果报告,进一步减少样板代码。查看官方示例仓库是快速上手的最佳途径。
6. 高级特性与生态工具
除了核心的记忆功能,Acontext还提供了一系列强大的周边工具,用于构建更复杂的智能体应用。
6.1 上下文工程
当技能文件越来越多,直接全部塞进上下文窗口是不现实的。Acontext的上下文工程功能可以帮助你压缩和管理这些技能内容。
- 总结:可以自动为长篇技能文件生成简洁的摘要,在
list_skills时返回摘要而非全文,供智能体初步筛选。 - 编辑策略:你可以定义规则,例如“只注入技能中的核心步骤列表”或“当技能被引用时,自动附上其创建日期和成功率统计”。这让你能精细控制进入上下文的记忆内容和格式。
6.2 磁盘与沙箱
这是Acontext将“技能即文件”理念贯彻到底的体现。
- 虚拟磁盘:为智能体提供一个持久化的、类似文件系统的存储空间。智能体可以通过工具在这里创建、读取、写入文件。这个磁盘的内容可以跨会话持久化。
- 沙箱环境:一个安全的代码执行环境(支持Bash, Python等)。智能体可以在这里运行代码来处理数据、测试脚本。
- 技能挂载:最强大的特性之一。你可以将整个技能文件夹“挂载”到沙箱的某个路径下。这意味着智能体在沙箱中执行代码时,可以直接像读写普通文件一样操作
skills/目录下的Markdown文件。例如,一个编码智能体可以将“最佳代码结构”技能挂载到沙箱,然后在编写新项目时直接读取参考。
6.3 预置的Agent工具集
Acontext SDK内置了一系列开箱即用的工具,方便智能体调用:
- 技能工具:
get_skill,list_skills,search_skills(基于关键词的简单搜索)。 - 磁盘工具:
read_file,write_file,list_directory。 - 沙箱工具:
run_bash_command,run_python_script。 这些工具都遵循LLM Function Calling的规范,可以轻松集成到大多数智能体框架中。
7. 常见问题与排查技巧实录
在实际集成和使用Acontext的过程中,你可能会遇到一些典型问题。以下是我根据经验整理的排查清单。
7.1 学习过程未触发
- 症状:会话消息存储了,但学习空间里始终没有生成新的技能文件。
- 排查步骤:
- 检查任务结果报告:确保在会话结束后调用了
client.sessions.report_task_outcome(...)。这是触发学习的主要信号。 - 检查学习关联:确认会话是否通过
client.learning_spaces.learn()正确关联到了目标学习空间。 - 查看后台处理状态:如果是自托管,查看Acontext后台服务的日志(通常通过
docker-compose logs -f core),看是否有提取或学习任务报错。常见错误是LLM API调用失败(额度不足、网络问题、错误的模型配置)。 - 确认消息格式:
store_message中的blob字段至少需要包含role和content。如果包含metadata,请确保其是合法的JSON字典。
- 检查任务结果报告:确保在会话结束后调用了
7.2 技能内容不理想或格式错误
- 症状:生成的技能文件内容杂乱、重复,或者没有按照你预期的
SKILL.md结构来组织。 - 排查步骤:
- 审查SKILL.md文件:学习空间的行为由
SKILL.md文件定义。你需要为你的学习空间上传或创建一个清晰、结构化的SKILL.md模板。这个文件应该用Markdown写明你希望技能如何分类(如按主题、按类型)、每个技能文件应包含哪些章节(如## 问题描述、## 解决步骤、## 注意事项)。Acontext的技能智能体会尽力遵循这个模板。 - 提供更丰富的上下文:在
report_task_outcome时,提供一个高质量的summary参数。这个总结会直接作为提炼步骤的重要输入,帮助LLM更好地理解任务核心。 - 调整LLM配置:在自托管配置中,尝试使用更强大的LLM模型(如从
gpt-3.5-turbo切换到gpt-4)来处理提炼和路由任务,质量通常会有显著提升。
- 审查SKILL.md文件:学习空间的行为由
7.3 智能体无法有效召回技能
- 症状:智能体似乎“忘记”了已有的技能,或者在需要时不会主动调用
get_skill。 - 排查步骤:
- 工具描述优化:确保你提供给LLM的
get_skill和list_skills的工具描述清晰明了。在描述中强调这些工具用于获取“过往的成功经验、操作流程或已知解决方案”。 - 在系统提示中引导:在给智能体的系统指令中,明确告知它:“你可以通过调用
list_skills工具来查看我们积累的知识库,并使用get_skill工具获取特定经验的详细内容来帮助解决当前问题。” - 检查技能命名:技能的命名应具有描述性且易于理解。像
handle_shipping_delay_complaint.md就比session_12345_summary.md要好得多。清晰的命名有助于LLM在list_skills返回的结果中进行推理和选择。
- 工具描述优化:确保你提供给LLM的
7.4 性能与成本考量
- 问题:每次学习都调用LLM,成本会不会很高?速度会不会慢?
- 经验之谈:
- 异步学习:在生产环境中,学习过程是完全异步的。你报告任务结果后即可继续,无需等待。学习在后台队列中处理,不影响主流程响应速度。
- 批量处理:对于高频但低价值的交互,不必每个会话都触发学习。可以积累多个相似会话后,进行一次性的批量学习和总结。
- 模型选型:在自托管中,可以为“提炼”和“路由”这两个LLM调用步骤配置不同的模型。例如,用
gpt-4做关键的经验提炼以保证质量,用更快的gpt-3.5-turbo来处理技能文件的路由和写入操作。 - 技能去重:Acontext会尝试将新知识合并到现有技能中,而不是总是创建新文件,这有助于控制技能库的规模,避免碎片化。
8. 项目模板与进阶构建
Acontext社区提供了丰富的示例模板,能让你在几分钟内搭建起一个集成了记忆功能的智能体应用。
使用acontext-cli可以快速拉取模板:
# 拉取一个基于OpenAI Agent SDK的基础模板 acontext create my-ai-assistant --template-path "python/openai-agent-basic" cd my-ai-assistant # 安装依赖,配置环境变量(你的Acontext和OpenAI API Key) pip install -r requirements.txt cp .env.example .env # 编辑 .env 文件填入你的密钥 # 然后就可以运行了 python main.py这些模板通常已经做好了以下集成:
- 初始化Acontext客户端并创建学习空间。
- 将Acontext的存储层挂钩到智能体框架的消息流中。
- 提供了
get_skill等工具的定义。 - 包含了一个简单的对话循环示例。
我强烈建议从**python/interactive-agent-skill** 或typescript/interactive-agent-skill模板开始。它演示了如何将技能文件挂载到沙箱,并让智能体在代码执行环境中直接读取这些文件,这个模式对于编码、数据分析等需要参考文档的智能体来说极其强大。
Acontext代表的是一种思维转变:将智能体从依赖模糊、不可控的“记忆”,转向管理清晰、可操作的“技能资产”。它可能不是所有场景的银弹,但对于那些需要智能体持续学习、积累领域知识、且过程要求透明可控的项目来说,它提供了一个极其优雅和实用的解决方案。开始尝试将你的智能体对话记录变成一个个Markdown文件吧,你会发现调试和改进它们的过程,变得和管理普通代码库一样熟悉和高效。
