
做能跑通Demo的AI智能体很容易做能稳定产出结果的Agent系统很难——这是我在过去大半年反复折腾Agent Harness智能体控制框架之后最深的体会。所谓Agent Harness通俗点理解就是套在LLM外面那层马鞍它负责决定给模型喂什么上下文、按什么顺序调用工具、怎么解析模型的输出、出错之后如何恢复。没有这层东西模型只是一台每句话都认真胡说八道的文本生成器有了它模型才真正变成一个能完成任务的智能体。这篇笔记是我从0到1搭建Agent系统的过程记录核心围绕两件事上下文管理和编排实践。适合正在从调API做问答往做真正的Agent进阶的开发者也适合已经在用LangGraph、Dify这类框架但总觉得差一口气的朋友。全文没有复杂晦涩的理论就是我自己踩坑之后换回来的实操经验。1. 为什么Agent系统必须先解决上下文管理很多团队做Agent的第一步是选模型、套框架、写工具结果一上线就发现效果稀烂模型经常忘记用户最早提的需求回答前后矛盾任务做一半开始自说自话。问题往往不出在模型本身而是出在上下文管理。1.1 Agent不同于ChatBot的核心差异聊天机器人是无状态的你问我答但智能体是有状态的持续任务执行者。一个典型Agent任务可能是帮我调研五家竞品的定价策略对比后输出一份表格并给出推荐方案——这个任务要分多步每一步都要依赖前一步的结果中间还可能要查多个数据源。这要求系统把所有中间状态、历史决策、已获取的信息都维护好再在合适的时机喂给模型。这就是Agent Harness里的上下文管理模块要做的事。没有它Agent就是金鱼记忆做任何超过三轮的任务都会崩。1.2 上下文窗口是硬约束从技术挑战到工程挑战目前主流模型单次输入上限大概是128K到200K token听起来很多但你要算一笔账系统提示词角色、规则、输出格式2K~8K token工具定义每个工具的JSON Schema 3个工具约1.5K10个工具可能到5K~10K当前任务相关的检索结果5K~15K token多轮对话历史和中间推理过程10K~50K token模型预留的输出空间至少5K~10K token这么算下来看似庞大的128K窗口真正留给有效上下文的往往只有一半。而且窗口越大响应越慢、费用越高超出后还会直接报错。所以上下文管理的本质不是塞得下而是塞得优——在有限预算里把最该让模型看到的信息放进去。2. 上下文管理实战从朴素截断到分层记忆我第一版Agent的上下文管理极其粗暴把所有历史消息拼起来超过限制就从最前面删。结果删掉的全是用户最早的核心需求模型越跑越偏。后来我总结出一套分层的记忆架构才算真正把这个问题按住。2.1 Token预算分配先规划再动手每次构建发给模型的上下文之前先算预算。我习惯用tiktoken做token统计而不是靠字符数估计因为中文和英文的token密度差别很大。核心规则是给各部分设定上限上下文分区预算占比说明系统提示词10%固定文案不含动态拼接工具定义10%~15%只加载当前步骤会用到的工具长期记忆检索结果15%~20%从向量库召回的相关片段短程对话历史30%~40%最近N轮含推理过程摘要当前任务信息10%~15%用户本次输入和中间产物输出预留10%~15%给模型生成留足空间这个分配不是死的但一定要在代码里显式做budget check。我在每次组装context前都会跑一遍token统计超出预算就逐级降级先压缩对话历史再减少检索片段最后才考虑减少工具定义。2.2 记忆分层短期、工作记忆和长期记忆把人脑的记忆模型搬到Agent上效果好得惊人短期记忆当前任务内的对话轮次直接用原始文本保留但要设上限比如最近8轮。工作记忆任务执行中的中间状态比如已经完成步骤2结果是XXX。这部分要结构化存储每次循环结束时更新。长期记忆跨会话需要留住的用户偏好、历史决策、已沉淀的知识通过向量数据库做相似度检索按需召回。长期记忆的关键是按需召回而不是全量加载。我实测下来全量加载一万个字符的历史记录模型回答准确率提升有限但token费用暴涨80%。改成向量检索后只加载和当前任务最相关的3~5个片段效果几乎一样成本却只有原来的三分之一。2.3 对话历史的压缩策略总结式替代法当对话超过短期记忆的轮次上限时不要简单丢弃而是用滚动摘要技术把前面的对话用LLM生成一段结构化摘要连同最近的原始对话一起作为新上下文。我第一次实现时踩过一个坑——每轮都重新总结整段历史导致重复计算、费用翻倍。正确做法是增量总结只把上一轮摘要和新增对话merge后再总结这样每次只花费一次总结的token。还有一个容易被忽略的细节摘要有信息损耗率。我后来给摘要加了一个强制字段key_info要求模型把涉及的用户硬性要求比如预算小于一万必须周五前交付单独列出而不是混在概括性描述里。这个改动直接让Agent在长任务中的需求保持率从60%提到了90%以上。3. 编排实践Agent Harness的骨架搭法上下文管理解决的是模型看到什么编排解决的是模型按什么顺序、什么节奏干活。我把这块拆成控制循环、编排模式、容错设计三个层面来聊。3.1 控制循环每个Agent都会跑圈关键是有迹可循现在主流Agent都基于ReAct模式思考Reasoning→ 行动Action→ 观察Observation→ 再思考。这个循环本身不难难的是在循环外面包一层管理壳。这层壳至少要管三件事记录每一轮的输入输出和token消耗方便回溯和计费。设定最大迭代轮数比如8轮防止Agent陷入死循环。每轮循环结束时做状态校验检查预期产出是否达成、是否需要切换策略。实操中我建议把控制循环做成独立模块不跟业务代码混在一起。我的做法是给每轮循环打上iteration_id所有日志、工具调用、上下文快照都挂在同一个id下面。排查问题时按id一拉就是完整链路。3.2 四种编排模式顺序、并联、条件分支和人机回环单个Agent能做的事有限复杂的业务场景需要多个步骤、甚至多个Agent协作。我在项目中沉淀下来的四种常用编排模式顺序编排任务严格按步骤执行前一步的输出是后一步的输入。适合流程固定、依赖关系强的场景比如数据清洗→分析→生成报告。并联编排把一个任务拆成多个独立子任务并行执行最后汇总。适合调研、批量处理等场景。我实测并行Agent比逐个串行快3~5倍token消耗却几乎不变。条件分支编排根据中间结果决定下一步走哪条路。比如Agent判断用户输入合法但需要人工复核就走人工审核节点而不是继续自动执行。人机回环编排在关键节点暂停等用户确认后再继续。涉及支付、发邮件、删除数据等不可逆操作必须插入人工确认点。这个模式看起来不够自动化但恰恰是Agent能落地到生产环境的前提。新手最容易犯的错是一上来就设计超级复杂的图编排。我现在的经验是先用顺序编排跑通最小闭环再按实际需求逐步加分支和并联。编排图越简单越容易排查问题。3.3 工具调用与容错设计Agent出错了要能自己爬出来工具调用是Agent跟外部世界交互的通道也是最容易出问题的地方。工具定义至少要注意参数名要见名知义、描述要写清楚使用场景、必填参数和可选参数要明确区分。我见过太多工具定义写得模棱两可导致模型反复用错参数格式。容错这块我一直推进三个设计原则重试退避工具调用失败后先记录错误信息返回给模型让它尝试修正参数后重试最多3次。每次重试间隔递增避免打爆下游服务。降级路径主工具失败时预备备用方案。比如主数据库查询失败自动切换到缓存版本如果备用也不行就明确告诉用户目前无法获取实时数据。自主容错让Agent学会低头。在系统提示词里明确写连续两次工具失败后必须停止盲目重试向用户说明当前卡点请求调整方案。这个约束看起来简单但能避免Agent在错误方向上耗费大量token和时间。4. Agent Harness落地选型LangGraph、Dify还是自研框架选型是每个团队都会纠结的问题。我的判断标准很简单项目阶段、团队技术栈、对可定制程度的要求。4.1 三种路线的对比方案优势劣势适合场景LangGraph编排灵活图结构表达能力强Python生态熟悉学习曲线陡需要自己写很多控制逻辑有开发能力、要做定制化Agent的团队Dify可视化编排内置上下文管理、知识库、工具市场定制深度受限复杂逻辑不好表达产品/运营主导、快速出原型验证自研Harness完全可控优化空间大开发周期长容错和边界情况全靠自己扛业务逻辑极度复杂、有专门团队维护我在实际项目里的路径是先用Dify把业务逻辑验证跑通确认价值后再用LangGraph做生产版本。这样既降低了前期的确认成本又保留了后期的控制力。4.2 用LangGraph搭建最小Harness的骨架LangGraph的核心概念是Graph节点Node代表处理逻辑边Edge代表流转条件。我搭的最小Agent Harness包含四个节点Router路由判断用户意图决定下一步是调用工具还是直接回答。ToolExecutor工具执行解析模型的工具调用请求执行并返回结果。MemoryUpdate记忆更新把本轮对话和中间结果写入工作记忆和长期记忆。Responder应答生成最终回复。伪代码结构大概是这样的from langgraph.graph import StateGraph, END class AgentState(dict): messages: list # 短程对话 memory: dict # 工作记忆 tool_results: dict # 工具执行结果 def router(state): # 根据意图决定走 tool 还是 respond return tool if state[need_tool] else respond graph StateGraph(AgentState) graph.add_node(router, router) graph.add_node(tool, execute_tool) graph.add_node(memory, update_memory) graph.add_node(respond, generate_response) graph.add_edge(router, tool, condition...) graph.add_edge(tool, memory) graph.add_edge(memory, router) graph.add_edge(router, respond)关键点是State定义。我把messages、memory、tool_results分开存这样每个节点只关心自己需要的部分避免一张大状态表谁都在改、改乱了。4.3 上下文组装的核心代码片段这是我项目里反复调优后沉淀出来的一段上下文组装逻辑核心是先测token再拼接import tiktoken encoding tiktoken.get_encoding(cl100k_base) def build_context(state, max_budget32000): parts [] budget_left max_budget # 1. 系统提示词固定 sys_prompt load_system_prompt() parts.append({role: system, content: sys_prompt}) budget_left - len(encoding.encode(sys_prompt)) # 2. 工具定义只加载本步骤需要的 tool_defs load_tools_for_current_step(state[current_step]) parts.append({role: tool_defs, content: json.dumps(tool_defs)}) budget_left - len(encoding.encode(json.dumps(tool_defs))) # 3. 长期记忆检索按相关性排序截取前K条 memories retrieve_memories(state[task_id], top_k5) memory_text format_memories(memories) if len(encoding.encode(memory_text)) budget_left * 0.2: memory_text truncate_text(memory_text, int(budget_left * 0.2)) parts.append({role: system, content: [记忆] memory_text}) budget_left - len(encoding.encode(memory_text)) # 4. 对话历史保留摘要 最近原始轮次 summary state[rolling_summary] recent state[messages][-6:] history_text f[摘要]{summary}\n[最近] {json.dumps(recent, ensure_asciiFalse)} parts.append({role: user, content: history_text}) return parts这套逻辑的核心是顺序固定、预算前置每一部分都在拼装前就计算token占用超预算第一时间截断而不是拼完才发现超了又重新裁。5. 常见问题与排查技巧实录最后这部分我把实际运维中遇到频率最高的几个问题整理成速查表都是真金白银换来的经验。5.1 上下文管理典型问题症状排查方向解决方案模型忘记早期需求摘要是否丢失硬性要求摘要加key_info强制字段token费用暴涨是否全量加载历史改增量滚动摘要 向量检索回答前后矛盾上下文中新旧信息冲突设置信息时间戳旧信息标注过期超长任务中断单次上下文预算太小任务拆阶段每阶段独立上下文排查问题最重要的是日志。我的习惯是每轮循环都打印一行摘要轮次号、token消耗、节点名称、工具调用结果状态。这样出问题扫日志就能定位不用一头扎进茫茫对话记录里翻。5.2 编排中的死循环和空转Agent陷入死循环是最让人头疼的问题。典型表现是模型反复调用同一个工具参数几乎不变结果也没变化。我总结的停止条件有三级最大轮次保护硬限制、结果去重检测同一工具同一参数的结果连续出现2次就停、目标达成判定工具返回结果已经满足任务定义中的完成条件。建议在系统提示词里明确写入如果连续两次工具调用得到相同或高度相似的结果请停止调用并转用其他方案或直接告知用户。这些工程上的小约束比期望模型自觉要可靠得多。5.3 工具失败后Agent的自主恢复早期版本里工具一报错Agent就只会把错误信息原样念给用户听体验极差。后来我在工具执行节点里加了一层错误分类器网络超时类错误走重试退避参数格式类错误走重新解析修正参数分支业务逻辑类错误比如数据不存在则直接让Agent调整策略换一个查询路径。这个分类器本身不复杂用规则判断错误码和错误信息关键词就能覆盖大部分场景。如果重试三次仍然失败就触发人工接管信号把当前状态打包成一份摘要连同失败原因一起发给用户确认。这套机制上线后工具相关任务的一次性成功率从82%提升到96%用户满意度明显改善。我个人做Agent Harness最大的体会是别急着堆新功能先把上下文和编排这两个地基打牢。框架从LangGraph换到自研都没问题但上下文管理的思路、编排模式的取舍这些是通用的。另外一个小技巧分享给大家在Agent系统的早期阶段就建立完整的日志和指标看板token消耗、轮次分布、工具成功率这几个指标每天看一遍你能比模型更快发现系统里的隐患。这套方法帮我省下了无数排查时间也希望对你有所帮助。