
1. 项目概述从“蛮力”到“巧劲”的架构演进最近在折腾一个叫 OpenClaw 的开源项目它本质上是一个基于大语言模型的智能体框架。项目初期为了追求极致的上下文完整性我们采用了一种现在看来有点“粗暴”的策略全量上下文注入。简单说就是把用户当前的问题、智能体过往的所有对话历史、知识库里的相关文档甚至一些系统指令一股脑儿全部塞进大模型的上下文窗口里然后让它去理解和回答。这么干初期效果确实不错模型“看”得全回答的连贯性和准确性都有保障。但很快问题就暴露了。随着对话轮次增加、知识库文档变多上下文长度呈指数级增长。这不仅带来了高昂的 API 调用成本毕竟 GPT-4 这类模型是按 Token 计费的更致命的是当上下文长度超过模型窗口限制比如 128K时要么需要昂贵的截断要么直接报错。更糟糕的是过长的上下文会导致模型注意力分散出现“中间迷失”现象——模型可能记不住或无法有效处理埋在冗长文本中间的关键信息回答质量反而下降。于是我们启动了一次核心架构优化目标很明确把“全量上下文注入”这个“蛮力”模式升级为更精巧的“路由 记忆 编排”组合拳。这不仅仅是技术上的优化更是一种设计思维的转变从“让模型看到一切”变为“为模型精准投喂它最需要的信息”。接下来我就把这套优化方案的思路、实现细节和踩过的坑毫无保留地分享出来。2. 核心架构设计拆解“路由、记忆、编排”三要素2.1 为什么是这三者在深入代码之前我们先要理解“路由”、“记忆”、“编排”这三个概念在智能体框架中分别扮演什么角色以及它们如何协同工作替代笨重的“全量上下文”。路由顾名思义就是信息分发的决策中心。它的核心职责是根据当前用户查询的意图和内容动态决定需要调用哪些能力、查询哪些知识、激活哪段记忆。比如用户问“上周我们讨论的关于项目预算的结论是什么”路由模块不应该把整个项目文档库都丢给模型而是应该识别出这是一个“记忆检索”任务并精准定位到“上周”和“项目预算”这两个关键维度。记忆是智能体的“经验库”和“工作记忆”。它不再是简单的聊天记录堆砌而是被结构化、向量化、并赋予时效性和相关性权重的信息存储。记忆系统需要能区分哪些是长期有用的知识如产品规格哪些是短期会话状态如用户刚说喜欢喝咖啡并能高效地根据路由的指令进行检索和更新。编排则是工作流的“指挥家”。它接收路由的决策结果按照预定义或动态生成的逻辑有序地调用各种工具如搜索 API、代码执行器、查询记忆系统、组织模型的多次调用并将最终结果整合后返回给用户。编排确保了复杂任务如“分析数据并生成报告”能够被分解、执行并汇总。这三者形成了一个高效的闭环路由做决策记忆供弹药编排管执行。模型在每一轮交互中接收到的都是经过这个闭环精心筛选和组织的、高浓度、高相关性的信息从而能用更短的上下文、更低的成本做出更精准的响应。2.2 新旧架构对比与选型考量为了更直观地理解这次重构的价值我们用一个表格来对比优化前后的核心差异对比维度旧架构全量上下文注入新架构路由记忆编排优化收益上下文长度随对话和知识库增长而线性/指数增长极易超限。动态可控仅包含路由结果、相关记忆和当前轮次信息长度稳定。显著降低Token消耗避免窗口超限错误。信息密度低。包含大量无关历史和信息噪音。高。经过路由筛选只保留最相关、最关键的信息。提升模型理解效率与回答质量缓解“中间迷失”。系统响应速度可能较慢尤其是准备超长上下文时。更快。记忆检索尤其是向量检索和轻量级路由决策速度快。改善用户体验支持更实时交互。架构复杂度低简单粗暴。高。需要设计并实现三个子系统及其交互协议。带来了长期的可维护性、可扩展性优势。成本高。每次调用都携带大量历史Token。低。仅传递精华信息API调用成本大幅下降。直接降低运营成本尤其对于高频应用。能力边界受限于单次模型窗口难以处理超长文档或多步骤复杂任务。可扩展。通过编排串联多个模型调用和工具使用能处理更复杂任务。解锁了复杂任务自动化的潜力。在技术选型上我们基于 OpenClaw 的 Python 技术栈和轻量级目标做出了以下选择路由模块初期采用基于嵌入向量的语义匹配 规则引擎的混合模式。我们使用sentence-transformers库生成查询和路由选项的向量计算余弦相似度进行粗筛再通过少量关键词规则进行精调。这比训练一个分类器更快速、可控。记忆模块核心是向量数据库。我们选择了ChromaDB因为它轻量、易嵌入、且 API 友好。长期记忆知识库和短期会话记忆都通过向量化存储于此。同时我们维护了一个简单的 SQLite 数据库来存储记忆的元数据如时间戳、来源、类型便于基于时间的过滤和管理。编排模块采用了基于LangChain 的 Expression Language进行原型设计。它提供了一种声明式的方式来定义任务链非常直观。对于更复杂的、有状态的多轮编排我们则基于asyncio和状态机自行实现了一个轻量级编排引擎。注意这里没有选择更重的 Airflow 或 Prefect是因为智能体任务的粒度更细、更动态需要一个能紧密集成在应用内部的轻量级解决方案。3. 核心模块实现细节与实操要点3.1 路由模块从意图识别到精准调度路由模块是整个系统的“大脑”它的设计好坏直接决定了后续步骤的效率。我们的路由逻辑分为两层意图识别和资源调度。意图识别部分我们放弃了训练复杂 NLP 模型的想法采用了“预定义技能槽 语义匹配”的方案。首先我们为智能体定义了若干核心“技能”或“任务类型”例如qa_from_knowledge_base从知识库问答search_internet联网搜索recall_conversation_memory回忆特定对话perform_calculation执行计算general_chat通用闲聊每个技能都有一个描述文本如“从本地知识库中查找相关信息并回答问题”和一组可能触发的关键词或短语。当用户查询到来时我们执行以下步骤向量化查询与技能描述使用all-MiniLM-L6-v2模型轻量且效果不错将用户查询和所有技能描述转化为向量。计算相似度计算查询向量与每个技能描述向量的余弦相似度。阈值过滤与规则修正取相似度最高的技能如果其分数超过预设阈值如0.7则初步判定为该意图。同时一个并行的规则引擎会检查查询中是否包含特定关键词如“搜索一下”、“查资料”对应search_internet对向量结果进行修正或加权。这解决了语义相似但意图不符例如“帮我找昨天的聊天记录”和“从知识库找昨天更新的文档”在向量上可能接近但意图不同的问题。生成路由指令最终输出一个结构化的路由指令对象例如{ intent: qa_from_knowledge_base, confidence: 0.85, parameters: { topic: 项目预算, time_range: last_week } }资源调度部分则根据路由指令决定需要拉取哪些记忆。例如对于qa_from_knowledge_base调度器会向记忆模块发起一个向量检索请求查询词可能是用户问题的核心部分对于recall_conversation_memory则会附加时间过滤条件。实操心得阈值不要设死。我们最初设定了固定的相似度阈值发现在不同类型的问题上效果不稳定。后来改为动态阈值对于关键任务如知识库问答阈值设高以减少误触发对于次要任务如闲聊阈值设低以提升覆盖率。也可以根据查询长度微调阈值。3.2 记忆模块向量化存储与智能检索记忆模块是系统的“仓库”设计目标是存得进、找得准、取得快。我们将其分为三个子存储区长期记忆知识库存储产品文档、手册、项目资料等静态知识。文档被切分成有重叠的 chunk如 500 字一段重叠 50 字然后向量化存入 ChromaDB。每个 chunk 都带有元数据如来源文件、标题、章节等。短期记忆会话记忆存储当前对话轮次中的关键信息。这里我们没有存储每一句对话而是设计了一个“记忆提炼”过程在每轮对话结束后让一个小模型如 GPT-3.5-turbo或启发式规则从对话中提取关键实体、事实结论或用户偏好形成一条结构化的记忆条目再向量化存储。例如提取出“用户偏好深色模式”、“本次讨论确定了项目截止日期为2024-10-01”。工作记忆上下文缓存这是一个纯内存缓存存储当前任务链的中间状态、工具调用结果等临时信息生命周期仅限于当前任务会话。检索过程是记忆模块的核心。当编排模块发起检索请求时记忆模块并非简单返回 top-k 个最相似的片段。我们实现了一个“混合检索与重排序”流程第一步向量召回用查询向量在 ChromaDB 中进行相似度搜索召回前 N 个如 20 个候选片段。第二步元数据过滤根据路由指令附带的参数如time_range利用 SQLite 中的元数据对候选片段进行过滤。第三步相关性重排序使用一个更精细的、计算代价稍高的重排序模型如cross-encoder/ms-marco-MiniLM-L-6-v2对过滤后的候选片段进行重新打分和排序。这一步能显著提升 top-k 结果的相关性。第四步多样性去重对得分最高的片段进行基于内容或语义的简单去重避免返回过多重复信息。最终返回将经过筛选、重排、去重后的 top-k如 5 个最相关记忆片段连同其元数据和相关性分数返回给编排模块。踩坑记录直接使用向量检索的 top-k 结果经常会遇到“信息冗余”和“关键信息漏检”的问题。比如同一个事实在不同 chunk 中重复出现会挤占其他有用信息的空间。引入重排序和去重后最终注入上下文的记忆质量有了质的提升。虽然增加了少量计算开销但相比大模型 API 的成本和效果提升是完全值得的。3.3 编排模块串联任务与状态管理编排模块是“双手”负责将路由的决策和记忆的弹药组合成一次有效的模型调用或任务流。我们定义了几种基本的编排模式简单问答链适用于qa_from_knowledge_base。编排逻辑是路由 - 检索记忆 - 构造提示词包含问题相关记忆 - 调用大模型 - 返回答案。工具使用链适用于search_internet或perform_calculation。逻辑是路由 - 解析参数 - 调用对应工具/API - 将工具结果作为上下文 - 调用大模型进行总结或回答 - 返回。多轮对话链适用于需要结合历史记忆的复杂对话。逻辑更复杂需要维护一个对话状态机在每一轮中更新对话状态 - 路由 - 检索相关长/短期记忆 - 根据状态和记忆构造提示词 - 调用模型 - 解析模型输出并更新状态/记忆。我们使用 LangChain 的 LCEL 来快速搭建简单链。例如一个简单的知识库问答链如下所示from langchain.prompts import ChatPromptTemplate from langchain.schema.output_parser import StrOutputParser from langchain.schema.runnable import RunnablePassthrough # 定义提示词模板 prompt_template ChatPromptTemplate.from_messages([ (system, 你是一个助手请根据以下提供的上下文信息来回答问题。如果上下文不包含答案请直接说不知道。\n\n上下文{context}), (human, {question}) ]) # 构建链检索器记忆模块 - 格式化上下文 - 提示词 - 模型 - 输出解析 retriever ... # 连接到我们的记忆模块检索接口 qa_chain ( {context: retriever, question: RunnablePassthrough()} | prompt_template | llm # 配置好的大模型对象 | StrOutputParser() ) # 使用 answer qa_chain.invoke(项目预算有多少)对于更复杂的、有状态的多轮编排我们则实现了一个简单的Orchestrator类内部维护一个会话状态字典并根据路由结果执行不同的处理函数。注意事项编排模块中提示词工程至关重要。传递给模型的提示词必须清晰地将“系统指令”、“路由决策背景”、“检索到的记忆”、“用户当前问题”以及“需要模型执行的动作”区分开来。我们通常使用明确的 XML 标签或章节标题来分隔不同部分例如system_role.../system_role\\nretrieved_memory.../retrieved_memory\\nuser_query.../user_query\\nthinking_instruction.../thinking_instruction。这能极大提高模型对复杂输入的理解能力。4. 集成与优化让三者协同工作4.1 数据流与接口设计三个模块通过清晰的接口进行解耦。我们定义了几个核心数据结构和 APIRoutingRequest/RoutingResponse: 路由模块的输入输出。MemoryQuery/MemoryRetrievalResult: 记忆模块的查询和返回。OrchestrationContext: 编排模块维护的上下文包含当前会话ID、用户查询、路由结果、已检索的记忆、工具调用历史等。整个系统的核心数据流如下用户发起查询。编排模块接收查询创建或加载本次会话的OrchestrationContext。编排模块将查询封装为RoutingRequest调用路由模块。路由模块返回RoutingResponse包含意图和参数。编排模块根据路由结果构造一个或多个MemoryQuery调用记忆模块。记忆模块返回MemoryRetrievalResult包含相关记忆片段列表。编排模块整合路由结果、检索到的记忆、历史上下文从工作记忆中构造最终的提示词。编排模块调用大语言模型 API获得回复。编排模块处理回复如提取结构化信息、触发工具调用等并更新会话记忆和工作记忆。将最终回复返回给用户并可选地触发异步的记忆提炼过程将本轮关键信息存入短期记忆。4.2 性能调优与缓存策略优化后性能瓶颈可能出现在向量检索和模型 API 调用上。我们采取了以下措施向量检索优化使用更快的嵌入模型如all-MiniLM-L6-v2在速度和效果间取得了良好平衡。对 ChromaDB 进行索引优化并考虑对高频查询的片段进行内存缓存。多级缓存意图缓存对用户查询进行哈希缓存其路由结果意图和参数。因为很多用户的重复性问题意图是相同的。记忆检索缓存对MemoryQuery查询文本过滤参数进行哈希缓存其检索结果。这避免了相同问题对向量数据库的重复查询。模型响应缓存对于完全相同的提示词在排除了会话ID等变量后缓存模型的回复。这能直接节省 API 调用成本和时间。异步处理记忆提炼、日志记录等非关键路径操作全部改为异步执行不阻塞主响应链路。4.3 效果评估与迭代如何评估这次架构优化的效果我们设定了几个关键指标平均每次对话的 Token 消耗直接对比优化前后目标下降 50% 以上。任务完成准确率设计一组测试用例涵盖各意图人工或通过规则评估回答是否准确完成用户请求。响应延迟P95监控系统端到端响应时间确保在引入额外模块后没有显著劣化。成本直接计算单位时间内的 API 调用费用。我们通过 A/B 测试一段时间内部分流量走新架构来收集这些数据。实测下来Token 消耗平均降低了约 65%任务准确率因信息更精准而提升了约 10%响应延迟因检索和路由的耗时增加了一些但通过缓存优化控制在了可接受范围增加 200ms。总体成本下降非常明显。5. 常见问题与排查技巧实录在实际部署和运行这套新架构时我们遇到了一些典型问题这里汇总一下排查思路。问题现象可能原因排查步骤与解决方案路由意图识别错误例如用户问知识库问题却被路由到闲聊。1. 向量相似度阈值设置不合理。2. 技能描述文本不够准确或区分度低。3. 规则引擎关键词覆盖不全或冲突。1. 检查路由日志查看错误查询与各技能描述的相似度分数。调整阈值或引入动态阈值。2. 优化技能描述使其更具体、更具区分性。例如“回答问题”改为“根据提供的知识库文档片段回答问题”。3. 分析错误案例补充或修正规则引擎的关键词列表。记忆检索结果不相关模型基于错误信息作答。1. 嵌入模型不适合当前领域文本。2. Chunk 切分不合理导致语义碎片化。3. 检索时 top-k 值太小或未使用重排序。1. 尝试在领域文本上微调嵌入模型或更换更强大的开源模型如bge-large-zh-v1.5。2. 调整 chunk 大小和重叠度。对于技术文档可能需要按章节或语义边界如 Markdown 标题切分而非固定长度。3. 增加向量召回的 top-k如从5到20并确保重排序模块已启用且模型合适。多轮对话中上下文丢失模型忘记之前讨论的内容。1. 短期记忆提炼策略失效未提取关键信息。2. 工作记忆上下文缓存在轮次间被意外清空。3. 检索短期记忆时时间或主题过滤条件过严。1. 检查记忆提炼的日志看是否成功生成了结构化记忆条目。可以尝试用一个小模型如 Claude Haiku来总结对话要点比规则更可靠。2. 确保会话ID在轮次间正确传递且编排模块的状态管理逻辑正确。3. 放宽短期记忆检索的过滤条件或确保路由模块传递了正确的会话ID用于关联检索。系统响应变慢。1. 向量检索或重排序耗时过长。2. 缓存未命中率高。3. 编排逻辑中出现同步阻塞操作如写数据库。1. 对向量数据库进行性能剖析考虑对嵌入向量做索引如 HNSW。评估重排序模型的耗时必要时对高分结果才启用重排。2. 分析缓存命中率优化缓存键的设计增加缓存容量或调整过期策略。3. 将所有非关键 I/O 操作日志、记忆提炼改为异步。模型回答出现“根据以上上下文”但上下文不对。提示词构造错误将错误的记忆片段或路由信息注入了上下文。仔细检查编排模块中构建最终提示词的代码逻辑。添加调试日志输出即将发送给模型的完整提示词脱敏后人工检查其结构和内容是否正确。确保不同部分系统指令、记忆、问题分隔清晰。一个具体的调试案例我们曾遇到模型在回答知识库问题时偶尔会引用一个完全不相关的文档标题。通过日志发现问题出在记忆检索环节。某个热门查询的向量检索结果中总有一个相关性分数很高但实际无关的片段。根本原因是那份无关文档的某个段落包含大量与查询向量空间中的“高频通用词”相匹配的内容。解决方案我们在重排序之前加入了一个基于元数据如文档类型、重要性评分的简单加权过滤并引入了检索结果的多样性采样避免 top 结果都来自同一份低质量文档。这次从“全量上下文注入”到“路由记忆编排”的架构改造本质上是从“以模型为中心”向“以系统为中心”的思维转变。我们不再期望一个万能的大模型去消化所有杂乱信息而是构建一个智能的信息预处理管道让模型专注于它最擅长的推理和生成部分。这套架构不仅解决了成本和长度的燃眉之急更重要的是为 OpenClaw 的未来扩展——比如集成更多工具、支持更复杂的多智能体协作——打下了坚实的基础。如果你也在构建类似的 AI 应用强烈建议尽早考虑这种解耦和精细化的设计思路前期多花一点设计时间后期会省下大量的调试和重构成本。