Nodejs也能写Agent - 22.LangGraph篇 - 上下文工程
上一篇我们把可观测性立起来了streamEvents、LangSmith、结构化日志。出了错你至少能看见「卡在哪一步」。但说句扎心的trace 再漂亮也救不了窗口里塞的是垃圾。历史消息、RAG 片段、ToolMessage 一股脑堆进去——要么超限直接报错要么噪声淹没关键句模型一本正经地胡话。可观测性回答「发生了什么」上下文工程Context Engineering回答「模型看到了什么」——这才直接决定它能不能推对。这一篇把 Context Engineering 和 Prompt Engineering 掰开讲清一次调用里上下文怎么组成、Token 怎么预算以及 RAG 注入时怎么少塞垃圾。老规矩本文以官网最新文档核对过Context engineering in agents、Short-term memory、Prebuilt middleware。Agent 入口继续用createAgent——别再抄createReactAgent。网上不少教程手写一个同名trimMessages——别这么干官方就有trimMessages摘要优先走summarizationMiddleware。一、Prompt Engineering ≠ Context EngineeringPromptTemplate/ChatPromptTemplate——那是Prompt Engineering把单条指令写清楚、格式对、few-shot 到位。对话一变长、接上 RAG、再套多轮 tool 调用上下文窗口就成了稀缺资源。这时你优化的不再是「这句话怎么措辞」而是「这一整窗里放什么、什么顺序、超了怎么砍」——这就是 Context Engineering。维度Prompt EngineeringContext Engineering关注点单条 prompt 的措辞、格式、few-shot整段上下文的组成、顺序、长度与质量范围通常 system 当前 usersystem 历史 检索片段 工具结果 元数据目标让模型「理解任务」让模型「在有限窗口内看到最相关信息」官网说得更狠一点Agent 不可靠往往不是模型不够聪明而是没把「对的」上下文喂进去。AI Engineer 的头号工作就是这件事。二、一次调用里模型到底看到什么先用落地直觉拆开——一次 Agent 调用的 context通常长这样Context 组成System Prompt历史 MessagesRAG 检索片段Tool 结果 ToolMessage当前 User Message部分来源说明System Prompt固定或动态角色、规则、工具使用约定历史 MessagesCheckpointer / Memory多轮对话累积RAG 片段Retriever Top-K注入 prompt 的参考文档Tool 结果ToolMessageReAct 环里每次 tool 返回当前 User Message用户输入本轮问题官网再给你一层更完整的坐标系——你能控的不只是「消息列表」而是三类上下文Context Type你在控什么Transient / PersistentModel Context进模型的东西instructions、message history、tools、用哪颗模型、response format多为Transient只改本轮喂给模型的内容Tool Context工具能读什么、写什么State / Store / Runtime ContextPersistentLife-cycle Context模型调用与工具调用之间发生什么摘要、guardrails、日志……Persistent数据从哪来也要分清数据源范围例子Runtime Context单次会话配置userId、权限、环境State短期记忆当前 threadmessages、tool 结果、上传文件Store长期记忆跨会话用户偏好、沉淀事实落地机制是 Middleware。createAgent的middleware让你在 agent loop 的钩子上改上下文——不必把裁剪逻辑糊进业务节点。tool_callsdone__start__beforeModelModel CallafterModelTools__end__wrapModelCall改本轮送给模型的 messages / tools / prompt——瞬时默认不改 State。beforeModel/afterModel可以返回 State 更新例如删消息、换摘要——持久Checkpointer 下次还能看见。搞不清 Transient vs Persistent你就会踩这个坑以为「裁过了」结果 Checkpointer 里旧历史还在下一轮又全塞回来。三、Token 预算三种控窗策略模型窗口有限本地小模型尤其狠。管理原则很简单给每块预算超限有明确裁剪 / 压缩规则。1. 保留最近 N 轮只留最近几轮 user-assistant更早的直接丢掉。实现最简单适合短会话、demo。别手写一个叫trimMessages的函数去抢官方名字——下面用官网 API。2. 官方trimMessages按 token / 边界裁剪LangChain 提供trimMessages按maxTokens、strategy: last、startOn/endOn裁消息列表尽量保住对话结构例如从 human 起、在 human/tool 结束避免 AI↔Tool 成对被拦腰砍断。瞬时裁剪只改本轮喂给模型的内容State 原样保留——用wrapModelCallimport{createAgent,createMiddleware,trimMessages}fromlangchain;import{ChatOllama}fromlangchain/ollama;import{tool}fromlangchain/core/tools;import*aszfromzod;constgetWeathertool(async({city}:{city:string})${city}晴25°C,{name:get_weather,description:查询城市天气,schema:z.object({city:z.string()}),});/** 教学用按「条数」近似计数生产请换成真实 tokenCounter或模型自带计数 */constroughCounter(msgs:{length:number}|unknown[])Array.isArray(msgs)?msgs.length:0;consttransientTrimcreateMiddleware({name:TransientTrim,wrapModelCall:async(request,handler){consttrimmedawaittrimMessages(request.messages,{maxTokens:12,// 演示阈值生产按模型窗口设strategy:last,startOn:human,endOn:[human,tool],includeSystem:true,// 保住开头的 systemtokenCounter:roughCounter,});// override只改本轮请求不写回 Statereturnhandler(request.override({messages:trimmed}));},});constllmnewChatOllama({model:qwen2.5:7b,temperature:0});constagentcreateAgent({model:llm,tools:[getWeather],systemPrompt:需要天气时调用 get_weather。回答简洁。,middleware:[transientTrim],});持久裁剪真把 State 里的旧消息清掉配合 Checkpointer 才有意义——beforeModelRemoveMessageimport{RemoveMessage}fromlangchain/core/messages;import{createAgent,createMiddleware,trimMessages}fromlangchain;import{MemorySaver,REMOVE_ALL_MESSAGES}fromlangchain/langgraph;import{ChatOllama}fromlangchain/ollama;constpersistTrimcreateMiddleware({name:PersistTrim,beforeModel:async(state){consttrimmedawaittrimMessages(state.messages,{maxTokens:20,strategy:last,startOn:human,endOn:[human,tool],includeSystem:true,tokenCounter:(msgs)msgs.length,// 演示用生产换真实计数});// 先清空再写入裁剪结果 → State 永久变短return{messages:[newRemoveMessage({id:REMOVE_ALL_MESSAGES}),...trimmed],};},});constagentcreateAgent({model:newChatOllama({model:qwen2.5:7b,temperature:0}),tools:[],middleware:[persistTrim],checkpointer:newMemorySaver(),});// 同一 thread_id 多轮 invoke裁剪结果会跟着存档走awaitagent.invoke({messages:[{role:user,content:我叫小明}]},{configurable:{thread_id:u-1}});策略改 State适合wrapModelCalltrimMessages否Transient调试、按调用临时瘦身、还想保留完整审计历史beforeModelRemoveMessage是PersistentCheckpointer 长会话必须真的减负summarizationMiddleware是Persistent长对话要「记得大概」不能硬砍细节3. 摘要压缩summarizationMiddleware硬 trim 会丢信息。长会话更常见的做法旧消息用另一颗可更小更便宜的模型压成摘要永久写回 State只保留最近若干条原文。import{createAgent,summarizationMiddleware}fromlangchain;import{ChatOllama}fromlangchain/ollama;import{MemorySaver}fromlangchain/langgraph;constchatModelnewChatOllama({model:qwen2.5:7b,temperature:0});// 摘要可以用同一模型生产常换成更小/更便宜的constsummaryModelnewChatOllama({model:qwen2.5:7b,temperature:0});constagentcreateAgent({model:chatModel,tools:[],checkpointer:newMemorySaver(),middleware:[summarizationMiddleware({model:summaryModel,trigger:{tokens:4000},// 越过阈值才摘要keep:{messages:20},// 保留最近 20 条原文}),],});触发条件还可写成「多条件 AND」或「数组 OR」见官网 Prebuilt middleware。注意摘要是文本向压缩——多模态大图不会被「压小」只会被摘要文字替代图多的场景要把媒体放对象存储消息里只留 URL。四、RAG 怎么注入才不搅浑这里只盯一件事检索到的文档怎么塞进 prompt——格式不对模型分不清「资料」和「问题」引用也乱。分隔符 引用格式--- 检索到的参考文档 --- [来源: doc-a.md] …… --- 参考文档结束 --- 用户问题……并明确要求答不出就说不知道引用时标[来源: xxx]。import{Document}fromlangchain/core/documents;import{ChatPromptTemplate}fromlangchain/core/prompts;import{ChatOllama}fromlangchain/ollama;import{StringOutputParser}fromlangchain/core/output_parsers;import{RunnableSequence}fromlangchain/core/runnables;/** 把检索文档格式化成带来源的 context */functionformatRagContext(docs:Document[]):string{returndocs.map((d,i)[来源:${d.metadata.source??doc-${i}}]\n${d.pageContent}).join(\n\n---\n\n);}constpromptChatPromptTemplate.fromTemplate(根据以下参考文档回答问题。若无法从文档得出答案请说「我不知道」。 回答时请用 [来源: xxx] 标注引用。 --- 检索到的参考文档 --- {context} --- 参考文档结束 --- 用户问题{question});constllmnewChatOllama({model:qwen2.5:7b,temperature:0});constragChainRunnableSequence.from([async(input:{question:string;docs:Document[]})({context:formatRagContext(input.docs),question:input.question,}),prompt,llm,newStringOutputParser(),]);// ragChain.invoke({ question: ..., docs: retrievedDocs })Top-K 与 chunk 也是预算旋钮太大建议起步k噪声淹没相关句35chunkSize单条占满窗口视文档类型FAQ 可 200300论述可更大多轮 RAG tool 结果三者叠加时先给历史 / RAG / tools 各自定预算再决定 trim 还是摘要——别等 API 报context length exceeded再救火。工具结果特别脏、特别长时还可以看官网的contextEditingMiddleware如ClearToolUsesEdit专门清旧 tool 调用块避免 ToolMessage 永久占地。本篇不展开知道有这号预置中间件即可。五、反模式速查表反模式后果缓解塞满 context「迷失」在无关信息里忽略关键句预算 trim / 摘要检索噪声淹没相关信息基于错误资料一本正经胡答降k、重排、分隔符、强制「无则不知」历史从不裁剪超限报错或被截断trimMessages/summarizationMiddlewareTool 结果永不清理ToolMessage 挤占 user 问题空间只留近几轮 tool或 context editingsystem prompt 过长规则/工具说明占满窗口精简 system动态工具子集官网 Tool Context把瞬时 trim 当永久清理Checkpointer 下轮又全量塞回长会话用 Persistent 策略常见坑只改 Prompt 不管理 context长对话 RAG 后必然爆窗。RAG 无分隔直接拼接模型分不清文档与问题。trim 丢掉 system角色与规则蒸发——设includeSystem: true或保证 system 始终在保留集里。裁断 AI↔Tool 成对消息部分供应商会直接拒收非法历史——用startOn/endOn保结构。k过大10 chunk 塞满窗口噪声赢相关。摘要当真理细节会丢关键事实该进 Store / 结构化记忆别全靠一段 summary。自写同名trimMessages和官方 API 撞车后人难维护——用官网的。

相关新闻

内容质量自动评分:从错别字到逻辑连贯性的多层次检测

内容质量自动评分:从错别字到逻辑连贯性的多层次检测

内容质量自动评分:从错别字到逻辑连贯性的多层次检测 一、个性化深度引言 一个内容平台每天产生数万篇文章,覆盖科技、财经、生活等多个领域。编辑团队只有 20 人,一审通过率不到 30%。我们尝试用规则引擎做初筛——错别字、敏感词、字数不足…

2026/7/22 13:58:19阅读更多 →
Amphenol ICC RJE1Y21A83C42401线束组件特点分析

Amphenol ICC RJE1Y21A83C42401线束组件特点分析

在现代通信设备、工业控制、服务器、网络设备以及智能硬件系统中,连接器与线束组件承担着信号传输、电源连接以及系统互联的重要作用。随着设备集成度不断提升,市场对于高可靠、小型化、高速率连接方案的需求持续增长。 Amphenol ICC(Commerc…

2026/7/22 13:58:19阅读更多 →
外包驻场开发如何做好需求沟通、任务管理和工作复盘

外包驻场开发如何做好需求沟通、任务管理和工作复盘

外包驻场开发如何做好需求沟通、任务管理和工作复盘 前言 作为一名外包驻场开发,平时经常会遇到这种情况: 甲方安排的任务比较零散;一会修改接口,一会查数据,一会处理配置;同时对接多个不同的人&#xff1b…

2026/7/22 13:56:18阅读更多 →
AI 音乐生成的 Prompt 工程:如何用自然语言精确描述音乐意图

AI 音乐生成的 Prompt 工程:如何用自然语言精确描述音乐意图

AI 音乐生成的 Prompt 工程:如何用自然语言精确描述音乐意图 一、你把"欢快的音乐"塞进生成模型,出来的却是 80 年代电子游戏配乐 AI 音乐生成最早令人失望的地方,不是音质不好,是你描述的和你得到的完全不搭。输入&quo…

2026/7/22 14:54:34阅读更多 →
小程序毕业设计-基于 SpringBoot + 微信小程序的博物馆线上预约平台的设计与实现 智慧博物馆参观预约票务管理小程序(源码+LW+部署文档+全bao+远程调试+代码讲解等)

小程序毕业设计-基于 SpringBoot + 微信小程序的博物馆线上预约平台的设计与实现 智慧博物馆参观预约票务管理小程序(源码+LW+部署文档+全bao+远程调试+代码讲解等)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/22 14:54:34阅读更多 →
小程序毕业设计-基于 SpringBoot + 微信小程序的线上预约订购服务平台的设计与实现 通用型线上预约与商品订购管理小程序(源码+LW+部署文档+全bao+远程调试+代码讲解等)

小程序毕业设计-基于 SpringBoot + 微信小程序的线上预约订购服务平台的设计与实现 通用型线上预约与商品订购管理小程序(源码+LW+部署文档+全bao+远程调试+代码讲解等)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/22 14:54:34阅读更多 →
前端性能 Budget 量化:FCP、LCP 与 TBT 的阈值设定方法论

前端性能 Budget 量化:FCP、LCP 与 TBT 的阈值设定方法论

前端性能 Budget 量化:FCP、LCP 与 TBT 的阈值设定方法论 一、老板说"页面太慢了",而你拿不出一个精确的数字来反驳 性能优化最怕的不是优化难,而是没有衡量标准。你说"慢了 200ms",老板说"200ms 是多慢…

2026/7/22 14:54:34阅读更多 →
一文搞懂Chatbot、Workflow、Agent,收藏这篇轻松入门大模型应用

一文搞懂Chatbot、Workflow、Agent,收藏这篇轻松入门大模型应用

本文深入浅出地解析了Chatbot、Workflow、Agent这三个在AI领域经常被提及但易混淆的概念。文章指出,这三者并非替代关系,而是适用于不同场景的解决方案。Chatbot擅长对话与问答,Workflow适合固定步骤的稳定执行,而Agent则能围绕目…

2026/7/22 14:54:34阅读更多 →
RocketMQ NameServer核心原理与源码解析

RocketMQ NameServer核心原理与源码解析

1. NameServer在RocketMQ架构中的核心作用NameServer是RocketMQ架构中至关重要的轻量级组件,它承担着整个消息队列集群的"交通指挥中心"角色。与ZooKeeper等重量级协调服务不同,NameServer采用去中心化设计,每个实例独立运行且节点…

2026/7/22 14:52:33阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 0:53:59阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 0:53:59阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 0:53:59阅读更多 →
中小企业小程序开发公司怎么选:预算、上手和售后避坑指南

中小企业小程序开发公司怎么选:预算、上手和售后避坑指南

中小企业做小程序,最常见的矛盾是预算有限,但又不希望功能太单薄;没有技术团队,但又希望后续能自己运营;想快速上线,又担心隐性收费和售后失联。选型时如果只看“低价套餐”或“案例数量”,很容…

2026/7/22 0:01:17阅读更多 →
GEO优化如何沉淀长期内容资产?广拓时代谈AI搜索时代的内容ROI

GEO优化如何沉淀长期内容资产?广拓时代谈AI搜索时代的内容ROI

企业做营销,最怕钱花完了,资产没有留下。 效果广告能带来一段时间的曝光,但预算停止后,流量往往也随之停止。短视频内容可能在几天内冲高,也可能很快沉下去。AI搜索时代,企业需要重新思考一个问题&#xff…

2026/7/22 0:01:17阅读更多 →
Agent 终态判定:何时该停止思考、给出最终回复

Agent 终态判定:何时该停止思考、给出最终回复

Agent 终态判定:何时该停止思考、给出最终回复 一、你的 Agent 在"再想想"的循环里绕了 12 轮,用户已经关窗口了 Agent 与人最大的区别是:人知道什么时候该停下来给答案,Agent 会一直"想"下去。你给 Agent 接…

2026/7/22 0:01:17阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/21 22:53:50阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/21 18:53:30阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/21 18:53:30阅读更多 →