六层架构各层详解——接口层、LLM引擎层、编排引擎层、工具标准层、记忆层、多Agent层
内容:六层架构逐层深度解析,涵盖接口层(多模态输入/输出格式化/流式输出)、LLM引擎层(模型抽象/多厂商适配/成本控制)、编排引擎层(StateGraph/条件路由/循环控制/子图嵌套)、工具标准层(MCP协议/工具注册/沙箱执行)、记忆层(短期/长期/工作记忆/遗忘机制)、多Agent层(Agent注册/消息路由/任务分发/结果聚合/冲突解决)
文档结构导航
| 一 | 六层架构总览 |
| 二 | 与OSI七层模型的类比 |
| 三 | Layer 1 接口层详解 |
| 四 | Layer 2 LLM引擎层详解 |
| 五 | Layer 3 编排引擎层详解 |
| 六 | Layer 4 工具标准层详解 |
| 七 | Layer 5 记忆层详解 |
| 八 | Layer 6 多Agent层详解 |
| 九 | 各层数据流与调用关系 |
| 十 | 各层配置与YAML驱动 |
| 十一 | 各层性能瓶颈分析与优化 |
| 十二 | 各层安全设计 |
| 十三 | 各层可测试性设计 |
| 十四 | 与LangChain/LangGraph架构的对比 |
| 十五 | 通俗实例:智能旅行规划Agent |
| 十六 | 完整代码实现:各层最小可运行实现 |
| 十七 | Q&A常见问题 |
一、六层架构总览
1.1 架构全景图
Agent系统的六层架构是一个自底向上、职责分明的分层设计。每一层都有明确的输入/输出契约,层与层之间通过定义良好的接口通信,从而实现高内聚、低耦合的工程目标。
#mermaid-svg-JwTbNtK9wvNjVpKb{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-JwTbNtK9wvNjVpKb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JwTbNtK9wvNjVpKb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JwTbNtK9wvNjVpKb .error-icon{fill:#552222;}#mermaid-svg-JwTbNtK9wvNjVpKb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JwTbNtK9wvNjVpKb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JwTbNtK9wvNjVpKb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JwTbNtK9wvNjVpKb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JwTbNtK9wvNjVpKb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JwTbNtK9wvNjVpKb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JwTbNtK9wvNjVpKb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JwTbNtK9wvNjVpKb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JwTbNtK9wvNjVpKb .marker.cross{stroke:#333333;}#mermaid-svg-JwTbNtK9wvNjVpKb svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JwTbNtK9wvNjVpKb p{margin:0;}#mermaid-svg-JwTbNtK9wvNjVpKb .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-JwTbNtK9wvNjVpKb .cluster-label text{fill:#333;}#mermaid-svg-JwTbNtK9wvNjVpKb .cluster-label span{color:#333;}#mermaid-svg-JwTbNtK9wvNjVpKb .cluster-label span p{background-color:transparent;}#mermaid-svg-JwTbNtK9wvNjVpKb .label text,#mermaid-svg-JwTbNtK9wvNjVpKb span{fill:#333;color:#333;}#mermaid-svg-JwTbNtK9wvNjVpKb .node rect,#mermaid-svg-JwTbNtK9wvNjVpKb .node circle,#mermaid-svg-JwTbNtK9wvNjVpKb .node ellipse,#mermaid-svg-JwTbNtK9wvNjVpKb .node polygon,#mermaid-svg-JwTbNtK9wvNjVpKb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JwTbNtK9wvNjVpKb .rough-node .label text,#mermaid-svg-JwTbNtK9wvNjVpKb .node .label text,#mermaid-svg-JwTbNtK9wvNjVpKb .image-shape .label,#mermaid-svg-JwTbNtK9wvNjVpKb .icon-shape .label{text-anchor:middle;}#mermaid-svg-JwTbNtK9wvNjVpKb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JwTbNtK9wvNjVpKb .rough-node .label,#mermaid-svg-JwTbNtK9wvNjVpKb .node .label,#mermaid-svg-JwTbNtK9wvNjVpKb .image-shape .label,#mermaid-svg-JwTbNtK9wvNjVpKb .icon-shape .label{text-align:center;}#mermaid-svg-JwTbNtK9wvNjVpKb .node.clickable{cursor:pointer;}#mermaid-svg-JwTbNtK9wvNjVpKb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JwTbNtK9wvNjVpKb .arrowheadPath{fill:#333333;}#mermaid-svg-JwTbNtK9wvNjVpKb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JwTbNtK9wvNjVpKb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JwTbNtK9wvNjVpKb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JwTbNtK9wvNjVpKb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JwTbNtK9wvNjVpKb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JwTbNtK9wvNjVpKb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JwTbNtK9wvNjVpKb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JwTbNtK9wvNjVpKb .cluster text{fill:#333;}#mermaid-svg-JwTbNtK9wvNjVpKb .cluster span{color:#333;}#mermaid-svg-JwTbNtK9wvNjVpKb 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-JwTbNtK9wvNjVpKb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JwTbNtK9wvNjVpKb rect.text{fill:none;stroke-width:0;}#mermaid-svg-JwTbNtK9wvNjVpKb .icon-shape,#mermaid-svg-JwTbNtK9wvNjVpKb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JwTbNtK9wvNjVpKb .icon-shape p,#mermaid-svg-JwTbNtK9wvNjVpKb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JwTbNtK9wvNjVpKb .icon-shape .label rect,#mermaid-svg-JwTbNtK9wvNjVpKb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JwTbNtK9wvNjVpKb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JwTbNtK9wvNjVpKb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JwTbNtK9wvNjVpKb :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
六层架构全景
Layer 6: 多Agent层Agent注册 · 角色定义 · 消息路由 · 任务分发 · 结果聚合 · 冲突解决
Layer 1: 接口层用户输入解析 · 多模态输入 · 输出格式化 · 流式输出 · API/WS/CLI
Layer 2: LLM引擎层模型抽象 · 多厂商适配 · 流式调用 · 工具调用适配 · 成本控制
Layer 3: 编排引擎层StateGraph · 条件路由 · 循环控制 · 并行执行 · 子图嵌套 · 错误恢复
Layer 4: 工具标准层MCP协议 · 工具注册 · 工具发现 · 参数验证 · 沙箱执行 · 结果处理
Layer 5: 记忆层短期记忆 · 长期记忆 · 工作记忆 · 记忆检索 · 记忆压缩 · 遗忘机制
1.2 各层职责一句话总结
| Layer 1 | 接口层 | 接收用户输入,格式化Agent输出 | 酒店前台:接待客人、传递消息 |
| Layer 2 | LLM引擎层 | 管理LLM调用,适配多厂商模型 | 翻译官:把想法翻译成语言 |
| Layer 3 | 编排引擎层 | 控制Agent的思考-行动循环 | 酒店经理:协调各部门工作流程 |
| Layer 4 | 工具标准层 | 管理工具的注册、发现和执行 | 酒店工具间:统一管理所有工具 |
| Layer 5 | 记忆层 | 存储和检索Agent的记忆 | 酒店档案室:保存客人档案和历史 |
| Layer 6 | 多Agent层 | 协调多个Agent协作完成任务 | 酒店集团总部:管理多家酒店协作 |
1.3 请求穿过六层的完整流程
Layer 6 多Agent层Layer 5 记忆层Layer 4 工具标准层Layer 3 编排引擎层Layer 2 LLM引擎层Layer 1 接口层用户Layer 6 多Agent层Layer 5 记忆层Layer 4 工具标准层Layer 3 编排引擎层Layer 2 LLM引擎层Layer 1 接口层用户#mermaid-svg-ViGGxNCPtzZMSmvc{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-ViGGxNCPtzZMSmvc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ViGGxNCPtzZMSmvc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ViGGxNCPtzZMSmvc .error-icon{fill:#552222;}#mermaid-svg-ViGGxNCPtzZMSmvc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ViGGxNCPtzZMSmvc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ViGGxNCPtzZMSmvc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ViGGxNCPtzZMSmvc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ViGGxNCPtzZMSmvc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ViGGxNCPtzZMSmvc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ViGGxNCPtzZMSmvc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ViGGxNCPtzZMSmvc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ViGGxNCPtzZMSmvc .marker.cross{stroke:#333333;}#mermaid-svg-ViGGxNCPtzZMSmvc svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ViGGxNCPtzZMSmvc p{margin:0;}#mermaid-svg-ViGGxNCPtzZMSmvc .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ViGGxNCPtzZMSmvc text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ViGGxNCPtzZMSmvc .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ViGGxNCPtzZMSmvc .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ViGGxNCPtzZMSmvc .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ViGGxNCPtzZMSmvc .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ViGGxNCPtzZMSmvc #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ViGGxNCPtzZMSmvc .sequenceNumber{fill:white;}#mermaid-svg-ViGGxNCPtzZMSmvc #sequencenumber{fill:#333;}#mermaid-svg-ViGGxNCPtzZMSmvc #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ViGGxNCPtzZMSmvc .messageText{fill:#333;stroke:none;}#mermaid-svg-ViGGxNCPtzZMSmvc .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ViGGxNCPtzZMSmvc .labelText,#mermaid-svg-ViGGxNCPtzZMSmvc .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ViGGxNCPtzZMSmvc .loopText,#mermaid-svg-ViGGxNCPtzZMSmvc .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ViGGxNCPtzZMSmvc .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ViGGxNCPtzZMSmvc .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ViGGxNCPtzZMSmvc .noteText,#mermaid-svg-ViGGxNCPtzZMSmvc .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ViGGxNCPtzZMSmvc .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ViGGxNCPtzZMSmvc .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ViGGxNCPtzZMSmvc .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ViGGxNCPtzZMSmvc .actorPopupMenu{position:absolute;}#mermaid-svg-ViGGxNCPtzZMSmvc .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-ViGGxNCPtzZMSmvc .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ViGGxNCPtzZMSmvc .actor-man circle,#mermaid-svg-ViGGxNCPtzZMSmvc line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ViGGxNCPtzZMSmvc :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}多Agent层在需要时介入分发子任务给多个Agent发送请求(文本/语音/图片)输入解析与标准化查询相关记忆返回历史上下文传递标准化请求+上下文调用LLM进行推理返回推理结果(可能含工具调用)执行工具调用返回工具结果再次调用LLM(结合工具结果)返回最终回答存储本次交互记忆返回格式化结果流式输出回答分发子任务(可选)聚合子结果(可选)
1.4 层间通信契约
每一层都通过明确的接口与上下层通信,这种设计确保了层间替换的灵活性:
#mermaid-svg-gboouPx6gUmszq3j{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-gboouPx6gUmszq3j .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gboouPx6gUmszq3j .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gboouPx6gUmszq3j .error-icon{fill:#552222;}#mermaid-svg-gboouPx6gUmszq3j .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gboouPx6gUmszq3j .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gboouPx6gUmszq3j .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gboouPx6gUmszq3j .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gboouPx6gUmszq3j .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gboouPx6gUmszq3j .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gboouPx6gUmszq3j .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gboouPx6gUmszq3j .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gboouPx6gUmszq3j .marker.cross{stroke:#333333;}#mermaid-svg-gboouPx6gUmszq3j svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gboouPx6gUmszq3j p{margin:0;}#mermaid-svg-gboouPx6gUmszq3j .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-gboouPx6gUmszq3j .cluster-label text{fill:#333;}#mermaid-svg-gboouPx6gUmszq3j .cluster-label span{color:#333;}#mermaid-svg-gboouPx6gUmszq3j .cluster-label span p{background-color:transparent;}#mermaid-svg-gboouPx6gUmszq3j .label text,#mermaid-svg-gboouPx6gUmszq3j span{fill:#333;color:#333;}#mermaid-svg-gboouPx6gUmszq3j .node rect,#mermaid-svg-gboouPx6gUmszq3j .node circle,#mermaid-svg-gboouPx6gUmszq3j .node ellipse,#mermaid-svg-gboouPx6gUmszq3j .node polygon,#mermaid-svg-gboouPx6gUmszq3j .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gboouPx6gUmszq3j .rough-node .label text,#mermaid-svg-gboouPx6gUmszq3j .node .label text,#mermaid-svg-gboouPx6gUmszq3j .image-shape .label,#mermaid-svg-gboouPx6gUmszq3j .icon-shape .label{text-anchor:middle;}#mermaid-svg-gboouPx6gUmszq3j .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gboouPx6gUmszq3j .rough-node .label,#mermaid-svg-gboouPx6gUmszq3j .node .label,#mermaid-svg-gboouPx6gUmszq3j .image-shape .label,#mermaid-svg-gboouPx6gUmszq3j .icon-shape .label{text-align:center;}#mermaid-svg-gboouPx6gUmszq3j .node.clickable{cursor:pointer;}#mermaid-svg-gboouPx6gUmszq3j .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gboouPx6gUmszq3j .arrowheadPath{fill:#333333;}#mermaid-svg-gboouPx6gUmszq3j .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gboouPx6gUmszq3j .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gboouPx6gUmszq3j .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gboouPx6gUmszq3j .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gboouPx6gUmszq3j .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gboouPx6gUmszq3j .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gboouPx6gUmszq3j .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gboouPx6gUmszq3j .cluster text{fill:#333;}#mermaid-svg-gboouPx6gUmszq3j .cluster span{color:#333;}#mermaid-svg-gboouPx6gUmszq3j 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-gboouPx6gUmszq3j .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gboouPx6gUmszq3j rect.text{fill:none;stroke-width:0;}#mermaid-svg-gboouPx6gUmszq3j .icon-shape,#mermaid-svg-gboouPx6gUmszq3j .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gboouPx6gUmszq3j .icon-shape p,#mermaid-svg-gboouPx6gUmszq3j .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gboouPx6gUmszq3j .icon-shape .label rect,#mermaid-svg-gboouPx6gUmszq3j .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gboouPx6gUmszq3j .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gboouPx6gUmszq3j .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gboouPx6gUmszq3j :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
C1
C2
C3
C5
C6
层间通信契约
接口契约1: StandardRequest{input, context, metadata}
接口契约2: LLMRequest{messages, tools, model_config}
接口契约3: GraphState{messages, tool_results, memory}
接口契约4: ToolCall{tool_name, arguments, sandbox_id}
接口契约5: MemoryQuery{query, top_k, memory_type}
接口契约6: AgentMessage{from, to, content, task_id}
L1
L2
L3
L4
L5
L6
关键设计原则:层间只通过数据契约通信,不直接调用对方内部方法。这意味着任何一层都可以被独立替换——比如把LLM引擎层从OpenAI换成Anthropic,接口层和编排层完全不需要改动。
二、与OSI七层模型的类比
2.1 类比对照表
理解Agent六层架构的最佳方式,是将其与大家熟悉的OSI七层网络模型做类比。两者都是分层架构,每层解决特定层次的问题,层间通过标准协议通信。
| 7 | 应用层 | 用户接口、HTTP/FTP | Layer 1 接口层 | 用户输入解析、输出格式化 | 都是最接近用户的一层 |
| 6 | 表示层 | 数据格式转换、加密 | Layer 2 LLM引擎层 | 模型抽象、文本↔结构化转换 | 都负责数据的"翻译"和格式转换 |
| 5 | 会话层 | 会话管理、同步 | Layer 3 编排引擎层 | 状态管理、流程编排 | 都管理"对话"的生命周期和流程 |
| 4 | 传输层 | 端到端可靠传输 | Layer 4 工具标准层 | 工具调用、可靠执行 | 都负责"可靠地传递到目标" |
| 3 | 网络层 | 路由、寻址 | Layer 5 记忆层 | 记忆检索、上下文路由 | 都负责"找到正确的目标位置" |
| 2 | 数据链路层 | 帧封装、差错控制 | Layer 6 多Agent层 | Agent间通信、冲突解决 | 都处理"节点间通信和错误" |
| 1 | 物理层 | 比特传输 | (底层基础设施) | GPU/网络/存储硬件 | 都是物理硬件基础 |
2.2 类比的Mermaid对照图
#mermaid-svg-3R5GdpJE5NysyMCs{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-3R5GdpJE5NysyMCs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3R5GdpJE5NysyMCs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3R5GdpJE5NysyMCs .error-icon{fill:#552222;}#mermaid-svg-3R5GdpJE5NysyMCs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3R5GdpJE5NysyMCs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3R5GdpJE5NysyMCs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3R5GdpJE5NysyMCs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3R5GdpJE5NysyMCs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3R5GdpJE5NysyMCs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3R5GdpJE5NysyMCs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3R5GdpJE5NysyMCs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3R5GdpJE5NysyMCs .marker.cross{stroke:#333333;}#mermaid-svg-3R5GdpJE5NysyMCs svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3R5GdpJE5NysyMCs p{margin:0;}#mermaid-svg-3R5GdpJE5NysyMCs .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-3R5GdpJE5NysyMCs .cluster-label text{fill:#333;}#mermaid-svg-3R5GdpJE5NysyMCs .cluster-label span{color:#333;}#mermaid-svg-3R5GdpJE5NysyMCs .cluster-label span p{background-color:transparent;}#mermaid-svg-3R5GdpJE5NysyMCs .label text,#mermaid-svg-3R5GdpJE5NysyMCs span{fill:#333;color:#333;}#mermaid-svg-3R5GdpJE5NysyMCs .node rect,#mermaid-svg-3R5GdpJE5NysyMCs .node circle,#mermaid-svg-3R5GdpJE5NysyMCs .node ellipse,#mermaid-svg-3R5GdpJE5NysyMCs .node polygon,#mermaid-svg-3R5GdpJE5NysyMCs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3R5GdpJE5NysyMCs .rough-node .label text,#mermaid-svg-3R5GdpJE5NysyMCs .node .label text,#mermaid-svg-3R5GdpJE5NysyMCs .image-shape .label,#mermaid-svg-3R5GdpJE5NysyMCs .icon-shape .label{text-anchor:middle;}#mermaid-svg-3R5GdpJE5NysyMCs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3R5GdpJE5NysyMCs .rough-node .label,#mermaid-svg-3R5GdpJE5NysyMCs .node .label,#mermaid-svg-3R5GdpJE5NysyMCs .image-shape .label,#mermaid-svg-3R5GdpJE5NysyMCs .icon-shape .label{text-align:center;}#mermaid-svg-3R5GdpJE5NysyMCs .node.clickable{cursor:pointer;}#mermaid-svg-3R5GdpJE5NysyMCs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3R5GdpJE5NysyMCs .arrowheadPath{fill:#333333;}#mermaid-svg-3R5GdpJE5NysyMCs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3R5GdpJE5NysyMCs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3R5GdpJE5NysyMCs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3R5GdpJE5NysyMCs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3R5GdpJE5NysyMCs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3R5GdpJE5NysyMCs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3R5GdpJE5NysyMCs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3R5GdpJE5NysyMCs .cluster text{fill:#333;}#mermaid-svg-3R5GdpJE5NysyMCs .cluster span{color:#333;}#mermaid-svg-3R5GdpJE5NysyMCs 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-3R5GdpJE5NysyMCs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3R5GdpJE5NysyMCs rect.text{fill:none;stroke-width:0;}#mermaid-svg-3R5GdpJE5NysyMCs .icon-shape,#mermaid-svg-3R5GdpJE5NysyMCs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3R5GdpJE5NysyMCs .icon-shape p,#mermaid-svg-3R5GdpJE5NysyMCs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3R5GdpJE5NysyMCs .icon-shape .label rect,#mermaid-svg-3R5GdpJE5NysyMCs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3R5GdpJE5NysyMCs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3R5GdpJE5NysyMCs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3R5GdpJE5NysyMCs :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Agent六层架构
OSI七层模型
L7 应用层 – HTTP/FTP/SMTP
L6 表示层 – JPEG/ASCII/SSL
L5 会话层 – RPC/Socket
L4 传输层 – TCP/UDP
L3 网络层 – IP/路由
L2 数据链路层 – Ethernet/PPP
L1 物理层 – 光纤/电缆
L6 多Agent层 – 消息路由/任务分发
L5 记忆层 – 向量检索/记忆压缩
L4 工具标准层 – MCP/Function Calling
L3 编排引擎层 – StateGraph/条件路由
L2 LLM引擎层 – 模型抽象/流式调用
L1 接口层 – API/WebSocket/CLI
基础设施层GPU/网络/存储
2.3 类比的核心启示
从OSI模型类比中,我们可以得到几个关键设计启示:
启示一:分层解耦
OSI模型中,TCP层不需要知道应用层是HTTP还是FTP;同样,Agent六层架构中,LLM引擎层不需要知道接口层是API还是CLI。每层只关心自己的输入/输出契约。
启示二:协议标准化
OSI模型通过标准化协议(TCP/IP)实现了全球互联;Agent六层架构通过标准化接口契约(如MCP协议)实现了工具的即插即用。
启示三:逐层封装
OSI模型中,每一层都封装了下层的复杂性;Agent六层架构中,接口层封装了LLM引擎的复杂性,编排层封装了工具调用的复杂性,用户只看到最顶层简洁的交互界面。
通俗理解:就像你寄快递——你只需要把包裹交给快递员(接口层),快递公司内部怎么分拣(编排层)、用什么运输工具(工具层)、走哪条路线(记忆层),你完全不需要关心。六层架构就是让Agent系统像快递公司一样高效运转。
三、Layer 1 接口层详解
3.1 接口层的核心职责
接口层是Agent系统与外部世界的"大门"。它负责:
#mermaid-svg-NlR374plQeg78VAH{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-NlR374plQeg78VAH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NlR374plQeg78VAH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NlR374plQeg78VAH .error-icon{fill:#552222;}#mermaid-svg-NlR374plQeg78VAH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NlR374plQeg78VAH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NlR374plQeg78VAH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NlR374plQeg78VAH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NlR374plQeg78VAH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NlR374plQeg78VAH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NlR374plQeg78VAH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NlR374plQeg78VAH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NlR374plQeg78VAH .marker.cross{stroke:#333333;}#mermaid-svg-NlR374plQeg78VAH svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NlR374plQeg78VAH p{margin:0;}#mermaid-svg-NlR374plQeg78VAH .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-NlR374plQeg78VAH .cluster-label text{fill:#333;}#mermaid-svg-NlR374plQeg78VAH .cluster-label span{color:#333;}#mermaid-svg-NlR374plQeg78VAH .cluster-label span p{background-color:transparent;}#mermaid-svg-NlR374plQeg78VAH .label text,#mermaid-svg-NlR374plQeg78VAH span{fill:#333;color:#333;}#mermaid-svg-NlR374plQeg78VAH .node rect,#mermaid-svg-NlR374plQeg78VAH .node circle,#mermaid-svg-NlR374plQeg78VAH .node ellipse,#mermaid-svg-NlR374plQeg78VAH .node polygon,#mermaid-svg-NlR374plQeg78VAH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NlR374plQeg78VAH .rough-node .label text,#mermaid-svg-NlR374plQeg78VAH .node .label text,#mermaid-svg-NlR374plQeg78VAH .image-shape .label,#mermaid-svg-NlR374plQeg78VAH .icon-shape .label{text-anchor:middle;}#mermaid-svg-NlR374plQeg78VAH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NlR374plQeg78VAH .rough-node .label,#mermaid-svg-NlR374plQeg78VAH .node .label,#mermaid-svg-NlR374plQeg78VAH .image-shape .label,#mermaid-svg-NlR374plQeg78VAH .icon-shape .label{text-align:center;}#mermaid-svg-NlR374plQeg78VAH .node.clickable{cursor:pointer;}#mermaid-svg-NlR374plQeg78VAH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NlR374plQeg78VAH .arrowheadPath{fill:#333333;}#mermaid-svg-NlR374plQeg78VAH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NlR374plQeg78VAH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NlR374plQeg78VAH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NlR374plQeg78VAH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NlR374plQeg78VAH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NlR374plQeg78VAH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NlR374plQeg78VAH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NlR374plQeg78VAH .cluster text{fill:#333;}#mermaid-svg-NlR374plQeg78VAH .cluster span{color:#333;}#mermaid-svg-NlR374plQeg78VAH 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-NlR374plQeg78VAH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NlR374plQeg78VAH rect.text{fill:none;stroke-width:0;}#mermaid-svg-NlR374plQeg78VAH .icon-shape,#mermaid-svg-NlR374plQeg78VAH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NlR374plQeg78VAH .icon-shape p,#mermaid-svg-NlR374plQeg78VAH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NlR374plQeg78VAH .icon-shape .label rect,#mermaid-svg-NlR374plQeg78VAH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NlR374plQeg78VAH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NlR374plQeg78VAH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NlR374plQeg78VAH :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
输出端
接口层处理
输入端
文本输入
语音输入
图片输入
文件输入
输入解析器
标准化模块
请求路由器
认证鉴权
REST API
WebSocket
CLI终端
gRPC
3.2 用户输入解析
3.2.1 输入类型与处理策略
| 纯文本 | string | 直接传递,可选预处理 | “帮我订一张去北京的机票” |
| 语音 | audio/wav | ASR转文本 + 保留原始音频 | 语音说"今天天气怎么样" |
| 图片 | image/png, jpg | 描述生成 + OCR文字提取 | 上传发票图片要求报销 |
| 文件 | pdf, docx, csv | 内容提取 + 摘要生成 | 上传合同要求审阅 |
| 多模态组合 | mixed | 各模态分别处理后合并 | 文字+图片:“这个表格怎么填” |
3.2.2 输入解析代码实现
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any, Optional, List, Union
from enum import Enum
import base64
import json
class InputType(Enum):
"""输入类型枚举"""
TEXT = "text"
VOICE = "voice"
IMAGE = "image"
FILE = "file"
MULTIMODAL = "multimodal"
@dataclass
class StandardRequest:
"""标准化请求——所有输入类型最终都转换为这个格式"""
input_type: InputType
text_content: str = ""
image_contents: List[str] = field(default_factory=list) # base64编码
file_contents: List[dict] = field(default_factory=list) # {filename, content, type}
metadata: dict = field(default_factory=dict)
user_id: str = ""
session_id: str = ""
timestamp: str = ""
def to_llm_messages(self) –> List[dict]:
"""转换为LLM可理解的消息格式"""
messages = []
if self.text_content:
messages.append({
"role": "user",
"content": self.text_content
})
if self.image_contents:
for img_b64 in self.image_contents:
messages.append({
"role": "user",
"content": [
{"type": "text", "text": self.text_content},
{"type": "image_url", "image_url": {
"url": f"data:image/png;base64,{img_b64}"
}}
]
})
return messages
class InputParser(ABC):
"""输入解析器抽象基类"""
@abstractmethod
def parse(self, raw_input: Any) –> StandardRequest:
"""将原始输入解析为标准化请求"""
pass
class TextInputParser(InputParser):
"""文本输入解析器"""
def parse(self, raw_input: str) –> StandardRequest:
# 预处理:去除多余空白、全角转半角
cleaned = raw_input.strip()
cleaned = cleaned.replace("\\u3000", " ") # 全角空格转半角
return StandardRequest(
input_type=InputType.TEXT,
text_content=cleaned
)
class VoiceInputParser(InputParser):
"""语音输入解析器"""
def __init__(self, asr_engine=None):
self.asr_engine = asr_engine # ASR引擎(如Whisper)
def parse(self, raw_input: bytes) –> StandardRequest:
if self.asr_engine is None:
raise RuntimeError("ASR引擎未配置,无法解析语音输入")
# 语音转文本
text = self.asr_engine.transcribe(raw_input)
return StandardRequest(
input_type=InputType.VOICE,
text_content=text,
metadata={"original_audio_size": len(raw_input)}
)
class ImageInputParser(InputParser):
"""图片输入解析器"""
def parse(self, raw_input: bytes) –> StandardRequest:
# 转base64
img_b64 = base64.b64encode(raw_input).decode("utf-8")
return StandardRequest(
input_type=InputType.IMAGE,
image_contents=[img_b64],
metadata={"image_size": len(raw_input)}
)
class MultimodalInputParser(InputParser):
"""多模态输入解析器——组合多个解析器"""
def __init__(self):
self.text_parser = TextInputParser()
self.image_parser = ImageInputParser()
def parse(self, raw_input: dict) –> StandardRequest:
"""raw_input格式: {"text": "…", "images": [bytes], "files": […]}"""
text_content = ""
image_contents = []
file_contents = []
if "text" in raw_input:
req = self.text_parser.parse(raw_input["text"])
text_content = req.text_content
if "images" in raw_input:
for img in raw_input["images"]:
req = self.image_parser.parse(img)
image_contents.extend(req.image_contents)
if "files" in raw_input:
for f in raw_input["files"]:
file_contents.append({
"filename": f.get("name", "unknown"),
"content": f.get("content", ""),
"type": f.get("type", "unknown")
})
return StandardRequest(
input_type=InputType.MULTIMODAL,
text_content=text_content,
image_contents=image_contents,
file_contents=file_contents
)
通俗理解:输入解析器就像酒店前台的翻译服务——不管客人说中文、英文还是用手语,前台都能把客人的需求翻译成酒店内部统一的工作语言(标准化请求)。
3.3 多模态输入支持
#mermaid-svg-prr6tru9OBHAAdR7{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-prr6tru9OBHAAdR7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-prr6tru9OBHAAdR7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-prr6tru9OBHAAdR7 .error-icon{fill:#552222;}#mermaid-svg-prr6tru9OBHAAdR7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-prr6tru9OBHAAdR7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-prr6tru9OBHAAdR7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-prr6tru9OBHAAdR7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-prr6tru9OBHAAdR7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-prr6tru9OBHAAdR7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-prr6tru9OBHAAdR7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-prr6tru9OBHAAdR7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-prr6tru9OBHAAdR7 .marker.cross{stroke:#333333;}#mermaid-svg-prr6tru9OBHAAdR7 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-prr6tru9OBHAAdR7 p{margin:0;}#mermaid-svg-prr6tru9OBHAAdR7 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-prr6tru9OBHAAdR7 .cluster-label text{fill:#333;}#mermaid-svg-prr6tru9OBHAAdR7 .cluster-label span{color:#333;}#mermaid-svg-prr6tru9OBHAAdR7 .cluster-label span p{background-color:transparent;}#mermaid-svg-prr6tru9OBHAAdR7 .label text,#mermaid-svg-prr6tru9OBHAAdR7 span{fill:#333;color:#333;}#mermaid-svg-prr6tru9OBHAAdR7 .node rect,#mermaid-svg-prr6tru9OBHAAdR7 .node circle,#mermaid-svg-prr6tru9OBHAAdR7 .node ellipse,#mermaid-svg-prr6tru9OBHAAdR7 .node polygon,#mermaid-svg-prr6tru9OBHAAdR7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-prr6tru9OBHAAdR7 .rough-node .label text,#mermaid-svg-prr6tru9OBHAAdR7 .node .label text,#mermaid-svg-prr6tru9OBHAAdR7 .image-shape .label,#mermaid-svg-prr6tru9OBHAAdR7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-prr6tru9OBHAAdR7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-prr6tru9OBHAAdR7 .rough-node .label,#mermaid-svg-prr6tru9OBHAAdR7 .node .label,#mermaid-svg-prr6tru9OBHAAdR7 .image-shape .label,#mermaid-svg-prr6tru9OBHAAdR7 .icon-shape .label{text-align:center;}#mermaid-svg-prr6tru9OBHAAdR7 .node.clickable{cursor:pointer;}#mermaid-svg-prr6tru9OBHAAdR7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-prr6tru9OBHAAdR7 .arrowheadPath{fill:#333333;}#mermaid-svg-prr6tru9OBHAAdR7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-prr6tru9OBHAAdR7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-prr6tru9OBHAAdR7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-prr6tru9OBHAAdR7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-prr6tru9OBHAAdR7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-prr6tru9OBHAAdR7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-prr6tru9OBHAAdR7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-prr6tru9OBHAAdR7 .cluster text{fill:#333;}#mermaid-svg-prr6tru9OBHAAdR7 .cluster span{color:#333;}#mermaid-svg-prr6tru9OBHAAdR7 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-prr6tru9OBHAAdR7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-prr6tru9OBHAAdR7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-prr6tru9OBHAAdR7 .icon-shape,#mermaid-svg-prr6tru9OBHAAdR7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-prr6tru9OBHAAdR7 .icon-shape p,#mermaid-svg-prr6tru9OBHAAdR7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-prr6tru9OBHAAdR7 .icon-shape .label rect,#mermaid-svg-prr6tru9OBHAAdR7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-prr6tru9OBHAAdR7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-prr6tru9OBHAAdR7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-prr6tru9OBHAAdR7 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
多模态输入处理流水线
1. 输入类型识别自动检测输入是文本/语音/图片/文件
2. 模态分别处理语音→ASR转文本图片→描述生成+OCR文件→内容提取
3. 模态融合将各模态信息合并为统一上下文
4. 上下文增强补充元数据、时间、用户信息
5. 标准化输出生成StandardRequest对象
3.4 输出格式化
Agent的内部回答需要格式化为用户友好的形式。输出格式化包括:
| Markdown | API/Web响应 | 将LLM输出的Markdown直接渲染 | **加粗** → 加粗文本 |
| 纯文本 | CLI终端 | 去除Markdown标记,保留纯文本 | **加粗** → 加粗文本 |
| JSON | API调用 | 结构化JSON输出 | {"answer": "…", "confidence": 0.95} |
| SSE流 | 实时流式 | Server-Sent Events逐token推送 | data: {"token": "你"} |
| 语音 | 语音助手 | TTS合成语音 | 文本 → 音频流 |
3.5 流式输出机制
流式输出是提升用户体验的关键——用户不需要等待Agent完整思考完毕,而是看到回答像打字一样逐字出现。
import asyncio
from typing import AsyncGenerator, AsyncIterator
import json
class StreamOutputFormatter:
"""流式输出格式化器"""
@staticmethod
async def format_sse_stream(
token_stream: AsyncIterator[str],
metadata: dict = None
) –> AsyncGenerator[str, None]:
"""将token流格式化为SSE格式
Args:
token_stream: LLM引擎产生的token流
metadata: 附加元数据
Yields:
SSE格式的字符串
"""
# 发送开始事件
start_data = {"type": "start", "metadata": metadata or {}}
yield f"data: {json.dumps(start_data)}\\n\\n"
# 逐token发送
full_text = ""
async for token in token_stream:
full_text += token
chunk_data = {"type": "token", "content": token}
yield f"data: {json.dumps(chunk_data)}\\n\\n"
await asyncio.sleep(0) # 让出事件循环
# 发送结束事件
end_data = {"type": "end", "full_text": full_text, "total_length": len(full_text)}
yield f"data: {json.dumps(end_data)}\\n\\n"
@staticmethod
async def format_cli_stream(
token_stream: AsyncIterator[str]
) –> AsyncGenerator[str, None]:
"""CLI终端流式输出(直接打印字符)"""
async for token in token_stream:
yield token
# CLI模式下直接flush到终端
print(token, end="", flush=True)
await asyncio.sleep(0)
print() # 最后换行
class OutputFormatterFactory:
"""输出格式化器工厂"""
@staticmethod
def create(channel: str) –> 'StreamOutputFormatter':
if channel == "api":
return StreamOutputFormatter()
elif channel == "cli":
return StreamOutputFormatter()
elif channel == "websocket":
return StreamOutputFormatter()
else:
raise ValueError(f"不支持的输出通道: {channel}")
通俗理解:流式输出就像看直播——你不需要等整场直播录完再看录像,而是实时看到主播的每一个动作。Agent的流式输出让用户实时看到每一个token的生成。
3.6 多通道接入
#mermaid-svg-zAx2o1Au6nNO6yP5{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-zAx2o1Au6nNO6yP5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zAx2o1Au6nNO6yP5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zAx2o1Au6nNO6yP5 .error-icon{fill:#552222;}#mermaid-svg-zAx2o1Au6nNO6yP5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zAx2o1Au6nNO6yP5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zAx2o1Au6nNO6yP5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zAx2o1Au6nNO6yP5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zAx2o1Au6nNO6yP5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zAx2o1Au6nNO6yP5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zAx2o1Au6nNO6yP5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zAx2o1Au6nNO6yP5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zAx2o1Au6nNO6yP5 .marker.cross{stroke:#333333;}#mermaid-svg-zAx2o1Au6nNO6yP5 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zAx2o1Au6nNO6yP5 p{margin:0;}#mermaid-svg-zAx2o1Au6nNO6yP5 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-zAx2o1Au6nNO6yP5 .cluster-label text{fill:#333;}#mermaid-svg-zAx2o1Au6nNO6yP5 .cluster-label span{color:#333;}#mermaid-svg-zAx2o1Au6nNO6yP5 .cluster-label span p{background-color:transparent;}#mermaid-svg-zAx2o1Au6nNO6yP5 .label text,#mermaid-svg-zAx2o1Au6nNO6yP5 span{fill:#333;color:#333;}#mermaid-svg-zAx2o1Au6nNO6yP5 .node rect,#mermaid-svg-zAx2o1Au6nNO6yP5 .node circle,#mermaid-svg-zAx2o1Au6nNO6yP5 .node ellipse,#mermaid-svg-zAx2o1Au6nNO6yP5 .node polygon,#mermaid-svg-zAx2o1Au6nNO6yP5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zAx2o1Au6nNO6yP5 .rough-node .label text,#mermaid-svg-zAx2o1Au6nNO6yP5 .node .label text,#mermaid-svg-zAx2o1Au6nNO6yP5 .image-shape .label,#mermaid-svg-zAx2o1Au6nNO6yP5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-zAx2o1Au6nNO6yP5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zAx2o1Au6nNO6yP5 .rough-node .label,#mermaid-svg-zAx2o1Au6nNO6yP5 .node .label,#mermaid-svg-zAx2o1Au6nNO6yP5 .image-shape .label,#mermaid-svg-zAx2o1Au6nNO6yP5 .icon-shape .label{text-align:center;}#mermaid-svg-zAx2o1Au6nNO6yP5 .node.clickable{cursor:pointer;}#mermaid-svg-zAx2o1Au6nNO6yP5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zAx2o1Au6nNO6yP5 .arrowheadPath{fill:#333333;}#mermaid-svg-zAx2o1Au6nNO6yP5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zAx2o1Au6nNO6yP5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zAx2o1Au6nNO6yP5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zAx2o1Au6nNO6yP5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zAx2o1Au6nNO6yP5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zAx2o1Au6nNO6yP5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zAx2o1Au6nNO6yP5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zAx2o1Au6nNO6yP5 .cluster text{fill:#333;}#mermaid-svg-zAx2o1Au6nNO6yP5 .cluster span{color:#333;}#mermaid-svg-zAx2o1Au6nNO6yP5 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-zAx2o1Au6nNO6yP5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zAx2o1Au6nNO6yP5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-zAx2o1Au6nNO6yP5 .icon-shape,#mermaid-svg-zAx2o1Au6nNO6yP5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zAx2o1Au6nNO6yP5 .icon-shape p,#mermaid-svg-zAx2o1Au6nNO6yP5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zAx2o1Au6nNO6yP5 .icon-shape .label rect,#mermaid-svg-zAx2o1Au6nNO6yP5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zAx2o1Au6nNO6yP5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zAx2o1Au6nNO6yP5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zAx2o1Au6nNO6yP5 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
多通道接入架构
统一处理
接入网关
认证鉴权中间件
客户端
REST API GatewayFastAPI / Flask
WebSocket Gateway实时双向通信
CLI Gateway命令行交互
gRPC Gateway高性能RPC
限流中间件
输入解析器
请求路由器
→ 编排引擎层
3.7 接口层各通道对比
| REST API | HTTP/HTTPS | 中 | Web应用、第三方集成 | SSE | 标准化、易调试 | 无状态、需轮询 |
| WebSocket | WS/WSS | 低 | 实时聊天、协作编辑 | 原生双向流 | 全双工、低延迟 | 连接管理复杂 |
| CLI | stdin/stdout | 极低 | 开发调试、本地工具 | 直接输出 | 简单直接、无网络开销 | 无远程访问 |
| gRPC | HTTP/2 | 低 | 微服务间通信 | 双向流 | 高性能、强类型 | 浏览器不原生支持 |
3.8 接口层完整实现
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import StreamingResponse
from typing import Dict, List
import uuid
import asyncio
app = FastAPI(title="Agent Interface Layer")
class InterfaceLayer:
"""接口层完整实现——统一管理所有接入通道"""
def __init__(self):
self.input_parsers: Dict[InputType, InputParser] = {
InputType.TEXT: TextInputParser(),
InputType.VOICE: VoiceInputParser(),
InputType.IMAGE: ImageInputParser(),
InputType.MULTIMODAL: MultimodalInputParser(),
}
self.active_sessions: Dict[str, dict] = {}
async def process_request(
self,
raw_input: Any,
input_type: InputType,
channel: str = "api",
user_id: str = "",
session_id: str = ""
) –> dict:
"""处理请求的统一入口"""
# 1. 选择合适的解析器
parser = self.input_parsers.get(input_type)
if parser is None:
raise ValueError(f"不支持的输入类型: {input_type}")
# 2. 解析输入
standard_request = parser.parse(raw_input)
standard_request.user_id = user_id
standard_request.session_id = session_id or str(uuid.uuid4())
# 3. 记录会话
self.active_sessions[standard_request.session_id] = {
"user_id": user_id,
"channel": channel,
"start_time": asyncio.get_event_loop().time()
}
# 4. 返回标准化请求(传递给编排层)
return {
"request": standard_request,
"session_id": standard_request.session_id
}
async def stream_response(
self,
token_stream: asyncio.AsyncIterator[str],
channel: str = "api"
):
"""流式返回响应"""
formatter = OutputFormatterFactory.create(channel)
if channel == "api":
async for chunk in formatter.format_sse_stream(token_stream):
yield chunk
elif channel == "cli":
async for chunk in formatter.format_cli_stream(token_stream):
yield chunk
# FastAPI路由
interface = InterfaceLayer()
@app.post("/api/chat")
async def chat_api(request: dict):
"""REST API入口"""
result = await interface.process_request(
raw_input=request.get("message", ""),
input_type=InputType.TEXT,
channel="api",
user_id=request.get("user_id", ""),
session_id=request.get("session_id", "")
)
return result
@app.post("/api/chat/stream")
async def chat_stream_api(request: dict):
"""REST API流式入口"""
result = await interface.process_request(
raw_input=request.get("message", ""),
input_type=InputType.TEXT,
channel="api"
)
# 假设orchestrator提供token_stream
# token_stream = orchestrator.run_stream(result["request"])
# return StreamingResponse(
# interface.stream_response(token_stream, "api"),
# media_type="text/event-stream"
# )
return {"status": "stream_endpoint", "session_id": result["session_id"]}
@app.websocket("/ws/chat")
async def chat_websocket(websocket: WebSocket):
"""WebSocket入口"""
await websocket.accept()
try:
while True:
data = await websocket.receive_json()
result = await interface.process_request(
raw_input=data.get("message", ""),
input_type=InputType.TEXT,
channel="websocket"
)
await websocket.send_json({
"type": "start",
"session_id": result["session_id"]
})
# 流式发送tokens
# async for chunk in interface.stream_response(token_stream, "websocket"):
# await websocket.send_text(chunk)
await websocket.send_json({"type": "end"})
except WebSocketDisconnect:
pass
3.9 接口层设计要点总结
#mermaid-svg-92508UOWq4N1j90g{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-92508UOWq4N1j90g .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-92508UOWq4N1j90g .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-92508UOWq4N1j90g .error-icon{fill:#552222;}#mermaid-svg-92508UOWq4N1j90g .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-92508UOWq4N1j90g .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-92508UOWq4N1j90g .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-92508UOWq4N1j90g .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-92508UOWq4N1j90g .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-92508UOWq4N1j90g .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-92508UOWq4N1j90g .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-92508UOWq4N1j90g .marker{fill:#333333;stroke:#333333;}#mermaid-svg-92508UOWq4N1j90g .marker.cross{stroke:#333333;}#mermaid-svg-92508UOWq4N1j90g svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-92508UOWq4N1j90g p{margin:0;}#mermaid-svg-92508UOWq4N1j90g .edge{stroke-width:3;}#mermaid-svg-92508UOWq4N1j90g .section–1 rect,#mermaid-svg-92508UOWq4N1j90g .section–1 path,#mermaid-svg-92508UOWq4N1j90g .section–1 circle,#mermaid-svg-92508UOWq4N1j90g .section–1 polygon,#mermaid-svg-92508UOWq4N1j90g .section–1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .section–1 text{fill:#ffffff;}#mermaid-svg-92508UOWq4N1j90g .node-icon–1{font-size:40px;color:#ffffff;}#mermaid-svg-92508UOWq4N1j90g .section-edge–1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .edge-depth–1{stroke-width:17;}#mermaid-svg-92508UOWq4N1j90g .section–1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-92508UOWq4N1j90g .disabled,#mermaid-svg-92508UOWq4N1j90g .disabled circle,#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:lightgray;}#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:#efefef;}#mermaid-svg-92508UOWq4N1j90g .section-0 rect,#mermaid-svg-92508UOWq4N1j90g .section-0 path,#mermaid-svg-92508UOWq4N1j90g .section-0 circle,#mermaid-svg-92508UOWq4N1j90g .section-0 polygon,#mermaid-svg-92508UOWq4N1j90g .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-92508UOWq4N1j90g .section-0 text{fill:black;}#mermaid-svg-92508UOWq4N1j90g .node-icon-0{font-size:40px;color:black;}#mermaid-svg-92508UOWq4N1j90g .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-92508UOWq4N1j90g .edge-depth-0{stroke-width:14;}#mermaid-svg-92508UOWq4N1j90g .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-92508UOWq4N1j90g .disabled,#mermaid-svg-92508UOWq4N1j90g .disabled circle,#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:lightgray;}#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:#efefef;}#mermaid-svg-92508UOWq4N1j90g .section-1 rect,#mermaid-svg-92508UOWq4N1j90g .section-1 path,#mermaid-svg-92508UOWq4N1j90g .section-1 circle,#mermaid-svg-92508UOWq4N1j90g .section-1 polygon,#mermaid-svg-92508UOWq4N1j90g .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .section-1 text{fill:black;}#mermaid-svg-92508UOWq4N1j90g .node-icon-1{font-size:40px;color:black;}#mermaid-svg-92508UOWq4N1j90g .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .edge-depth-1{stroke-width:11;}#mermaid-svg-92508UOWq4N1j90g .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-92508UOWq4N1j90g .disabled,#mermaid-svg-92508UOWq4N1j90g .disabled circle,#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:lightgray;}#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:#efefef;}#mermaid-svg-92508UOWq4N1j90g .section-2 rect,#mermaid-svg-92508UOWq4N1j90g .section-2 path,#mermaid-svg-92508UOWq4N1j90g .section-2 circle,#mermaid-svg-92508UOWq4N1j90g .section-2 polygon,#mermaid-svg-92508UOWq4N1j90g .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .section-2 text{fill:#ffffff;}#mermaid-svg-92508UOWq4N1j90g .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-92508UOWq4N1j90g .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .edge-depth-2{stroke-width:8;}#mermaid-svg-92508UOWq4N1j90g .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-92508UOWq4N1j90g .disabled,#mermaid-svg-92508UOWq4N1j90g .disabled circle,#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:lightgray;}#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:#efefef;}#mermaid-svg-92508UOWq4N1j90g .section-3 rect,#mermaid-svg-92508UOWq4N1j90g .section-3 path,#mermaid-svg-92508UOWq4N1j90g .section-3 circle,#mermaid-svg-92508UOWq4N1j90g .section-3 polygon,#mermaid-svg-92508UOWq4N1j90g .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .section-3 text{fill:black;}#mermaid-svg-92508UOWq4N1j90g .node-icon-3{font-size:40px;color:black;}#mermaid-svg-92508UOWq4N1j90g .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .edge-depth-3{stroke-width:5;}#mermaid-svg-92508UOWq4N1j90g .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-92508UOWq4N1j90g .disabled,#mermaid-svg-92508UOWq4N1j90g .disabled circle,#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:lightgray;}#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:#efefef;}#mermaid-svg-92508UOWq4N1j90g .section-4 rect,#mermaid-svg-92508UOWq4N1j90g .section-4 path,#mermaid-svg-92508UOWq4N1j90g .section-4 circle,#mermaid-svg-92508UOWq4N1j90g .section-4 polygon,#mermaid-svg-92508UOWq4N1j90g .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .section-4 text{fill:black;}#mermaid-svg-92508UOWq4N1j90g .node-icon-4{font-size:40px;color:black;}#mermaid-svg-92508UOWq4N1j90g .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .edge-depth-4{stroke-width:2;}#mermaid-svg-92508UOWq4N1j90g .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-92508UOWq4N1j90g .disabled,#mermaid-svg-92508UOWq4N1j90g .disabled circle,#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:lightgray;}#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:#efefef;}#mermaid-svg-92508UOWq4N1j90g .section-5 rect,#mermaid-svg-92508UOWq4N1j90g .section-5 path,#mermaid-svg-92508UOWq4N1j90g .section-5 circle,#mermaid-svg-92508UOWq4N1j90g .section-5 polygon,#mermaid-svg-92508UOWq4N1j90g .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .section-5 text{fill:black;}#mermaid-svg-92508UOWq4N1j90g .node-icon-5{font-size:40px;color:black;}#mermaid-svg-92508UOWq4N1j90g .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .edge-depth-5{stroke-width:-1;}#mermaid-svg-92508UOWq4N1j90g .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-92508UOWq4N1j90g .disabled,#mermaid-svg-92508UOWq4N1j90g .disabled circle,#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:lightgray;}#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:#efefef;}#mermaid-svg-92508UOWq4N1j90g .section-6 rect,#mermaid-svg-92508UOWq4N1j90g .section-6 path,#mermaid-svg-92508UOWq4N1j90g .section-6 circle,#mermaid-svg-92508UOWq4N1j90g .section-6 polygon,#mermaid-svg-92508UOWq4N1j90g .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .section-6 text{fill:black;}#mermaid-svg-92508UOWq4N1j90g .node-icon-6{font-size:40px;color:black;}#mermaid-svg-92508UOWq4N1j90g .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .edge-depth-6{stroke-width:-4;}#mermaid-svg-92508UOWq4N1j90g .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-92508UOWq4N1j90g .disabled,#mermaid-svg-92508UOWq4N1j90g .disabled circle,#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:lightgray;}#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:#efefef;}#mermaid-svg-92508UOWq4N1j90g .section-7 rect,#mermaid-svg-92508UOWq4N1j90g .section-7 path,#mermaid-svg-92508UOWq4N1j90g .section-7 circle,#mermaid-svg-92508UOWq4N1j90g .section-7 polygon,#mermaid-svg-92508UOWq4N1j90g .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .section-7 text{fill:black;}#mermaid-svg-92508UOWq4N1j90g .node-icon-7{font-size:40px;color:black;}#mermaid-svg-92508UOWq4N1j90g .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .edge-depth-7{stroke-width:-7;}#mermaid-svg-92508UOWq4N1j90g .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-92508UOWq4N1j90g .disabled,#mermaid-svg-92508UOWq4N1j90g .disabled circle,#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:lightgray;}#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:#efefef;}#mermaid-svg-92508UOWq4N1j90g .section-8 rect,#mermaid-svg-92508UOWq4N1j90g .section-8 path,#mermaid-svg-92508UOWq4N1j90g .section-8 circle,#mermaid-svg-92508UOWq4N1j90g .section-8 polygon,#mermaid-svg-92508UOWq4N1j90g .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .section-8 text{fill:black;}#mermaid-svg-92508UOWq4N1j90g .node-icon-8{font-size:40px;color:black;}#mermaid-svg-92508UOWq4N1j90g .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .edge-depth-8{stroke-width:-10;}#mermaid-svg-92508UOWq4N1j90g .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-92508UOWq4N1j90g .disabled,#mermaid-svg-92508UOWq4N1j90g .disabled circle,#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:lightgray;}#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:#efefef;}#mermaid-svg-92508UOWq4N1j90g .section-9 rect,#mermaid-svg-92508UOWq4N1j90g .section-9 path,#mermaid-svg-92508UOWq4N1j90g .section-9 circle,#mermaid-svg-92508UOWq4N1j90g .section-9 polygon,#mermaid-svg-92508UOWq4N1j90g .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .section-9 text{fill:black;}#mermaid-svg-92508UOWq4N1j90g .node-icon-9{font-size:40px;color:black;}#mermaid-svg-92508UOWq4N1j90g .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .edge-depth-9{stroke-width:-13;}#mermaid-svg-92508UOWq4N1j90g .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-92508UOWq4N1j90g .disabled,#mermaid-svg-92508UOWq4N1j90g .disabled circle,#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:lightgray;}#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:#efefef;}#mermaid-svg-92508UOWq4N1j90g .section-10 rect,#mermaid-svg-92508UOWq4N1j90g .section-10 path,#mermaid-svg-92508UOWq4N1j90g .section-10 circle,#mermaid-svg-92508UOWq4N1j90g .section-10 polygon,#mermaid-svg-92508UOWq4N1j90g .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .section-10 text{fill:black;}#mermaid-svg-92508UOWq4N1j90g .node-icon-10{font-size:40px;color:black;}#mermaid-svg-92508UOWq4N1j90g .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .edge-depth-10{stroke-width:-16;}#mermaid-svg-92508UOWq4N1j90g .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-92508UOWq4N1j90g .disabled,#mermaid-svg-92508UOWq4N1j90g .disabled circle,#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:lightgray;}#mermaid-svg-92508UOWq4N1j90g .disabled text{fill:#efefef;}#mermaid-svg-92508UOWq4N1j90g .section-root rect,#mermaid-svg-92508UOWq4N1j90g .section-root path,#mermaid-svg-92508UOWq4N1j90g .section-root circle,#mermaid-svg-92508UOWq4N1j90g .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-92508UOWq4N1j90g .section-root text{fill:#ffffff;}#mermaid-svg-92508UOWq4N1j90g .section-root span{color:#ffffff;}#mermaid-svg-92508UOWq4N1j90g .section-2 span{color:#ffffff;}#mermaid-svg-92508UOWq4N1j90g .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-92508UOWq4N1j90g .edge{fill:none;}#mermaid-svg-92508UOWq4N1j90g .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-92508UOWq4N1j90g :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
接口层设计要点
输入处理
多模态支持
文本
语音ASR
图片OCR+描述
文件内容提取
标准化
统一StandardRequest
元数据补充
预处理
全角转半角
空白清理
编码统一
输出处理
格式化
Markdown渲染
JSON结构化
纯文本
语音TTS
流式输出
SSE事件流
WebSocket双向流
CLI直接打印
通道管理
REST API
WebSocket
CLI
gRPC
安全
认证鉴权
限流控制
输入校验
四、Layer 2 LLM引擎层详解
4.1 LLM引擎层的核心职责
LLM引擎层是Agent的"大脑引擎室"。它负责:
#mermaid-svg-Tny3ozVIRp6t0esI{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-Tny3ozVIRp6t0esI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Tny3ozVIRp6t0esI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Tny3ozVIRp6t0esI .error-icon{fill:#552222;}#mermaid-svg-Tny3ozVIRp6t0esI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Tny3ozVIRp6t0esI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Tny3ozVIRp6t0esI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Tny3ozVIRp6t0esI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Tny3ozVIRp6t0esI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Tny3ozVIRp6t0esI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Tny3ozVIRp6t0esI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Tny3ozVIRp6t0esI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Tny3ozVIRp6t0esI .marker.cross{stroke:#333333;}#mermaid-svg-Tny3ozVIRp6t0esI svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Tny3ozVIRp6t0esI p{margin:0;}#mermaid-svg-Tny3ozVIRp6t0esI .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Tny3ozVIRp6t0esI .cluster-label text{fill:#333;}#mermaid-svg-Tny3ozVIRp6t0esI .cluster-label span{color:#333;}#mermaid-svg-Tny3ozVIRp6t0esI .cluster-label span p{background-color:transparent;}#mermaid-svg-Tny3ozVIRp6t0esI .label text,#mermaid-svg-Tny3ozVIRp6t0esI span{fill:#333;color:#333;}#mermaid-svg-Tny3ozVIRp6t0esI .node rect,#mermaid-svg-Tny3ozVIRp6t0esI .node circle,#mermaid-svg-Tny3ozVIRp6t0esI .node ellipse,#mermaid-svg-Tny3ozVIRp6t0esI .node polygon,#mermaid-svg-Tny3ozVIRp6t0esI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Tny3ozVIRp6t0esI .rough-node .label text,#mermaid-svg-Tny3ozVIRp6t0esI .node .label text,#mermaid-svg-Tny3ozVIRp6t0esI .image-shape .label,#mermaid-svg-Tny3ozVIRp6t0esI .icon-shape .label{text-anchor:middle;}#mermaid-svg-Tny3ozVIRp6t0esI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Tny3ozVIRp6t0esI .rough-node .label,#mermaid-svg-Tny3ozVIRp6t0esI .node .label,#mermaid-svg-Tny3ozVIRp6t0esI .image-shape .label,#mermaid-svg-Tny3ozVIRp6t0esI .icon-shape .label{text-align:center;}#mermaid-svg-Tny3ozVIRp6t0esI .node.clickable{cursor:pointer;}#mermaid-svg-Tny3ozVIRp6t0esI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Tny3ozVIRp6t0esI .arrowheadPath{fill:#333333;}#mermaid-svg-Tny3ozVIRp6t0esI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Tny3ozVIRp6t0esI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Tny3ozVIRp6t0esI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Tny3ozVIRp6t0esI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Tny3ozVIRp6t0esI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Tny3ozVIRp6t0esI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Tny3ozVIRp6t0esI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Tny3ozVIRp6t0esI .cluster text{fill:#333;}#mermaid-svg-Tny3ozVIRp6t0esI .cluster span{color:#333;}#mermaid-svg-Tny3ozVIRp6t0esI 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-Tny3ozVIRp6t0esI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Tny3ozVIRp6t0esI rect.text{fill:none;stroke-width:0;}#mermaid-svg-Tny3ozVIRp6t0esI .icon-shape,#mermaid-svg-Tny3ozVIRp6t0esI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Tny3ozVIRp6t0esI .icon-shape p,#mermaid-svg-Tny3ozVIRp6t0esI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Tny3ozVIRp6t0esI .icon-shape .label rect,#mermaid-svg-Tny3ozVIRp6t0esI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Tny3ozVIRp6t0esI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Tny3ozVIRp6t0esI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Tny3ozVIRp6t0esI :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
LLM引擎层内部结构
统一输出
核心引擎
模型适配器
统一LLMResponse{content, tool_calls, usage, metadata}
OpenAI适配器GPT-4o/GPT-4o-mini
模型抽象层统一LLM接口
Anthropic适配器Claude-3.5-Sonnet
Google适配器Gemini-1.5-Pro
本地模型适配器Llama/Qwen/Ollama
流式调用管理器
工具调用适配器
成本控制器
重试与降级器
4.2 模型抽象层
模型抽象层是LLM引擎层的核心——它定义了统一的LLM调用接口,使得上层(编排层)不需要关心底层使用的是哪个厂商的模型。
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any, Optional, List, Dict, AsyncIterator
from enum import Enum
import asyncio
import time
class ModelProvider(Enum):
"""模型厂商枚举"""
OPENAI = "openai"
ANTHROPIC = "anthropic"
GOOGLE = "google"
LOCAL = "local"
AZURE = "azure"
@dataclass
class LLMConfig:
"""LLM调用配置"""
provider: ModelProvider = ModelProvider.OPENAI
model_name: str = "gpt-4o"
temperature: float = 0.7
max_tokens: int = 4096
top_p: float = 1.0
timeout: int = 30
retry_count: int = 3
retry_delay: float = 1.0
budget_limit: float = 10.0 # 美元预算上限
@dataclass
class ToolCallResult:
"""工具调用结果(统一格式)"""
tool_name: str
arguments: dict
call_id: str = ""
@dataclass
class LLMResponse:
"""统一的LLM响应格式"""
content: str = ""
tool_calls: List[ToolCallResult] = field(default_factory=list)
usage: dict = field(default_factory=dict) # {prompt_tokens, completion_tokens, total_tokens}
model: str = ""
provider: str = ""
latency_ms: float = 0.0
cost_usd: float = 0.0
finish_reason: str = "stop"
metadata: dict = field(default_factory=dict)
class BaseLLMAdapter(ABC):
"""LLM适配器抽象基类——所有厂商适配器必须实现此接口"""
def __init__(self, config: LLMConfig):
self.config = config
self.total_cost = 0.0
self.total_tokens = 0
@abstractmethod
async def invoke(
self,
messages: List[dict],
tools: Optional[List[dict]] = None,
**kwargs
) –> LLMResponse:
"""同步调用LLM"""
pass
@abstractmethod
async def stream_invoke(
self,
messages: List[dict],
tools: Optional[List[dict]] = None,
**kwargs
) –> AsyncIterator[str]:
"""流式调用LLM,逐token返回"""
pass
@abstractmethod
def estimate_cost(self, usage: dict) –> float:
"""根据token使用量估算成本"""
pass
def check_budget(self) –> bool:
"""检查是否超出预算"""
return self.total_cost < self.config.budget_limit
4.3 多厂商适配器实现
4.3.1 OpenAI适配器
class OpenAIAdapter(BaseLLMAdapter):
"""OpenAI GPT系列适配器"""
# 价格表(美元/1K tokens),2026年参考价格
PRICING = {
"gpt-4o": {"input": 0.0025, "output": 0.01},
"gpt-4o-mini": {"input": 0.00015, "output": 0.0006},
"gpt-4-turbo": {"input": 0.01, "output": 0.03},
"o1": {"input": 0.015, "output": 0.06},
}
def __init__(self, config: LLMConfig, api_key: str = ""):
super().__init__(config)
self.api_key = api_key
self.client = None # 实际使用时初始化 openai.AsyncOpenAI(api_key=api_key)
async def invoke(
self,
messages: List[dict],
tools: Optional[List[dict]] = None,
**kwargs
) –> LLMResponse:
"""调用OpenAI API"""
start_time = time.time()
# 构建请求参数
request_params = {
"model": self.config.model_name,
"messages": messages,
"temperature": self.config.temperature,
"max_tokens": self.config.max_tokens,
"top_p": self.config.top_p,
}
if tools:
request_params["tools"] = self._format_tools_openai(tools)
request_params["tool_choice"] = "auto"
# 模拟API调用(实际使用openai库)
await asyncio.sleep(0.01) # 模拟网络延迟
# 模拟响应
response = {
"content": "这是模拟的LLM回答",
"tool_calls": [],
"usage": {
"prompt_tokens": 150,
"completion_tokens": 50,
"total_tokens": 200
},
"finish_reason": "stop"
}
latency = (time.time() – start_time) * 1000
cost = self.estimate_cost(response["usage"])
self.total_cost += cost
self.total_tokens += response["usage"]["total_tokens"]
return LLMResponse(
content=response["content"],
tool_calls=[
ToolCallResult(
tool_name=tc["function"]["name"],
arguments=json.loads(tc["function"]["arguments"]),
call_id=tc.get("id", "")
)
for tc in response.get("tool_calls", [])
],
usage=response["usage"],
model=self.config.model_name,
provider="openai",
latency_ms=latency,
cost_usd=cost,
finish_reason=response["finish_reason"]
)
async def stream_invoke(
self,
messages: List[dict],
tools: Optional[List[dict]] = None,
**kwargs
) –> AsyncIterator[str]:
"""流式调用OpenAI API"""
# 模拟流式输出
mock_response = "这是流式输出的模拟回答"
for char in mock_response:
await asyncio.sleep(0.02) # 模拟token生成延迟
yield char
def estimate_cost(self, usage: dict) –> float:
"""计算调用成本"""
pricing = self.PRICING.get(self.config.model_name, {"input": 0.001, "output": 0.002})
input_cost = (usage.get("prompt_tokens", 0) / 1000) * pricing["input"]
output_cost = (usage.get("completion_tokens", 0) / 1000) * pricing["output"]
return round(input_cost + output_cost, 6)
def _format_tools_openai(self, tools: List[dict]) –> List[dict]:
"""将统一工具格式转为OpenAI格式"""
return [
{
"type": "function",
"function": {
"name": t["name"],
"description": t.get("description", ""),
"parameters": t.get("parameters", {})
}
}
for t in tools
]
4.3.2 Anthropic适配器
class AnthropicAdapter(BaseLLMAdapter):
"""Anthropic Claude系列适配器"""
PRICING = {
"claude-3-5-sonnet-20241022": {"input": 0.003, "output": 0.015},
"claude-3-5-haiku-20241022": {"input": 0.0008, "output": 0.004},
"claude-3-opus-20240229": {"input": 0.015, "output": 0.075},
}
def __init__(self, config: LLMConfig, api_key: str = ""):
super().__init__(config)
self.api_key = api_key
async def invoke(
self,
messages: List[dict],
tools: Optional[List[dict]] = None,
**kwargs
) –> LLMResponse:
"""调用Anthropic API"""
start_time = time.time()
# Anthropic的消息格式与OpenAI略有不同
# 需要将system message单独提取
system_msg = ""
filtered_messages = []
for msg in messages:
if msg["role"] == "system":
system_msg += msg["content"] + "\\n"
else:
filtered_messages.append(msg)
# 模拟API调用
await asyncio.sleep(0.01)
response = {
"content": "这是Claude的模拟回答",
"tool_calls": [],
"usage": {
"prompt_tokens": 120,
"completion_tokens": 40,
"total_tokens": 160
},
"finish_reason": "end_turn"
}
latency = (time.time() – start_time) * 1000
cost = self.estimate_cost(response["usage"])
self.total_cost += cost
self.total_tokens += response["usage"]["total_tokens"]
return LLMResponse(
content=response["content"],
usage=response["usage"],
model=self.config.model_name,
provider="anthropic",
latency_ms=latency,
cost_usd=cost,
finish_reason=response["finish_reason"]
)
async def stream_invoke(
self,
messages: List[dict],
tools: Optional[List[dict]] = None,
**kwargs
) –> AsyncIterator[str]:
mock_response = "这是Claude的流式回答"
for char in mock_response:
await asyncio.sleep(0.02)
yield char
def estimate_cost(self, usage: dict) –> float:
pricing = self.PRICING.get(self.config.model_name, {"input": 0.003, "output": 0.015})
input_cost = (usage.get("prompt_tokens", 0) / 1000) * pricing["input"]
output_cost = (usage.get("completion_tokens", 0) / 1000) * pricing["output"]
return round(input_cost + output_cost, 6)
4.4 LLM引擎管理器
class LLMEngineManager:
"""LLM引擎管理器——统一管理所有适配器"""
def __init__(self):
self.adapters: Dict[ModelProvider, BaseLLMAdapter] = {}
self.default_provider: ModelProvider = ModelProvider.OPENAI
self.fallback_chain: List[ModelProvider] = []
def register_adapter(
self,
provider: ModelProvider,
adapter: BaseLLMAdapter
):
"""注册适配器"""
self.adapters[provider] = adapter
def set_fallback_chain(self, chain: List[ModelProvider]):
"""设置降级链:主模型失败时按顺序尝试备选模型"""
self.fallback_chain = chain
async def invoke(
self,
messages: List[dict],
tools: Optional[List[dict]] = None,
provider: Optional[ModelProvider] = None,
**kwargs
) –> LLMResponse:
"""统一调用入口,支持自动降级"""
providers_to_try = [provider or self.default_provider] + self.fallback_chain
providers_to_try = list(dict.fromkeys(providers_to_try)) # 去重
last_error = None
for p in providers_to_try:
adapter = self.adapters.get(p)
if adapter is None:
continue
if not adapter.check_budget():
continue # 预算不足,跳过
try:
response = await adapter.invoke(messages, tools, **kwargs)
return response
except Exception as e:
last_error = e
continue # 尝试下一个
raise RuntimeError(f"所有模型调用均失败,最后错误: {last_error}")
async def stream_invoke(
self,
messages: List[dict],
tools: Optional[List[dict]] = None,
provider: Optional[ModelProvider] = None,
**kwargs
) –> AsyncIterator[str]:
"""流式调用入口"""
p = provider or self.default_provider
adapter = self.adapters.get(p)
if adapter is None:
raise RuntimeError(f"未找到 {p} 的适配器")
async for token in adapter.stream_invoke(messages, tools, **kwargs):
yield token
通俗理解:LLM引擎层就像一个多语言翻译中心——你只需要说"翻译这段话",中心会自动选择最合适的翻译官(模型)。如果首选翻译官请假了(API报错),中心会自动找备选翻译官顶上(降级机制)。
4.5 成本控制机制
#mermaid-svg-wZUMoxHmUwT4AT5E{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-wZUMoxHmUwT4AT5E .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wZUMoxHmUwT4AT5E .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wZUMoxHmUwT4AT5E .error-icon{fill:#552222;}#mermaid-svg-wZUMoxHmUwT4AT5E .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wZUMoxHmUwT4AT5E .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wZUMoxHmUwT4AT5E .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wZUMoxHmUwT4AT5E .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wZUMoxHmUwT4AT5E .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wZUMoxHmUwT4AT5E .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wZUMoxHmUwT4AT5E .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wZUMoxHmUwT4AT5E .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wZUMoxHmUwT4AT5E .marker.cross{stroke:#333333;}#mermaid-svg-wZUMoxHmUwT4AT5E svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wZUMoxHmUwT4AT5E p{margin:0;}#mermaid-svg-wZUMoxHmUwT4AT5E .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-wZUMoxHmUwT4AT5E .cluster-label text{fill:#333;}#mermaid-svg-wZUMoxHmUwT4AT5E .cluster-label span{color:#333;}#mermaid-svg-wZUMoxHmUwT4AT5E .cluster-label span p{background-color:transparent;}#mermaid-svg-wZUMoxHmUwT4AT5E .label text,#mermaid-svg-wZUMoxHmUwT4AT5E span{fill:#333;color:#333;}#mermaid-svg-wZUMoxHmUwT4AT5E .node rect,#mermaid-svg-wZUMoxHmUwT4AT5E .node circle,#mermaid-svg-wZUMoxHmUwT4AT5E .node ellipse,#mermaid-svg-wZUMoxHmUwT4AT5E .node polygon,#mermaid-svg-wZUMoxHmUwT4AT5E .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wZUMoxHmUwT4AT5E .rough-node .label text,#mermaid-svg-wZUMoxHmUwT4AT5E .node .label text,#mermaid-svg-wZUMoxHmUwT4AT5E .image-shape .label,#mermaid-svg-wZUMoxHmUwT4AT5E .icon-shape .label{text-anchor:middle;}#mermaid-svg-wZUMoxHmUwT4AT5E .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wZUMoxHmUwT4AT5E .rough-node .label,#mermaid-svg-wZUMoxHmUwT4AT5E .node .label,#mermaid-svg-wZUMoxHmUwT4AT5E .image-shape .label,#mermaid-svg-wZUMoxHmUwT4AT5E .icon-shape .label{text-align:center;}#mermaid-svg-wZUMoxHmUwT4AT5E .node.clickable{cursor:pointer;}#mermaid-svg-wZUMoxHmUwT4AT5E .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wZUMoxHmUwT4AT5E .arrowheadPath{fill:#333333;}#mermaid-svg-wZUMoxHmUwT4AT5E .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wZUMoxHmUwT4AT5E .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wZUMoxHmUwT4AT5E .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wZUMoxHmUwT4AT5E .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wZUMoxHmUwT4AT5E .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wZUMoxHmUwT4AT5E .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wZUMoxHmUwT4AT5E .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wZUMoxHmUwT4AT5E .cluster text{fill:#333;}#mermaid-svg-wZUMoxHmUwT4AT5E .cluster span{color:#333;}#mermaid-svg-wZUMoxHmUwT4AT5E 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-wZUMoxHmUwT4AT5E .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wZUMoxHmUwT4AT5E rect.text{fill:none;stroke-width:0;}#mermaid-svg-wZUMoxHmUwT4AT5E .icon-shape,#mermaid-svg-wZUMoxHmUwT4AT5E .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wZUMoxHmUwT4AT5E .icon-shape p,#mermaid-svg-wZUMoxHmUwT4AT5E .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wZUMoxHmUwT4AT5E .icon-shape .label rect,#mermaid-svg-wZUMoxHmUwT4AT5E .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wZUMoxHmUwT4AT5E .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wZUMoxHmUwT4AT5E .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wZUMoxHmUwT4AT5E :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
成本控制流程
未超预算
已超预算
调用请求
预算检查
执行调用
拒绝调用抛出BudgetExceededError
计算实际成本
累计总成本
生成成本报告
超阈值告警
class CostController:
"""成本控制器"""
def __init__(self, daily_budget: float = 10.0, alert_threshold: float = 0.8):
self.daily_budget = daily_budget
self.alert_threshold = alert_threshold
self.daily_spend = 0.0
self.call_history: List[dict] = []
def check_before_call(self, estimated_cost: float = 0.0) –> bool:
"""调用前检查预算"""
projected = self.daily_spend + estimated_cost
if projected > self.daily_budget:
return False
if projected > self.daily_budget * self.alert_threshold:
# 告警但不阻止
pass
return True
def record_call(self, cost: float, model: str, tokens: int):
"""记录调用成本"""
self.daily_spend += cost
self.call_history.append({
"cost": cost,
"model": model,
"tokens": tokens,
"cumulative_spend": self.daily_spend
})
def get_report(self) –> dict:
"""生成成本报告"""
return {
"daily_budget": self.daily_budget,
"daily_spend": round(self.daily_spend, 4),
"remaining": round(self.daily_budget – self.daily_spend, 4),
"usage_ratio": round(self.daily_spend / self.daily_budget, 2),
"total_calls": len(self.call_history),
"by_model": self._group_by_model()
}
def _group_by_model(self) –> dict:
groups = {}
for call in self.call_history:
model = call["model"]
if model not in groups:
groups[model] = {"calls": 0, "cost": 0, "tokens": 0}
groups[model]["calls"] += 1
groups[model]["cost"] += call["cost"]
groups[model]["tokens"] += call["tokens"]
return groups
4.6 各厂商模型对比
| OpenAI | GPT-4o | 128K | $0.0025 | $0.01 | 原生支持 | 支持 | 通用、工具调用强 |
| OpenAI | GPT-4o-mini | 128K | $0.00015 | $0.0006 | 原生支持 | 支持 | 低成本场景 |
| OpenAI | o1 | 200K | $0.015 | $0.06 | 有限支持 | 支持 | 复杂推理 |
| Anthropic | Claude-3.5-Sonnet | 200K | $0.003 | $0.015 | 原生支持 | 支持 | 长文本、代码 |
| Anthropic | Claude-3.5-Haiku | 200K | $0.0008 | $0.004 | 原生支持 | 支持 | 低成本场景 |
| Gemini-1.5-Pro | 2M | $0.00125 | $0.005 | 原生支持 | 支持 | 超长上下文 | |
| 本地 | Qwen2.5-72B | 128K | 免费 | 免费 | 需适配 | 支持 | 私有化部署 |
| 本地 | Llama-3.1-70B | 128K | 免费 | 免费 | 需适配 | 支持 | 私有化部署 |
五、Layer 3 编排引擎层详解
5.1 编排引擎层的核心职责
编排引擎层是Agent系统的"指挥中心"。它决定了Agent如何思考、何时行动、如何循环、何时停止。具体职责包括:
#mermaid-svg-WNfdj6YocPCcc29g{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-WNfdj6YocPCcc29g .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WNfdj6YocPCcc29g .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WNfdj6YocPCcc29g .error-icon{fill:#552222;}#mermaid-svg-WNfdj6YocPCcc29g .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WNfdj6YocPCcc29g .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WNfdj6YocPCcc29g .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WNfdj6YocPCcc29g .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WNfdj6YocPCcc29g .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WNfdj6YocPCcc29g .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WNfdj6YocPCcc29g .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WNfdj6YocPCcc29g .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WNfdj6YocPCcc29g .marker.cross{stroke:#333333;}#mermaid-svg-WNfdj6YocPCcc29g svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WNfdj6YocPCcc29g p{margin:0;}#mermaid-svg-WNfdj6YocPCcc29g .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-WNfdj6YocPCcc29g .cluster-label text{fill:#333;}#mermaid-svg-WNfdj6YocPCcc29g .cluster-label span{color:#333;}#mermaid-svg-WNfdj6YocPCcc29g .cluster-label span p{background-color:transparent;}#mermaid-svg-WNfdj6YocPCcc29g .label text,#mermaid-svg-WNfdj6YocPCcc29g span{fill:#333;color:#333;}#mermaid-svg-WNfdj6YocPCcc29g .node rect,#mermaid-svg-WNfdj6YocPCcc29g .node circle,#mermaid-svg-WNfdj6YocPCcc29g .node ellipse,#mermaid-svg-WNfdj6YocPCcc29g .node polygon,#mermaid-svg-WNfdj6YocPCcc29g .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WNfdj6YocPCcc29g .rough-node .label text,#mermaid-svg-WNfdj6YocPCcc29g .node .label text,#mermaid-svg-WNfdj6YocPCcc29g .image-shape .label,#mermaid-svg-WNfdj6YocPCcc29g .icon-shape .label{text-anchor:middle;}#mermaid-svg-WNfdj6YocPCcc29g .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WNfdj6YocPCcc29g .rough-node .label,#mermaid-svg-WNfdj6YocPCcc29g .node .label,#mermaid-svg-WNfdj6YocPCcc29g .image-shape .label,#mermaid-svg-WNfdj6YocPCcc29g .icon-shape .label{text-align:center;}#mermaid-svg-WNfdj6YocPCcc29g .node.clickable{cursor:pointer;}#mermaid-svg-WNfdj6YocPCcc29g .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WNfdj6YocPCcc29g .arrowheadPath{fill:#333333;}#mermaid-svg-WNfdj6YocPCcc29g .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WNfdj6YocPCcc29g .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WNfdj6YocPCcc29g .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WNfdj6YocPCcc29g .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WNfdj6YocPCcc29g .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WNfdj6YocPCcc29g .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WNfdj6YocPCcc29g .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WNfdj6YocPCcc29g .cluster text{fill:#333;}#mermaid-svg-WNfdj6YocPCcc29g .cluster span{color:#333;}#mermaid-svg-WNfdj6YocPCcc29g 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-WNfdj6YocPCcc29g .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WNfdj6YocPCcc29g rect.text{fill:none;stroke-width:0;}#mermaid-svg-WNfdj6YocPCcc29g .icon-shape,#mermaid-svg-WNfdj6YocPCcc29g .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WNfdj6YocPCcc29g .icon-shape p,#mermaid-svg-WNfdj6YocPCcc29g .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WNfdj6YocPCcc29g .icon-shape .label rect,#mermaid-svg-WNfdj6YocPCcc29g .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WNfdj6YocPCcc29g .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WNfdj6YocPCcc29g .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WNfdj6YocPCcc29g :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
编排引擎层核心组件
StateGraph状态图定义
条件路由器决定下一步
循环控制器ReAct循环管理
并行执行器多工具并行
子图管理器嵌套子图
错误恢复器重试/降级/跳过
5.2 StateGraph状态图
StateGraph是编排引擎的核心数据结构。它用有向图来描述Agent的执行流程——每个节点代表一个状态(如"思考"、“行动”、“观察”),每条边代表一个状态转移。
#mermaid-svg-5VTQa520vsO1Y7fs{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-5VTQa520vsO1Y7fs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5VTQa520vsO1Y7fs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5VTQa520vsO1Y7fs .error-icon{fill:#552222;}#mermaid-svg-5VTQa520vsO1Y7fs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5VTQa520vsO1Y7fs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5VTQa520vsO1Y7fs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5VTQa520vsO1Y7fs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5VTQa520vsO1Y7fs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5VTQa520vsO1Y7fs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5VTQa520vsO1Y7fs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5VTQa520vsO1Y7fs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5VTQa520vsO1Y7fs .marker.cross{stroke:#333333;}#mermaid-svg-5VTQa520vsO1Y7fs svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5VTQa520vsO1Y7fs p{margin:0;}#mermaid-svg-5VTQa520vsO1Y7fs defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-5VTQa520vsO1Y7fs g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-5VTQa520vsO1Y7fs g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-5VTQa520vsO1Y7fs g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-5VTQa520vsO1Y7fs g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-5VTQa520vsO1Y7fs g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-5VTQa520vsO1Y7fs .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-5VTQa520vsO1Y7fs .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-5VTQa520vsO1Y7fs .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-5VTQa520vsO1Y7fs .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-5VTQa520vsO1Y7fs .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-5VTQa520vsO1Y7fs .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-5VTQa520vsO1Y7fs .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-5VTQa520vsO1Y7fs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5VTQa520vsO1Y7fs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5VTQa520vsO1Y7fs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5VTQa520vsO1Y7fs .edgeLabel .label text{fill:#333;}#mermaid-svg-5VTQa520vsO1Y7fs .label div .edgeLabel{color:#333;}#mermaid-svg-5VTQa520vsO1Y7fs .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-5VTQa520vsO1Y7fs .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-5VTQa520vsO1Y7fs .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-5VTQa520vsO1Y7fs .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-5VTQa520vsO1Y7fs .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-5VTQa520vsO1Y7fs .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5VTQa520vsO1Y7fs .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5VTQa520vsO1Y7fs #statediagram-barbEnd{fill:#333333;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5VTQa520vsO1Y7fs .cluster-label,#mermaid-svg-5VTQa520vsO1Y7fs .nodeLabel{color:#131300;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-5VTQa520vsO1Y7fs .note-edge{stroke-dasharray:5;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram-note text{fill:black;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram-note .nodeLabel{color:black;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagram .edgeLabel{color:red;}#mermaid-svg-5VTQa520vsO1Y7fs #dependencyStart,#mermaid-svg-5VTQa520vsO1Y7fs #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-5VTQa520vsO1Y7fs .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5VTQa520vsO1Y7fs :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
接收到请求
初始化状态
需要调用工具
已有最终答案
工具执行完毕
继续思考
工具执行失败
错误恢复后继续
无法恢复,返回错误
返回结果
START
THINK
ACT
END
OBSERVE
ERROR
from dataclasses import dataclass, field
from typing import Any, Callable, Optional, List, Dict, Set
from enum import Enum
import asyncio
class NodeType(Enum):
"""节点类型"""
START = "start"
THINK = "think" # 思考节点
ACT = "act" # 行动节点(工具调用)
OBSERVE = "observe" # 观察节点(处理工具结果)
END = "end" # 结束节点
ERROR = "error" # 错误节点
SUBGRAPH = "subgraph" # 子图节点
@dataclass
class GraphState:
"""图状态——在节点间传递的数据"""
messages: List[dict] = field(default_factory=list)
current_thought: str = ""
tool_calls: List[dict] = field(default_factory=list)
tool_results: List[dict] = field(default_factory=list)
final_answer: str = ""
error: Optional[str] = None
iteration_count: int = 0
max_iterations: int = 10
metadata: dict = field(default_factory=dict)
def is_finished(self) –> bool:
"""判断是否已完成"""
return bool(self.final_answer) or bool(self.error)
def is_max_iterations(self) –> bool:
"""判断是否达到最大迭代次数"""
return self.iteration_count >= self.max_iterations
@dataclass
class Node:
"""图节点"""
name: str
node_type: NodeType
execute_func: Callable[[GraphState], GraphState]
description: str = ""
@dataclass
class Edge:
"""图边"""
source: str
target: str
condition: Optional[Callable[[GraphState], bool]] = None
description: str = ""
class StateGraph:
"""状态图——编排引擎的核心"""
def __init__(self):
self.nodes: Dict[str, Node] = {}
self.edges: List[Edge] = []
self.entry_point: str = ""
def add_node(self, node: Node):
"""添加节点"""
self.nodes[node.name] = node
def add_edge(self, edge: Edge):
"""添加边"""
self.edges.append(edge)
def set_entry_point(self, name: str):
"""设置入口节点"""
if name not in self.nodes:
raise ValueError(f"节点 {name} 不存在")
self.entry_point = name
def get_next_nodes(self, current: str, state: GraphState) –> List[str]:
"""根据当前节点和状态,获取下一步可达的节点"""
next_nodes = []
for edge in self.edges:
if edge.source == current:
if edge.condition is None or edge.condition(state):
next_nodes.append(edge.target)
return next_nodes
async def execute(self, initial_state: GraphState) –> GraphState:
"""执行状态图"""
state = initial_state
current = self.entry_point
while current and not state.is_finished():
node = self.nodes.get(current)
if node is None:
raise RuntimeError(f"节点 {current} 不存在")
# 执行节点
state = await self._execute_node(node, state)
state.iteration_count += 1
if state.is_max_iterations():
state.error = "达到最大迭代次数"
break
# 获取下一步
next_nodes = self.get_next_nodes(current, state)
if next_nodes:
current = next_nodes[0]
else:
break
return state
async def _execute_node(self, node: Node, state: GraphState) –> GraphState:
"""执行单个节点"""
try:
if asyncio.iscoroutinefunction(node.execute_func):
return await node.execute_func(state)
else:
return node.execute_func(state)
except Exception as e:
state.error = f"节点 {node.name} 执行失败: {str(e)}"
return state
5.3 条件路由
条件路由是编排引擎的"大脑"——它根据当前状态决定Agent的下一步行动。
class ConditionalRouter:
"""条件路由器"""
@staticmethod
def needs_tool_call(state: GraphState) –> bool:
"""判断是否需要调用工具"""
return len(state.tool_calls) > 0 and not state.final_answer
@staticmethod
def has_final_answer(state: GraphState) –> bool:
"""判断是否已有最终答案"""
return bool(state.final_answer)
@staticmethod
def has_error(state: GraphState) –> bool:
"""判断是否有错误"""
return bool(state.error)
@staticmethod
def should_retry(state: GraphState) –> bool:
"""判断是否应该重试"""
return (bool(state.error)
and state.iteration_count < state.max_iterations
and "达到最大迭代" not in (state.error or ""))
@staticmethod
def route_from_think(state: GraphState) –> str:
"""从思考节点路由"""
if ConditionalRouter.has_final_answer(state):
return "end"
elif ConditionalRouter.needs_tool_call(state):
return "act"
else:
return "end" # 无法继续,直接结束
@staticmethod
def route_from_observe(state: GraphState) –> str:
"""从观察节点路由"""
if ConditionalRouter.has_error(state):
if ConditionalRouter.should_retry(state):
return "think" # 错误恢复后继续思考
else:
return "end" # 无法恢复,结束
else:
return "think" # 正常继续思考
@staticmethod
def route_from_error(state: GraphState) –> str:
"""从错误节点路由"""
if ConditionalRouter.should_retry(state):
return "think"
else:
return "end"
通俗理解:条件路由就像GPS导航——你走到一个路口,GPS根据当前交通状况(状态)告诉你该左转、右转还是直行。条件路由器根据Agent当前的状态决定下一步该思考、行动还是结束。
5.4 循环控制:ReAct模式
ReAct(Reasoning + Acting)是Agent最经典的循环模式。Agent先思考(Reasoning),然后行动(Acting),观察结果(Observation),再继续思考,如此循环直到得出最终答案。
#mermaid-svg-CKg7pSCWJLzXPApE{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-CKg7pSCWJLzXPApE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CKg7pSCWJLzXPApE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CKg7pSCWJLzXPApE .error-icon{fill:#552222;}#mermaid-svg-CKg7pSCWJLzXPApE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CKg7pSCWJLzXPApE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CKg7pSCWJLzXPApE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CKg7pSCWJLzXPApE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CKg7pSCWJLzXPApE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CKg7pSCWJLzXPApE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CKg7pSCWJLzXPApE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CKg7pSCWJLzXPApE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CKg7pSCWJLzXPApE .marker.cross{stroke:#333333;}#mermaid-svg-CKg7pSCWJLzXPApE svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CKg7pSCWJLzXPApE p{margin:0;}#mermaid-svg-CKg7pSCWJLzXPApE .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-CKg7pSCWJLzXPApE .cluster-label text{fill:#333;}#mermaid-svg-CKg7pSCWJLzXPApE .cluster-label span{color:#333;}#mermaid-svg-CKg7pSCWJLzXPApE .cluster-label span p{background-color:transparent;}#mermaid-svg-CKg7pSCWJLzXPApE .label text,#mermaid-svg-CKg7pSCWJLzXPApE span{fill:#333;color:#333;}#mermaid-svg-CKg7pSCWJLzXPApE .node rect,#mermaid-svg-CKg7pSCWJLzXPApE .node circle,#mermaid-svg-CKg7pSCWJLzXPApE .node ellipse,#mermaid-svg-CKg7pSCWJLzXPApE .node polygon,#mermaid-svg-CKg7pSCWJLzXPApE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CKg7pSCWJLzXPApE .rough-node .label text,#mermaid-svg-CKg7pSCWJLzXPApE .node .label text,#mermaid-svg-CKg7pSCWJLzXPApE .image-shape .label,#mermaid-svg-CKg7pSCWJLzXPApE .icon-shape .label{text-anchor:middle;}#mermaid-svg-CKg7pSCWJLzXPApE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CKg7pSCWJLzXPApE .rough-node .label,#mermaid-svg-CKg7pSCWJLzXPApE .node .label,#mermaid-svg-CKg7pSCWJLzXPApE .image-shape .label,#mermaid-svg-CKg7pSCWJLzXPApE .icon-shape .label{text-align:center;}#mermaid-svg-CKg7pSCWJLzXPApE .node.clickable{cursor:pointer;}#mermaid-svg-CKg7pSCWJLzXPApE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CKg7pSCWJLzXPApE .arrowheadPath{fill:#333333;}#mermaid-svg-CKg7pSCWJLzXPApE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CKg7pSCWJLzXPApE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CKg7pSCWJLzXPApE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CKg7pSCWJLzXPApE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CKg7pSCWJLzXPApE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CKg7pSCWJLzXPApE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CKg7pSCWJLzXPApE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CKg7pSCWJLzXPApE .cluster text{fill:#333;}#mermaid-svg-CKg7pSCWJLzXPApE .cluster span{color:#333;}#mermaid-svg-CKg7pSCWJLzXPApE 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-CKg7pSCWJLzXPApE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CKg7pSCWJLzXPApE rect.text{fill:none;stroke-width:0;}#mermaid-svg-CKg7pSCWJLzXPApE .icon-shape,#mermaid-svg-CKg7pSCWJLzXPApE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CKg7pSCWJLzXPApE .icon-shape p,#mermaid-svg-CKg7pSCWJLzXPApE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CKg7pSCWJLzXPApE .icon-shape .label rect,#mermaid-svg-CKg7pSCWJLzXPApE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CKg7pSCWJLzXPApE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CKg7pSCWJLzXPApE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CKg7pSCWJLzXPApE :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
ReAct循环
需要工具
继续
已有答案
思考 Think分析当前状态决定下一步
行动 Act调用工具执行操作
观察 Observe处理工具结果更新状态
结束返回最终答案
class ReActLoopController:
"""ReAct循环控制器"""
def __init__(self, max_iterations: int = 10):
self.max_iterations = max_iterations
self.iteration_history: List[dict] = []
async def run_react_loop(
self,
think_func: Callable,
act_func: Callable,
observe_func: Callable,
initial_state: GraphState
) –> GraphState:
"""运行ReAct循环
Args:
think_func: 思考函数
act_func: 行动函数
observe_func: 观察函数
initial_state: 初始状态
Returns:
最终状态
"""
state = initial_state
state.max_iterations = self.max_iterations
while not state.is_finished() and not state.is_max_iterations():
# Think: 思考
state = await self._run_step("think", think_func, state)
if state.is_finished():
break
# Act: 行动
if state.tool_calls:
state = await self._run_step("act", act_func, state)
# Observe: 观察
state = await self._run_step("observe", observe_func, state)
state.iteration_count += 1
if not state.final_answer and not state.error:
state.final_answer = "达到最大迭代次数,未能得出最终答案"
return state
async def _run_step(
self,
step_name: str,
func: Callable,
state: GraphState
) –> GraphState:
"""执行单步"""
start_time = time.time()
try:
if asyncio.iscoroutinefunction(func):
new_state = await func(state)
else:
new_state = func(state)
self.iteration_history.append({
"step": step_name,
"iteration": state.iteration_count,
"duration_ms": (time.time() – start_time) * 1000,
"success": True
})
return new_state
except Exception as e:
self.iteration_history.append({
"step": step_name,
"iteration": state.iteration_count,
"duration_ms": (time.time() – start_time) * 1000,
"success": False,
"error": str(e)
})
state.error = f"{step_name}步骤失败: {str(e)}"
return state
5.5 并行执行
当LLM返回多个工具调用时,编排引擎可以并行执行这些工具,大幅减少总执行时间。
#mermaid-svg-5DHhvOD13VvkSSjY{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-5DHhvOD13VvkSSjY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5DHhvOD13VvkSSjY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5DHhvOD13VvkSSjY .error-icon{fill:#552222;}#mermaid-svg-5DHhvOD13VvkSSjY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5DHhvOD13VvkSSjY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5DHhvOD13VvkSSjY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5DHhvOD13VvkSSjY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5DHhvOD13VvkSSjY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5DHhvOD13VvkSSjY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5DHhvOD13VvkSSjY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5DHhvOD13VvkSSjY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5DHhvOD13VvkSSjY .marker.cross{stroke:#333333;}#mermaid-svg-5DHhvOD13VvkSSjY svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5DHhvOD13VvkSSjY p{margin:0;}#mermaid-svg-5DHhvOD13VvkSSjY .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-5DHhvOD13VvkSSjY .cluster-label text{fill:#333;}#mermaid-svg-5DHhvOD13VvkSSjY .cluster-label span{color:#333;}#mermaid-svg-5DHhvOD13VvkSSjY .cluster-label span p{background-color:transparent;}#mermaid-svg-5DHhvOD13VvkSSjY .label text,#mermaid-svg-5DHhvOD13VvkSSjY span{fill:#333;color:#333;}#mermaid-svg-5DHhvOD13VvkSSjY .node rect,#mermaid-svg-5DHhvOD13VvkSSjY .node circle,#mermaid-svg-5DHhvOD13VvkSSjY .node ellipse,#mermaid-svg-5DHhvOD13VvkSSjY .node polygon,#mermaid-svg-5DHhvOD13VvkSSjY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5DHhvOD13VvkSSjY .rough-node .label text,#mermaid-svg-5DHhvOD13VvkSSjY .node .label text,#mermaid-svg-5DHhvOD13VvkSSjY .image-shape .label,#mermaid-svg-5DHhvOD13VvkSSjY .icon-shape .label{text-anchor:middle;}#mermaid-svg-5DHhvOD13VvkSSjY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5DHhvOD13VvkSSjY .rough-node .label,#mermaid-svg-5DHhvOD13VvkSSjY .node .label,#mermaid-svg-5DHhvOD13VvkSSjY .image-shape .label,#mermaid-svg-5DHhvOD13VvkSSjY .icon-shape .label{text-align:center;}#mermaid-svg-5DHhvOD13VvkSSjY .node.clickable{cursor:pointer;}#mermaid-svg-5DHhvOD13VvkSSjY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5DHhvOD13VvkSSjY .arrowheadPath{fill:#333333;}#mermaid-svg-5DHhvOD13VvkSSjY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5DHhvOD13VvkSSjY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5DHhvOD13VvkSSjY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5DHhvOD13VvkSSjY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5DHhvOD13VvkSSjY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5DHhvOD13VvkSSjY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5DHhvOD13VvkSSjY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5DHhvOD13VvkSSjY .cluster text{fill:#333;}#mermaid-svg-5DHhvOD13VvkSSjY .cluster span{color:#333;}#mermaid-svg-5DHhvOD13VvkSSjY 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-5DHhvOD13VvkSSjY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5DHhvOD13VvkSSjY rect.text{fill:none;stroke-width:0;}#mermaid-svg-5DHhvOD13VvkSSjY .icon-shape,#mermaid-svg-5DHhvOD13VvkSSjY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5DHhvOD13VvkSSjY .icon-shape p,#mermaid-svg-5DHhvOD13VvkSSjY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5DHhvOD13VvkSSjY .icon-shape .label rect,#mermaid-svg-5DHhvOD13VvkSSjY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5DHhvOD13VvkSSjY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5DHhvOD13VvkSSjY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5DHhvOD13VvkSSjY :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
串行 vs 并行执行对比
并行执行
工具A (2s)
总耗时: 3s (取最长)
工具B (3s)
工具C (1s)
串行执行
工具A (2s)
工具B (3s)
工具C (1s)
总耗时: 6s
class ParallelExecutor:
"""并行执行器"""
def __init__(self, max_concurrency: int = 5):
self.max_concurrency = max_concurrency
self.semaphore = asyncio.Semaphore(max_concurrency)
async def execute_tools_parallel(
self,
tool_calls: List[ToolCallResult],
tool_executor: Callable
) –> List[dict]:
"""并行执行多个工具调用
Args:
tool_calls: 工具调用列表
tool_executor: 工具执行函数
Returns:
工具结果列表(顺序与输入一致)
"""
tasks = [
self._execute_single(tc, tool_executor, idx)
for idx, tc in enumerate(tool_calls)
]
results = await asyncio.gather(*tasks, return_exceptions=True)
# 处理异常结果
processed = []
for idx, result in enumerate(results):
if isinstance(result, Exception):
processed.append({
"tool_name": tool_calls[idx].tool_name,
"success": False,
"error": str(result),
"result": None
})
else:
processed.append(result)
return processed
async def _execute_single(
self,
tool_call: ToolCallResult,
executor: Callable,
index: int
) –> dict:
"""执行单个工具调用(带并发控制)"""
async with self.semaphore:
try:
if asyncio.iscoroutinefunction(executor):
result = await executor(tool_call)
else:
result = executor(tool_call)
return {
"tool_name": tool_call.tool_name,
"success": True,
"result": result,
"index": index
}
except Exception as e:
return {
"tool_name": tool_call.tool_name,
"success": False,
"error": str(e),
"result": None,
"index": index
}
5.6 子图嵌套
复杂任务可以分解为多个子图,每个子图独立运行,结果回传给父图。
#mermaid-svg-lufsynzEh7JUSrxP{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-lufsynzEh7JUSrxP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lufsynzEh7JUSrxP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lufsynzEh7JUSrxP .error-icon{fill:#552222;}#mermaid-svg-lufsynzEh7JUSrxP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lufsynzEh7JUSrxP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lufsynzEh7JUSrxP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lufsynzEh7JUSrxP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lufsynzEh7JUSrxP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lufsynzEh7JUSrxP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lufsynzEh7JUSrxP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lufsynzEh7JUSrxP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lufsynzEh7JUSrxP .marker.cross{stroke:#333333;}#mermaid-svg-lufsynzEh7JUSrxP svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lufsynzEh7JUSrxP p{margin:0;}#mermaid-svg-lufsynzEh7JUSrxP .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-lufsynzEh7JUSrxP .cluster-label text{fill:#333;}#mermaid-svg-lufsynzEh7JUSrxP .cluster-label span{color:#333;}#mermaid-svg-lufsynzEh7JUSrxP .cluster-label span p{background-color:transparent;}#mermaid-svg-lufsynzEh7JUSrxP .label text,#mermaid-svg-lufsynzEh7JUSrxP span{fill:#333;color:#333;}#mermaid-svg-lufsynzEh7JUSrxP .node rect,#mermaid-svg-lufsynzEh7JUSrxP .node circle,#mermaid-svg-lufsynzEh7JUSrxP .node ellipse,#mermaid-svg-lufsynzEh7JUSrxP .node polygon,#mermaid-svg-lufsynzEh7JUSrxP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lufsynzEh7JUSrxP .rough-node .label text,#mermaid-svg-lufsynzEh7JUSrxP .node .label text,#mermaid-svg-lufsynzEh7JUSrxP .image-shape .label,#mermaid-svg-lufsynzEh7JUSrxP .icon-shape .label{text-anchor:middle;}#mermaid-svg-lufsynzEh7JUSrxP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lufsynzEh7JUSrxP .rough-node .label,#mermaid-svg-lufsynzEh7JUSrxP .node .label,#mermaid-svg-lufsynzEh7JUSrxP .image-shape .label,#mermaid-svg-lufsynzEh7JUSrxP .icon-shape .label{text-align:center;}#mermaid-svg-lufsynzEh7JUSrxP .node.clickable{cursor:pointer;}#mermaid-svg-lufsynzEh7JUSrxP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lufsynzEh7JUSrxP .arrowheadPath{fill:#333333;}#mermaid-svg-lufsynzEh7JUSrxP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lufsynzEh7JUSrxP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lufsynzEh7JUSrxP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lufsynzEh7JUSrxP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lufsynzEh7JUSrxP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lufsynzEh7JUSrxP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lufsynzEh7JUSrxP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lufsynzEh7JUSrxP .cluster text{fill:#333;}#mermaid-svg-lufsynzEh7JUSrxP .cluster span{color:#333;}#mermaid-svg-lufsynzEh7JUSrxP 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-lufsynzEh7JUSrxP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lufsynzEh7JUSrxP rect.text{fill:none;stroke-width:0;}#mermaid-svg-lufsynzEh7JUSrxP .icon-shape,#mermaid-svg-lufsynzEh7JUSrxP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lufsynzEh7JUSrxP .icon-shape p,#mermaid-svg-lufsynzEh7JUSrxP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lufsynzEh7JUSrxP .icon-shape .label rect,#mermaid-svg-lufsynzEh7JUSrxP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lufsynzEh7JUSrxP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lufsynzEh7JUSrxP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lufsynzEh7JUSrxP :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
子图2:计算
子图1:搜索
父图
开始
思考:需要搜索+计算
子图1:搜索
子图2:计算
结果合并
结束
搜索关键词提取
执行搜索
结果过滤
返回结果
解析算式
执行计算
验证结果
返回结果
class SubGraphManager:
"""子图管理器"""
def __init__(self):
self.sub_graphs: Dict[str, StateGraph] = {}
def register_sub_graph(self, name: str, graph: StateGraph):
"""注册子图"""
self.sub_graphs[name] = graph
async def execute_sub_graph(
self,
name: str,
input_state: GraphState
) –> GraphState:
"""执行子图"""
graph = self.sub_graphs.get(name)
if graph is None:
raise ValueError(f"子图 {name} 未注册")
# 执行子图
result_state = await graph.execute(input_state)
return result_state
async def execute_multiple_sub_graphs(
self,
sub_graph_specs: List[tuple], # [(name, input_state), …]
parallel: bool = True
) –> List[GraphState]:
"""执行多个子图
Args:
sub_graph_specs: 子图规格列表
parallel: 是否并行执行
"""
if parallel:
tasks = [
self.execute_sub_graph(name, state)
for name, state in sub_graph_specs
]
return await asyncio.gather(*tasks)
else:
results = []
for name, state in sub_graph_specs:
result = await self.execute_sub_graph(name, state)
results.append(result)
return results
5.7 错误恢复策略
#mermaid-svg-JaDWbFwz6R8iC48E{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-JaDWbFwz6R8iC48E .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JaDWbFwz6R8iC48E .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JaDWbFwz6R8iC48E .error-icon{fill:#552222;}#mermaid-svg-JaDWbFwz6R8iC48E .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JaDWbFwz6R8iC48E .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JaDWbFwz6R8iC48E .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JaDWbFwz6R8iC48E .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JaDWbFwz6R8iC48E .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JaDWbFwz6R8iC48E .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JaDWbFwz6R8iC48E .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JaDWbFwz6R8iC48E .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JaDWbFwz6R8iC48E .marker.cross{stroke:#333333;}#mermaid-svg-JaDWbFwz6R8iC48E svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JaDWbFwz6R8iC48E p{margin:0;}#mermaid-svg-JaDWbFwz6R8iC48E .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-JaDWbFwz6R8iC48E .cluster-label text{fill:#333;}#mermaid-svg-JaDWbFwz6R8iC48E .cluster-label span{color:#333;}#mermaid-svg-JaDWbFwz6R8iC48E .cluster-label span p{background-color:transparent;}#mermaid-svg-JaDWbFwz6R8iC48E .label text,#mermaid-svg-JaDWbFwz6R8iC48E span{fill:#333;color:#333;}#mermaid-svg-JaDWbFwz6R8iC48E .node rect,#mermaid-svg-JaDWbFwz6R8iC48E .node circle,#mermaid-svg-JaDWbFwz6R8iC48E .node ellipse,#mermaid-svg-JaDWbFwz6R8iC48E .node polygon,#mermaid-svg-JaDWbFwz6R8iC48E .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JaDWbFwz6R8iC48E .rough-node .label text,#mermaid-svg-JaDWbFwz6R8iC48E .node .label text,#mermaid-svg-JaDWbFwz6R8iC48E .image-shape .label,#mermaid-svg-JaDWbFwz6R8iC48E .icon-shape .label{text-anchor:middle;}#mermaid-svg-JaDWbFwz6R8iC48E .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JaDWbFwz6R8iC48E .rough-node .label,#mermaid-svg-JaDWbFwz6R8iC48E .node .label,#mermaid-svg-JaDWbFwz6R8iC48E .image-shape .label,#mermaid-svg-JaDWbFwz6R8iC48E .icon-shape .label{text-align:center;}#mermaid-svg-JaDWbFwz6R8iC48E .node.clickable{cursor:pointer;}#mermaid-svg-JaDWbFwz6R8iC48E .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JaDWbFwz6R8iC48E .arrowheadPath{fill:#333333;}#mermaid-svg-JaDWbFwz6R8iC48E .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JaDWbFwz6R8iC48E .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JaDWbFwz6R8iC48E .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JaDWbFwz6R8iC48E .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JaDWbFwz6R8iC48E .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JaDWbFwz6R8iC48E .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JaDWbFwz6R8iC48E .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JaDWbFwz6R8iC48E .cluster text{fill:#333;}#mermaid-svg-JaDWbFwz6R8iC48E .cluster span{color:#333;}#mermaid-svg-JaDWbFwz6R8iC48E 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-JaDWbFwz6R8iC48E .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JaDWbFwz6R8iC48E rect.text{fill:none;stroke-width:0;}#mermaid-svg-JaDWbFwz6R8iC48E .icon-shape,#mermaid-svg-JaDWbFwz6R8iC48E .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JaDWbFwz6R8iC48E .icon-shape p,#mermaid-svg-JaDWbFwz6R8iC48E .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JaDWbFwz6R8iC48E .icon-shape .label rect,#mermaid-svg-JaDWbFwz6R8iC48E .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JaDWbFwz6R8iC48E .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JaDWbFwz6R8iC48E .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JaDWbFwz6R8iC48E :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
网络超时
参数错误
权限不足
模型不可用
未知错误
成功
失败
成功
失败
成功
失败
工具执行失败
错误分类
重试策略指数退避重试3次
修复策略LLM重新生成参数
跳过策略跳过并告知用户
降级策略切换备选模型
安全策略返回错误信息
继续执行
终止流程
class ErrorRecoveryHandler:
"""错误恢复处理器"""
def __init__(self, max_retries: int = 3, base_delay: float = 1.0):
self.max_retries = max_retries
self.base_delay = base_delay
self.error_history: List[dict] = []
async def handle_error(
self,
error: Exception,
context: dict,
retry_func: Callable = None
) –> dict:
"""处理错误
Returns:
{"action": "retry|fix|skip|fallback|abort", "result": …}
"""
error_type = self._classify_error(error)
self.error_history.append({
"error": str(error),
"type": error_type,
"context": context,
"timestamp": time.time()
})
if error_type == "timeout" and retry_func:
return await self._retry_with_backoff(retry_func, context)
elif error_type == "param_error":
return {"action": "fix", "message": "需要LLM重新生成参数"}
elif error_type == "permission":
return {"action": "skip", "message": "权限不足,跳过此工具"}
elif error_type == "model_unavailable":
return {"action": "fallback", "message": "切换到备选模型"}
else:
return {"action": "abort", "message": f"未知错误: {str(error)}"}
def _classify_error(self, error: Exception) –> str:
"""分类错误类型"""
error_str = str(error).lower()
if "timeout" in error_str or "timed out" in error_str:
return "timeout"
elif "param" in error_str or "argument" in error_str:
return "param_error"
elif "permission" in error_str or "unauthorized" in error_str:
return "permission"
elif "model" in error_str or "api" in error_str:
return "model_unavailable"
else:
return "unknown"
async def _retry_with_backoff(
self,
retry_func: Callable,
context: dict
) –> dict:
"""指数退避重试"""
for attempt in range(self.max_retries):
delay = self.base_delay * (2 ** attempt) # 1s, 2s, 4s
await asyncio.sleep(delay)
try:
if asyncio.iscoroutinefunction(retry_func):
result = await retry_func(context)
else:
result = retry_func(context)
return {"action": "retry", "result": result, "attempts": attempt + 1}
except Exception as e:
continue
return {"action": "fallback", "message": "重试失败,需要降级"}
5.8 编排引擎完整组装
class OrchestrationEngine:
"""编排引擎——组装所有组件"""
def __init__(self, llm_engine: LLMEngineManager, tool_layer=None):
self.llm_engine = llm_engine
self.tool_layer = tool_layer
self.graph = self._build_default_graph()
self.react_controller = ReActLoopController(max_iterations=10)
self.parallel_executor = ParallelExecutor(max_concurrency=5)
self.error_handler = ErrorRecoveryHandler(max_retries=3)
self.sub_graph_manager = SubGraphManager()
def _build_default_graph(self) –> StateGraph:
"""构建默认的ReAct状态图"""
graph = StateGraph()
# 添加节点
graph.add_node(Node(
name="start",
node_type=NodeType.START,
execute_func=self._start_node
))
graph.add_node(Node(
name="think",
node_type=NodeType.THINK,
execute_func=self._think_node
))
graph.add_node(Node(
name="act",
node_type=NodeType.ACT,
execute_func=self._act_node
))
graph.add_node(Node(
name="observe",
node_type=NodeType.OBSERVE,
execute_func=self._observe_node
))
graph.add_node(Node(
name="end",
node_type=NodeType.END,
execute_func=self._end_node
))
graph.add_node(Node(
name="error",
node_type=NodeType.ERROR,
execute_func=self._error_node
))
# 添加边(带条件路由)
graph.add_edge(Edge(source="start", target="think"))
graph.add_edge(Edge(
source="think", target="act",
condition=ConditionalRouter.needs_tool_call
))
graph.add_edge(Edge(
source="think", target="end",
condition=ConditionalRouter.has_final_answer
))
graph.add_edge(Edge(source="act", target="observe"))
graph.add_edge(Edge(
source="observe", target="think",
condition=lambda s: not ConditionalRouter.has_error(s)
))
graph.add_edge(Edge(
source="observe", target="error",
condition=ConditionalRouter.has_error
))
graph.add_edge(Edge(
source="error", target="think",
condition=ConditionalRouter.should_retry
))
graph.add_edge(Edge(
source="error", target="end",
condition=lambda s: not ConditionalRouter.should_retry(s)
))
graph.set_entry_point("start")
return graph
async def _start_node(self, state: GraphState) –> GraphState:
"""开始节点"""
return state
async def _think_node(self, state: GraphState) –> GraphState:
"""思考节点——调用LLM"""
response = await self.llm_engine.invoke(
messages=state.messages,
tools=self._get_available_tools()
)
state.current_thought = response.content
state.tool_calls = [
{"name": tc.tool_name, "arguments": tc.arguments}
for tc in response.tool_calls
]
if not response.tool_calls:
state.final_answer = response.content
return state
async def _act_node(self, state: GraphState) –> GraphState:
"""行动节点——执行工具"""
if state.tool_calls and self.tool_layer:
results = await self.parallel_executor.execute_tools_parallel(
[ToolCallResult(
tool_name=tc["name"],
arguments=tc["arguments"]
) for tc in state.tool_calls],
self.tool_layer.execute_tool
)
state.tool_results = results
return state
async def _observe_node(self, state: GraphState) –> GraphState:
"""观察节点——处理工具结果"""
for result in state.tool_results:
if result.get("success"):
state.messages.append({
"role": "tool",
"content": str(result["result"]),
"tool_name": result["tool_name"]
})
else:
state.error = result.get("error", "工具执行失败")
state.tool_calls = [] # 清空当前工具调用
return state
async def _end_node(self, state: GraphState) –> GraphState:
"""结束节点"""
return state
async def _error_node(self, state: GraphState) –> GraphState:
"""错误节点"""
recovery = await self.error_handler.handle_error(
Exception(state.error or "未知错误"),
{"state": state}
)
if recovery["action"] == "abort":
state.final_answer = f"执行失败: {state.error}"
elif recovery["action"] == "retry":
state.error = None # 清除错误,准备重试
return state
def _get_available_tools(self) –> List[dict]:
"""获取可用工具列表"""
if self.tool_layer:
return self.tool_layer.get_tool_schemas()
return []
async def run(self, messages: List[dict]) –> GraphState:
"""运行编排引擎"""
initial_state = GraphState(
messages=messages,
max_iterations=10
)
return await self.graph.execute(initial_state)
通俗理解:编排引擎就像交响乐指挥——指挥不需要亲自演奏乐器(不需要自己生成文本),但他决定什么时候小提琴入场(调用工具)、什么时候全体合奏(并行执行)、什么时候重复一段乐章(循环控制)、出了错怎么补救(错误恢复)。
六、Layer 4 工具标准层详解
6.1 工具标准层的核心职责
工具标准层是Agent与外部世界交互的"手"。它负责:
#mermaid-svg-oXLLJmu1mnqNMqmc{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-oXLLJmu1mnqNMqmc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oXLLJmu1mnqNMqmc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oXLLJmu1mnqNMqmc .error-icon{fill:#552222;}#mermaid-svg-oXLLJmu1mnqNMqmc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oXLLJmu1mnqNMqmc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oXLLJmu1mnqNMqmc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oXLLJmu1mnqNMqmc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oXLLJmu1mnqNMqmc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oXLLJmu1mnqNMqmc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oXLLJmu1mnqNMqmc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oXLLJmu1mnqNMqmc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oXLLJmu1mnqNMqmc .marker.cross{stroke:#333333;}#mermaid-svg-oXLLJmu1mnqNMqmc svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oXLLJmu1mnqNMqmc p{margin:0;}#mermaid-svg-oXLLJmu1mnqNMqmc .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-oXLLJmu1mnqNMqmc .cluster-label text{fill:#333;}#mermaid-svg-oXLLJmu1mnqNMqmc .cluster-label span{color:#333;}#mermaid-svg-oXLLJmu1mnqNMqmc .cluster-label span p{background-color:transparent;}#mermaid-svg-oXLLJmu1mnqNMqmc .label text,#mermaid-svg-oXLLJmu1mnqNMqmc span{fill:#333;color:#333;}#mermaid-svg-oXLLJmu1mnqNMqmc .node rect,#mermaid-svg-oXLLJmu1mnqNMqmc .node circle,#mermaid-svg-oXLLJmu1mnqNMqmc .node ellipse,#mermaid-svg-oXLLJmu1mnqNMqmc .node polygon,#mermaid-svg-oXLLJmu1mnqNMqmc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oXLLJmu1mnqNMqmc .rough-node .label text,#mermaid-svg-oXLLJmu1mnqNMqmc .node .label text,#mermaid-svg-oXLLJmu1mnqNMqmc .image-shape .label,#mermaid-svg-oXLLJmu1mnqNMqmc .icon-shape .label{text-anchor:middle;}#mermaid-svg-oXLLJmu1mnqNMqmc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oXLLJmu1mnqNMqmc .rough-node .label,#mermaid-svg-oXLLJmu1mnqNMqmc .node .label,#mermaid-svg-oXLLJmu1mnqNMqmc .image-shape .label,#mermaid-svg-oXLLJmu1mnqNMqmc .icon-shape .label{text-align:center;}#mermaid-svg-oXLLJmu1mnqNMqmc .node.clickable{cursor:pointer;}#mermaid-svg-oXLLJmu1mnqNMqmc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oXLLJmu1mnqNMqmc .arrowheadPath{fill:#333333;}#mermaid-svg-oXLLJmu1mnqNMqmc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oXLLJmu1mnqNMqmc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oXLLJmu1mnqNMqmc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oXLLJmu1mnqNMqmc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oXLLJmu1mnqNMqmc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oXLLJmu1mnqNMqmc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oXLLJmu1mnqNMqmc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oXLLJmu1mnqNMqmc .cluster text{fill:#333;}#mermaid-svg-oXLLJmu1mnqNMqmc .cluster span{color:#333;}#mermaid-svg-oXLLJmu1mnqNMqmc 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-oXLLJmu1mnqNMqmc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oXLLJmu1mnqNMqmc rect.text{fill:none;stroke-width:0;}#mermaid-svg-oXLLJmu1mnqNMqmc .icon-shape,#mermaid-svg-oXLLJmu1mnqNMqmc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oXLLJmu1mnqNMqmc .icon-shape p,#mermaid-svg-oXLLJmu1mnqNMqmc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oXLLJmu1mnqNMqmc .icon-shape .label rect,#mermaid-svg-oXLLJmu1mnqNMqmc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oXLLJmu1mnqNMqmc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oXLLJmu1mnqNMqmc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oXLLJmu1mnqNMqmc :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
工具标准层架构
协议层
执行管理
工具管理
MCP协议适配器Model Context Protocol
工具注册中心注册/注销工具
工具发现服务动态发现可用工具
Schema管理器参数/返回值Schema
参数验证器调用前验证
沙箱执行器隔离执行环境
结果处理器标准化返回
Function Calling适配器OpenAI格式
A2A协议适配器Agent间通信
6.2 MCP协议详解
2026年,MCP(Model Context Protocol)已成为事实上的工具标准。由Anthropic提出的MCP协议定义了Agent与工具之间的标准通信方式。
| MCP | Anthropic | 通用工具协议 | 主流厂商全面支持 | 通用工具调用 |
| Function Calling | OpenAI | 动态Schema | 广泛支持 | OpenAI生态 |
| A2A | Agent间通信 | 新兴标准 | 多Agent协作 |
MCP的核心优势在于它定义了"工具供应端"标准——工具提供方只需实现一次MCP接口,所有支持MCP的Agent都能自动发现并使用该工具。
#mermaid-svg-hHeYKhOb13DcsFGK{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-hHeYKhOb13DcsFGK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hHeYKhOb13DcsFGK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hHeYKhOb13DcsFGK .error-icon{fill:#552222;}#mermaid-svg-hHeYKhOb13DcsFGK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hHeYKhOb13DcsFGK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hHeYKhOb13DcsFGK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hHeYKhOb13DcsFGK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hHeYKhOb13DcsFGK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hHeYKhOb13DcsFGK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hHeYKhOb13DcsFGK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hHeYKhOb13DcsFGK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hHeYKhOb13DcsFGK .marker.cross{stroke:#333333;}#mermaid-svg-hHeYKhOb13DcsFGK svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hHeYKhOb13DcsFGK p{margin:0;}#mermaid-svg-hHeYKhOb13DcsFGK .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-hHeYKhOb13DcsFGK .cluster-label text{fill:#333;}#mermaid-svg-hHeYKhOb13DcsFGK .cluster-label span{color:#333;}#mermaid-svg-hHeYKhOb13DcsFGK .cluster-label span p{background-color:transparent;}#mermaid-svg-hHeYKhOb13DcsFGK .label text,#mermaid-svg-hHeYKhOb13DcsFGK span{fill:#333;color:#333;}#mermaid-svg-hHeYKhOb13DcsFGK .node rect,#mermaid-svg-hHeYKhOb13DcsFGK .node circle,#mermaid-svg-hHeYKhOb13DcsFGK .node ellipse,#mermaid-svg-hHeYKhOb13DcsFGK .node polygon,#mermaid-svg-hHeYKhOb13DcsFGK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hHeYKhOb13DcsFGK .rough-node .label text,#mermaid-svg-hHeYKhOb13DcsFGK .node .label text,#mermaid-svg-hHeYKhOb13DcsFGK .image-shape .label,#mermaid-svg-hHeYKhOb13DcsFGK .icon-shape .label{text-anchor:middle;}#mermaid-svg-hHeYKhOb13DcsFGK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hHeYKhOb13DcsFGK .rough-node .label,#mermaid-svg-hHeYKhOb13DcsFGK .node .label,#mermaid-svg-hHeYKhOb13DcsFGK .image-shape .label,#mermaid-svg-hHeYKhOb13DcsFGK .icon-shape .label{text-align:center;}#mermaid-svg-hHeYKhOb13DcsFGK .node.clickable{cursor:pointer;}#mermaid-svg-hHeYKhOb13DcsFGK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hHeYKhOb13DcsFGK .arrowheadPath{fill:#333333;}#mermaid-svg-hHeYKhOb13DcsFGK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hHeYKhOb13DcsFGK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hHeYKhOb13DcsFGK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hHeYKhOb13DcsFGK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hHeYKhOb13DcsFGK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hHeYKhOb13DcsFGK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hHeYKhOb13DcsFGK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hHeYKhOb13DcsFGK .cluster text{fill:#333;}#mermaid-svg-hHeYKhOb13DcsFGK .cluster span{color:#333;}#mermaid-svg-hHeYKhOb13DcsFGK 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-hHeYKhOb13DcsFGK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hHeYKhOb13DcsFGK rect.text{fill:none;stroke-width:0;}#mermaid-svg-hHeYKhOb13DcsFGK .icon-shape,#mermaid-svg-hHeYKhOb13DcsFGK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hHeYKhOb13DcsFGK .icon-shape p,#mermaid-svg-hHeYKhOb13DcsFGK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hHeYKhOb13DcsFGK .icon-shape .label rect,#mermaid-svg-hHeYKhOb13DcsFGK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hHeYKhOb13DcsFGK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hHeYKhOb13DcsFGK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hHeYKhOb13DcsFGK :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
MCP协议架构
工具服务器(MCP服务端)
MCP请求
路由
路由
路由
路由
MCP响应
MCP响应
MCP响应
MCP响应
标准化结果
文件系统工具
Agent(MCP客户端)
MCP代理层发现+路由
数据库工具
Web搜索工具
代码执行工具
6.3 工具注册与发现
from dataclasses import dataclass, field
from typing import Any, Callable, Optional, List, Dict
from abc import ABC, abstractmethod
import json
import inspect
@dataclass
class ToolSchema:
"""工具Schema定义"""
name: str
description: str
parameters: dict # JSON Schema格式
returns: dict = field(default_factory=dict) # 返回值Schema
tags: List[str] = field(default_factory=list) # 标签用于分类
version: str = "1.0.0"
deprecated: bool = False
def to_openai_format(self) –> dict:
"""转换为OpenAI Function Calling格式"""
return {
"type": "function",
"function": {
"name": self.name,
"description": self.description,
"parameters": self.parameters
}
}
def to_mcp_format(self) –> dict:
"""转换为MCP格式"""
return {
"name": self.name,
"description": self.description,
"inputSchema": self.parameters,
"outputSchema": self.returns,
"tags": self.tags,
"version": self.version
}
@dataclass
class RegisteredTool:
"""已注册的工具"""
schema: ToolSchema
executor: Callable
is_async: bool = False
sandbox_required: bool = False
max_timeout: int = 30 # 秒
class ToolRegistry:
"""工具注册中心"""
def __init__(self):
self.tools: Dict[str, RegisteredTool] = {}
self.tool_categories: Dict[str, List[str]] = {}
def register(
self,
name: str,
description: str,
parameters: dict,
executor: Callable,
tags: List[str] = None,
returns: dict = None,
sandbox_required: bool = False,
max_timeout: int = 30
):
"""注册工具
Args:
name: 工具名称
description: 工具描述
parameters: 参数Schema(JSON Schema)
executor: 执行函数
tags: 分类标签
returns: 返回值Schema
sandbox_required: 是否需要沙箱
max_timeout: 最大超时秒数
"""
schema = ToolSchema(
name=name,
description=description,
parameters=parameters,
returns=returns or {},
tags=tags or []
)
tool = RegisteredTool(
schema=schema,
executor=executor,
is_async=inspect.iscoroutinefunction(executor),
sandbox_required=sandbox_required,
max_timeout=max_timeout
)
self.tools[name] = tool
# 按标签分类
for tag in (tags or ["uncategorized"]):
if tag not in self.tool_categories:
self.tool_categories[tag] = []
self.tool_categories[tag].append(name)
def unregister(self, name: str):
"""注销工具"""
if name in self.tools:
tool = self.tools.pop(name)
for tag in tool.schema.tags:
if tag in self.tool_categories:
self.tool_categories[tag] = [
t for t in self.tool_categories[tag] if t != name
]
def get_tool(self, name: str) –> Optional[RegisteredTool]:
"""获取工具"""
return self.tools.get(name)
def discover_tools(
self,
tags: List[str] = None,
keyword: str = None
) –> List[ToolSchema]:
"""发现工具
Args:
tags: 按标签过滤
keyword: 按关键词搜索
Returns:
匹配的工具Schema列表
"""
results = []
for name, tool in self.tools.items():
if tool.schema.deprecated:
continue
# 标签过滤
if tags:
if not any(t in tool.schema.tags for t in tags):
continue
# 关键词搜索
if keyword:
search_text = (name + " " + tool.schema.description).lower()
if keyword.lower() not in search_text:
continue
results.append(tool.schema)
return results
def get_tool_schemas(self) –> List[dict]:
"""获取所有工具的Schema(用于LLM)"""
return [
t.schema.to_openai_format()
for t in self.tools.values()
if not t.schema.deprecated
]
def list_all(self) –> List[str]:
"""列出所有工具名称"""
return list(self.tools.keys())
通俗理解:工具注册中心就像酒店的"服务目录"——前台有一本目录列出所有可用服务(工具),每个服务都有详细的说明(Schema)告诉你怎么使用(参数)。新服务可以随时加入目录(注册),过时的服务可以撤掉(注销)。
6.4 参数验证
class ParameterValidator:
"""参数验证器"""
@staticmethod
def validate(arguments: dict, schema: dict) –> tuple:
"""验证参数是否符合Schema
Returns:
(is_valid: bool, errors: List[str])
"""
errors = []
properties = schema.get("properties", {})
required = schema.get("required", [])
# 检查必需参数
for req in required:
if req not in arguments:
errors.append(f"缺少必需参数: {req}")
# 检查参数类型和约束
for key, value in arguments.items():
if key not in properties:
# 允许额外参数但记录警告
continue
prop_schema = properties[key]
expected_type = prop_schema.get("type")
# 类型检查
if expected_type == "string" and not isinstance(value, str):
errors.append(f"参数 {key} 应为字符串类型")
elif expected_type == "number" and not isinstance(value, (int, float)):
errors.append(f"参数 {key} 应为数字类型")
elif expected_type == "integer" and not isinstance(value, int):
errors.append(f"参数 {key} 应为整数类型")
elif expected_type == "boolean" and not isinstance(value, bool):
errors.append(f"参数 {key} 应为布尔类型")
elif expected_type == "array" and not isinstance(value, list):
errors.append(f"参数 {key} 应为数组类型")
elif expected_type == "object" and not isinstance(value, dict):
errors.append(f"参数 {key} 应为对象类型")
# 枚举检查
if "enum" in prop_schema and value not in prop_schema["enum"]:
errors.append(
f"参数 {key} 的值必须是 {prop_schema['enum']} 之一"
)
# 范围检查
if expected_type in ("number", "integer"):
if "minimum" in prop_schema and value < prop_schema["minimum"]:
errors.append(f"参数 {key} 不能小于 {prop_schema['minimum']}")
if "maximum" in prop_schema and value > prop_schema["maximum"]:
errors.append(f"参数 {key} 不能大于 {prop_schema['maximum']}")
# 字符串长度检查
if expected_type == "string" and isinstance(value, str):
if "minLength" in prop_schema and len(value) < prop_schema["minLength"]:
errors.append(f"参数 {key} 长度不能小于 {prop_schema['minLength']}")
if "maxLength" in prop_schema and len(value) > prop_schema["maxLength"]:
errors.append(f"参数 {key} 长度不能大于 {prop_schema['maxLength']}")
return (len(errors) == 0, errors)
6.5 沙箱执行
沙箱执行确保工具在隔离环境中运行,防止恶意代码或意外副作用影响主系统。
import subprocess
import tempfile
import os
import asyncio
class SandboxExecutor:
"""沙箱执行器"""
def __init__(
self,
timeout: int = 30,
memory_limit_mb: int = 256,
allowed_paths: List[str] = None
):
self.timeout = timeout
self.memory_limit_mb = memory_limit_mb
self.allowed_paths = allowed_paths or ["/tmp"]
async def execute_in_sandbox(
self,
tool: RegisteredTool,
arguments: dict
) –> dict:
"""在沙箱中执行工具
Returns:
{"success": bool, "result": Any, "error": str, "duration_ms": float}
"""
start_time = time.time()
try:
# 设置超时
result = await asyncio.wait_for(
self._execute(tool, arguments),
timeout=tool.max_timeout or self.timeout
)
duration = (time.time() – start_time) * 1000
return {
"success": True,
"result": result,
"duration_ms": duration
}
except asyncio.TimeoutError:
return {
"success": False,
"error": f"工具执行超时({self.timeout}秒)",
"duration_ms": self.timeout * 1000
}
except Exception as e:
return {
"success": False,
"error": str(e),
"duration_ms": (time.time() – start_time) * 1000
}
async def _execute(self, tool: RegisteredTool, arguments: dict):
"""实际执行工具"""
if tool.is_async:
return await tool.executor(**arguments)
else:
# 在线程池中执行同步函数
loop = asyncio.get_event_loop()
return await loop.run_in_executor(
None,
lambda: tool.executor(**arguments)
)
async def execute_code_sandbox(self, code: str, language: str = "python") –> dict:
"""代码执行沙箱——安全执行用户代码"""
with tempfile.NamedTemporaryFile(
mode="w", suffix=f".{language}", delete=False
) as f:
f.write(code)
temp_file = f.name
try:
if language == "python":
cmd = ["python", temp_file]
else:
return {"success": False, "error": f"不支持的语言: {language}"}
proc = await asyncio.create_subprocess_exec(
*cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
try:
stdout, stderr = await asyncio.wait_for(
proc.communicate(),
timeout=self.timeout
)
return {
"success": proc.returncode == 0,
"result": stdout.decode("utf-8"),
"error": stderr.decode("utf-8") if stderr else "",
"return_code": proc.returncode
}
except asyncio.TimeoutError:
proc.kill()
return {"success": False, "error": "代码执行超时"}
finally:
os.unlink(temp_file)
6.6 工具标准层完整实现
class ToolStandardLayer:
"""工具标准层完整实现"""
def __init__(self):
self.registry = ToolRegistry()
self.validator = ParameterValidator()
self.sandbox = SandboxExecutor(timeout=30)
def register_tool(
self,
name: str,
description: str,
parameters: dict,
executor: Callable,
**kwargs
):
"""注册工具"""
self.registry.register(
name=name,
description=description,
parameters=parameters,
executor=executor,
**kwargs
)
async def execute_tool(self, tool_call: ToolCallResult) –> dict:
"""执行工具调用(完整流程)"""
# 1. 查找工具
tool = self.registry.get_tool(tool_call.tool_name)
if tool is None:
return {"success": False, "error": f"工具 {tool_call.tool_name} 未注册"}
# 2. 验证参数
is_valid, errors = self.validator.validate(
tool_call.arguments,
tool.schema.parameters
)
if not is_valid:
return {"success": False, "error": f"参数验证失败: {errors}"}
# 3. 沙箱执行
result = await self.sandbox.execute_in_sandbox(tool, tool_call.arguments)
# 4. 结果标准化
if result["success"]:
result["tool_name"] = tool_call.tool_name
result["arguments"] = tool_call.arguments
return result
def get_tool_schemas(self) –> List[dict]:
"""获取所有工具Schema"""
return self.registry.get_tool_schemas()
def discover_tools(self, **kwargs) –> List[ToolSchema]:
"""发现工具"""
return self.registry.discover_tools(**kwargs)
6.7 预置工具示例
# === 预置工具注册示例 ===
# 搜索工具
def web_search(query: str, max_results: int = 5) –> dict:
"""网页搜索工具"""
# 模拟搜索结果
return {
"query": query,
"results": [
{"title": f"搜索结果{i+1}", "url": f"https://example.com/{i+1}", "snippet": "…"}
for i in range(max_results)
]
}
# 计算器工具
def calculator(expression: str) –> dict:
"""数学计算工具"""
try:
# 安全评估表达式(实际使用时需严格限制)
allowed = set("0123456789+-*/.() ")
if not all(c in allowed for c in expression):
return {"error": "表达式包含非法字符"}
result = eval(expression)
return {"expression": expression, "result": result}
except Exception as e:
return {"error": str(e)}
# 文件读取工具
def read_file(filepath: str, encoding: str = "utf-8") –> dict:
"""文件读取工具"""
try:
with open(filepath, "r", encoding=encoding) as f:
content = f.read()
return {"filepath": filepath, "content": content[:10000]} # 限制长度
except Exception as e:
return {"error": str(e)}
# 注册预置工具
tool_layer = ToolStandardLayer()
tool_layer.register_tool(
name="web_search",
description="搜索互联网获取信息",
parameters={
"type": "object",
"properties": {
"query": {"type": "string", "description": "搜索关键词"},
"max_results": {"type": "integer", "minimum": 1, "maximum": 20, "default": 5}
},
"required": ["query"]
},
executor=web_search,
tags=["search", "web"],
sandbox_required=False
)
tool_layer.register_tool(
name="calculator",
description="执行数学计算",
parameters={
"type": "object",
"properties": {
"expression": {"type": "string", "description": "数学表达式"}
},
"required": ["expression"]
},
executor=calculator,
tags=["math", "utility"],
sandbox_required=True
)
tool_layer.register_tool(
name="read_file",
description="读取本地文件内容",
parameters={
"type": "object",
"properties": {
"filepath": {"type": "string", "description": "文件路径"},
"encoding": {"type": "string", "default": "utf-8"}
},
"required": ["filepath"]
},
executor=read_file,
tags=["file", "io"],
sandbox_required=True
)
七、Layer 5 记忆层详解
7.1 记忆层的核心职责
记忆层是Agent的"记忆系统"。它负责:
#mermaid-svg-gtlu7h0Mti0DfPmc{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-gtlu7h0Mti0DfPmc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gtlu7h0Mti0DfPmc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gtlu7h0Mti0DfPmc .error-icon{fill:#552222;}#mermaid-svg-gtlu7h0Mti0DfPmc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gtlu7h0Mti0DfPmc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gtlu7h0Mti0DfPmc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gtlu7h0Mti0DfPmc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gtlu7h0Mti0DfPmc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gtlu7h0Mti0DfPmc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gtlu7h0Mti0DfPmc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gtlu7h0Mti0DfPmc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gtlu7h0Mti0DfPmc .marker.cross{stroke:#333333;}#mermaid-svg-gtlu7h0Mti0DfPmc svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gtlu7h0Mti0DfPmc p{margin:0;}#mermaid-svg-gtlu7h0Mti0DfPmc .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-gtlu7h0Mti0DfPmc .cluster-label text{fill:#333;}#mermaid-svg-gtlu7h0Mti0DfPmc .cluster-label span{color:#333;}#mermaid-svg-gtlu7h0Mti0DfPmc .cluster-label span p{background-color:transparent;}#mermaid-svg-gtlu7h0Mti0DfPmc .label text,#mermaid-svg-gtlu7h0Mti0DfPmc span{fill:#333;color:#333;}#mermaid-svg-gtlu7h0Mti0DfPmc .node rect,#mermaid-svg-gtlu7h0Mti0DfPmc .node circle,#mermaid-svg-gtlu7h0Mti0DfPmc .node ellipse,#mermaid-svg-gtlu7h0Mti0DfPmc .node polygon,#mermaid-svg-gtlu7h0Mti0DfPmc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gtlu7h0Mti0DfPmc .rough-node .label text,#mermaid-svg-gtlu7h0Mti0DfPmc .node .label text,#mermaid-svg-gtlu7h0Mti0DfPmc .image-shape .label,#mermaid-svg-gtlu7h0Mti0DfPmc .icon-shape .label{text-anchor:middle;}#mermaid-svg-gtlu7h0Mti0DfPmc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gtlu7h0Mti0DfPmc .rough-node .label,#mermaid-svg-gtlu7h0Mti0DfPmc .node .label,#mermaid-svg-gtlu7h0Mti0DfPmc .image-shape .label,#mermaid-svg-gtlu7h0Mti0DfPmc .icon-shape .label{text-align:center;}#mermaid-svg-gtlu7h0Mti0DfPmc .node.clickable{cursor:pointer;}#mermaid-svg-gtlu7h0Mti0DfPmc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gtlu7h0Mti0DfPmc .arrowheadPath{fill:#333333;}#mermaid-svg-gtlu7h0Mti0DfPmc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gtlu7h0Mti0DfPmc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gtlu7h0Mti0DfPmc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gtlu7h0Mti0DfPmc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gtlu7h0Mti0DfPmc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gtlu7h0Mti0DfPmc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gtlu7h0Mti0DfPmc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gtlu7h0Mti0DfPmc .cluster text{fill:#333;}#mermaid-svg-gtlu7h0Mti0DfPmc .cluster span{color:#333;}#mermaid-svg-gtlu7h0Mti0DfPmc 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-gtlu7h0Mti0DfPmc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gtlu7h0Mti0DfPmc rect.text{fill:none;stroke-width:0;}#mermaid-svg-gtlu7h0Mti0DfPmc .icon-shape,#mermaid-svg-gtlu7h0Mti0DfPmc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gtlu7h0Mti0DfPmc .icon-shape p,#mermaid-svg-gtlu7h0Mti0DfPmc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gtlu7h0Mti0DfPmc .icon-shape .label rect,#mermaid-svg-gtlu7h0Mti0DfPmc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gtlu7h0Mti0DfPmc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gtlu7h0Mti0DfPmc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gtlu7h0Mti0DfPmc :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
记忆层架构
存储后端
记忆管理
记忆类型
向量数据库Chroma/Pinecone
短期记忆当前对话上下文Token窗口内消息
记忆检索器向量检索+关键词检索
长期记忆跨会话持久化用户画像+历史决策
工作记忆当前任务中间态临时变量+计划
记忆压缩器摘要+截断+合并
遗忘管理器TTL+重要性衰减
键值存储Redis/SQLite
文档存储JSON/文件
7.2 三级记忆模型
Agent的记忆系统参考了人类认知科学的三级记忆模型:
| 短期记忆 | 工作记忆 | 有限(Token窗口) | 当前会话 | 消息列表 | 顺序遍历 |
| 工作记忆 | 草稿纸 | 小 | 当前任务 | 键值对 | 直接查找 |
| 长期记忆 | 长期记忆 | 无限 | 永久 | 向量数据库+KV | 语义检索 |
通俗理解:短期记忆就像你打电话时记住的对话内容——容量有限,挂了电话就忘。工作记忆就像你算数学题时在草稿纸上写的中间步骤——任务做完就扔。长期记忆就像你的日记本——记录了所有重要经历,需要时翻出来看。
7.3 短期记忆实现
from dataclasses import dataclass, field
from typing import Any, Optional, List, Dict
import time
import hashlib
@dataclass
class MemoryItem:
"""记忆条目"""
id: str
content: str
memory_type: str # short_term, long_term, working
timestamp: float = field(default_factory=time.time)
importance: float = 0.5 # 0-1,重要性分数
embedding: List[float] = field(default_factory=list) # 向量嵌入
metadata: dict = field(default_factory=dict)
ttl: Optional[int] = None # 生存时间(秒),None表示永久
access_count: int = 0 # 被访问次数
last_accessed: float = field(default_factory=time.time)
class ShortTermMemory:
"""短期记忆——管理当前对话的消息历史"""
def __init__(self, max_tokens: int = 8000, max_messages: int = 50):
self.max_tokens = max_tokens
self.max_messages = max_messages
self.messages: List[dict] = []
self.estimated_tokens = 0
def add_message(self, role: str, content: str, **kwargs):
"""添加消息"""
msg = {"role": role, "content": content, **kwargs}
self.messages.append(msg)
self.estimated_tokens += self._estimate_tokens(content)
# 超出限制时自动压缩
if self.estimated_tokens > self.max_tokens or len(self.messages) > self.max_messages:
self._compress()
def get_messages(self, include_summary: bool = True) –> List[dict]:
"""获取消息列表(可能包含压缩摘要)"""
if include_summary and hasattr(self, '_summary'):
return [{"role": "system", "content": f"[对话摘要] {self._summary}"}] + self.messages
return self.messages
def _compress(self):
"""压缩记忆——将早期消息摘要化"""
if len(self.messages) < 6:
return # 消息太少,不需要压缩
# 保留最近4条消息
to_summarize = self.messages[:–4]
recent = self.messages[–4:]
# 生成摘要(实际使用LLM生成)
summary_text = " | ".join([
f"{m['role']}: {m['content'][:100]}"
for m in to_summarize
])
self._summary = f"之前对话摘要: {summary_text[:500]}"
self.messages = recent
self.estimated_tokens = sum(
self._estimate_tokens(m["content"]) for m in self.messages
)
def clear(self):
"""清空短期记忆"""
self.messages = []
self.estimated_tokens = 0
if hasattr(self, '_summary'):
del self._summary
def _estimate_tokens(self, text: str) –> int:
"""粗略估算token数(约4字符=1token)"""
return len(text) // 4
7.4 长期记忆实现
class LongTermMemory:
"""长期记忆——跨会话的持久化记忆"""
def __init__(self, storage_path: str = "memory_store"):
self.storage_path = storage_path
self.memories: Dict[str, MemoryItem] = {}
self._load()
async def store(
self,
content: str,
importance: float = 0.5,
memory_type: str = "long_term",
metadata: dict = None,
ttl: int = None
) –> str:
"""存储记忆
Returns:
memory_id
"""
memory_id = hashlib.md5(
f"{content}{time.time()}".encode()
).hexdigest()[:16]
# 生成向量嵌入(实际使用embedding模型)
embedding = self._generate_embedding(content)
item = MemoryItem(
id=memory_id,
content=content,
memory_type=memory_type,
importance=importance,
embedding=embedding,
metadata=metadata or {},
ttl=ttl
)
self.memories[memory_id] = item
self._save()
return memory_id
async def retrieve(
self,
query: str,
top_k: int = 5,
min_importance: float = 0.0
) –> List[MemoryItem]:
"""检索记忆
Args:
query: 查询文本
top_k: 返回数量
min_importance: 最小重要性阈值
Returns:
相关记忆列表
"""
query_embedding = self._generate_embedding(query)
# 计算相似度
scored = []
for mem_id, item in self.memories.items():
if item.importance < min_importance:
continue
if self._is_expired(item):
continue
similarity = self._cosine_similarity(query_embedding, item.embedding)
# 综合相似度和重要性
score = similarity * 0.7 + item.importance * 0.3
scored.append((score, item))
# 排序取top_k
scored.sort(key=lambda x: x[0], reverse=True)
results = [item for _, item in scored[:top_k]]
# 更新访问记录
for item in results:
item.access_count += 1
item.last_accessed = time.time()
return results
def forget(self, memory_id: str) –> bool:
"""主动遗忘"""
if memory_id in self.memories:
del self.memories[memory_id]
self._save()
return True
return False
def _is_expired(self, item: MemoryItem) –> bool:
"""检查记忆是否过期"""
if item.ttl is None:
return False
return (time.time() – item.timestamp) > item.ttl
def _generate_embedding(self, text: str) –> List[float]:
"""生成向量嵌入(模拟)"""
# 实际使用 text-embedding-3-small 等模型
# 这里用简单的hash模拟
return [hash(text) % 1000 / 1000.0] * 128
def _cosine_similarity(self, a: List[float], b: List[float]) –> float:
"""计算余弦相似度"""
if len(a) != len(b) or not a:
return 0.0
dot = sum(x * y for x, y in zip(a, b))
norm_a = sum(x * x for x in a) ** 0.5
norm_b = sum(y * y for y in b) ** 0.5
if norm_a == 0 or norm_b == 0:
return 0.0
return dot / (norm_a * norm_b)
def _save(self):
"""持久化保存"""
# 实际使用JSON文件或数据库
pass
def _load(self):
"""从存储加载"""
pass
7.5 工作记忆实现
class WorkingMemory:
"""工作记忆——当前任务的临时变量和状态"""
def __init__(self):
self.variables: Dict[str, Any] = {}
self.task_stack: List[dict] = [] # 任务栈
self.plan: List[str] = [] # 当前计划
def set(self, key: str, value: Any):
"""设置变量"""
self.variables[key] = value
def get(self, key: str, default: Any = None) –> Any:
"""获取变量"""
return self.variables.get(key, default)
def delete(self, key: str):
"""删除变量"""
self.variables.pop(key, None)
def push_task(self, task: dict):
"""压入任务"""
self.task_stack.append(task)
def pop_task(self) –> Optional[dict]:
"""弹出任务"""
if self.task_stack:
return self.task_stack.pop()
return None
def current_task(self) –> Optional[dict]:
"""获取当前任务"""
if self.task_stack:
return self.task_stack[–1]
return None
def set_plan(self, steps: List[str]):
"""设置计划"""
self.plan = steps
def next_step(self) –> Optional[str]:
"""获取下一步"""
if self.plan:
return self.plan.pop(0)
return None
def clear(self):
"""清空工作记忆"""
self.variables.clear()
self.task_stack.clear()
self.plan.clear()
def snapshot(self) –> dict:
"""获取快照"""
return {
"variables": dict(self.variables),
"task_stack": list(self.task_stack),
"plan": list(self.plan)
}
def restore(self, snapshot: dict):
"""从快照恢复"""
self.variables = snapshot.get("variables", {})
self.task_stack = snapshot.get("task_stack", [])
self.plan = snapshot.get("plan", [])
7.6 记忆检索与压缩
#mermaid-svg-fljhkb0qL4WoE7Jv{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-fljhkb0qL4WoE7Jv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fljhkb0qL4WoE7Jv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fljhkb0qL4WoE7Jv .error-icon{fill:#552222;}#mermaid-svg-fljhkb0qL4WoE7Jv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fljhkb0qL4WoE7Jv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fljhkb0qL4WoE7Jv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fljhkb0qL4WoE7Jv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fljhkb0qL4WoE7Jv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fljhkb0qL4WoE7Jv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fljhkb0qL4WoE7Jv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fljhkb0qL4WoE7Jv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fljhkb0qL4WoE7Jv .marker.cross{stroke:#333333;}#mermaid-svg-fljhkb0qL4WoE7Jv svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fljhkb0qL4WoE7Jv p{margin:0;}#mermaid-svg-fljhkb0qL4WoE7Jv .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-fljhkb0qL4WoE7Jv .cluster-label text{fill:#333;}#mermaid-svg-fljhkb0qL4WoE7Jv .cluster-label span{color:#333;}#mermaid-svg-fljhkb0qL4WoE7Jv .cluster-label span p{background-color:transparent;}#mermaid-svg-fljhkb0qL4WoE7Jv .label text,#mermaid-svg-fljhkb0qL4WoE7Jv span{fill:#333;color:#333;}#mermaid-svg-fljhkb0qL4WoE7Jv .node rect,#mermaid-svg-fljhkb0qL4WoE7Jv .node circle,#mermaid-svg-fljhkb0qL4WoE7Jv .node ellipse,#mermaid-svg-fljhkb0qL4WoE7Jv .node polygon,#mermaid-svg-fljhkb0qL4WoE7Jv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fljhkb0qL4WoE7Jv .rough-node .label text,#mermaid-svg-fljhkb0qL4WoE7Jv .node .label text,#mermaid-svg-fljhkb0qL4WoE7Jv .image-shape .label,#mermaid-svg-fljhkb0qL4WoE7Jv .icon-shape .label{text-anchor:middle;}#mermaid-svg-fljhkb0qL4WoE7Jv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fljhkb0qL4WoE7Jv .rough-node .label,#mermaid-svg-fljhkb0qL4WoE7Jv .node .label,#mermaid-svg-fljhkb0qL4WoE7Jv .image-shape .label,#mermaid-svg-fljhkb0qL4WoE7Jv .icon-shape .label{text-align:center;}#mermaid-svg-fljhkb0qL4WoE7Jv .node.clickable{cursor:pointer;}#mermaid-svg-fljhkb0qL4WoE7Jv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fljhkb0qL4WoE7Jv .arrowheadPath{fill:#333333;}#mermaid-svg-fljhkb0qL4WoE7Jv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fljhkb0qL4WoE7Jv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fljhkb0qL4WoE7Jv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fljhkb0qL4WoE7Jv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fljhkb0qL4WoE7Jv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fljhkb0qL4WoE7Jv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fljhkb0qL4WoE7Jv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fljhkb0qL4WoE7Jv .cluster text{fill:#333;}#mermaid-svg-fljhkb0qL4WoE7Jv .cluster span{color:#333;}#mermaid-svg-fljhkb0qL4WoE7Jv 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-fljhkb0qL4WoE7Jv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fljhkb0qL4WoE7Jv rect.text{fill:none;stroke-width:0;}#mermaid-svg-fljhkb0qL4WoE7Jv .icon-shape,#mermaid-svg-fljhkb0qL4WoE7Jv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fljhkb0qL4WoE7Jv .icon-shape p,#mermaid-svg-fljhkb0qL4WoE7Jv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fljhkb0qL4WoE7Jv .icon-shape .label rect,#mermaid-svg-fljhkb0qL4WoE7Jv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fljhkb0qL4WoE7Jv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fljhkb0qL4WoE7Jv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fljhkb0qL4WoE7Jv :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
记忆压缩流程
冗长对话历史
分段
LLM摘要
合并摘要
压缩后摘要
记忆检索流程
查询文本
生成查询向量
计算相似度
过滤(重要性+时效性)
排序
取Top-K
返回相关记忆
7.7 遗忘机制
class ForgettingManager:
"""遗忘管理器——自动淘汰过时记忆"""
def __init__(
self,
decay_rate: float = 0.01, # 每天重要性衰减率
min_importance: float = 0.1, # 低于此值则遗忘
max_memories: int = 10000 # 最大记忆条数
):
self.decay_rate = decay_rate
self.min_importance = min_importance
self.max_memories = max_memories
def apply_decay(self, memories: Dict[str, MemoryItem]) –> List[str]:
"""应用重要性衰减
Returns:
被遗忘的记忆ID列表
"""
forgotten = []
current_time = time.time()
for mem_id, item in memories.items():
# 计算距上次访问的天数
days_since_access = (current_time – item.last_accessed) / 86400
# 衰减重要性
item.importance *= (1 – self.decay_rate) ** days_since_access
# 低于阈值则遗忘
if item.importance < self.min_importance:
forgotten.append(mem_id)
# 超出容量限制时,遗忘最不重要的
if len(memories) > self.max_memories:
sorted_items = sorted(
memories.items(),
key=lambda x: x[1].importance
)
excess = len(memories) – self.max_memories
for mem_id, _ in sorted_items[:excess]:
if mem_id not in forgotten:
forgotten.append(mem_id)
return forgotten
def boost_importance(self, item: MemoryItem, boost: float = 0.1):
"""提升记忆重要性(被访问时调用)"""
item.importance = min(1.0, item.importance + boost)
item.access_count += 1
item.last_accessed = time.time()
通俗理解:遗忘机制就像大脑的"自动清理"——你不会记住一个月前午饭吃了什么(重要性低且时间久远),但你会记得上周的重要会议(重要性高且时间较近)。Agent的记忆系统也会自动遗忘不重要的旧记忆,保持记忆库的高效。
7.8 记忆层完整组装
class MemoryLayer:
"""记忆层完整实现——整合三级记忆"""
def __init__(self, storage_path: str = "memory_store"):
self.short_term = ShortTermMemory(max_tokens=8000, max_messages=50)
self.long_term = LongTermMemory(storage_path=storage_path)
self.working = WorkingMemory()
self.forgetting = ForgettingManager()
async def add_message(self, role: str, content: str, **kwargs):
"""添加对话消息"""
self.short_term.add_message(role, content, **kwargs)
async def store_long_term(
self,
content: str,
importance: float = 0.5,
**kwargs
) –> str:
"""存储长期记忆"""
return await self.long_term.store(content, importance, **kwargs)
async def retrieve_relevant(
self,
query: str,
top_k: int = 5
) –> List[MemoryItem]:
"""检索相关记忆"""
return await self.long_term.retrieve(query, top_k=top_k)
def get_context_messages(self) –> List[dict]:
"""获取完整上下文消息(短期记忆+检索到的长期记忆)"""
messages = self.short_term.get_messages()
return messages
def run_maintenance(self):
"""运行记忆维护(遗忘+压缩)"""
forgotten = self.forgetting.apply_decay(self.long_term.memories)
for fid in forgotten:
self.long_term.forget(fid)
return {"forgotten_count": len(forgotten)}






