
做AI Agent这件事我踩过最大的坑不是模型不会说话而是地基没打好就急着盖楼。最近我们在LCODER实战系列里推进“问数项目智能体搭建”目标很直接让业务同学用大白话问一句“上个月华东区销售额同比变化怎么样”Agent自己拆解问题、查库、算数、最后吐出一段能看懂的结论。听起来不难但真做起来你会发现难点根本不在写Prompt而在基础设施怎么搭。这篇是系列第二篇专门讲基础设施搭建模型接入层怎么设计、向量库怎么选、Agent编排框架走哪条路、MCP协议到底要不要上。如果你也在给团队做数据分析类Agent或者准备自建一套Agent底座这篇应该能帮你省掉不少弯路。1. 整体设计思路先画“地基图纸”再谈Agent能力1.1 问数项目的基础设施到底包含哪几块很多人一提Agent开发就直奔LangChain或者LangGraph把工具链、数据库连接、Prompt全揉在一起代码写了两三千行最后发现换个模型要改一半加个数据源要动全局。问数项目跟聊天机器人不一样它要连接真实的业务库、要控制权限、要保证数值算得对所以基础设施的边界必须先划清楚。我习惯把基础设施拆成五层模型接入层负责统一封装各家大模型API提供统一的对话、工具调用入口屏蔽供应商差异。知识库与向量检索层承载业务元数据、指标口径、历史FAQ解决“模型不知道你公司里‘GMV’是什么意思”的问题。工具与数据源连接层对接数据库、Excel、API、数仓是问数Agent的“手脚”。Agent编排与执行层负责任务规划、工具选择、多步执行、结果校验是Agent的大脑。可观测与安全层记录链路日志、监控费用、做权限控制这块最容易被忽略但生产环境出事全靠它兜底。这篇文章重点讲前四层中的基础设施部分也就是模型接入、向量库、编排框架选型、MCP协议接入。安全与可观测我会穿插着讲但不会单独展开太多后面实战篇再细聊。1.2 为什么先做基础设施而不是直接写Agent逻辑我见过不少团队的做法是先拿一个模型API跑通Demo然后开始堆Agent逻辑等需求一变或者线上并发一上来就各种返工。其实正确的顺序应该是反过来。原因有三点。第一模型供应商、向量库、编排框架这三样东西一旦写死在业务代码里后面换任何一个都是大手术。我自己的体会是基础设施层的代码应该尽量“与业务无关”它只提供能力不关心你的Agent是问数的还是写文案的。这样以后做第二个、第三个Agent直接复用边际成本极低。第二问数项目对模型的要求很特殊既要推理能力强又要工具调用稳定还得分短期用便宜的、复杂场景用贵的。没有一个统一的模型接入层你根本没法在代码里灵活做模型路由。第三工具层如果不抽象好Agent每接一个数据源就要重新写一套逻辑而且数据库连接、权限校验、SQL校验这些事很容易跟Agent流程耦合在一起出问题的时候极难排查。所以LCODER这条系列里我们把基础设施当成一个独立阶段来做做完之后Agent逻辑反而写得很快。注意我这里讲的“基础设施”不是让你去搭一套K8s集群或者自研推理框架那是运维和算法团队的活。对做应用层的开发来说基础设施就是“把模型、数据、工具、编排能力封装成可靠的服务”重点在设计不在炫技。2. 模型接入与统一网关设计2.1 模型选型的三个关键判断标准问数项目的模型选型跟普通聊天场景不一样我一般只看三件事工具调用能力、推理准确度、成本与延迟。工具调用Function Calling能力排在第一位。问数Agent最核心的动作是“根据问题生成查询计划并调用工具”如果模型老是编造不存在的函数名或者参数传错格式后面所有逻辑都白搭。以我目前的实测来看国产模型里像通义千问的Qwen系列、智谱GLM系列以及DeepSeek的R1/V3系列在工具调用上都已经达到了能用于生产的水平关键是选一个在你业务数据上表现最好的而不是网上评分最高的。推理准确度排第二。问数场景经常涉及“同比怎么算”“环比口径是什么”“复购率用哪个分母”模型必须理解业务口径。这里有个很实用的技巧不要只看模型的通用榜单直接把你们团队真实的业务问题和SQL放进去做一批评测集用评测集跑分好坏立刻见分晓。我们LCODER项目组现在每换一个模型第一件事就是跑那100条业务评测Case。成本与延迟排第三。问数Agent常常需要多轮规划、多次调用工具一次问答背后可能藏着5到10次模型请求。所以一定要做模型分级简单问题走便宜的小模型复杂推理才上大模型。基础设施层做好这一件事每月账单能降一半还不止。2.2 统一模型网关把供应商差异关在门外模型网关听起来高大上其实核心就三件事统一接口格式、统一Key管理、统一重试与限流。我建议不要直接用各家SDK而是全部走OpenAI兼容接口。这已经是行业事实标准了无论是阿里云百炼、智谱、DeepSeek还是OpenAI本身都提供了兼容端点。你在代码里只依赖一个OpenAI SDK换模型就是改环境变量的事。下面是我们网关服务里最核心的一小段封装去掉所有业务逻辑之后就这么点东西# gateway.py 统一模型调用入口 import os from openai import OpenAI _client OpenAI( api_keyos.getenv(LLM_API_KEY), base_urlos.getenv(LLM_BASE_URL), timeout60, max_retries2, ) def chat(messages, toolsNone, modelNone, temperature0.1): resp _client.chat.completions.create( modelmodel or os.getenv(LLM_DEFAULT_MODEL), messagesmessages, toolstools, temperaturetemperature, ) return resp.choices[0].message统一Key管理这块我强烈建议Put到一个内部配置中心或者环境变量管理平台不要散落在各个服务的配置里。我们的做法是每个数据源、每个模型供应商一个独立Key服务启动时从配置中心拉取运行时动态切换。这样做还有个额外好处可以按团队、按项目做配额和费用统计月底对账的时候一目了然。重试与限流也放在网关层。模型供应商经常会有限流和偶发超时网关里统一做好指数退避重试、并发限流和熔断上层Agent逻辑就不用天天处理乱七八糟的异常了。实测下来网关层做好重试之后我们问数项目的一次完整问答成功率从87%提到了96%左右效果非常明显。提示模型网关不要过度设计。我见过有人把网关做成独立微服务部署结果链路一长延迟飙升。对于团队内部项目把网关做成一个公共库Library即可和Agent服务同进程部署简单高效。3. 向量库与知识库基础设施搭建3.1 Embedding模型选型与向量库对比问数项目为什么需要向量库因为模型不认识你公司的专属词汇。比如业务说“新客成交率”老员工都懂模型可不知道它的准确计算口径。我们需要把指标口径文档、数据字典、历史问答沉淀成知识库通过向量检索在模型回答之前把相关知识取出来。Embedding模型我目前倾向于国内的开源模型比如BGE系列中文效果扎实而且可以本地部署数据不用出内网。向量维度一般在1024左右具体的维度和你的Embedding模型强相关千万不要混用否则检索结果会莫名其妙。向量库的选择我拿我们实际对比过的几个举例向量库部署方式优势劣势适合场景QdrantDocker单机/集群轻量、支持Payload过滤、API清爽文档略少中小团队、业务知识库MilvusDocker/分布式亿级向量、生态完善部署偏重、学习成本高大规模生产环境ChromaPython内嵌零部署、上手最快性能一般、不适合生产原型验证ES 向量插件Docker/集群全文检索与向量并存运维成本高已有ES团队的场景我们LCODER的问数项目最终选了Qdrant原因很朴素团队规模不大Qdrant一个Docker容器就能跑起来检索性能足够还自带Payload过滤。什么叫Payload过滤就是我在存向量时给每条数据打上“部门”“数据源”“业务域”等标签查询时直接过滤掉权限范围外的内容这个功能对问数Agent来说太重要了等于把权限控制前置到了检索层。3.2 文档采集、切分与入库的标准流程知识库不是把文档一股脑丢进向量库就完事切分策略直接决定召回质量。我踩过的坑是早期图省事按固定长度500字硬切结果把一个指标的应用示例从中间切断导致检索到的内容上半句不搭下半句Agent回答完全跑偏。现在我们的切分逻辑是“结构优先、长度兜底”把文档按Markdown标题层级切块切出来的块再按句子边界做二次修正每块控制在300到500字之间。每个块同时记录来源文档名、所属章节、业务标签、版本号这些元数据一起存进Payload。入库流程推荐写成定时任务文档更新后自动重跑。我们的流程大概是拉取数据字典和口径文档 - 按结构切块 - 调用Embedding模型生成向量 - 写入Qdrant集合。下面是入库核心代码的思路# knowledge_builder.py from qdrant_client import QdrantClient from qdrant_client.models import VectorParams, Distance, PointStruct client QdrantClient(hostlocalhost, port6333) client.create_collection( collection_namebusiness_docs, vectors_configVectorParams(size1024, distanceDistance.COSINE), ) # chunks 是切好的文本块embeddings 由 Embedding 模型统一生成 points [ PointStruct( idi, vectorembeddings[i], payload{ chunk: chunks[i][text], title: chunks[i][title], biz_domain: chunks[i][biz_domain], source: chunks[i][source], }, ) for i in range(len(chunks)) ] client.upsert(collection_namebusiness_docs, pointspoints)这里有一个细节集合创建时就要把向量维度写死后续如果不一致会报错。所以Embedding模型的选型要和向量库一起定下来中途切换的代价比较大。4. Agent编排框架与MCP协议接入4.1 编排框架选型LangGraph、Spring AI还是自研Agent编排框架是基础设施里争议最大的一块。LCODER项目组在选型时把市面上的方案都过了一遍最后得出一个结论没有银弹只看你的团队栈和场景复杂度。LangGraph是目前开源社区里最活跃的选择。它把Agent流程抽象成状态图节点就是“规划”“调用工具”“判读结果”这些步骤边定义了执行顺序和条件跳转。对问数这种需要多步工具调用的场景LangGraph的循环和状态管理很顺手尤其是“某次工具结果不对需要重试”这种复杂控制流写起来很自然。Spring AI则更适合Java技术栈的团队。它走的是Spring生态的路线提供了ChatClient、Advisor、多Agent编排等抽象如果你团队全是Java工程师为了一个Agent项目强行引入Python技术栈后面维护成本会很高。Spring AI的多Agent支持也确实在快速演进适合从Spring Boot老项目长出来的团队平滑进阶。自研编排则要慎重。自研的好处是完全可控、没有框架约束坏处是你得自己处理状态存储、并发控制、错误恢复这些框架已经解决的问题。我见过一些团队上来就自研最后代码量翻了好几倍。如果只是做问数这类垂直场景我不建议自研除非你的需求里面有一堆现成框架都搞不定的特殊控制流。说回我们的选择。LCODER问数项目因为核心链路涉及大量数据处理和SQL执行校验我们用LangGraph做Python侧的Agent编排同时预留Spring AI的接入窗口方便那些Java背景的同事在别的子项目里复用。框架之间不冲突关键是接口层定义清楚。4.2 MCP协议统一工具层的正确姿势MCPModel Context Protocol是目前很热的一个方向核心思想是把Agent要调用的所有外部工具统一成一套协议工具侧不用管Agent用的是什么框架Agent侧也不用为每个工具写一套专属调用代码。为什么问数项目需要MCP因为你的工具不止一个。你要连MySQL、要读Excel、要调内部指标平台API、可能还要查数仓。如果没有MCPLangGraph要写MySQL的Tool类Spring AI要写另一套每个模型供应商还得对齐工具schema工作量是乘法关系。有了MCP数据库查询、文件读取这些都做成标准化的MCP ServerAgent侧直接用MCP客户端去发现和调用工具与Agent解耦。以MCP的数据库工具为例我们写了一个data-center的MCP Server把“查询销售数据”暴露成一个标准工具# mcp_server.py 简化示例 from mcp.server.fastmcp import FastMCP mcp FastMCP(data-center) mcp.tool() def query_sales(region: str, date_from: str, date_to: str) - str: 按区域和日期范围查询销售汇总数据返回JSON字符串 # 这里内部会走SQL白名单校验、权限过滤、结果截断 return run_sale_query(region, date_from, date_to) if __name__ __main__: mcp.run()Agent侧想要调用这个工具只需要通过MCP协议发现这个Server拿到工具名和参数schema剩下的交给模型。MCP的另一个好处是安全边界更清晰工具参数做白名单校验、SQL只允许SELECT、返回结果强制截断这些都可以在MCP Server层统一实现不需要每个Agent重复写一遍。注意MCP现在还处在快速迭代期协议本身偶尔会有breaking change。我建议不要把MCP和业务强绑定做一个薄薄的适配层这样协议升级时影响面可控。如果你团队工具总共就两三个也可以先不引入MCP直接写工具函数就够。MCP是解药但不是万用药工具多了才划算。5. 实操过程与核心环节实现5.1 环境准备与基础依赖在写任何Agent逻辑之前先把环境收拾干净。我们项目的推荐环境是这样的Python 3.11虚拟环境用uv管理比pip快很多。Docker用于本地启动Qdrant等中间件。一个统一的Redis实例用来做会话状态缓存和限流计数。配置文件全部走环境变量本地开发用.env文件测试和生产走配置中心。Qdrant本地启动最简单的方式是Docker Compose我直接贴我们用的配置# docker-compose.yml services: qdrant: image: qdrant/qdrant:v1.9.1 ports: - 6333:6333 - 6334:6334 volumes: - ./qdrant_storage:/qdrant/storage启动后访问本机的6333端口打开Dashboard就可以看到集合状态。这里提醒一下Qdrant的Dashboard自带一个简单Web UI用来查看向量集合和测试检索很直观调试知识库的时候非常有用。5.2 搭建最小可用链路从模型网关到Agent执行基础设施搭得对不对要拿一条最小链路来验证。我们的目标是先实现一个最简单的Agent用户提问“华东区上周销量是多少”Agent识别出需要调用查询工具调用MCP Server返回数据最后模型把数据整理成自然语言回复。Step 1确认模型网关可用。先不接Agent直接用Python脚本发一条消息确认模型的对话和工具调用都能通。这一步如果通不过后面全是白费。Step 2启动MCP Server注册数据库查询工具。先用一个虚拟数据库表测试工具调用是否正常。确认工具参数能正确传递返回内容能结构化解析。Step 3用LangGraph把最小的Agent图搭起来。我们第一版只用了三个节点理解意图、调用工具、生成回答。# agent_graph.py 最小链路 from langgraph.graph import StateGraph, END from typing import TypedDict, List class AgentState(TypedDict): query: str tool_calls: List[dict] final_answer: str def understand(state: AgentState): # 调用模型获取工具调用意图 return {tool_calls: extract_tool_calls(state[query])} def run_tool(state: AgentState): # 根据tool_calls调用MCP Server result call_mcp_tool(state[tool_calls]) return {tool_result: result} def answer(state: AgentState): # 把工具结果交给模型生成最终回答 return {final_answer: compose_answer(state)} builder StateGraph(AgentState) builder.add_node(understand, understand) builder.add_node(run_tool, run_tool) builder.add_node(answer, answer) builder.add_edge(understand, run_tool) builder.add_edge(run_tool, answer) builder.add_edge(answer, END) graph builder.compile()Step 4在Spring AI侧搭一个旁路验证服务。如果你团队有Java同事可以用Spring AI的ChatClient快速接同样的模型网关确认两条技术路线都能访问同一套基础设施。Spring AI的配置非常简单spring: ai: openai: base-url: ${LLM_BASE_URL} api-key: ${LLM_API_KEY} chat: options: model: ${LLM_DEFAULT_MODEL}这一步的意义在于验证基础设施的“中立性”不管上层用Python还是Java模型网关和MCP Server都能复用。如果验证通过说明你的地基设计是健康的。5.3 链路联调与效果验证最小链路跑通后一定要做效果验证我管这叫“基线确认”。拿我们LCODER项目组来说会准备一组20到30条覆盖常见问法的测试问题比如简单聚合“上个月总营收是多少”条件过滤“最近7天华东区退货率多少”口径关联“复购率怎么算的按这个口径给出上个季度数据。”跑完链路后重点看三个指标意图理解是否准确、工具调用参数是否正确、最终回答里数字是否和SQL查询结果一致。这三条只要有一条不稳就去查对应层级而不是整体推倒重来。我现在还记得第一次跑通完整链路时看着Agent自己说出“华东区上周销量是8,300件环比增长5.2%”的那个瞬间——虽然中间还有各种问题但那一刻你会觉得整套基础设施的方向是走对了。6. 常见问题与排查技巧实录6.1 高频报错与解决方案速查基础设施搭完到真正好用之间隔着无数个“为什么又报错了”。下面这张表是我把这段时间遇到的问题梳理之后整理的哪条你都用得上。问题现象根本原因解决方案模型返回的结果经常解析失败温度设置太高模型“自由发挥”工具调用相关的请求temperature设为0.1以下工具参数老是传错比如日期格式不对工具Schema写得太复杂嵌套过深尽量扁平化参数复杂结构让模型先生成JSON再二次校验向量检索召回结果明显不相关切分策略不合理或者Embedding模型混用检查文档切分边界统一Embedding模型并重新入库Agent响应超时单次问答里模型调用次数过多给Agent加最大步数限制短问题走后端缓存数据库查询结果超大模型上下文放不下返回结果未截断MCP Server层强制结果截断比如只返回前50行Spring AI接入OpenAI兼容端点报401环境变量未加载或Base URL拼错确认环境变量生效Base URL不要加/v1之外的路径6.2 几个容易踩的隐蔽坑除了上面这些明显的报错还有几个坑是藏得比较深的。第一个坑工具调用的参数Schema不要用复杂的嵌套Object。模型对嵌套结构的支持没有你想象的那么稳尤其是国产模型最稳定的还是扁平化的参数列表。如果确实需要传结构化数据让模型先输出JSON字符串代码里解析后再做校验成功率会高很多。第二个坑向量库的Payload过滤条件一定要和权限系统打通。我们早期只把权限做在MCP Server层结果发现检索阶段就把不该看的文档内容塞进了上下文虽然最终回答前被拦住了但白白浪费了Token还拖慢了速度。后来把部门、数据域标签下沉到向量Payload里查询时直接过滤效果立竿见影。第三个坑模型返回的“正确数字”不一定正确。这是问数项目最要命的一点。模型在总结时可能口算错误比如SQL查出同比是12.3%模型生成回答时写成“12%”倒还好就怕它自己画蛇添足算错数。我们的兜底方案是在回答模板里强制要求模型原样引用工具返回的数字不做二次计算同时链路里加了一个数值校验节点从最终回答里抽取数字和工具结果比对不一致就打回重写。提示排查这类问题最有效的办法是给每次Agent回答生成一个trace_id把模型调用记录、工具返回结果、最终回答全文都串起来。一旦用户反馈数字不对凭trace_id立刻定位是哪个环节出了问题。这个习惯建议从第一天就养成不要等出了问题再补日志。最后再分享一个我在实战里的体会。基础设施搭建最容易走极端一种是把所有东西都做得特别重网关、注册中心、监控告警全套上马结果业务逻辑还没写几行另一种是能省则省全部代码堆在业务模块里等第二、第三个Agent启动时才发现要推倒重来。我的经验是找到中间的平衡点模型网关、工具层抽象、向量检索这三块一定要做扎实因为它们决定了你未来扩展Agent的上限其余的按需添加真正需要的时候再引入不要为了技术而技术。问数项目的基础设施到这里就基本立住了。下一步是Agent的核心逻辑怎么让Agent理解复杂业务问题、怎么拆解多表关联查询、怎么把分析结果讲清楚。这些我们在LCODER系列的下一篇里继续聊如果你也正在搭类似的系统欢迎按上面的思路先跑一遍有不一样的选型心得咱们评论区见。