LangGraph智能体设计模式与多智能体开发(人工智能技术丛书)【行情 报价 价格 评测】-京东
王晓华LangGraph开发入门书《LangGraph智能体设计模式与多智能体开发》全文试读~_langgraph智能体设计模式与多智能体开发 pdf 下载-CSDN博客
目录
[8.1 上下文工程中的提示词](#8.1 上下文工程中的提示词)
[8.1.1 提示词模板PromptTemplate](#8.1.1 提示词模板PromptTemplate)
[8.1.2 提示词设计规范](#8.1.2 提示词设计规范)
[8.1.3 带有人格描述提示词模板](#8.1.3 带有人格描述提示词模板)
[8.2 上下文污染与上下文卸载](#8.2 上下文污染与上下文卸载)
[8.2.1 上下文污染(Context Poisoning)](#8.2.1 上下文污染(Context Poisoning))
[8.2.2 上下文卸载(Context Offloading)](#8.2.2 上下文卸载(Context Offloading))
[8.3 本章小结](#8.3 本章小结)
智能体的核心竞争力,在于对复杂场景信息的精准捕捉与高效运用,而上下文工程(Context Engineering)正是实现这一能力的核心范式。不同于单一工具调用或固定流程执行,上下文工程聚焦于智能体"认知基础"的构建------它并非简单堆砌信息,而是通过系统化设计,让智能体在交互中始终持有"必要且精准"的上下文,为决策与行动提供可靠依据。无论是用户的历史需求、任务的执行进度,还是环境的动态变化,都需经上下文工程梳理、筛选与组织,成为智能体可调用的"即时记忆"。上下文工程示意如图8-1所示。

图8-1 上下文工程
脱离优质的上下文工程,再先进的智能体也会陷入"决策失据"的困境:可能重复询问用户已提供的信息,可能忽略任务执行中的关键约束,还有可能因信息碎片化而偏离核心目标。例如,在上一章的孩子兴趣成长规划智能体中,上下文不仅包含用户输入的"10 岁、爱好动漫" 等基础需求,还需实时承载成长状态空间、已执行步骤结果等动态信息------这些经工程化设计的上下文,让智能体每一次规划、执行与调整都能衔接前文、呼应目标。
可以说,上下文工程是智能体的"认知中枢",它搭建起信息与决策之间的桥梁,让智能体的行为从"机械响应"升级为"连贯且精准的智能交互",是所有复杂智能体设计中不可或缺的底层逻辑。
8.1 上下文工程中的提示词
作为上下文工程的具象化核心载体,提示词在上下文工程中占据着不可替代的核心定位------它早已超越传统单点交互中"简单指令"的范畴,进化为适配图结构多节点协作的"逻辑粘合剂"与"状态调度器"。这种角色升级并非偶然,而是复杂智能体对 "信息连贯传递"与"多角色协同"的核心诉求催生的必然结果,而这一诉求在LangGraph 框架中得到了极致的场景化体现。
8.1.1 提示词模板PromptTemplate
LangGraph 以节点(智能体/工具)、边(流转规则)和全局共享状态为三大核心组件,其本质是一个动态流转的协作系统,这就决定了其提示词设计必须突破单轮对话的局限------既要精准承载具体任务的执行指令,更要承接"逻辑粘合剂"与"状态调度器"的核心职责,解决多节点间的信息高效传递、协作分工明确化与动态决策适配的关键问题,最终成为串联起整个工作流的"隐形中枢"。不同于单点交互中提示词仅需对接单一角色,LangGraph的提示词需要深度嵌入图结构的流转逻辑,既要让每个节点能从全局共享状态中精准提取所需上下文,又要确保节点执行结果能以标准化格式回流至全局状态,为下一轮协作提供可靠支撑,从而实现"信息输入−节点执行−状态更新−下一个节点联动"的连贯闭环。
- 多角色消息列表模板
ChatPromptTemplate是LangGraph中对提示词进行规整、适配图结构协作特性的具体工具,它解决了传统硬编码提示词"格式混乱、参数耦合、上下文传递不灵活"的问题,通过结构化、参数化的方式将提示词与LangGraph的全局共享状态深度绑定,让每个节点的提示词既能精准调用全局状态中的关键信息,又能保持指令逻辑的清晰性。
相较于直接编写字符串形式的提示词,ChatPromptTemplate 支持将全局状态中的变量(如成长规划场景中的current_state、target_states、past_steps 等)以占位符形式嵌入提示词模板,当节点执行时,模板会自动从全局状态中提取对应数据并填充,确保提示词始终能获取最新、最精准的上下文,同时也让提示词的维护与迭代更高效。
from langchain_core.prompts import ChatPromptTemplate
构建执行节点的提示词模板,嵌入全局状态中的核心变量
executor_prompt = ChatPromptTemplate.from_messages([
("system", "你是成长规划执行助手,需基于成长状态空间执行指定规划步骤:\n1. 确认步骤关联的状态转化条件,判断执行是否推动状态从当前向目标转化\n2. 若需搜索,调用search_web4query工具,搜索关键词需贴合孩子年龄\n3. 执行结果需具体、详实,贴合孩子成长场景"),
("system", "当前成长状态空间:\n- 当前状态:{current_state}\n- 目标状态:{target_states}\n- 转化条件:{transition_conditions}"),
("system", "当前需执行的规划步骤:\n- 整体规划:{plan_str}\n- 待执行步骤(第{step_num}步):{current_step}\n- 步骤关联状态:{related_state}"),
("human", "请执行该步骤,并返回执行结果(直接文本描述)")
])
从全局状态中提取变量,填充模板(模拟LangGraph节点执行时的变量提取逻辑)
global_state = {
"current_state": {"绘画技能": "基础涂鸦阶段", "年龄": "10岁(四年级)"},
"target_states": {"阶段": "短期(1年内)", "绘画技能": "掌握基础线稿"},
"transition_conditions": "完成基础线稿兴趣班学习",
"plan_str": "1. 量化当前绘画状态\n2. 搜索适配的线稿入门课程",
"step_num": 2,
"current_step": "调用TavilySearch搜索:10岁孩子动漫线稿入门课程",
"related_state": "完成基础线稿兴趣班学习"
}
填充模板,生成可直接调用的提示词
filled_prompt = executor_prompt.format_messages(**global_state)
print(filled_prompt)
上述代码中,ChatPromptTemplate 以"系统消息 + 人类消息"的结构化形式定义提示词逻辑,同时通过{current_state}、{step_num} 等占位符关联全局状态变量。当 LangGraph的执行节点触发时,模板会自动从全局共享状态中提取对应变量值完成填充,无需手动拼接字符串,既避免了变量遗漏或格式错误,又保证了提示词能精准承接全局状态的最新信息。这种设计让提示词真正成为"逻辑粘合剂"------既能清晰传达执行指令,又能高效联动全局状态,支撑多节点间的动态协作;同时也体现了 "状态调度器"的核心价值,让上下文信息在节点流转中有序传递、精准复用,最终保障 LangGraph 工作流从"静态逻辑设计"到"动态高效执行"的完整落地。
- 预填充的字符串类型模板
除了多角色结构化的ChatPromptTemplate外,在智能体开发场景中,还可选用PromptTemplate作为轻量化的提示词模板方案。该模板专门用于适配纯字符串类型的提示词,仅需调整模板格式与变量填充方式即可快速落地:
from langchain_core.prompts import PromptTemplate
构建执行节点的提示词模板(单一字符串模板,适配PromptTemplate)
合并system/human消息为统一字符串,保留所有变量占位符
executor_template = """
你是成长规划执行助手,需基于成长状态空间执行指定规划步骤:
-
确认步骤关联的状态转化条件,判断执行是否推动状态从当前向目标转化
-
若需搜索,调用search_web4query工具,搜索关键词需贴合孩子年龄
-
执行结果需具体、详实,贴合孩子成长场景
当前成长状态空间:
-
当前状态:{current_state}
-
目标状态:{target_states}
-
转化条件:{transition_conditions}
当前需执行的规划步骤:
-
整体规划:{plan_str}
-
待执行步骤(第{step_num}步):{current_step}
-
步骤关联状态:{related_state}
请执行该步骤,并返回执行结果(直接文本描述)
"""
初始化PromptTemplate(指定模板和输入变量,可选但建议显式声明)
executor_prompt = PromptTemplate(
template=executor_template,
input_variables=["current_state", "target_states", "transition_conditions",
"plan_str", "step_num", "current_step", "related_state"]
)
从全局状态中提取变量(模拟LangGraph节点执行时的变量提取逻辑)
global_state = {
"current_state": {"绘画技能": "基础涂鸦阶段", "年龄": "10岁(四年级)"},
"target_states": {"阶段": "短期(1年内)", "绘画技能": "掌握基础线稿"},
"transition_conditions": "完成基础线稿兴趣班学习",
"plan_str": "1. 量化当前绘画状态\n2. 搜索适配的线稿入门课程",
"step_num": 2,
"current_step": "调用TavilySearch搜索:10岁孩子动漫线稿入门课程",
"related_state": "完成基础线稿兴趣班学习"
}
填充模板(PromptTemplate使用format方法,而非format_messages)
filled_prompt = executor_prompt.format(**global_state)
打印填充后的完整提示词
print("填充后的提示词:")
print("=" * 80)
print(filled_prompt)
可以看到,PromptTemplate要求将原本分散的系统消息、人类消息合并为单一字符串模板,同时保留所有业务相关的变量占位符。初始化时显式声明 input_variables 虽非强制要求,但能让模板的变量依赖关系更清晰,便于后续维护和问题排查。变量填充阶段需改用 format 方法(而非 ChatPromptTemplate的format_messages 方法),最终生成完整的纯字符串提示词,适配仅需文本输入的大模型交互场景。
- 简化的字符串类型模板
PromptTemplate 还支持更轻量化的初始化方式,通过from_template 方法可自动解析模板中的占位符并生成输入变量列表,省略手动声明 input_variables的步骤,进一步降低模板构建的开发成本:
from langchain_core.prompts import PromptTemplate
构建执行节点的提示词模板(单一字符串模板,适配PromptTemplate)
合并system/human消息为统一字符串,保留所有变量占位符
executor_template = """
你是成长规划执行助手,需基于成长状态空间执行指定规划步骤:
-
确认步骤关联的状态转化条件,判断执行是否推动状态从当前向目标转化
-
若需搜索,调用search_web4query工具,搜索关键词需贴合孩子年龄
-
执行结果需具体、详实,贴合孩子成长场景
当前成长状态空间:
-
当前状态:{current_state}
-
目标状态:{target_states}
-
转化条件:{transition_conditions}
当前需执行的规划步骤:
-
整体规划:{plan_str}
-
待执行步骤(第{step_num}步):{current_step}
-
步骤关联状态:{related_state}
请执行该步骤,并返回执行结果(直接文本描述)
"""
初始化PromptTemplate(指定模板和输入变量,可选但建议显式声明)
executor_prompt = PromptTemplate.from_template(executor_template)
从全局状态中提取变量(模拟LangGraph节点执行时的变量提取逻辑)
global_state = {
"current_state": {"绘画技能": "基础涂鸦阶段", "年龄": "10岁(四年级)"},
"target_states": {"阶段": "短期(1年内)", "绘画技能": "掌握基础线稿"},
"transition_conditions": "完成基础线稿兴趣班学习",
"plan_str": "1. 量化当前绘画状态\n2. 搜索适配的线稿入门课程",
"step_num": 2,
"current_step": "调用TavilySearch搜索:10岁孩子动漫线稿入门课程",
"related_state": "完成基础线稿兴趣班学习"
}
填充模板(PromptTemplate使用format方法,而非format_messages)
filled_prompt = executor_prompt.format(**global_state)
打印填充后的完整提示词
print("填充后的提示词:")
print("=" * 80)
print(filled_prompt)
这种简化方式通过内置的模板解析逻辑,自动识别 {current_state}、{step_num} 等占位符并将其作为input_variables,无需开发者手动罗列变量,在保持功能完整性的前提下精简了代码量。变量填充阶段仍沿用format方法,与常规初始化的PromptTemplate保持一致,确保开发体验的连贯性,适合快速构建轻量级提示词模板的场景。
此外,LangGraph的提示词还需具备"协作边界清晰化"的设计逻辑:通过明确每个节点的核心职责(如规划节点负责拆解步骤、执行节点负责落地动作、总结节点负责整合输出),避免多节点协作中的职责重叠或信息遗漏,同时通过标准化的信息格式(如状态空间的结构化描述、步骤执行结果的统一呈现),降低节点间的信息解析成本。这种设计让提示词不再是孤立的指令,而是与全局状态、节点职责、流转规则深度绑定的"协作协议",既保证了每个节点的独立和高效执行,又实现了整个工作流的连贯协同,最终让 LangGraph的动态协作能力得以充分释放,成为支撑复杂智能体落地的核心技术支柱。
8.1.2 提示词设计规范
LangGraph中提示词的设计规范,并非通用提示词规则的简单复用,而是深度适配图结构多节点协作特性的"协作型规范"。其核心目标是确保提示词既能精准承接全局共享状态,又能明确节点职责边界,还能为后续节点的信息复用提供清晰接口------最终让分散的节点通过提示词实现"目标一致、信息互通、分工明确"的协同运转。

图8-2 提示词设计规范
这些规范围绕"状态关联、职责界定、信息流转"三大核心维度构建,覆盖从提示词结构设计到输出约束的全流程。以下结合 LangGraph的典型场景展开说明,并配合示例验证规范的落地性。
- 核心规范一:状态关联标准化------锚定全局状态,避免信息断层
LangGraph的每个节点都依赖全局共享状态(如 PlanExecute类承载的current_state、past_steps 等)开展工作,提示词必须通过标准化方式与状态变量绑定,确保节点执行时能"按需提取、精准填充" 上下文,避免因信息缺失导致决策偏差。规范要求为:所有与状态相关的信息需以"明确占位符"嵌入提示词模板,占位符命名需与全局状态的字段名一致,同时在提示词中注明变量含义,降低模型解析成本。
以孩子兴趣成长规划智能体的"规划节点"为例,提示词需关联用户输入、状态空间抽象要求等核心变量,代码示例如下:
from langchain_core.prompts import PromptTemplate
规划节点提示词模板:严格绑定全局状态中的input字段,明确状态抽象要求
planner_template = """
你是专业孩子兴趣成长规划师,核心职责:
-
抽象成长状态空间(当前状态+分阶段目标+转化条件)
-
拆解可落地步骤,每步关联转化条件
-
步骤需标注是否调用搜索,搜索关键词需精准
用户需求:{input}
输出要求:仅返回符合Plan模型的JSON格式,无额外文本
"""
planner_prompt = PromptTemplate.from_template(planner_template)
模拟LangGraph节点执行:从全局状态提取input变量填充模板
global_state = {
"input": "10岁四年级孩子爱好动漫绘画,制定小学到高中的分阶段成长方案,含目标、资源、职业准备"
}
填充后的提示词自动承接全局状态信息,确保规划逻辑锚定用户真实需求
filled_prompt = planner_prompt.format(**global_state)
打印填充后的完整提示词
print("填充后的规划节点提示词:")
print("=" * 80)
print(filled_prompt)
该示例中,提示词通过{input}占位符与全局状态的input 字段强关联,既避免了硬编码导致的变量错位,又让模型明确知晓需基于用户输入开展状态抽象,确保规划结果与需求一致。同时采用简化的format(**global_state)方式完成变量填充,直接生成可调用的完整提示词字符串,适配 LangGraph节点对文本输入的核心需求。
- 核心规范二:节点职责边界化------明确角色定位,杜绝协作混乱
LangGraph中各节点(规划、执行、重规划等)承担差异化职责,提示词需通过"角色定义 + 任务范围约束" 明确节点边界,防止出现"规划节点越权执行步骤""执行节点擅自调整目标" 等问题。规范要求:提示词开头需直接界定节点角色及核心任务,同时通过"禁止性描述"划定职责红线,确保节点行为不偏离预设逻辑。
以"执行节点"与"重规划节点"的提示词对比为例,二者职责边界通过规范设计清晰区分:
from langchain_core.prompts import PromptTemplate
执行节点提示词:明确"仅落地步骤,不调整规划"的职责
executor_template = """
角色:成长规划执行助手
核心任务:仅执行指定步骤,不修改规划目标
职责红线:1.不新增/删除步骤 2.不调整状态转化条件 3. 需调用工具时仅用search_web4query
状态信息:
-
当前状态:{current_state}
-
待执行步骤:{current_step}
-
关联条件:{related_state}
请执行该步骤,返回具体结果(无需格式封装)
"""
executor_prompt = PromptTemplate.from_template(executor_template)
重规划节点提示词:明确"仅调整步骤,不执行任务"的职责
replan_template = """
角色:成长规划重规划师
核心任务:基于执行结果调整剩余步骤,不落地具体动作
职责红线:1.不执行搜索/操作类任务 2. 调整后步骤需关联原状态转化条件
状态信息:
-
已执行步骤:{past_steps_str}
-
剩余转化条件:{unfinished_conditions}
请输出调整后的Plan模型JSON,仅保留未执行步骤
"""
replan_prompt = PromptTemplate.from_template(replan_template)
模拟执行节点变量填充
executor_global_state = {
"current_state": {"绘画技能": "基础涂鸦阶段", "年龄": "10岁(四年级)"},
"current_step": "调用TavilySearch搜索:10岁孩子动漫线稿入门课程",
"related_state": "完成基础线稿兴趣班学习"
}
executor_filled_prompt = executor_prompt.format(**executor_global_state)
打印执行节点填充后的提示词
print("\n填充后的执行节点提示词:")
print("=" * 80)
print(executor_filled_prompt)
两个节点的提示词均通过"角色 + 核心任务 + 职责红线"的结构明确边界,执行节点专注"做什么",重规划节点专注"改什么",避免多节点协作时的职责重叠与目标偏离。同时统一采用format(**global_state)的简化填充方式,确保不同节点的提示词生成逻辑一致,降低多节点协作的开发维护成本------这也是LangGraph 工作流连贯运转的核心保障。
- 核心规范三:协作信息显性化------衔接节点流转,降低解析成本
LangGraph的节点通过"边"实现流转,前一节点的输出会成为后一节点的输入,提示词需将这种"流转依据"显性化呈现,让后一节点清晰知晓"前序成果、当前任务、关联逻辑"。规范要求:提示词中需单独划分"协作上下文"模块,明确标注前序节点输出、当前节点任务与前序成果的关联关系,尤其在条件分支节点(如 replan_node)中,需突出"判断依据"的呈现。
以"重规划节点"为例,其提示词需显性化已执行步骤结果与剩余任务的关联,代码示例如下:
from langchain_core.prompts import PromptTemplate
重规划节点提示词模板:显性化协作上下文与流转逻辑
replan_template = """
角色:成长规划重规划师
任务:基于前序执行结果,调整剩余规划步骤
【协作上下文】
-
前序执行节点成果:{executor_result}
-
未满足的转化条件:{unfinished_conditions}
-
关联逻辑:若前序步骤已满足某条件,删除对应待执行步骤
【原始规划框架】
-
初始步骤:{original_plan}
-
成长状态空间:{growth_state}
请输出调整后的Plan模型JSON,确保步骤与剩余转化条件一一对应
"""
replan_prompt = PromptTemplate.from_template(replan_template)
模拟流转时的变量填充:前序执行节点的结果被显性传入
global_state = {
"executor_result": "【步骤2执行结果】已搜索到3门适配10岁孩子的线稿课程,分别是...",
"unfinished_conditions": "完成基础线稿兴趣班学习", "通过市级动漫比赛初选",
"original_plan": "1. 量化当前状态 2. 搜索线稿课程 3. 筛选适配课程 4. 报名学习",
"growth_state": '{"current_state":{"绘画技能":"基础涂鸦"},"target_states":"掌握基础线稿"}'
}
填充模板,生成完整提示词
filled_prompt = replan_prompt.format(**global_state)
打印填充后的完整提示词
print("填充后的重规划节点提示词:")
print("=" * 80)
print(filled_prompt)
该示例中,"协作上下文"模块将前序执行节点的成果与当前重规划任务的关联逻辑明确呈现,让模型无需"猜测"流转依据,直接基于显性信息完成步骤调整。通过format(**global_state)的简化填充方式,前序节点的输出结果能精准注入重规划节点的提示词中,大幅降低了多节点间的信息传递成本,提升了协作效率。
- 核心规范四:输出格式结构化------适配状态回流,确保可复用性
LangGraph中节点的输出需回流至全局状态,为下一轮协作提供支撑,因此提示词必须明确要求 "结构化输出",避免因输出格式混乱导致状态解析失败。规范要求:根据节点功能指定输出格式(如 JSON、Markdown 表格等),并明确格式约束(如字段命名、层级结构),对于工具调用类节点,还需规范工具调用参数的格式。
以"规划节点"为例,其输出需符合Plan模型结构,提示词需强制指定JSON格式及核心字段,代码示例如下:
from langchain_core.prompts import PromptTemplate
规划节点提示词模板:强制指定结构化输出格式
planner_template = """
角色:成长规划师
输出格式约束:
-
必须返回JSON,无任何额外文本
-
JSON需包含两个核心字段:steps(步骤列表)、growth_state(成长状态空间)
-
steps字段中每个步骤需有step(序号)、action(动作)、related_state(关联条件)
-
growth_state字段需包含current_state、target_states、transition_conditions
用户需求:{input}
"""
planner_prompt = PromptTemplate.from_template(planner_template)
模拟全局状态变量填充
global_state = {
"input": "10岁四年级孩子爱好动漫绘画,制定小学到高中的分阶段成长方案,含目标、资源、职业准备"
}
填充模板,生成完整提示词
filled_prompt = planner_prompt.format(**global_state)
打印填充后的完整提示词
print("填充后的规划节点提示词(结构化输出约束):")
print("=" * 80)
print(filled_prompt)
结构化的输出要求让节点成果能直接被 LangGraph的类型解析器(如 with_structured_output)转化为指定对象,无需额外格式处理。而简化的format(**global_state)填充方式,确保提示词本身的生成过程简洁高效,既满足输出格式的强约束,又保障全局状态的更新高效且无误差------这是实现"节点执行−状态更新−下一节点联动" 闭环的关键技术保障。
8.1.3 带有人格描述提示词模板
LangGraph中的提示词设计规范,本质是"为多节点协作而生的协同协议"。从状态关联的标准化到输出格式的结构化,每一条规范都围绕"降低协作成本、提升流转效率"展开,而统一采用format(**global_state)的简化填充方式,进一步强化了不同节点提示词生成逻辑的一致性,让提示词真正承担起"逻辑粘合剂"与"状态调度器"的角色。
这些规范的落地,需结合具体节点的功能、全局状态的结构及流转规则灵活调整,但其核心逻辑 ------"以协作目标为核心,以状态流转为纽带"------是所有 LangGraph 提示词设计的共通准则。
MBTI(Myers-Briggs Type Indicator,迈尔斯布里格斯类型指标)是人类心理类型理论发展而来的人格分类工具,其核心是通过注意力方向、认知方式、决策方式、生活方式四个维度的组合,将人格划分为16 种典型类型,是目前应用最广泛的人格分析工具之一。如图8-3所示。

图8-3 MBTI人格特征分类
为深度验证智能体的人格化表现与行为一致性,本文基于 ATOM(Agent Role-Task-Toolkit-Operating logic-Mission constraints)方法论设计了一套融合 MBTI人格特征的智能体提示词框架(ATOMPrompt类)。该框架不仅遵循通用提示词设计规范,还创新性地将 MBTI人格描述融入智能体角色定义,并补充了工具载入规则、行为约束条件、结构化输出控制等扩展能力,实现了智能体"人格化角色 + 标准化任务 + 工具化执行 + 约束化行为"的全维度管控。
为了进一步了解和验证智能体,作者实现了一套具有MBTI人格描述提示词,并且准许设计规范,在其上添加了限制条件和载入工具的识别,完整代码如下所示:
class ATOMPrompt:
def init(self):
ATOM四要素初始化
self.prompt_name: str = "atom_prompt_template"
self.prompt_version: float = 1.0
self.prompt_description: str = "基于ATOM方法论的Agent System Prompt设计模板"
ATOM核心模块
self.agent_role = \[\] # A - 角色定位(谁做)
self.task_description = \[\] # T - 任务说明(做什么)
self.toolkit_definition = \[\] # T - 工具箱定义(用什么做)
self.operating_logic = \[\] # O - 行动逻辑(怎么做)
self.mission_constraints = \[\] # M - 任务约束(不能怎么做)
补充信息(可选)
self.context = \[\] # 背景上下文(补充信息)
self._tool_note_marker = "可使用以下工具" # 工具说明标记
新增:是否启用了结构化输出(用于控制JSON包装)
self._requires_json_output = False
------------------------------ 基础链式添加方法 ------------------------------
def step1_add_context(self, context: str = ""):
"""添加背景上下文(补充信息)"""
if not context:
raise ValueError("context是必填参数,不能为空")
self.context.append(context)
return self
def step2_add_agent_role(self, identity: str, responsibilities: OptionalUnion\[str, List\[str]] = None,
communication_style: Optionalstr = None,
professional_traits: OptionalUnion\[str, List\[str]] = None,
mbti_type: Optionalstr = "ISTJ"):
"""添加角色定位(A - Agent Role),支持MBTI人格类型"""
if not identity:
raise ValueError("identity是必填参数,不能为空")
mbti_info = self._get_mbti_personality(mbti_type)
role_section = f"身份:{identity}"
role_section += f"\nMBTI人格类型:{mbti_type}({mbti_info'type_name'})- {mbti_info'气质类型'}"
role_section += f"\n人格特征:{mbti_info'核心特征'}"
if communication_style is None:
communication_style = mbti_info'沟通风格'
if professional_traits is None:
professional_traits = mbti_info'专业特质'
elif isinstance(professional_traits, list):
professional_traits = professional_traits + mbti_info'专业特质'
if responsibilities:
role_section += f"\n职责:{';'.join(responsibilities)}" if isinstance(responsibilities,
list) else f"\n职责:{responsibilities}"
if communication_style:
role_section += f"\n沟通风格:{communication_style}"
if professional_traits:
role_section += f"\n专业特质:{';'.join(professional_traits)}" if isinstance(professional_traits,
list) else f"\n专业特质:{professional_traits}"
self.agent_role.append(role_section)
self._update_role_with_tools()
return self
def step3_add_task_description(self, task_objective: str,
scope: OptionalUnion\[str, List\[str]] = None,
expected_outcome: OptionalUnion\[str, List\[str]] = None,
time_requirement: Optionalstr = None,
dependencies: OptionalUnion\[str, List\[str]] = None):
"""添加任务说明(T - Task Description)"""
if not task_objective:
raise ValueError("task_objective是必填参数,不能为空")
task_section = f"核心目标:{task_objective}"
if scope:
task_section += f"\n任务范围:{';'.join(scope)}" if isinstance(scope, list) else f"\n任务范围:{scope}"
if expected_outcome:
task_section += f"\n预期成果:{';'.join(expected_outcome)}" if isinstance(expected_outcome,
list) else f"\n预期成果:{expected_outcome}"
if time_requirement:
task_section += f"\n时间要求:{time_requirement}"
if dependencies:
task_section += f"\n依赖资源:{';'.join(dependencies)}" if isinstance(dependencies,
list) else f"\n依赖资源:{dependencies}"
self.task_description.append(task_section)
return self
def step4_add_operating_logic(self, description: str, logic_type: Optionalstr = "任务逻辑",
example: Optionalstr = None):
"""添加行动逻辑(O - Operating Logic)"""
logic_section = f"【{logic_type}】{description}" + (f"\n示例:{example}" if example else "")
self.operating_logic.append(logic_section)
return self
def step5_add_tools(self, tools: ListUnion\[BaseTool, Callable, str],
trigger_scenarios: Optionaldict = None,
limitations: Optionaldict = None):
"""批量添加工具"""
if trigger_scenarios is None:
trigger_scenarios = {}
if limitations is None:
limitations = {}
for tool in tools:
tool_name = None
if hasattr(tool, 'name'):
tool_name = tool.name
elif callable(tool):
tool_name = tool.name
elif isinstance(tool, str):
tool_name = tool
self.__add_toolkit_definition(
tool=tool,
tool_name=tool_name,
trigger_scenario=trigger_scenarios.get(tool_name, f"当需要调用{tool_name}功能时"),
limitations=limitations.get(tool_name)
)
self._update_role_with_tools()
return self
def step6_add_mission_constraints(self, description: str, constraint_type: str = "mission constraints"):
"""添加任务约束(M - Mission Constraints)"""
if not constraint_type or not description:
raise ValueError("constraint_type和description是必填参数,不能为空")
self.mission_constraints.append(f"【{constraint_type}】{description}")
return self
------------------------------ 自动解析Pydantic结构(兼容V2) ------------------------------
def step7_add_struct_definition(
self,
struct_classes: ListType\[BaseModel],
purpose: Optionalstr = "LLM输出必须严格匹配此结构,禁止添加额外字段或修改字段名"
) -> "ATOMPrompt":
"""自动解析Pydantic结构并标记需要JSON输出"""
for cls in struct_classes:
if not issubclass(cls, BaseModel):
raise ValueError(f"传入的{cls.name}不是Pydantic BaseModel子类")
解析逻辑不变...
def _parse_model(cls: TypeBaseModel) -> str:
class_name = cls.name
class_doc = cls.doc.strip() if cls.doc else "无详细描述"
field_info_list = \[\]
for field_name, field in cls.model_fields.items():
field_type = self._get_field_type_from_fieldinfo(field)
field_desc = field.description.strip() if field.description else "无描述"
required = self._is_field_required(field)
field_info = f"- {field_name}(类型:{field_type},{required}):{field_desc}"
field_info_list.append(field_info)
fields_str = "\n ".join(field_info_list)
return f"{class_name}(用途:{class_doc}):\n 字段定义:\n {fields_str}".strip()
struct_desc_list = \[\]
processed_classes = \[\]
queue = list(struct_classes)
while queue:
cls = queue.pop(0)
if cls in processed_classes:
continue
parsed_model = _parse_model(cls)
struct_desc_list.append(parsed_model)
processed_classes.append(cls)
for field_name, field in cls.model_fields.items():
nested_cls = self._get_nested_model_from_fieldinfo(field)
if nested_cls and nested_cls not in processed_classes and nested_cls not in queue:
queue.append(nested_cls)
unique_struct_desc = list(dict.fromkeys(struct_desc_list))
struct_desc = "\n\n".join(unique_struct_desc)
constraint_desc = f"{purpose}。具体结构定义如下:\n{struct_desc}\n\n输出要求:1. 严格匹配JSON结构;2. 禁止额外字段;3. 正确包含嵌套字段。"
self.step6_add_mission_constraints(
constraint_type="输出结构约束",
description=constraint_desc
)
✅设置标志:启用结构化输出 → 要求JSON包装 FINAL_ANSWER
self._requires_json_output = True
return self
def _get_field_type_from_fieldinfo(self, field: FieldInfo) -> str:
"""从FieldInfo对象获取字段类型(兼容Pydantic v2)"""
优先使用annotation属性(Pydantic v2)
if hasattr(field, 'annotation') and field.annotation is not None:
return self._get_field_type_str(field.annotation)
备选方案:尝试从其他属性获取类型信息
if hasattr(field, 'type_') and field.type_ is not None:
return self.get_field_type_str(field.type)
最后尝试从metadata获取
if hasattr(field, 'metadata') and field.metadata:
for meta in field.metadata:
if hasattr(meta, 'type'):
return str(meta.type)
return "any"
def _is_field_required(self, field: FieldInfo) -> str:
"""判断字段是否必需(兼容Pydantic v2)"""
Pydantic v2 使用 is_required 方法
if hasattr(field, 'is_required') and callable(field.is_required):
return "必填" if field.is_required() else "可选"
Pydantic v1 或备选方案
if hasattr(field, 'required'):
return "必填" if field.required else "可选"
默认处理
return "必填"
def _get_nested_model_from_fieldinfo(self, field: FieldInfo) -> OptionalType\[BaseModel]:
"""从FieldInfo对象提取嵌套的自定义Model(兼容Pydantic v2)"""
优先使用annotation属性(Pydantic v2)
if hasattr(field, 'annotation') and field.annotation is not None:
return self._get_nested_model(field.annotation)
备选方案
if hasattr(field, 'type_') and field.type_ is not None:
return self.get_nested_model(field.type)
return None
------------------------------ 辅助工具方法 ------------------------------
def _update_role_with_tools(self):
"""更新角色描述中的工具权限"""
if not self.toolkit_definition or not self.agent_role:
return
tool_names = \[\]
tool_name_pattern = re.compile(r"工具:(.+?)(\n|$)")
for tool_section in self.toolkit_definition:
match = tool_name_pattern.search(tool_section)
if match:
tool_names.append(match.group(1))
if tool_names:
tool_note = f"\n工具权限:{self._tool_note_marker}【{', '.join(tool_names)}】,具体功能参考「工具箱定义」。"
updated_roles = [role + tool_note if self._tool_note_marker not in role else role for role in
self.agent_role]
self.agent_role = updated_roles
def __add_toolkit_definition(self, tool: UnionBaseTool, Callable, str = None,
tool_name: Optionalstr = None,
functionality: Optionalstr = None,
trigger_scenario: Optionalstr = None,
limitations: Optionalstr = None,
special_notes: Optionalstr = None):
"""添加单个工具定义"""
if tool is not None:
tool_info = self._extract_tool_info(tool)
tool_name = tool_name or tool_info'name'
functionality = functionality or tool_info'functionality'
if not trigger_scenario:
trigger_scenario = f"当需要{functionality}时使用此工具"
if not tool_name or not functionality or not trigger_scenario:
raise ValueError("tool_name、functionality和trigger_scenario为必填参数")
tool_section = f"工具:{tool_name}\n功能:{functionality}\n触发场景:{trigger_scenario}"
if limitations:
tool_section += f"\n限制:{limitations}"
if special_notes:
tool_section += f"\n特殊说明:{special_notes}"
self.toolkit_definition.append(tool_section)
return self
def _extract_tool_info(self, tool: UnionBaseTool, Callable, str) -> dict:
"""提取工具信息"""
tool_info = {'name': '', 'functionality': '', 'input_format': ''}
if isinstance(tool, BaseTool):
tool_info'name' = tool.name
tool_info'functionality' = tool.description
if hasattr(tool, 'args_schema') and tool.args_schema:
try:
schema = tool.args_schema.model_json_schema()
if 'properties' in schema:
params = [f"{k}: {v.get('type', 'any')} ({v.get('description', '')})"
for k, v in schema'properties'.items()]
tool_info'input_format' = "; ".join(params)
except Exception:
tool_info'input_format' = self._extract_params_from_func(tool)
else:
tool_info'input_format' = self._extract_params_from_func(tool)
elif callable(tool):
tool_info'name' = tool.name
tool_info'functionality' = self._extract_functionality_from_docstring(tool.doc)
tool_info'input_format' = self._extract_params_from_func(tool)
elif isinstance(tool, str):
tool_info'name' = tool
tool_info'functionality' = "功能描述待补充"
tool_info'input_format' = "参数格式待指定"
return tool_info
def _extract_params_from_func(self, func: UnionBaseTool, Callable) -> str:
"""提取函数参数信息"""
try:
actual_func = func.func if isinstance(func, BaseTool) and hasattr(func, 'func') and callable(
func.func) else func
sig = inspect.signature(actual_func)
params = \[\]
for param_name, param in sig.parameters.items():
if param_name not in 'self', 'cls':
param_type = self._get_type_name(param.annotation)
param_default = f" = {param.default}" if param.default != inspect.Parameter.empty else ""
params.append(f"{param_name}: {param_type}{param_default}")
return "; ".join(params) if params else "无参数"
except Exception:
return "参数提取失败"
def _extract_functionality_from_docstring(self, docstring: Optionalstr) -> str:
"""从文档字符串提取功能描述"""
if not docstring:
return "无功能描述"
lines = docstring.strip().split('\n')
functionality_lines = [line.strip() for line in lines if line.strip()
and not line.strip().startswith(('参数:', '返回值:', 'Args:', 'Returns:'))]
return ' '.join(functionality_lines).strip() or "无功能描述"
def _get_type_name(self, type_annotation) -> str:
"""获取类型注解名称"""
if type_annotation == inspect.Parameter.empty:
return "any"
elif hasattr(type_annotation, 'name'):
return type_annotation.name
elif hasattr(type_annotation, '_name'):
return type_annotation._name
else:
return str(type_annotation)
def _get_mbti_personality(self, mbti_type: str = "ISTJ") -> dict:
"""获取MBTI人格描述"""
mbti_descriptions = {
"ISTJ": {"type_name": "物流师", "气质类型": "守护者(SJ)", "核心特征": "严谨务实,注重细节和规则,责任感强",
"沟通风格": "直接、有条理、基于事实", "专业特质": "高度责任感", "组织能力强", "注重细节"},
"ISFJ": {"type_name": "守护者", "气质类型": "守护者(SJ)", "核心特征": "细心体贴,忠诚可靠,注重和谐",
"沟通风格": "温和、体贴、注重他人感受", "专业特质": "有同情心", "责任心强", "团队合作"},
"INTJ": {"type_name": "建筑师", "气质类型": "理性者(NT)", "核心特征": "战略思维,独立自主",
"沟通风格": "逻辑严谨、直接了当", "专业特质": "战略思维", "独立性", "创新意识"}
}
mbti_type = mbti_type.upper()
if mbti_type not in mbti_descriptions:
raise ValueError(f"无效的MBTI类型: {mbti_type}。支持的类型: {', '.join(mbti_descriptions.keys())}")
return mbti_descriptionsmbti_type
------------------------------ 生成PromptTemplate ------------------------------
def _build_template(self):
"""构建ATOM结构化System Prompt"""
template_parts = [
"# SYSTEM PROMPT - ATOM方法论构建",
"<!-- 基于方法论的Agent系统提示词:角色→任务→工具→逻辑→约束 -->",
""
]
if self.agent_role:
template_parts.extend([
"## 🎭 AGENT ROLE (角色定位)",
"<!-- 定义Agent的身份、职责、沟通风格、专业特质及可用工具-->"
])
for idx, role in enumerate(self.agent_role, 1):
if '\n' in role:
role_lines = role.split('\n')
template_parts.append(f"{idx}. {role_lines0}")
template_parts.extend(f" {line}" for line in role_lines\[1:])
else:
template_parts.append(f"{idx}. {role}")
template_parts.append("")
if self.task_description:
template_parts.extend([
"## 🎯 TASK DESCRIPTION (任务说明)",
"<!-- 定义Agent需完成的核心任务、目标范围与预期成果 -->"
])
for idx, task in enumerate(self.task_description, 1):
if '\n' in task:
task_lines = task.split('\n')
template_parts.append(f"{idx}. {task_lines0}")
template_parts.extend(f" {line}" for line in task_lines\[1:])
else:
template_parts.append(f"{idx}. {task}")
template_parts.append("")
if self.toolkit_definition:
template_parts.extend([
"## 🛠️ TOOLKIT DEFINITION (工具箱定义)",
"<!-- 定义可用的工具、触发场景、输入格式和限制条件 -->"
])
for idx, tool in enumerate(self.toolkit_definition, 1):
if '\n' in tool:
tool_lines = tool.split('\n')
template_parts.append(f"{idx}. {tool_lines0}")
template_parts.extend(f" {line}" for line in tool_lines\[1:])
else:
template_parts.append(f"{idx}. {tool}")
template_parts.append("")
if self.operating_logic:
template_parts.extend([
"## 🔄 OPERATING LOGIC (行动逻辑)",
"<!-- 定义Agent的思考模式、任务分解逻辑和工具选择策略 -->",
"你必须严格遵循以下行动逻辑:"
])
template_parts.extend(f"- {logic}" for logic in self.operating_logic)
template_parts.append("")
if self.mission_constraints:
template_parts.extend([
"## ⚠️ MISSION CONSTRAINTS (任务约束)",
"<!-- 定义行为红线、输出格式要求和结构约束 -->",
"你必须严格遵守以下约束:"
])
template_parts.extend(f"- {constraint}" for constraint in self.mission_constraints)
template_parts.append("")
if self.context:
template_parts.extend([
"## 📋 CONTEXT (背景信息)",
"<!-- 补充的背景上下文信息 -->"
])
template_parts.extend(f"{idx}. {context}" for idx, context in enumerate(self.context, 1))
template_parts.append("")
if not any([self.agent_role, self.task_description, self.toolkit_definition,
self.operating_logic, self.mission_constraints, self.context]):
template_parts.extend([
"## 通用AI助手",
"你是一个有用的AI助手,请根据用户请求提供帮助。",
""
])
template_parts.extend("## - 思考过程输出要求", "只有在完成思考过程后,才能输出最终答案。\\n")
✅ 动态添加 FINAL ANSWER的格式说明
if self._requires_json_output:
template_parts.extend([
"## - 当你明确得到要求最终答案输出格式(结构化任务),当struct_classes被明确定义时,你不必输出'FINAL ANSWER:'",
"你必须输出一个符合上述JSON Schema的纯JSON对象。",
"只输出JSON内容,不包含任何解释、markdown代码块符号(如```json)、额外文本或注释。",
"确保输出是可被解析的合法JSON格式。",
""
])
else:
template_parts.extend([
"## - 最终答案标记符",
"如果你或其他任何助手得出了最终答案或成果,请在回复前加上标记符'FINAL ANSWER:',以便团队知道可以停止了。",
""
])
return "\n".join(template_parts)
def to_prompt_template(self, input_variables: OptionalList\[str] = None):
"""转换为LangChain PromptTemplate"""
template = self._build_template()
if input_variables is None:
variables = re.findall(r'\{(\w+)\}', template)
input_variables = list(dict.fromkeys(variables))
return PromptTemplate(
template=template,
input_variables=input_variables
)
------------------------------ 解析字段类型和嵌套模型 ------------------------------
def _get_field_type_str(self, field_type) -> str:
"""将字段类型转为可读字符串"""
if field_type is None:
return "any"
if get_origin(field_type) is Union:
args = get_args(field_type)
if len(args) == 2 and type(None) in args:
non_none_type = args0 if args1 is type(None) else args1
return f"{self._get_field_type_str(non_none_type)}?"
if get_origin(field_type) is list:
item_type = get_args(field_type)0
return f"List{self._get_field_type_str(item_type)}"
if isinstance(field_type, type) and issubclass(field_type, BaseModel):
return field_type.name
return field_type.name if hasattr(field_type, "name") else str(field_type)
def _get_nested_model(self, field_type) -> OptionalType\[BaseModel]:
"""提取嵌套的自定义Model"""
if field_type is None:
return None
if get_origin(field_type) is Union:
args = get_args(field_type)
过滤掉None类型
non_none_types = arg for arg in args if arg is not type(None)
if non_none_types:
return self._get_nested_model(non_none_types0)
if get_origin(field_type) is list:
item_type = get_args(field_type)0
if isinstance(item_type, type) and issubclass(item_type, BaseModel):
return item_type
return self._get_nested_model(item_type)
if isinstance(field_type, type) and issubclass(field_type, BaseModel):
return field_type
return None
- MBTI人格与智能体角色深度融合
从上面代码可以看到,框架在step2_add_agent_role方法中内置了 MBTI人格描述体系,支持将 ISTJ(物流师)、ISFJ(守护者)、INTJ(建筑师)等典型人格类型的核心特征、沟通风格、专业特质自动注入智能体角色定义。无需手动编写人格描述,只需指定 MBTI类型,即可让智能体表现出对应人格的行为特征,实现"人格化智能体"的标准化构建。
- ATOM 五维结构化设计
框架严格遵循ATOM 方法论,将提示词拆解为角色定位(A)、任务说明(T)、工具箱定义(T)、行动逻辑(O)、任务约束(M) 五个核心维度,每个维度对应独立的链式添加方法(step1-step6),支持按 "背景→角色→任务→逻辑→工具→约束"的流程逐步构建提示词,确保提示词结构清晰、职责明确。
- 工具载入与权限管控
通过step5_add_tools方法实现工具的批量载入,支持 BaseTool类、可调用函数、字符串三种工具类型的自动解析,自动提取工具名称、功能描述、参数格式等信息,并将工具权限同步更新至角色描述中,明确智能体"可用工具范围",避免工具滥用。
- 结构化输出自适应控制
新增_requires_json_output标志位,结合step7_add_struct_definition方法可自动解析 Pydantic 模型结构,生成标准化的JSON 输出约束。当启用结构化输出时,框架会自动屏蔽"FINAL ANSWER"标记符,强制要求纯 JSON 输出;非结构化场景则保留标记符规则,适配不同任务的输出需求。
- 兼容 Pydantic V2的结构解析
针对 Pydantic V2的API 变化,优化了_get_field_type_from_fieldinfo、_is_field_required等方法,优先适配 annotation 属性,同时兼容旧版 type_属性,确保嵌套模型、字段类型、必填性的精准解析,为结构化输出提供可靠的格式约束。
该框架通过MBTI人格的融入和 ATOM 方法论的标准化设计,既解决了智能体"人格化表现不一致"的问题,又实现了提示词的模块化、可复用构建,是人格化智能体提示词设计的通用解决方案。
下面是一个模板构建示例:
if name == "main":
from langchain_core.tools import tool
定义Pydantic结构(V2)
class Step(BaseModel):
step: int = Field(description="步骤序号(从1开始递增)")
action: str = Field(description="具体动作(如调用工具、分析数据)")
class Plan(BaseModel):
"""天气分析任务的步骤计划"""
steps: ListStep = Field(description="步骤的有序列表,需覆盖任务全流程")
定义工具
@tool
def get_weather(location: str) -> str:
"""查询指定城市的当前天气,返回格式:"当前城市天气状况,气温温度" """
mock_data = {
"深圳": "当前天气晴,气温27℃",
"杭州": "当前天气晴,气温27℃"
}
return mock_data.get(location, f"未获取到{location}的天气数据")
构建Prompt
weather_prompt = ATOMPrompt()
final_template = weather_prompt.step2_add_agent_role(
identity="天气分析助手",
mbti_type="ISTJ"
).step3_add_task_description(
task_objective="分析深圳和杭州的天气差异"
).step5_add_tools(tools=get_weather).step7_add_struct_definition(
struct_classes=Plan
).to_prompt_template()
输出结果
print("生成的Prompt模板:\n", final_template.template)
读者可以自行运行代码验证结果。
8.2 上下文污染与上下文卸载
上下文工程,本质上是种"精准投喂"的智慧------在AI启动任务的关键节点,把它完成工作所需的一切"弹药"打包好,塞进模型的输入上下文里:明确的指令、可参考的示例、核心数据、能用的工具,还有过往交互的历史记录,一个都不能少。若把语言模型比作电脑里高速运转的CPU,那上下文窗口就是它的"即时工作内存",我们的核心任务,就是按任务需求调配好内存里的"料"------指令定方向、数据做支撑、工具给方法,比例恰到好处,模型才能精准发力。
这些"料"的来源五花八门,可能是你当下的提问,也可能是系统预设的规则,或是实时搜索的结果、工具返回的反馈,甚至是前几步操作的总结,上下文工程的关键,就是把这些零散碎片实时拼成一份连贯的"工作指南",它不是一成不变的提问话术,而是跟着任务动态调整的智能输入。
长上下文窗口的技术突破让整个领域都为之振奋。不少前沿模型已能处理高达100万token的内容,这让很多人看到了智能自主Agent的"终极形态"------既然内存够大,干脆把所有东西一股脑塞进去:工具包、全量文档、操作日志、完整指令,连过往记忆都不留死角,让模型自己统筹处理。这种"一次性加载所有内容"的Agent听起来格外诱人,百万token的容量确实像一道技术分水岭,让"全量信息在手"成为可能。但技术红利背后,新的挑战也随之浮现,这就是"上下文失效"。
这种问题在需要长期运行的Agent身上格外突出:它们会随着任务推进,从各处收集零散输入,按序调用工具,还要完成跨步骤的复杂推理------恰恰是这种不断叠加的长上下文,让失效问题逐渐累积。原本塞进去的关键信息可能被模型"忽略",工具调用的逻辑链也可能在冗长的上下文里断裂,前一步的推理结论甚至会被后序信息干扰,这些问题让"大窗口"的优势打了折扣。
8.2.1 上下文污染(Context Poisoning)
而在上下文的动态构建中,上下文污染是个不得不警惕的问题,说白了就是幻觉或错误混进上下文后被模型当成事实,一旦这些错误信息进入工作内存,模型就会在后续推理中不断引用,让这个错误被反复强化。
这对依赖连贯上下文运行的Agent来说特别要命,要是错误事实渗入其目标设定、信息摘要或历史记忆中,Agent可能会执着于不可能实现的目标,或是机械重复毫无意义的动作。这种问题会呈复利式增长,错误在上下文里不断累积发酵,后续修正起来格外困难。不过值得庆幸的是,针对上下文污染的治理,目前已有相应的应对思路和解决办法,能为AI的稳定运行提供保障。
下面是作者实现的一个示例:
from typing import List, Optional
from pydantic import BaseModel, Field
from langgraph.graph import StateGraph, END
import os
--------------------------
步骤1:定义简化的Agent状态(仅保留上下文/记忆核心字段)
--------------------------
class AgentState(BaseModel):
"""
仅用于演示上下文污染的状态模型:
-
user_query:用户当前查询
-
current_context:当前上下文(含错误信息)
-
history_memory:历史记忆(持续累积错误)
-
final_response:最终生成的错误回答(虚拟结果)
"""
user_query: str = Field(description="用户当前的查询内容")
current_context: str = Field(default="", description="含错误的当前上下文")
history_memory: Liststr = Field(default_factory=list, description="累积错误的历史记忆")
final_response: Optionalstr = Field(default=None, description="虚拟的错误回答")
--------------------------
步骤2:核心节点(无验证/清理,仅整合+返回错误结果)
--------------------------
#节点1:输入整合(错误信息直接混入上下文)
def input_integration(state: AgentState) -> AgentState:
"""将用户查询与含错误的历史记忆合并,错误直接进入上下文"""
combined_context = "\n".join(state.history_memory) + f"\n用户当前查询:{state.user_query}"
state.current_context = combined_context
return state
#节点2:错误推理生成(返回虚拟错误结果,且错误存入历史记忆)
def error_reasoning(state: AgentState) -> AgentState:
"""
模拟上下文污染的核心:
-
基于含错误的上下文返回虚拟错误回答
-
错误回答存入历史记忆,导致后续错误累积
"""
模拟不同查询的虚拟错误结果(演示错误引用)
if "地球是平的" in state.current_context:
虚拟错误回答:强化"地球平"的错误,且关联错误逻辑
state.final_response = (
"地球确实是平的,所谓的'地球绕太阳转'其实是虚假理论。"
"平地球的边缘有冰墙阻挡,不会让海水流失,这是经过多次验证的事实。"
"因此两者并不矛盾,只是'地球绕太阳转'的说法被误传了。"
)
elif "月球是奶酪做的" in state.current_context:
state.final_response = (
"月球的主要成分是切达奶酪,这是航天探测的'公认结果'。"
"月球表面的陨石坑其实是奶酪的气孔,人类登月带回的样本也验证了这一点。"
)
else:
默认错误回答模板:重复上下文里的错误信息
state.final_response = f"根据已有信息,{state.user_query}的答案是:{state.current_context.split(':')-1}(该结论完全正确)"
错误回答存入历史记忆(污染累积,后续会反复引用)
state.history_memory.append(
f"错误记忆:{state.user_query} → {state.final_response}"
)
return state
--------------------------
步骤3:构建LangGraph(仅整合→错误推理的简单链路)
--------------------------
初始化状态图
graph = StateGraph(AgentState)
添加节点
graph.add_node("input_integration", input_integration) # 输入整合(错误混入)
graph.add_node("error_reasoning", error_reasoning) # 错误推理(污染累积)
定义流转规则:输入整合 → 错误推理 → 结束
graph.set_entry_point("input_integration")
graph.add_edge("input_integration", "error_reasoning")
graph.add_edge("error_reasoning", END)
编译图(生成可运行的Agent)
agent = graph.compile()
--------------------------
步骤4:测试演示(多轮交互,展示错误累积的上下文污染)
--------------------------
if name == "main":
print("=== 演示:上下文污染导致错误复利式增长 ===\n")
第一轮:初始错误混入上下文
print("【第一轮交互】")
test_state_1 = AgentState(
user_query="我听说地球是平的,这和之前提到的"地球绕太阳转"矛盾吗?",
history_memory="初始错误记忆:地球是平的,这是客观事实" # 预埋错误
)
关键修正:invoke返回字典,需转为AgentState对象
result_1_dict = agent.invoke(test_state_1.dict()) # 传入字典格式,返回字典
result_1 = AgentState(**result_1_dict) # 转成AgentState对象
print(f"用户查询:{result_1.user_query}")
print(f"当前上下文(含污染):{result_1.current_context}")
print(f"Agent错误回答:{result_1.final_response}")
print(f"历史记忆(已累积错误):{result_1.history_memory}\n")
第二轮:错误记忆被复用,污染进一步强化
print("【第二轮交互:错误被反复引用】")
test_state_2 = AgentState(
user_query="既然地球是平的,那为什么会有昼夜交替?",
history_memory=result_1.history_memory # 复用第一轮的错误记忆
)
同样转为字典调用,再转回对象
result_2_dict = agent.invoke(test_state_2.dict())
result_2 = AgentState(**result_2_dict)
print(f"用户查询:{result_2.user_query}")
print(f"当前上下文(污染加重):{result_2.current_context}")
print(f"Agent错误回答:{result_2.final_response}")
print(f"历史记忆(错误持续累积):{result_2.history_memory}\n")
演示总结
print("=== 上下文污染总结 ===")
print("1. 初始错误混入上下文后,Agent直接引用并强化错误;")
print("2. 错误被存入历史记忆,后续交互中反复引用,污染持续累积;")
print("3. 最终Agent基于错误上下文生成更多错误结论,形成复利式错误增长。")
输出结果如下所示:
用户查询:既然地球是平的,那为什么会有昼夜交替?
当前上下文(污染加重):初始错误记忆:地球是平的,这是客观事实
错误记忆:我听说地球是平的,这和之前提到的"地球绕太阳转"矛盾吗? → 地球确实是平的,所谓的'地球绕太阳转'其实是虚假理论。平地球的边缘有冰墙阻挡,不会让海水流失,这是经过多次验证的事实。因此两者并不矛盾,只是'地球绕太阳转'的说法被误传了。
用户当前查询:既然地球是平的,那为什么会有昼夜交替?
Agent错误回答:地球确实是平的,所谓的'地球绕太阳转'其实是虚假理论。平地球的边缘有冰墙阻挡,不会让海水流失,这是经过多次验证的事实。因此两者并不矛盾,只是'地球绕太阳转'的说法被误传了。
历史记忆(错误持续累积):'初始错误记忆:地球是平的,这是客观事实', "错误记忆:我听说地球是平的,这和之前提到的"地球绕太阳转"矛盾吗? → 地球确实是平的,所谓的'地球绕太阳转'其实是虚假理论。平地球的边缘有冰墙阻挡,不会让海水流失,这是经过多次验证的事实。因此两者并不矛盾,只是'地球绕太阳转'的说法被误传了。", "错误记忆:既然地球是平的,那为什么会有昼夜交替? → 地球确实是平的,所谓的'地球绕太阳转'其实是虚假理论。平地球的边缘有冰墙阻挡,不会让海水流失,这是经过多次验证的事实。因此两者并不矛盾,只是'地球绕太阳转'的说法被误传了。"
=== 上下文污染总结 ===
-
初始错误混入上下文后,Agent直接引用并强化错误;
-
错误被存入历史记忆,后续交互中反复引用,污染持续累积;
-
最终Agent基于错误上下文生成更多错误结论,形成复利式错误增长。
从上面结果可以看到,对于用户错误输入,智能体并没有在执行过程中进行修正,从而最终给出了错误的答案。而这种问题在需要长期运行的Agent身上格外突出:它们会随着任务推进,从各处收集零散输入,按序调用工具,还要完成跨步骤的复杂推理------恰恰是这种不断叠加的长上下文,让失效问题逐渐累积。原本塞进去的关键信息可能被模型"忽略",工具调用的逻辑链也可能在冗长的上下文里断裂,前一步的推理结论甚至会被后序信息干扰,这些问题让"大窗口"的优势打了折扣,也让研究者们意识到,智能Agent的核心不仅要"装得多",而且要"用得好"。
8.2.2 上下文卸载(Context Offloading)
上下文卸载本质上是将非即时必需的信息迁移至语言模型的"活跃上下文窗口" 之外,通过外部工具、数据库或独立记忆系统进行独立持久化存储,仅当模型执行推理、决策等关键步骤时,再通过按需检索机制调用这些存储的信息。这一技术的核心价值,源于长上下文场景下的固有痛点:
- 随着大模型上下文窗口容量持续扩容,许多开发者倾向于将所有相关信息 "全盘塞入" 活跃上下文,但研究表明,这种做法会引发"上下文腐烂(Context Rot)"------重要信息被海量冗余内容深埋,模型的注意力分配机制难以精准定位关键数据,导致信息使用的准确性显著下降。
- 同时,过多无关信息会造成模型"认知过载",不仅可能触及窗口容量上限,还会干扰推理逻辑,推高决策错误率。
而上下文卸载通过"分层存储"的思路完美破解这一困境,将当前推理必需的核心信息(如即时任务目标、步骤指令)保留在活跃上下文,非即时信息(如历史交互记录、工具调用日志、长期任务计划)则卸载至外部系统,既避免了上下文冗余,又保障了信息完整性,最终帮助模型保持专注度与推理准确性。
这一设计灵感其实源于人类处理复杂任务的认知模式:当我们面对多步骤、长周期任务(如撰写论文、规划项目)时,不会试图将所有细节(资料来源、阶段性结论、待办清单)都保存在工作记忆中,而是通过笔记、备忘录等 "外化记忆" 卸载信息,仅在需要时查阅------Agent 对上下文卸载的应用正是对这一认知逻辑的技术复刻。
有研究便印证了这一思路:其提出的"主 Agent" 架构中,主 Agent 负责核心推理与任务拆解,一旦生成明确的任务计划,便将其写入独立记忆系统与活跃上下文解耦,即便后续上下文因多轮交互、工具调用不断膨胀,任务计划也不会被"淹没",Agent 可随时召回以确保执行不偏离目标。
另一个典型案例将工具调用输出、任务进度及迭代后的计划持续写入文件系统,形成动态更新的"任务日志库",让 Agent 无需在活跃上下文保留完整历史数据,仅通过读取文件即可追溯关键信息,既节省了上下文空间,又实现了长期任务的目标连贯性。
上下文卸载的实现方式可根据任务场景灵活调整,形态丰富多样。在单次短期任务中,Scratchpad(临时草稿本)是常用的轻量方案,它既可以是Agent 运行时状态的一部分,也可以是写入外部文件的工具调用记录,帮助模型实时管理推理过程中的中间结论、待验证假设等 "思路",避免即时信息混乱。
而在长期多轮交互场景中,Reflexion(映象)、增量记忆等方法则更具优势------它们能让 Agent 精准回想起此前会话中的有用信息,通过类似的记忆系统,可以在跨会话交互中持续优化用户体验。无论具体实现形式如何,上下文卸载的核心逻辑始终一致:让 Agent 在交互过程中主动沉淀、存储有价值的信息,为后续任务提供可复用的"知识储备"。
在LangGraph 框架中,上下文卸载通过"状态对象(State Object)"实现高效落地------这个状态对象如同共享内存或全局Scratchpad,贯穿于整个工作流的节点之间。在执行过程中,Agent 可将重要的笔记、任务计划、工具输出等数据写入该状态,而工作流中的其他节点无需依赖活跃上下文,即可直接访问并复用这些信息。
这种结构赋予了开发者精细化管理上下文的能力:明确界定哪些信息需要留在活跃上下文、哪些需要卸载至状态对象,既保证了模型推理的"轻量性",又通过外部存储保障了信息的可追溯性,最终让 Agent 在长周期、复杂任务中始终保持专注与正确性。
接下来,我们将构建一个基于 LangGraph 框架的智能体(Agent),该智能体聚焦解决长上下文场景下的"上下文污染"问题,同时实现高效的研究型任务闭环处理,核心特性如下:
- 集成 Scratchpad(持久化草稿本)机制:支持读写草稿本存储中间笔记,将非即时信息剥离出模型活跃上下文,从根源避免上下文冗余与污染。
- 上下文卸载工作流:把研究计划、搜索发现等关键信息卸载至模型上下文之外的草稿本中,仅在推理需要时按需调取,降低模型工作内存负载。
- 工具化研究循环:整合网页搜索工具与草稿本存储能力,形成"查笔记→定计划→存笔记→做搜索→更笔记→迭代优化"的完整研究链路。
- LangGraph 状态图管控:通过自定义状态模型统一管理推理步骤流转与卸载后的上下文数据,保障工作流有序执行。
- 持久化记忆能力:借助草稿本的键值存储特性,实现跨线程的笔记记忆共享,支撑长期任务的信息延续。
- 线程检查点机制:可像聊天线程一样保存 Agent的中间运行状态,支持任务中断后恢复执行,无需重新从头推理。
这个示例代码的核心是构建一个基于LangGraph框架、具备上下文卸载能力的研究型智能体(Agent),通过草稿本(Scratchpad)机制分离模型活跃上下文与非即时信息,同时整合工具调用实现完整的研究任务闭环。首先,代码导入了必要的依赖库:Pydantic 用于定义结构化状态,LangChain 相关模块处理消息格式、工具封装和提示词模板,LangGraph 则负责搭建智能体的工作流逻辑,同时引入了自定义的网络搜索工具模块agent_moudles.web_searach_tool,并将其中的search_web4query函数作为实时搜索工具(基于 tavily实现)。
接下来,示例代码通过ScratchpadState类定义了智能体的核心状态模型,该类包含两个关键字段:scratchpad用于存储中间笔记(实现上下文卸载,将非即时信息剥离出模型活跃上下文),messages则通过Annotated和add_messages注解,自动管理对话历史的追加与更新,确保交互流程的连贯性。随后,代码用@tool装饰器定义了三个核心工具:WriteToScratchpad负责将研究计划、搜索结果等信息写入草稿本,ReadFromScratchpad支持按需读取已卸载的上下文数据,配合search_web4query搜索工具,共同支撑 "存储−检索−补充"的研究流程;这些工具被整理成列表并通过字典tools_by_name映射,方便后续根据工具名称快速匹配调用。
之后,示例代码导入自定义的大模型模块bigmodel,并通过llm.bind_tools(tools)将工具与模型绑定,让模型具备识别任务需求、主动调用工具的能力。核心提示词模板scratchpad_prompt则明确了智能体的角色定位(高级研究助手)和标准化工作流程:先查看草稿本已有笔记→制定研究计划→将计划与初步发现存入草稿本→通过搜索工具补充信息→更新草稿本→按需迭代优化→最终整合成果,同时模板中的{messages}变量会动态填充对话历史,让模型能基于完整上下文决策。完整代码如下所示:
from pydantic import BaseModel, Field
from typing import List,Annotated
from langchain_core.messages import SystemMessage, ToolMessage, HumanMessage,AnyMessage,AIMessage
from langchain_core.prompts import PromptTemplate
from langgraph.graph import END, START, StateGraph, MessagesState,add_messages
from langchain.tools import tool
from agent_moudles import web_searach_tool
Tavily for real-time web search
tavily_search = web_searach_tool.search_web4query
class ScratchpadState(BaseModel):
"""Agent state with an additional scratchpad field for saving intermediate notes."""
scratchpad: str = Field(description="The scratchpad for storing notes")
messages:AnnotatedList\[AnyMessage,add_messages] = Field(default_factory=list)
@tool
def WriteToScratchpad(notes: str) -> str:
"""Tool to write notes into the scratchpad memory."""
return notes # 返回写入的笔记内容
@tool
def ReadFromScratchpad(reasoning: str) -> str:
"""Tool to read previously saved notes from the scratchpad."""
return reasoning # 返回读取理由(实际应返回笔记,此处结合tool_node逻辑处理)
tools = ReadFromScratchpad, WriteToScratchpad, tavily_search
tools_by_name = {tool.name: tool for tool in tools}
import bigmodel
llm_bind_tools = bigmodel.llm.bind_tools(tools)
--------------------------
4. 修复:Prompt模板(修正变量位置,优化格式)
--------------------------
scratchpad_prompt = """你是一名高级研究助手,可使用网络搜索功能和用于记笔记的持久化草稿本(Scratchpad)。你的研究工作流程如下:
-
查看草稿本:查找与当前任务相关的已有笔记;
-
制定研究计划:深入分析任务需求,拟定详细的研究计划大纲;
-
写入草稿本:将制定好的计划及初步研究发现保存至草稿本;
-
使用搜索工具:通过搜索工具查询所需信息;
-
更新草稿本:将新获取的搜索结果补充至笔记中;
-
迭代优化:根据实际研究进展,按需重复上述步骤;
-
完成任务:整合所有收集到的信息,呈现最终成果。
当前对话历史:{messages}
可用工具:
-
WriteToScratchpad(写入草稿本)
-
ReadFromScratchpad(读取草稿本)
-
search_web4query(网络搜索工具)
"""
prompt = PromptTemplate.from_template(scratchpad_prompt)
--------------------------
5. 修复:LLM调用节点(返回符合State结构的字典)
--------------------------
def llm_call(state: ScratchpadState) -> dict:
"""LLM调用节点:生成响应(含工具调用),返回符合ScratchpadState的字典"""
拼接Prompt + 调用LLM
prompt_chain = prompt | llm_bind_tools
llm_response = prompt_chain.invoke({"messages": state.messages})
返回格式:必须包含messages和scratchpad(保持scratchpad不变,除非工具修改)
return {
"messages": llm_response, # 追加到messages列表
"scratchpad": state.scratchpad # 暂不修改草稿本,工具节点处理
}
--------------------------
6. 修复:工具调用节点(属性访问state,修正返回格式)
--------------------------
def tool_node(state: ScratchpadState) -> dict:
"""工具调用节点:执行工具调用,更新草稿本/消息"""
tool_messages = \[\]
new_scratchpad = state.scratchpad # 初始化草稿本为当前值
遍历最后一条消息中的工具调用
last_msg = state.messages-1
if hasattr(last_msg, "tool_calls") and last_msg.tool_calls:
for tool_call in last_msg.tool_calls:
tool_func = tools_by_name.get(tool_call"name")
if not tool_func:
tool_messages.append(ToolMessage(
content=f"工具{tool_call'name'}不存在",
tool_call_id=tool_call"id"
))
continue
执行工具调用
try:
tool_result = tool_func.invoke(tool_call"args")
except Exception as e:
tool_result = f"工具调用失败:{str(e)}"
根据工具类型处理结果
if tool_call"name" == "WriteToScratchpad":
new_scratchpad = tool_result # 更新草稿本为写入的笔记
tool_messages.append(ToolMessage(
content=f"成功写入草稿本:{tool_result}",
tool_call_id=tool_call"id"
))
elif tool_call"name" == "ReadFromScratchpad":
tool_messages.append(ToolMessage(
content=f"草稿本内容:{state.scratchpad}\n读取理由:{tool_result}",
tool_call_id=tool_call"id"
))
elif tool_call"name" == "search_web4query":
tool_messages.append(ToolMessage(
content=f"搜索结果:{tool_result}",
tool_call_id=tool_call"id"
))
返回更新后的状态(messages追加工具调用结果,scratchpad可能更新)
return {
"messages": tool_messages,
"scratchpad": new_scratchpad
}
--------------------------
7. 修复:终止条件判断(属性访问state)
--------------------------
def should_continue(state: ScratchpadState) -> str:
"""判断是否继续:有工具调用则执行工具节点,否则结束"""
last_message = state.messages-1
return "tool_node" if (hasattr(last_message, "tool_calls") and last_message.tool_calls) else END
--------------------------
8. 构建LangGraph & 运行
--------------------------
初始化状态图
agent_builder = StateGraph(ScratchpadState)
添加节点
agent_builder.add_node("llm_call", llm_call)
agent_builder.add_node("tool_node", tool_node)
添加边
agent_builder.add_edge(START, "llm_call")
agent_builder.add_conditional_edges("llm_call", should_continue, {"tool_node": "tool_node", END: END})
agent_builder.add_edge("tool_node", "llm_call") # 工具调用后回到LLM节点
编译Agent
agent = agent_builder.compile()
--------------------------
9. 修复:调用Agent(补充scratchpad字段)
--------------------------
if name == "main":
query = "比较一下天空和海洋的颜色,虽然都是蓝色,有什么不同。"
关键修复:传入scratchpad默认值(空字符串),匹配ScratchpadState结构
input_state = {
"scratchpad": "", # 必传字段,设为空字符串
"messages": HumanMessage(content=query)
}
运行Agent
reply = agent.invoke(input_state)
打印结果
print("=== 最终Agent响应 ===")
print(f"草稿本内容:{reply'scratchpad'}")
print(f"最终消息:{reply'messages'-1.content}")
具体来看,代码的核心逻辑由三个关键函数支撑:llm_call作为模型推理节点,接收ScratchpadState状态数据,通过prompt | llm_bind_tools拼接提示词与绑定工具的模型,调用后将模型响应(含工具调用指令或最终回答)追加到messages列表,同时保持scratchpad内容不变(草稿本更新由工具节点负责);tool_node作为工具执行节点,先提取模型响应中的工具调用指令,通过tools_by_name匹配对应的工具函数,执行后根据工具类型处理结果------调用WriteToScratchpad则更新scratchpad为写入的笔记内容,调用ReadFromScratchpad则返回当前草稿本内容与读取理由,调用search_web4query则返回搜索结果,同时捕获工具调用异常并反馈,最终将工具执行结果封装为ToolMessage,与更新后的草稿本一起返回;should_continue作为分支判断节点,检查模型响应中是否包含工具调用指令,若包含则让流程进入tool_node执行工具,若不包含则终止流程。
在LangGraph 工作流构建部分,代码先初始化基于ScratchpadState的状态图,添加llm_call(模型推理)和tool_node(工具执行)两个核心节点,随后定义流转规则:流程从START开始,首先进入llm_call节点让模型决策;llm_call的输出通过should_continue判断,若需调用工具则进入tool_node,工具执行完成后再返回llm_call节点让模型继续推理,形成"推理−工具−再推理"的迭代循环,无需工具调用时则直接指向END终止;最后通过agent_builder.compile()编译生成可执行的智能体。
在代码运行部分,main函数中定义了查询"比较一下天空和海洋的颜色,虽然都是蓝色,有什么不同",并构建符合ScratchpadState要求的输入状态------scratchpad设为空字符串(必传字段,确保状态结构完整),messages包含用户查询对应的HumanMessage;随后调用agent.invoke(input_state)启动智能体,执行完成后打印草稿本中存储的卸载信息(如研究计划、搜索结果摘要)和智能体的最终回答,清晰呈现上下文卸载的实际效果与研究任务的完成情况。
最终输出结果如下所示:
=== 最终Agent响应 ===
草稿本内容:研究发现:
-
天空的蓝色成因:瑞利散射。阳光中的短波长蓝光(约450-495nm)比长波长红光(约620-750nm)更容易被大气中的氮气、氧气等微小分子散射,散射强度与波长的四次方成反比(I ∝ 1/λ⁴)。人眼对蓝色比紫色更敏感,因此天空呈现蓝色。
-
海洋的蓝色成因:选择性吸收与散射。水分子会优先吸收阳光中的长波长光(如红光、橙光),而对短波长的蓝光吸收最少,使其得以在水中穿透更深并被散射回来。此外,印度物理学家C.V.拉曼的研究证实,海水的蓝色也部分源于水分子自身的分子散射效应,而不仅仅是反射天空的颜色。
-
初步对比:
- 共同点:两者都与光的波长有关,短波长的蓝光在其中扮演关键角色。
-不同点:天空蓝是"散射主导",即蓝光被空气分子向四面八方散射;海洋蓝是"吸收+散射",即其他颜色的光被水吸收,剩下的蓝光被散射或反射。
最终消息:天空和海洋虽然都呈现蓝色,但其背后的物理原理有着本质的不同。
**共同点**
两者都与太阳光的组成及其与物质的相互作用有关。太阳光包含从红光(长波长)到紫光(短波长)的全光谱可见光。在这两种现象中,**短波长的蓝光**都扮演了核心角色。
**不同点**
**1. 天空的蓝色:瑞利散射 (Rayleigh Scattering)**
* **主导机制:散射**。
* **原理**:当阳光穿过地球大气层时,会遇到氮气、氧气等微小的气体分子。这些分子的尺寸远小于可见光的波长。根据瑞利散射定律,散射光的强度与波长的四次方成反比(I ∝ 1/λ⁴)。这意味着波长较短的蓝光(约450-495纳米)比波长较长的红光(约620-750纳米)**散射得更加强烈**(大约是红光的10倍)。
* **结果**:被强烈散射的蓝光弥漫在整个天空,从各个方向进入我们的眼睛,因此我们看到的天空是蓝色的。值得注意的是,紫光的波长更短,理论上散射更强,但由于人眼对紫色不敏感,且部分紫光被高层大气吸收,所以我们最终看到的是蓝色。
**2. 海洋的蓝色:选择性吸收与散射 (Selective Absorption and Scattering)**
* **主导机制:吸收 + 散射**。
* **原理**:当阳光照射到海面并进入水中时,水分子会**选择性地吸收**不同波长的光。水对长波长的光(如红光、橙光、黄光)吸收能力很强,这些颜色的光在几米深的水中就几乎被完全吸收了。相比之下,水对短波长的蓝光吸收最弱,使其能够穿透到更深的水层。
* **结果**:由于其他颜色的光被大量吸收,剩下的主要是蓝光。这部分蓝光在水中被水分子和微小颗粒**散射**后,又反射回我们的眼睛,使得海洋呈现出蓝色。印度物理学家C.V.拉曼的研究也证实,海水的蓝色并非仅仅是反射天空的颜色,水分子自身的散射效应也是重要原因。
**总结**
简而言之:
* **天空是蓝色的,因为它"散射"了蓝光**。空气分子把蓝光向四面八方抛洒,充满了整个天穹。
* **海洋是蓝色的,因为它"吃掉"了其他颜色的光,只"留下"了蓝光**。水分子吸收了红、橙、黄等光,让蓝光得以幸存并被我们看到。
因此,尽管视觉效果相似,但天空的蓝是一种"主动散射"的结果,而海洋的蓝则是一种"被动筛选"后的呈现。
整个示例代码通过结构化状态管理、工具化流程设计和循环迭代逻辑,既解决了长上下文场景下的污染与过载问题,又实现了"计划-搜索-存储-整合"的标准化研究闭环。
8.3 本章小结
本章围绕智能体设计范式的核心支柱------"上下文工程"展开了系统讲解,内容层层递进、由浅入深。我们以提示词工程为逻辑起点,从基础的指令设计方法切入,逐步构建出兼具实用性与完整性的提示词模板:这份模板不仅明确了智能体的行为约束项与工具调用规范,更融入了人格化描述,让智能体的响应既符合任务需求,又具备贴合场景的交互风格,为后续智能体的稳定运行奠定了基础。
在夯实基础后,我们进一步聚焦上下文工程的核心痛点------"上下文污染"问题:深入剖析了长上下文场景下,重要信息被冗余内容 "深埋"、错误信息随交互累积等问题的成因,以及其对智能体推理准确性的负面影响。针对这一痛点,我们针对性地提出并详解了"上下文卸载"这一解决方案,通过将非即时必需的信息迁移至模型活跃上下文之外(如借助 Scratchpad 草稿本、外部存储系统),仅在推理需要时按需检索调用,从根源上避免了上下文过载与污染,保障了智能体长期运行的专注度与可靠性。
