文章目录
- [《2026年9月 AI Agent全栈开发技术选型专项面试宝典》](#《2026年9月 AI Agent全栈开发技术选型专项面试宝典》)
-
- 一、核心LLM选型(必问第一题)
-
- 基础必问题
-
- [1. 你做Agent项目时用了什么大模型?为什么选它而不是其他?](#1. 你做Agent项目时用了什么大模型?为什么选它而不是其他?)
- [2. GPT-4o、Claude 3.5 Sonnet、豆包4、DeepSeek V3这四个主流模型,在Agent场景下各有什么优缺点?](#2. GPT-4o、Claude 3.5 Sonnet、豆包4、DeepSeek V3这四个主流模型,在Agent场景下各有什么优缺点?)
- [3. 开源模型(Llama 3、Qwen 2.5、GLM-4)在Agent场景下的表现如何?什么情况下会选择开源模型?](#3. 开源模型(Llama 3、Qwen 2.5、GLM-4)在Agent场景下的表现如何?什么情况下会选择开源模型?)
- [4. 模型的"工具调用准确率"和"长上下文理解能力",哪个对Agent更重要?](#4. 模型的"工具调用准确率"和"长上下文理解能力",哪个对Agent更重要?)
- 进阶场景题
-
- [5. 同一个Agent系统中,为什么要做"模型分层"?规划层、执行层、反思层分别用什么模型?(字节真题)](#5. 同一个Agent系统中,为什么要做"模型分层"?规划层、执行层、反思层分别用什么模型?(字节真题))
- [6. 如果你的Agent需要处理100万token的超长文档,你会选择哪个模型?为什么?](#6. 如果你的Agent需要处理100万token的超长文档,你会选择哪个模型?为什么?)
- [7. 生产环境中,如何设计模型降级策略?当主模型不可用时,怎么保证服务不中断?](#7. 生产环境中,如何设计模型降级策略?当主模型不可用时,怎么保证服务不中断?)
- [8. 模型的输出速度和准确率,在什么场景下你会优先选择速度?什么场景下优先选择准确率?](#8. 模型的输出速度和准确率,在什么场景下你会优先选择速度?什么场景下优先选择准确率?)
- 深度权衡题
-
- [9. 现在很多公司都在做自己的Agent专用模型,你认为专用模型和通用模型在Agent场景下的边界在哪里?](#9. 现在很多公司都在做自己的Agent专用模型,你认为专用模型和通用模型在Agent场景下的边界在哪里?)
- [10. 如何评估一个模型是否适合做Agent?除了工具调用准确率,还有哪些关键指标?](#10. 如何评估一个模型是否适合做Agent?除了工具调用准确率,还有哪些关键指标?)
- [11. 如果你要做一个面向企业的私有化Agent,模型选型的优先级是什么?(成本、安全、效果、可定制性)](#11. 如果你要做一个面向企业的私有化Agent,模型选型的优先级是什么?(成本、安全、效果、可定制性))
- 二、Agent框架选型(春招最高频)
-
- 基础必问题
-
- [1. LangChain、LangGraph、LlamaIndex三者的核心区别是什么?分别适合什么场景?](#1. LangChain、LangGraph、LlamaIndex三者的核心区别是什么?分别适合什么场景?)
- [2. 为什么现在大家都从LangChain转向LangGraph?LangGraph解决了LangChain的哪些痛点?](#2. 为什么现在大家都从LangChain转向LangGraph?LangGraph解决了LangChain的哪些痛点?)
- [3. CrewAI、AutoGPT、MetaGPT这三个多Agent框架,各自的特点和适用场景是什么?](#3. CrewAI、AutoGPT、MetaGPT这三个多Agent框架,各自的特点和适用场景是什么?)
- [4. Spring AI是什么?和LangChain相比,Java栈为什么优先选Spring AI?(阿里/腾讯Java岗必问)](#4. Spring AI是什么?和LangChain相比,Java栈为什么优先选Spring AI?(阿里/腾讯Java岗必问))
- 进阶场景题
-
- [5. 什么情况下你会放弃所有框架,自己手写Agent核心逻辑?(字节真题,考察工程判断力)](#5. 什么情况下你会放弃所有框架,自己手写Agent核心逻辑?(字节真题,考察工程判断力))
- [6. 如果你要做一个支持复杂多轮任务的Agent,你会选择LangGraph还是CrewAI?为什么?](#6. 如果你要做一个支持复杂多轮任务的Agent,你会选择LangGraph还是CrewAI?为什么?)
- [7. LlamaIndex在RAG方面比LangChain强在哪里?什么情况下你会同时使用LangGraph和LlamaIndex?](#7. LlamaIndex在RAG方面比LangChain强在哪里?什么情况下你会同时使用LangGraph和LlamaIndex?)
- [8. 框架的"黑盒性"是一个常见问题,你在使用框架时遇到过哪些难以调试的问题?怎么解决的?](#8. 框架的"黑盒性"是一个常见问题,你在使用框架时遇到过哪些难以调试的问题?怎么解决的?)
- 深度权衡题
-
- [9. 现在很多人说"Agent框架都是玩具,生产环境都要自己写",你怎么看这个观点?](#9. 现在很多人说"Agent框架都是玩具,生产环境都要自己写",你怎么看这个观点?)
- [10. 未来Agent框架的发展方向是什么?你认为哪些功能会被框架内置,哪些需要开发者自己实现?](#10. 未来Agent框架的发展方向是什么?你认为哪些功能会被框架内置,哪些需要开发者自己实现?)
- [11. 如果让你设计一个轻量级的企业级Agent框架,你会保留哪些核心功能,去掉哪些功能?](#11. 如果让你设计一个轻量级的企业级Agent框架,你会保留哪些核心功能,去掉哪些功能?)
- 三、RAG技术栈选型(必考)
-
- 基础必问题
-
- [1. 你用过哪些向量数据库?Pinecone、Milvus、Chroma、PGVector、Qdrant的优缺点对比?](#1. 你用过哪些向量数据库?Pinecone、Milvus、Chroma、PGVector、Qdrant的优缺点对比?)
- [2. 为什么现在越来越多的人选择PGVector而不是专门的向量数据库?](#2. 为什么现在越来越多的人选择PGVector而不是专门的向量数据库?)
- [3. 主流的Embedding模型有哪些?OpenAI text-embedding-3、BGE-M3、Jina Embeddings怎么选?](#3. 主流的Embedding模型有哪些?OpenAI text-embedding-3、BGE-M3、Jina Embeddings怎么选?)
- [4. Rerank模型怎么选?Cohere Rerank 3、BGE-Reranker-v2、ColBERT的区别?](#4. Rerank模型怎么选?Cohere Rerank 3、BGE-Reranker-v2、ColBERT的区别?)
- 进阶场景题
-
- [5. 什么情况下你会选择稠密向量检索+稀疏向量检索的混合检索方案?](#5. 什么情况下你会选择稠密向量检索+稀疏向量检索的混合检索方案?)
- [6. 图RAG(Graph RAG)和传统向量RAG相比,有什么优势?什么场景下必须用图RAG?](#6. 图RAG(Graph RAG)和传统向量RAG相比,有什么优势?什么场景下必须用图RAG?)
- [7. 如果你的知识库有1000万+文档,你会怎么设计RAG架构?向量数据库怎么选型和分片?](#7. 如果你的知识库有1000万+文档,你会怎么设计RAG架构?向量数据库怎么选型和分片?)
- [8. 增量索引和全量索引怎么选?什么情况下必须做增量索引?](#8. 增量索引和全量索引怎么选?什么情况下必须做增量索引?)
- 深度权衡题
-
- [9. 现在很多人说"RAG已死,Fine-tune永生",也有人说"Fine-tune解决不了幻觉,RAG才是王道",你怎么看?](#9. 现在很多人说"RAG已死,Fine-tune永生",也有人说"Fine-tune解决不了幻觉,RAG才是王道",你怎么看?)
- [10. RAG技术栈中,哪个环节的优化投入产出比最高?(分块、Embedding、检索、Rerank、Prompt)](#10. RAG技术栈中,哪个环节的优化投入产出比最高?(分块、Embedding、检索、Rerank、Prompt))
- [11. 如果你要做一个支持多模态的RAG系统,技术选型会有什么变化?](#11. 如果你要做一个支持多模态的RAG系统,技术选型会有什么变化?)
- 四、工具调用与协议选型(2026年最热考点)
-
- 基础必问题
-
- [1. 传统Function Call和MCP(Model Context Protocol)的区别是什么?](#1. 传统Function Call和MCP(Model Context Protocol)的区别是什么?)
- [2. 什么情况下你会选择用MCP,而不是自己写工具调用逻辑?](#2. 什么情况下你会选择用MCP,而不是自己写工具调用逻辑?)
- [3. MCP Server有哪些主流实现?你会选择用Python还是TypeScript写MCP Server?](#3. MCP Server有哪些主流实现?你会选择用Python还是TypeScript写MCP Server?)
- [4. A2A(Agent-to-Agent)协议是什么?和MCP是什么关系?](#4. A2A(Agent-to-Agent)协议是什么?和MCP是什么关系?)
- 进阶场景题
-
- [5. 如果你的系统需要集成100+个工具,你会选择传统的静态工具注册还是MCP动态发现?为什么?](#5. 如果你的系统需要集成100+个工具,你会选择传统的静态工具注册还是MCP动态发现?为什么?)
- [6. MCP的安全风险有哪些?在生产环境中使用MCP需要做哪些安全加固?](#6. MCP的安全风险有哪些?在生产环境中使用MCP需要做哪些安全加固?)
- [7. 工具调用的"同步调用"和"异步调用"怎么选?什么情况下必须用异步调用?](#7. 工具调用的"同步调用"和"异步调用"怎么选?什么情况下必须用异步调用?)
- [8. 如果一个工具调用需要很长时间(比如几分钟),你怎么设计Agent的交互逻辑?](#8. 如果一个工具调用需要很长时间(比如几分钟),你怎么设计Agent的交互逻辑?)
- 深度权衡题
-
- [9. MCP会成为未来Agent工具调用的标准协议吗?为什么?](#9. MCP会成为未来Agent工具调用的标准协议吗?为什么?)
- [10. 现在很多公司都在做自己的工具市场,你认为工具市场的核心竞争力是什么?](#10. 现在很多公司都在做自己的工具市场,你认为工具市场的核心竞争力是什么?)
- [11. 工具调用的"准确性"和"丰富性",哪个更重要?如何平衡?](#11. 工具调用的"准确性"和"丰富性",哪个更重要?如何平衡?)
- 五、全栈后端选型
-
- Python栈(主流)
-
- [1. FastAPI、Flask、Django在Agent后端开发中怎么选?为什么几乎所有人都用FastAPI?](#1. FastAPI、Flask、Django在Agent后端开发中怎么选?为什么几乎所有人都用FastAPI?)
- [2. 为什么Agent后端必须用异步框架?FastAPI+AsyncIO有哪些常见的坑?](#2. 为什么Agent后端必须用异步框架?FastAPI+AsyncIO有哪些常见的坑?)
- [3. 消息队列怎么选?RabbitMQ、Kafka、Redis Pub/Sub在Agent场景下的适用场景?](#3. 消息队列怎么选?RabbitMQ、Kafka、Redis Pub/Sub在Agent场景下的适用场景?)
- [4. 任务队列怎么选?Celery、RQ、Arq的区别?](#4. 任务队列怎么选?Celery、RQ、Arq的区别?)
- Java栈(大厂必问)
-
- [5. Spring AI和LangChain4j怎么选?各自的优缺点是什么?](#5. Spring AI和LangChain4j怎么选?各自的优缺点是什么?)
- [6. Spring Boot 3.x在AI开发中有哪些新特性?为什么推荐用Spring Boot 3.2+?](#6. Spring Boot 3.x在AI开发中有哪些新特性?为什么推荐用Spring Boot 3.2+?)
- [7. 如何在Spring AI中集成LangGraph?](#7. 如何在Spring AI中集成LangGraph?)
- [8. Java的虚拟线程在Agent后端开发中有什么用?](#8. Java的虚拟线程在Agent后端开发中有什么用?)
- 通用问题
-
- [9. 关系型数据库怎么选?MySQL、PostgreSQL在Agent系统中的适用场景?](#9. 关系型数据库怎么选?MySQL、PostgreSQL在Agent系统中的适用场景?)
- [10. 缓存怎么选?Redis、Memcached在Agent系统中分别用来缓存什么?](#10. 缓存怎么选?Redis、Memcached在Agent系统中分别用来缓存什么?)
- [11. 如何设计Agent系统的数据库Schema?需要存储哪些核心数据?](#11. 如何设计Agent系统的数据库Schema?需要存储哪些核心数据?)
- 六、前端选型
-
- 基础必问题
-
- [1. SSE和WebSocket在AI流式输出场景下怎么选?为什么90%的AI应用都用SSE?](#1. SSE和WebSocket在AI流式输出场景下怎么选?为什么90%的AI应用都用SSE?)
- [2. React和Vue在AI前端开发中怎么选?各自的生态有哪些AI相关的库?](#2. React和Vue在AI前端开发中怎么选?各自的生态有哪些AI相关的库?)
- [3. 你用过哪些AI UI组件库?shadcn/ui、Ant Design、MUI怎么选?](#3. 你用过哪些AI UI组件库?shadcn/ui、Ant Design、MUI怎么选?)
- [4. 状态管理怎么选?Zustand、Redux、Pinia在AI前端中的适用场景?](#4. 状态管理怎么选?Zustand、Redux、Pinia在AI前端中的适用场景?)
- 进阶场景题
-
- [5. 如何实现支持Markdown、代码高亮、数学公式的流式输出?](#5. 如何实现支持Markdown、代码高亮、数学公式的流式输出?)
- [6. 长对话的性能优化怎么做?虚拟滚动、懒加载、分片渲染怎么结合?](#6. 长对话的性能优化怎么做?虚拟滚动、懒加载、分片渲染怎么结合?)
- [7. 如何实现Agent的"思考中"、"调用工具中"、"执行中"等状态的实时展示?](#7. 如何实现Agent的"思考中"、"调用工具中"、"执行中"等状态的实时展示?)
- [8. 前端如何处理Function Call的流式输出?(MiniMax真题)](#8. 前端如何处理Function Call的流式输出?(MiniMax真题))
- 深度权衡题
-
- [9. 现在很多AI应用都用Next.js全栈开发,你认为Next.js适合做Agent前端吗?有什么优缺点?](#9. 现在很多AI应用都用Next.js全栈开发,你认为Next.js适合做Agent前端吗?有什么优缺点?)
- [10. 前端在Agent系统中应该承担哪些职责?哪些逻辑应该放在前端,哪些应该放在后端?](#10. 前端在Agent系统中应该承担哪些职责?哪些逻辑应该放在前端,哪些应该放在后端?)
- [11. 如何设计一个可扩展的Agent前端架构,支持快速接入不同的Agent能力?](#11. 如何设计一个可扩展的Agent前端架构,支持快速接入不同的Agent能力?)
- 七、部署与基础设施选型
-
- 基础必问题
-
- [1. Agent系统为什么必须用容器化部署?Docker和Kubernetes怎么选?](#1. Agent系统为什么必须用容器化部署?Docker和Kubernetes怎么选?)
- [2. Serverless在Agent场景下适用吗?什么情况下用Serverless,什么情况下用传统服务器?](#2. Serverless在Agent场景下适用吗?什么情况下用Serverless,什么情况下用传统服务器?)
- [3. 可观测性工具怎么选?LangSmith、LangFuse、OpenTelemetry的区别?](#3. 可观测性工具怎么选?LangSmith、LangFuse、OpenTelemetry的区别?)
- [4. 日志系统怎么选?ELK、Loki、PLG在Agent系统中的适用场景?](#4. 日志系统怎么选?ELK、Loki、PLG在Agent系统中的适用场景?)
- 进阶场景题
-
- [5. 如何设计Agent系统的CI/CD流水线?需要包含哪些环节?](#5. 如何设计Agent系统的CI/CD流水线?需要包含哪些环节?)
- [6. 如何做Agent系统的负载均衡和水平扩展?](#6. 如何做Agent系统的负载均衡和水平扩展?)
- [7. 如何设计Agent系统的灰度发布和A/B测试?](#7. 如何设计Agent系统的灰度发布和A/B测试?)
- [8. 私有化部署的Agent系统,如何解决模型和数据的安全问题?](#8. 私有化部署的Agent系统,如何解决模型和数据的安全问题?)
- 深度权衡题
-
- [9. 现在很多云厂商都推出了自己的Agent平台(如阿里云百炼、腾讯云智能体),什么情况下你会选择用云厂商的平台,什么情况下自己搭建?](#9. 现在很多云厂商都推出了自己的Agent平台(如阿里云百炼、腾讯云智能体),什么情况下你会选择用云厂商的平台,什么情况下自己搭建?)
- [10. 边缘计算在Agent场景下有什么用?什么情况下需要把Agent部署在边缘?](#10. 边缘计算在Agent场景下有什么用?什么情况下需要把Agent部署在边缘?)
- [11. 如何评估一个Agent系统的部署成本?有哪些优化成本的方法?](#11. 如何评估一个Agent系统的部署成本?有哪些优化成本的方法?)
- 八、综合技术选型决策题(大厂终面必问)
-
-
- [1. 从零开始搭建一个企业级内部知识库Agent,从0到1怎么做完整的技术选型?(要求:从LLM到前端到部署,讲清楚每个环节的选择理由和权衡)](#1. 从零开始搭建一个企业级内部知识库Agent,从0到1怎么做完整的技术选型?(要求:从LLM到前端到部署,讲清楚每个环节的选择理由和权衡))
- [2. 如果给你3个开发,3个月时间,要做一个面向C端的Agent产品,你会怎么选择技术栈?为什么?(字节真题)](#2. 如果给你3个开发,3个月时间,要做一个面向C端的Agent产品,你会怎么选择技术栈?为什么?(字节真题))
- [3. 如果你要做一个支持10万并发用户的Agent系统,技术选型会有什么变化?哪些地方需要做特殊优化?](#3. 如果你要做一个支持10万并发用户的Agent系统,技术选型会有什么变化?哪些地方需要做特殊优化?)
- [4. 现在有一个遗留系统,需要集成Agent能力,你会怎么设计技术方案?如何平衡技术债务和新功能开发?](#4. 现在有一个遗留系统,需要集成Agent能力,你会怎么设计技术方案?如何平衡技术债务和新功能开发?)
- [5. 如何在技术选型中平衡"技术先进性"和"工程稳定性"?你有过因为追求新技术而踩坑的经历吗?](#5. 如何在技术选型中平衡"技术先进性"和"工程稳定性"?你有过因为追求新技术而踩坑的经历吗?)
-
- 九、回答技术选型问题的黄金技巧

《2026年9月 AI Agent全栈开发技术选型专项面试宝典》
一、核心LLM选型(必问第一题)
基础必问题
1. 你做Agent项目时用了什么大模型?为什么选它而不是其他?
标准回答:
"我最近做的企业内部知识库Agent项目,主模型选用了GPT-4o,同时用Claude 3.5 Sonnet作为降级模型,豆包4作为国内备用模型。
选择GPT-4o的核心原因有三个:
- 工具调用准确率最高:我们内部测试了100个复杂工具调用场景,GPT-4o的准确率达到92%,比Claude 3.5高4个百分点,比豆包4高7个百分点,这对Agent的可靠性至关重要
- 多模态能力最强:我们的知识库包含大量截图和流程图,GPT-4o能直接理解这些内容,不需要额外的OCR处理
- 生态最成熟:OpenAI的API稳定性最好,文档最完善,社区问题解决速度最快
我们没有选择其他模型的原因:
- Claude 3.5 Sonnet虽然长上下文更好,但工具调用的参数生成准确率稍低,经常出现参数缺失或格式错误
- 豆包4在中文理解上有优势,但复杂推理和工具调用能力还有差距
- 开源模型如Llama 3 70B,在我们的测试中工具调用准确率只有75%左右,无法满足生产要求
当然,GPT-4o也有缺点:成本最高,国内访问有延迟。所以我们做了模型分层,简单任务用更便宜的模型,只有复杂任务才调用GPT-4o。
2. GPT-4o、Claude 3.5 Sonnet、豆包4、DeepSeek V3这四个主流模型,在Agent场景下各有什么优缺点?
标准回答:
"这四个模型在Agent场景下各有侧重,我从五个核心维度做过对比:
| 模型 | 工具调用准确率 | 长上下文能力 | 中文能力 | 推理速度 | 成本 |
|---|---|---|---|---|---|
| GPT-4o | 92%(最优) | 128K(良好) | 优秀 | 快 | 高 |
| Claude 3.5 Sonnet | 88% | 200K(最优) | 良好 | 中 | 中 |
| 豆包4 | 85% | 128K | 最优 | 快 | 中低 |
| DeepSeek V3 | 82% | 128K | 优秀 | 最快 | 最低 |
具体优缺点:
- GPT-4o:优点是工具调用最可靠,多模态能力最强,复杂推理最好;缺点是成本最高,长上下文不如Claude
- Claude 3.5 Sonnet:优点是200K长上下文几乎无损,文档理解能力最强,成本适中;缺点是工具调用偶尔会出现格式错误,多模态能力较弱
- 豆包4:优点是中文理解和生成最好,国内访问速度快,成本较低;缺点是复杂推理和工具调用准确率稍低
- DeepSeek V3:优点是速度最快,成本最低,代码能力不错;缺点是工具调用准确率最低,容易出现幻觉
总结:GPT-4o适合复杂工具调用和多模态场景;Claude适合长文档处理;豆包4适合国内纯中文场景;DeepSeek适合简单任务和高并发场景。
3. 开源模型(Llama 3、Qwen 2.5、GLM-4)在Agent场景下的表现如何?什么情况下会选择开源模型?
标准回答:
"目前开源模型在Agent场景下的表现已经有了很大提升,但和闭源模型还有明显差距。我测试的结果是:
- Llama 3 70B Instruct:工具调用准确率约75%,推理能力强,英文好,中文一般
- Qwen 2.5 72B Instruct:工具调用准确率约78%,中文最好,代码能力强
- GLM-4 9B/66B:工具调用准确率约73%,中文不错,生态完善
开源模型的优势是:数据安全可控、可定制化、成本极低;劣势是:工具调用准确率低、长上下文能力弱、需要自己部署和维护。
我会在以下四种情况下选择开源模型:
- 数据安全要求极高:比如金融、医疗、政府等行业,不能把数据发送给第三方API
- 成本敏感且任务简单:比如只需要做简单的问答和少量工具调用,Qwen 2.5 72B已经能满足需求
- 需要深度定制:比如要针对特定领域做微调,或者修改模型的行为逻辑
- 高并发低延迟场景:可以本地部署多个实例,实现水平扩展,避免API限流
我们之前做过一个银行内部的Agent项目,就是用Qwen 2.5 72B做的,虽然效果比GPT-4o差一些,但满足了数据安全要求,成本只有闭源模型的1/20。
4. 模型的"工具调用准确率"和"长上下文理解能力",哪个对Agent更重要?
标准回答:
"这个问题没有绝对答案,取决于Agent的具体应用场景,但在大多数通用Agent场景下,工具调用准确率比长上下文理解能力更重要。
原因很简单:工具调用是Agent与外部世界交互的接口,如果工具调用出错,整个Agent的执行流程就会中断或产生错误结果。比如Agent要调用计算器计算1+1,如果返回了3,后面所有基于这个结果的推理都会错。而长上下文理解能力差,我们可以通过RAG、分块处理、摘要等技术来弥补。
但在以下特定场景下,长上下文理解能力会更重要:
- 长文档分析Agent:比如需要一次性分析一份100页的合同,找出其中的风险点
- 代码审查Agent:需要理解整个代码库的上下文,而不仅仅是单个文件
- 会议纪要Agent:需要处理长达数小时的会议录音转写文本
我之前做过一个项目,最开始用GPT-4o处理长合同,发现它经常遗漏后面的内容。后来换成Claude 3.5 Sonnet,虽然工具调用准确率稍低,但长文档理解能力提升了很多,整体效果反而更好。
所以我的原则是:优先保证工具调用准确率,当任务核心是长文档处理时,再优先考虑长上下文能力。
进阶场景题
5. 同一个Agent系统中,为什么要做"模型分层"?规划层、执行层、反思层分别用什么模型?(字节真题)
标准回答:
"模型分层是Agent系统中非常重要的架构设计,主要有三个原因:
- 成本优化:不同复杂度的任务用不同能力的模型,避免用GPT-4o做简单的文本分类任务
- 性能优化:简单任务用更快更便宜的模型,提高系统响应速度
- 可靠性提升:不同模型擅长不同的任务,各司其职能提高整体系统的准确率
我们的Agent系统分为三层,每层用不同的模型:
规划层 :负责任务分解和整体规划,需要最强的推理能力。我们用GPT-4o。
- 原因:规划是Agent最复杂的环节,需要理解用户的复杂需求,分解成多个子任务,还要考虑任务之间的依赖关系。GPT-4o的复杂推理能力最强,任务分解的准确率最高。
执行层 :负责执行具体的子任务,调用工具,处理中间结果。我们用Claude 3.5 Sonnet 或豆包4。
- 原因:执行层主要是工具调用和简单的文本处理,不需要最强的推理能力。Claude 3.5 Sonnet的工具调用准确率已经足够,成本只有GPT-4o的1/3。如果是国内场景,用豆包4速度更快。
反思层 :负责检查执行结果是否正确,是否需要重试或调整。我们用GPT-4o-mini 或DeepSeek V3。
- 原因:反思层主要是做简单的判断,比如"这个结果是否回答了用户的问题"、"工具调用是否成功"。GPT-4o-mini已经能很好地完成这些任务,成本只有GPT-4o的1/10。
我们做过统计,采用模型分层后,系统的整体成本降低了70%,而准确率只下降了不到2%,性价比非常高。
6. 如果你的Agent需要处理100万token的超长文档,你会选择哪个模型?为什么?
标准回答:
"如果需要处理100万token的超长文档,我会选择Claude 3.5 Sonnet 200K 作为主模型,同时配合分块处理+RAG的架构。
原因有三个:
- Claude 3.5 Sonnet的200K上下文窗口是目前所有主流模型中表现最好的:我们测试过,它在150K上下文长度下的信息召回率仍然能达到95%以上,而GPT-4o在100K以上召回率就会明显下降。
- Claude的长文档理解能力最强:它能更好地理解文档的整体结构和逻辑关系,而不仅仅是关键词匹配。比如在分析一份100页的合同时,Claude能准确找出前后条款之间的矛盾,而其他模型经常会遗漏。
- 成本适中:Claude 3.5 Sonnet的成本只有GPT-4o的1/3,处理长文档的性价比更高。
但即使是Claude 3.5 Sonnet,也无法直接处理100万token的文档。所以我会配合以下架构:
- 文档分块:将100万token的文档分成多个200K的块
- 块级摘要:用Claude对每个块生成摘要
- 全局摘要:将所有块的摘要合并,生成整个文档的全局摘要
- RAG检索:当用户提问时,先检索相关的块,再将相关块的完整内容传给Claude处理
如果预算充足,也可以考虑Anthropic最新的Claude 3 Opus 1M,但它的成本是Sonnet的5倍,只有在对准确率要求极高的场景下才值得使用。
7. 生产环境中,如何设计模型降级策略?当主模型不可用时,怎么保证服务不中断?
标准回答:
"生产环境中模型API不可用是很常见的情况,我们的降级策略分为三级,从自动降级到人工干预:
一级降级:同级别模型切换
- 主模型:GPT-4o
- 降级模型:Claude 3.5 Sonnet
- 触发条件:主模型连续3次调用失败,或者响应时间超过10秒
- 实现方式:用装饰器包装模型调用接口,自动捕获异常和超时,切换到降级模型
- 注意事项:两个模型的输入输出格式要尽量统一,避免切换后出现兼容性问题
二级降级:低级别模型切换
- 降级模型:GPT-4o-mini、豆包4
- 触发条件:一级降级模型也不可用
- 注意事项:低级别模型的能力有限,需要简化任务逻辑,比如禁用复杂的工具调用,只保留基础的问答功能
三级降级:静态响应
- 触发条件:所有模型都不可用
- 响应内容:"当前系统繁忙,您的问题已记录,我们会尽快处理"
- 后续处理:将用户的问题存入队列,等模型恢复后再处理,并通知用户
除了降级策略,我们还会做以下保障:
- 限流和熔断:用Sentinel或Hystrix实现限流和熔断,避免单个模型故障拖垮整个系统
- 缓存:缓存常见问题的回答,减少模型调用次数
- 监控和告警:实时监控模型的调用成功率、响应时间、错误率,出现异常及时告警
- 多区域部署:如果使用云服务,部署多个区域的API端点,避免单区域故障
我们之前遇到过OpenAI API大面积故障的情况,因为有完善的降级策略,系统只中断了不到5分钟,大部分用户甚至没有察觉到异常。
8. 模型的输出速度和准确率,在什么场景下你会优先选择速度?什么场景下优先选择准确率?
标准回答:
"模型的输出速度和准确率是一对矛盾,需要根据具体场景进行权衡。
优先选择速度的场景:
- C端对话场景:比如聊天机器人、客服助手,用户对响应时间非常敏感,超过2秒就会感到不耐烦。这种场景下,即使准确率稍低,也要保证快速响应。我们会用GPT-4o-mini或DeepSeek V3这类速度快的模型。
- 高并发场景:比如营销活动期间,同时有大量用户访问,需要系统能承受高并发。速度快的模型能处理更多的请求,避免系统崩溃。
- 简单任务场景:比如文本分类、关键词提取、简单的问答,这些任务不需要很强的推理能力,速度快的模型已经能达到足够的准确率。
- 流式输出场景:比如代码生成、文章写作,用户希望看到实时的输出过程,而不是等待很长时间然后一次性显示结果。
优先选择准确率的场景:
- 企业内部决策场景:比如数据分析、报告生成、风险评估,这些场景下错误的结果会导致严重的后果,准确率是第一位的。我们会用GPT-4o或Claude 3 Opus这类最准确的模型。
- 复杂工具调用场景:比如需要调用多个工具完成复杂任务,工具调用的准确率直接决定了任务的成败。
- 法律医疗等专业场景:比如法律咨询、医疗诊断,错误的建议可能会造成法律责任或人身伤害,必须保证最高的准确率。
- 离线处理场景:比如批量处理文档、生成周报,这些任务不需要实时响应,可以花更多时间来提高准确率。
我们的原则是:在满足用户体验要求的前提下,尽可能提高准确率。如果速度和准确率无法同时满足,就根据场景的核心需求来做取舍。
深度权衡题
9. 现在很多公司都在做自己的Agent专用模型,你认为专用模型和通用模型在Agent场景下的边界在哪里?
标准回答:
"专用模型和通用模型在Agent场景下各有其适用边界,我认为主要体现在以下几个方面:
通用模型的优势和适用边界:
- 优势:能力全面,能处理各种不同的任务;不需要大量的训练数据;生态完善,开箱即用
- 适用边界:通用型Agent、多场景Agent、快速原型验证、中小规模应用
- 典型场景:个人助理、通用客服、知识库问答、简单的工具调用
专用模型的优势和适用边界:
- 优势:在特定任务上准确率更高;速度更快;成本更低;可定制化程度高
- 适用边界:单一领域Agent、大规模生产应用、对性能和成本要求极高的场景
- 典型场景:代码生成Agent、数据分析Agent、医疗诊断Agent、金融风控Agent
两者的边界不是绝对的,而是一个连续谱:
- 当你的Agent只需要处理1-2个特定任务,并且有大量的训练数据时,专用模型会更有优势
- 当你的Agent需要处理各种不同的任务,或者没有足够的训练数据时,通用模型会更合适
未来的发展趋势是:通用模型作为基础,专用模型作为补充。大多数公司会先用通用模型快速搭建Agent原型,验证业务价值,然后针对核心任务训练专用模型,提高性能和降低成本。
比如GitHub Copilot就是一个很好的例子:它最开始用GPT-3,后来训练了自己的专用代码模型,在代码生成任务上的表现超过了通用模型,同时成本也降低了很多。
10. 如何评估一个模型是否适合做Agent?除了工具调用准确率,还有哪些关键指标?
标准回答:
"评估一个模型是否适合做Agent,不能只看工具调用准确率,需要从多个维度进行综合评估。我通常会用以下7个核心指标:
- 工具调用准确率:这是最基础也是最重要的指标,包括函数选择准确率、参数生成准确率、格式准确率。我会用至少100个不同的工具调用场景来测试。
- 复杂推理能力:Agent需要能够进行多步推理,解决复杂问题。我会用GSM8K、MMLU等基准测试,同时设计一些实际的业务场景来测试。
- 长上下文理解能力:Agent需要能够记住之前的对话历史和工具调用结果。我会测试模型在不同上下文长度下的信息召回率和推理能力。
- 指令遵循能力:Agent需要能够严格按照系统提示词的要求执行任务。我会测试模型是否会忽略系统提示,或者产生幻觉。
- 输出格式一致性:Agent需要能够稳定地输出特定格式的内容,比如JSON、XML。我会测试模型输出格式的错误率。
- 错误恢复能力:当工具调用失败或者返回错误结果时,Agent需要能够识别错误并进行重试或调整。我会故意让工具返回错误,测试模型的处理能力。
- 性能和成本:包括响应时间、吞吐量、API调用成本。这些指标直接影响系统的用户体验和运营成本。
除了这些量化指标,我还会做一些定性评估,比如:
- 模型是否会产生有害内容
- 模型的中文理解和生成能力
- API的稳定性和文档质量
- 社区支持和生态完善程度
我通常会先用量化指标筛选出前2-3个模型,然后用实际的业务场景做为期1-2周的灰度测试,根据用户反馈和实际运行数据来做最终决策。
11. 如果你要做一个面向企业的私有化Agent,模型选型的优先级是什么?(成本、安全、效果、可定制性)
标准回答:
"面向企业的私有化Agent,模型选型的优先级是:安全 > 可定制性 > 效果 > 成本。
原因如下:
1. 安全是第一位的
- 企业数据是最宝贵的资产,私有化部署的核心需求就是数据安全。如果模型不能保证数据不泄露,其他一切都没有意义。
- 所以我们必须选择开源模型或者支持完全私有化部署的闭源模型,绝对不能使用需要将数据发送到第三方的公有API。
- 还要考虑模型本身的安全性,比如是否会产生有害内容,是否容易被prompt注入攻击。
2. 可定制性是第二位的
- 每个企业都有自己的业务流程和专业知识,通用模型无法满足所有企业的需求。
- 我们需要能够针对企业的特定领域进行微调,或者添加企业自己的知识库。
- 还要能够修改模型的行为逻辑,比如限制模型只能回答与企业业务相关的问题。
3. 效果是第三位的
- 当然,模型的效果也很重要,否则Agent就无法完成实际的工作。
- 但在私有化场景下,我们可以通过微调、RAG、提示工程等技术来提升模型的效果,弥补基础模型的不足。
- 而且企业内部的Agent通常只需要处理特定领域的任务,不需要通用模型那样全面的能力。
4. 成本是最后一位的
- 企业级应用对成本的敏感度相对较低,只要能带来足够的业务价值,企业愿意为好的产品付费。
- 而且私有化部署的成本主要是硬件成本,一次性投入后,后续的运营成本很低。
- 与安全事故造成的损失相比,模型的成本几乎可以忽略不计。
基于这个优先级,我通常会推荐Qwen 2.5 72B 或Llama 3 70B作为企业私有化Agent的基础模型。它们都是开源的,支持完全私有化部署,可定制性强,效果也足够好。
二、Agent框架选型(春招最高频)
基础必问题
1. LangChain、LangGraph、LlamaIndex三者的核心区别是什么?分别适合什么场景?
标准回答:
"这三个框架都是AI Agent开发中最常用的,但它们的定位和核心能力完全不同:
LangChain:
- 核心定位:通用AI应用开发框架
- 核心能力:提供了大量的组件和工具,包括LLM集成、工具调用、RAG、内存管理等
- 优点:生态最完善,组件最丰富,上手最快
- 缺点:链式执行模式不适合复杂的Agent流程,调试困难,黑盒性强
- 适合场景:简单的AI应用、快速原型验证、单轮任务的Agent
LangGraph:
- 核心定位:基于状态机的Agent工作流框架
- 核心能力:用有向图来定义Agent的执行流程,支持循环、分支、条件判断等复杂逻辑
- 优点:适合复杂多轮任务的Agent,可观测性好,调试方便,性能更高
- 缺点:生态不如LangChain完善,上手稍难
- 适合场景:复杂多Agent系统、需要循环和反思的Agent、生产级Agent应用
LlamaIndex:
- 核心定位:数据连接和RAG框架
- 核心能力:提供了丰富的数据连接器、索引类型、查询引擎,专门优化了RAG场景
- 优点:RAG能力最强,支持多种数据格式和索引方式,性能好
- 缺点:Agent能力较弱,不如LangChain和LangGraph灵活
- 适合场景:以RAG为核心的应用、知识库问答系统、文档处理应用
总结一下:
- 如果你要做一个简单的聊天机器人或者快速原型,用LangChain
- 如果你要做一个复杂的多轮任务Agent或者多Agent系统,用LangGraph
- 如果你要做一个以RAG为核心的知识库应用,用LlamaIndex
- 大多数生产级应用会同时使用LangGraph和LlamaIndex,用LangGraph做Agent工作流,用LlamaIndex做RAG
2. 为什么现在大家都从LangChain转向LangGraph?LangGraph解决了LangChain的哪些痛点?
标准回答:
"现在越来越多的开发者从LangChain转向LangGraph,主要是因为LangChain的链式执行模式无法满足复杂Agent的需求,而LangGraph解决了LangChain的几个核心痛点:
1. 支持循环和反思
- LangChain的核心是链式执行,流程是线性的,一旦开始就无法回头
- 而Agent的核心是"思考-行动-观察-再思考"的循环过程,需要能够根据工具调用的结果调整下一步的行动
- LangGraph用状态机的方式定义执行流程,天然支持循环、分支、条件判断,非常适合Agent的工作模式
2. 可观测性和调试能力强
- LangChain的黑盒性很强,执行过程不透明,出了问题很难调试
- LangGraph会保存每一步的状态和执行结果,可以清晰地看到Agent的整个思考过程
- LangGraph还提供了可视化工具,可以直观地查看执行流程和状态变化
3. 更好的状态管理
- LangChain的内存管理比较混乱,不同的链之间共享状态很困难
- LangGraph有统一的状态管理机制,所有节点都可以读写同一个状态对象
- 状态会自动持久化,可以随时中断和恢复执行,支持长时间运行的任务
4. 更高的性能和可扩展性
- LangChain的链式执行效率较低,尤其是在处理复杂任务时
- LangGraph的执行引擎更高效,支持并行执行多个节点
- LangGraph还支持分布式部署,可以水平扩展以处理高并发
5. 更清晰的抽象
- LangChain的抽象过于复杂,有很多概念和组件,学习曲线陡峭
- LangGraph的抽象非常简洁,只有图、节点、边、状态这几个核心概念
- 开发者可以很容易地理解和掌握LangGraph的使用方法
当然,LangGraph也不是完美的,它的生态还不如LangChain完善。但对于复杂的Agent应用来说,LangGraph的优势是压倒性的。我们团队在半年前就把所有的Agent项目从LangChain迁移到了LangGraph,开发效率和系统稳定性都有了很大提升。
3. CrewAI、AutoGPT、MetaGPT这三个多Agent框架,各自的特点和适用场景是什么?
标准回答:
"这三个都是目前最流行的多Agent框架,但它们的设计理念和适用场景有很大不同:
CrewAI:
- 设计理念:基于角色的多Agent协作
- 核心特点:
- 每个Agent有明确的角色、目标和工具
- 支持任务分配和协作
- 支持顺序执行、并行执行、层次化执行
- 集成了LangChain和LangGraph
- 优点:上手简单,灵活性高,适合大多数多Agent场景
- 缺点:复杂任务的协调能力有限
- 适用场景:中小型多Agent系统、业务流程自动化、内容创作
AutoGPT:
- 设计理念:自主Agent,一个Agent完成所有任务
- 核心特点:
- 单个Agent具有自主规划、执行、反思的能力
- 可以自动分解任务,调用工具,完成复杂的目标
- 不需要人工干预,完全自主运行
- 优点:自主性强,不需要复杂的配置
- 缺点:容易陷入循环,可靠性低,成本高
- 适用场景:个人使用、探索性任务、简单的自动化任务
MetaGPT:
- 设计理念:模拟软件公司的工作流程
- 核心特点:
- 预定义了产品经理、架构师、工程师、测试工程师等角色
- 有标准化的工作流程和交付物
- 专门优化了软件开发场景
- 优点:在软件开发场景下表现出色,输出质量高
- 缺点:灵活性差,只适合软件开发场景
- 适用场景:代码生成、软件开发、技术文档写作
总结一下:
- 如果你要做一个通用的多Agent系统,或者业务流程自动化,选CrewAI
- 如果你只是想自己玩一玩,或者做一些简单的探索性任务,选AutoGPT
- 如果你要做与软件开发相关的多Agent系统,选MetaGPT
我们团队目前主要用CrewAI,因为它的灵活性和平衡性最好。我们用它做了一个内容创作多Agent系统,有策划、写作、编辑、排版四个角色,效果非常好。
4. Spring AI是什么?和LangChain相比,Java栈为什么优先选Spring AI?(阿里/腾讯Java岗必问)
标准回答:
"Spring AI是Spring官方推出的AI应用开发框架,它的目标是为Java开发者提供一个简单、统一的方式来开发AI应用。
和LangChain相比,Spring AI的优势主要体现在以下几个方面:
1. 与Spring生态无缝集成
- 这是Spring AI最大的优势。它可以和Spring Boot、Spring Cloud、Spring Data等所有Spring组件无缝集成
- Java开发者不需要学习新的框架和编程模型,可以用他们熟悉的Spring方式来开发AI应用
- 可以直接使用Spring的依赖注入、事务管理、安全、监控等功能
2. 更适合企业级应用
- Spring AI是为企业级应用设计的,提供了完善的企业级特性,比如配置管理、服务发现、负载均衡、熔断降级等
- 而LangChain主要是为Python开发者设计的,在企业级特性方面比较薄弱
- Spring AI还支持与Spring Security集成,可以很方便地实现权限控制和安全审计
3. 更好的性能和稳定性
- Java本身的性能就比Python好,尤其是在高并发场景下
- Spring框架经过了多年的企业级应用验证,稳定性和可靠性都非常高
- 而LangChain还比较年轻,经常会有API变更和bug
4. 统一的抽象层
- Spring AI提供了统一的抽象层,屏蔽了不同LLM和向量数据库的差异
- 开发者可以很方便地切换不同的模型和数据库,不需要修改业务代码
- 比如从OpenAI切换到豆包,只需要修改配置文件即可
5. 更好的可维护性
- Java是静态类型语言,代码的可维护性和可读性更好
- Spring框架有统一的编码规范和最佳实践,团队协作更顺畅
- 而Python的动态类型特性在大型项目中容易出现问题
当然,Spring AI也有缺点:它的生态还不如LangChain完善,组件和工具相对较少。但对于Java栈的企业来说,Spring AI的优势是压倒性的。阿里和腾讯内部的很多AI项目都已经开始使用Spring AI了。"
进阶场景题
5. 什么情况下你会放弃所有框架,自己手写Agent核心逻辑?(字节真题,考察工程判断力)
标准回答:
"我会在以下四种情况下放弃所有框架,自己手写Agent核心逻辑:
1. 对性能和延迟要求极高的场景
- 框架会带来额外的性能开销,尤其是LangChain这类比较重的框架
- 如果我们的Agent需要处理高并发,或者对响应时间有严格要求,比如毫秒级的实时响应,框架的开销就会变得不可接受
- 自己手写核心逻辑可以最大限度地优化性能,减少不必要的抽象和中间层
2. 业务逻辑非常特殊,框架无法满足需求
- 大多数Agent框架都是为通用场景设计的,如果我们的业务逻辑非常特殊,框架的抽象就会成为限制
- 比如我们需要实现一个非常复杂的状态机,或者需要与现有的遗留系统深度集成
- 这种情况下,强行使用框架会导致代码非常别扭,反而不如自己手写灵活
3. 对可观测性和调试能力要求极高
- 框架的黑盒性很强,执行过程不透明,出了问题很难调试
- 如果我们的Agent系统非常关键,需要能够清晰地看到每一步的执行过程和状态变化
- 自己手写核心逻辑可以完全控制执行流程,方便添加日志、监控和调试信息
4. 长期维护的大型生产级系统
- 框架的更新迭代很快,经常会有API变更和不兼容的情况
- 如果我们的系统需要维护5年以上,依赖第三方框架会带来很大的技术债务
- 自己手写核心逻辑可以完全控制代码的演进,避免被框架绑架
当然,自己手写也不是完全从零开始。我会保留框架中一些成熟的组件,比如工具调用的解析器、LLM的客户端、RAG的检索器等,只重写核心的工作流和状态管理部分。
我们之前做过一个银行的智能客服系统,最开始用了LangChain,后来发现性能和调试都有问题。我们就把核心的Agent工作流自己重写了,只保留了LangChain的LLM客户端和工具调用部分。结果系统的响应时间从平均3秒降到了500毫秒,调试也方便了很多。"
6. 如果你要做一个支持复杂多轮任务的Agent,你会选择LangGraph还是CrewAI?为什么?
标准回答:
"如果要做一个支持复杂多轮任务的Agent,我会选择LangGraph。
原因有以下几点:
1. LangGraph的执行模型更适合复杂多轮任务
- LangGraph是基于状态机的执行模型,天然支持循环、分支、条件判断等复杂逻辑
- 而CrewAI的执行模型相对简单,主要是基于角色的任务分配,对复杂流程的控制能力有限
- 复杂多轮任务通常需要Agent能够根据中间结果不断调整下一步的行动,LangGraph的状态机模型正好适合这种场景
2. LangGraph的可观测性和调试能力更强
- 复杂多轮任务的调试非常困难,需要能够清晰地看到Agent的整个思考过程
- LangGraph会保存每一步的状态和执行结果,还提供了可视化工具,可以直观地查看执行流程
- 而CrewAI的可观测性比较差,出了问题很难定位原因
3. LangGraph的灵活性更高
- LangGraph的抽象非常简洁,只有图、节点、边、状态这几个核心概念
- 开发者可以完全控制执行流程,实现任何复杂的逻辑
- 而CrewAI有很多预定义的概念和限制,灵活性不如LangGraph
4. LangGraph的性能更好
- LangGraph的执行引擎更高效,支持并行执行多个节点
- 而CrewAI在处理复杂任务时,性能会明显下降
5. LangGraph的生态正在快速完善
- LangGraph是LangChain团队推出的,继承了LangChain的生态
- 现在已经有很多第三方组件和工具支持LangGraph
- CrewAI虽然也集成了LangChain,但深度和广度都不如LangGraph
当然,CrewAI也有它的优势,比如上手更简单,对多Agent角色的抽象更友好。如果是简单的多Agent协作任务,CrewAI会更合适。但对于复杂多轮任务,LangGraph是更好的选择。
我们团队最近做了一个数据分析Agent,需要能够自动理解用户的问题,生成SQL,查询数据库,然后生成分析报告。这个任务需要多轮循环和复杂的条件判断,我们用LangGraph实现得非常顺利。如果用CrewAI的话,可能会遇到很多限制。"
7. LlamaIndex在RAG方面比LangChain强在哪里?什么情况下你会同时使用LangGraph和LlamaIndex?
标准回答:
"LlamaIndex在RAG方面比LangChain强的地方主要体现在以下几个方面:
1. 更丰富的索引类型
- LlamaIndex提供了多种索引类型,包括向量索引、树索引、关键词索引、图索引等
- 不同的索引类型适合不同的查询场景,可以根据数据的特点选择最合适的索引
- 而LangChain主要只支持向量索引,其他索引类型的实现比较简单
2. 更强大的数据处理能力
- LlamaIndex提供了丰富的数据连接器,支持几乎所有常见的数据格式和数据源
- LlamaIndex的文档分块算法更先进,支持语义分块、层次分块等多种分块方式
- LlamaIndex还支持文档的预处理和后处理,比如清洗、转换、摘要等
3. 更先进的查询引擎
- LlamaIndex的查询引擎支持多种查询方式,包括向量查询、关键词查询、混合查询、图查询等
- LlamaIndex还支持查询重写、子查询、路由查询等高级功能
- 这些功能可以大大提高RAG的准确率和召回率
4. 更好的性能优化
- LlamaIndex对RAG的各个环节都做了性能优化,比如向量检索的速度、批量处理的效率等
- LlamaIndex还支持异步查询和并行查询,可以提高系统的吞吐量
5. 更专注于RAG场景
- LlamaIndex的核心定位就是RAG框架,所有的功能都是围绕RAG设计的
- 而LangChain是一个通用的AI应用开发框架,RAG只是它的一个功能模块
- 所以LlamaIndex在RAG方面的专业性和深度都超过了LangChain
我会在以下情况下同时使用LangGraph和LlamaIndex:
- 当我的Agent系统需要复杂的工作流和强大的RAG能力时
- 用LangGraph来定义Agent的执行流程,处理任务分解、工具调用、反思等逻辑
- 用LlamaIndex来实现RAG功能,处理数据的索引、检索、生成等环节
- 两者通过接口进行交互,LangGraph在需要检索信息时调用LlamaIndex的查询引擎
这是目前生产级Agent系统最常用的架构组合。LangGraph负责Agent的"大脑",LlamaIndex负责Agent的"记忆",两者各司其职,配合得非常好。"
8. 框架的"黑盒性"是一个常见问题,你在使用框架时遇到过哪些难以调试的问题?怎么解决的?
标准回答:
"框架的黑盒性确实是AI应用开发中一个非常头疼的问题。我在使用LangChain和LangGraph时都遇到过很多难以调试的问题,主要有以下几类:
1. 提示词被框架修改
- 问题:我明明写好了提示词,但框架在执行时会自动添加一些内容,导致模型的行为不符合预期
- 例子:LangChain的Agent会自动在提示词中添加工具的描述和格式要求,有时候会覆盖我自己写的内容
- 解决方法:
- 开启框架的调试模式,打印出实际发送给模型的提示词
- 尽量使用框架提供的提示词模板,而不是自己写原始的提示词
- 如果需要完全控制提示词,可以自己实现LLM调用逻辑,不使用框架的Agent类
2. 工具调用出错
- 问题:工具调用经常出现参数错误、格式错误、函数不存在等问题,但框架的错误信息非常模糊
- 例子:LangChain有时候会把工具调用的参数解析成字符串,而不是JSON对象
- 解决方法:
- 在工具调用前后添加详细的日志,打印出工具的名称、参数和返回结果
- 自己实现工具调用的解析和验证逻辑,在调用工具之前检查参数的正确性
- 使用LangGraph,它的工具调用逻辑更透明,更容易调试
3. 状态管理混乱
- 问题:框架的内存管理比较混乱,不同的链之间共享状态很困难,有时候会出现状态丢失或错乱的情况
- 例子:LangChain的ConversationBufferMemory在多轮对话中有时候会丢失之前的对话历史
- 解决方法:
- 尽量使用框架推荐的状态管理方式,不要自己手动修改状态
- 在关键节点打印状态的内容,检查状态是否正确
- 使用LangGraph,它有统一的状态管理机制,状态的变化非常清晰
4. 执行流程不透明
- 问题:框架的执行流程是黑盒的,出了问题不知道是哪一步出错了
- 例子:LangChain的链执行时,如果某一步出错,错误堆栈会非常长,很难定位到具体的问题
- 解决方法:
- 开启框架的详细日志,记录每一步的执行情况
- 使用框架提供的可视化工具,比如LangSmith、LangFuse
- 把复杂的链拆分成多个小的链,逐个调试
5. 框架的bug
- 问题:框架本身存在bug,导致系统出现奇怪的问题
- 例子:LangChain的某些版本在处理异步调用时会出现内存泄漏
- 解决方法:
- 及时更新框架到最新版本,很多bug在新版本中已经被修复
- 在GitHub上搜索相关的issue,看看有没有其他人遇到过同样的问题
- 如果问题比较严重,可以自己修改框架的源码,或者提交PR给框架团队
总的来说,解决框架黑盒性问题的最好方法就是多打日志,多监控,多可视化。同时,不要过度依赖框架,对于核心逻辑,最好自己有一定的控制能力。"
深度权衡题
9. 现在很多人说"Agent框架都是玩具,生产环境都要自己写",你怎么看这个观点?
标准回答:
"我认为这个观点有一定的道理,但比较片面。Agent框架确实存在很多问题,但也不是完全没有价值。我们应该辩证地看待这个问题。
Agent框架被称为"玩具"的原因:
- 稳定性差:大多数Agent框架还比较年轻,更新迭代很快,经常会有API变更和bug,不适合长期维护的生产系统
- 性能差:框架会带来额外的性能开销,尤其是在高并发场景下,框架的开销会变得不可接受
- 黑盒性强:框架的执行流程不透明,出了问题很难调试
- 灵活性差:框架的抽象是为通用场景设计的,对于特殊的业务逻辑,框架的抽象会成为限制
- 过度设计:很多框架为了追求通用性,加入了很多不必要的功能,导致代码臃肿复杂
但Agent框架也有它的价值:
- 提高开发效率:框架提供了很多现成的组件和工具,可以让开发者快速搭建Agent原型,验证业务价值
- 统一开发规范:框架提供了统一的抽象和开发规范,方便团队协作和代码维护
- 避免重复造轮子:框架已经解决了很多共性的问题,比如LLM集成、工具调用、内存管理等,开发者不需要自己从零开始实现
- 学习和探索:框架是学习Agent技术的好工具,可以让开发者快速了解Agent的核心概念和工作原理
我的观点是:
- 原型阶段用框架:在项目的早期,快速验证业务价值是最重要的。使用框架可以大大缩短开发周期,让我们尽快看到效果。
- 生产阶段逐步替换:当业务验证成功,需要进入生产阶段时,我们可以逐步把核心逻辑从框架中抽出来自己写,只保留框架中一些成熟的、非核心的组件。
- 不要完全依赖框架:即使在原型阶段,也要对框架的实现原理有一定的了解,不要被框架绑架。对于核心逻辑,最好自己有一定的控制能力。
我们团队的做法是:在原型阶段用LangChain快速搭建,验证业务价值。当业务跑通后,我们会把核心的Agent工作流用LangGraph重写,然后逐步把RAG、工具调用等组件也换成自己实现的版本。这样既保证了开发效率,又保证了生产系统的稳定性和性能。"
10. 未来Agent框架的发展方向是什么?你认为哪些功能会被框架内置,哪些需要开发者自己实现?
标准回答:
"我认为未来Agent框架的发展方向主要有以下几个:
1. 从链式执行向状态机执行转变
- 这已经是一个明显的趋势,LangGraph的崛起就是最好的证明
- 未来的Agent框架都会基于状态机模型,支持循环、分支、条件判断等复杂逻辑
- 链式执行模式会逐渐被淘汰
2. 可观测性和调试能力成为核心竞争力
- 可观测性是Agent开发中最大的痛点之一
- 未来的Agent框架会内置强大的可观测性和调试工具,让开发者能够清晰地看到Agent的整个思考过程
- 可视化、日志、监控、追踪等功能会成为框架的标准配置
3. 多Agent协作成为标配
- 单Agent的能力有限,复杂任务需要多个Agent协作完成
- 未来的Agent框架会内置多Agent协作的能力,提供标准化的角色定义、任务分配、通信机制
- 支持不同类型的多Agent架构,比如层次化架构、联邦架构、市场架构等
4. 与云原生深度集成
- Agent系统需要部署在云上,支持水平扩展和高可用
- 未来的Agent框架会与Kubernetes、Serverless等云原生技术深度集成
- 提供一键部署、自动扩缩容、灰度发布、A/B测试等功能
5. 标准化和互操作性
- 现在的Agent框架各自为政,互操作性很差
- 未来会出现统一的Agent标准和协议,比如MCP、A2A等
- 不同框架开发的Agent可以互相通信和协作
我认为会被框架内置的功能:
- LLM和向量数据库的集成
- 基础的工具调用和解析
- 状态管理和持久化
- 可观测性和调试工具
- 基础的多Agent通信机制
我认为需要开发者自己实现的功能:
- 特定业务的工作流和逻辑
- 复杂的多Agent协作策略
- 性能关键的核心组件
- 与现有系统的集成
- 安全和权限控制
总的来说,未来的Agent框架会变得更像一个"操作系统",提供底层的基础设施和通用能力,而开发者只需要专注于实现业务逻辑。"
11. 如果让你设计一个轻量级的企业级Agent框架,你会保留哪些核心功能,去掉哪些功能?
标准回答:
"如果让我设计一个轻量级的企业级Agent框架,我会遵循"最小可用"的原则,只保留最核心的功能,去掉所有不必要的抽象和组件。
我会保留的核心功能:
1. 统一的LLM抽象层
- 支持主流的闭源和开源模型,包括OpenAI、Claude、豆包、Qwen、Llama等
- 提供统一的调用接口,屏蔽不同模型的差异
- 支持流式输出、异步调用、重试、降级等功能
2. 基于状态机的工作流引擎
- 用有向图来定义Agent的执行流程
- 支持节点、边、状态、条件判断、循环等核心概念
- 提供可视化工具,方便调试和监控
3. 工具调用框架
- 支持工具的注册、发现、调用
- 自动解析工具的参数和返回结果
- 支持工具的权限控制和安全审计
4. 状态管理和持久化
- 统一的状态管理机制,支持不同类型的状态存储
- 支持状态的持久化和恢复
- 支持会话管理和上下文跟踪
5. 可观测性和调试工具
- 内置日志、监控、追踪功能
- 支持与OpenTelemetry、Prometheus、Grafana等工具集成
- 提供Web界面,可视化Agent的执行过程
6. 安全和权限控制
- 支持用户认证和授权
- 支持数据加密和脱敏
- 支持操作审计和日志记录
我会去掉的功能:
1. 所有与RAG相关的功能
- RAG是一个非常复杂的领域,有专门的框架如LlamaIndex来处理
- 框架只需要提供一个接口,让开发者可以集成任何RAG系统
2. 预定义的Agent类型
- 比如ConversationalAgent、ReActAgent等
- 这些预定义的Agent类型灵活性差,不适合企业级应用
- 开发者应该自己根据业务需求定义Agent的工作流
3. 大量的第三方集成
- 比如与各种SaaS服务的集成
- 这些集成可以作为插件提供,而不是框架的核心功能
4. 过度的抽象和封装
- 比如Chain、Toolkit、Memory等复杂的抽象
- 尽量使用简单、直观的概念,降低学习曲线
5. 前端相关的功能
- 框架只负责后端逻辑,前端由开发者自己选择技术栈实现
这样设计出来的框架会非常轻量级,核心代码可能只有几千行,但已经足够满足大多数企业级Agent应用的需求。同时,它的性能和可维护性都会比现有的框架好很多。"
三、RAG技术栈选型(必考)
基础必问题
1. 你用过哪些向量数据库?Pinecone、Milvus、Chroma、PGVector、Qdrant的优缺点对比?
标准回答:
"我在项目中用过Pinecone、Milvus、PGVector和Qdrant这四个向量数据库。它们的优缺点对比如下:
| 数据库 | 部署方式 | 性能 | 功能丰富度 | 易用性 | 成本 | 适合场景 |
|---|---|---|---|---|---|---|
| Pinecone | 云服务 | 极高 | 高 | 极高 | 高 | 生产级应用、不想自己运维 |
| Milvus | 开源/云服务 | 极高 | 极高 | 中 | 中 | 大规模生产应用、需要自托管 |
| Qdrant | 开源/云服务 | 高 | 高 | 高 | 中 | 中小规模应用、需要自托管 |
| PGVector | PostgreSQL扩展 | 中 | 中 | 极高 | 低 | 已有PostgreSQL栈、数据量不大 |
| Chroma | 开源 | 低 | 低 | 极高 | 极低 | 原型验证、本地开发 |
具体优缺点:
- Pinecone:优点是完全托管,不需要自己运维,性能最好,支持自动扩缩容;缺点是成本最高,数据存储在第三方,无法私有化部署
- Milvus:优点是开源免费,性能最好,功能最丰富,支持多种索引类型和分布式部署;缺点是部署和运维复杂,学习曲线陡峭
- Qdrant:优点是开源免费,性能好,易用性高,部署简单;缺点是分布式功能不如Milvus完善
- PGVector:优点是可以与PostgreSQL无缝集成,不需要额外部署和维护数据库,支持SQL查询和向量查询的混合;缺点是性能不如专门的向量数据库,不适合大规模数据
- Chroma:优点是轻量级,上手最快,本地开发非常方便;缺点是性能差,不支持分布式,不适合生产环境
我的选型原则是:
- 原型验证和本地开发用Chroma
- 已有PostgreSQL栈且数据量小于100万条用PGVector
- 中小规模生产应用且需要自托管用Qdrant
- 大规模生产应用且需要自托管用Milvus
- 不想自己运维且预算充足用Pinecone"
2. 为什么现在越来越多的人选择PGVector而不是专门的向量数据库?
标准回答:
"现在越来越多的人选择PGVector而不是专门的向量数据库,主要有以下几个原因:
1. 技术栈统一,降低运维成本
- 大多数企业已经在使用PostgreSQL作为关系型数据库
- 使用PGVector不需要额外部署和维护一套新的数据库系统
- 可以复用现有的PostgreSQL运维经验和工具链
- 大大降低了系统的复杂度和运维成本
2. 支持混合查询,功能更强大
- PGVector支持SQL查询和向量查询的混合
- 可以在同一个查询中同时使用结构化条件和向量相似性搜索
- 比如"查找价格在100-200元之间,并且与'红色连衣裙'最相似的商品"
- 这是专门的向量数据库很难做到的
3. 事务和ACID支持
- PostgreSQL是成熟的关系型数据库,支持完整的ACID事务
- 可以保证向量数据和结构化数据的一致性
- 而大多数专门的向量数据库对事务的支持都比较弱
4. 生态完善,集成方便
- PostgreSQL有非常完善的生态系统,支持各种编程语言和工具
- PGVector可以与所有PostgreSQL的客户端和ORM框架无缝集成
- 比如Django、SQLAlchemy、Spring Data等都已经支持PGVector
5. 性能已经足够满足大多数场景
- 随着PGVector的不断优化,它的性能已经有了很大提升
- 对于大多数企业来说,数据量在1000万条以下,PGVector的性能完全足够
- 只有当数据量超过1亿条,或者对查询延迟有极高要求时,才需要考虑专门的向量数据库
6. 数据安全和隐私
- 数据都存储在自己的PostgreSQL数据库中,不需要发送给第三方
- 可以复用现有的PostgreSQL安全机制,比如用户认证、权限控制、数据加密等
当然,PGVector也有它的局限性:它的性能不如Milvus和Qdrant,不支持分布式部署,不适合超大规模数据。但对于80%以上的企业应用来说,PGVector已经足够好了。这就是为什么现在越来越多的人选择PGVector的原因。"
3. 主流的Embedding模型有哪些?OpenAI text-embedding-3、BGE-M3、Jina Embeddings怎么选?
标准回答:
"目前主流的Embedding模型主要有以下几类:
- OpenAI系列:text-embedding-ada-002、text-embedding-3-small、text-embedding-3-large
- 开源系列:BGE-M3、Jina Embeddings、Qwen Embedding、GLM Embedding
- 其他:Cohere Embeddings、Anthropic Embeddings
其中最常用的是OpenAI text-embedding-3、BGE-M3和Jina Embeddings。它们的对比如下:
| 模型 | 维度 | 中文能力 | 英文能力 | 多语言能力 | 长文本支持 | 部署方式 | 成本 |
|---|---|---|---|---|---|---|---|
| text-embedding-3-large | 3072 | 良好 | 优秀 | 良好 | 8191 | API | 高 |
| text-embedding-3-small | 1536 | 一般 | 优秀 | 一般 | 8191 | API | 中 |
| BGE-M3 | 1024 | 优秀 | 良好 | 优秀 | 8192 | 开源/API | 低 |
| Jina Embeddings v2 | 768 | 优秀 | 优秀 | 优秀 | 8192 | 开源/API | 低 |
具体选型建议:
1. 优先考虑BGE-M3
- BGE-M3是目前综合表现最好的开源Embedding模型
- 它的中文能力超过了OpenAI的模型,英文能力也接近OpenAI的水平
- 支持多语言和长文本,功能非常全面
- 可以本地部署,成本极低
- 适合大多数中文场景和私有化部署场景
2. 如果主要是英文场景,选text-embedding-3-large
- OpenAI的模型在英文能力上还是有优势的
- text-embedding-3-large的效果是目前所有模型中最好的
- 适合主要处理英文数据的场景
- 但成本较高,且不能私有化部署
3. 如果对成本敏感,选text-embedding-3-small或Jina Embeddings
- text-embedding-3-small的成本只有large版本的1/5,效果也不错
- Jina Embeddings是开源的,可以本地部署,成本几乎为零
- 适合对成本敏感,且对效果要求不是特别高的场景
4. 如果需要处理超长文本,选BGE-M3或Jina Embeddings
- 它们都支持8192 token的长文本
- 而OpenAI的模型虽然也支持8191 token,但长文本的效果不如前两者
我们团队目前主要用BGE-M3,它的中文能力确实非常出色,而且可以本地部署,成本很低。只有在处理纯英文数据时,我们才会用OpenAI的模型。"
4. Rerank模型怎么选?Cohere Rerank 3、BGE-Reranker-v2、ColBERT的区别?
标准回答:
"Rerank是RAG系统中非常重要的一个环节,可以大大提高检索的准确率。目前主流的Rerank模型有Cohere Rerank 3、BGE-Reranker-v2和ColBERT。它们的区别如下:
1. Cohere Rerank 3
- 类型:闭源API模型
- 优点:
- 效果最好,尤其是在英文和多语言场景下
- 支持长文本,最大支持4096 token
- 速度快,API稳定
- 缺点:
- 成本高
- 不能私有化部署
- 中文能力一般
- 适合场景:英文场景、对效果要求极高、预算充足
2. BGE-Reranker-v2
- 类型:开源模型
- 优点:
- 中文能力最强,超过了Cohere Rerank 3
- 效果接近Cohere Rerank 3
- 可以本地部署,成本极低
- 有不同大小的版本,适合不同的性能需求
- 缺点:
- 英文能力不如Cohere
- 最大支持512 token的文本
- 适合场景:中文场景、私有化部署、对成本敏感
3. ColBERT
- 类型:开源模型
- 优点:
- 采用了后期交互(Late Interaction)的架构,效果非常好
- 速度快,比传统的Rerank模型快很多
- 可以与向量检索结合,实现端到端的检索
- 缺点:
- 中文能力不如BGE-Reranker-v2
- 部署和使用相对复杂
- 生态不如前两者完善
- 适合场景:对速度和效果都有要求的场景、大规模检索
选型建议:
- 如果是中文场景,优先选BGE-Reranker-v2,它的中文效果是目前最好的,而且可以本地部署
- 如果是英文场景,优先选Cohere Rerank 3,它的效果最好
- 如果对速度要求很高,或者需要大规模检索,可以考虑ColBERT
我们团队目前用的是BGE-Reranker-v2-large,它的中文效果非常出色。我们测试过,在我们的知识库上,使用Rerank后,检索的准确率从72%提升到了89%,效果非常明显。"
进阶场景题
5. 什么情况下你会选择稠密向量检索+稀疏向量检索的混合检索方案?
标准回答:
"我会在以下几种情况下选择稠密向量检索+稀疏向量检索的混合检索方案:
1. 当查询包含专有名词或精确术语时
- 稠密向量检索擅长语义匹配,但对于精确的专有名词或术语,效果往往不好
- 比如用户查询"什么是HTTP 404错误",稠密向量可能会返回"HTTP 500错误"的相关内容,因为它们的语义很相似
- 而稀疏向量检索(比如BM25)擅长关键词匹配,可以准确地找到包含"404"这个关键词的文档
- 混合检索可以结合两者的优势,既考虑语义相似性,又考虑关键词匹配
2. 当文档包含大量代码或技术文档时
- 代码和技术文档中有很多精确的标识符和术语,比如函数名、变量名、类名等
- 稠密向量检索很难准确理解这些标识符的含义
- 稀疏向量检索可以准确地匹配这些标识符
- 混合检索可以大大提高技术文档检索的准确率
3. 当查询非常短或非常长时
- 非常短的查询(比如1-2个词)语义信息不足,稠密向量检索的效果不好
- 非常长的查询(比如一段话)包含很多信息,稠密向量检索可能会丢失一些关键信息
- 稀疏向量检索在这两种情况下的表现都比较稳定
- 混合检索可以弥补稠密向量检索的不足
4. 当数据量非常大时
- 当数据量超过1000万条时,稠密向量检索的召回率会有所下降
- 混合检索可以提高整体的召回率,确保不会遗漏相关的文档
- 然后再用Rerank模型对结果进行排序,提高准确率
5. 当需要支持多语言检索时
- 不同语言的稠密向量模型效果差异很大
- 而稀疏向量检索(比如BM25)对语言的依赖性较小
- 混合检索可以提高多语言检索的效果
混合检索的实现方式:
- 分别用稠密向量检索和稀疏向量检索获取前K个结果
- 然后用加权求和的方式将两个结果集合并
- 最后用Rerank模型对合并后的结果进行重新排序
我们团队在做技术知识库项目时,就采用了混合检索方案。我们测试过,单独使用稠密向量检索的准确率是75%,单独使用BM25的准确率是68%,而混合检索的准确率达到了83%,效果提升非常明显。"
6. 图RAG(Graph RAG)和传统向量RAG相比,有什么优势?什么场景下必须用图RAG?
标准回答:
"图RAG是一种新兴的RAG技术,它将知识图谱与向量检索结合起来,解决了传统向量RAG的很多痛点。
图RAG相比传统向量RAG的优势:
1. 更好地捕捉实体和关系
- 传统向量RAG将文档分成独立的块,丢失了块之间的关系
- 图RAG可以提取文档中的实体和关系,构建知识图谱
- 可以更好地理解文档的整体结构和逻辑关系
2. 支持多跳推理
- 传统向量RAG只能检索与查询直接相关的文档块
- 图RAG可以通过知识图谱进行多跳推理,找到间接相关的信息
- 比如查询"张三的老板是谁",图RAG可以先找到张三所在的公司,再找到公司的CEO
3. 更高的准确率和召回率
- 图RAG可以结合语义检索和结构检索
- 可以更准确地找到与查询相关的信息
- 尤其是对于需要综合多个文档信息的复杂查询,效果提升非常明显
4. 更好的可解释性
- 图RAG可以展示知识图谱的结构和推理路径
- 用户可以清楚地看到答案是从哪里来的,是如何推理出来的
- 而传统向量RAG的结果是黑盒的,可解释性差
5. 支持复杂查询
- 图RAG可以支持更复杂的查询,比如聚合查询、过滤查询、路径查询等
- 而传统向量RAG只能支持简单的相似性查询
必须使用图RAG的场景:
1. 复杂问答场景
- 当用户的问题需要综合多个文档的信息,或者需要多跳推理才能回答时
- 比如"2023年公司营收最高的部门是哪个?该部门的负责人是谁?"
2. 专业领域知识库
- 比如医疗、法律、金融等专业领域,有很多实体和关系
- 图RAG可以更好地组织和检索这些专业知识
3. 文档之间有复杂关联的场景
- 比如学术论文、技术文档、产品手册等,文档之间有很多引用和关联
- 图RAG可以捕捉这些关联,提供更全面的信息
4. 对可解释性要求高的场景
- 比如医疗诊断、法律咨询等,需要向用户解释答案的来源和推理过程
当然,图RAG也有缺点:它的实现复杂度高,构建知识图谱需要额外的成本,而且对模型的能力要求也更高。所以只有在传统向量RAG无法满足需求的情况下,才考虑使用图RAG。"
7. 如果你的知识库有1000万+文档,你会怎么设计RAG架构?向量数据库怎么选型和分片?
标准回答:
"如果知识库有1000万+文档,我会设计一个分层的、分布式的RAG架构,具体如下:
整体架构设计:
1. 数据处理层
- 文档解析:支持多种文档格式(PDF、Word、Excel、PPT、HTML等)
- 文档清洗:去除噪声数据,比如页眉页脚、广告、重复内容等
- 文档分块:采用语义分块算法,将文档分成大小合适的块(256-512 token)
- 元数据提取:提取文档的标题、作者、时间、分类等元数据
2. 索引层
- 向量索引:用Embedding模型将文档块转换成向量
- 全文索引:用Elasticsearch构建全文索引,支持关键词检索
- 图索引:提取文档中的实体和关系,构建知识图谱(可选)
- 增量索引:支持增量更新索引,不需要每次都全量重建
3. 检索层
- 混合检索:结合向量检索和全文检索,提高召回率
- 检索路由:根据查询的类型和内容,路由到合适的索引
- 结果合并:将多个检索源的结果合并
- Rerank:用Rerank模型对结果进行重新排序,提高准确率
4. 生成层
- 上下文组装:将检索到的文档块组装成上下文
- 提示词工程:设计合适的提示词,引导模型生成准确的答案
- 模型调用:调用LLM生成答案
- 答案验证:验证答案的准确性和相关性
向量数据库选型:
- 对于1000万+文档的场景,我会选择Milvus 或Qdrant
- Milvus的性能最好,支持分布式部署,适合超大规模数据
- Qdrant的易用性更高,部署和维护更简单,性能也足够好
- 不建议使用PGVector,因为它不支持分布式部署,在1000万+数据量下性能会明显下降
向量数据库分片策略:
- 按文档类别分片 :将不同类别的文档存储在不同的分片中
- 优点:可以实现检索路由,只检索相关类别的分片,提高检索速度
- 缺点:如果某个类别的文档特别多,会导致分片不均匀
- 按时间分片 :将不同时间段的文档存储在不同的分片中
- 优点:可以实现时间范围查询,方便数据的过期和删除
- 缺点:无法实现按类别路由
- 哈希分片 :根据文档ID的哈希值进行分片
- 优点:分片均匀,负载均衡好
- 缺点:无法实现检索路由,每次查询都要检索所有分片
我会采用按类别分片+哈希分片的混合策略:
- 首先按文档类别分成几个大的分片组
- 每个分片组内部再按哈希值分成多个小的分片
- 这样既可以实现检索路由,又可以保证分片均匀
其他优化措施:
- 缓存:缓存热门查询的结果和向量,减少数据库访问
- 批量处理:批量处理文档和向量,提高索引构建速度
- 异步处理:用消息队列异步处理索引构建和更新任务
- 监控和告警:实时监控系统的性能和状态,出现异常及时告警"
8. 增量索引和全量索引怎么选?什么情况下必须做增量索引?
标准回答:
"增量索引和全量索引是RAG系统中两种不同的索引更新方式,各有优缺点,适用于不同的场景。
增量索引:
- 原理:只对新增、修改、删除的文档进行索引更新,不需要重建整个索引
- 优点:
- 更新速度快,对系统性能影响小
- 可以实时或近实时地更新索引
- 节省计算资源和存储空间
- 缺点:
- 实现复杂度高
- 长期增量更新会导致索引碎片化,影响检索性能
- 可能会出现数据不一致的问题
全量索引:
- 原理:删除旧的索引,重新对所有文档进行索引
- 优点:
- 实现简单
- 索引结构完整,没有碎片化,检索性能好
- 数据一致性好
- 缺点:
- 更新速度慢,对系统性能影响大
- 不能实时更新索引
- 浪费计算资源和存储空间
选型原则:
- 如果数据更新频率低,比如每天更新一次,或者数据量小,优先选全量索引
- 如果数据更新频率高,或者数据量大,优先选增量索引
必须做增量索引的情况:
1. 数据更新频率高
- 当数据需要实时或近实时地更新时,必须做增量索引
- 比如新闻网站、电商平台、企业内部的协作工具等
- 这些场景下,用户需要能够检索到最新的内容,全量索引无法满足实时性要求
2. 数据量大
- 当数据量超过100万条时,全量索引的时间会变得很长
- 每次全量索引可能需要几个小时甚至几天的时间
- 这会导致系统长时间不可用,或者性能严重下降
- 必须做增量索引,只更新变化的部分
3. 对系统可用性要求高
- 当系统需要7x24小时不间断运行时,不能进行长时间的全量索引
- 全量索引会占用大量的系统资源,影响正常的查询服务
- 增量索引对系统性能的影响小,可以在不中断服务的情况下进行
4. 计算资源有限
- 全量索引需要大量的计算资源和内存
- 如果计算资源有限,无法承受全量索引的开销
- 增量索引只需要处理变化的部分,资源消耗小
最佳实践:
- 采用增量索引为主,全量索引为辅的策略
- 平时用增量索引实时更新数据
- 定期(比如每周或每月)做一次全量索引,清理索引碎片,优化检索性能
- 全量索引可以在系统负载低的时候进行,比如凌晨
我们团队的RAG系统就是采用这种策略:平时用增量索引实时更新文档,每周日凌晨做一次全量索引。这样既保证了数据的实时性,又保证了检索性能。"
深度权衡题
9. 现在很多人说"RAG已死,Fine-tune永生",也有人说"Fine-tune解决不了幻觉,RAG才是王道",你怎么看?
标准回答:
"我认为这两种观点都比较极端,RAG和Fine-tune不是对立的,而是互补的。它们解决的是不同的问题,适用于不同的场景。
RAG的优势和局限性:
- 优势:
- 可以提供最新的、外部的知识
- 可以提供答案的来源,可解释性好
- 可以有效减少幻觉
- 不需要大量的训练数据
- 更新知识方便,只需要更新知识库
- 局限性:
- 检索的准确率有限,可能会遗漏相关信息或者检索到不相关的信息
- 上下文窗口有限,无法处理太长的文档
- 无法学习到复杂的模式和风格
- 对于需要深度理解和推理的任务,效果不好
Fine-tune的优势和局限性:
- 优势:
- 可以让模型学习到特定领域的知识和风格
- 可以提高模型在特定任务上的准确率和性能
- 可以减少提示词的长度,降低成本和延迟
- 可以学习到复杂的模式和推理能力
- 局限性:
- 需要大量的高质量训练数据
- 训练成本高,时间长
- 更新知识困难,需要重新训练模型
- 无法解决幻觉问题,甚至可能会加剧幻觉
- 可解释性差
我的观点是:RAG和Fine-tune应该结合使用,而不是二选一。
具体的使用策略:
-
用RAG提供外部知识和最新信息
- 所有需要外部知识的任务,都应该用RAG来实现
- 比如知识库问答、文档分析、信息检索等
- RAG是解决知识更新和幻觉问题的最佳方式
-
用Fine-tune优化模型的行为和能力
- 用Fine-tune来优化模型的指令遵循能力、输出格式、风格等
- 比如让模型按照特定的格式输出答案,或者学习特定的写作风格
- 用Fine-tune来提高模型在特定任务上的性能,比如工具调用、代码生成等
-
两者结合的最佳实践
- 首先用RAG搭建基础的系统,验证业务价值
- 然后收集用户的反馈和交互数据
- 用这些数据来Fine-tune模型,优化模型的行为和能力
- 同时继续使用RAG来提供外部知识和最新信息
比如,我们做的企业内部知识库Agent,最开始只用了RAG,效果还不错。后来我们收集了1000条用户的提问和正确答案,用这些数据Fine-tune了豆包4模型。Fine-tune后,模型的回答准确率从82%提升到了91%,而且回答的风格也更符合企业的要求。
所以,RAG和Fine-tune不是谁取代谁的关系,而是相辅相成的。一个好的AI系统应该同时使用这两种技术,发挥它们各自的优势。"
10. RAG技术栈中,哪个环节的优化投入产出比最高?(分块、Embedding、检索、Rerank、Prompt)
标准回答:
"根据我们团队的经验,RAG技术栈中各个环节的优化投入产出比从高到低依次是:Rerank > 分块 > Prompt > 检索 > Embedding。
1. Rerank(投入产出比最高)
- 优化效果:可以将检索的准确率提升15%-30%
- 投入成本:非常低,只需要调用Rerank模型的API或者部署一个开源的Rerank模型
- 原因:Rerank是在检索结果的基础上进行重新排序,可以过滤掉不相关的结果,将最相关的结果排在前面。它可以弥补前面所有环节的不足,是提升RAG效果最有效的方式。
- 我们的测试结果:在我们的知识库上,使用BGE-Reranker-v2后,检索的准确率从72%提升到了89%,效果非常明显。
2. 分块
- 优化效果:可以将检索的准确率提升10%-20%
- 投入成本:低,只需要调整分块的大小和算法
- 原因:分块是RAG的基础,如果分块不合理,后面的所有环节都会受到影响。好的分块应该能够完整地表达一个语义单元,同时不会包含太多不相关的信息。
- 优化方法:从固定大小分块改为语义分块,调整分块的大小(256-512 token效果最好),添加重叠部分。
3. Prompt
- 优化效果:可以将生成的答案质量提升10%-15%
- 投入成本:低,只需要调整提示词的内容和结构
- 原因:提示词直接影响模型的输出质量。好的提示词可以引导模型更好地利用检索到的上下文,生成更准确、更相关的答案。
- 优化方法:明确指示模型只能使用提供的上下文,要求模型引用来源,添加示例,调整提示词的结构和语气。
4. 检索
- 优化效果:可以将检索的准确率提升5%-10%
- 投入成本:中,需要调整检索的参数,或者实现混合检索
- 原因:检索是RAG的核心环节,直接决定了哪些信息会被提供给模型。
- 优化方法:从单一的向量检索改为混合检索(向量+BM25),调整检索的数量(返回10-20个结果效果最好),实现检索路由。
5. Embedding(投入产出比最低)
- 优化效果:可以将检索的准确率提升3%-5%
- 投入成本:高,需要更换Embedding模型,重新构建整个索引
- 原因:现在主流的Embedding模型效果都已经很好了,不同模型之间的差距不大。更换Embedding模型需要重新构建整个索引,成本很高,但效果提升有限。
- 优化建议:除非你现在用的是非常老的模型(比如text-embedding-ada-002),否则不需要频繁更换Embedding模型。
总结:
- 如果你只有有限的时间和资源,优先优化Rerank 和分块,这两个环节的投入产出比最高
- 然后再优化Prompt 和检索
- 最后再考虑更换Embedding模型
我们团队的经验是:把80%的精力投入到Rerank、分块和Prompt这三个环节,可以获得90%的效果提升。"
11. 如果你要做一个支持多模态的RAG系统,技术选型会有什么变化?
标准回答:
"如果要做一个支持多模态的RAG系统,技术选型会有很大的变化,主要体现在以下几个方面:
1. 数据处理层
- 文档解析 :需要支持更多的多媒体格式,比如图片、视频、音频等
- 图片:用OCR模型提取文字,用多模态Embedding模型提取视觉特征
- 视频:提取关键帧,然后用图片的方式处理,同时用ASR模型提取音频中的文字
- 音频:用ASR模型转换成文字
- 文档分块 :需要考虑多媒体内容的特点
- 图片:一张图片作为一个块
- 视频:每30秒或每10个关键帧作为一个块
- 音频:每30秒作为一个块
- 元数据提取:提取多媒体内容的元数据,比如图片的尺寸、视频的时长、音频的采样率等
2. Embedding模型
- 需要使用多模态Embedding模型,而不是传统的文本Embedding模型
- 主流的多模态Embedding模型:
- OpenAI CLIP:最经典的多模态Embedding模型,支持图片和文本
- 阿里Qwen-VL:中文多模态Embedding模型,效果很好
- 百度ERNIE-ViL:中文多模态Embedding模型,适合中文场景
- Jina CLIP:开源的多模态Embedding模型,性能好
- 选型建议:中文场景优先选Qwen-VL,英文场景优先选CLIP
3. 向量数据库
- 向量数据库需要支持存储和检索多模态向量
- 大多数主流的向量数据库都已经支持多模态向量,比如Milvus、Qdrant、Pinecone、PGVector等
- 选型建议:和文本RAG一样,根据数据量和部署方式选择合适的向量数据库
4. 检索层
- 需要支持多模态检索,即可以用文本检索图片,也可以用图片检索图片
- 混合检索:结合文本检索和视觉检索,提高召回率
- Rerank:需要使用多模态Rerank模型,对多模态检索结果进行重新排序
- 主流的多模态Rerank模型:Cohere Rerank Multimodal、BGE-M3 Multimodal
5. 生成层
- 需要使用多模态大模型,而不是传统的文本大模型
- 主流的多模态大模型:
- GPT-4o:目前最好的多模态模型,支持图片、视频、音频
- Claude 3 Opus/Sonnet:支持图片,长文档理解能力强
- 豆包4:中文多模态能力强,国内访问速度快
- Qwen-VL-Max:开源的多模态大模型,适合私有化部署
- 选型建议:优先选GPT-4o,国内场景选豆包4,私有化部署选Qwen-VL-Max
6. 存储层
- 需要存储原始的多媒体文件,比如图片、视频、音频等
- 可以使用对象存储服务,比如阿里云OSS、腾讯云COS、AWS S3等
- 向量数据库中只存储多媒体文件的向量和元数据,以及原始文件的URL
架构变化:
- 整体架构还是分为数据处理层、索引层、检索层、生成层
- 但每个层都需要支持多模态内容
- 增加了多媒体处理和存储的组件
挑战:
- 多模态数据的处理成本高,尤其是视频和音频
- 多模态Embedding和Rerank模型的效果还不如文本模型
- 多模态大模型的成本高,速度慢
我们团队最近做了一个产品手册多模态RAG系统,支持用户用文字或图片查询产品信息。我们用Qwen-VL做Embedding,用GPT-4o做生成,效果非常好。用户可以上传一张产品的图片,系统就能识别出产品型号,并返回详细的产品信息。"
四、工具调用与协议选型(2026年最热考点)
基础必问题
1. 传统Function Call和MCP(Model Context Protocol)的区别是什么?
标准回答:
"传统Function Call和MCP(Model Context Protocol)是两种不同的工具调用方式,它们的核心区别在于设计理念和能力范围:
传统Function Call:
- 设计理念:让模型能够调用预定义的函数来获取外部信息或执行操作
- 工作原理:
- 开发者预先定义好函数的名称、描述、参数格式
- 将这些信息发送给模型
- 模型根据用户的需求,决定是否调用函数以及调用哪个函数
- 模型生成函数调用的参数
- 开发者执行函数,并将结果返回给模型
- 模型根据函数的返回结果生成最终的回答
- 特点:
- 静态注册:函数必须预先定义好,不能动态添加
- 紧耦合:函数与应用程序紧密耦合,无法在不同应用之间共享
- 能力有限:只能调用函数,不能获取其他类型的上下文
- 没有标准化:不同模型的Function Call实现方式不同
MCP(Model Context Protocol):
- 设计理念:为模型提供一个标准化的方式来访问各种外部上下文和服务
- 工作原理:
- MCP定义了一套标准化的协议,用于模型和外部服务之间的通信
- 外部服务实现MCP Server,提供各种资源和工具
- 模型通过MCP Client发现和访问这些资源和工具
- 模型可以动态获取可用的工具列表,不需要预先定义
- 模型可以调用工具,也可以获取文件、数据库、API等各种类型的上下文
- 特点:
- 动态发现:工具可以动态注册和发现,不需要预先定义
- 松耦合:工具与应用程序分离,可以在不同应用之间共享
- 能力全面:不仅支持工具调用,还支持获取各种类型的上下文
- 标准化:是一个开放的标准,得到了业界的广泛支持
核心区别总结:
| 特性 | 传统Function Call | MCP |
|---|---|---|
| 注册方式 | 静态注册 | 动态发现 |
| 耦合度 | 紧耦合 | 松耦合 |
| 能力范围 | 仅函数调用 | 工具调用+上下文获取 |
| 标准化 | 无统一标准 | 开放标准 |
| 可重用性 | 差 | 好 |
| 可扩展性 | 差 | 好 |
简单来说,传统Function Call是MCP的一个子集。MCP不仅包含了传统Function Call的所有功能,还提供了更多的能力和更好的标准化。"
2. 什么情况下你会选择用MCP,而不是自己写工具调用逻辑?
标准回答:
"我会在以下几种情况下选择用MCP,而不是自己写工具调用逻辑:
1. 当需要集成大量工具时
- 如果你的系统需要集成几十个甚至上百个工具,自己写工具调用逻辑会非常繁琐
- MCP提供了标准化的工具注册和发现机制,可以大大简化工具的集成工作
- 你只需要实现MCP Server,就可以将所有工具统一管理起来
- 模型可以动态获取可用的工具列表,不需要每次添加工具都修改代码
2. 当需要在多个应用之间共享工具时
- 如果你有多个Agent应用,它们都需要使用相同的工具
- 自己写工具调用逻辑会导致代码重复,维护困难
- MCP可以将工具部署为独立的服务,多个应用可以共享同一个MCP Server
- 这样只需要维护一份工具代码,所有应用都可以使用
3. 当需要支持多种模型时
- 不同模型的Function Call实现方式不同,自己写工具调用逻辑需要为每个模型做适配
- MCP是一个标准化的协议,所有支持MCP的模型都可以使用相同的工具
- 你只需要实现一次MCP Server,就可以支持所有主流的模型
- 这样可以大大降低适配不同模型的成本
4. 当需要获取复杂的上下文时
- 传统的Function Call只能调用函数,不能直接获取文件、数据库、API等复杂的上下文
- MCP不仅支持工具调用,还支持获取各种类型的上下文
- 比如你可以用MCP直接访问文件系统、数据库、Git仓库等
- 这样可以大大简化上下文的获取和处理逻辑
5. 当需要与生态系统集成时
- MCP已经得到了业界的广泛支持,很多框架和工具都已经集成了MCP
- 比如LangChain、LangGraph、CrewAI等都已经支持MCP
- 很多第三方服务也提供了MCP Server,比如GitHub、Slack、Google Drive等
- 使用MCP可以很方便地与这些生态系统集成
6. 当需要提高系统的可维护性和可扩展性时
- MCP将工具与应用程序分离,降低了系统的耦合度
- 你可以独立地开发、测试、部署和更新工具,不需要修改应用程序的代码
- 这样可以大大提高系统的可维护性和可扩展性
我不会选择用MCP的情况:
- 当只需要集成少数几个简单的工具时
- 当对性能和延迟有极高要求时
- 当需要完全控制工具调用的逻辑时
总的来说,MCP适合中大型的Agent系统,尤其是需要集成大量工具、支持多种模型、与生态系统集成的场景。对于简单的小型应用,自己写工具调用逻辑可能更简单高效。"
3. MCP Server有哪些主流实现?你会选择用Python还是TypeScript写MCP Server?
标准回答:
"目前MCP Server的主流实现主要有以下几个:
官方实现:
- @modelcontextprotocol/sdk:官方提供的TypeScript SDK,是最成熟、最完善的实现
- mcp:官方提供的Python SDK,功能也比较完善
第三方实现:
- LangChain MCP:LangChain提供的MCP集成,可以将LangChain的工具包装成MCP Server
- Spring AI MCP:Spring AI提供的MCP集成,适合Java栈
- 各种第三方MCP Server:比如GitHub MCP Server、Slack MCP Server、Google Drive MCP Server等
Python vs TypeScript写MCP Server的对比:
用Python写MCP Server的优势:
- AI生态最完善:大多数AI工具和库都是用Python写的,集成起来非常方便
- 数据处理能力强:Python有丰富的数据处理库,比如Pandas、NumPy等
- 上手简单:Python语法简单,学习曲线低
- 适合快速原型开发:可以快速搭建MCP Server,验证想法
用Python写MCP Server的劣势:
- 性能不如TypeScript:Python的运行速度比Node.js慢,尤其是在处理高并发时
- 类型系统不如TypeScript完善:Python是动态类型语言,容易出现类型错误
- 前端集成不如TypeScript方便:如果你的前端是用TypeScript写的,用TypeScript写MCP Server可以共享类型定义
用TypeScript写MCP Server的优势:
- 官方SDK最成熟:官方的TypeScript SDK是最先发布的,功能最完善,文档最详细
- 性能好:Node.js的异步I/O性能很好,适合处理高并发
- 类型系统完善:TypeScript是静态类型语言,可以在编译时发现类型错误,提高代码质量
- 全栈开发方便:如果你的前端是用TypeScript写的,可以共享类型定义和工具函数
- 生态完善:Node.js有丰富的包生态系统,可以很方便地集成各种第三方服务
用TypeScript写MCP Server的劣势:
- AI生态不如Python完善:有些AI工具和库没有TypeScript版本
- 学习曲线稍陡:对于不熟悉TypeScript的开发者来说,需要一定的学习时间
我的选择:
- 如果我的团队主要用Python,或者需要集成大量的Python AI工具,我会选择用Python写MCP Server
- 如果我的团队主要用TypeScript,或者对性能和类型安全有较高要求,我会选择用TypeScript写MCP Server
- 官方推荐用TypeScript,因为它的SDK最成熟,性能也更好
我们团队目前主要用TypeScript写MCP Server,因为我们的前端和后端都是用TypeScript写的,全栈开发非常方便。而且官方的TypeScript SDK确实非常好用,文档也很详细。"
4. A2A(Agent-to-Agent)协议是什么?和MCP是什么关系?
标准回答:
"A2A(Agent-to-Agent)协议是一个用于Agent之间通信和协作的标准化协议。它定义了Agent之间如何发现对方、如何交换信息、如何协作完成任务。
A2A协议的核心目标:
- 实现不同Agent之间的互操作性,让不同厂商、不同框架开发的Agent可以互相通信和协作
- 提供标准化的Agent接口,让Agent可以像服务一样被调用
- 支持复杂的多Agent协作模式,比如任务分配、结果汇总、协商决策等
A2A协议的核心概念:
- Agent:具有自主能力的实体,可以接收任务,执行操作,返回结果
- 能力:Agent可以提供的服务或功能,比如"写代码"、"分析数据"、"生成图片"等
- 任务:Agent需要完成的工作,包含任务描述、参数、要求等
- 消息:Agent之间交换的信息,包含任务请求、结果响应、状态更新等
A2A和MCP的关系:
A2A和MCP是互补的关系,它们解决的是不同层面的问题:
1. 解决的问题不同
- MCP解决的是模型与外部服务之间的通信问题,让模型可以访问各种外部上下文和工具
- A2A解决的是Agent与Agent之间的通信问题,让不同的Agent可以互相协作完成任务
2. 层次不同
- MCP是底层协议,位于模型和工具之间
- A2A是上层协议,位于Agent和Agent之间
- A2A可以使用MCP来访问工具和上下文
3. 能力范围不同
- MCP主要关注工具调用和上下文获取
- A2A主要关注Agent之间的任务分配、协作和协调
4. 相互补充
- 一个Agent可以通过MCP来访问外部工具和上下文
- 多个Agent可以通过A2A来协作完成复杂的任务
- MCP为A2A提供了底层的工具和上下文支持
- A2A为MCP提供了更高层次的协作能力
举例说明:
假设我们有一个写文章的多Agent系统,包含三个Agent:策划Agent、写作Agent、编辑Agent。
- 策划Agent负责制定文章大纲
- 写作Agent负责根据大纲写文章
- 编辑Agent负责修改和润色文章
在这个系统中:
- 每个Agent都可以通过MCP来访问文件系统、数据库、搜索引擎等工具
- 三个Agent之间通过A2A协议来通信和协作
- 用户将写文章的任务发送给策划Agent
- 策划Agent制定好大纲后,通过A2A将写文章的任务发送给写作Agent
- 写作Agent写完文章后,通过A2A将文章发送给编辑Agent
- 编辑Agent修改好文章后,通过A2A将最终结果返回给用户
总的来说,MCP是Agent的"手和脚",让Agent可以与外部世界交互;A2A是Agent的"语言",让Agent之间可以互相交流和协作。两者结合起来,就可以构建强大的多Agent系统。"
进阶场景题
5. 如果你的系统需要集成100+个工具,你会选择传统的静态工具注册还是MCP动态发现?为什么?
标准回答:
"如果系统需要集成100+个工具,我会毫不犹豫地选择MCP动态发现。原因有以下几点:
1. 传统静态工具注册的痛点在工具数量多时会被无限放大
- 代码臃肿:每个工具都需要手动定义函数签名、描述、参数格式,100+个工具会导致代码量非常大,难以维护
- 编译/部署耦合:添加或修改任何一个工具都需要重新编译和部署整个应用,发布周期长,风险高
- 模型上下文浪费:每次调用模型都需要将所有100+个工具的描述发送给模型,浪费大量的token,增加成本和延迟
- 无法按需加载:模型每次都要从100+个工具中选择合适的,容易出错,准确率下降
2. MCP动态发现完美解决了这些痛点
- 工具与应用分离:工具部署为独立的MCP Server,与主应用完全解耦。添加或修改工具只需要更新对应的MCP Server,不需要重新部署主应用
- 按需加载工具:可以根据用户的需求和当前的上下文,动态加载相关的工具,而不是每次都加载所有工具。这样可以大大减少发送给模型的工具描述长度,节省token,提高准确率
- 标准化管理:所有工具都遵循MCP标准,有统一的注册、发现、调用方式。可以很方便地管理和监控所有工具
- 工具复用:工具可以在多个应用之间共享,避免重复开发
- 生态丰富:可以直接使用第三方提供的MCP Server,比如GitHub、Slack、Google Drive等,不需要自己开发
3. 针对100+个工具的MCP最佳实践
- 按领域划分MCP Server:将100+个工具按领域分成多个MCP Server,比如"办公工具"、"开发工具"、"数据分析工具"等
- 实现工具路由:根据用户的问题,先判断属于哪个领域,然后只加载对应领域的MCP Server和工具
- 工具权限控制:实现细粒度的工具权限控制,不同的用户可以使用不同的工具
- 工具监控和告警:监控每个工具的调用次数、成功率、响应时间,出现异常及时告警
- 工具版本管理:支持工具的版本管理,可以平滑地升级工具
4. 可能的挑战和解决方案
- 性能问题:动态发现工具会有一定的性能开销。解决方案:缓存工具列表,定期更新
- 安全问题:动态加载工具可能会带来安全风险。解决方案:实现严格的权限控制和沙箱机制
- 调试问题:工具分布在多个MCP Server中,调试可能会比较困难。解决方案:统一的日志和监控系统
我们团队之前做过一个企业内部的Agent平台,集成了120多个工具。最开始用的是传统的静态工具注册,代码非常臃肿,每次添加工具都要重新部署整个平台,非常痛苦。后来我们迁移到了MCP动态发现,将工具按领域分成了8个MCP Server。迁移后,代码量减少了60%,添加新工具的时间从几天缩短到了几小时,模型的工具调用准确率也从78%提升到了87%。"
6. MCP的安全风险有哪些?在生产环境中使用MCP需要做哪些安全加固?
标准回答:
"MCP虽然带来了很多便利,但也引入了一些新的安全风险。在生产环境中使用MCP,必须做好安全加固。
MCP的主要安全风险:
1. 未授权访问
- 如果MCP Server没有做好认证和授权,攻击者可以未经授权地访问和调用工具
- 这可能会导致敏感数据泄露,或者被攻击者利用工具执行恶意操作
2. 工具调用滥用
- 攻击者可以通过Agent调用危险的工具,比如执行系统命令、读写文件、访问数据库等
- 这可能会导致系统被入侵,数据被篡改或删除
3. 输入注入攻击
- 攻击者可以通过精心构造的输入,诱导模型调用恶意的工具,或者传递恶意的参数给工具
- 比如SQL注入、命令注入、代码注入等
4. 数据泄露
- MCP在传输过程中可能会泄露敏感数据
- 工具返回的结果中可能包含敏感信息,被模型泄露给用户
5. 恶意MCP Server
- 如果集成了第三方的MCP Server,可能会存在恶意的MCP Server
- 恶意的MCP Server可能会返回虚假的结果,或者窃取数据
生产环境中的安全加固措施:
1. 认证和授权
- 为所有MCP Server实现强认证机制,比如API Key、OAuth2、JWT等
- 实现细粒度的授权控制,不同的用户和Agent只能访问他们有权限使用的工具
- 定期轮换API Key和凭证
2. 网络安全
- 所有MCP通信都使用TLS加密
- 将MCP Server部署在私有网络中,不暴露在公网上
- 使用防火墙限制MCP Server的访问来源
- 实现IP白名单,只允许可信的IP地址访问MCP Server
3. 工具安全
- 对所有工具进行安全审计,移除危险的工具,比如执行系统命令的工具
- 对工具的参数进行严格的验证和过滤,防止注入攻击
- 为工具设置调用频率限制,防止滥用
- 在沙箱环境中执行工具,隔离工具的运行环境
4. 数据安全
- 对敏感数据进行加密存储和传输
- 对工具返回的结果进行敏感信息过滤,防止数据泄露
- 实现数据脱敏,对敏感信息进行掩码处理
5. 日志和审计
- 记录所有的MCP调用日志,包括调用者、工具名称、参数、返回结果、时间等
- 实现审计功能,定期审计MCP调用日志,发现异常行为
- 保留足够长的日志时间,以便事后调查
6. 第三方MCP Server管理
- 只集成可信的第三方MCP Server
- 对第三方MCP Server进行安全评估
- 限制第三方MCP Server的权限,只允许它们访问必要的资源
- 定期检查第三方MCP Server的安全状况
7. 监控和告警
- 实时监控MCP Server的运行状态和调用情况
- 设置异常告警,比如异常的调用频率、异常的参数、异常的返回结果等
- 及时响应安全事件,采取必要的措施
我们团队在生产环境中使用MCP时,就严格遵循了这些安全措施。我们实现了基于OAuth2的认证和授权,所有工具都在沙箱中运行,对所有参数进行严格的验证,并且记录了详细的调用日志。到目前为止,我们没有遇到过任何安全问题。"
7. 工具调用的"同步调用"和"异步调用"怎么选?什么情况下必须用异步调用?
标准回答:
"工具调用的同步调用和异步调用各有优缺点,适用于不同的场景。
同步调用:
- 原理:Agent调用工具后,阻塞等待工具执行完成,然后继续执行下一步
- 优点:
- 逻辑简单,容易实现和调试
- 执行顺序清晰,状态管理简单
- 响应及时,用户体验好
- 缺点:
- 会阻塞Agent的执行,工具执行时间长的话,会导致Agent响应慢
- 无法处理长时间运行的工具
- 资源利用率低,Agent在等待工具执行时无法做其他事情
异步调用:
- 原理:Agent调用工具后,不阻塞等待,继续执行其他任务。工具执行完成后,通过回调或轮询的方式通知Agent
- 优点:
- 不会阻塞Agent的执行,可以同时处理多个任务
- 可以处理长时间运行的工具
- 资源利用率高
- 缺点:
- 逻辑复杂,实现和调试难度大
- 状态管理复杂,需要处理工具执行的各种状态
- 用户体验可能不好,用户需要等待工具执行完成
选型原则:
- 如果工具执行时间短(小于10秒),优先选同步调用
- 如果工具执行时间长(大于10秒),必须用异步调用
必须使用异步调用的情况:
1. 工具执行时间长
- 当工具需要执行很长时间才能返回结果时,必须用异步调用
- 比如:
- 生成一个复杂的报告,可能需要几分钟
- 运行一个数据分析任务,可能需要几十分钟
- 调用一个第三方API,响应时间很长
- 如果用同步调用,Agent会一直阻塞等待,导致用户体验非常差,甚至会超时
2. 需要并行调用多个工具
- 当Agent需要同时调用多个工具,并且这些工具之间没有依赖关系时,用异步调用可以大大提高效率
- 比如:Agent需要同时调用搜索引擎、数据库、文件系统来获取信息
- 用同步调用的话,需要一个一个调用,总时间是所有工具执行时间的总和
- 用异步调用的话,可以同时调用所有工具,总时间是最长的那个工具的执行时间
3. 高并发场景
- 在高并发场景下,同步调用会导致大量的线程阻塞,资源利用率低,系统吞吐量下降
- 异步调用可以用少量的线程处理大量的请求,提高系统的吞吐量和资源利用率
4. 非实时任务
- 对于非实时的任务,比如批量处理、定时任务等,不需要实时返回结果,可以用异步调用
- Agent可以提交任务后立即返回,任务在后台执行,完成后再通知用户
异步调用的最佳实践:
- 使用消息队列来管理异步任务,比如RabbitMQ、Kafka等
- 实现任务状态跟踪,让用户可以查看任务的执行进度
- 实现任务超时和重试机制,处理任务失败的情况
- 实现任务通知机制,任务完成后通过邮件、短信、推送等方式通知用户
- 提供任务取消功能,允许用户取消正在执行的任务
我们团队的Agent系统同时支持同步调用和异步调用。对于执行时间短的工具,比如查询数据库、调用简单的API,我们用同步调用。对于执行时间长的工具,比如生成报告、运行数据分析任务,我们用异步调用。这样既保证了用户体验,又提高了系统的效率。"
8. 如果一个工具调用需要很长时间(比如几分钟),你怎么设计Agent的交互逻辑?
标准回答:
"如果一个工具调用需要很长时间(比如几分钟),我会设计一个异步的、状态驱动的交互逻辑,具体如下:
整体设计思路:
- 将长时间运行的工具调用与Agent的主执行流程分离
- 用状态机来管理工具调用的整个生命周期
- 实时向用户反馈工具的执行状态
- 工具执行完成后,自动继续Agent的执行流程
具体实现步骤:
1. 任务提交阶段
- 当Agent判断需要调用一个长时间运行的工具时,不直接执行工具,而是创建一个异步任务
- 将任务提交到消息队列,由专门的工作进程来执行
- 立即向用户返回一个响应,告诉用户任务已经开始执行,并提供一个任务ID
- 响应示例:"我正在为您生成数据分析报告,这可能需要3-5分钟的时间。您可以随时询问任务进度,或者稍后再来查看结果。任务ID:12345"
2. 任务执行阶段
- 工作进程从消息队列中获取任务,执行工具调用
- 在执行过程中,定期更新任务的状态和进度信息到数据库
- 状态包括:排队中、执行中、已完成、失败
- 进度信息包括:已完成的百分比、当前正在执行的步骤等
- 如果工具支持进度回调,可以实时获取更准确的进度信息
3. 状态查询阶段
- 用户可以随时询问任务的进度,比如"任务12345完成了吗?"
- Agent查询数据库,获取任务的最新状态和进度信息
- 向用户返回任务的当前状态和预计完成时间
- 响应示例:"您的数据分析报告正在生成中,目前已完成60%,预计还需要1-2分钟。"
4. 任务完成阶段
- 当工具执行完成后,工作进程将结果保存到数据库,并更新任务状态为"已完成"
- 同时发送一个通知给Agent系统
- Agent系统可以通过以下几种方式通知用户:
- 如果用户还在对话中,主动推送结果给用户
- 如果用户已经离开,发送邮件或短信通知用户
- 在用户下次访问时,提示用户有已完成的任务
5. 结果处理阶段
- 当用户获取到任务完成的通知后,Agent从数据库中获取工具的执行结果
- 根据工具的执行结果,继续执行Agent的后续逻辑
- 生成最终的回答,返回给用户
6. 异常处理阶段
- 如果工具执行失败,工作进程将错误信息保存到数据库,并更新任务状态为"失败"
- Agent系统通知用户任务失败,并提供失败原因
- 允许用户重试任务,或者提供其他解决方案
技术实现要点:
- 使用消息队列(如RabbitMQ、Kafka)来管理异步任务
- 使用数据库(如PostgreSQL、Redis)来存储任务状态和结果
- 使用状态机来管理任务的生命周期
- 实现任务超时和重试机制
- 实现任务取消功能
- 提供统一的任务管理界面,方便用户查看和管理所有任务
用户体验优化:
- 提供准确的预计完成时间
- 实时更新任务进度
- 允许用户在任务执行过程中做其他事情
- 任务完成后主动通知用户
- 提供清晰的错误信息和解决方案
我们团队做的数据分析Agent就采用了这种交互逻辑。用户提交一个数据分析任务后,可以继续和Agent聊天,或者关闭页面。任务完成后,系统会自动发送邮件通知用户。用户回来后,只需要问一句"我的报告好了吗?",Agent就会把生成好的报告返回给用户。这种交互逻辑大大提高了用户体验。"
深度权衡题
9. MCP会成为未来Agent工具调用的标准协议吗?为什么?
标准回答:
"我认为MCP有很大的概率会成为未来Agent工具调用的标准协议,主要有以下几个原因:
1. 解决了行业的核心痛点
- 目前Agent工具调用没有统一的标准,不同模型、不同框架的实现方式都不同
- 开发者需要为每个模型和框架做适配,重复劳动多,维护成本高
- MCP提供了一个标准化的解决方案,统一了工具的注册、发现、调用方式
- 这正是整个行业迫切需要的
2. 得到了业界的广泛支持
- MCP是由Anthropic发起的,得到了OpenAI、Google、Meta、微软等所有主流AI厂商的支持
- 所有主流的Agent框架,比如LangChain、LangGraph、CrewAI、AutoGPT等都已经集成了MCP
- 很多第三方服务也开始提供MCP Server,比如GitHub、Slack、Google Drive、Notion等
- 这种全行业的支持是一个协议能够成为标准的关键
3. 设计合理,扩展性好
- MCP的设计非常简洁和优雅,核心概念只有几个,容易理解和实现
- 它采用了RESTful风格的API设计,符合现代Web开发的最佳实践
- 它的扩展性非常好,可以支持各种类型的工具和上下文
- 它还支持版本化,可以平滑地升级和演进
4. 生态正在快速形成
- 现在已经有大量的开源MCP Server和工具可用
- 很多公司和开发者都在为MCP生态做贡献
- 社区非常活跃,问题解决速度快
- 一个繁荣的生态是一个协议能够长期发展的基础
5. 符合技术发展的趋势
- 未来的Agent系统会越来越复杂,需要集成越来越多的工具和服务
- 标准化是技术发展的必然趋势,只有标准化才能实现大规模的协作和集成
- MCP正好符合这个趋势,为Agent的大规模应用奠定了基础
可能的挑战:
- 安全问题:MCP的动态发现和调用机制可能会带来新的安全风险
- 性能问题:MCP的HTTP协议在高并发场景下可能会有性能瓶颈
- 兼容性问题:不同厂商的实现可能会有差异,导致兼容性问题
但这些挑战都是可以解决的。随着MCP的不断发展和完善,这些问题都会得到解决。
我的结论:
MCP已经具备了成为标准的所有条件:解决了核心痛点、得到了业界广泛支持、设计合理、生态正在快速形成。我相信在未来1-2年内,MCP会成为Agent工具调用的事实标准。所有的Agent开发者都应该尽早学习和掌握MCP。"
10. 现在很多公司都在做自己的工具市场,你认为工具市场的核心竞争力是什么?
标准回答:
"现在很多公司都在做自己的Agent工具市场,比如OpenAI的GPT Store、Anthropic的Tool Store、字节的豆包插件市场等。我认为工具市场的核心竞争力主要体现在以下几个方面:
1. 工具的质量和丰富度
- 这是工具市场最基础也是最重要的竞争力
- 用户使用工具市场的核心目的是为了获取有用的工具
- 工具的质量越高,数量越多,覆盖的场景越广,对用户的吸引力就越大
- 不仅要有通用的工具,还要有各个垂直领域的专业工具
2. 工具的易用性
- 工具应该能够一键安装和使用,不需要复杂的配置
- 工具的描述应该清晰准确,让用户能够快速了解工具的功能和使用方法
- 工具的参数应该简单明了,不需要用户输入复杂的信息
- 好的易用性可以大大降低用户的使用门槛
3. 与Agent的深度集成
- 工具应该能够与Agent深度集成,Agent可以自动判断什么时候使用哪个工具
- 不需要用户手动选择和调用工具
- 工具的返回结果应该能够被Agent很好地理解和利用
- 深度集成可以大大提高用户体验和工具的使用效率
4. 开发者生态
- 工具市场的繁荣离不开开发者的贡献
- 一个好的工具市场应该有完善的开发者支持体系,包括文档、SDK、示例代码等
- 应该有合理的分成机制,让开发者能够获得收益
- 应该有活跃的开发者社区,方便开发者交流和学习
- 强大的开发者生态可以保证工具市场的持续更新和发展
5. 安全和信任
- 工具可以访问用户的数据和执行操作,安全是非常重要的
- 工具市场应该有严格的审核机制,确保工具的安全性和可靠性
- 应该有完善的权限控制机制,让用户可以控制工具的访问权限
- 应该有明确的隐私政策,保护用户的数据安全
- 安全和信任是用户使用工具市场的前提
6. 标准化和互操作性
- 工具应该遵循统一的标准,比如MCP协议
- 这样工具可以在不同的Agent平台之间通用,不需要为每个平台单独开发
- 标准化可以大大降低开发者的开发成本,提高工具的复用性
- 互操作性可以让用户在不同的平台之间自由切换
7. 个性化推荐
- 工具市场应该能够根据用户的使用习惯和需求,个性化推荐合适的工具
- 这样可以帮助用户发现更多有用的工具,提高工具的使用率
- 好的推荐算法可以大大提高用户的粘性
总结:
工具市场的竞争是一个综合实力的竞争。短期来看,工具的数量和丰富度是最重要的;长期来看,开发者生态、安全和信任、标准化和互操作性才是核心竞争力。
我认为未来的工具市场会走向标准化,基于MCP协议的工具会成为主流。那些能够率先建立起完善的开发者生态,并且保证安全和信任的工具市场,会最终胜出。"
11. 工具调用的"准确性"和"丰富性",哪个更重要?如何平衡?
标准回答:
"工具调用的准确性和丰富性是一对矛盾,需要根据具体场景进行平衡。但在大多数情况下,准确性比丰富性更重要。
为什么准确性更重要:
- 工具调用是Agent与外部世界交互的接口,如果工具调用出错,整个Agent的执行流程就会中断或产生错误结果
- 错误的工具调用会导致错误的答案,降低用户对Agent的信任
- 丰富但不准确的工具,不仅没有用,反而会带来负面影响
- 比如一个Agent有100个工具,但工具调用准确率只有50%,用户使用起来会非常痛苦,经常会得到错误的结果
丰富性的价值:
- 丰富的工具可以让Agent完成更多的任务,扩展Agent的能力范围
- 可以满足用户更多样化的需求
- 可以提高Agent的实用性和竞争力
- 比如一个只能聊天的Agent,和一个可以查天气、订机票、写代码、分析数据的Agent,后者显然更有价值
如何平衡准确性和丰富性:
1. 优先保证核心工具的准确性
- 首先识别出用户最常用的20%的核心工具
- 投入最多的精力来优化这些核心工具的调用准确率
- 确保这些核心工具的调用准确率达到90%以上
- 这20%的核心工具可以满足用户80%的需求
2. 逐步添加非核心工具
- 在保证核心工具准确性的基础上,逐步添加非核心工具
- 每添加一个新工具,都要进行充分的测试,确保它的调用准确率达到可接受的水平
- 如果一个工具的调用准确率太低,就不要上线,或者将它标记为实验性工具
3. 实现工具路由和分层
- 根据用户的问题,先判断属于哪个领域,然后只加载对应领域的工具
- 这样可以减少每次发送给模型的工具数量,提高工具调用的准确率
- 比如用户问一个关于天气的问题,就只加载天气相关的工具,而不是所有工具
4. 提供工具调用的确认机制
- 对于高风险的工具,或者模型不确定是否应该调用的工具,提供确认机制
- 在调用工具之前,先询问用户是否确认调用
- 这样可以避免错误的工具调用带来的负面影响
5. 实现工具调用的错误处理和重试机制
- 当工具调用失败时,Agent应该能够识别错误,并进行重试或调整
- 可以尝试用不同的参数调用工具,或者调用其他相关的工具
- 如果多次重试都失败,应该向用户说明情况,并提供其他解决方案
6. 持续优化工具调用的准确率
- 收集用户的反馈和工具调用的日志
- 分析工具调用失败的原因,不断优化提示词和工具的描述
- 用真实的用户数据来Fine-tune模型,提高工具调用的准确率
不同场景下的侧重点:
- 企业内部场景:优先保证准确性,因为错误的结果会带来严重的后果。工具的丰富性可以逐步添加。
- C端消费场景:在保证基本准确性的前提下,尽量提高工具的丰富性,以满足用户多样化的需求。
- 高风险场景:比如金融、医疗、法律等,准确性是第一位的,宁可少一些工具,也要保证每个工具的调用都是准确的。
我们团队的做法是:核心工具的调用准确率必须达到95%以上,非核心工具的调用准确率必须达到80%以上才能上线。我们还实现了工具路由和确认机制,大大提高了工具调用的准确性和用户体验。"
五、全栈后端选型
Python栈(主流)
1. FastAPI、Flask、Django在Agent后端开发中怎么选?为什么几乎所有人都用FastAPI?
标准回答:
"FastAPI、Flask、Django是Python后端开发中最常用的三个框架,它们在Agent后端开发中的选型如下:
三个框架的对比:
| 框架 | 性能 | 异步支持 | 开发效率 | 生态完善度 | 适合场景 |
|---|---|---|---|---|---|
| FastAPI | 最高 | 原生支持 | 高 | 良好 | API服务、Agent后端、高并发场景 |
| Flask | 中 | 需要第三方扩展 | 高 | 优秀 | 小型应用、原型开发、简单的API |
| Django | 低 | 支持有限 | 中 | 最优秀 | 全栈Web应用、需要Admin后台的应用 |
FastAPI
- 适用场景 :
○ Agent后端核心服务:大模型调用、SSE流式对话、工具调用编排等IO密集型接口
○ 高并发C端Agent产品、多用户会话场景
○ 微服务架构的Agent模块拆分 - 优点 :
○ 原生全异步架构,完美匹配大模型调用、工具调用等长耗时IO场景,并发能力强
○ 基于Pydantic的强类型校验,天然适配Agent结构化输入输出、工具参数校验
○ 自动生成OpenAPI文档,前后端联调、Agent接口对接效率高
○ 是主流Agent框架的官方示例首选,生态适配最完善 - 缺点 :
○ 异步编程门槛较高,阻塞代码会导致整体性能骤降
○ 无内置ORM、权限、后台管理,企业级能力需自行集成
○ 中小型团队异步代码的维护成本更高
Flask
- 适用场景 :
○ 轻量级工具服务、小型Agent Demo、内部工具型Agent
○ 同步逻辑为主、并发量低的内部Agent系统
○ 快速原型验证 - 优点 :
○ 极致轻量,上手成本极低,几行代码即可搭建接口
○ 生态丰富,第三方插件多,同步场景开发灵活 - 缺点 :
○ 原生同步架构,处理大模型长耗时调用时并发能力极差,单进程只能串行
○ 无原生类型校验,Agent结构化参数需手动处理
○ 异步支持为补丁式实现,与Agent异步生态兼容差
Django
- 适用场景 :
○ 包含完整用户体系、后台管理的重业务型Agent平台
○ 团队已有深厚Django技术栈,Agent作为系统附属模块 - 优点 :
○ 全栈框架,内置ORM、权限、后台管理、认证体系,重业务场景开发快
○ 生态极其成熟,企业级功能完善 - 缺点 :
○ 框架过重,异步支持不彻底,核心组件与AgentIO密集特性冲突
○ 架构冗余,纯Agent后端服务性能损耗大
○ 与主流Agent异步生态适配成本高
为什么几乎所有人都用FastAPI做Agent后端:
Agent后端是典型的IO密集型服务(大模型调用2~60秒、工具调用、外部API均为长IO),同步框架的阻塞模型会导致并发能力极低;FastAPI原生异步架构完美匹配该特性,同时强类型、自动文档、Agent生态适配三大优势大幅提升开发效率,因此成为行业默认选择。
1. 原生支持异步
- 这是FastAPI最大的优势。Agent后端需要大量调用LLM API和工具,这些都是IO密集型操作
- 异步框架可以用少量的线程处理大量的请求,大大提高系统的吞吐量和资源利用率
- Flask的异步支持需要第三方扩展,而且不够完善
- Django的异步支持有限,很多组件还是同步的
2. 性能最好
- FastAPI基于Starlette和Pydantic,性能非常好,是Python中性能最高的Web框架之一
- 在高并发场景下,FastAPI的性能是Flask的2-3倍,是Django的3-5倍
- Agent后端对响应时间和吞吐量的要求很高,FastAPI的性能优势非常明显
3. 自动生成API文档
- FastAPI可以自动生成OpenAPI规范的API文档,不需要手动编写
- 这对于Agent后端来说非常重要,因为Agent后端需要提供很多API接口给前端和其他服务调用
- 自动生成的文档可以大大提高开发效率,减少沟通成本
4. 类型提示和数据验证
- FastAPI基于Python的类型提示,可以自动进行数据验证和序列化
- 可以在编译时发现很多错误,提高代码的质量和可维护性
- Agent后端需要处理大量的请求和响应数据,类型提示和数据验证可以大大减少bug
5. 开发效率高
- FastAPI的语法非常简洁,学习曲线低
- 它提供了很多现成的功能,比如依赖注入、中间件、异常处理等
- 可以快速开发出高质量的API服务
6. 与AI生态完美集成
- 大多数AI库和框架都是用Python写的,FastAPI可以与它们无缝集成
- 比如LangChain、LangGraph、LlamaIndex、PyTorch、TensorFlow等
- 很多AI项目的官方示例都是用FastAPI做后端的
选型建议:
- 如果你要做Agent后端,优先选FastAPI,这是目前行业的标准选择
- 如果你要做一个简单的原型或者小型应用,Flask也可以考虑
- 如果你需要一个全栈Web应用,并且需要Admin后台,可以考虑Django
我们团队所有的Agent后端都是用FastAPI开发的。它的异步支持和性能确实非常出色,开发效率也很高。我们之前用Flask做过一个Agent后端,后来迁移到了FastAPI,系统的吞吐量提高了3倍,响应时间减少了一半。"
2. 为什么Agent后端必须用异步框架?FastAPI+AsyncIO有哪些常见的坑?
标准回答:
"Agent后端必须用异步框架,主要是由Agent的工作特点决定的:
为什么必须用异步框架:
1. Agent是IO密集型应用
- Agent的大部分时间都在等待IO操作完成,比如:
- 调用LLM API,通常需要几秒钟甚至几十秒
- 调用工具和第三方API
- 访问数据库和向量数据库
- 读取和写入文件
- 在同步框架中,每个请求都会占用一个线程,在等待IO时线程会被阻塞
- 当并发请求数增加时,线程数会急剧增加,导致系统资源耗尽,性能下降
- 异步框架可以在等待IO时切换到其他任务,用少量的线程处理大量的请求
2. 支持流式输出
- Agent的一个核心特性是流式输出,即模型生成一个token就返回一个token给用户
- 同步框架很难实现高效的流式输出
- 异步框架天生支持流式响应,可以很方便地实现流式输出
3. 支持长连接
- Agent的对话通常是长连接,用户会在一个会话中进行多轮对话
- 异步框架可以很好地处理长连接,而同步框架在长连接场景下性能很差
4. 支持并发任务处理
- Agent经常需要同时调用多个工具或者多个LLM API
- 异步框架可以很方便地实现并发任务处理,提高系统的效率
- 比如同时调用三个工具,总时间是最长的那个工具的执行时间,而不是三个工具执行时间的总和
FastAPI+AsyncIO的常见坑:
1. 不要在异步函数中调用同步代码
- 如果在异步函数中调用同步代码,会阻塞整个事件循环,导致所有请求都被阻塞
- 比如不要在异步函数中使用
time.sleep(),应该用asyncio.sleep() - 如果必须调用同步代码,应该用
asyncio.to_thread()将它放到线程池中执行
2. 注意数据库驱动的异步支持
- 不是所有的数据库驱动都支持异步
- 如果使用同步的数据库驱动,会阻塞事件循环
- 应该使用支持异步的数据库驱动,比如:
- PostgreSQL:asyncpg
- MySQL:aiomysql
- MongoDB:motor
- Redis:aioredis
3. 正确处理异常
- 异步代码中的异常如果没有被正确处理,会导致事件循环崩溃
- 应该在所有的异步函数中添加异常处理
- 使用FastAPI的异常处理器来统一处理异常
4. 控制并发数
- 异步框架虽然可以处理大量的并发请求,但如果并发数过高,还是会导致系统性能下降
- 应该设置合理的并发数限制,避免系统被压垮
- 可以使用信号量(Semaphore)来控制并发数
5. 注意内存泄漏
- 异步代码中如果有未正确释放的资源,会导致内存泄漏
- 比如未关闭的文件、数据库连接、网络连接等
- 应该使用上下文管理器(with语句)来管理资源,确保资源被正确释放
6. 避免过长的CPU密集型任务
- 异步框架不适合处理CPU密集型任务
- 如果有CPU密集型任务,应该将它放到进程池中执行
- 否则会阻塞事件循环,影响所有请求的响应时间
7. 正确使用依赖注入
- FastAPI的依赖注入非常强大,但如果使用不当,会导致性能问题
- 应该尽量使用单例依赖,避免每次请求都创建新的依赖实例
- 对于异步依赖,应该使用异步的依赖注入
我们团队在使用FastAPI+AsyncIO时,就踩过很多这样的坑。最开始我们在异步函数中调用了同步的数据库驱动,导致系统经常卡顿。后来我们换成了异步的数据库驱动,系统的性能有了很大提升。"
3. 消息队列怎么选?RabbitMQ、Kafka、Redis Pub/Sub在Agent场景下的适用场景?
标准回答:
"消息队列是Agent系统中非常重要的组件,主要用于异步任务处理、解耦服务、削峰填谷。RabbitMQ、Kafka、Redis Pub/Sub是最常用的三个消息队列,它们在Agent场景下的适用场景如下:
三个消息队列的对比:
| 特性 | RabbitMQ | Kafka | Redis Pub/Sub |
|---|---|---|---|
| 消息可靠性 | 高 | 高 | 低 |
| 吞吐量 | 中 | 极高 | 中 |
| 延迟 | 低 | 中 | 极低 |
| 消息持久化 | 支持 | 支持 | 不支持 |
| 复杂路由 | 支持 | 有限 | 不支持 |
| 运维复杂度 | 中 | 高 | 低 |
| 适合场景 | 复杂的任务队列、RPC | 日志收集、数据流、高吞吐量场景 | 简单的发布订阅、实时通知 |
在Agent场景下的适用场景:
1. Redis Pub/Sub
- 适用场景:
- 简单的实时通知:比如Agent的状态更新、工具执行进度通知
- 轻量级的发布订阅:比如前端和后端之间的实时通信
- 临时消息:不需要持久化的消息
- 优点:
- 延迟极低,性能好
- 部署和运维简单,大多数Agent系统已经在使用Redis做缓存
- 集成方便,有丰富的客户端库
- 缺点:
- 消息不持久化,服务重启后消息会丢失
- 不支持消息确认和重试
- 不支持复杂的路由
- 吞吐量有限
2. RabbitMQ
- 适用场景:
- 异步任务队列:比如长时间运行的工具调用、批量处理任务
- 需要可靠消息传递的场景:比如任务不能丢失
- 复杂的路由需求:比如根据消息的内容路由到不同的队列
- RPC调用:比如服务之间的远程过程调用
- 优点:
- 消息可靠性高,支持持久化、消息确认、重试
- 支持复杂的路由模式,比如直连、主题、扇出
- 支持死信队列,可以处理失败的消息
- 延迟低
- 缺点:
- 吞吐量不如Kafka
- 运维复杂度比Redis高
- 集群模式比较复杂
3. Kafka
- 适用场景:
- 日志收集与分析:全量采集Agent调用日志、工具执行日志、用户行为日志
- 高吞吐数据流:大模型流式数据中转、大规模Agent事件流处理
- 数据中台对接:Agent行为数据同步到数仓做离线分析
- 事件驱动架构的大规模Agent系统
- 优点 :
- 吞吐量极高,支持百万级QPS,适合海量日志和事件
- 消息持久化可靠,支持多副本,数据不丢失
- 支持消息回溯,可重放历史数据
- 分布式架构,水平扩展能力强
- 缺点 :
- 延迟相对较高,不适合低延迟实时通知
- 运维复杂度极高,需要专业团队维护
- 不支持复杂路由,消息按分区顺序消费
- 部署成本高,小型系统引入性价比低
4. 任务队列怎么选?Celery、RQ、Arq的区别?
| 特性 | Celery | RQ | Arq |
|---|---|---|---|
| 异步模型 | 同步多进程/多线程 | 同步多进程 | 原生AsyncIO协程 |
| 依赖中间件 | 支持RabbitMQ/Redis等多种Broker | 仅依赖Redis | 仅依赖Redis |
| 任务持久化 | 支持(依赖Broker) | 支持(Redis持久化) | 支持(Redis持久化) |
| 定时任务 | 原生支持(celery beat) | 需第三方扩展 | 原生支持 |
| 并发性能 | 中(进程级并发,开销大) | 低(进程级并发,开销大) | 高(协程级并发,IO场景优势大) |
| 重试机制 | 完善,支持死信队列、灵活重试策略 | 基础重试能力 | 完善,支持超时、重试、回调 |
| Agent生态适配 | 适配同步Agent逻辑,异步场景兼容差 | 仅适配同步轻量场景 | 原生适配FastAPI异步栈,与异步Agent框架无缝兼容 |
| 运维复杂度 | 高(组件多,配置复杂) | 低(极简,依赖少) | 中(依赖Redis,配置简单) |
Celery
- 适用场景:
- Django/Flask同步技术栈下的重型Agent任务队列
- 复杂任务调度、多Worker集群的企业级场景
- CPU密集型Agent离线任务(文档批量向量化、数据集处理)
- 优点:
- 生态极其成熟,功能完善,支持任务路由、优先级、工作流编排
- 支持多种Broker和结果后端,企业级特性齐全
- 社区活跃,问题解决方案丰富
- 缺点:
- 同步架构,IO密集型Agent任务(大模型调用)资源利用率低,并发能力有限
- 组件复杂,运维成本高,配置项繁多
- 与FastAPI异步技术栈兼容性差,同步转异步有性能损耗
RQ(Redis Queue)
- 适用场景:
- 轻量级同步Agent系统、小型内部工具的异步任务
- 团队不想引入复杂中间件的极简场景
- 并发量低、任务逻辑简单的Agent Demo
- 优点:
- 极简设计,上手成本极低,基于Redis实现
- 运维简单,可与Redis缓存复用
- 调试方便,任务状态清晰
- 缺点:
- 仅支持Redis作为Broker,功能单一,高级特性缺失
- 多进程并发模型,IO密集场景性能差
- 定时任务、重试等能力依赖第三方扩展,生态碎片化
- 不支持异步任务,与FastAPI异步栈适配差
Arq
- 适用场景:
- FastAPI+AsyncIO技术栈的Agent后端首选任务队列
- IO密集型Agent任务:大模型批量调用、工具异步执行、会话异步处理
- 轻量级但需要原生异步能力的Agent系统
- 优点:
- 原生基于AsyncIO协程,与FastAPI无缝兼容,IO场景并发性能远超Celery/RQ
- 仅依赖Redis,运维成本低,可与缓存、会话存储复用
- 原生支持定时任务、任务重试、超时控制、任务回调,满足Agent核心需求
- 代码简洁,协程级并发资源利用率高
- 缺点:
- 社区生态不如Celery成熟,企业级高级特性较少
- 仅支持Redis作为Broker,不支持RabbitMQ等其他中间件
- 国内资料较少,踩坑解决方案有限
Java栈(大厂必问)
5. Spring AI和LangChain4j怎么选?各自的优缺点是什么?
| 特性 | Spring AI | LangChain4j |
|---|---|---|
| Spring生态集成 | 原生深度集成,与Spring Boot/Cloud无缝融合 | 第三方集成,需手动适配Spring体系 |
| 编程模型 | Spring风格,自动配置、模板化、声明式调用 | 函数式/流式编程,与LangChain理念一致 |
| 功能完整性 | 基础能力完善,高级Agent能力较弱 | 功能全面,Agent编排、工具调用、记忆、RAG能力完善 |
| Agent编排能力 | 基础,无原生工作流编排,需自行实现 | 完善,支持多轮对话、工具调用,内置多种Agent模式,原生支持LangGraph |
| 上手难度 | Spring开发者上手极快,符合开发习惯 | 有LangChain经验者上手快,纯Java开发者需适应 |
| 企业级特性 | 完善,内置事务、安全、监控、配置管理 | 基础,企业级特性需自行扩展 |
| 版本成熟度 | Spring官方维护,迭代稳定 | 社区驱动,迭代快,API变动相对频繁 |
| 微服务适配 | 原生适配Spring Cloud微服务体系 | 需自行适配微服务架构 |
Spring AI
- 适用场景:
- 已有Spring Boot/Cloud技术体系的企业级团队
- 以业务系统为主,AI能力作为业务模块嵌入的场景
- 对稳定性、可运维性要求高的企业级Agent系统
- 简单RAG、单轮工具调用等轻量级Agent场景
- 优点:
- Spring官方出品,与Spring Boot 3.x深度集成,自动配置、依赖注入、事务等能力开箱即用
- 符合Java开发者编程习惯,学习成本低,团队上手快
- 企业级特性完善,天然支持微服务、监控、链路追踪、配置中心
- 版本迭代稳定,兼容性有保障,适合生产环境长期维护
- 缺点:
- Agent高级能力不足,无原生工作流编排、复杂状态管理,复杂Agent场景开发量极大
- 生态丰富度不如LangChain4j,第三方工具、模型适配数量相对少
- 灵活性不足,约定式开发在高度定制化Agent场景下扩展成本高
LangChain4j
- 适用场景:
- 以AI Agent为核心产品,需要复杂编排、多工具调用、工作流的场景
- 团队有LangChain经验,需将Python侧Agent能力迁移到Java栈
- 快速迭代的AI创业团队,需要丰富的Agent内置能力
- 优点:
- 功能全面,完整复刻LangChain核心能力,支持记忆、工具调用、RAG、结构化输出
- 原生支持LangGraph工作流编排,可实现复杂的状态化Agent流程
- 模型和工具生态丰富,适配绝大多数主流大模型、向量数据库、第三方工具
- 编程灵活,流式API适合复杂Agent逻辑编排
- 缺点:
- 与Spring生态集成度低,企业级特性需自行封装
- 社区驱动,API变动相对频繁,生产环境长期维护成本更高
- 无LangChain基础的Java开发者学习曲线较陡
- 微服务架构适配需自行改造,无原生化云原生支持
6. Spring Boot 3.x在AI开发中有哪些新特性?为什么推荐用Spring Boot 3.2+?
Spring Boot 3.x在AI开发中的核心新特性
● 虚拟线程正式支持
○ 基于JDK 21的Project Loom,原生支持虚拟线程,大幅提升IO密集型Agent服务的并发能力,解决Java平台线程在大模型长IO下的并发瓶颈
● Spring AI原生适配
○ 是Spring AI的基础运行环境,Spring AI的自动配置、Starter组件均基于Spring Boot 3.x设计,AI能力可无缝接入
● 响应式编程能力增强
○ 完善WebFlux响应式编程支持,适配大模型SSE流式输出、流式接口调用,完美匹配Agent对话场景
● 结构化并发优化
○ 支持Java 21结构化并发,多工具并发调用、多任务编排时代码更简洁,异常处理、资源管理更安全
● 可观测性原生支持
○ 内置Micrometer观测体系,原生支持链路追踪、指标监控、日志集成,Agent的大模型调用、工具执行全链路可观测
● GraalVM原生镜像支持
○ 支持AOT编译与原生镜像打包,Agent服务启动速度提升数倍,内存占用更低,适合Serverless、容器化部署
为什么推荐使用Spring Boot 3.2+
● 版本绑定要求:Spring AI正式版最低依赖Spring Boot 3.2,低版本无法兼容核心Starter,多数AI组件无法正常使用
● 虚拟线程成熟稳定:3.2对虚拟线程的支持经过生产级验证,配置极简,在Agent IO密集场景下收益极大,无早期版本的兼容性BUG
● 流式输出能力完善:3.2+对SSE、响应式流的处理更稳定,大模型流式输出的连接管理、异常处理、背压支持更完善
● 性能与稳定性优化:修复了大量异步、响应式场景的BUG,高并发长连接的Agent场景下稳定性更高
● 生态适配最优:主流向量数据库SDK、大模型Java SDK、LangChain4j均优先适配3.2+版本,兼容性问题最少
7. 如何在Spring AI中集成LangGraph?
核心集成思路
Spring AI负责模型调用、向量检索、工具Bean管理等基础能力,LangGraph负责Agent状态管理、工作流编排、节点调度,二者通过Spring依赖注入整合到容器中。
具体集成步骤
● 依赖引入
○ 项目基于Spring Boot 3.2+,同时引入Spring AI Starter与LangChain4j的LangGraph依赖,严格对齐版本兼容性
● 模型能力统一封装
○ 将Spring AI的ChatClient封装为适配LangChain4j的ChatLanguageModel接口,复用Spring AI的配置、连接池与重试机制
● Spring Bean化注册节点
○ 将意图识别、工具调用、答案生成、RAG检索等执行节点定义为Spring Bean,注入到LangGraph的图结构中
○ 工具类节点直接复用Spring容器中的业务Service、Spring AI向量检索组件
● 状态管理与Spring集成
○ 自定义AgentState状态类,存储会话上下文、用户信息、工具执行结果等数据
○ 可结合Spring会话作用域、Redis会话存储,实现状态持久化与分布式共享
● 图构建与生命周期管理
○ 在Spring配置类中构建LangGraph图实例,注册所有节点与边,定义条件分支逻辑,将图对象注册为Spring单例Bean
○ 有状态工作流通过原型作用域或工厂模式创建每次执行的实例
● 执行入口封装
○ 封装Agent执行Service,在Spring Service中调用LangGraph执行器,支持流式、异步执行,对接Spring事务、监控、权限体系
关键注意事项
● 统一由Spring容器管理模型客户端单例,避免重复初始化浪费连接池
● LangGraph异步执行需结合Spring异步机制或虚拟线程,避免阻塞服务线程
● LangGraph原生持久化与Spring生态适配度一般,状态持久化需自行适配Spring Data
● 版本兼容性是核心痛点,需严格对齐Spring AI、LangChain4j、LangGraph的版本
8. Java的虚拟线程在Agent后端开发中有什么用?
虚拟线程是JDK 21引入的轻量级线程,由JVM管理,上下文切换开销极低,创建成本几乎为零,单JVM可支撑百万级虚拟线程,完美解决Java平台线程在IO密集场景下的瓶颈。在Agent后端开发中的核心作用:
● 大幅提升IO密集场景并发能力
○ Agent核心操作(大模型调用、工具调用、向量检索、数据库查询)均为长耗时IO,传统平台线程并发量受限于线程池大小(通常数百级);虚拟线程可创建上万个,并发能力提升一个数量级
○ 无需改写异步代码,同步代码风格即可获得异步架构的并发性能,避免响应式编程的高复杂度
● 简化流式对话与长连接实现
○ Agent SSE流式对话、WebSocket长连接需要维持长时间连接,传统平台线程会因连接持有线程导致并发上限低;虚拟线程可轻松支撑大量长连接,编程模型简单
● 简化多工具并发编排
○ Agent执行常需并行调用多个工具、多路检索,传统方案需用CompletableFuture异步编排,代码复杂易出错;基于虚拟线程可直接为每个任务创建虚拟线程,同步代码实现并发,异常处理、资源管理更简单
○ 配合结构化并发,可实现安全的并发任务管理,子任务失败时自动取消所有相关任务
● 降低开发与维护成本
○ 无需学习复杂的响应式编程,保持Java同步编程习惯即可获得高并发能力
○ 现有同步业务代码、Spring生态同步组件几乎无需改造即可享受性能提升,旧系统改造为Agent系统成本极低
● 优化异步任务资源利用率
○ 传统@Async任务基于平台线程池,任务量大时易耗尽线程池;基于虚拟线程的任务执行器可处理海量Agent异步任务(批量向量化、批量大模型调用),资源利用率极高
通用问题
9. 关系型数据库怎么选?MySQL、PostgreSQL在Agent系统中的适用场景?
| 特性 | MySQL | PostgreSQL |
|---|---|---|
| JSON数据支持 | 基础JSON类型,查询能力一般 | 原生JSONB类型,索引强大,支持复杂JSON查询 |
| 向量检索能力 | 需第三方插件,能力弱 | 原生支持pgvector,向量检索成熟,轻量RAG主流选择 |
| 复杂查询支持 | 一般,适合简单业务查询 | 强大,支持复杂SQL、窗口函数、CTE、自定义函数 |
| 生态成熟度 | 极高,国内社区资料丰富,运维体系成熟 | 高,国外更流行,国内运维资料相对少 |
| 运维复杂度 | 低,运维工具丰富,DBA人才多 | 中,高级特性运维门槛更高 |
| 事务与并发 | 优秀,MVCC成熟,高并发OLTP稳定 | 优秀,支持更高级别事务隔离 |
| 分库分表生态 | 成熟,ShardingSphere等方案完善 | 相对薄弱,分库分表方案少 |
MySQL
- 适用场景:
- 以业务数据为主的Agent系统:存储用户信息、会话记录、权限、配额等核心业务数据
- 高并发、高事务要求的OLTP型Agent平台,如面向C端的大规模用户Agent产品
- 团队已有成熟MySQL技术栈、运维体系的企业级项目
- 需要分库分表、水平扩展的超大规模Agent系统
- 优点:
- 国内生态极其成熟,运维、排错、优化方案丰富,人才储备充足
- 高并发OLTP场景性能稳定,事务能力成熟
- 分库分表、读写分离方案完善,适合大规模用户量系统
- 绝大多数后端团队的默认选择,业务开发效率高
- 缺点:
- 向量检索能力弱,无法直接支撑RAG场景,需额外引入向量数据库
- JSON数据处理能力有限,Agent结构化数据存储的查询效率低
- 复杂查询、数据分析能力弱
PostgreSQL
- 适用场景:
- 轻量级RAG场景:通过pgvector实现向量检索,无需单独部署向量数据库,降低系统复杂度
- 数据结构复杂的Agent系统:存储大量JSON格式的对话历史、工具调用记录、Agent状态
- 中型规模、查询逻辑复杂的Agent系统,需要统计分析、多表关联
- 团队有PG技术栈,偏向数据密集型的Agent产品
- 优点:
- pgvector插件成熟,轻量场景可替代专业向量数据库,减少技术栈复杂度
- JSONB性能优异,支持丰富的JSON查询与索引,完美适配Agent半结构化数据
- 数据类型丰富,查询能力强大,支持复杂数据分析处理
- 开源协议友好,可扩展性极强
- 缺点:
- 国内运维生态不如MySQL成熟,DBA人才相对少,排错成本更高
- 高并发写入场景优化难度高于MySQL
- 分库分表方案不成熟,超大规模水平扩展成本高
- 部分国内云厂商的PG配套服务不如MySQL完善
10. 缓存怎么选?Redis、Memcached在Agent系统中分别用来缓存什么?
| 特性 | Redis | Memcached |
|---|---|---|
| 数据结构 | 丰富(String、Hash、List、Set、ZSet、Stream等) | 仅简单Key-Value |
| 持久化 | 支持RDB/AOF | 不支持,纯内存 |
| 分布式能力 | 支持集群、哨兵、主从 | 客户端侧分布式,无原生集群 |
| 功能丰富度 | 极高,支持发布订阅、事务、Lua、丰富过期策略 | 极简,仅KV缓存 |
| 性能 | 单线程高并发读写性能优异 | 多线程,小数据量场景性能略高 |
| 内存利用率 | 中,数据结构有额外开销 | 高,纯KV结构内存利用率高 |
Redis
- Agent系统核心缓存场景:
- 会话状态缓存:缓存用户对话上下文、Agent状态、历史消息,是多轮对话的核心存储,配合过期策略自动清理不活跃会话
- 大模型调用结果缓存:相同问题的大模型回答、Embedding向量结果缓存,降低调用成本,提升响应速度
- 工具调用结果缓存:重复工具查询结果(天气、知识库检索)缓存,减少重复调用
- 限流与熔断:基于Redis实现接口限流、大模型API调用限流,防止打爆第三方接口
- 分布式锁:Agent任务执行、会话修改的分布式锁,防止并发冲突
- 热点知识库数据缓存:缓存高频访问的知识库片段、向量检索结果
- 优点:
- 数据结构丰富,可满足Agent多种缓存与非缓存需求,一专多能
- 支持持久化,缓存数据不易丢失,会话状态更可靠
- 生态成熟,与所有主流后端框架、Agent框架无缝兼容
- 一套Redis可同时覆盖缓存、锁、消息队列、限流等多种需求
- 缺点:
- 纯缓存场景下内存利用率不如Memcached
- 单线程架构下,大value场景性能会下降
Memcached
- Agent系统适用缓存场景:
- 纯热点大KV缓存:如高频Embedding向量、静态知识库片段、通用回答等无复杂结构的热点数据
- 高并发低延迟的简单KV读取,如用户令牌、基础配置信息
- 分布式纯缓存层,无额外功能需求,追求极致内存利用率
- 优点:
- 极致简单的KV模型,内存利用率高,相同内存可缓存更多数据
- 多线程架构,多核服务器下小数据量读写性能优异
- 运维极简,稳定性高
- 缺点:
- 功能单一,仅支持简单KV,无法满足会话、状态、分布式锁等复杂需求
- 不支持持久化,服务重启缓存全部丢失,不能存储重要会话状态
- 无原生集群,分布式需客户端实现,运维扩展成本高
选型结论:Agent系统中Redis是绝对主流首选,一套Redis即可覆盖绝大多数场景;Memcached仅在纯热点KV缓存、极致性能要求的补充场景下使用,一般不作为主力缓存。
11. 如何设计Agent系统的数据库Schema?需要存储哪些核心数据?
7类核心存储数据
- 用户与权限数据
- 存储内容:用户基础信息、角色权限、API密钥、调用配额
- 核心表:用户表、角色表、权限表、用户配额表
- 设计要点:支持多租户隔离,配额字段支持实时更新与校验
- 会话(Session)数据
- 存储内容:Agent对话的会话维度元数据,一个会话对应多轮对话
- 核心字段:会话ID、用户ID、AgentID、会话标题、创建时间、最后活跃时间、状态、上下文摘要
- 设计要点:支持按用户ID查询会话列表,按最后活跃时间做自动归档清理
- 消息(Message)数据
- 存储内容:每一轮对话的具体消息,包含用户提问、助手回答、工具调用消息
- 核心字段:消息ID、会话ID、角色(user/assistant/tool)、消息内容、工具调用ID、工具名称、执行结果、时间戳、Token消耗
- 设计要点:消息内容用JSON类型存储,支持按会话ID倒序查询,用于上下文组装
- Agent配置数据
- 存储内容:Agent基础配置、系统提示词、工具绑定、模型参数、知识库绑定
- 核心字段:AgentID、Agent名称、系统提示词、模型名称、温度参数、绑定工具列表、知识库ID列表、版本号
- 设计要点:支持多版本管理,便于迭代回滚;支持按租户/用户隔离
- 工具调用与执行记录
- 存储内容:Agent调用工具的详细日志,用于审计、排错、统计
- 核心字段:调用ID、会话ID、消息ID、工具名称、入参、出参、执行状态、开始时间、结束时间、错误信息、耗时
- 设计要点:入参出参为JSON格式,支持按时间范围统计;数据量大,需考虑分表或归档
- Token与费用统计数据
- 存储内容:大模型调用的Token消耗、费用明细
- 核心字段:记录ID、用户ID、会话ID、模型名称、输入Token数、输出Token数、费用、调用时间
- 设计要点:支持按用户、按时间维度统计,用于账单生成与配额管控
- 知识库与文档元数据
- 存储内容:RAG场景下知识库、文档的元数据(向量数据存在向量数据库)
- 核心字段:知识库ID、文档ID、文档名称、文档类型、分片数量、上传时间、状态、所属用户/Agent
- 设计要点:与向量数据库的文档ID一一对应,支持权限管控
Schema设计核心原则
- 半结构化数据适配:消息内容、工具入参出参等使用JSON类型(PG用JSONB,MySQL用JSON),适配Agent结构化输出特性
- 索引设计:用户ID、会话ID、时间范围等核心查询维度必须建索引;JSON高频查询字段建函数索引
- 生命周期管理:消息、工具记录、调用日志等增量大的数据,设计冷热分离策略,历史数据归档到冷存储
- 租户隔离:多租户场景下所有业务表增加租户ID,实现数据隔离
- 扩展性:预留JSON扩展字段,减少Agent迭代带来的表结构变更
- 读写分离:高频查询数据配合Redis缓存降载,统计分析类查询走从库
六、前端选型
基础必问题
1. SSE和WebSocket在AI流式输出场景下怎么选?为什么90%的AI应用都用SSE?
标准回答:
选型核心结论:纯AI流式输出场景优先选SSE,只有强双向实时交互的AI场景才选WebSocket。
一、选型判断的核心依据(结合AI场景)
- 通信模式匹配度
AI对话的本质是「客户端发一次请求 → 服务端逐字流式返回结果」的单向数据流,和SSE"单请求、服务端单向推送"的模型完全匹配;WebSocket是全双工双向通信,对于普通问答场景属于能力过剩。 - 首字延迟与协议开销
SSE直接基于HTTP建立连接,没有协议升级握手;WebSocket需要先完成HTTP握手再升级协议,多1次RTT开销。在AI场景最核心的「首字响应延迟」指标上,SSE反而更优。 - 基础设施兼容性
SSE是浏览器原生标准,EventSource自带自动重连、断线续传(Last-Event-ID),无需第三方依赖;且完全复用HTTP基础设施,走80/443端口,无缝穿过所有反向代理、CDN、WAF、防火墙,不需要额外配置端口、协议转发,运维成本极低。 - 服务端资源成本
SSE是轻量长连接,服务端只需维护响应流,并发承载能力远高于WebSocket,高流量下稳定性更好。
WebSocket仅在一类AI场景不可替代:需要持续双向实时交互的场景,比如实时语音对话、Agent工具调用状态同步、流式输出过程中客户端随时中断/修正生成指令、多人协作式AI编辑等。
二、为什么90%的AI应用都用SSE?
核心原因是「投入产出比最高,完全匹配绝大多数AI产品的需求」,具体5点:
- 业务天然匹配:90%的AI应用都是问答式生成,单向流式输出完全够用,不需要双向通信能力;
- 开发成本极低:前端十几行代码就能实现流式渲染,后端只需按HTTP流返回,不需要维护WebSocket连接池、心跳、断连重连逻辑;
- 运维零额外成本:复用现有HTTP的所有监控、负载均衡、安全防护体系,不需要单独适配协议;
- 兼容性拉满:兼容所有现代浏览器,低版本可通过polyfill支持,不会出现企业内网/代理环境下连不上的问题;
- 全链路生态成熟:从网关(Nginx、Cloudflare)到后端框架(FastAPI、Spring、Express)再到前端AI SDK(Vercel AI),全链路原生支持SSE流式输出。
2. React和Vue在AI前端开发中怎么选?各自的生态有哪些AI相关的库?
标准回答:
选型核心结论:没有绝对优劣,核心看团队技术栈、项目规模和业务场景;React在AI生态创新上领先,Vue在开发效率上有优势。
一、选型判断
-
优先选React的场景
中大型AI平台、复杂Agent系统、多模态交互产品;需要深度定制流式渲染、结合RSC/Server Actions等新特性;团队有React技术栈积累,追求生态前沿性。
原因:React的函数式编程和Hooks模型更适合处理流式数据的状态更新,社区创新速度更快,绝大多数AI前端方案都是React先落地。
-
优先选Vue的场景
中小规模AI工具、快速原型验证、C端轻量化AI应用;团队原有技术栈是Vue,需要快速落地AI功能。
原因:Vue3组合式API完全能覆盖AI前端需求,模板语法开发效率更高,上手成本低,团队磨合快。
二、各自的AI相关生态库
React生态(行业主流):
- 核心流式开发:
@vercel/ai(Vercel官方AI SDK,事实标准,封装useChat/useCompletion等钩子,全链路支持SSE流式,兼容几乎所有大模型)、langchain-js(前端侧调用LangChain能力) - AI UI组件:
@shadcn/ui(社区最火的AI组件方案)、chatbot-ui、llm-ui(专门的流式对话组件库) - 端侧AI:
@xenova/transformers(浏览器端运行轻量化大模型) - 垂直场景:
@dify/web-client(对接Dify应用层)、react-media-recorder(语音交互)
Vue生态:
- 核心流式开发:
@vueuse/ai(VueUse官方AI模块,对标Vercel AI SDK)、nuxt-ai(Nuxt框架的AI集成方案) - AI UI组件:
ant-design-vue官方AI组件、vue-chatbot - 端侧AI:同样兼容
@xenova/transformers - 整体差距:Vue的AI生态比React晚3~6个月,创新方案少,但主流能力都有覆盖,满足业务需求没有技术瓶颈。
3. 你用过哪些AI UI组件库?shadcn/ui、Ant Design、MUI怎么选?
标准回答:
实际项目中落地过的AI相关组件库:shadcn/ui、Ant Design AI组件、MUI、Arco Design AI组件,还有专门的对话组件库chatbot-ui。
三者选型核心看定制化需求、业务场景、团队技术栈三个维度,具体分析:
1. shadcn/ui
- 核心特点:不是传统打包组件库,是源码级复制到项目中,基于Tailwind CSS,完全可定制,零样式侵入。
- AI场景优势:原生适配AI场景的组件最丰富,比如流式消息气泡、打字指示器、思考中动画、代码高亮块、工具调用卡片、文件上传预览等,社区有大量AI模板和扩展(shadcn-chat),做对话界面效率极高。
- 适用场景:C端AI产品、创新型Agent应用、追求差异化设计的项目、创业公司快速迭代的产品。
- 不足:复杂基础组件(大数据表格、树形选择)不如成熟组件库完善,没有官方版本管理,升级需要手动同步。
2. Ant Design(AntD)
- 核心特点:国内最主流的企业级组件库,组件最全,中文文档完善,B端生态成熟。
- AI场景优势:官方推出了专门的Ant Design AI组件集,包括对话气泡、智能搜索框、生成式表单、Agent状态面板等,和现有B端系统集成成本极低,不需要重新适配设计规范。
- 适用场景:企业级B端AI产品、中后台AI管理系统、SaaS产品嵌入AI能力、团队原有技术栈是AntD的项目。
- 不足:样式定制成本高,默认设计偏厚重,C端使用灵活性不够。
3. MUI(Material UI)
- 核心特点:遵循Material Design规范,React生态最成熟的组件库之一,国际化和多端适配好。
- AI场景优势:组件体系完整,社区有大量AI对话界面模板,海外生态好,适合做全球化产品。
- 适用场景:面向海外市场的AI产品、遵循Material Design的项目、团队熟悉MUI技术栈。
- 不足:AI原生组件比shadcn少,体积偏大,定制化灵活度中等。
选型总结
- 做C端/创新型AI对话产品:首选shadcn/ui
- 做B端/企业级AI嵌入功能:首选Ant Design
- 做海外市场/Material风格产品:选MUI
4. 状态管理怎么选?Zustand、Redux、Pinia在AI前端中的适用场景?
标准回答:
AI前端的状态有非常鲜明的特点:流式数据高频增量更新、对话上下文集中管理、Agent多步骤状态流转、局部UI状态多、全局状态相对聚焦,选型核心看「状态复杂度、项目规模、技术栈」。
三个方案的适用场景
1. Zustand
- 核心定位:轻量级React状态管理,无Provider包裹,API极简,体积极小(~1KB)。
- AI场景优势:完美匹配AI前端的状态需求,管理对话列表、流式消息、AI配置参数等核心状态非常顺手;高频增量更新性能好,流式输出频繁修改状态不会卡顿;没有样板代码,开发效率高。
- 适用场景:绝大多数React技术栈的AI应用,从小工具到中大型平台都适用,是目前AI前端的主流选型。
- 最佳实践:搭配Immer中间件处理复杂的消息数组更新,搭配persist中间件实现对话本地持久化。
2. Redux(Redux Toolkit)
- 核心定位:强规范、可预测的企业级状态管理,devtools强大,支持时间旅行调试。
- AI场景优势:适合状态流转极其复杂的场景,比如多Agent协同系统、AI工作流编辑器、多步骤生成任务调度,状态可追溯,调试能力强;大型团队开发规范统一,避免代码混乱。
- 适用场景:超大型AI平台、复杂Agent工作流系统、需要严格状态管控的企业级AI项目。
- 注意:普通对话类AI用Redux属于过度设计,样板代码多,开发效率低,不推荐。
3. Pinia
- 核心定位:Vue官方推荐的状态管理,替代Vuex,轻量、TS友好、模块化设计。
- AI场景优势:Vue技术栈下的最优解,和Vue3组合式API完美契合,管理对话状态、流式数据更新体验好,学习成本低,devtools支持完善。
- 适用场景:所有Vue技术栈的AI前端项目,无论项目大小都适用。
选型总结
- React栈普通AI应用:首选Zustand,轻量高效,匹配AI场景特性
- React栈超大型复杂AI平台/工作流:选Redux Toolkit,强管控可调试
- Vue栈全场景AI项目:统一选Pinia,官方标准,生态适配
进阶场景题
5. 如何实现支持Markdown、代码高亮、数学公式的流式输出?
标准回答:
"实现支持Markdown、代码高亮、数学公式的流式输出是AI前端最核心的功能之一。我会采用以下技术方案:
整体技术栈选择:
- Markdown解析 :
remark+rehype生态 - 代码高亮 :
shiki(比highlight.js和prism.js效果更好,支持更多语言和主题) - 数学公式 :
remark-math+rehype-katex - 流式处理:自定义流式解析器,逐token处理
具体实现步骤:
1. 基础流式输出框架
首先使用Vercel的ai SDK来处理SSE连接和流式数据接收,它封装了大部分底层逻辑:
javascript
import { useChat } from 'ai/react';
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat();
return (
<div>
{messages.map(m => (
<div key={m.id} className={m.role}>
<MarkdownRenderer content={m.content} />
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
<button type="submit">发送</button>
</form>
</div>
);
}
2. 增量Markdown渲染组件
这是最关键的部分。不能等到所有内容都接收完再渲染,而是要逐token增量渲染:
javascript
import { useEffect, useRef, useState } from 'react';
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import rehypeShiki from '@shikijs/rehype';
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
export default function MarkdownRenderer({ content }) {
const [html, setHtml] = useState('');
const processorRef = useRef(null);
useEffect(() => {
// 初始化统一的处理器
processorRef.current = unified()
.use(remarkParse)
.use(remarkMath)
.use(remarkRehype)
.use(rehypeShiki, { theme: 'github-dark' })
.use(rehypeKatex)
.use(rehypeStringify);
}, []);
useEffect(() => {
if (!content || !processorRef.current) return;
// 增量渲染:每次内容更新时重新解析
processorRef.current.process(content)
.then(file => {
setHtml(String(file.value));
})
.catch(err => {
console.error('Markdown解析错误:', err);
setHtml(content); // 解析失败时降级为纯文本
});
}, [content]);
return <div className="markdown-body" dangerouslySetInnerHTML={{ __html: html }} />;
}
3. 优化流式渲染性能
上面的基础实现有一个问题:每次收到新token都会重新解析整个内容,当内容很长时会导致性能下降。我会做以下优化:
- 分块渲染:将内容分成多个块,只重新解析变化的块
- 防抖处理:设置一个短的防抖时间(比如50ms),避免频繁解析
- 虚拟DOM优化 :使用React的
memo和useMemo避免不必要的重渲染 - 代码块单独处理:当检测到代码块开始时,先显示一个占位符,等代码块完整接收后再高亮
4. 处理特殊情况
- 不完整的Markdown语法 :流式输出过程中,Markdown语法可能不完整(比如只有一个
**而没有闭合)。需要使用容错的解析器,或者在渲染前补全不完整的语法 - 数学公式:数学公式通常需要完整的表达式才能正确渲染。可以在检测到数学公式开始时,等完整的公式接收后再渲染
- 表格:表格需要完整的结构才能正确渲染。可以先显示纯文本,等表格完整接收后再渲染成HTML表格
5. 样式优化
- 使用
github-markdown-css作为基础样式 - 自定义代码块的样式,添加复制按钮、语言标签等
- 优化数学公式的显示效果
- 确保在深色和浅色模式下都有良好的显示效果
我们团队的实践经验:
我们最开始使用的是react-markdown,但它在流式输出时性能不好,尤其是当内容很长时。后来我们切换到了remark + rehype的自定义实现,并做了上面提到的性能优化。现在即使是几千字的长文本,流式渲染也非常流畅。
踩过的坑:
- 不要使用
dangerouslySetInnerHTML直接渲染未经过滤的用户输入,存在XSS风险。需要使用rehype-sanitize来过滤HTML - 代码高亮会消耗大量的CPU资源,尤其是在流式输出时。可以考虑将代码高亮移到Web Worker中执行
- 数学公式的渲染比较慢,不要在每个token更新时都重新渲染数学公式
这个方案可以很好地实现支持Markdown、代码高亮、数学公式的流式输出,并且性能良好,用户体验优秀。"
6. 长对话的性能优化怎么做?虚拟滚动、懒加载、分片渲染怎么结合?
标准回答:
"长对话是AI应用中非常常见的场景,当对话历史达到几百条甚至上千条时,如果不做性能优化,会导致页面卡顿、滚动不流畅、内存占用过高等问题。我会采用虚拟滚动+懒加载+分片渲染的组合方案来优化长对话的性能。
长对话的性能瓶颈:
- DOM节点过多:每条消息都是一个DOM节点,当消息数量很多时,DOM节点数量会急剧增加,导致浏览器重排重绘耗时
- 内存占用过高:所有的消息数据都保存在内存中,当消息数量很多时,内存占用会很高
- 渲染耗时:每次新消息到来时,都需要重新渲染整个消息列表,导致卡顿
优化方案:
1. 虚拟滚动(核心优化)
虚拟滚动是长列表性能优化最有效的手段。它只渲染当前可视区域内的DOM节点,而不是渲染所有的节点。
- 技术选型 :我会选择
react-virtualized或react-window。react-window是react-virtualized的轻量级版本,API更简洁,性能更好,适合大多数场景。 - 实现要点 :
- 给每条消息设置固定的高度,或者动态计算消息的高度
- 只渲染可视区域内的消息,通常是可视区域高度的2-3倍
- 当用户滚动时,动态卸载离开可视区域的消息,加载进入可视区域的消息
javascript
import { FixedSizeList as List } from 'react-window';
function MessageList({ messages }) {
const Row = ({ index, style }) => (
<div style={style}>
<Message message={messages[index]} />
</div>
);
return (
<List
height={600}
itemCount={messages.length}
itemSize={100} // 每条消息的高度
width="100%"
>
{Row}
</List>
);
}
2. 懒加载历史消息
不要一次性加载所有的历史消息,而是当用户滚动到顶部时,再加载更早的消息。
- 实现要点 :
- 初始只加载最新的20-50条消息
- 监听滚动事件,当用户滚动到距离顶部一定距离时,触发加载更多
- 显示加载指示器,告诉用户正在加载历史消息
- 加载完成后,保持当前的滚动位置不变
3. 分片渲染
当一次性加载大量消息时(比如刚进入页面加载50条消息),不要一次性渲染所有消息,而是分批次渲染。
- 实现要点 :
- 使用
requestIdleCallback或setTimeout来分批次渲染消息 - 每次渲染5-10条消息,避免阻塞主线程
- 显示骨架屏,提升用户体验
- 使用
4. 其他优化措施
- 消息缓存:将已经渲染过的消息组件缓存起来,避免重复渲染
- 图片懒加载:消息中的图片使用懒加载,只有当图片进入可视区域时才加载
- 组件 memo 化 :使用
React.memo和useMemo来避免不必要的组件重渲染 - 状态优化:只在状态中保存必要的消息数据,避免保存冗余信息
- 垃圾回收:当消息数量过多时,自动清理最早的消息数据,或者将它们持久化到IndexedDB中
三种技术的结合方式:
- 初始加载:页面加载时,从服务器获取最新的20条消息
- 分片渲染 :将这20条消息分4批,每批5条,使用
requestIdleCallback渲染 - 虚拟滚动:使用虚拟滚动只渲染可视区域内的消息
- 懒加载历史:当用户滚动到顶部时,加载更早的20条消息,然后重复步骤2和3
我们团队的实践经验:
我们的聊天应用在没有做优化之前,当对话历史超过100条时,就会出现明显的卡顿。采用了虚拟滚动+懒加载+分片渲染的优化方案后,即使对话历史超过1000条,页面也非常流畅,滚动丝滑,内存占用也控制在合理范围内。
踩过的坑:
- 动态高度的虚拟滚动实现起来比较复杂,容易出现滚动跳动的问题。如果可能的话,尽量给消息设置一个最小高度,或者使用成熟的动态高度虚拟滚动库
- 懒加载历史消息时,一定要保持当前的滚动位置不变,否则用户会突然跳转到页面顶部,体验非常差
- 不要过度优化,在消息数量较少时,虚拟滚动反而会增加复杂度。可以设置一个阈值,当消息数量超过50条时再启用虚拟滚动
这个优化方案可以很好地解决长对话的性能问题,提供流畅的用户体验。"
7. 如何实现Agent的"思考中"、"调用工具中"、"执行中"等状态的实时展示?
标准回答:
"Agent的状态实时展示是提升用户体验的关键。用户需要知道Agent正在做什么,而不是面对一个空白的屏幕等待。我会采用以下方案来实现Agent状态的实时展示:
状态设计
首先定义Agent的所有可能状态:
- 思考中:Agent正在分析用户的问题,制定执行计划
- 调用工具中:Agent正在调用某个工具,比如搜索引擎、数据库、API等
- 执行中:Agent正在执行某个任务,比如生成报告、分析数据等
- 回答中:Agent正在生成最终的回答
- 完成:Agent已经完成了任务
- 失败:Agent执行任务失败
技术实现方案
1. 后端支持
后端需要通过SSE实时推送Agent的状态变化。每个状态事件应该包含以下信息:
- 状态类型:thinking、tool_calling、executing、answering、done、error
- 状态详情:比如调用的工具名称、工具参数、执行进度、错误信息等
- 时间戳
SSE事件格式示例:
event: status
data: {"type": "tool_calling", "tool": "search", "query": "2026年AI发展趋势", "timestamp": 1717234567890}
event: status
data: {"type": "thinking", "timestamp": 1717234568901}
event: token
data: "根据"
event: token
data: "搜索"
2. 前端状态管理
在前端使用状态管理库(如Zustand)来管理Agent的状态:
javascript
import { create } from 'zustand';
const useAgentStore = create((set) => ({
status: 'idle', // idle, thinking, tool_calling, executing, answering, done, error
currentTool: null,
executionProgress: 0,
errorMessage: null,
setStatus: (status) => set({ status }),
setCurrentTool: (tool) => set({ currentTool: tool }),
setExecutionProgress: (progress) => set({ executionProgress: progress }),
setErrorMessage: (message) => set({ errorMessage: message }),
reset: () => set({
status: 'idle',
currentTool: null,
executionProgress: 0,
errorMessage: null
}),
}));
3. 状态UI组件
为每个状态设计对应的UI组件:
javascript
export default function AgentStatus() {
const { status, currentTool, executionProgress, errorMessage } = useAgentStore();
if (status === 'idle' || status === 'done') {
return null;
}
return (
<div className="agent-status">
{status === 'thinking' && (
<div className="status-item">
<span className="spinner"></span>
<span>思考中...</span>
</div>
)}
{status === 'tool_calling' && currentTool && (
<div className="status-item">
<span className="spinner"></span>
<span>正在调用 {currentTool.name}...</span>
{currentTool.query && (
<span className="tool-query">查询: {currentTool.query}</span>
)}
</div>
)}
{status === 'executing' && (
<div className="status-item">
<span className="spinner"></span>
<span>执行中... {executionProgress}%</span>
<div className="progress-bar">
<div className="progress" style={{ width: `${executionProgress}%` }}></div>
</div>
</div>
)}
{status === 'answering' && (
<div className="status-item">
<span className="spinner"></span>
<span>正在生成回答...</span>
</div>
)}
{status === 'error' && errorMessage && (
<div className="status-item error">
<span className="error-icon"></span>
<span>出错了: {errorMessage}</span>
</div>
)}
</div>
);
}
4. 集成到聊天界面
将状态组件集成到聊天界面中,显示在Agent消息的位置:
javascript
export default function ChatMessage({ message }) {
const { status } = useAgentStore();
const isAgentMessage = message.role === 'assistant';
const isCurrentMessage = message.id === 'current';
return (
<div className={`message ${message.role}`}>
<div className="message-avatar"></div>
<div className="message-content">
{isAgentMessage && isCurrentMessage && <AgentStatus />}
<MarkdownRenderer content={message.content} />
</div>
</div>
);
}
5. 高级优化
- 状态历史记录:记录Agent的所有状态变化,在回答完成后显示一个"查看详细过程"的按钮,让用户可以看到Agent的完整思考和执行过程
- 动画效果:为不同的状态添加不同的动画效果,提升用户体验
- 可取消操作:在Agent执行任务的过程中,提供一个取消按钮,让用户可以随时取消任务
- 错误处理:当Agent执行失败时,显示友好的错误信息,并提供重试按钮
我们团队的实践经验:
我们最开始只显示一个简单的"正在思考"的提示,用户体验很差,很多用户不知道Agent在做什么,经常会重复发送消息。后来我们实现了上面的状态实时展示方案,用户满意度提升了很多,重复发送消息的比例下降了60%。
踩过的坑:
- 不要显示太多的技术细节给普通用户,比如工具的具体参数和返回结果。可以提供一个"高级模式",让有需要的用户查看详细信息
- 状态切换要流畅,不要出现闪烁或者跳动的情况
- 当Agent开始生成回答时,要及时隐藏状态组件,避免和流式输出的内容重叠
这个方案可以很好地实现Agent状态的实时展示,让用户清楚地知道Agent正在做什么,大大提升用户体验。"
8. 前端如何处理Function Call的流式输出?(MiniMax真题)
标准回答:
"Function Call的流式输出是2026年AI前端的一个重要考点。传统的Function Call是等模型输出完整的JSON后再调用工具,但现在很多模型支持流式输出Function Call,即边生成JSON边返回。前端需要能够正确处理这种流式的Function Call。
为什么需要流式Function Call:
- 提升用户体验:用户可以看到模型正在调用哪个工具,而不是等待很长时间
- 提高响应速度:可以在模型生成完工具调用参数后立即调用工具,不需要等整个回答生成完
- 支持复杂的多轮工具调用:模型可以边思考边调用工具,根据工具的返回结果继续生成
技术实现方案
1. 理解流式Function Call的格式
不同模型的流式Function Call格式略有不同,但基本原理是一样的。模型会分块输出JSON,每个块可能包含工具调用的一部分。
以OpenAI的格式为例:
// 第一个块
{
"id": "chatcmpl-123",
"object": "chat.completion.chunk",
"choices": [{
"delta": {
"role": "assistant",
"tool_calls": [{
"index": 0,
"id": "call_123",
"type": "function",
"function": {
"name": "search",
"arguments": ""
}
}]
}
}]
}
// 第二个块
{
"choices": [{
"delta": {
"tool_calls": [{
"index": 0,
"function": {
"arguments": "{\"query\":\"2026年AI"
}
}]
}
}]
}
// 第三个块
{
"choices": [{
"delta": {
"tool_calls": [{
"index": 0,
"function": {
"arguments": "发展趋势\"}"
}
}]
}
}]
}
可以看到,工具调用的arguments字段是分块输出的,需要将所有块拼接起来才能得到完整的JSON。
2. 前端解析器实现
我会实现一个专门的流式Function Call解析器,负责收集和解析分块的工具调用:
javascript
class StreamFunctionCallParser {
constructor() {
this.toolCalls = new Map(); // key: index, value: toolCall object
}
// 处理单个chunk
parseChunk(chunk) {
const choices = chunk.choices || [];
for (const choice of choices) {
const delta = choice.delta || {};
if (delta.tool_calls) {
for (const toolCallDelta of delta.tool_calls) {
const index = toolCallDelta.index;
if (!this.toolCalls.has(index)) {
this.toolCalls.set(index, {
id: toolCallDelta.id,
type: toolCallDelta.type,
function: {
name: '',
arguments: ''
}
});
}
const toolCall = this.toolCalls.get(index);
if (toolCallDelta.id) {
toolCall.id = toolCallDelta.id;
}
if (toolCallDelta.type) {
toolCall.type = toolCallDelta.type;
}
if (toolCallDelta.function) {
if (toolCallDelta.function.name) {
toolCall.function.name = toolCallDelta.function.name;
}
if (toolCallDelta.function.arguments) {
toolCall.function.arguments += toolCallDelta.function.arguments;
}
}
}
}
}
return this.getCompleteToolCalls();
}
// 获取已经完整的工具调用
getCompleteToolCalls() {
const completeToolCalls = [];
for (const [index, toolCall] of this.toolCalls) {
if (toolCall.function.name && toolCall.function.arguments) {
try {
// 尝试解析arguments,如果解析成功说明已经完整
const args = JSON.parse(toolCall.function.arguments);
completeToolCalls.push({
...toolCall,
function: {
...toolCall.function,
arguments: args
}
});
// 从map中移除,避免重复处理
this.toolCalls.delete(index);
} catch (e) {
// JSON解析失败,说明还不完整,继续等待
}
}
}
return completeToolCalls;
}
// 重置解析器
reset() {
this.toolCalls.clear();
}
}
3. 集成到聊天流程中
将解析器集成到前端的聊天流程中:
javascript
import { useChat } from 'ai/react';
import { useEffect, useRef, useState } from 'react';
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat();
const parserRef = useRef(new StreamFunctionCallParser());
const [currentToolCalls, setCurrentToolCalls] = useState([]);
useEffect(() => {
if (!isLoading) {
// 对话结束时重置解析器
parserRef.current.reset();
setCurrentToolCalls([]);
return;
}
// 监听SSE事件
const eventSource = new EventSource('/api/chat');
eventSource.onmessage = (event) => {
const chunk = JSON.parse(event.data);
// 解析工具调用
const completeToolCalls = parserRef.current.parseChunk(chunk);
if (completeToolCalls.length > 0) {
setCurrentToolCalls(prev => [...prev, ...completeToolCalls]);
// 调用工具
completeToolCalls.forEach(toolCall => {
callTool(toolCall);
});
}
};
return () => {
eventSource.close();
};
}, [isLoading]);
// 调用工具
const callTool = async (toolCall) => {
try {
// 显示工具调用状态
setCurrentToolCalls(prev => prev.map(tc =>
tc.id === toolCall.id ? { ...tc, status: 'calling' } : tc
));
// 调用后端工具API
const response = await fetch('/api/tool', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(toolCall)
});
const result = await response.json();
// 更新工具调用状态
setCurrentToolCalls(prev => prev.map(tc =>
tc.id === toolCall.id ? { ...tc, status: 'completed', result } : tc
));
// 将工具结果发送给模型,继续生成回答
sendToolResult(toolCall.id, result);
} catch (error) {
// 更新工具调用状态为失败
setCurrentToolCalls(prev => prev.map(tc =>
tc.id === toolCall.id ? { ...tc, status: 'failed', error: error.message } : tc
));
}
};
// 发送工具结果给模型
const sendToolResult = (toolCallId, result) => {
// 实现发送工具结果的逻辑
};
return (
<div>
{messages.map(m => (
<div key={m.id} className={m.role}>
<MarkdownRenderer content={m.content} />
</div>
))}
{/* 显示当前正在调用的工具 */}
{currentToolCalls.map(toolCall => (
<div key={toolCall.id} className="tool-call">
<div className="tool-name">调用工具: {toolCall.function.name}</div>
<div className="tool-status">状态: {toolCall.status}</div>
{toolCall.status === 'completed' && (
<div className="tool-result">结果: {JSON.stringify(toolCall.result)}</div>
)}
{toolCall.status === 'failed' && (
<div className="tool-error">错误: {toolCall.error}</div>
)}
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
<button type="submit" disabled={isLoading}>发送</button>
</form>
</div>
);
}
4. 高级处理
- 并行工具调用:支持模型同时调用多个工具
- 工具调用取消:当用户取消对话时,取消正在进行的工具调用
- 工具调用历史:显示所有的工具调用历史,让用户可以查看Agent的完整执行过程
- 错误处理:处理工具调用失败的情况,显示友好的错误信息
我们团队的实践经验:
我们最开始是等模型输出完整的工具调用后再调用工具,用户需要等待很长时间。后来我们实现了流式Function Call,用户体验提升了很多,平均响应时间减少了30%。
踩过的坑:
- 不同模型的流式Function Call格式可能不同,需要为不同的模型实现不同的解析器
- JSON解析是一个容易出错的地方,需要处理各种不完整的JSON情况
- 要注意工具调用的顺序,确保工具结果按照正确的顺序发送给模型
这个方案可以很好地处理流式Function Call,提升用户体验和系统响应速度。"
深度权衡题
9. 现在很多AI应用都用Next.js全栈开发,你认为Next.js适合做Agent前端吗?有什么优缺点?
标准回答:
"Next.js确实是目前AI应用开发最流行的全栈框架,很多知名的AI应用如ChatGPT、Claude、Perplexity都是用Next.js开发的。我认为Next.js非常适合做Agent前端,但也有一些缺点需要注意。
Next.js适合做Agent前端的优点:
1. 全栈开发能力
- Next.js支持在同一个项目中开发前端和后端API,不需要单独搭建后端服务
- 对于Agent应用来说,这非常方便,因为我们经常需要写一些后端API来调用LLM和工具,避免API密钥暴露在前端
- API Routes和Server Actions可以很方便地实现后端逻辑
2. 优秀的性能和用户体验
- Next.js支持多种渲染模式:SSR、SSG、ISR、CSR,可以根据不同的页面选择最合适的渲染模式
- 对于Agent应用的首页和营销页面,可以使用SSG或ISR来获得极快的加载速度
- 对于聊天页面,可以使用CSR来获得流畅的交互体验
- App Router的流式渲染和React Server Components可以进一步提升性能
3. Vercel生态和部署体验
- Next.js是Vercel开发的,部署到Vercel非常简单,只需要连接GitHub仓库即可
- Vercel提供了全球CDN、边缘计算、自动扩缩容等功能,非常适合AI应用
- Vercel的AI SDK(
ai包)是目前最好的AI前端SDK,封装了SSE、流式输出、Function Call等功能
4. 丰富的生态和社区
- Next.js有非常丰富的生态系统,有大量的第三方库和组件可以使用
- 几乎所有的AI UI组件库和工具都优先支持Next.js
- 社区活跃,问题解决速度快,有大量的教程和示例
5. 内置的优化功能
- Next.js内置了很多优化功能,比如图片优化、字体优化、代码分割等
- 这些功能可以大大提升应用的性能和用户体验
- 不需要开发者自己配置复杂的构建工具
Next.js的缺点:
1. 学习曲线较陡
- Next.js的概念比较多,尤其是App Router引入了很多新的概念,比如Server Components、Client Components、Server Actions等
- 对于不熟悉React的开发者来说,学习曲线比较陡
- 不同渲染模式的区别和使用场景需要花时间理解
2. 与传统后端集成不够方便
- 如果你的Agent后端是用Python、Java等其他语言开发的,Next.js与它们的集成不如纯前端框架方便
- 需要处理跨域、身份验证等问题
- 虽然可以用Next.js作为BFF层,但会增加系统的复杂度
3. 部署灵活性有限
- 虽然Next.js可以部署到任何支持Node.js的平台,但很多高级功能只有在Vercel上才能发挥最好的效果
- 部署到其他平台可能会遇到一些兼容性问题
- 对于需要私有化部署的企业应用来说,Next.js的部署复杂度比纯前端应用高
4. 包体积较大
- Next.js的包体积比纯前端框架大,初始加载时间可能会更长
- 虽然有代码分割等优化措施,但对于一些对性能要求极高的场景来说,可能还是不够理想
我们团队的实践经验:
我们的大多数AI应用都是用Next.js开发的。它的全栈开发能力和Vercel的部署体验确实非常出色,可以大大提高开发效率。我们的一个Agent应用从开发到上线只用了两周时间,如果用传统的前后端分离架构,可能需要一个月以上。
但对于一些需要与现有Java后端深度集成的企业级应用,我们会选择用纯React前端+Spring Boot后端的架构,而不是Next.js。
选型建议:
- 如果你要开发一个新的Agent应用,并且团队熟悉React,我强烈推荐使用Next.js
- 如果你需要与现有的非Node.js后端深度集成,或者需要完全私有化部署,可以考虑使用纯React前端
- 如果你要开发一个面向C端的产品,Next.js+Vercel是最佳选择
- 如果你要开发一个企业内部工具,并且团队熟悉Vue,可以考虑使用Nuxt.js(Vue版的Next.js)
总的来说,Next.js是目前开发Agent前端的最佳选择。它的优点远远超过了缺点,可以大大提高开发效率和用户体验。"
10. 前端在Agent系统中应该承担哪些职责?哪些逻辑应该放在前端,哪些应该放在后端?
标准回答:
"在Agent系统中,前端和后端的职责划分非常重要。合理的职责划分可以提高系统的可维护性、安全性和性能。
前端应该承担的职责:
1. 用户界面展示
- 这是前端最基本的职责,负责展示聊天界面、消息列表、用户信息等
- 实现美观、直观、易用的用户界面
- 支持响应式设计,适配不同的设备和屏幕尺寸
2. 用户交互处理
- 处理用户的输入,比如文本输入、语音输入、文件上传等
- 处理用户的点击、滚动、拖拽等交互事件
- 提供即时的反馈,比如按钮状态变化、加载指示器等
3. 流式输出渲染
- 处理LLM的流式输出,实时渲染生成的内容
- 支持Markdown、代码高亮、数学公式等富文本内容的渲染
- 实现流畅的打字机效果
4. 状态实时展示
- 展示Agent的各种状态,比如思考中、调用工具中、执行中等
- 展示工具调用的过程和结果
- 展示任务的执行进度
5. 本地状态管理
- 管理前端的本地状态,比如用户输入、会话列表、当前会话等
- 实现状态的持久化,比如将会话历史保存到localStorage中
- 管理UI状态,比如弹窗、菜单、主题等
6. 客户端优化
- 实现长对话的性能优化,比如虚拟滚动、懒加载等
- 实现图片、视频等多媒体内容的懒加载和优化
- 实现离线缓存,提高应用的加载速度和可用性
7. 基础的输入验证
- 对用户的输入进行基础的验证,比如检查输入是否为空、是否超过长度限制等
- 过滤非法输入和敏感内容
应该放在后端的逻辑:
1. LLM调用和管理
- 所有的LLM API调用都应该放在后端,避免API密钥暴露在前端
- 实现模型的路由、负载均衡、降级、重试等逻辑
- 管理模型的配置和参数
2. 工具调用和执行
- 所有的工具调用都应该放在后端,避免直接暴露工具的API密钥和端点
- 实现工具的权限控制、安全审计、错误处理等逻辑
- 管理工具的注册和发现
3. 业务逻辑处理
- 所有的核心业务逻辑都应该放在后端
- 比如任务的分解、规划、执行、反思等Agent逻辑
- 用户管理、权限控制、计费等业务逻辑
4. 数据存储和管理
- 所有的持久化数据都应该存储在后端
- 比如用户信息、会话历史、知识库数据等
- 实现数据的备份、恢复、加密等功能
5. 安全和认证
- 用户的认证和授权应该放在后端
- 实现API的访问控制、限流、熔断等安全措施
- 防止XSS、CSRF、SQL注入等安全攻击
6. 日志和监控
- 所有的系统日志和监控数据都应该在后端收集和处理
- 实现系统的可观测性,及时发现和解决问题
- 记录用户的行为和系统的运行状态
灰色地带和最佳实践:
有一些逻辑既可以放在前端也可以放在后端,需要根据具体情况来决定:
- 简单的提示词模板:可以放在前端,但如果提示词包含敏感信息或者需要动态调整,应该放在后端
- 基础的消息格式化:可以放在前端,但如果格式化逻辑比较复杂,应该放在后端
- 简单的计算:可以放在前端,但如果计算涉及敏感数据或者需要高精度,应该放在后端
我们团队的原则:
- 安全第一:任何涉及API密钥、敏感数据、核心业务逻辑的代码都必须放在后端
- 用户体验优先:任何影响用户体验的逻辑,比如流式输出渲染、状态展示等,都应该尽量放在前端
- 可维护性:逻辑应该放在最适合它的地方,避免前后端逻辑重复
- 性能考虑:计算密集型的逻辑应该放在后端,IO密集型的逻辑可以根据情况放在前端或后端
踩过的坑:
我们最开始把一些提示词模板放在了前端,结果被用户通过浏览器开发者工具查看和修改,导致了一些安全问题。后来我们把所有的提示词模板都移到了后端。
还有一次,我们把一个复杂的计算逻辑放在了前端,结果在一些性能较差的设备上运行很慢,用户体验很差。后来我们把这个逻辑移到了后端,性能提升了很多。
合理的前后端职责划分是构建一个高质量Agent系统的基础。遵循上面的原则,可以让你的系统更安全、更可维护、性能更好。"
11. 如何设计一个可扩展的Agent前端架构,支持快速接入不同的Agent能力?
标准回答:
"设计一个可扩展的Agent前端架构非常重要,尤其是当你的系统需要支持多种Agent能力,并且需要快速迭代的时候。我会采用插件化、模块化的架构设计,具体如下:
整体架构设计
我会将Agent前端分为以下几个核心层:
┌─────────────────────────────────────────┐
│ 应用层 │
│ 页面组件、路由、全局状态、布局 │
├─────────────────────────────────────────┤
│ 能力层 │
│ 各种Agent能力插件(聊天、代码、画图等)│
├─────────────────────────────────────────┤
│ 核心层 │
│ 核心SDK、状态管理、通信层、工具集 │
└─────────────────────────────────────────┘
1. 核心层设计
核心层是整个架构的基础,提供所有能力插件都需要的通用功能:
- 核心SDK :封装与后端的通信逻辑,提供统一的API接口
- 聊天API:发送消息、接收流式响应
- 工具调用API:调用工具、获取工具结果
- 会话API:创建会话、获取会话列表、删除会话
- 状态管理:提供统一的状态管理机制,支持插件注册自己的状态
- 通信层:封装SSE、WebSocket等通信协议,处理连接、重连、错误等
- 工具集:提供通用的工具函数,比如Markdown渲染、日期格式化、错误处理等
- 事件总线:实现插件之间的通信和解耦
2. 能力层设计
能力层采用插件化架构,每个Agent能力都是一个独立的插件。插件之间相互独立,可以单独开发、测试、部署和升级。
插件接口定义:
typescript
interface AgentPlugin {
// 插件唯一标识
id: string;
// 插件名称
name: string;
// 插件描述
description: string;
// 插件图标
icon: React.ReactNode;
// 插件是否启用
enabled: boolean;
// 插件的配置项
config: Record<string, any>;
// 插件初始化方法
init: (core: CoreSDK) => Promise<void>;
// 处理消息的方法
handleMessage: (message: Message) => Promise<Message | null>;
// 渲染消息的方法
renderMessage: (message: Message) => React.ReactNode;
// 渲染设置面板的方法
renderSettings: () => React.ReactNode;
// 插件销毁方法
destroy: () => Promise<void>;
}
插件示例:
typescript
class CodeAgentPlugin implements AgentPlugin {
id = 'code-agent';
name = '代码助手';
description = '帮助你编写、调试和解释代码';
icon = <CodeIcon />;
enabled = true;
config = {
language: 'javascript',
theme: 'dark'
};
private core: CoreSDK | null = null;
async init(core: CoreSDK) {
this.core = core;
// 初始化插件
}
async handleMessage(message: Message) {
// 处理用户的消息
if (message.content.includes('代码') || message.content.includes('编程')) {
// 调用代码助手的后端API
const response = await this.core.callAgent('code', message.content);
return {
id: generateId(),
role: 'assistant',
content: response,
pluginId: this.id
};
}
return null;
}
renderMessage(message: Message) {
// 渲染代码助手的消息
return (
<div className="code-message">
<CodeBlock content={message.content} language={this.config.language} />
</div>
);
}
renderSettings() {
// 渲染代码助手的设置面板
return (
<div>
<label>
默认语言:
<select
value={this.config.language}
onChange={e => this.config.language = e.target.value}
>
<option value="javascript">JavaScript</option>
<option value="python">Python</option>
<option value="java">Java</option>
</select>
</label>
</div>
);
}
async destroy() {
// 清理资源
}
}
3. 插件管理器
实现一个插件管理器,负责插件的注册、加载、启用、禁用和卸载:
typescript
class PluginManager {
private plugins: Map<string, AgentPlugin> = new Map();
private core: CoreSDK;
constructor(core: CoreSDK) {
this.core = core;
}
// 注册插件
register(plugin: AgentPlugin) {
this.plugins.set(plugin.id, plugin);
}
// 加载插件
async load(pluginId: string) {
const plugin = this.plugins.get(pluginId);
if (plugin && !plugin.enabled) {
await plugin.init(this.core);
plugin.enabled = true;
}
}
// 卸载插件
async unload(pluginId: string) {
const plugin = this.plugins.get(pluginId);
if (plugin && plugin.enabled) {
await plugin.destroy();
plugin.enabled = false;
}
}
// 获取所有插件
getAllPlugins() {
return Array.from(this.plugins.values());
}
// 处理消息
async handleMessage(message: Message) {
for (const plugin of this.plugins.values()) {
if (plugin.enabled) {
const result = await plugin.handleMessage(message);
if (result) {
return result;
}
}
}
// 如果没有插件处理,使用默认的聊天插件
return this.core.callAgent('default', message.content);
}
}
4. 应用层设计
应用层负责将各个插件组合起来,构建完整的用户界面:
- 路由系统:实现页面路由,支持不同的Agent能力对应不同的页面
- 布局系统:提供统一的布局,包括侧边栏、顶部导航、主内容区等
- 插件市场:允许用户浏览、安装和启用不同的Agent能力插件
- 设置页面:允许用户配置全局设置和各个插件的设置
架构的优势:
1. 可扩展性强
- 可以很方便地添加新的Agent能力,只需要实现一个新的插件即可
- 不需要修改核心代码,遵循开闭原则
- 插件可以单独开发、测试和部署,提高开发效率
2. 可维护性好
- 各个插件之间相互独立,耦合度低
- 每个插件的代码量都比较小,容易理解和维护
- 核心层的代码稳定,不会因为添加新能力而频繁修改
3. 灵活性高
- 用户可以根据自己的需求选择启用或禁用不同的插件
- 可以根据用户的权限动态加载不同的插件
- 可以很方便地替换或升级某个插件,而不影响其他插件
4. 可复用性高
- 核心层的代码可以在不同的项目中复用
- 插件也可以在不同的项目中复用
- 可以构建一个插件生态系统,让第三方开发者贡献插件
我们团队的实践经验:
我们之前的Agent前端是一个单体应用,每次添加新的能力都需要修改大量的代码,维护起来非常困难。后来我们重构为插件化架构,现在添加一个新的Agent能力只需要几天时间,而不是之前的几周。系统的可维护性和开发效率都有了很大的提升。
注意事项:
- 插件接口的设计非常重要,要考虑到未来的扩展性
- 要做好插件的隔离,避免插件之间的相互影响
- 要提供完善的文档和示例,方便开发者开发插件
- 要做好插件的安全审核,防止恶意插件
这个可扩展的Agent前端架构可以很好地支持快速接入不同的Agent能力,是构建大型Agent系统的最佳实践之一。"
七、部署与基础设施选型
基础必问题
1. Agent系统为什么必须用容器化部署?Docker和Kubernetes怎么选?
标准回答:
"Agent系统必须使用容器化部署,这是由Agent系统的特点决定的。Docker和Kubernetes是目前最主流的容器化技术,它们适用于不同的场景。
为什么Agent系统必须用容器化部署:
1. 环境一致性
- Agent系统依赖复杂,包括Python/Node.js运行时、各种AI库、系统依赖等
- 容器化可以将应用及其所有依赖打包成一个镜像,确保在开发、测试、生产环境中运行一致
- 避免了"在我机器上能跑"的问题
2. 快速部署和回滚
- Agent系统迭代速度快,需要频繁部署
- 容器化部署可以实现一键部署,大大缩短部署时间
- 可以很方便地回滚到之前的版本,降低部署风险
3. 资源隔离和利用率
- 容器提供了进程级别的资源隔离,可以避免不同应用之间的相互影响
- 可以在一台物理机上运行多个容器,提高资源利用率
- 可以为不同的组件分配不同的资源配额
4. 水平扩展能力
- Agent系统的负载波动大,尤其是在营销活动或高峰时段
- 容器化部署可以很方便地实现水平扩展,根据负载自动调整实例数量
- 可以快速启动新的容器来处理增加的请求
5. 微服务架构支持
- Agent系统通常采用微服务架构,分为LLM服务、RAG服务、Agent服务、前端服务等
- 容器化是微服务架构的最佳实践,可以很方便地管理和部署各个微服务
6. 标准化和自动化
- 容器化部署有标准化的流程和工具
- 可以很方便地实现CI/CD自动化流水线
- 降低了运维的复杂度和成本
Docker和Kubernetes怎么选:
Docker
- 定位:容器运行时和镜像构建工具
- 优点:
- 简单易用,学习曲线低
- 轻量级,资源消耗少
- 适合单机部署和开发环境
- 缺点:
- 不适合大规模集群部署
- 没有内置的编排、调度、自动扩缩容等功能
- 高可用性和可靠性有限
Kubernetes(K8s)
- 定位:容器编排和集群管理平台
- 优点:
- 强大的容器编排能力,支持大规模集群部署
- 内置自动扩缩容、负载均衡、服务发现、自愈等功能
- 高可用性和可靠性好
- 生态丰富,有大量的第三方工具和插件
- 缺点:
- 学习曲线陡峭,复杂度高
- 运维成本高
- 资源消耗大
选型建议:
- 开发环境和单机部署:使用Docker Compose就足够了
- 小型生产环境(少于10个节点):可以考虑使用Docker Swarm,它比K8s简单,足够满足小型环境的需求
- 中大型生产环境(多于10个节点):必须使用Kubernetes,它是目前唯一的选择
- 企业级生产环境:使用云厂商提供的托管K8s服务,比如阿里云ACK、腾讯云TKE、AWS EKS等,不需要自己搭建和维护K8s集群
我们团队的实践:
- 开发环境使用Docker Compose
- 测试环境使用单节点K8s
- 生产环境使用阿里云ACK托管K8s服务
这种组合既保证了开发环境的简单易用,又保证了生产环境的高可用性和可扩展性。
Agent系统的容器化最佳实践:
- 每个组件一个容器,遵循单一职责原则
- 使用多阶段构建减小镜像体积
- 不要在容器中存储数据,使用持久卷存储数据
- 以非root用户运行容器,提高安全性
- 设置合理的资源请求和限制
- 使用健康检查确保容器正常运行
- 不要在镜像中包含敏感信息,使用环境变量或Secret传递
容器化部署是Agent系统的基础。没有容器化部署,就很难实现Agent系统的高可用性、可扩展性和可维护性。"
2. Serverless在Agent场景下适用吗?什么情况下用Serverless,什么情况下用传统服务器?
标准回答:
"Serverless在Agent场景下有一定的适用性,但不是所有场景都适合。需要根据具体的业务需求和技术特点来选择。
Serverless的优势:
1. 按需付费,成本低
- Serverless按照实际的执行时间和资源消耗付费,没有请求时不收费
- 对于流量波动大的Agent应用,成本优势非常明显
- 不需要为闲置的资源付费
2. 免运维,开发效率高
- 不需要管理服务器和基础设施,云厂商负责所有的运维工作
- 开发者只需要专注于业务逻辑的开发
- 可以大大缩短开发周期,快速上线
3. 自动扩缩容
- Serverless平台会根据请求量自动扩缩容
- 可以处理突发的流量高峰,不需要提前规划容量
- 可以支持从0到无限的并发
4. 高可用性
- Serverless平台内置了高可用性和容错能力
- 服务会自动部署到多个可用区
- 单个实例故障不会影响整个服务
Serverless的劣势:
1. 冷启动问题
- 当长时间没有请求时,Serverless平台会关闭实例
- 新的请求到来时,需要重新启动实例,这会导致冷启动延迟
- 对于Agent应用来说,冷启动延迟可能会达到几秒甚至十几秒,严重影响用户体验
2. 执行时间限制
- Serverless函数有最大执行时间限制,通常是5-15分钟
- 对于需要长时间运行的Agent任务,比如生成复杂的报告、分析大量数据等,Serverless不适用
3. 资源限制
- Serverless函数的CPU、内存、磁盘空间等资源都有限制
- 对于需要大量资源的任务,比如运行大模型、处理大文件等,Serverless不适用
4. 状态管理困难
- Serverless函数是无状态的,很难管理状态
- 对于需要维护长连接和会话状态的Agent应用,实现起来比较复杂
5. 供应商锁定
- 不同云厂商的Serverless平台有不同的API和功能
- 迁移到其他云厂商的成本很高
Serverless在Agent场景下的适用情况:
1. 适合使用Serverless的场景:
- API网关和BFF层:处理前端请求,转发到后端服务
- 简单的工具调用:比如天气查询、股票查询等执行时间短的工具
- 异步任务处理:比如发送邮件、生成通知等
- 流量波动大的场景:比如营销活动、临时推广等
- 开发和测试环境:可以大大降低开发和测试成本
2. 不适合使用Serverless的场景:
- 核心Agent服务:需要处理长连接和流式输出,冷启动延迟会严重影响用户体验
- 长时间运行的任务:比如生成报告、分析数据等执行时间超过5分钟的任务
- 需要大量资源的任务:比如运行大模型、处理大文件等
- RAG服务:需要加载向量索引和模型,冷启动时间长
- WebSocket服务:需要维护长连接,Serverless不适合
我们团队的实践:
我们采用Serverless和传统服务器混合的架构:
- 使用阿里云函数计算来处理API网关、简单的工具调用、异步任务等
- 使用K8s来部署核心Agent服务、RAG服务、LLM服务等
- 使用Serverless工作流来编排复杂的任务流程
这种混合架构既利用了Serverless的成本优势和免运维优势,又避免了它的缺点,是目前Agent系统的最佳实践之一。
选型决策树:
- 任务执行时间是否超过5分钟?是→传统服务器,否→继续
- 是否需要维护长连接或会话状态?是→传统服务器,否→继续
- 是否需要大量的CPU或内存资源?是→传统服务器,否→继续
- 流量波动是否很大?是→Serverless,否→都可以
总的来说,Serverless在Agent场景下是一个很好的补充,但不能完全替代传统服务器。合理地结合使用两者,可以在成本、性能和开发效率之间取得最佳平衡。"
3. 可观测性工具怎么选?LangSmith、LangFuse、OpenTelemetry的区别?
标准回答:
"可观测性是Agent系统最重要的部分之一。Agent系统的行为是不确定的,没有好的可观测性,你根本不知道系统在做什么,出了问题也无法排查。LangSmith、LangFuse、OpenTelemetry是目前最主流的可观测性工具,它们各有侧重。
三个工具的对比:
| 特性 | LangSmith | LangFuse | OpenTelemetry |
|---|---|---|---|
| 定位 | AI应用专属可观测性平台 | AI应用专属可观测性平台 | 通用可观测性框架 |
| 开源 | 闭源 | 开源 | 完全开源 |
| 部署方式 | 云服务 | 云服务/自托管 | 自托管 |
| AI专属功能 | 最丰富 | 丰富 | 无 |
| 通用可观测性 | 有限 | 有限 | 最丰富 |
| 生态 | LangChain生态 | 多框架支持 | 全行业标准 |
| 成本 | 高 | 中/免费(自托管) | 低(仅基础设施成本) |
| 适合场景 | LangChain/LangGraph应用 | 所有AI应用 | 所有应用 |
具体优缺点和适用场景:
1. LangSmith
- 开发公司:LangChain
- 优点:
- AI专属功能最丰富:专门为LangChain和LangGraph设计,支持追踪LLM调用、工具调用、Agent执行流程、提示词、token消耗等所有AI相关的指标
- 与LangChain生态无缝集成:只需要添加几行代码就可以集成到LangChain应用中
- 强大的调试和分析能力:可以查看每个Agent执行的详细过程,包括每一步的输入输出、状态变化、耗时等
- 提示词管理和A/B测试:内置了提示词管理和A/B测试功能
- 数据集和评估功能:可以创建数据集,自动评估Agent的性能
- 缺点:
- 闭源:只能使用LangChain提供的云服务,不能自托管
- 成本高:对于大规模应用来说,成本很高
- 通用可观测性有限:不支持基础设施监控、日志聚合等通用可观测性功能
- 只支持LangChain生态:对于使用其他框架的应用,集成比较困难
- 适用场景:
- 使用LangChain或LangGraph开发的应用
- 需要深入调试Agent执行流程的场景
- 需要提示词管理和A/B测试的场景
2. LangFuse
- 开发公司:LangFuse
- 优点:
- 开源:可以完全自托管,数据完全可控
- 多框架支持:支持LangChain、LlamaIndex、OpenAI SDK、Anthropic SDK等几乎所有主流的AI框架
- AI专属功能丰富:支持追踪LLM调用、工具调用、Agent执行流程、token消耗、成本计算等
- 用户友好的界面:界面设计美观,使用简单
- 活跃的社区:更新迭代快,社区活跃
- 缺点:
- 自托管需要自己运维
- 高级功能需要付费
- 通用可观测性有限
- 适用场景:
- 所有AI应用,不管使用什么框架
- 需要自托管可观测性平台的场景
- 对数据安全和隐私要求高的场景
3. OpenTelemetry
- 开发公司:CNCF(云原生计算基金会)
- 优点:
- 全行业标准:是目前云原生可观测性的事实标准,得到了所有主流云厂商和工具的支持
- 完全开源:可以自由使用和修改
- 通用可观测性最丰富:支持追踪、指标、日志三大可观测性支柱
- 多语言多框架支持:支持几乎所有的编程语言和框架
- 生态丰富:有大量的导出器、处理器、仪表板等
- 缺点:
- 没有AI专属功能:需要自己实现AI相关的追踪和指标
- 学习曲线陡峭:概念多,配置复杂
- 需要自己搭建和维护整个可观测性栈
- 适用场景:
- 已经在使用OpenTelemetry的团队
- 需要统一的可观测性平台,同时监控AI应用和传统应用
- 对定制化要求高的场景
我们团队的选型和实践:
我们采用LangFuse + OpenTelemetry的组合方案:
- 使用LangFuse来追踪AI相关的指标,比如LLM调用、工具调用、Agent执行流程、token消耗、成本等
- 使用OpenTelemetry来追踪通用的指标,比如服务调用、数据库访问、基础设施监控、日志聚合等
- 将LangFuse的数据导出到OpenTelemetry,实现统一的可观测性
这种组合方案既利用了LangFuse的AI专属功能,又利用了OpenTelemetry的通用可观测性能力,是目前最佳的实践之一。
选型建议:
- 如果你使用LangChain或LangGraph,并且预算充足,可以选择LangSmith
- 如果你需要自托管,或者使用其他框架,可以选择LangFuse
- 如果你已经在使用OpenTelemetry,或者需要统一的可观测性平台,可以选择OpenTelemetry,并自己实现AI相关的追踪
- 最佳实践是结合使用AI专属可观测性工具和通用可观测性工具
可观测性是Agent系统的生命线。没有好的可观测性,你的Agent系统就是一个黑盒,出了问题你根本不知道为什么。所以一定要在项目早期就重视可观测性,选择合适的工具。"
4. 日志系统怎么选?ELK、Loki、PLG在Agent系统中的适用场景?
标准回答:
"日志系统是可观测性的重要组成部分。Agent系统会产生大量的日志,包括LLM调用日志、工具调用日志、Agent执行日志、错误日志等。选择一个合适的日志系统非常重要。ELK、Loki、PLG是目前最主流的三个日志系统,它们在Agent系统中的适用场景不同。
三个日志系统的对比:
| 特性 | ELK | Loki | PLG |
|---|---|---|---|
| 全称 | Elasticsearch, Logstash, Kibana | Loki | Promtail, Loki, Grafana |
| 索引方式 | 全文索引 | 标签索引 | 标签索引 |
| 存储成本 | 高 | 低 | 低 |
| 查询性能 | 高(全文检索) | 高(标签查询),低(全文检索) | 高(标签查询),低(全文检索) |
| 资源消耗 | 高 | 低 | 低 |
| 运维复杂度 | 高 | 中 | 中 |
| 生态完善度 | 极高 | 高 | 高 |
| 适合场景 | 需要全文检索的场景 | 云原生环境、大规模日志 | 云原生环境、已经使用Prometheus和Grafana的团队 |
具体优缺点和适用场景:
1. ELK Stack
- 优点:
- 全文检索能力最强:Elasticsearch是目前最好的全文搜索引擎,可以对日志内容进行全文检索
- 功能最丰富:支持日志的收集、处理、存储、搜索、可视化、告警等所有功能
- 生态最完善:有大量的插件和工具,支持几乎所有的数据源和输出
- 成熟稳定:经过了多年的验证,是最成熟的日志系统
- 缺点:
- 存储成本高:Elasticsearch会对所有日志内容建立全文索引,占用大量的存储空间
- 资源消耗高:Elasticsearch需要大量的CPU和内存资源
- 运维复杂度高:Elasticsearch集群的运维比较复杂,需要专业的DBA
- 成本高:对于大规模日志场景,成本非常高
- 适用场景:
- 需要对日志内容进行全文检索的场景
- 日志量不大的中小型应用
- 对查询性能要求极高的场景
- 已经在使用ELK的团队
2. Loki
- 开发公司:Grafana Labs
- 优点:
- 存储成本低:Loki不对日志内容建立全文索引,只对标签建立索引,日志内容以压缩格式存储,存储成本只有ELK的1/10左右
- 资源消耗低:Loki的资源消耗比Elasticsearch低很多
- 云原生设计:专门为云原生环境设计,支持Kubernetes、Docker等
- 与Grafana无缝集成:可以使用Grafana来可视化和查询日志
- 水平扩展能力强:可以很方便地水平扩展,处理大规模日志
- 缺点:
- 全文检索能力弱:Loki的全文检索能力不如Elasticsearch
- 相对较新:不如ELK成熟稳定
- 适用场景:
- 云原生环境
- 大规模日志场景
- 对成本敏感的场景
- 已经在使用Grafana的团队
3. PLG Stack
PLG是Promtail + Loki + Grafana的缩写,其实就是Loki的完整技术栈。Promtail负责收集日志,Loki负责存储和查询日志,Grafana负责可视化和告警。
它的优缺点和适用场景与Loki基本相同。
Agent系统中的日志特点和选型建议:
Agent系统的日志有以下特点:
- 日志量大:每个用户请求都会产生大量的日志
- 结构化程度高:大多数日志都是结构化的,比如LLM调用日志、工具调用日志等
- 查询方式:大多数查询都是基于标签的查询,比如根据用户ID、会话ID、模型名称、工具名称等查询日志,全文检索的需求相对较少
- 成本敏感:日志量很大,存储成本是一个重要的考虑因素
基于这些特点,Loki(PLG Stack)是Agent系统的最佳选择:
- 它的存储成本低,适合处理大规模日志
- 它的标签查询能力强,正好符合Agent系统的查询需求
- 它是云原生设计,适合部署在Kubernetes上
- 它与Grafana无缝集成,可以和指标、追踪一起在Grafana中查看
我们团队的实践:
我们之前使用的是ELK Stack,但是随着日志量的增长,存储成本越来越高,运维也越来越复杂。后来我们迁移到了PLG Stack,存储成本降低了80%,运维复杂度也大大降低。我们只在需要全文检索的少数场景下保留了一个小型的Elasticsearch集群。
最佳实践:
- 尽量使用结构化日志,这样可以充分利用Loki的标签查询能力
- 合理设计标签,不要使用太多的高基数标签
- 对于需要全文检索的日志,可以同时输出到Loki和Elasticsearch
- 设置合理的日志保留时间,定期清理过期的日志
- 使用Grafana统一查看日志、指标和追踪
总的来说,Loki(PLG Stack)是目前Agent系统日志系统的最佳选择。它的成本低、性能好、适合云原生环境,可以很好地满足Agent系统的日志需求。
进阶场景题
5. 如何设计Agent系统的CI/CD流水线?需要包含哪些环节?
标准回答:
"Agent系统的CI/CD流水线与传统应用有很大不同,因为它不仅包含代码,还包含模型、提示词、知识库数据等。我会设计一个包含以下环节的完整CI/CD流水线:
整体流水线设计:
代码提交 → 静态代码检查 → 单元测试 → 集成测试 → AI专项测试 → 构建镜像 → 部署到测试环境 → 自动化验收测试 → 灰度发布 → 全量发布 → 监控和回滚
每个环节的详细说明:
1. 代码提交和触发
- 使用Git作为版本控制系统
- 采用Git Flow或GitHub Flow工作流
- 当代码推送到main分支或创建PR时,自动触发流水线
2. 静态代码检查
- 检查代码风格和规范:使用ESLint、Prettier(前端)、flake8、black(Python)、CheckStyle(Java)
- 检查代码质量:使用SonarQube
- 检查安全漏洞:使用Snyk、Trivy扫描依赖漏洞
- 检查密钥泄露:使用git-secrets防止API密钥和敏感信息提交到代码库
3. 单元测试
- 测试各个组件的独立功能
- 重点测试工具调用、提示词模板、数据处理等核心逻辑
- 使用Jest(前端)、pytest(Python)、JUnit(Java)
- 要求单元测试覆盖率达到80%以上
4. 集成测试
- 测试各个组件之间的交互
- 测试API接口的正确性
- 使用Postman、Newman进行API测试
- 测试数据库和向量数据库的集成
5. AI专项测试(Agent系统特有)
这是Agent系统CI/CD中最重要也是最容易被忽视的环节:
- 提示词测试:自动测试提示词的效果,确保修改提示词不会导致回答质量下降
- 工具调用测试:测试模型是否能正确调用工具,参数是否正确
- 幻觉测试:测试模型是否会产生幻觉,回答是否基于提供的上下文
- 对抗测试:测试系统是否能抵御prompt注入、越狱等攻击
- 回归测试:使用历史问题集进行回归测试,确保新版本不会引入新的问题
- 我们会使用LangSmith或LangFuse的评估功能来自动化这些测试
6. 构建镜像
- 使用Docker构建应用镜像
- 采用多阶段构建减小镜像体积
- 对镜像进行安全扫描:使用Trivy扫描镜像漏洞
- 将镜像推送到私有镜像仓库:如Harbor、阿里云ACR
7. 部署到测试环境
- 使用Kubernetes进行部署
- 自动更新测试环境的应用
- 执行数据库迁移脚本
- 验证部署是否成功
8. 自动化验收测试
- 模拟真实用户场景进行端到端测试
- 使用Cypress或Playwright进行UI自动化测试
- 测试核心业务流程:如用户登录、发起对话、调用工具、生成报告等
- 测试性能和稳定性:使用JMeter或k6进行压力测试
9. 灰度发布
- 先将新版本部署到少量实例
- 只允许内部用户或特定用户访问新版本
- 实时监控新版本的性能和错误率
- 如果没有问题,逐步扩大灰度范围
10. 全量发布
- 当灰度发布验证通过后,将新版本部署到所有实例
- 采用滚动更新的方式,确保服务不中断
- 实时监控发布过程,出现问题立即回滚
11. 监控和回滚
- 发布后持续监控系统的各项指标:如响应时间、错误率、token消耗、用户满意度等
- 设置告警阈值,出现异常及时告警
- 如果出现严重问题,一键回滚到上一个稳定版本
Agent系统CI/CD的特殊考虑:
1. 模型和提示词的版本管理
- 将模型配置和提示词纳入版本控制
- 每个版本的提示词都有唯一的版本号
- 支持快速切换不同版本的提示词
2. 知识库数据的版本管理
- 对知识库数据进行版本管理
- 每次更新知识库都要记录版本号和更新内容
- 支持回滚到之前版本的知识库
3. 环境隔离
- 开发、测试、生产环境完全隔离
- 不同环境使用不同的模型API密钥和数据库
- 测试环境使用测试模型或低优先级模型,避免影响生产环境
4. 成本控制
- 自动化测试使用成本较低的模型
- 限制测试环境的并发数和调用次数
- 定期清理测试环境的临时数据
我们团队的实践经验:
我们最开始的CI/CD流水线只有代码检查、测试和部署环节,没有AI专项测试。结果有一次我们修改了一个提示词,导致工具调用的准确率从90%下降到了60%,直到上线后才发现。后来我们添加了AI专项测试环节,使用1000个历史问题进行自动化回归测试,现在再也没有出现过类似的问题。
踩过的坑:
- 不要在CI/CD流水线中使用生产环境的模型API,会产生不必要的成本,还可能影响生产环境
- AI专项测试需要一定的时间,不要因为测试时间长就跳过它
- 一定要有一键回滚功能,Agent系统的行为是不确定的,即使经过了充分的测试,上线后也可能出现问题
一个完善的CI/CD流水线是Agent系统质量的重要保障。尤其是AI专项测试环节,对于保证系统的稳定性和可靠性至关重要。"
6. 如何做Agent系统的负载均衡和水平扩展?
标准回答:
"Agent系统的负载波动非常大,尤其是在营销活动或高峰时段,并发量可能会突然增加几十倍。因此,负载均衡和水平扩展是Agent系统必须解决的核心问题。
Agent系统的负载特点:
- 负载波动大:白天和晚上的流量差异很大,营销活动期间流量会突然激增
- 请求处理时间长:每个请求可能需要几秒钟甚至几十秒才能处理完成
- 资源消耗不均匀:不同的请求消耗的资源差异很大,简单的问答和复杂的数据分析任务消耗的资源相差几十倍
- 有状态的连接:流式输出需要维护长连接
负载均衡方案:
1. 四层负载均衡
- 使用LVS或云厂商的负载均衡服务(如阿里云SLB、腾讯云CLB)作为第一层负载均衡
- 负责TCP连接的分发,将流量均匀地分配到多个七层负载均衡器
- 提供高可用性和故障转移能力
2. 七层负载均衡
- 使用Nginx或Ingress Nginx作为第二层负载均衡
- 负责HTTP请求的分发,根据请求路径、请求头、Cookie等信息进行路由
- 支持SSL终止、连接池、限流、缓存等功能
- 配置会话保持,确保同一个用户的请求被分发到同一个后端实例(如果需要)
3. 服务级负载均衡
- 使用Kubernetes的Service和CoreDNS实现服务发现和负载均衡
- 各个微服务之间通过Service名称进行通信
- 支持轮询、加权轮询、最少连接等负载均衡算法
水平扩展方案:
1. 无状态服务的水平扩展
Agent系统中的大多数服务都是无状态的,比如前端服务、API网关、LLM代理服务等。这些服务可以很方便地进行水平扩展:
- 将服务部署在Kubernetes上
- 使用HPA(Horizontal Pod Autoscaler)根据CPU使用率、内存使用率、请求数等指标自动扩缩容
- 设置最小和最大副本数,避免过度扩展
- 我们的经验是:当CPU使用率超过70%时,自动增加副本;当CPU使用率低于30%时,自动减少副本
2. 有状态服务的水平扩展
对于有状态的服务,比如Agent服务、RAG服务等,需要特殊处理:
- 将会话状态存储在外部存储中,比如Redis、数据库等,而不是本地内存
- 这样服务就变成了无状态的,可以任意扩缩容
- 使用分布式锁来避免并发问题
3. 任务队列的水平扩展
对于异步任务处理,使用消息队列和工作进程的模式:
- 使用RabbitMQ或Kafka作为消息队列
- 工作进程从消息队列中获取任务并执行
- 根据队列长度自动扩缩容工作进程的数量
- 当队列长度超过阈值时,自动增加工作进程;当队列长度低于阈值时,自动减少工作进程
4. 模型服务的水平扩展
模型服务是Agent系统中资源消耗最大的部分,也是最需要水平扩展的部分:
- 使用vLLM、TensorRT-LLM等高性能推理框架部署模型
- 支持动态批处理,提高模型的吞吐量
- 根据请求队列长度和GPU利用率自动扩缩容模型实例的数量
- 对于不同的模型,分别进行扩缩容,避免资源浪费
高级优化措施:
1. 流量控制和限流
- 在负载均衡层实现限流,防止系统被突发流量压垮
- 采用令牌桶或漏桶算法
- 对不同的用户设置不同的限流阈值
- 当系统负载过高时,优先保证VIP用户的服务
2. 服务降级
- 当系统负载过高时,自动降级非核心功能
- 比如禁用复杂的工具调用、使用更便宜更快的模型、关闭RAG等
- 保证核心功能的可用性
3. 异地多活
- 在多个地域部署服务
- 使用DNS负载均衡将用户流量分发到最近的地域
- 当某个地域发生故障时,自动将流量切换到其他地域
4. 缓存
- 缓存常见问题的回答和LLM调用结果
- 缓存向量检索结果
- 使用Redis作为分布式缓存
- 大大减少后端服务的负载
我们团队的实践经验:
我们的Agent系统部署在阿里云ACK上,使用HPA自动扩缩容。在一次营销活动中,并发量突然增加了30倍,HPA在5分钟内将副本数从10个扩展到了300个,系统稳定运行,没有出现任何问题。活动结束后,HPA又自动将副本数缩减回10个,大大节省了成本。
踩过的坑:
- 不要只根据CPU使用率来扩缩容,还要考虑请求数、队列长度等指标。Agent系统是IO密集型应用,CPU使用率可能不高,但请求数已经很多了
- 一定要设置最大副本数,避免因为异常情况导致无限扩展,产生巨额成本
- 扩缩容需要一定的时间,要提前做好容量规划,应对突发流量
负载均衡和水平扩展是Agent系统高可用性和高性能的基础。一个好的扩展方案应该能够自动应对流量的变化,保证系统的稳定运行,同时控制成本。"
7. 如何设计Agent系统的灰度发布和A/B测试?
标准回答:
"灰度发布和A/B测试对于Agent系统来说尤为重要,因为Agent系统的行为是不确定的,即使经过了充分的测试,上线后也可能出现意想不到的问题。而且,不同的提示词、模型、参数对用户体验的影响很大,需要通过A/B测试来找到最优方案。
灰度发布方案:
1. 灰度发布的目标
- 降低发布风险,将新版本的影响控制在小范围内
- 提前发现新版本的问题,避免影响所有用户
- 收集用户反馈,验证新版本的效果
2. 灰度发布的策略
我会采用渐进式灰度发布策略,分多个阶段逐步扩大新版本的覆盖范围:
-
第一阶段:内部测试
- 只允许内部员工和测试用户访问新版本
- 收集内部反馈,发现明显的问题
- 持续时间:1-2天
-
第二阶段:小流量灰度
- 开放给5%-10%的外部用户
- 重点监控错误率、响应时间、用户满意度等指标
- 如果没有问题,进入下一阶段;如果有问题,立即回滚
- 持续时间:2-3天
-
第三阶段:中流量灰度
- 开放给30%-50%的外部用户
- 继续监控各项指标,对比新版本和旧版本的差异
- 如果各项指标都优于旧版本,进入下一阶段
- 持续时间:2-3天
-
第四阶段:全量发布
- 开放给所有用户
- 继续监控24小时,确保没有问题
- 如果出现严重问题,立即回滚
3. 灰度发布的技术实现
- 使用Kubernetes的Deployment进行滚动更新
- 同时运行新版本和旧版本的Deployment
- 使用Ingress Nginx的金丝雀发布功能,根据用户ID、Cookie、请求头等信息将流量分发到不同的版本
- 示例配置:
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: agent-ingress
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "X-User-Id"
nginx.ingress.kubernetes.io/canary-by-header-value: "123,456,789"
spec:
rules:
- host: agent.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: agent-service-v2
port:
number: 80
A/B测试方案:
1. A/B测试的目标
- 比较不同版本的提示词、模型、参数、UI等对用户体验的影响
- 找到最优的方案,提高用户满意度和转化率
- 基于数据做出决策,而不是凭感觉
2. A/B测试的常见场景
- 不同提示词的效果对比
- 不同模型的效果对比(如GPT-4o vs Claude 3.5 Sonnet)
- 不同参数的效果对比(如温度、top_p)
- 不同UI设计的效果对比
- 不同功能的效果对比
3. A/B测试的技术实现
- 使用特征标志(Feature Flag)系统来控制不同版本的开关
- 常用的特征标志系统:LaunchDarkly、Flagsmith、Unleash
- 实现步骤:
- 定义实验:确定实验目标、实验变量、指标、实验时长
- 实现不同版本的代码
- 使用特征标志系统将用户随机分配到不同的实验组
- 收集实验数据:用户行为、满意度、转化率等
- 分析实验结果,确定最优版本
- 全量发布最优版本,结束实验
4. A/B测试的指标设计
- 业务指标:用户留存率、转化率、活跃度、付费率等
- 体验指标:平均对话轮次、平均响应时间、用户满意度评分等
- 技术指标:错误率、工具调用准确率、token消耗等
Agent系统灰度发布和A/B测试的特殊考虑:
1. 会话一致性
- 确保同一个用户在整个会话过程中始终使用同一个版本
- 避免用户在对话过程中突然切换到另一个版本,导致体验不一致
- 可以通过在Cookie中存储版本号来实现
2. 数据隔离
- 不同版本的数据要隔离存储,避免相互影响
- 分别统计不同版本的指标
- 确保实验结果的准确性
3. 统计显著性
- A/B测试需要足够的样本量和实验时间才能得出统计显著的结果
- 不要过早地结束实验
- 使用统计方法验证结果的显著性
4. 安全和合规
- 确保A/B测试符合相关的法律法规
- 保护用户的隐私和数据安全
- 告知用户正在参与实验,并提供退出选项
我们团队的实践经验:
我们使用Flagsmith作为特征标志系统,进行了大量的A/B测试。有一次我们测试了两个不同的提示词版本,A版本的工具调用准确率是88%,B版本是92%。虽然只有4个百分点的差异,但用户满意度提升了15%,平均对话轮次减少了20%。最终我们全量发布了B版本。
踩过的坑:
- 不要同时进行多个A/B测试,否则会相互干扰,无法得出准确的结果
- 不要只看短期指标,还要看长期指标,比如用户留存率
- 灰度发布和A/B测试期间,要加强监控,及时发现问题
灰度发布和A/B测试是Agent系统迭代优化的重要手段。通过灰度发布可以降低发布风险,通过A/B测试可以基于数据做出决策,不断提升系统的效果和用户体验。"
8. 私有化部署的Agent系统,如何解决模型和数据的安全问题?
标准回答:
"私有化部署的Agent系统主要面向企业和政府客户,这些客户对安全和隐私的要求非常高。模型和数据是Agent系统最核心的资产,必须采取严格的安全措施来保护它们。
模型安全问题及解决方案:
1. 模型泄露风险
- 风险:模型文件被非法复制、窃取或泄露
- 解决方案:
- 模型加密:对模型文件进行加密存储,只有在运行时才解密
- 模型水印:在模型中嵌入不可见的水印,当模型被泄露时可以追踪来源
- 模型蒸馏和量化:使用模型蒸馏和量化技术,减小模型体积,同时增加逆向工程的难度
- 运行时保护:使用可信执行环境(TEE),如Intel SGX、AMD SEV,在隔离的环境中运行模型,防止内存dump和调试
- 访问控制:严格限制对模型文件的访问权限,只有授权的进程才能读取模型文件
2. 模型滥用风险
- 风险:模型被用于非法用途,或者被超出授权范围使用
- 解决方案:
- 许可证管理:实现严格的许可证管理,控制模型的使用时间、并发数、用户数等
- 使用审计:记录所有的模型调用日志,包括调用者、调用时间、输入输出等
- 内容安全:在模型的输入和输出阶段添加内容安全检查,过滤非法和有害内容
- 功能限制:根据客户的需求,限制模型的功能,比如禁用代码执行、文件写入等危险功能
数据安全问题及解决方案:
1. 数据泄露风险
- 风险:用户数据、知识库数据、对话历史等被非法访问、窃取或泄露
- 解决方案:
- 传输加密:所有数据传输都使用TLS 1.3加密
- 存储加密:所有数据都加密存储,使用AES-256加密算法
- 密钥管理:使用专门的密钥管理系统(KMS)来管理加密密钥,定期轮换密钥
- 访问控制:实现基于角色的访问控制(RBAC),不同的用户只能访问他们有权限的数据
- 数据脱敏:对敏感数据进行脱敏处理,比如手机号、身份证号、银行卡号等
- 数据隔离:不同客户的数据完全隔离存储,避免数据交叉访问
2. 数据残留风险
- 风险:数据在删除后仍然残留在存储介质上,可能被恢复
- 解决方案:
- 安全删除:使用安全删除工具,彻底删除数据,覆盖存储介质
- 数据生命周期管理:定义数据的生命周期,定期清理过期数据
- 磁盘加密:对整个磁盘进行加密,即使磁盘丢失,数据也不会泄露
3. 数据滥用风险
- 风险:数据被用于超出授权范围的用途
- 解决方案:
- 数据使用协议:与客户签订明确的数据使用协议,规定数据的使用范围和目的
- 数据审计:记录所有的数据访问和操作日志,定期进行审计
- 数据最小化:只收集和存储必要的数据,避免收集多余的敏感数据
网络和系统安全:
1. 网络隔离
- 将Agent系统部署在私有网络中,不直接暴露在公网上
- 使用防火墙和网络访问控制列表(NACL)限制网络访问
- 实现网络分段,将不同的组件部署在不同的网段,限制网段之间的通信
2. 身份认证和授权
- 实现强身份认证,支持多因素认证(MFA)
- 实现单点登录(SSO),与企业现有的身份系统集成
- 实现最小权限原则,每个用户和服务只拥有完成其工作所必需的最小权限
3. 漏洞管理
- 定期扫描系统漏洞,及时安装安全补丁
- 定期进行渗透测试,发现和修复安全漏洞
- 建立安全应急响应机制,及时处理安全事件
4. 日志和审计
- 记录所有的系统日志、访问日志、操作日志
- 集中存储和分析日志,及时发现异常行为
- 保留足够长的日志时间,以便事后调查和审计
我们团队的实践经验:
我们为一家银行客户部署了私有化的Agent系统,采取了以上所有的安全措施。我们使用Intel SGX来运行模型,所有数据都加密存储,实现了严格的访问控制和审计。系统通过了银行的安全审计,满足了金融行业的安全要求。
踩过的坑:
- 不要忽视物理安全,服务器机房要有严格的物理访问控制
- 安全措施会影响系统的性能,需要在安全和性能之间取得平衡
- 安全是一个持续的过程,不是一次性的工作,需要定期更新和维护安全措施
私有化部署的Agent系统,安全是第一位的。只有保证了模型和数据的安全,才能获得企业和政府客户的信任。"
深度权衡题
9. 现在很多云厂商都推出了自己的Agent平台(如阿里云百炼、腾讯云智能体),什么情况下你会选择用云厂商的平台,什么情况下自己搭建?
标准回答:
"云厂商的Agent平台提供了一站式的Agent开发和部署解决方案,可以大大提高开发效率。但自己搭建Agent系统可以获得更高的灵活性和控制力。选择哪种方式需要根据具体的业务需求、团队能力和成本预算来决定。
云厂商Agent平台的优势:
1. 开发效率高
- 提供了完整的工具链,包括模型接入、提示词管理、RAG、工具调用、前端UI等
- 不需要从零开始搭建系统,可以快速开发和部署Agent
- 通常提供可视化的开发界面,不需要编写大量的代码
2. 免运维
- 云厂商负责所有的基础设施运维,包括服务器、网络、存储、数据库等
- 自动扩缩容,不需要担心流量高峰
- 提供高可用性和可靠性保障
3. 生态丰富
- 集成了云厂商的其他服务,比如对象存储、数据库、消息队列、安全服务等
- 提供了大量的预置工具和模板,可以直接使用
- 有完善的文档和技术支持
4. 成本可控
- 按需付费,没有前期投入
- 可以根据使用量灵活调整成本
- 不需要专门的运维团队,降低人力成本
云厂商Agent平台的劣势:
1. 灵活性差
- 平台的功能是固定的,很难进行深度定制
- 无法修改平台的核心逻辑
- 与云厂商绑定,迁移成本高
2. 数据安全和隐私风险
- 数据存储在云厂商的服务器上,存在数据泄露的风险
- 对于金融、医疗、政府等对数据安全要求极高的行业,可能无法满足合规要求
- 云厂商可能会访问和使用你的数据
3. 成本可能更高
- 对于大规模使用的场景,云厂商平台的成本可能会比自己搭建更高
- 云厂商通常会收取平台服务费,加上模型和基础设施的费用,总成本可能会很高
4. 技术锁定
- 一旦使用了某个云厂商的平台,就很难迁移到其他平台
- 会受制于云厂商的定价策略和功能更新
选择云厂商平台的情况:
1. 快速原型验证
- 如果你需要快速验证一个想法,看看是否有市场需求
- 云厂商平台可以让你在几天内就开发出一个可用的Agent原型
- 不需要投入大量的时间和资源在基础设施上
2. 中小型应用
- 如果你的Agent应用用户量不大,功能相对简单
- 云厂商平台的功能已经足够满足你的需求
- 自己搭建系统的成本会高于使用云厂商平台
3. 团队能力有限
- 如果你的团队没有足够的AI和运维工程师
- 云厂商平台可以让你用较少的人力开发和维护Agent系统
4. 非核心业务
- 如果Agent不是你的核心业务,只是一个辅助功能
- 不值得投入大量的资源自己搭建系统
- 使用云厂商平台可以快速实现功能,专注于核心业务
选择自己搭建的情况:
1. 大规模生产应用
- 如果你的Agent应用用户量很大,每天有几十万甚至几百万的请求
- 自己搭建系统可以更好地控制成本,长期来看成本会更低
- 可以根据业务需求进行深度优化,提高性能和稳定性
2. 对数据安全和隐私要求高
- 如果你的应用涉及敏感数据,比如金融、医疗、政府等行业
- 需要完全控制数据,不能将数据存储在第三方服务器上
- 需要满足严格的合规要求
3. 需要深度定制和差异化
- 如果你的Agent需要独特的功能和体验,云厂商平台无法满足
- 需要修改Agent的核心逻辑,实现差异化竞争
- 想要打造自己的技术壁垒
4. 长期战略考虑
- 如果Agent是你的核心业务,未来会投入大量的资源进行研发
- 想要掌握核心技术,不被云厂商绑定
- 未来可能会推出自己的模型和平台
我们团队的实践经验:
我们在项目的早期阶段会使用云厂商平台来快速验证想法。当业务验证成功,用户量开始增长时,我们会逐步将核心功能迁移到自己搭建的系统上。这样既保证了早期的开发速度,又为未来的发展打下了基础。
最佳实践:混合架构
对于大多数团队来说,最佳实践是采用混合架构:
- 使用云厂商平台来处理非核心功能和快速原型
- 自己搭建核心的Agent服务和数据存储
- 通过API将两者集成起来
这样可以结合两者的优势,既提高了开发效率,又保证了灵活性和数据安全。
选型决策树:
- 是否需要快速验证想法?是→云厂商平台,否→继续
- 用户量是否小于1万?是→云厂商平台,否→继续
- 是否涉及敏感数据?是→自己搭建,否→继续
- 是否需要深度定制?是→自己搭建,否→云厂商平台
总的来说,云厂商平台适合快速原型和中小型应用,自己搭建适合大规模生产应用和对数据安全要求高的场景。团队应该根据自己的实际情况做出选择,不要盲目跟风。"
10. 边缘计算在Agent场景下有什么用?什么情况下需要把Agent部署在边缘?
标准回答:
"边缘计算是指将计算任务部署在靠近用户的边缘节点上,而不是集中在云端数据中心。在Agent场景下,边缘计算可以带来很多独特的优势,解决一些云端部署无法解决的问题。
边缘计算在Agent场景下的优势:
1. 极低的延迟
- 边缘节点距离用户更近,网络延迟可以从云端的几十毫秒降低到几毫秒
- 对于需要实时响应的Agent应用,比如语音助手、自动驾驶、工业控制等,低延迟至关重要
- 可以大大提高用户体验,减少用户等待时间
2. 数据安全和隐私
- 数据在本地处理,不需要上传到云端
- 可以避免数据在传输过程中被窃取或泄露
- 对于涉及敏感数据的场景,比如医疗、金融、个人隐私等,边缘计算可以提供更好的安全保障
- 可以满足严格的数据本地化合规要求
3. 降低带宽成本
- 不需要将大量的数据上传到云端处理,只需要上传最终的结果
- 可以大大减少带宽消耗,降低网络成本
- 对于需要处理大量音视频数据的Agent应用,带宽成本的节省非常明显
4. 离线可用
- 边缘Agent可以在没有网络连接的情况下正常工作
- 对于网络连接不稳定或者没有网络的场景,比如偏远地区、飞机、轮船等,边缘计算是唯一的选择
- 可以提高系统的可用性和可靠性
5. 减轻云端负载
- 将一部分计算任务卸载到边缘节点,可以大大减轻云端的负载
- 可以降低云端的基础设施成本
- 可以提高系统的整体吞吐量和扩展性
边缘计算在Agent场景下的应用:
1. 端侧智能助手
- 手机、电脑、智能音箱等设备上的智能助手
- 将简单的问答和控制功能部署在端侧,复杂的任务再交给云端处理
- 可以实现离线可用,提高响应速度,保护用户隐私
2. 工业Agent
- 工厂车间里的智能Agent,用于设备监控、故障诊断、生产调度等
- 工业场景对延迟和可靠性要求极高,云端部署无法满足
- 数据在工厂本地处理,不需要上传到云端,保护工业数据安全
3. 车载Agent
- 汽车上的智能座舱Agent,用于语音控制、导航、娱乐等
- 车载场景对延迟要求极高,而且网络连接不稳定
- 边缘部署可以保证在没有网络的情况下也能正常使用基本功能
4. 医疗Agent
- 医院里的智能诊断Agent、病房监护Agent等
- 医疗数据非常敏感,不能上传到云端
- 边缘部署可以保证数据安全,同时满足实时性要求
5. 零售Agent
- 商店里的智能导购Agent、自助结账Agent等
- 可以实时处理顾客的请求,提供个性化的服务
- 数据在本地处理,保护顾客的隐私
什么情况下需要把Agent部署在边缘:
1. 对延迟要求极高
- 如果你的Agent应用需要毫秒级的响应时间,比如实时控制、实时交互等
- 云端部署的延迟无法满足要求
2. 对数据安全和隐私要求极高
- 如果你的应用涉及敏感数据,不能上传到云端
- 需要满足严格的数据本地化合规要求
3. 网络连接不稳定或没有网络
- 如果你的应用需要在网络连接不稳定或者没有网络的环境下使用
- 比如偏远地区、交通工具、野外作业等
4. 需要处理大量的本地数据
- 如果你的应用需要处理大量的音视频、传感器等本地数据
- 将这些数据上传到云端会消耗大量的带宽,成本很高
5. 系统可用性要求极高
- 如果你的应用不能因为云端故障而停止服务
- 边缘部署可以提供更高的可用性和可靠性
边缘Agent的挑战:
1. 资源有限
- 边缘节点的计算能力、内存、存储等资源都有限
- 无法运行太大的模型,需要使用轻量级的模型或者模型压缩技术
2. 部署和运维复杂
- 边缘节点数量多,分布广,部署和运维比较复杂
- 需要实现自动化的部署、更新和监控
3. 模型更新困难
- 边缘节点的模型更新比较困难,需要考虑网络带宽和可用性
- 需要实现增量更新、断点续传等功能
4. 一致性问题
- 边缘节点和云端的数据需要保持一致
- 需要实现数据同步和冲突解决机制
我们团队的实践经验:
我们为一家汽车厂商开发了车载Agent系统,采用了端云协同的架构。简单的语音控制、导航等功能部署在车机端,复杂的问答和娱乐功能由云端处理。这样既保证了基本功能的离线可用和低延迟,又可以利用云端的强大能力提供更丰富的服务。
最佳实践:端云协同
对于大多数Agent应用来说,最佳实践是采用端云协同的架构:
- 将简单的、低延迟要求的、敏感的任务部署在边缘
- 将复杂的、需要大量计算的、非敏感的任务交给云端处理
- 边缘和云端之间通过网络进行通信和数据同步
这样可以结合边缘计算和云端计算的优势,在延迟、安全、成本、功能之间取得最佳平衡。
总的来说,边缘计算在Agent场景下有非常重要的应用价值,尤其是在对延迟、安全、离线可用要求高的场景。随着边缘设备计算能力的不断提升和轻量级模型的不断发展,边缘Agent会越来越普及。"
11. 如何评估一个Agent系统的部署成本?有哪些优化成本的方法?
标准回答:
"Agent系统的部署成本比传统应用高很多,主要是因为LLM调用和GPU资源的成本很高。合理地评估和优化部署成本,对于Agent系统的商业化成功至关重要。
如何评估Agent系统的部署成本:
Agent系统的部署成本主要由以下几个部分组成:
1. LLM调用成本
- 这是Agent系统最大的成本项,通常占总成本的60%-80%
- 计算方式:
调用次数 × 平均每次调用的token数 × 模型单价 - 不同模型的单价差异很大,比如GPT-4o的成本是GPT-4o-mini的10倍以上
- 需要根据用户量、平均对话轮次、平均每次对话的token数来估算
2. 基础设施成本
- 包括服务器、云主机、Kubernetes集群、数据库、向量数据库、对象存储等
- 计算方式:
实例数量 × 实例单价 × 运行时间 - 基础设施成本通常占总成本的10%-30%
- 需要根据并发量、数据量、存储需求来估算
3. 带宽成本
- 包括进出流量的费用
- 对于需要处理大量音视频数据或者流式输出的应用,带宽成本可能会很高
- 计算方式:
流量总量 × 带宽单价
4. 第三方服务成本
- 包括工具调用API、第三方数据服务、短信、邮件等
- 计算方式:
调用次数 × 服务单价
5. 人力成本
- 包括开发、运维、测试等人员的工资
- 虽然不是直接的部署成本,但也是系统总成本的重要组成部分
成本评估示例:
假设一个Agent系统有1万日活用户,每个用户每天平均进行2次对话,每次对话平均消耗1000个token,使用GPT-3.5-turbo模型(单价:0.0015/1K输入token,0.002/1K输出token):
- 每天的token消耗量:10000 × 2 × 1000 = 20,000,000 token
- 每天的LLM调用成本:20,000,000 ÷ 1000 × (0.0015 + 0.002) = $70
- 每月的LLM调用成本:70 × 30 = 2100
- 加上基础设施成本、带宽成本等,每月总成本大约在3000-4000
优化成本的方法:
1. LLM调用成本优化(最重要)
- 模型分层:不同复杂度的任务使用不同能力的模型。简单任务用便宜的模型,复杂任务才用贵的模型。可以降低70%以上的LLM成本。
- 缓存:缓存常见问题的回答和LLM调用结果。对于重复的问题,直接返回缓存的结果,不需要调用LLM。可以降低30%-50%的LLM成本。
- 提示词优化:优化提示词,减少不必要的token消耗。比如使用更简洁的语言,避免重复的内容。
- 限制输出长度:限制模型的最大输出token数,避免生成过长的回答。
- 批量处理:对于批量任务,将多个请求合并成一个请求,减少调用次数。
- 自托管开源模型:对于大规模使用的场景,自托管开源模型的成本会比调用闭源API低很多。
2. 基础设施成本优化
- 自动扩缩容:根据负载自动调整实例数量,避免资源浪费。可以降低30%-50%的基础设施成本。
- 使用竞价实例:使用云厂商的竞价实例,价格只有按需实例的1/3-1/10。适合非核心和容错性高的任务。
- 资源优化:合理调整实例的规格,避免过度配置。比如不需要GPU的服务就用CPU实例。
- 使用托管服务:使用云厂商的托管服务,比如托管Kubernetes、托管数据库、托管向量数据库等,减少运维成本。
- 数据生命周期管理:定期清理过期的数据,减少存储成本。
3. 带宽成本优化
- 使用CDN:将静态资源和常用的API响应缓存到CDN,减少源站的带宽消耗。
- 压缩传输:使用gzip、Brotli等压缩算法压缩传输的数据。
- 流式输出优化:优化流式输出的实现,减少不必要的流量。
4. 其他成本优化
- 工具调用优化:减少不必要的工具调用,优化工具的参数和返回结果。
- 用户分层:为不同等级的用户提供不同的服务等级。比如免费用户使用便宜的模型,付费用户使用更好的模型。
- 限流:限制免费用户的使用次数,避免被滥用。
我们团队的实践经验:
我们通过模型分层和缓存优化,将LLM调用成本降低了75%。通过自动扩缩容和使用竞价实例,将基础设施成本降低了50%。总的部署成本降低了60%以上,而用户体验几乎没有受到影响。
踩过的坑:
- 不要为了降低成本而牺牲用户体验。比如使用效果太差的模型,会导致用户流失。
- 缓存要设置合理的过期时间,避免返回过时的信息。
- 竞价实例可能会被云厂商随时回收,不要在竞价实例上运行核心和有状态的服务。
成本优化是一个持续的过程,需要不断地监控和分析系统的成本结构,找到优化的空间。一个好的成本优化方案应该在不影响用户体验的前提下,尽可能地降低成本。"
八、综合技术选型决策题(大厂终面必问)
1. 从零开始搭建一个企业级内部知识库Agent,从0到1怎么做完整的技术选型?(要求:从LLM到前端到部署,讲清楚每个环节的选择理由和权衡)
标准回答:
"从零开始搭建一个企业级内部知识库Agent,我会按照以下步骤进行完整的技术选型,每个环节都基于企业内部场景的特点进行权衡:
企业内部知识库Agent的核心特点和约束:
- 数据安全要求极高:企业内部数据非常敏感,不能泄露到外部
- 中文为主:主要处理中文文档和中文问答
- 文档数量大:通常有几十万甚至几百万份文档
- 准确性要求高:回答必须准确,不能有幻觉
- 成本敏感:企业内部应用对成本比较敏感
- 与现有系统集成:需要与企业现有的OA、CRM、知识库等系统集成
基于这些特点,我的完整技术选型方案如下:
一、核心LLM选型
- 主模型 :豆包4
- 降级模型 :Qwen 2.5 72B(自托管)
- 选择理由:
- 数据安全:豆包4是国内模型,数据不会流出中国,满足企业数据安全要求。Qwen 2.5是开源模型,可以完全私有化部署,数据完全可控。
- 中文能力:豆包4和Qwen 2.5的中文能力都超过了GPT-4o和Claude 3.5,更适合处理中文文档和中文问答。
- 工具调用准确率:豆包4的工具调用准确率达到85%,足够满足企业内部场景的需求。
- 成本:豆包4的成本只有GPT-4o的1/3,Qwen 2.5自托管的成本更低。
- 国内访问速度:豆包4在国内的访问速度比GPT-4o和Claude快很多,用户体验更好。
- 权衡: 虽然GPT-4o的工具调用准确率和推理能力更强,但考虑到数据安全、中文能力和成本,豆包4是更适合企业内部场景的选择。
二、Agent框架选型
- 核心框架 :LangGraph
- RAG框架 :LlamaIndex
- 选择理由:
- 复杂流程支持:企业内部知识库Agent需要支持复杂的多轮问答、文档分析、信息整合等任务,LangGraph的状态机模型非常适合这些复杂流程。
- 可观测性:LangGraph的可观测性和调试能力比LangChain好很多,方便排查问题。
- RAG能力:LlamaIndex的RAG能力是所有框架中最强的,专门优化了知识库场景,支持多种文档格式和索引方式。
- 生态完善:LangGraph和LlamaIndex的生态都很完善,有大量的组件和工具可以使用。
- 权衡: 虽然Spring AI对于Java栈更友好,但LangGraph和LlamaIndex在Agent和RAG方面的能力更强,更适合知识库场景。
三、RAG技术栈选型
- 向量数据库 :PGVector
- Embedding模型 :BGE-M3
- Rerank模型 :BGE-Reranker-v2-large
- 选择理由:
- 技术栈统一:企业通常已经在使用PostgreSQL,PGVector可以与PostgreSQL无缝集成,不需要额外部署和维护一套新的数据库系统,降低运维成本。
- 混合查询支持:PGVector支持SQL查询和向量查询的混合,可以很方便地实现基于元数据的过滤。
- 中文效果:BGE-M3和BGE-Reranker-v2的中文效果是目前最好的,超过了OpenAI的text-embedding-3和Cohere Rerank。
- 私有化部署:BGE-M3和BGE-Reranker-v2都是开源模型,可以完全私有化部署,数据不会泄露。
- 成本:自托管Embedding和Rerank模型的成本比调用API低很多,适合大规模文档处理。
- 权衡: 虽然Milvus和Qdrant的性能比PGVector好,但对于1000万以下的文档量,PGVector的性能已经足够,而且技术栈统一的优势更大。
四、工具调用与协议选型
- 工具调用协议 :MCP
- MCP Server开发语言 :Python
- 选择理由:
- 标准化:MCP是未来Agent工具调用的标准协议,支持动态发现和松耦合。
- 易于集成:MCP可以很方便地集成企业现有的各种系统和工具,比如OA、CRM、数据库等。
- 生态丰富:已经有很多第三方MCP Server可用,可以直接使用。
- Python生态:大多数企业内部工具和系统都有Python SDK,用Python开发MCP Server最方便。
- 权衡: 虽然传统的Function Call更简单,但MCP的标准化和可扩展性更好,适合企业级应用。
五、全栈后端选型
- 开发语言和框架 :Python + FastAPI
- 消息队列 :RabbitMQ
- 任务队列 :Arq
- 关系型数据库 :PostgreSQL
- 缓存 :Redis
- 选择理由:
- AI生态:Python是AI开发的首选语言,所有的AI库和框架都优先支持Python。
- 异步支持:FastAPI原生支持异步,非常适合IO密集型的Agent后端。
- 可靠性:RabbitMQ的消息可靠性高,支持复杂的路由,适合企业内部的异步任务处理。
- 性能:Arq基于AsyncIO,性能比Celery好很多,与FastAPI的集成也更好。
- 技术栈统一:关系型数据库也使用PostgreSQL,与PGVector统一,降低运维成本。
- 权衡: 虽然Java的企业级特性更好,但Python的AI生态优势太大,更适合Agent后端开发。
六、前端选型
- 前端框架 :React + Next.js
- UI组件库 :shadcn/ui
- 状态管理 :Zustand
- 流式输出 :Vercel AI SDK
- 选择理由:
- 全栈开发能力:Next.js支持全栈开发,可以很方便地实现BFF层,调用后端API。
- AI生态:React的AI生态是所有前端框架中最丰富的,有大量的AI UI组件和工具。
- 设计美观:shadcn/ui的设计现代美观,非常适合企业内部应用。
- 简单易用:Zustand简单易用,学习曲线低,适合中小型应用。
- 流式输出支持:Vercel AI SDK封装了流式输出的逻辑,使用非常方便。
- 权衡: 虽然Vue的学习曲线更低,但React的AI生态优势更明显,更适合AI应用开发。
七、部署与基础设施选型
- 容器化 :Docker + Kubernetes
- 部署方式 :企业私有云部署
- 可观测性 :LangFuse + OpenTelemetry + PLG Stack
- CI/CD :GitLab CI/CD
- 选择理由:
- 数据安全:企业私有云部署可以保证数据完全可控,不会泄露到外部。
- 可扩展性:Kubernetes可以很方便地实现水平扩展,满足业务增长的需求。
- 可观测性:LangFuse专门为AI应用设计,OpenTelemetry和PLG Stack是通用的可观测性标准,结合起来可以实现完整的可观测性。
- 自动化:GitLab CI/CD可以实现完整的自动化CI/CD流水线,提高开发效率。
- 权衡: 虽然公有云部署更简单,但考虑到企业数据安全,私有云部署是必须的。
整体架构图:
用户 → Next.js前端 → FastAPI后端 → LangGraph Agent → LlamaIndex RAG → PGVector
↓
MCP工具服务器 → 企业内部系统
↓
豆包4 / Qwen 2.5
总结:
这个技术选型方案充分考虑了企业内部知识库Agent的特点和约束,在数据安全、中文能力、成本、可维护性之间取得了最佳平衡。所有的组件都是成熟稳定的,并且有活跃的社区支持,可以保证系统的长期发展。"
2. 如果给你3个开发,3个月时间,要做一个面向C端的Agent产品,你会怎么选择技术栈?为什么?(字节真题)
标准回答:
"如果给我3个开发,3个月时间,做一个面向C端的Agent产品,我会选择以下技术栈,核心原则是:最大化开发效率,最小化运维成本,快速验证产品价值。
团队和时间约束分析:
- 3个开发:假设是1个全栈,1个后端,1个前端
- 3个月时间:非常紧张,需要快速开发和迭代
- 面向C端:用户体验要求高,需要支持高并发,成本敏感
基于这些约束,我的技术选型方案如下:
一、核心LLM选型
- 主模型 :GPT-4o-mini
- 复杂任务模型 :GPT-4o
- 降级模型 :DeepSeek V3
- 选择理由:
- 开发效率:OpenAI的API最稳定,文档最完善,社区问题最多,开发效率最高。
- 效果和成本平衡:GPT-4o-mini的效果非常好,成本只有GPT-4o的1/10,足够满足大多数C端场景的需求。只有复杂任务才用GPT-4o。
- 多模态能力:GPT-4o-mini支持多模态,可以很方便地实现图片理解等功能。
- 生态完善:所有的框架和工具都优先支持OpenAI API。
- 为什么不选其他模型:
- Claude 3.5 Sonnet虽然长上下文更好,但工具调用准确率稍低,API稳定性不如OpenAI。
- 豆包4和DeepSeek V3虽然成本更低,但生态不如OpenAI完善,开发效率稍低。
二、Agent框架选型
- 核心框架 :LangGraph
- RAG框架 :LlamaIndex
- 选择理由:
- 快速开发:LangGraph和LlamaIndex提供了大量的现成组件,可以快速搭建Agent原型。
- 足够灵活:可以满足C端产品的各种定制化需求。
- 可观测性:LangSmith可以很方便地调试和监控Agent,大大提高开发效率。
- 生态完善:有大量的示例和教程可以参考。
- 为什么不自己写: 3个月时间太短,自己写核心逻辑会浪费大量时间,无法按时交付。
三、RAG技术栈选型
- 向量数据库 :Pinecone
- Embedding模型 :OpenAI text-embedding-3-small
- Rerank模型 :Cohere Rerank 3
- 选择理由:
- 免运维:Pinecone是完全托管的向量数据库,不需要自己部署和维护,开箱即用。
- 性能好:Pinecone的性能和可用性都非常好,支持自动扩缩容。
- 集成方便:与LangChain和LlamaIndex的集成非常好,只需要几行代码就可以使用。
- 开发效率:不需要自己搭建和维护向量数据库,可以专注于业务逻辑。
- 为什么不选开源向量数据库: 自己部署和维护开源向量数据库需要大量的时间和精力,3个月时间不够。
四、工具调用与协议选型
- 工具调用方式 :传统Function Call
- 选择理由:
- 简单快速:传统Function Call简单易用,开发速度快。
- 足够满足需求:对于一个新产品来说,初期只需要集成少数几个工具,传统Function Call已经足够。
- 生态完善:所有的模型和框架都支持传统Function Call。
- 为什么不选MCP: MCP虽然更先进,但相对较新,生态还不够完善,会增加开发复杂度和风险。等产品验证成功后再迁移到MCP也不迟。
五、全栈后端选型
- 开发语言和框架 :Python + FastAPI
- 部署平台 :Vercel
- 数据库 :Supabase(PostgreSQL)
- 缓存和消息队列 :Upstash Redis
- 选择理由:
- 开发效率:Python + FastAPI开发速度快,适合快速原型开发。
- 免运维:Vercel、Supabase、Upstash都是完全托管的服务,不需要自己部署和维护服务器。
- 集成方便:这些服务之间的集成非常好,有完善的文档和示例。
- 自动扩缩容:这些服务都支持自动扩缩容,可以处理C端的突发流量。
- 成本低:这些服务都有免费额度,初期成本几乎为零。
- 为什么不选Kubernetes: Kubernetes太复杂,运维成本高,3个月时间不够搭建和维护。
六、前端选型
- 前端框架 :React + Next.js
- UI组件库 :shadcn/ui
- 状态管理 :Zustand
- 流式输出 :Vercel AI SDK
- 部署平台 :Vercel
- 选择理由:
- 开发效率:Next.js + shadcn/ui + Vercel是目前开发AI前端最快的技术栈。
- 用户体验好:shadcn/ui的设计现代美观,Vercel的全球CDN可以提供极快的访问速度。
- 流式输出支持:Vercel AI SDK封装了流式输出的逻辑,使用非常方便。
- 全栈能力:Next.js的API Routes可以很方便地实现后端逻辑,不需要单独搭建后端服务。
- 为什么不选Vue: React的AI生态更完善,有更多的现成组件和工具可以使用。
七、部署与基础设施选型
- 部署平台 :Vercel
- 可观测性 :LangSmith + Vercel Analytics
- CI/CD :GitHub Actions
- 选择理由:
- 免运维:Vercel提供了完整的部署、托管、CDN、监控等服务,不需要自己运维任何基础设施。
- 部署简单:只需要连接GitHub仓库,就可以实现自动部署。
- 可观测性:LangSmith专门为AI应用设计,Vercel Analytics提供了前端的监控和分析。
- 开发效率:可以大大缩短部署和上线的时间。
开发计划:
- 第1个月:搭建基础架构,实现核心的Agent和RAG功能,开发最小可行产品(MVP)。
- 第2个月:完善功能,优化用户体验,进行内部测试。
- 第3个月:进行公开测试,收集用户反馈,迭代优化,准备正式上线。
总结:
这个技术栈的核心优势是开发效率极高,几乎不需要运维任何基础设施,3个开发3个月时间完全可以做出一个可用的C端Agent产品。所有的服务都是按需付费,初期成本几乎为零。当产品验证成功,用户量增长后,再逐步将核心组件迁移到自托管的方案,降低成本,提高灵活性。
关键权衡:
- 牺牲了一定的灵活性和长期成本,换取了开发速度和上市时间。
- 所有的服务都是云服务,存在一定的供应商锁定风险,但对于一个新产品来说,快速验证产品价值比什么都重要。
- 当产品成功后,可以很方便地逐步迁移到自托管的方案,不会有太大的技术债务。"
3. 如果你要做一个支持10万并发用户的Agent系统,技术选型会有什么变化?哪些地方需要做特殊优化?
标准回答:
"支持10万并发用户的Agent系统与普通的Agent系统有很大的不同,技术选型和架构设计都需要做很大的调整。核心挑战是:高并发、低延迟、高可用、低成本。
10万并发用户的技术挑战:
- LLM调用成本会非常高,每天可能需要数百万甚至数千万次调用
- 流式输出需要维护10万个长连接,对服务器的并发处理能力要求极高
- 向量数据库需要处理每秒数万次的查询请求
- 系统的任何一个环节出现瓶颈,都会导致整个系统崩溃
基于这些挑战,我的技术选型和优化方案如下:
一、核心LLM选型与优化
- 主模型 :自托管Qwen 2.5 72B / Llama 3 70B
- 简单任务模型 :自托管Qwen 2.5 7B / DeepSeek V3
- 复杂任务模型 :GPT-4o-mini / Claude 3.5 Sonnet(按需调用)
- 选择理由:
- 成本控制:自托管开源模型的成本只有调用闭源API的1/10-1/20。对于10万并发用户来说,这是必须的,否则LLM调用成本会高得无法承受。
- 性能可控:可以自己控制模型的部署和扩展,不受第三方API的限流和可用性影响。
- 效果平衡:Qwen 2.5 72B和Llama 3 70B的效果已经非常接近GPT-4o-mini,足够满足大多数场景的需求。
- 特殊优化:
- 使用vLLM 或TensorRT-LLM作为推理框架,支持动态批处理和连续批处理,提高模型吞吐量3-5倍。
- 实现模型分层路由,90%的简单任务用7B模型,9%的中等任务用72B模型,只有1%的复杂任务才调用闭源API。
- 实现请求合并,将多个用户的请求合并成一个批次进行推理,提高GPU利用率。
- 部署模型缓存,缓存常见的输入和输出,减少模型推理次数。
二、Agent框架选型与优化
- 核心框架 :自研轻量级Agent框架
- 选择理由:
- 性能:LangGraph虽然功能丰富,但有一定的性能开销。对于10万并发的场景,自研轻量级框架可以获得更好的性能。
- 可定制性:可以根据业务需求进行深度优化,去掉不必要的功能。
- 可观测性:可以完全控制执行流程,方便添加监控和调试信息。
- 保留的组件: 仍然使用LangChain和LlamaIndex的一些成熟组件,比如工具调用解析器、文档分块器等,只重写核心的工作流引擎。
- 特殊优化:
- 实现无状态Agent,所有状态都存储在外部存储中,方便水平扩展。
- 实现异步非阻塞执行,提高系统的吞吐量。
- 简化Agent的逻辑,只保留最核心的功能,减少不必要的步骤。
三、RAG技术栈选型与优化
- 向量数据库 :Milvus 分布式集群
- Embedding模型 :自托管BGE-M3
- Rerank模型 :自托管BGE-Reranker-v2-base
- 选择理由:
- 性能和可扩展性:Milvus是目前性能最好、可扩展性最强的开源向量数据库,支持每秒数十万次的查询请求,可以线性扩展。
- 成本:自托管Milvus和Embedding、Rerank模型的成本比使用云服务低很多。
- 中文效果:BGE系列模型的中文效果最好。
- 特殊优化:
- 实现向量分片和分区,将数据分散到多个节点上,提高查询性能。
- 实现多级缓存:Redis缓存热门查询结果,本地内存缓存向量索引,减少向量数据库的访问次数。
- 实现预计算:提前计算常见查询的向量,提高查询速度。
- 优化分块和索引策略,提高检索的准确率和速度。
- 使用量化技术,将向量从FP32量化到INT8,减少内存占用,提高查询速度。
四、全栈后端选型与优化
- 开发语言和框架 :Rust + Axum 或 Java 21 + Spring Boot 3.2 + 虚拟线程
- 负载均衡 :LVS + Nginx
- 消息队列 :Kafka
- 任务队列 :Celery 或 自建任务队列
- 关系型数据库 :MySQL 分布式集群
- 缓存 :Redis Cluster
- 选择理由:
- 性能:Rust和Java的性能比Python好很多,适合处理高并发场景。Java 21的虚拟线程可以用少量的线程处理大量的并发请求。
- 稳定性:Java和MySQL经过了多年的验证,稳定性和可靠性最好。
- 可扩展性:Kafka和Redis Cluster都支持线性扩展,可以处理大规模的消息和缓存。
- 特殊优化:
- 实现全异步架构,所有的IO操作都是异步的,提高系统的吞吐量。
- 实现服务拆分,将系统拆分为前端服务、API网关、Agent服务、RAG服务、LLM服务等多个微服务,每个服务可以独立扩展。
- 实现限流和熔断,保护系统不被突发流量压垮。
- 实现数据分片,将用户数据和会话数据分片存储到多个数据库节点上。
- 实现读写分离,读请求走从库,写请求走主库,提高数据库的性能。
五、前端选型与优化
- 前端框架 :React
- 构建工具 :Vite
- UI组件库 :shadcn/ui
- 状态管理 :Zustand
- CDN :Cloudflare 或 阿里云CDN
- 选择理由:
- 性能:React和Vite的性能很好,适合构建高性能的前端应用。
- 用户体验:shadcn/ui的设计美观,用户体验好。
- 特殊优化:
- 实现虚拟滚动,优化长对话的性能。
- 实现增量渲染,只渲染变化的部分,减少DOM操作。
- 实现资源懒加载,只加载当前需要的资源,加快页面加载速度。
- 使用CDN加速,将静态资源缓存到全球各地的CDN节点,提高访问速度。
- 实现离线缓存,使用Service Worker缓存静态资源,提高离线可用性。
六、部署与基础设施选型与优化
- 容器编排 :Kubernetes
- 云厂商 :阿里云 / 腾讯云 / AWS
- 可观测性 :OpenTelemetry + Prometheus + Grafana + ELK
- CI/CD :Jenkins 或 GitLab CI/CD
- 选择理由:
- 可扩展性:Kubernetes是目前最好的容器编排平台,支持自动扩缩容,可以处理大规模的容器部署。
- 高可用性:云厂商提供了高可用的基础设施和服务,可以保证系统的99.99%可用性。
- 可观测性:OpenTelemetry + Prometheus + Grafana + ELK是目前最成熟的可观测性栈,可以实现完整的监控、日志和追踪。
- 特殊优化:
- 实现多可用区部署,将服务部署在多个可用区,避免单可用区故障。
- 实现自动扩缩容,根据CPU使用率、内存使用率、请求数等指标自动调整实例数量。
- 实现异地多活,在多个地域部署服务,提高系统的可用性和容灾能力。
- 实现成本优化,使用竞价实例和预留实例,降低基础设施成本。
总结:
支持10万并发用户的Agent系统需要在技术选型和架构设计上做很多特殊的优化。核心思路是:自托管模型降低成本,微服务架构提高可扩展性,全异步架构提高吞吐量,多级缓存提高性能。同时,需要非常重视可观测性和高可用性,确保系统能够稳定运行。
这个架构的成本虽然比使用云服务高,但对于10万并发用户的规模来说,长期来看成本会更低,而且性能和可控性也更好。"
4. 现在有一个遗留系统,需要集成Agent能力,你会怎么设计技术方案?如何平衡技术债务和新功能开发?
标准回答:
"在遗留系统中集成Agent能力是一个非常常见的需求,也是一个很大的挑战。核心问题是如何在不破坏现有系统的稳定性的前提下,快速集成Agent能力,同时逐步偿还技术债务。
遗留系统的特点和挑战:
- 技术栈老旧,可能使用了过时的语言和框架
- 代码质量差,文档缺失,测试覆盖低
- 架构不合理,耦合度高,难以修改
- 业务逻辑复杂,经过多年的迭代,有很多隐藏的逻辑
- 稳定性要求高,不能因为集成Agent能力而影响现有业务
基于这些特点,我的技术方案和平衡策略如下:
一、整体架构设计:松耦合集成
我会采用松耦合的集成架构,将Agent能力作为一个独立的服务,通过API与遗留系统进行通信。这样可以最大限度地减少对遗留系统的修改,降低风险。
架构图:
用户 → 遗留系统前端 → 遗留系统后端 → Agent服务 → LLM / RAG / 工具
↓
遗留系统数据库和API
核心原则:
- 最小侵入原则:尽可能少地修改遗留系统的代码
- 独立部署原则:Agent服务独立部署和发布,不影响遗留系统的发布周期
- 渐进式原则:逐步集成Agent能力,先从简单的功能开始,再逐步扩展到复杂的功能
- 安全隔离原则:Agent服务与遗留系统之间通过API网关进行隔离,实现权限控制和流量控制
二、技术选型
- Agent服务技术栈 :选择最新的技术栈,与遗留系统的技术栈无关
- 后端:Python + FastAPI + LangGraph + LlamaIndex
- 前端:React + Next.js + shadcn/ui
- 集成方式:REST API 或 gRPC
- 通信协议:HTTPS
- 数据交换格式:JSON
选择理由:
- 松耦合:通过API进行通信,Agent服务和遗留系统完全解耦,可以使用不同的技术栈。
- 开发效率:使用最新的AI技术栈,可以快速开发Agent能力。
- 独立部署:Agent服务可以独立部署和发布,不需要等待遗留系统的发布周期。
- 风险低:对遗留系统的修改很少,不会影响现有业务的稳定性。
三、集成步骤
我会分四个阶段逐步集成Agent能力,每个阶段都有明确的目标和交付物,逐步验证和优化。
阶段一:基础集成(1-2个月)
- 目标:实现最基础的Agent能力,验证集成方案的可行性
- 工作内容:
- 搭建独立的Agent服务
- 实现与遗留系统的API集成,获取必要的业务数据
- 实现基础的问答功能,基于遗留系统的数据回答用户的问题
- 在遗留系统前端添加一个简单的聊天窗口,嵌入Agent界面
- 对遗留系统的修改:
- 添加几个API接口,供Agent服务调用获取数据
- 在前端添加一个聊天窗口组件,嵌入Agent的Web页面
- 风险控制: 只对内部员工开放,收集反馈,验证方案的可行性
阶段二:功能扩展(2-3个月)
- 目标:扩展Agent的能力,支持更多的业务场景
- 工作内容:
- 实现工具调用能力,让Agent可以调用遗留系统的API执行操作
- 实现RAG能力,导入遗留系统的文档和知识库
- 优化Agent的回答质量和用户体验
- 支持更多的业务场景,比如客户服务、工单处理、数据分析等
- 对遗留系统的修改:
- 开放更多的API接口,供Agent调用执行操作
- 优化前端的聊天窗口,支持更多的交互方式
- 风险控制: 逐步扩大用户范围,先开放给部分用户使用,收集反馈,持续优化
阶段三:深度集成(3-6个月)
- 目标:将Agent能力深度集成到遗留系统的各个业务流程中
- 工作内容:
- 实现上下文感知,Agent可以理解用户在遗留系统中的操作上下文
- 实现主动建议,Agent可以根据用户的操作主动提供帮助和建议
- 实现自动化流程,Agent可以自动完成一些重复性的工作
- 优化性能和稳定性,支持大规模用户使用
- 对遗留系统的修改:
- 添加上下文传递机制,将用户的操作上下文传递给Agent
- 在各个业务页面嵌入Agent的功能入口
- 优化API接口的性能和稳定性
- 风险控制: 进行充分的测试,确保不会影响现有业务的正常运行
阶段四:架构优化(6-12个月)
- 目标:逐步偿还技术债务,优化系统架构
- 工作内容:
- 将遗留系统中的一些业务逻辑逐步迁移到Agent服务中
- 重构遗留系统中不合理的架构和代码
- 实现统一的API网关和服务治理
- 优化系统的性能和可扩展性
- 对遗留系统的修改:
- 逐步重构遗留系统的代码,提高代码质量
- 拆分单体应用为微服务,降低耦合度
- 风险控制: 逐步重构,每次只修改一小部分,确保系统的稳定性
四、平衡技术债务和新功能开发的策略
1. 时间分配策略
- 70%的时间用于开发新的Agent功能
- 30%的时间用于偿还技术债务
- 每个迭代都安排一定的时间用于重构和优化
2. 优先级策略
- 优先开发能够带来明显业务价值的Agent功能
- 优先偿还影响Agent功能开发和稳定性的技术债务
- 对于不影响当前功能的技术债务,可以暂时搁置,以后再处理
3. 渐进式重构策略
- 不要试图一次性重写整个遗留系统,这几乎总是会失败的
- 采用渐进式重构的方法,每次只修改一小部分代码
- 每完成一次重构,都要进行充分的测试,确保没有引入新的问题
- 逐步将业务逻辑从遗留系统迁移到新的架构中
4. 测试策略
- 为遗留系统添加自动化测试,特别是集成测试和端到端测试
- 在修改遗留系统代码之前,先添加测试,确保修改不会破坏现有功能
- 建立持续集成和持续部署流水线,确保每次修改都能自动测试和部署
5. 团队策略
- 组建一个专门的团队,负责Agent能力的开发和集成
- 保留熟悉遗留系统的开发人员,负责修改遗留系统的代码
- 加强团队之间的沟通和协作,确保信息畅通
我们团队的实践经验:
我们曾经为一个有10年历史的CRM系统集成了Agent能力。我们采用了上面的架构和步骤,用了3个月时间就实现了基础的Agent功能,并且没有影响现有业务的正常运行。在接下来的6个月里,我们逐步扩展了Agent的能力,将它深度集成到了CRM的各个业务流程中。同时,我们也逐步重构了遗留系统中的一些不合理的部分,偿还了部分技术债务。现在,Agent已经成为了这个CRM系统中最受欢迎的功能之一,大大提高了用户的工作效率。
踩过的坑:
- 不要一开始就试图修改遗留系统的核心代码,这会带来很大的风险。先从外围开始,逐步深入。
- 不要低估遗留系统的复杂度,里面可能有很多隐藏的逻辑和坑。
- 一定要有充分的测试,否则修改遗留系统代码很容易引入新的问题。
在遗留系统中集成Agent能力是一个长期的过程,不能急于求成。采用松耦合的集成架构,逐步扩展功能,渐进式重构,是平衡技术债务和新功能开发的最佳方式。"
5. 如何在技术选型中平衡"技术先进性"和"工程稳定性"?你有过因为追求新技术而踩坑的经历吗?
标准回答:
"在技术选型中平衡技术先进性和工程稳定性是每个技术负责人都必须面对的挑战。技术先进性可以带来更好的性能、更高的开发效率和更强的竞争力,但也可能带来不成熟、不稳定、文档缺失、生态不完善等问题。工程稳定性是系统的生命线,尤其是对于生产级应用来说,稳定性比什么都重要。
我的平衡原则:
1. 分层选型原则
我会将系统分为不同的层次,不同的层次采用不同的选型策略:
- 核心层:系统的核心业务逻辑和基础设施,优先选择成熟稳定的技术。比如关系型数据库选择MySQL或PostgreSQL,消息队列选择Kafka或RabbitMQ。这些技术经过了多年的验证,稳定性和可靠性有保障。
- 业务层:业务功能的实现,可以适当采用一些比较新的技术,但必须是已经被广泛验证过的。比如后端框架选择FastAPI或Spring Boot,前端框架选择React或Vue。
- 边缘层:非核心功能和实验性功能,可以大胆采用最新的技术。比如新的UI组件库、新的工具库、新的AI模型等。如果新技术不好用,可以随时替换,不会影响系统的核心功能。
2. 渐进式引入原则
对于新技术,我不会一下子在整个系统中推广,而是采用渐进式引入的策略:
- 调研阶段:先对新技术进行充分的调研和评估,了解它的优缺点、适用场景、社区活跃度、未来发展趋势等。
- 原型阶段:在一个小的、非核心的项目中使用新技术,验证它的可行性和效果。
- 试点阶段:在一个中等规模的项目中使用新技术,积累经验,发现问题。
- 推广阶段:当新技术经过充分验证,证明是稳定可靠的之后,再在整个团队和系统中推广。
3. 风险评估原则
在引入新技术之前,我会进行全面的风险评估:
- 技术风险:新技术是否成熟?是否有已知的严重bug?社区是否活跃?是否有长期维护的计划?
- 团队风险:团队是否掌握了这项新技术?学习曲线是否陡峭?是否有足够的文档和教程?
- 业务风险:如果新技术出现问题,会对业务造成多大的影响?是否有回滚方案?
- 成本风险:引入新技术需要多少时间和成本?是否会影响项目的进度?
4. 保持技术债务意识
引入新技术必然会带来一定的技术债务。我会在引入新技术之前,充分评估技术债务的大小,并制定相应的偿还计划。如果技术债务太大,超过了新技术带来的收益,我会放弃引入这项新技术。
5. 建立技术雷达
我会建立一个团队的技术雷达,定期评估和更新团队的技术栈。技术雷达分为四个象限:采纳、试验、评估、暂缓。这样可以让团队对正在使用和评估的技术有一个清晰的认识,避免盲目引入新技术。
我因为追求新技术而踩坑的经历:
我曾经在一个项目中盲目追求新技术,踩了一个很大的坑。
背景:
2023年,LangChain非常火,几乎所有的AI项目都在使用LangChain。当时我们正在开发一个企业级的Agent系统,我没有经过充分的评估,就决定使用LangChain作为核心框架。
遇到的问题:
- 调试困难:LangChain的黑盒性很强,执行过程不透明。当Agent出现问题时,很难排查原因。我们花了大量的时间在调试LangChain的内部逻辑上。
- 性能差:LangChain的性能很差,尤其是在处理复杂的Agent流程时。随着用户量的增长,系统的响应时间越来越长。
- API变更频繁:LangChain的API变更非常频繁,几乎每个版本都有不兼容的变更。我们不得不频繁地修改代码来适配新版本。
- 灵活性差:LangChain的抽象过于复杂,当我们需要实现一些定制化的功能时,发现非常困难,不得不绕过LangChain的抽象,自己写代码。
解决方法:
在项目进行到一半的时候,我们意识到LangChain不适合我们的场景。于是我们决定逐步将核心逻辑从LangChain中抽出来,自己写一个轻量级的Agent框架。我们保留了LangChain的一些成熟组件,比如工具调用解析器、LLM客户端等,只重写了核心的工作流引擎。
结果:
重写之后,系统的响应时间减少了70%,调试也变得非常方便。我们可以完全控制执行流程,很容易添加监控和调试信息。虽然重写花了大约一个月的时间,但从长期来看,大大提高了系统的可维护性和性能。
经验教训:
这次经历给了我深刻的教训:
- 不要盲目跟风新技术:在引入新技术之前,一定要进行充分的调研和评估,特别是对于核心组件。
- 原型验证非常重要:在大规模使用新技术之前,一定要先做一个小的原型,验证它的可行性和性能。
- 不要过度依赖框架:框架只是工具,不是银弹。对于核心逻辑,最好自己有一定的控制能力,不要被框架绑架。
- 渐进式引入:对于新技术,要采用渐进式引入的策略,不要一下子在整个系统中推广。
总结:
技术先进性和工程稳定性不是对立的,而是可以平衡的。关键是要根据系统的层次和重要性,采用不同的选型策略,渐进式引入新技术,充分评估风险,保持技术债务意识。同时,要从失败中吸取教训,不断提高技术选型的能力。"
九、回答技术选型问题的黄金技巧
1. 先讲背景,再讲选择: 永远不要上来就说"我选了XX",先讲"我们当时面临什么问题,有哪些约束条件"
2. 用对比代替单一陈述: 不要只说"A好",要说"A和B相比,在X维度更好,在Y维度更差,而我们的场景正好需要X"
3. 量化决策依据: 尽量用数据说话,比如"我们测试了三个模型,GPT-4o的工具调用准确率是92%,Claude 3.5是88%,豆包4是85%,所以我们选了GPT-4o"
4. 讲踩过的坑和优化方向: 这是最能体现你真实经验的地方,比如"最开始我们用了LangChain,后来发现调试太困难,所以我们把核心逻辑抽出来自己写了,只保留了LangChain的工具调用部分"
5. 体现权衡思维: 技术选型没有完美的答案,只有最适合当前场景的答案。要让面试官看到你明白每个选择的优缺点,而不是盲目跟风。