AI Agent 工程化落地实战系列 · 第20篇(核心技能篇收官)
摘要
本文是 AI Agent 工程化落地实战系列第一季核心技能阶段(第06-19篇)的收官总结。我们将系统回顾 LLM 选型、核心能力循环、Function Calling、MCP 协议、工具设计、Prompt 工程进阶、输出质量保障、上下文工程、记忆系统、RAG 2.0、自主决策、多模态和流式输出共14个核心技能主题,构建一张完整的 Agent 核心技能知识地图。文章提供一份覆盖50个必须掌握知识点的检查清单、10道实战自测场景题,并梳理从核心技能到架构设计的衔接路径。无论你是跟读全系列的忠实读者,还是刚入门想快速定位技能缺口的工程师,这份阶段性总结都将帮助你查漏补缺,为进入第二季架构设计篇做好充分准备。
版本声明: 本文基于 2025-2026 年主流 Agent 工程实践撰写,涉及框架包括 LangChain v0.3+、LangGraph 0.2+、OpenAI Agents SDK、Claude MCP 等。技术迭代迅速,部分 API 细节可能随版本变化,但核心方法论和设计原则具有持久参考价值。
适用边界: 本文面向已有一定 LLM 应用开发经验的工程师,假设读者具备 Python 基础、了解 REST API 调用、熟悉基本的 Prompt 编写。不适合零基础读者作为入门材料,建议先从第01-05篇读起。
文章目录
-
- 摘要
- 一、核心技能篇回顾:06-19篇知识地图
-
- [1.1 技能链路全景](#1.1 技能链路全景)
- [1.2 各篇核心一句话总结](#1.2 各篇核心一句话总结)
- 二、Agent核心技能矩阵:技能点/掌握标准/自测方法
-
- [2.1 五维技能矩阵](#2.1 五维技能矩阵)
- [2.2 技能掌握标准与自测方法](#2.2 技能掌握标准与自测方法)
- 三、技能检查清单:50个必须掌握的知识点
-
- [基础层(知识点 1-8)](#基础层(知识点 1-8))
- [工具层(知识点 9-20)](#工具层(知识点 9-20))
- [控制层(知识点 21-32)](#控制层(知识点 21-32))
- [认知层(知识点 33-42)](#认知层(知识点 33-42))
- [交互层(知识点 43-50)](#交互层(知识点 43-50))
- 四、实战自测题:10道场景题
-
- [场景题 1:模型选型困境](#场景题 1:模型选型困境)
- [场景题 2:Function Calling 死循环](#场景题 2:Function Calling 死循环)
- [场景题 3:上下文窗口溢出](#场景题 3:上下文窗口溢出)
- [场景题 4:MCP Server 设计](#场景题 4:MCP Server 设计)
- [场景题 5:记忆系统设计](#场景题 5:记忆系统设计)
- [场景题 6:RAG 管线优化](#场景题 6:RAG 管线优化)
- [场景题 7:多工具并行调用](#场景题 7:多工具并行调用)
- [场景题 8:流式输出中的 JSON 解析](#场景题 8:流式输出中的 JSON 解析)
- [场景题 9:Agent 自主决策失败恢复](#场景题 9:Agent 自主决策失败恢复)
- [场景题 10:多模态 Agent 设计](#场景题 10:多模态 Agent 设计)
- 五、从技能到架构:核心技能篇如何衔接架构设计篇
-
- [5.1 技能到架构的映射](#5.1 技能到架构的映射)
- [5.2 第二季内容预告](#5.2 第二季内容预告)
- 六、第一季回顾:01-20篇完整认知框架
-
- [6.1 第一季三阶段划分](#6.1 第一季三阶段划分)
- [6.2 三个阶段的设计逻辑](#6.2 三个阶段的设计逻辑)
- [6.3 关键认知转变](#6.3 关键认知转变)
- 七、适用边界与学习建议
-
- [7.1 本文的适用边界](#7.1 本文的适用边界)
- [7.2 学习路径建议](#7.2 学习路径建议)
- [7.3 实践建议](#7.3 实践建议)
- 八、总结
- 参考资料
一、核心技能篇回顾:06-19篇知识地图
从第06篇到第19篇,我们完成了14个核心技能主题的深入探讨。这14篇文章构成了一个 Agent 工程师必备的技能栈------从"选什么模型"到"怎么让 Agent 自主决策",再到"如何处理多模态和流式输出",形成了一条完整的技能链路。
1.1 技能链路全景
#mermaid-svg-441vpf3kR5ecZjlC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-441vpf3kR5ecZjlC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-441vpf3kR5ecZjlC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-441vpf3kR5ecZjlC .error-icon{fill:#552222;}#mermaid-svg-441vpf3kR5ecZjlC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-441vpf3kR5ecZjlC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-441vpf3kR5ecZjlC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-441vpf3kR5ecZjlC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-441vpf3kR5ecZjlC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-441vpf3kR5ecZjlC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-441vpf3kR5ecZjlC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-441vpf3kR5ecZjlC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-441vpf3kR5ecZjlC .marker.cross{stroke:#333333;}#mermaid-svg-441vpf3kR5ecZjlC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-441vpf3kR5ecZjlC p{margin:0;}#mermaid-svg-441vpf3kR5ecZjlC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-441vpf3kR5ecZjlC .cluster-label text{fill:#333;}#mermaid-svg-441vpf3kR5ecZjlC .cluster-label span{color:#333;}#mermaid-svg-441vpf3kR5ecZjlC .cluster-label span p{background-color:transparent;}#mermaid-svg-441vpf3kR5ecZjlC .label text,#mermaid-svg-441vpf3kR5ecZjlC span{fill:#333;color:#333;}#mermaid-svg-441vpf3kR5ecZjlC .node rect,#mermaid-svg-441vpf3kR5ecZjlC .node circle,#mermaid-svg-441vpf3kR5ecZjlC .node ellipse,#mermaid-svg-441vpf3kR5ecZjlC .node polygon,#mermaid-svg-441vpf3kR5ecZjlC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-441vpf3kR5ecZjlC .rough-node .label text,#mermaid-svg-441vpf3kR5ecZjlC .node .label text,#mermaid-svg-441vpf3kR5ecZjlC .image-shape .label,#mermaid-svg-441vpf3kR5ecZjlC .icon-shape .label{text-anchor:middle;}#mermaid-svg-441vpf3kR5ecZjlC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-441vpf3kR5ecZjlC .rough-node .label,#mermaid-svg-441vpf3kR5ecZjlC .node .label,#mermaid-svg-441vpf3kR5ecZjlC .image-shape .label,#mermaid-svg-441vpf3kR5ecZjlC .icon-shape .label{text-align:center;}#mermaid-svg-441vpf3kR5ecZjlC .node.clickable{cursor:pointer;}#mermaid-svg-441vpf3kR5ecZjlC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-441vpf3kR5ecZjlC .arrowheadPath{fill:#333333;}#mermaid-svg-441vpf3kR5ecZjlC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-441vpf3kR5ecZjlC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-441vpf3kR5ecZjlC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-441vpf3kR5ecZjlC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-441vpf3kR5ecZjlC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-441vpf3kR5ecZjlC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-441vpf3kR5ecZjlC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-441vpf3kR5ecZjlC .cluster text{fill:#333;}#mermaid-svg-441vpf3kR5ecZjlC .cluster span{color:#333;}#mermaid-svg-441vpf3kR5ecZjlC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-441vpf3kR5ecZjlC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-441vpf3kR5ecZjlC rect.text{fill:none;stroke-width:0;}#mermaid-svg-441vpf3kR5ecZjlC .icon-shape,#mermaid-svg-441vpf3kR5ecZjlC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-441vpf3kR5ecZjlC .icon-shape p,#mermaid-svg-441vpf3kR5ecZjlC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-441vpf3kR5ecZjlC .icon-shape .label rect,#mermaid-svg-441vpf3kR5ecZjlC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-441vpf3kR5ecZjlC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-441vpf3kR5ecZjlC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-441vpf3kR5ecZjlC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 交互层
认知层
控制层
工具层
基础层
06 LLM选型
07 核心能力循环
08 Function Calling
09 MCP协议
10 实现MCP Server
11 工具设计原则
12 Prompt工程进阶
13 输出质量保障
14 上下文工程
15 记忆系统
16 RAG 2.0
17 自主决策
18 多模态Agent
19 流式输出
20 阶段总结
本文
这张知识地图揭示了 Agent 核心技能栈的五个层次结构:
基础层 解决"用什么"的问题------选择合适的 LLM 并理解 Agent 的核心能力循环(感知-推理-行动)。这是所有后续技能的地基。
工具层 解决"做什么"的问题------从 Function Calling 到 MCP 协议,从实现 MCP Server 到工具设计原则。Agent 的本质差异在于工具使用能力,这一层决定了 Agent 能力的上限。
控制层 解决"怎么做得好"的问题------Prompt 工程决定指令质量,输出质量保障确保可靠性,上下文工程管理有限窗口内的信息密度。这一层决定了 Agent 表现的下限。
认知层 解决"怎么变得聪明"的问题------记忆系统让 Agent 具备跨会话连续性,RAG 2.0 从检索增强走向知识增强,自主决策机制让 Agent 在复杂场景中自主选择路径。这一层决定了 Agent 的智能深度。
交互层 解决"怎么与人和世界交互"的问题------多模态让 Agent 拥有更丰富的感知通道,流式输出让 Agent 的响应更自然流畅。这一层决定了 Agent 的用户体验。

图:五层金字塔结构知识地图:基础层(LLM选型/能力循环)→工具层(Function Calling/MCP/工具设计)→控制层(Prompt工程/质量保障/上下文工程)→认知层(记忆系统/RAG 2.0/自主决策)→交互层(多模态/流式输出),每层标注核心知识点
1.2 各篇核心一句话总结
| 篇目 | 标题 | 核心知识点一句话 |
|---|---|---|
| 06 | LLM选型指南 | Agent 选型不是选参数最大的,而是选延迟/成本/能力三角最匹配场景的 |
| 07 | Agent核心能力循环 | 感知→推理→行动→反馈的闭环是 Agent 区别于 Chatbot 的本质特征 |
| 08 | Function Calling深度解析 | 结构化函数调用是 LLM 从"说话"到"做事"的桥梁,Schema 设计决定成功率 |
| 09 | MCP协议详解 | MCP 是 AI 工具生态的 USB-C 接口,标准化了模型与工具的连接方式 |
| 10 | 实现MCP Server | 从协议到实现,一个生产级 MCP Server 需要考虑鉴权、错误处理、版本管理 |
| 11 | Agent工具设计原则 | 好工具设计遵循"三高一低":高内聚、高可组合、高可观测、低认知负荷 |
| 12 | Prompt工程进阶 | 从单条 Prompt 到 Prompt 体系,模块化+变量化+测试化是工程化路径 |
| 13 | Agent输出质量保障 | 质量保障需要四层防线:输入校验、过程监控、输出验证、回归测试 |
| 14 | Agent上下文工程 | 上下文工程本质是信息密度管理------在有限 Token 窗口内最大化有效信息 |
| 15 | 记忆系统分层架构 | 短期/工作/长期三层记忆架构模拟人类认知,各有不同的存储和检索策略 |
| 16 | RAG 2.0 | RAG 2.0 从"检索-拼接"走向"理解-推理-整合",核心是知识增强而非简单拼接 |
| 17 | Agent自主决策机制 | 自主决策的核心是"何时自主"和"自主到什么程度"的平衡,ReAct/Plan-Execute是两条主线 |
| 18 | 多模态Agent入门 | 多模态 Agent 不是简单加图片输入,而是跨模态推理和统一表征 |
| 19 | Agent流式输出实战 | 流式输出不只是体验优化,更是 Agent 实时性和可中断性的架构基础 |
二、Agent核心技能矩阵:技能点/掌握标准/自测方法
2.1 五维技能矩阵
将14篇文章的知识点按五个维度重新组织,形成一张可直接用于团队能力评估的技能矩阵:
#mermaid-svg-a3j1AH1Tc6gqnG4K{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-a3j1AH1Tc6gqnG4K .error-icon{fill:#552222;}#mermaid-svg-a3j1AH1Tc6gqnG4K .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-a3j1AH1Tc6gqnG4K .marker{fill:#333333;stroke:#333333;}#mermaid-svg-a3j1AH1Tc6gqnG4K .marker.cross{stroke:#333333;}#mermaid-svg-a3j1AH1Tc6gqnG4K svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-a3j1AH1Tc6gqnG4K p{margin:0;}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge{stroke-width:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section--1 rect,#mermaid-svg-a3j1AH1Tc6gqnG4K .section--1 path,#mermaid-svg-a3j1AH1Tc6gqnG4K .section--1 circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .section--1 polygon,#mermaid-svg-a3j1AH1Tc6gqnG4K .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .section--1 text{fill:#ffffff;}#mermaid-svg-a3j1AH1Tc6gqnG4K .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-depth--1{stroke-width:17;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:lightgray;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:#efefef;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-0 rect,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-0 path,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-0 circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-0 polygon,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-0 text{fill:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .node-icon-0{font-size:40px;color:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-depth-0{stroke-width:14;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:lightgray;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:#efefef;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-1 rect,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-1 path,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-1 circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-1 polygon,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-1 text{fill:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .node-icon-1{font-size:40px;color:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-depth-1{stroke-width:11;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:lightgray;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:#efefef;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-2 rect,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-2 path,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-2 circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-2 polygon,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-2 text{fill:#ffffff;}#mermaid-svg-a3j1AH1Tc6gqnG4K .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-depth-2{stroke-width:8;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:lightgray;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:#efefef;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-3 rect,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-3 path,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-3 circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-3 polygon,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-3 text{fill:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .node-icon-3{font-size:40px;color:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-depth-3{stroke-width:5;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:lightgray;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:#efefef;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-4 rect,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-4 path,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-4 circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-4 polygon,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-4 text{fill:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .node-icon-4{font-size:40px;color:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-depth-4{stroke-width:2;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:lightgray;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:#efefef;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-5 rect,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-5 path,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-5 circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-5 polygon,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-5 text{fill:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .node-icon-5{font-size:40px;color:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-depth-5{stroke-width:-1;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:lightgray;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:#efefef;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-6 rect,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-6 path,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-6 circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-6 polygon,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-6 text{fill:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .node-icon-6{font-size:40px;color:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-depth-6{stroke-width:-4;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:lightgray;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:#efefef;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-7 rect,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-7 path,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-7 circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-7 polygon,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-7 text{fill:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .node-icon-7{font-size:40px;color:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-depth-7{stroke-width:-7;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:lightgray;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:#efefef;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-8 rect,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-8 path,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-8 circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-8 polygon,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-8 text{fill:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .node-icon-8{font-size:40px;color:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-depth-8{stroke-width:-10;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:lightgray;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:#efefef;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-9 rect,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-9 path,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-9 circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-9 polygon,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-9 text{fill:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .node-icon-9{font-size:40px;color:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-depth-9{stroke-width:-13;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:lightgray;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:#efefef;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-10 rect,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-10 path,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-10 circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-10 polygon,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-10 text{fill:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .node-icon-10{font-size:40px;color:black;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge-depth-10{stroke-width:-16;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:lightgray;}#mermaid-svg-a3j1AH1Tc6gqnG4K .disabled text{fill:#efefef;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-root rect,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-root path,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-root circle,#mermaid-svg-a3j1AH1Tc6gqnG4K .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-root text{fill:#ffffff;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-root span{color:#ffffff;}#mermaid-svg-a3j1AH1Tc6gqnG4K .section-2 span{color:#ffffff;}#mermaid-svg-a3j1AH1Tc6gqnG4K .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-a3j1AH1Tc6gqnG4K .edge{fill:none;}#mermaid-svg-a3j1AH1Tc6gqnG4K .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-a3j1AH1Tc6gqnG4K :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Agent核心
技能矩阵
模型理解力
LLM能力边界认知
模型选型方法论
成本/延迟/质量三角
多模型路由策略
工具工程力
Function Calling实现
MCP协议理解
MCP Server开发
工具Schema设计
工具组合与编排
指令工程力
Prompt模块化设计
Prompt变量化
Prompt测试与评估
输出质量控制
上下文窗口管理
认知系统力
记忆架构设计
知识检索与增强
自主决策机制
多步推理规划
交互体验力
多模态处理
流式输出实现
人机协作设计
异常处理与降级

图:五维技能雷达图+热力图组合:模型理解力、工具工程力、指令工程力、认知系统力、交互体验力五个维度的掌握程度可视化
2.2 技能掌握标准与自测方法
下面这张表格详细定义了每个技能维度的掌握标准和自测方法,可以作为团队能力评估的参考框架:
| 技能维度 | 技能点 | 掌握标准 | 自测方法 |
|---|---|---|---|
| 模型理解力 | LLM能力边界 | 能准确说出3种主流模型在Function Calling、长文本、代码生成上的差异 | 做一次3模型对比测试 |
| 模型理解力 | 模型选型 | 能为给定的业务场景给出选型理由,包含成本和延迟分析 | 写一份选型决策文档 |
| 模型理解力 | 多模型路由 | 能实现一个基于任务类型的简单路由策略 | 代码实现路由器 |
| 工具工程力 | Function Calling | 能独立设计完整的函数Schema,参数描述清晰,错误处理完善 | 设计一个天气查询工具 |
| 工具工程力 | MCP协议 | 能解释MCP的三个核心角色及其通信流程 | 画出MCP架构图 |
| 工具工程力 | MCP Server | 能实现一个生产级MCP Server,包含鉴权和错误处理 | 实现一个文件操作Server |
| 工具工程力 | 工具设计 | 能从用户需求反推工具集设计,遵循高内聚低耦合 | 给定场景设计工具集 |
| 指令工程力 | Prompt模块化 | 能将复杂Prompt拆分为可复用的模块 | 重构一个复杂Prompt |
| 指令工程力 | Prompt测试 | 能为Prompt编写测试用例和评估指标 | 为一个Prompt写3个测试 |
| 指令工程力 | 质量保障 | 能设计四层质量防线方案 | 为Agent设计质量保障体系 |
| 指令工程力 | 上下文工程 | 能在4K Token内完成一个复杂任务的上下文组织 | 压缩一个长Prompt到4K |
| 认知系统力 | 记忆架构 | 能设计三层记忆系统的存储和检索策略 | 画出记忆架构图并解释 |
| 认知系统力 | RAG 2.0 | 能实现一个带reranking和推理的RAG管线 | 搭建一个知识问答Agent |
| 认知系统力 | 自主决策 | 能实现ReAct或Plan-Execute模式 | 代码实现一个决策循环 |
| 交互体验力 | 多模态 | 能实现图片+文本的混合输入处理 | 做一个图片理解Agent |
| 交互体验力 | 流式输出 | 能实现SSE流式输出并处理中断 | 实现流式聊天 |
三、技能检查清单:50个必须掌握的知识点
以下是50个必须掌握的知识点,按技能链路的五个层次组织。建议逐条自测,标记"✅ 已掌握"、"⚠️ 部分掌握"或"❌ 未掌握"。
基础层(知识点 1-8)
- 能说出 GPT-4o、Claude 3.5 Sonnet、DeepSeek V3 在 Agent 场景下的优劣
- 理解 Context Window 大小对 Agent 行为的影响
- 知道如何根据延迟要求选择本地模型 vs API 模型
- 能计算一次 Agent 调用的 Token 成本
- 理解 Agent 核心循环:感知→推理→行动→反馈
- 能区分 ReAct 模式与 Plan-Execute 模式的适用场景
- 知道 Agent 的"停止条件"如何设计
- 理解 temperature/top_p 等采样参数对 Agent 行为的影响
工具层(知识点 9-20)
- 能手写一个完整的 Function Calling JSON Schema
- 理解 Function Calling 中的 required vs optional 参数设计
- 能处理 Function Calling 的错误返回和重试逻辑
- 理解 MCP 的三个核心角色:Host / Client / Server
- 知道 MCP 的三种传输方式:stdio / SSE / Streamable HTTP
- 能解释 MCP Tools / Resources / Prompts 三种原语的区别
- 能用 Python SDK 实现一个 MCP Server
- 知道 MCP Server 的鉴权和错误处理最佳实践
- 理解"工具粒度"设计------什么时候该拆分,什么时候该合并
- 能为一个给定场景设计不超过5个工具就能覆盖80%需求
- 知道工具描述(description)质量对模型选择准确率的影响
- 理解工具组合编排的几种模式:串行、并行、条件分支
控制层(知识点 21-32)
- 能将一段复杂 Prompt 拆分为 system / instruction / examples 模块
- 知道如何用变量化 Prompt 实现动态注入
- 理解 Few-shot 选择的策略:固定 vs 检索式
- 能为 Agent 输出设计结构化验证(JSON Schema 校验)
- 知道如何实现输出质量自动评估的 pipeline
- 理解上下文窗口管理的三种策略:截断 / 压缩 / 检索替换
- 能实现对话历史的智能压缩(保留首尾、摘要中间)
- 知道如何量化上下文中的"信息密度"
- 理解 System Prompt 在 Agent 全周期中的演变
- 能设计一个 Agent 的"系统消息"模板
- 知道如何处理 Tool 返回结果过长时的上下文截断
- 理解多轮对话中状态管理的两种模式:无状态 vs 有状态
认知层(知识点 33-42)
- 能画出短时记忆 / 工作记忆 / 长期记忆的三层架构
- 知道向量数据库在记忆系统中的定位和使用边界
- 理解记忆的写入策略:全量写入 vs 摘要写入 vs 选择性写入
- 能实现一个带时间衰减的记忆检索策略
- 知道 RAG 2.0 与传统 RAG 的三个核心区别
- 能实现一个带 reranking 的 RAG 管线
- 理解 ReAct 模式的 Thought-Action-Observation 循环
- 能实现 Plan-Execute 模式并处理计划执行失败
- 知道如何设计 Agent 的"人工介入"触发条件
- 理解多 Agent 协作中的任务分配策略
交互层(知识点 43-50)
- 能实现图片输入到文本输出的多模态 Agent
- 知道如何处理多模态输入的 Token 计算和成本
- 理解跨模态推理的基本范式
- 能用 SSE 实现流式输出
- 知道如何处理流式输出中的 JSON 不完整问题
- 能实现流式输出中的用户中断处理
- 理解流式输出对 Agent 架构的影响(可中断性)
- 知道如何设计 Agent 的"正在思考..."状态反馈
四、实战自测题:10道场景题
以下10道场景题模拟真实工程中可能遇到的问题,每题提供参考答案思路。建议先独立思考,再对照参考。
场景题 1:模型选型困境
题目: 你正在开发一个代码审查 Agent,需要处理整个代码仓库的上下文(平均 50K Token),要求响应延迟 < 10秒,预算有限。如何选型?
参考答案思路:
首先分析需求三角:长上下文(50K+)排除了小窗口模型;延迟 < 10秒 排除了过大的模型;预算有限排除了最贵的选项。
推荐方案:主模型用 Claude 3.5 Sonnet(200K窗口,代码理解强,延迟适中),辅以 DeepSeek V3 处理简单文件(降低成本)。实现一个简单的路由策略------文件复杂度低用 DeepSeek,复杂度高的核心逻辑用 Claude。预估成本:一个中型仓库审查约 $0.5-2。
python
# 代码审查 Agent 的模型路由策略
from dataclasses import dataclass
from typing import Literal
@dataclass
class ModelRouteConfig:
"""模型路由配置:根据文件复杂度选择模型"""
# 简单文件:配置文件、测试mock、文档注释
simple_model: str = "deepseek-v3"
simple_max_tokens: int = 4096
simple_threshold_lines: int = 100 # 100行以下走简单模型
# 复杂文件:核心业务逻辑、算法实现
complex_model: str = "claude-3-5-sonnet"
complex_max_tokens: int = 8192
# 判断逻辑
def select_model(self, file_path: str, content: str) -> tuple[str, int]:
"""
根据文件路径和内容特征选择模型
返回: (model_name, max_tokens)
"""
lines = content.count('\n')
# 简单文件判定
is_config = any(file_path.endswith(ext) for ext in ['.json', '.yaml', '.yml', '.toml'])
is_test = 'test' in file_path.lower() or 'mock' in file_path.lower()
is_short = lines < self.simple_threshold_lines
if is_config or is_test or is_short:
return self.simple_model, self.simple_max_tokens
# 复杂文件:核心业务逻辑
return self.complex_model, self.complex_max_tokens
# 使用示例
router = ModelRouteConfig()
model, max_tokens = router.select_model("src/auth/handler.py", complex_code_content)
print(f"选用模型: {model}, 最大Token: {max_tokens}")
代码解释: 这段代码实现了一个代码审查 Agent 的模型路由策略。
ModelRouteConfig类封装了简单模型和复杂模型的配置,select_model方法根据文件类型、行数和路径特征自动选择最合适的模型。配置文件和测试文件走低成本的 DeepSeek V3,核心业务逻辑文件走代码理解更强的 Claude 3.5 Sonnet。这种路由策略可以在保证审查质量的同时降低约 40-60% 的 API 成本。
场景题 2:Function Calling 死循环
题目: 你的 Agent 在调用某个工具后,收到了错误返回,但不断重复调用同一个工具,进入死循环。如何排查和修复?
参考答案思路:
排查步骤:1)检查工具返回的错误信息是否清晰,模型可能因为不理解错误而重试;2)检查是否设置了最大迭代次数限制;3)检查 System Prompt 是否有错误处理指引。
修复方案:设置硬性的最大迭代次数(如10次);在 System Prompt 中明确错误处理策略(如"同一工具连续失败2次后,向用户报告而非重试");为错误返回添加结构化的错误码和建议的替代方案。
python
# Agent 循环控制与防死循环机制
import json
from collections import defaultdict
class AgentLoopGuard:
"""Agent循环守卫,防止工具调用死循环"""
def __init__(self, max_iterations: int = 10, max_same_tool_calls: int = 3):
self.max_iterations = max_iterations
self.max_same_tool_calls = max_same_tool_calls
self.iteration_count = 0
self.tool_call_history: list[str] = []
self.consecutive_same_tool = defaultdict(int)
def check_before_call(self, tool_name: str) -> tuple[bool, str]:
"""
在工具调用前检查是否允许执行
返回: (允许调用, 原因)
"""
self.iteration_count += 1
# 检查1:总迭代次数
if self.iteration_count > self.max_iterations:
return False, f"已达到最大迭代次数 {self.max_iterations}"
# 检查2:连续调用同一工具的次数
if self.tool_call_history and self.tool_call_history[-1] == tool_name:
self.consecutive_same_tool[tool_name] += 1
else:
self.consecutive_same_tool[tool_name] = 1
if self.consecutive_same_tool[tool_name] > self.max_same_tool_calls:
return False, (
f"工具 {tool_name} 已连续调用 "
f"{self.consecutive_same_tool[tool_name]} 次,疑似死循环"
)
self.tool_call_history.append(tool_name)
return True, "OK"
def build_error_context(self) -> str:
"""构建错误上下文,注入到System Prompt中"""
return (
f"当前迭代: {self.iteration_count}/{self.max_iterations}\n"
f"工具调用历史: {self.tool_call_history[-5:]}\n"
f"如果工具连续失败,请换一种方法或向用户报告问题。"
)
# 使用示例
guard = AgentLoopGuard(max_iterations=10, max_same_tool_calls=3)
# 在Agent循环中:
# allowed, reason = guard.check_before_call("search_web")
# if not allowed:
# return {"error": "loop_detected", "message": reason}
代码解释:
AgentLoopGuard实现了双重防护机制:第一重是总迭代次数限制(默认10次),防止 Agent 无限运行;第二重是连续同一工具调用次数限制(默认3次),专门针对"反复调用同一工具"的死循环模式。build_error_context方法将当前状态注入 System Prompt,让 LLM 感知自己正在重复,从而选择不同策略。这是一个生产环境中非常实用的防御性编程模式。
场景题 3:上下文窗口溢出
题目: 你的 Agent 在执行一个需要多轮工具调用的复杂任务时,上下文很快超过模型的窗口限制。如何设计上下文管理策略?
参考答案思路:
分层策略:1)System Prompt 保持精简,只保留核心指令;2)工具返回结果即时压缩------长结果提取摘要后替换原文;3)对话历史使用"首尾保留+中间摘要"策略;4)实现一个 Context Budget Manager,为不同类型的内容分配 Token 预算。
python
# 上下文预算管理器
import tiktoken
class ContextBudgetManager:
"""管理Agent上下文的Token预算分配"""
def __init__(self, model: str = "gpt-4o", total_budget: int = 128000):
self.encoder = tiktoken.encoding_for_model(model)
self.total_budget = total_budget
# 预算分配比例
self.budgets = {
"system_prompt": int(total_budget * 0.10), # 10% 给系统提示
"user_input": int(total_budget * 0.05), # 5% 给用户输入
"tool_results": int(total_budget * 0.40), # 40% 给工具结果
"conversation_history": int(total_budget * 0.35), # 35% 给对话历史
"reasoning_buffer": int(total_budget * 0.10), # 10% 给推理输出
}
def count_tokens(self, text: str) -> int:
"""计算文本的Token数"""
return len(self.encoder.encode(text))
def truncate_tool_result(self, result: str, tool_name: str) -> str:
"""截断工具返回结果,保留关键信息"""
budget = self.budgets["tool_results"]
tokens = self.count_tokens(result)
if tokens <= budget:
return result
# 策略:保留头部和尾部,中间用摘要替代
head_ratio = 0.3
tail_ratio = 0.3
head_tokens = int(budget * head_ratio)
tail_tokens = int(budget * tail_ratio)
# 简化实现:按字符截断(实际应按Token截断)
chars_per_token = len(result) / tokens
head_chars = int(head_tokens * chars_per_token)
tail_chars = int(tail_tokens * chars_per_token)
head = result[:head_chars]
tail = result[-tail_chars:]
summary = f"\n[...中间部分已省略,原始长度 {tokens} tokens...]\n"
return f"{head}{summary}{tail}"
def compress_history(self, messages: list[dict]) -> list[dict]:
"""压缩对话历史:首尾保留,中间摘要"""
budget = self.budgets["conversation_history"]
if not messages:
return messages
total_tokens = sum(self.count_tokens(m.get("content", "")) for m in messages)
if total_tokens <= budget:
return messages
# 保留最近5轮和最早1轮
keep_recent = 5
keep_earliest = 1
if len(messages) <= keep_recent + keep_earliest:
return messages # 消息太少,不需要压缩
earliest = messages[:keep_earliest]
middle = messages[keep_earliest:-keep_recent]
recent = messages[-keep_recent:]
# 将中间部分压缩为摘要
middle_text = " ".join(m.get("content", "") for m in middle)
summary = f"[历史摘要: 之前进行了 {len(middle)} 轮对话,主要内容涉及: {middle_text[:200]}...]"
return earliest + [{"role": "system", "content": summary}] + recent
# 使用示例
manager = ContextBudgetManager(model="gpt-4o", total_budget=128000)
# 在Agent循环中:
# compressed_result = manager.truncate_tool_result(long_tool_output, "search")
# compressed_history = manager.compress_history(conversation_messages)
代码解释:
ContextBudgetManager实现了上下文 Token 的预算管理。核心设计是按比例分配 Token 预算------系统提示10%、用户输入5%、工具结果40%、对话历史35%、推理输出10%。truncate_tool_result方法对超长的工具返回结果采用"头尾保留+中间省略"的策略压缩。compress_history方法对对话历史做"首尾保留+中间摘要"处理,保留最早的1轮(设定基调)和最近的5轮(保持上下文连续性),中间部分压缩为摘要。这种策略在实践中能将上下文压缩到原来的30-40%而保持关键信息不丢失。
场景题 4:MCP Server 设计
题目: 需要设计一个文件系统操作的 MCP Server,支持读写、搜索文件。如何设计工具集?
参考答案思路:
遵循"高内聚低耦合"原则,不要把所有操作塞到一个工具里。设计5个工具:read_file(读单个文件)、write_file(写文件)、list_directory(列目录)、search_files(按名称搜索)、grep_content(按内容搜索)。每个工具描述要清晰,参数要有类型约束和默认值。
python
# 文件系统 MCP Server(精简版)
from mcp.server import Server
from mcp.types import Tool, TextContent
import os
import fnmatch
import re
import pathlib
app = Server("filesystem-server")
# 定义工具集------5个高内聚工具覆盖80%文件操作需求
TOOLS = [
Tool(
name="read_file",
description="读取指定路径文件的内容。返回文本内容,文件不存在返回错误。",
inputSchema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "文件路径,支持相对路径和绝对路径"
},
"encoding": {
"type": "string",
"description": "文件编码,默认utf-8",
"default": "utf-8"
}
},
"required": ["path"]
}
),
Tool(
name="write_file",
description="将内容写入指定路径文件。文件已存在则覆盖。",
inputSchema={
"type": "object",
"properties": {
"path": {"type": "string", "description": "文件路径"},
"content": {"type": "string", "description": "要写入的内容"}
},
"required": ["path", "content"]
}
),
Tool(
name="list_directory",
description="列出指定目录下的文件和子目录。",
inputSchema={
"type": "object",
"properties": {
"path": {"type": "string", "description": "目录路径", "default": "."},
"pattern": {"type": "string", "description": "文件名过滤模式,如 *.py", "default": "*"}
}
}
),
Tool(
name="search_files",
description="在指定目录下按文件名模式搜索文件。",
inputSchema={
"type": "object",
"properties": {
"directory": {"type": "string", "description": "搜索根目录"},
"pattern": {"type": "string", "description": "文件名模式,如 *.py"},
"recursive": {"type": "boolean", "description": "是否递归搜索", "default": True}
},
"required": ["directory", "pattern"]
}
),
Tool(
name="grep_content",
description="在文件内容中搜索匹配的行。支持正则表达式。",
inputSchema={
"type": "object",
"properties": {
"path": {"type": "string", "description": "文件或目录路径"},
"pattern": {"type": "string", "description": "正则表达式模式"},
"case_insensitive": {"type": "boolean", "description": "是否忽略大小写", "default": False}
},
"required": ["path", "pattern"]
}
),
]
async def handle_read_file(path: str, encoding: str = "utf-8") -> str:
"""处理文件读取"""
try:
file_path = pathlib.Path(path).resolve()
# 安全检查:防止目录穿越攻击
if not str(file_path).startswith(str(pathlib.Path.cwd())):
return f"错误: 路径超出允许范围"
content = file_path.read_text(encoding=encoding)
# 限制返回长度,防止上下文溢出
max_chars = 10000
if len(content) > max_chars:
content = content[:max_chars] + f"\n\n[...文件过长,已截断,共 {len(content)} 字符...]"
return content
except FileNotFoundError:
return f"错误: 文件 {path} 不存在"
except Exception as e:
return f"错误: {str(e)}"
# 注册工具处理器
@app.list_tools()
async def list_tools() -> list[Tool]:
return TOOLS
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""统一工具调用入口"""
if name == "read_file":
result = await handle_read_file(**arguments)
elif name == "write_file":
pathlib.Path(arguments["path"]).write_text(arguments["content"])
result = f"成功写入 {len(arguments['content'])} 字符到 {arguments['path']}"
elif name == "list_directory":
entries = os.listdir(arguments.get("path", "."))
pattern = arguments.get("pattern", "*")
filtered = [e for e in entries if fnmatch.fnmatch(e, pattern)]
result = "\n".join(filtered)
elif name == "search_files":
root = pathlib.Path(arguments["directory"])
pattern = arguments["pattern"]
recursive = arguments.get("recursive", True)
matches = list(root.rglob(pattern) if recursive else root.glob(pattern))
result = "\n".join(str(m) for m in matches[:100]) # 限制结果数量
elif name == "grep_content":
flags = re.IGNORECASE if arguments.get("case_insensitive") else 0
pattern = re.compile(arguments["pattern"], flags)
target = pathlib.Path(arguments["path"])
if target.is_file():
lines = target.read_text().splitlines()
matches = [f"{i+1}: {line}" for i, line in enumerate(lines) if pattern.search(line)]
result = "\n".join(matches[:50])
else:
result = "暂不支持目录级grep,请指定具体文件"
else:
result = f"未知工具: {name}"
return [TextContent(type="text", text=result)]
代码解释: 这个 MCP Server 设计了5个高内聚的工具来覆盖文件系统的核心操作。关键设计决策包括:1)
read_file做了路径安全检查防止目录穿越攻击,并限制了返回长度防止上下文溢出;2)search_files限制了结果数量为100条,避免返回过多数据;3)grep_content限制匹配结果为50行并带行号输出;4)所有工具的description都包含了行为说明和错误情况描述,帮助 LLM 正确选择和使用工具。统一的call_tool入口简化了工具注册和管理。
场景题 5:记忆系统设计
题目: 为一个客服 Agent 设计记忆系统,需要记住用户的偏好和历史问题,但不能无限存储。如何设计?
参考答案思路:
三层记忆架构:1)短期记忆------当前对话的最近10轮;2)工作记忆------当前会话的关键实体和状态(如用户ID、当前问题类别);3)长期记忆------跨会话的用户画像,存储在向量数据库中。写入策略:短期记忆自动写入,长期记忆只在出现新的偏好或重要信息时写入(通过 LLM 判断"是否值得记住")。
#mermaid-svg-KQ3jfcGjGvkMOfX5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KQ3jfcGjGvkMOfX5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .error-icon{fill:#552222;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .marker.cross{stroke:#333333;}#mermaid-svg-KQ3jfcGjGvkMOfX5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KQ3jfcGjGvkMOfX5 p{margin:0;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .cluster-label text{fill:#333;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .cluster-label span{color:#333;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .cluster-label span p{background-color:transparent;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .label text,#mermaid-svg-KQ3jfcGjGvkMOfX5 span{fill:#333;color:#333;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .node rect,#mermaid-svg-KQ3jfcGjGvkMOfX5 .node circle,#mermaid-svg-KQ3jfcGjGvkMOfX5 .node ellipse,#mermaid-svg-KQ3jfcGjGvkMOfX5 .node polygon,#mermaid-svg-KQ3jfcGjGvkMOfX5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .rough-node .label text,#mermaid-svg-KQ3jfcGjGvkMOfX5 .node .label text,#mermaid-svg-KQ3jfcGjGvkMOfX5 .image-shape .label,#mermaid-svg-KQ3jfcGjGvkMOfX5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .rough-node .label,#mermaid-svg-KQ3jfcGjGvkMOfX5 .node .label,#mermaid-svg-KQ3jfcGjGvkMOfX5 .image-shape .label,#mermaid-svg-KQ3jfcGjGvkMOfX5 .icon-shape .label{text-align:center;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .node.clickable{cursor:pointer;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .arrowheadPath{fill:#333333;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KQ3jfcGjGvkMOfX5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KQ3jfcGjGvkMOfX5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KQ3jfcGjGvkMOfX5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .cluster text{fill:#333;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .cluster span{color:#333;}#mermaid-svg-KQ3jfcGjGvkMOfX5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KQ3jfcGjGvkMOfX5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .icon-shape,#mermaid-svg-KQ3jfcGjGvkMOfX5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .icon-shape p,#mermaid-svg-KQ3jfcGjGvkMOfX5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .icon-shape .label rect,#mermaid-svg-KQ3jfcGjGvkMOfX5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KQ3jfcGjGvkMOfX5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KQ3jfcGjGvkMOfX5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KQ3jfcGjGvkMOfX5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 记忆检索
长期记忆
记忆写入判断
用户对话
是
否
用户消息
短期记忆
最近10轮
工作记忆
关键实体/状态
LLM判断:
是否值得长期记住?
提取关键信息
丢弃
向量数据库
用户画像JSON
偏好/历史问题
相关性检索
增量更新
注入上下文
Agent回复
python
# 客服Agent记忆系统(精简实现)
import json
import time
from dataclasses import dataclass, field
@dataclass
class MemoryItem:
"""记忆条目"""
content: str
timestamp: float = field(default_factory=time.time)
importance: float = 0.0 # 0-1,由LLM评估
category: str = "general" # preference/issue/feedback/general
class ConversationMemory:
"""对话级记忆------短期记忆"""
def __init__(self, max_turns: int = 10):
self.max_turns = max_turns
self.messages: list[dict] = []
def add(self, role: str, content: str):
self.messages.append({"role": role, "content": content})
# 超出上限时移除最旧的消息
if len(self.messages) > self.max_turns * 2: # 用户+助手各算一轮
self.messages = self.messages[-self.max_turns * 2:]
def get_context(self) -> list[dict]:
return self.messages
class UserMemory:
"""用户级长期记忆"""
def __init__(self, user_id: str):
self.user_id = user_id
self.profile: dict = {
"preferences": [],
"common_issues": [],
"satisfaction_trend": []
}
self.episodic_memories: list[MemoryItem] = []
def should_remember(self, content: str) -> bool:
"""判断是否值得长期记住(简化版规则,生产环境用LLM判断)"""
# 规则1:包含偏好信息
preference_keywords = ["喜欢", "不喜欢", "希望", "不要", "prefer", "always", "never"]
if any(kw in content.lower() for kw in preference_keywords):
return True
# 规则2:重复出现的问题
for mem in self.episodic_memories:
if content[:50] in mem.content[:50]:
return True
# 规则3:包含明确反馈
feedback_keywords = ["满意", "不满意", "太慢了", "很好", "很差", "建议"]
if any(kw in content for kw in feedback_keywords):
return True
return False
def add_memory(self, content: str, category: str = "general"):
"""添加长期记忆"""
if self.should_remember(content):
importance = self._assess_importance(content)
item = MemoryItem(
content=content,
importance=importance,
category=category
)
self.episodic_memories.append(item)
self._update_profile(item)
# 限制长期记忆数量
if len(self.episodic_memories) > 100:
# 移除最旧且最不重要的
self.episodic_memories.sort(key=lambda x: (x.importance, x.timestamp), reverse=True)
self.episodic_memories = self.episodic_memories[:100]
def _assess_importance(self, content: str) -> float:
"""评估记忆重要性(简化版,生产环境用LLM)"""
score = 0.5 # 基础分
if any(kw in content for kw in ["喜欢", "不要", "always", "never"]):
score += 0.3 # 偏好类加分
if any(kw in content for kw in ["满意", "不满意", "建议"]):
score += 0.2 # 反馈类加分
return min(score, 1.0)
def _update_profile(self, item: MemoryItem):
"""增量更新用户画像"""
if item.category == "preference" and item.content not in self.profile["preferences"]:
self.profile["preferences"].append(item.content[:100])
elif item.category == "issue":
self.profile["common_issues"].append(item.content[:100])
def get_relevant_memories(self, query: str, top_k: int = 3) -> list[MemoryItem]:
"""检索相关记忆(简化版:基于关键词匹配,生产环境用向量检索)"""
scored = []
for mem in self.episodic_memories:
# 时间衰减因子:越新的记忆权重越高
age_hours = (time.time() - mem.timestamp) / 3600
recency_factor = 1.0 / (1.0 + age_hours * 0.01)
# 关键词匹配度(简化版)
query_words = set(query.lower())
mem_words = set(mem.content.lower())
overlap = len(query_words & mem_words) / max(len(query_words), 1)
score = mem.importance * 0.4 + overlap * 0.3 + recency_factor * 0.3
scored.append((mem, score))
scored.sort(key=lambda x: x[1], reverse=True)
return [item for item, _ in scored[:top_k]]
def build_context(self, query: str) -> str:
"""构建长期记忆上下文"""
relevant = self.get_relevant_memories(query)
if not relevant:
return ""
lines = ["[用户记忆]"]
if self.profile["preferences"]:
lines.append(f"偏好: {'; '.join(self.profile['preferences'][:3])}")
for mem in relevant:
lines.append(f"- {mem.content[:80]}")
return "\n".join(lines)
# 使用示例
short_term = ConversationMemory(max_turns=10)
long_term = UserMemory(user_id="user_123")
# 模拟对话
short_term.add("user", "我想查询我的订单状态")
short_term.add("assistant", "请提供您的订单号")
long_term.add_memory("用户偏好晚上8点后不被打扰", category="preference")
long_term.add_memory("用户多次遇到配送延迟问题", category="issue")
# 构建完整上下文
context = long_term.build_context("我的订单到哪了")
print(context)
# 输出会包含用户偏好和历史问题摘要
代码解释: 这套记忆系统实现了三层架构的核心逻辑。
ConversationMemory管理短期记忆(最近10轮对话),自动淘汰旧消息。UserMemory管理长期记忆,核心亮点有三个:1)should_remember方法通过关键词规则判断信息是否值得长期存储,避免无意义信息污染记忆库;2)get_relevant_memories方法融合了重要性(40%)、关键词匹配度(30%)和时间衰减(30%)三个因子做记忆检索,模拟人类"重要的、相关的、最近的更容易想起"的认知特性;3)_update_profile方法实现用户画像的增量更新,将零散记忆结构化为偏好和问题历史。生产环境中应将关键词匹配替换为向量检索,将规则判断替换为 LLM 评估。
场景题 6:RAG 管线优化
题目: 你的 RAG 系统检索结果准确率低,经常返回不相关的内容。如何优化?
参考答案思路:
问题通常出在三个环节:1)Chunk 切分不合理------按固定长度切分会打断语义;2)Embedding 模型与领域不匹配------通用模型在专业领域表现差;3)缺少 reranking------向量相似度高不等于语义相关。优化方案:语义切分 + 领域微调 Embedding + Cross-encoder Reranking。
python
# RAG 2.0 管线:语义切分 + 向量检索 + 重排序
from dataclasses import dataclass
from typing import Optional
import hashlib
@dataclass
class Chunk:
"""知识库文档块"""
content: str
metadata: dict # source, page, section等
chunk_id: str = ""
def __post_init__(self):
if not self.chunk_id:
self.chunk_id = hashlib.md5(self.content.encode()).hexdigest()[:12]
class SemanticChunker:
"""语义切分器:基于段落和句子的语义边界切分"""
def __init__(self, min_chunk_size: int = 200, max_chunk_size: int = 800):
self.min_chunk_size = min_chunk_size
self.max_chunk_size = max_chunk_size
def chunk_document(self, text: str, source: str = "") -> list[Chunk]:
"""
语义切分策略:
1. 先按段落切分(双换行)
2. 超长段落按句子进一步切分
3. 过短的段落与前一段合并
"""
paragraphs = [p.strip() for p in text.split("\n\n") if p.strip()]
chunks = []
buffer = ""
for para in paragraphs:
# 超长段落进一步切分
if len(para) > self.max_chunk_size:
if buffer:
chunks.append(self._make_chunk(buffer, source))
buffer = ""
sentences = self._split_sentences(para)
current = ""
for sent in sentences:
if len(current) + len(sent) > self.max_chunk_size:
if current:
chunks.append(self._make_chunk(current, source))
current = sent
else:
current += sent
if current:
buffer = current
# 短段落尝试合并
elif len(buffer) + len(para) > self.max_chunk_size:
chunks.append(self._make_chunk(buffer, source))
buffer = para
else:
buffer = buffer + "\n\n" + para if buffer else para
if buffer:
chunks.append(self._make_chunk(buffer, source))
return chunks
def _split_sentences(self, text: str) -> list[str]:
"""按句子切分(中英文混合)"""
import re
# 中英文句号、问号、感叹号
sentences = re.split(r'(?<=[。!?.!?])\s*', text)
return [s for s in sentences if s.strip()]
def _make_chunk(self, content: str, source: str) -> Chunk:
return Chunk(content=content.strip(), metadata={"source": source, "chars": len(content)})
class RAGRetriever:
"""RAG检索器:向量检索 + 重排序"""
def __init__(self, top_k_retrieval: int = 20, top_k_final: int = 5):
self.top_k_retrieval = top_k_retrieval # 向量检索返回数量
self.top_k_final = top_k_final # 重排序后保留数量
self.chunks: list[Chunk] = []
def add_documents(self, chunks: list[Chunk]):
"""添加文档块到知识库"""
self.chunks.extend(chunks)
def retrieve(self, query: str) -> list[tuple[Chunk, float]]:
"""检索:向量检索 + 重排序"""
# 第一步:粗排------向量检索(简化版用关键词匹配模拟)
rough_results = self._vector_search(query)
# 第二步:精排------Cross-encoder重排序
reranked = self._rerank(query, rough_results)
return reranked[:self.top_k_final]
def _vector_search(self, query: str) -> list[tuple[Chunk, float]]:
"""向量检索(简化版:关键词匹配,生产环境用embedding)"""
query_words = set(query.lower())
scored = []
for chunk in self.chunks:
chunk_words = set(chunk.content.lower())
overlap = len(query_words & chunk_words)
score = overlap / max(len(query_words), 1)
scored.append((chunk, score))
scored.sort(key=lambda x: x[1], reverse=True)
return scored[:self.top_k_retrieval]
def _rerank(self, query: str, results: list[tuple[Chunk, float]]) -> list[tuple[Chunk, float]]:
"""
重排序(简化版:综合多个信号)
生产环境用Cross-encoder模型(如bge-reranker-large)
"""
reranked = []
for chunk, vector_score in results:
# 信号1:向量相似度
s1 = vector_score
# 信号2:关键词覆盖度
query_terms = query.lower().split()
chunk_terms = chunk.content.lower()
coverage = sum(1 for t in query_terms if t in chunk_terms) / max(len(query_terms), 1)
s2 = coverage
# 信号3:位置权重(匹配在开头比在结尾更重要)
first_match_pos = min(
(chunk.content.lower().find(t) for t in query_terms if t in chunk.content.lower()),
default=len(chunk.content)
)
position_score = 1.0 - (first_match_pos / max(len(chunk.content), 1))
s3 = position_score
# 信号4:文档来源权重(可配置)
source_boost = chunk.metadata.get("source_boost", 1.0)
# 综合评分
final_score = (s1 * 0.3 + s2 * 0.3 + s3 * 0.2) * source_boost
reranked.append((chunk, final_score))
reranked.sort(key=lambda x: x[1], reverse=True)
return reranked
# 使用示例
chunker = SemanticChunker(min_chunk_size=200, max_chunk_size=800)
retriever = RAGRetriever(top_k_retrieval=20, top_k_final=5)
doc = "本文介绍了AI Agent的架构设计。Agent由感知、推理、行动三个模块组成。\n\nFunction Calling是Agent调用外部工具的标准方式。\n\nMCP协议标准化了模型与工具的连接。"
chunks = chunker.chunk_document(doc, source="architecture.md")
retriever.add_documents(chunks)
results = retriever.retrieve("Agent如何调用工具")
for chunk, score in results:
print(f"[{score:.2f}] {chunk.content[:60]}...")
代码解释: 这套 RAG 2.0 管线实现了两个关键优化。
SemanticChunker实现语义切分:先按段落(双换行)切分,超长段落进一步按中英文标点切分为句子,过短段落与前一段合并。这种策略避免了固定长度切分打断语义的问题。RAGRetriever实现两阶段检索:第一阶段粗排(向量检索,返回top-20),第二阶段精排(多信号重排序,返回top-5)。重排序融合了4个信号------向量相似度(30%)、关键词覆盖度(30%)、位置权重(20%)和来源权重(可配置),生产环境中应替换为 Cross-encoder 模型如bge-reranker-large。这种两阶段策略在保持检索速度的同时显著提升了结果准确率。
场景题 7:多工具并行调用
题目: 用户问"对比这三只股票的基本面和技术面",Agent 需要调用6个工具(3只股票 × 2个维度)。如何优化并行调用?
参考答案思路:
利用 Function Calling 的并行调用能力(OpenAI 和 Claude 都支持)。将查询拆分为6个独立的工具调用请求在一次 LLM 调用中发出。注意工具需要设计为无副作用的(只读操作)才能安全并行。
python
# 多工具并行调用编排器
import asyncio
from dataclasses import dataclass
from typing import Any, Callable
@dataclass
class ToolCallRequest:
"""工具调用请求"""
tool_name: str
arguments: dict
depends_on: list[str] = None # 依赖的其他工具结果
class ParallelToolOrchestrator:
"""并行工具调用编排器"""
def __init__(self, max_concurrent: int = 5):
self.max_concurrent = max_concurrent
self.semaphore = asyncio.Semaphore(max_concurrent)
self.tool_registry: dict[str, Callable] = {}
def register_tool(self, name: str, func: Callable):
"""注册工具函数"""
self.tool_registry[name] = func
async def execute_parallel(self, calls: list[ToolCallRequest]) -> dict[str, Any]:
"""
并行执行工具调用
支持:1.无依赖调用完全并行 2.有依赖调用等待前置完成
"""
results = {}
pending = list(calls)
running = {}
while pending or running:
# 启动所有可执行的调用(无依赖或依赖已完成)
ready = []
for call in pending[:]:
if call.depends_on is None or all(d in results for d in call.depends_on):
ready.append(call)
pending.remove(call)
# 并行启动就绪的调用
for call in ready:
if call.depends_on:
for dep in call.depends_on:
call.arguments[f"_dep_{dep}"] = results[dep]
task = asyncio.create_task(self._execute_single(call))
running[call.tool_name] = task
# 等待至少一个完成
if running:
done, _ = await asyncio.wait(
running.values(), return_when=asyncio.FIRST_COMPLETED
)
for task in done:
for name, t in list(running.items()):
if t == task:
results[name] = task.result()
del running[name]
break
return results
async def _execute_single(self, call: ToolCallRequest) -> Any:
"""执行单个工具调用(带并发限制和错误处理)"""
async with self.semaphore:
func = self.tool_registry[call.tool_name]
try:
clean_args = {k: v for k, v in call.arguments.items() if not k.startswith("_dep_")}
result = await func(**clean_args) if asyncio.iscoroutinefunction(func) else func(**clean_args)
return {"status": "success", "data": result}
except Exception as e:
return {"status": "error", "error": str(e)}
# 使用示例:股票对比查询
async def get_stock_fundamentals(symbol: str) -> dict:
"""获取股票基本面数据"""
await asyncio.sleep(0.5) # 模拟API调用
return {"symbol": symbol, "pe_ratio": 15.2, "revenue_growth": 0.12}
async def get_stock_technicals(symbol: str) -> dict:
"""获取股票技术面数据"""
await asyncio.sleep(0.5)
return {"symbol": symbol, "rsi": 45.3, "macd": "bullish"}
async def compare_stocks():
orchestrator = ParallelToolOrchestrator(max_concurrent=5)
orchestrator.register_tool("get_fundamentals", get_stock_fundamentals)
orchestrator.register_tool("get_technicals", get_stock_technicals)
stocks = ["AAPL", "GOOGL", "MSFT"]
calls = []
for stock in stocks:
calls.append(ToolCallRequest(
tool_name=f"fundamentals_{stock}",
arguments={"symbol": stock}
))
orchestrator.register_tool(f"fundamentals_{stock}", get_stock_fundamentals)
calls.append(ToolCallRequest(
tool_name=f"technicals_{stock}",
arguments={"symbol": stock}
))
orchestrator.register_tool(f"technicals_{stock}", get_stock_technicals)
results = await orchestrator.execute_parallel(calls)
return results
代码解释:
ParallelToolOrchestrator实现了支持依赖关系的并行工具调用编排。核心设计:1)asyncio.Semaphore限制最大并发数,防止过多并发请求压垮下游服务;2)depends_on字段支持工具间的依赖关系------有依赖的调用会等待前置完成后再执行,无依赖的调用完全并行;3)_execute_single方法统一处理错误,返回结构化的{"status": ..., "data": ...}结果。在股票对比场景中,6个独立查询可以完全并行执行,总耗时从串行的3秒降低到并行的约0.5秒,提升6倍。
场景题 8:流式输出中的 JSON 解析
题目: 你的 Agent 使用流式输出返回结构化 JSON,但流式传输过程中 JSON 不完整,如何处理?
参考答案思路:
实现一个流式 JSON 解析器,能够处理不完整的 JSON 片段。策略:1)使用增量 JSON 解析库(如 partial-json-parser);2)缓冲足够的内容后再开始解析;3)对最终结果做完整性校验。
python
# 流式JSON解析器
import json
import re
class StreamingJSONParser:
"""流式JSON解析器:处理不完整的JSON片段"""
def __init__(self):
self.buffer = ""
self.last_valid = None
def feed(self, chunk: str) -> dict | None:
"""
输入新的chunk,尝试返回最新的解析结果
对于不完整的JSON,返回尽可能解析的部分
"""
self.buffer += chunk
# 尝试完整解析
try:
result = json.loads(self.buffer)
self.last_valid = result
return result
except json.JSONDecodeError:
pass
# 尝试部分解析(修复不完整的JSON)
partial = self._try_partial_parse(self.buffer)
if partial is not None:
self.last_valid = partial
return partial
return self.last_valid # 返回上一次有效的结果
def _try_partial_parse(self, text: str) -> dict | None:
"""尝试解析不完整的JSON,逐步修复截断问题"""
s = text.strip()
if not s or s[0] not in '{[':
return None
# 计算未闭合的括号
brace_count = 0
bracket_count = 0
in_string = False
escape = False
for char in s:
if escape:
escape = False
continue
if char == '\\' and in_string:
escape = True
continue
if char == '"' and not escape:
in_string = not in_string
continue
if in_string:
continue
if char == '{':
brace_count += 1
elif char == '}':
brace_count -= 1
elif char == '[':
bracket_count += 1
elif char == ']':
bracket_count -= 1
# 修复截断的字符串
if in_string:
s += '"'
# 修复未闭合的括号
if brace_count > 0 or bracket_count > 0:
trimmed = self._trim_incomplete(s)
if trimmed != s:
s = trimmed
s += ']' * max(bracket_count, 0)
s += '}' * max(brace_count, 0)
try:
return json.loads(s)
except json.JSONDecodeError:
return None
def _trim_incomplete(self, s: str) -> str:
"""移除尾部不完整的键值对"""
s = re.sub(r':\s*$', '', s.rstrip())
s = re.sub(r',\s*"[^"]*"\s*:?\s*$', '', s.rstrip())
s = re.sub(r',\s*$', '', s.rstrip())
return s
def reset(self):
"""重置解析器状态"""
self.buffer = ""
self.last_valid = None
# 使用示例
parser = StreamingJSONParser()
chunks = [
'{"action": "search",',
' "query": "AI Agent 架',
'构设计", "paramet',
'ers": {"limit": 10, "filte',
'r": "recent"}, "resul',
't_format": "json"}'
]
print("流式解析过程:")
for i, chunk in enumerate(chunks):
result = parser.feed(chunk)
print(f" Chunk {i+1}: 收到 {len(chunk)} 字符 → {result}")
代码解释:
StreamingJSONParser解决了流式输出中 JSON 不完整的问题。核心方法_try_partial_parse实现了三层修复:1)计算未闭合的括号数量(区分字符串内外),自动补全闭合括号;2)修复截断的字符串(自动补全引号);3)_trim_incomplete方法移除尾部不完整的键值对。每收到一个 chunk 就调用feed方法,解析器返回当前能解析出的最完整结果。这对于实现"打字机效果"的实时 JSON 输出非常重要------用户可以在 Agent 完成全部输出前就看到部分结果。
场景题 9:Agent 自主决策失败恢复
题目: Agent 在执行多步任务时,第3步失败了。如何设计失败恢复机制?
参考答案思路:
设计三级恢复策略:1)自动重试(临时错误);2)换一种工具/方法(工具错误);3)回退到上一步重新规划(策略错误)。关键是让 Agent 感知到失败类型并选择合适的恢复策略。
python
# Agent失败恢复机制
from enum import Enum
from dataclasses import dataclass
from typing import Any, Optional
class FailureType(Enum):
"""失败类型分类"""
TRANSIENT = "transient" # 临时错误(网络超时、限流)
TOOL_ERROR = "tool_error" # 工具返回错误(参数错误、权限不足)
STRATEGY = "strategy" # 策略错误(方向走偏)
FATAL = "fatal" # 致命错误(无法恢复)
@dataclass
class ExecutionStep:
"""执行步骤"""
step_id: int
description: str
tool_name: str
arguments: dict
result: Any = None
status: str = "pending" # pending/running/success/failed
failure_type: Optional[FailureType] = None
retry_count: int = 0
class FailureRecoveryManager:
"""失败恢复管理器"""
MAX_RETRIES = {
FailureType.TRANSIENT: 3,
FailureType.TOOL_ERROR: 1,
FailureType.STRATEGY: 1,
FailureType.FATAL: 0,
}
RECOVERY_STRATEGY = {
FailureType.TRANSIENT: "retry_same",
FailureType.TOOL_ERROR: "retry_with_fix",
FailureType.STRATEGY: "replan_from_step",
FailureType.FATAL: "escalate_to_human",
}
def __init__(self):
self.steps: list[ExecutionStep] = []
self.current_step_idx = 0
def classify_failure(self, error: Exception, context: dict) -> FailureType:
"""分类失败类型"""
error_msg = str(error).lower()
# 临时错误:网络、超时、限流
if any(kw in error_msg for kw in ["timeout", "rate limit", "connection", "503", "429"]):
return FailureType.TRANSIENT
# 工具错误:参数、权限
if any(kw in error_msg for kw in ["invalid parameter", "permission", "not found", "400", "403"]):
return FailureType.TOOL_ERROR
# 策略错误:结果不符合预期
if "unexpected result" in error_msg or context.get("result_validation_failed"):
return FailureType.STRATEGY
# 其他视为致命错误
return FailureType.FATAL
def get_recovery_action(self, step: ExecutionStep) -> dict:
"""根据失败类型决定恢复动作"""
failure_type = step.failure_type
strategy = self.RECOVERY_STRATEGY.get(failure_type, "escalate_to_human")
max_retries = self.MAX_RETRIES.get(failure_type, 0)
if step.retry_count >= max_retries:
# 超过重试上限,升级恢复策略
if failure_type == FailureType.TRANSIENT:
strategy = "retry_with_fix"
elif failure_type == FailureType.TOOL_ERROR:
strategy = "replan_from_step"
elif failure_type == FailureType.STRATEGY:
strategy = "escalate_to_human"
return {
"strategy": strategy,
"retry_count": step.retry_count,
"max_retries": max_retries,
"action": self._build_action(strategy, step)
}
def _build_action(self, strategy: str, step: ExecutionStep) -> str:
"""构建恢复动作描述"""
if strategy == "retry_same":
return f"重试步骤 {step.step_id},使用相同参数"
elif strategy == "retry_with_fix":
return f"重试步骤 {step.step_id},修正参数后重试。建议:检查参数类型和值"
elif strategy == "replan_from_step":
last_success = self._find_last_success()
return f"回退到步骤 {last_success.step_id if last_success else 0},重新规划后续步骤"
elif strategy == "escalate_to_human":
return f"无法自动恢复,请求人工介入。步骤 {step.step_id} 失败原因:{step.result}"
return "未知策略"
def _find_last_success(self) -> Optional[ExecutionStep]:
"""找到最后一个成功的步骤"""
for step in reversed(self.steps[:self.current_step_idx]):
if step.status == "success":
return step
return None
def build_recovery_prompt(self, failed_step: ExecutionStep) -> str:
"""构建恢复Prompt,注入到Agent上下文"""
action = self.get_recovery_action(failed_step)
return (
f"## 执行失败恢复\n"
f"失败步骤: {failed_step.description}\n"
f"失败类型: {failed_step.failure_type.value}\n"
f"重试次数: {failed_step.retry_count}/{action['max_retries']}\n"
f"恢复策略: {action['strategy']}\n"
f"建议动作: {action['action']}\n\n"
f"请根据上述信息调整执行策略。"
)
# 使用示例
manager = FailureRecoveryManager()
step1 = ExecutionStep(1, "搜索相关文档", "search_docs", {"query": "AI Agent"})
step1.status = "success"
step1.result = {"count": 5, "docs": [...]}
manager.steps.append(step1)
step2 = ExecutionStep(2, "读取第一篇文档", "read_file", {"path": "doc1.md"})
step2.status = "success"
step2.result = "文档内容..."
manager.steps.append(step2)
step3 = ExecutionStep(3, "分析文档内容", "analyze", {"content": "文档内容..."})
step3.status = "failed"
step3.failure_type = manager.classify_failure(
Exception("timeout: API call exceeded 30 seconds"), {}
)
step3.retry_count = 0
manager.steps.append(step3)
manager.current_step_idx = 2
recovery_prompt = manager.build_recovery_prompt(step3)
print(recovery_prompt)
代码解释:
FailureRecoveryManager实现了 Agent 失败恢复的完整决策链。核心设计:1)classify_failure方法根据错误特征将失败分为四类------临时错误(自动重试3次)、工具错误(修正后重试1次)、策略错误(重新规划1次)、致命错误(直接升级人工);2)get_recovery_action实现了恢复策略的逐级升级------当重试次数超过上限时,自动升级到更高级别的恢复策略;3)build_recovery_prompt将恢复信息结构化注入 Agent 上下文,让 LLM 感知失败原因并按建议调整策略。_find_last_success方法找到最后一个成功的步骤作为回退点,避免从头开始。这种分级恢复机制在实际生产中能自动处理80%以上的临时故障。
场景题 10:多模态 Agent 设计
题目: 设计一个能同时处理文本和图片输入的客服 Agent,用户可能发送产品照片并问"这个怎么用"。如何设计?
参考答案思路:
关键设计:1)输入分类器------判断是否需要图片理解;2)图片理解 Prompt------结构化提取图片中的关键信息;3)融合策略------将图片理解结果与文本问题组合成统一的上下文。不要直接把图片和问题一起扔给多模态模型,而是先用结构化 Prompt 提取图片信息,再将结构化信息与文本问题一起交给 LLM 生成回复,这样可以降低 Token 消耗并提高可控性。
python
# 多模态客服Agent
from dataclasses import dataclass
from typing import Optional
@dataclass
class UserInput:
"""用户输入(多模态)"""
text: str
image_url: Optional[str] = None
image_description: Optional[str] = None # 由Agent填充
class MultimodalCustomerServiceAgent:
"""多模态客服Agent"""
IMAGE_ANALYSIS_PROMPT = """你是一个产品图片分析助手。请按以下结构分析图片:
## 图片分析结果
- 产品类型: [识别产品类型]
- 产品型号: [如果能识别型号]
- 可见特征: [列出可见的外部特征,如颜色、按钮、接口等]
- 状态: [全新/使用中/损坏]
- 可见问题: [如果有可见的损坏或异常]
只输出上述结构化信息,不要添加额外解释。"""
SERVICE_PROMPT = """你是一个专业的产品客服Agent。
当前用户信息:
- 用户问题: {user_question}
- 图片分析结果: {image_analysis}
- 用户历史: {user_history}
请基于以上信息回答用户问题。如果图片分析结果与用户问题相关,
请引用图片中的具体特征。如果不相关,忽略图片信息。"""
def __init__(self):
self.conversation_history: list[dict] = []
async def process_input(self, user_input: UserInput) -> str:
"""处理多模态输入"""
# 步骤1:如果有图片,先进行图片理解
if user_input.image_url:
user_input.image_description = await self._analyze_image(user_input.image_url)
# 步骤2:构建融合上下文
context = self._build_context(user_input)
# 步骤3:生成回复
response = await self._generate_response(context)
# 步骤4:更新对话历史
self._update_history(user_input, response)
return response
async def _analyze_image(self, image_url: str) -> str:
"""图片理解:使用多模态模型提取结构化信息"""
# 生产环境实现:
# response = await client.chat.completions.create(
# model="gpt-4o",
# messages=[
# {"role": "system", "content": self.IMAGE_ANALYSIS_PROMPT},
# {"role": "user", "content": [
# {"type": "text", "text": "请分析这张产品图片。"},
# {"type": "image_url", "image_url": {"url": image_url}}
# ]}
# ]
# )
# return response.choices[0].message.content
return """## 图片分析结果
- 产品类型: 智能音箱
- 产品型号: 可能是Xiaomi Sound Pro
- 可见特征: 圆柱形,黑色,顶部有触控面板,底部有LED灯带
- 状态: 使用中(已连接电源)
- 可见问题: 无明显损坏"""
def _build_context(self, user_input: UserInput) -> str:
"""构建融合上下文"""
history = self._get_compressed_history()
return self.SERVICE_PROMPT.format(
user_question=user_input.text,
image_analysis=user_input.image_description or "无图片",
user_history=history
)
async def _generate_response(self, context: str) -> str:
"""生成回复(调用LLM)"""
# 生产环境:调用LLM API
return f"基于图片分析结果和您的问题,以下是使用建议..."
def _get_compressed_history(self) -> str:
"""获取压缩后的对话历史"""
if not self.conversation_history:
return "无历史记录"
recent = self.conversation_history[-6:] # 最近3轮
return " | ".join(f"{m['role']}: {m['content'][:50]}" for m in recent)
def _update_history(self, user_input: UserInput, response: str):
"""更新对话历史"""
self.conversation_history.append({"role": "user", "content": user_input.text})
self.conversation_history.append({"role": "assistant", "content": response})
# 使用示例
agent = MultimodalCustomerServiceAgent()
user_input = UserInput(
text="这个怎么用?",
image_url="data:image/jpeg;base64,/9j/4AAQ..." # 用户上传的产品照片
)
# response = await agent.process_input(user_input)
# print(response)
代码解释: 这个多模态客服 Agent 的核心设计是"先理解再融合"的两阶段处理。
_analyze_image方法使用结构化 Prompt 从图片中提取产品类型、型号、可见特征等关键信息,而不是直接让多模态模型"看图回答问题"。这种设计有三个优势:1)结构化信息更易于后续 LLM 理解和推理;2)图片理解结果可以缓存,避免重复调用多模态 API;3)将图片理解和回复生成解耦,可以分别优化两个环节的 Prompt。_build_context方法将图片分析结果、用户问题和对话历史融合为统一的文本上下文,交给纯文本 LLM 生成最终回复,降低了 Token 消耗。
五、从技能到架构:核心技能篇如何衔接架构设计篇
掌握了06-19篇的14个核心技能后,下一站是什么?第二季将进入架构设计篇,从单技能实现走向系统级架构。以下是核心技能与架构设计的衔接路径:
5.1 技能到架构的映射
核心技能篇解决的是"如何实现一个功能"的问题,架构设计篇要解决的是"如何将多个功能组合成可靠系统"的问题。这个跨越需要完成三个转变:
第一个转变:从单工具到工具链。 第08-11篇讲了 Function Calling、MCP 协议和工具设计,但都是单个工具的视角。架构篇需要设计工具注册中心、工具发现机制、工具权限管理和工具调用编排------这是从"会用工具"到"管理工具生态"的跃迁。
第二个转变:从单轮到工作流。 第07篇的核心能力循环是感知→推理→行动,但在生产环境中,Agent 需要处理的是多步骤、有条件分支、有异常处理的工作流。LangGraph 的图结构编排、State Machine 模式的状态管理,都是从循环走向工作流的关键技术。
第三个转变:从单 Agent 到多 Agent。 第17篇的自主决策机制是单 Agent 内部的决策,架构篇需要处理多个 Agent 之间的协作模式------Leader-Worker、Pipeline、MapReduce、Supervisor 等模式,以及它们之间的通信协议和状态同步。
5.2 第二季内容预告
| 篇目 | 预计主题 | 衔接的前置技能 |
|---|---|---|
| 21 | Agent 架构模式总览 | 07 核心能力循环、17 自主决策 |
| 22 | 单 Agent 完整架构 | 05 从零搭建、14 上下文工程 |
| 23 | 多 Agent 协作模式 | 17 自主决策、11 工具设计 |
| 24 | Agent 工作流引擎 | 07 核心能力循环、13 质量保障 |
| 25 | Agent 状态管理 | 14 上下文工程、15 记忆系统 |
| 26 | Agent 监控与可观测性 | 13 质量保障、19 流式输出 |
| 27 | Agent 安全工程 | 11 工具设计、09 MCP协议 |
| 28 | Agent 性能优化 | 06 LLM选型、14 上下文工程 |
| 29 | Agent 部署与运维 | 10 MCP Server、19 流式输出 |
| 30 | 第一季总结与展望 | 全部 |

图:三阶段路线图:第一阶段"单技能实现"(独立技能点)→第二阶段"技能组合"(工具注册中心/工作流编排/状态管理)→第三阶段"系统架构"(多Agent协作/分布式部署/监控运维),箭头连接各阶段
六、第一季回顾:01-20篇完整认知框架
从第01篇到第20篇,我们完成了一个完整的 Agent 工程师认知框架构建。以下是第一季的全景回顾:
6.1 第一季三阶段划分
#mermaid-svg-y7tmtb933nPKeeD3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-y7tmtb933nPKeeD3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-y7tmtb933nPKeeD3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-y7tmtb933nPKeeD3 .error-icon{fill:#552222;}#mermaid-svg-y7tmtb933nPKeeD3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-y7tmtb933nPKeeD3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-y7tmtb933nPKeeD3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-y7tmtb933nPKeeD3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-y7tmtb933nPKeeD3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-y7tmtb933nPKeeD3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-y7tmtb933nPKeeD3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-y7tmtb933nPKeeD3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-y7tmtb933nPKeeD3 .marker.cross{stroke:#333333;}#mermaid-svg-y7tmtb933nPKeeD3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-y7tmtb933nPKeeD3 p{margin:0;}#mermaid-svg-y7tmtb933nPKeeD3 .mermaid-main-font{font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-y7tmtb933nPKeeD3 .exclude-range{fill:#eeeeee;}#mermaid-svg-y7tmtb933nPKeeD3 .section{stroke:none;opacity:0.2;}#mermaid-svg-y7tmtb933nPKeeD3 .section0{fill:rgba(102, 102, 255, 0.49);}#mermaid-svg-y7tmtb933nPKeeD3 .section2{fill:#fff400;}#mermaid-svg-y7tmtb933nPKeeD3 .section1,#mermaid-svg-y7tmtb933nPKeeD3 .section3{fill:white;opacity:0.2;}#mermaid-svg-y7tmtb933nPKeeD3 .sectionTitle0{fill:#333;}#mermaid-svg-y7tmtb933nPKeeD3 .sectionTitle1{fill:#333;}#mermaid-svg-y7tmtb933nPKeeD3 .sectionTitle2{fill:#333;}#mermaid-svg-y7tmtb933nPKeeD3 .sectionTitle3{fill:#333;}#mermaid-svg-y7tmtb933nPKeeD3 .sectionTitle{text-anchor:start;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-y7tmtb933nPKeeD3 .grid .tick{stroke:lightgrey;opacity:0.8;shape-rendering:crispEdges;}#mermaid-svg-y7tmtb933nPKeeD3 .grid .tick text{font-family:"trebuchet ms",verdana,arial,sans-serif;fill:#333;}#mermaid-svg-y7tmtb933nPKeeD3 .grid path{stroke-width:0;}#mermaid-svg-y7tmtb933nPKeeD3 .today{fill:none;stroke:red;stroke-width:2px;}#mermaid-svg-y7tmtb933nPKeeD3 .task{stroke-width:2;}#mermaid-svg-y7tmtb933nPKeeD3 .taskText{text-anchor:middle;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-y7tmtb933nPKeeD3 .taskTextOutsideRight{fill:black;text-anchor:start;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-y7tmtb933nPKeeD3 .taskTextOutsideLeft{fill:black;text-anchor:end;}#mermaid-svg-y7tmtb933nPKeeD3 .task.clickable{cursor:pointer;}#mermaid-svg-y7tmtb933nPKeeD3 .taskText.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-y7tmtb933nPKeeD3 .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-y7tmtb933nPKeeD3 .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-y7tmtb933nPKeeD3 .taskText0,#mermaid-svg-y7tmtb933nPKeeD3 .taskText1,#mermaid-svg-y7tmtb933nPKeeD3 .taskText2,#mermaid-svg-y7tmtb933nPKeeD3 .taskText3{fill:white;}#mermaid-svg-y7tmtb933nPKeeD3 .task0,#mermaid-svg-y7tmtb933nPKeeD3 .task1,#mermaid-svg-y7tmtb933nPKeeD3 .task2,#mermaid-svg-y7tmtb933nPKeeD3 .task3{fill:#8a90dd;stroke:#534fbc;}#mermaid-svg-y7tmtb933nPKeeD3 .taskTextOutside0,#mermaid-svg-y7tmtb933nPKeeD3 .taskTextOutside2{fill:black;}#mermaid-svg-y7tmtb933nPKeeD3 .taskTextOutside1,#mermaid-svg-y7tmtb933nPKeeD3 .taskTextOutside3{fill:black;}#mermaid-svg-y7tmtb933nPKeeD3 .active0,#mermaid-svg-y7tmtb933nPKeeD3 .active1,#mermaid-svg-y7tmtb933nPKeeD3 .active2,#mermaid-svg-y7tmtb933nPKeeD3 .active3{fill:#bfc7ff;stroke:#534fbc;}#mermaid-svg-y7tmtb933nPKeeD3 .activeText0,#mermaid-svg-y7tmtb933nPKeeD3 .activeText1,#mermaid-svg-y7tmtb933nPKeeD3 .activeText2,#mermaid-svg-y7tmtb933nPKeeD3 .activeText3{fill:black!important;}#mermaid-svg-y7tmtb933nPKeeD3 .done0,#mermaid-svg-y7tmtb933nPKeeD3 .done1,#mermaid-svg-y7tmtb933nPKeeD3 .done2,#mermaid-svg-y7tmtb933nPKeeD3 .done3{stroke:grey;fill:lightgrey;stroke-width:2;}#mermaid-svg-y7tmtb933nPKeeD3 .doneText0,#mermaid-svg-y7tmtb933nPKeeD3 .doneText1,#mermaid-svg-y7tmtb933nPKeeD3 .doneText2,#mermaid-svg-y7tmtb933nPKeeD3 .doneText3{fill:black!important;}#mermaid-svg-y7tmtb933nPKeeD3 .doneText0.taskTextOutsideLeft,#mermaid-svg-y7tmtb933nPKeeD3 .doneText0.taskTextOutsideRight,#mermaid-svg-y7tmtb933nPKeeD3 .doneText1.taskTextOutsideLeft,#mermaid-svg-y7tmtb933nPKeeD3 .doneText1.taskTextOutsideRight,#mermaid-svg-y7tmtb933nPKeeD3 .doneText2.taskTextOutsideLeft,#mermaid-svg-y7tmtb933nPKeeD3 .doneText2.taskTextOutsideRight,#mermaid-svg-y7tmtb933nPKeeD3 .doneText3.taskTextOutsideLeft,#mermaid-svg-y7tmtb933nPKeeD3 .doneText3.taskTextOutsideRight{fill:black!important;}#mermaid-svg-y7tmtb933nPKeeD3 .crit0,#mermaid-svg-y7tmtb933nPKeeD3 .crit1,#mermaid-svg-y7tmtb933nPKeeD3 .crit2,#mermaid-svg-y7tmtb933nPKeeD3 .crit3{stroke:#ff8888;fill:red;stroke-width:2;}#mermaid-svg-y7tmtb933nPKeeD3 .activeCrit0,#mermaid-svg-y7tmtb933nPKeeD3 .activeCrit1,#mermaid-svg-y7tmtb933nPKeeD3 .activeCrit2,#mermaid-svg-y7tmtb933nPKeeD3 .activeCrit3{stroke:#ff8888;fill:#bfc7ff;stroke-width:2;}#mermaid-svg-y7tmtb933nPKeeD3 .doneCrit0,#mermaid-svg-y7tmtb933nPKeeD3 .doneCrit1,#mermaid-svg-y7tmtb933nPKeeD3 .doneCrit2,#mermaid-svg-y7tmtb933nPKeeD3 .doneCrit3{stroke:#ff8888;fill:lightgrey;stroke-width:2;cursor:pointer;shape-rendering:crispEdges;}#mermaid-svg-y7tmtb933nPKeeD3 .milestone{transform:rotate(45deg) scale(0.8,0.8);}#mermaid-svg-y7tmtb933nPKeeD3 .milestoneText{font-style:italic;}#mermaid-svg-y7tmtb933nPKeeD3 .doneCritText0,#mermaid-svg-y7tmtb933nPKeeD3 .doneCritText1,#mermaid-svg-y7tmtb933nPKeeD3 .doneCritText2,#mermaid-svg-y7tmtb933nPKeeD3 .doneCritText3{fill:black!important;}#mermaid-svg-y7tmtb933nPKeeD3 .doneCritText0.taskTextOutsideLeft,#mermaid-svg-y7tmtb933nPKeeD3 .doneCritText0.taskTextOutsideRight,#mermaid-svg-y7tmtb933nPKeeD3 .doneCritText1.taskTextOutsideLeft,#mermaid-svg-y7tmtb933nPKeeD3 .doneCritText1.taskTextOutsideRight,#mermaid-svg-y7tmtb933nPKeeD3 .doneCritText2.taskTextOutsideLeft,#mermaid-svg-y7tmtb933nPKeeD3 .doneCritText2.taskTextOutsideRight,#mermaid-svg-y7tmtb933nPKeeD3 .doneCritText3.taskTextOutsideLeft,#mermaid-svg-y7tmtb933nPKeeD3 .doneCritText3.taskTextOutsideRight{fill:black!important;}#mermaid-svg-y7tmtb933nPKeeD3 .vert{stroke:navy;}#mermaid-svg-y7tmtb933nPKeeD3 .vertText{font-size:15px;text-anchor:middle;fill:navy!important;}#mermaid-svg-y7tmtb933nPKeeD3 .activeCritText0,#mermaid-svg-y7tmtb933nPKeeD3 .activeCritText1,#mermaid-svg-y7tmtb933nPKeeD3 .activeCritText2,#mermaid-svg-y7tmtb933nPKeeD3 .activeCritText3{fill:black!important;}#mermaid-svg-y7tmtb933nPKeeD3 .titleText{text-anchor:middle;font-size:18px;fill:#333;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-y7tmtb933nPKeeD3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 01月 01月 01月 01月 02月 02月 02月 02月 03月 01 范式跃迁 02 框架横评 03 三位一体架构 04 Agent Engineering 05 从零搭建 06 LLM选型 07 能力循环 08 Function Calling 09 MCP协议 10 MCP Server 11 工具设计 12 Prompt进阶 13 质量保障 14 上下文工程 15 记忆系统 16 RAG 2.0 17 自主决策 18 多模态 19 流式输出 20 阶段总结 第一阶段:认知奠基 第二阶段:核心技能 第三阶段:阶段总结 第一季文章规划时间线
6.2 三个阶段的设计逻辑
第一阶段(01-05):认知奠基。 回答"Agent 是什么"和"为什么要学"的问题。从范式跃迁的大背景出发,通过框架横评建立全局视野,拆解三位一体架构,区分 Prompt Engineering 和 Agent Engineering,最后用一个从零搭建的实例让读者获得手感。这个阶段的目标是让读者建立信心:我能搭建一个 Agent。
第二阶段(06-19):核心技能。 回答"Agent 怎么做"的问题。14篇文章覆盖五层技能栈,从基础层到交互层逐层递进。每篇文章都包含理论讲解、代码实现和实战要点,确保读者不仅能理解概念,还能动手实现。这个阶段的目标是让读者建立能力:我能做好一个 Agent。
第三阶段(20):阶段总结。 回答"我学到了什么程度"的问题。也就是本文------通过知识地图、检查清单和实战自测,帮助读者评估自己的掌握程度,找到薄弱环节,为第二季学习做好准备。
6.3 关键认知转变
第一季20篇文章试图帮助读者完成以下认知转变:
-
从 Chatbot 到 Agent:Agent 不是一个会聊天的机器人,而是一个能感知、推理、行动的自主系统。核心区别在于闭环------Agent 有目标导向的行为循环。
-
从 Prompt 到 Engineering:写好 Prompt 只是起点,Agent Engineering 涉及工具设计、上下文管理、质量控制、记忆架构等系统工程问题。Prompt 是重要的一环,但不是全部。
-
从单模型到多模型:没有万能模型,生产环境需要多模型路由------不同任务用不同模型,根据延迟、成本和能力三角做权衡。
-
从功能到质量:能跑通不等于能用好。输出质量保障需要四层防线,从输入校验到回归测试,形成完整的质量体系。
-
从无状态到有记忆:生产级 Agent 需要记忆系统。三层记忆架构(短期/工作/长期)模拟人类认知,让 Agent 具备跨会话连续性。
-
从检索到推理:RAG 2.0 不只是检索拼接,而是理解-推理-整合。知识增强的目标是让 Agent 具备推理能力,而不是简单的问答。
-
从单 Agent 到多 Agent:复杂任务需要多 Agent 协作。这是第二季的重点,但第一季的自主决策机制已经为此埋下伏笔。
七、适用边界与学习建议
7.1 本文的适用边界
本文总结的技能栈和检查清单基于以下前提条件:
-
技术栈前提:以 Python 为主,涉及 LangChain/LangGraph 生态、OpenAI/Claude API、MCP 协议。如果你使用 TypeScript 或其他语言,核心方法论通用,但具体 API 和工具链不同。
-
场景前提:面向 ToB 场景的 Agent 开发------客服、代码审查、数据分析、知识问答等。ToC 场景(如 AI 伴侣、游戏 NPC)的技能侧重点有所不同。
-
规模前提:单 Agent 或少量协作 Agent 的场景。大规模分布式 Agent 系统的架构设计在第二季展开。
-
模型前提:基于 2025-2026 年可用的主流 LLM。如果未来出现颠覆性的新模型范式(如真正的多模态原生模型),部分技能点可能需要更新。
7.2 学习路径建议
根据你的当前水平,推荐不同的学习路径:
路径一:零基础入门者
如果你刚开始接触 Agent 开发,建议:
- 先读 01-05 篇建立认知
- 跟着 05 篇动手搭建第一个 Agent
- 用本文的50个知识点清单逐项自测
- 标记未掌握的知识点,回到对应文章深入学习
- 完成本文10道场景题,对照参考答案
- 预估时间:2-3 个月
路径二:有一定经验的开发者
如果你已经做过简单的 Agent 项目,建议:
- 直接用本文的检查清单自测
- 重点学习标记为"⚠️ 部分掌握"的知识点
- 完成场景题,特别关注自己不擅长的方向
- 进入第二季架构设计篇
- 预估时间:2-4 周
路径三:资深工程师
如果你已有丰富的 Agent 开发经验,建议:
- 快速浏览本文的知识地图和技能矩阵
- 重点看场景题,挑战自己能否给出比参考答案更好的方案
- 评估团队能力,用技能矩阵做团队能力盘点
- 直接进入第二季
- 预估时间:1-2 天
7.3 实践建议
无论你处于哪个水平,以下实践建议都适用:
-
动手实现比阅读更重要。 每篇文章的代码示例都要自己跑一遍,修改参数观察变化。
-
构建自己的 Agent 项目。 选一个真实的业务场景,用学到的技能搭建一个完整的 Agent。实践中遇到的问题比文章中的场景题更有价值。
-
保持对新技术的好奇心。 Agent 领域技术迭代极快,本文的内容是基于2025-2026年的实践。关注新的模型、框架和协议,保持学习。
-
参与社区交流。 在实际工作中遇到的问题,往往可以通过社区讨论获得启发。分享你的实践经验,也从他人的经验中学习。
-
不要追求完美。 Agent 工程是实用工程,不是理论科学。能用、好用、可靠比理论完美更重要。先跑通,再优化。
八、总结
本文作为第一季核心技能阶段的收官总结,完成了一次系统性的知识盘点。我们从一个完整的五层技能栈知识地图出发,将14篇核心技能文章的知识点重新组织为基础层、工具层、控制层、认知层和交互层五个层次;提供了一份覆盖50个必须掌握知识点的检查清单,每个知识点都对应到具体的实践技能;设计了10道实战场景题,涵盖了模型选型、死循环防护、上下文管理、MCP Server 设计、记忆系统、RAG 优化、并行调用、流式 JSON 解析、失败恢复和多模态处理等真实工程问题。
核心技能篇的价值不在于教会你某个 API 怎么调用,而在于建立一套完整的 Agent 工程思维框架。这套框架的核心是:Agent 是一个需要系统工程方法论的复杂软件系统,不是简单的 Prompt 堆砌。从模型选型的成本/延迟/能力三角,到上下文工程的信息密度管理,再到记忆系统的三层架构,每一个环节都需要工程化的设计思维。
从技能到架构的跃迁是第二季的核心命题。当你已经掌握了单个技能的实现方法后,下一个挑战是如何将这些技能组合成一个可靠、可扩展、可维护的系统。多 Agent 协作、工作流引擎、状态管理、监控可观测性、安全工程------这些架构层面的课题,将在第二季架构设计篇中展开。
Agent 工程是一个快速演进的领域。本文的内容基于2025-2026年的实践,但核心的设计原则和工程方法论具有持久价值。无论技术如何迭代,对工具设计的高内聚低耦合追求、对上下文的信息密度管理意识、对质量保障的多层防线思维、对记忆系统的分层架构设计------这些工程思维不会过时。
希望这份总结能帮助你找到自己的技能短板,为下一步学习指明方向。第二季架构设计篇,我们不见不散。
参考资料
- 1 OpenAI. Function Calling Guide. https://platform.openai.com/docs/guides/function-calling
- 2 Anthropic. Claude MCP Specification. https://modelcontextprotocol.io/
- 3 LangChain. LangGraph Documentation. https://langchain-ai.github.io/langgraph/
- 4 Harrison Chase et al. ReAct: Synergizing Reasoning and Acting in Language Models. arXiv:2210.03629
- 5 Shunyu Yao et al. Tree of Thoughts: Deliberate Problem Solving with Large Language Models. arXiv:2305.10601
- 6 Yunzhi Yao et al. Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. arXiv:2005.11401
- 7 OpenAI. GPT-4o Technical Report. https://openai.com/index/hello-gpt-4o/
- 8 Anthropic. Claude 3.5 Sonnet Technical Report. https://www.anthropic.com/news/claude-3-5-sonnet
- 9 DeepSeek. DeepSeek V3 Technical Report. https://github.com/deepseek-ai/DeepSeek-V3
- 10 MCP Python SDK. https://github.com/modelcontextprotocol/python-sdk
- 11 BGE Reranker. https://huggingface.co/BAAI/bge-reranker-large
- 12 LangChain Memory. https://python.langchain.com/docs/modules/memory/