先给结论:AgentMesh 这类以 AI Agent 为核心、用微服务方式组织的实战项目,最值得研究的不是它“多智能体”的宣传点,而是 Agent 状态管理、工具编排、模型调用和会话存储怎么在分布式系统里落地。它本质上是一个“把大模型接入业务系统”的工程框架,适合已经会 Spring Boot、了解微服务基础、想从写 Demo 进阶到做项目的人。如果你只是调 API 玩聊天,不需要这套东西;但如果你要做带业务流程、会话记忆、工具调用和任务调度的 Agent 平台,这套架构思路可以直接复用。
先说清楚一个容易混淆的点:AgentMesh 不是一个像 ChatGPT 那样开箱即用的产品,它是一个面向服务端集成与二次开发的平台工程底座。也就是说,它解决的主要问题不是“怎么生成一句高质量回答”,而是“在多个微服务环境下,如何把用户的请求可靠地路由到 Agent、让 Agent 去调用工具、维护多轮会话上下文、再把最终结果异步返回给前端”。这种项目更适合从“能不能跑通”和“能不能批量跑”两个维度去验收。
下面我按照从零跑一个微服务 Agent 平台的顺序,把环境、核心模块、代码链路、参数策略、批量处理和排查经验拆开讲。
1. 先理解 AgentMesh 要解决的工程问题
很多初学者一听到 AI Agent 微服务,就默认要把 LangChain、向量库、大模型全部堆到一套系统里。实际上,一旦进入微服务架构,最先要处理的不是“智能”,而是“服务边界”。
1.1 Agent 是业务编排层,不是模型网关
在常规单体应用里,用户请求到了 Controller,然后同步调用一个 LLM SDK,返回结果就结束了。但在 AgentMesh 这类平台里,Agent 不是直接对接大模型的一层,而是夹在业务服务和模型服务之间的编排层。
一个典型的请求链路是这样的:
- 前端发来一个自然语言请求
- 网关服务先做鉴权、限流、路由
- 请求进入 Agent 编排服务
- 编排服务判断这个请求是否需要调用工具,比如查询订单、写入工单、检索知识库
- 如果需要工具,则调用对应的微服务接口
- 全部工具结果收集完成后,再组装成最终 Prompt 发给 LLM
- LLM 返回结果后,Agent 服务把回复、调用日志、会话 ID 持久化
为什么这样设计?因为模型本身不具备执行业务操作的能力,它只能生成文本。所有真正影响数据的操作,比如下单、改状态、发通知,都必须由业务系统执行。Agent 只是根据模型生成的结构化意图,决定“接下来调用哪个服务”。
所以你在看 AgentMesh 代码时,不要只盯着“怎么跟模型聊天”,要把重心放在“模型输出如何映射成服务调用”。这一步做得不好,后面所有环节都是空中楼阁。
1.2 为什么要微服务,而不是单体
如果只是做个人助手,单体完全够用。但 AgentMesh 面向的是多业务域平台场景:用户服务、会话服务、工具服务、模型接入服务、审计日志服务各管一摊。把这些拆开有几个实际好处:
- 模型调用和业务逻辑解耦,换模型时不用改业务代码
- 工具接口可以独立扩展,新增一个工具服务不影响主链路
- 会话存储和消息队列分开,批量任务不会卡用户请求
- 不同服务的资源需求不同,模型推理和数据库操作可以单独扩容
坏处也很明显:开发复杂度高、链路长、排错难。所以我不建议第一次做 Agent 项目的人直接上十几个微服务。先从核心的 4 到 5 个服务起步,比如网关、Agent 编排、工具服务、模型接入、会话存储。
2. 环境准备与前置条件
AgentMesh 类项目对开发机的要求不算低,但只要不跑超大模型,普通配置也能完成开发调试。我自己通常这样划分环境。
2.1 基础软件栈
这里给一套通用组合,具体版本以自己的项目为准,但要保证这些组件齐全:
| 组件 | 用途 | 版本建议 |
|---|---|---|
| JDK | 服务端开发 | 17 或 21 |
| Spring Boot | 微服务基础框架 | 3.x |
| Spring Cloud Alibaba | 服务注册、配置、网关 | 2023.x 或对应版本 |
| Nacos | 注册中心 + 配置中心 | 2.x |
| MySQL | 会话、任务、日志存储 | 5.7 或 8.x |
| Redis | 缓存、分布式锁、临时状态 | 6.x |
| RabbitMQ 或 Kafka | 异步消息、批量任务队列 | 按已有环境选择 |
| Maven | 依赖管理 | 3.8+ |
如果你的机器只有 16G 内存,建议把 MySQL、Redis、Nacos 都装在同一台机器或直接用 Docker Compose 起,不要开太多中间件节点,否则还没写业务代码,机器就卡死了。
2.2 模型接入准备
AgentMesh 的模型层通常通过 Spring AI 这类框架做统一封装。在开始之前,你需要准备一个可用的 LLM 接口。这个接口可以是云端 API,也可以是本地部署的开源模型。
如果你是学习用途,建议先接云端 API,把链路跑通后再考虑本地模型。本地模型的好处是数据不出内网,但代价是显存和推理速度。一个 7B 模型在消费级显卡上勉强能用,并发稍微上来就很容易超时。
我不建议第一次做这个项目时就同时接多家模型。先接一个,跑通 Agent 编排,再抽象统一接口,慢慢加其他模型。
2.3 启动顺序的讲究
微服务项目最容易犯的错就是“一次性启动全部服务”,结果一屏报错完全不知道该看哪个。更稳妥的顺序是:
- 先启动 Nacos,确认注册中心可用
- 再启动 MySQL、Redis,确认数据源和缓存正常
- 启动基础服务,比如用户服务、会话服务
- 启动模型接入服务,确认模型接口连通
- 最后启动网关和 Agent 编排服务
每启动一个服务,就看一下 Nacos 的服务列表,确认服务注册成功再启动下一个。这样即使启动报错,也能快速定位是哪一层的问题。
注意:不要在还没确认数据库连接的情况下就启动 Agent 服务。Agent 服务启动时通常会初始化表结构、缓存连接和工具注册列表,数据库不可用会导致它反复重启。
3. 项目模块设计与基础骨架
AgentMesh 的模块划分建议按照业务边界来,而不是按技术层来。下面是一份通用的模块参考,不是所有项目都必须照抄,但对理解整体架构很有帮助。
3.1 服务模块一览
| 服务名 | 职责 | 关键功能 |
|---|---|---|
| agent-gateway | 统一网关 | 鉴权、限流、路由、日志 |
| agent-server | Agent 编排中心 | 会话管理、工具编排、Prompt 组装、LLM 调用 |
| agent-tool-server | 工具服务 | 提供可被 Agent 调用的业务接口 |
| agent-model-server | 模型接入服务 | 统一封装不同模型 API,处理超时和重试 |
| agent-session-server | 会话存储服务 | 会话历史、上下文窗口、向量存储 |
| agent-task-server | 任务服务 | 异步任务、批量任务、定时触发、失败重试 |
主要目录可以这样组织:
AgentMesh/ ├── agent-gateway/ ├── agent-server/ ├── agent-tool-server/ ├── agent-model-server/ ├── agent-session-server/ ├── agent-task-server/ ├── agent-common/ │ ├── result/ │ ├── exception/ │ ├── context/ │ └── utils/ └── agent-api/ ├── dto/ ├── enums/ └── feign/agent-common放公共返回结构、异常处理、工具类;agent-api放服务间调用的 DTO 和 Feign 接口。这个拆分很重要,因为多个服务之间要共享请求参数和返回结构,如果每个服务各写一份,很容易字段不一致。
3.2 数据表设计要提前想好的地方
Agent 平台的数据表比普通业务系统更依赖状态和日志。除了用户表,下面几张表很关键:
- 会话表:记录会话 ID、用户 ID、会话标题、创建时间、最近活跃时间
- 消息表:记录每条消息的角色、内容、消息类型、关联会话 ID、令牌数
- 任务表:记录异步任务状态、任务类型、输入参数、输出结果、失败原因
- 工具调用记录表:记录 Agent 每一次调用工具的参数、返回结果、耗时
在设计时,状态字段不要用无意义的 0 和 1,建议用字符串枚举,比如 WAITING、RUNNING、SUCCESS、FAILED。这样日志和排查时一眼就能看懂。
另外,消息表会随着多轮会话快速增长。如果不做归档,查询历史会话时会越来越慢。建议按时间做分区,或者把冷数据定期同步到归档表。
4. Agent 编排核心链路实战
这一节是整篇的重点。AgentMesh 的 Agent 编排服务是整个系统的控制中枢,核心逻辑可以拆成五步:接收请求、判断是否需要工具、调用工具、组装最终 Prompt、返回结果。
4.1 从 HTTP 入口到 Agent 编排
网关把请求转发到 agent-server 后,在 controller 层不会做太多业务逻辑,只负责参数校验和调用编排服务。
@RestController @RequestMapping("/agent") public class AgentController { private final AgentOrchestrator orchestrator; public AgentController(AgentOrchestrator orchestrator) { this.orchestrator = orchestrator; } @PostMapping("/chat") public Result<AgentResponse> chat(@RequestBody AgentRequest request) { String userId = UserContext.getUserId(); AgentResponse response = orchestrator.execute(userId, request); return Result.success(response); } }这里的重点不是 controller,而是AgentOrchestrator。编排器决定了整个 Agent 的执行流程。
@Service public class AgentOrchestrator { private final ToolRegistry toolRegistry; private final ModelService modelService; private final SessionService sessionService; public AgentResponse execute(String userId, AgentRequest request) { // 1. 读取或创建会话状态 Conversation conversation = sessionService.getOrCreate(userId, request.getSessionId()); // 2. 先让模型判断是否需要工具调用 String input = request.getInput(); AgentDecision decision = modelService.decide(input, conversation.getRecentMessages()); // 3. 如果需要工具,则执行工具并收集结果 List<ToolResult> toolResults = new ArrayList<>(); if (decision.isNeedTool()) { for (ToolCall call : decision.getToolCalls()) { ToolResult result = toolRegistry.execute(call); toolResults.add(result); } } // 4. 组装最终 Prompt 并调用模型生成回复 String finalPrompt = PromptBuilder.build(conversation, decision.getReasoning(), toolResults); String answer = modelService.generate(finalPrompt); // 5. 持久化消息 sessionService.saveMessage(userId, conversation.getId(), "user", input); sessionService.saveMessage(userId, conversation.getId(), "assistant", answer); return AgentResponse.builder() .sessionId(conversation.getId()) .answer(answer) .toolResults(toolResults) .build(); } }这里有个容易被忽略的设计点:模型先输出一个“决策结果”,这个结果可能是普通文本,也可能是结构化工具调用指令。在实际项目里,我会定义一个统一的AgentDecision结构,里面包含reasoning、needTool、toolCalls三个字段。
reasoning是模型思考过程的简短文本,一般只在调试时记录,不返回给用户needTool表示这次请求需不需要调用工具toolCalls包含要调用的工具名和参数
如果没有这个中间层,直接把原始模型输出拿来解析,容易因为格式不稳定而出错。
4.2 工具注册与执行机制
工具是 Agent 与业务系统交互的桥梁。每个工具本质上就是一个可以被模型选择的“函数”,但它在代码里的形式需要统一,方便注册和调用。
我建议定义一个工具接口,所有工具都实现该接口:
public interface AgentTool { String getName(); String getDescription(); ToolSchema getSchema(); ToolResult execute(Map<String, Object> params); }getName是工具标识,比如query_ordergetDescription是给模型看的说明,模型根据这个描述决定是否使用工具getSchema描述工具需要哪些参数,供模型生成结构化调用execute是真正执行逻辑
工具注册可以用 Spring 的ApplicationContext自动收集,也可以用ToolRegistry手动登记:
@Component public class ToolRegistry { private final Map<String, AgentTool> tools = new ConcurrentHashMap<>(); public void register(AgentTool tool) { tools.put(tool.getName(), tool); } public ToolResult execute(ToolCall call) { AgentTool tool = tools.get(call.getToolName()); if (tool == null) { return ToolResult.failure("tool not found: " + call.getToolName()); } try { return tool.execute(call.getParams()); } catch (Exception e) { return ToolResult.failure("tool execute error: " + e.getMessage()); } } }这个注册机制最重要的作用,是让“模型能看到的工具列表”和“代码里真正能执行的工具”保持一致。如果模型描述了一堆工具,代码里根本没有实现,调用时就会报错。我一般在启动时会把已注册的工具列表打印出来,确认注册数量和预期一致。
4.3 工具调用失败时怎么办
工具调用不是百分之百成功的。数据库可能超时、权限可能不足、参数可能不合法。在 Agent 编排里,工具失败后不应该直接让整个请求失败,而是应该把错误信息作为上下文,让模型自己决定怎么处理。
我的做法是:当ToolResult返回失败时,不中断流程,而是把这段失败信息拼到最终 Prompt 里。比如:
工具 query_order 调用失败,原因:orderId 不能为空。 请根据这个错误帮助用户检查输入。这样模型就能生成一个更自然的应对,用户看到的不再是“系统错误”,而是“请确认订单号是否有误”。
但如果工具连续失败多次,说明可能是系统级问题,而不是参数问题。这时候要设置重试上限和熔断机制,避免模型反复调用一个必然失败的工具。
5. 会话状态与上下文管理
Agent 平台不能像普通接口那样无状态。用户上一句说“帮我查订单”,下一句说“把价格最高的那个取消”,模型必须理解“那个”指的是哪条订单。所以会话状态管理是 AgentMesh 的核心基础设施。
5.1 会话 ID 的生成和传递
会话 ID 最好由服务端生成,使用 UUID 或雪花算法。不建议用自增 ID,因为并发环境下容易出现重复,而且暴露业务量。
在首次对话时,前端可以不传 sessionId,由 agent 服务创建后返回。后续对话必须带上同一个 sessionId,服务端根据这个 ID 拉取历史消息。
会话 ID 的传递要注意:不要把一次请求的 ID 和会话 ID 混用。一次请求关心的是本次调用的 requestId,用于日志追踪;会话 ID 关心的是用户身份和时间范围,用于上下文管理。两者职责不同。
5.2 上下文窗口怎么截断
大模型有上下文窗口限制,不管支持 128K 还是 200K,都不能无限塞历史消息。从工程角度看,我一般分三个级别管理上下文:
- 短期记忆:最近 N 轮对话直接放进 Prompt
- 中期摘要:超过轮数后,由模型把早期消息生成一段摘要
- 长期知识:需要查外部知识库或向量库,按需检索
最简单的方式是只保留最近 10 到 20 轮消息。第一次实现时建议先用这种方案,代码简单,效果也还行。等跑通了,再考虑摘要和向量检索。
截断策略里最容易出的问题是:token 统计和截断顺序不一致。有的模型按 token 计费,有的按字符计费;如果统计方式不统一,可能出现本地看起来没超限,调用模型时报超长错误。
我一个比较稳妥的做法是:在PromptBuilder里预留一个 Tokenizer 接口,先按字符估算,等接入具体模型后再换成对应模型的 Tokenizer。不要一开始就写死“2000 个字符”,因为不同模型的分词差异很大。
5.3 会话存储选型
会话存储可以简单拆成两层:
- 热数据:最近一小时活跃的会话,用 Redis 缓存,用 TTL 维护过期
- 冷数据:完整历史记录,存入 MySQL 或 MongoDB
查询时,先用 Redis 拿最近消息,如果没有再查数据库。这样既能保持响应的速度,又不会让数据库承受所有读取压力。
但要注意缓存和数据库的一致性。在保存消息时,我先写数据库,再更新 Redis 缓存。如果先更新缓存再写库,写库失败就会导致缓存里有用户看不到的假消息。
6. 模型接入层设计
模型接入层是 agent-model-server 的核心职责。它的作用不是写死在某个模型的 SDK 调用里,而是通过适配器模式,让上层 Agent 编排服务不感知具体模型。
6.1 统一模型接口
建议定义这样一个接口:
public interface ChatModelAdapter { String modelType(); ChatResponse chat(ChatRequest request); }每一个模型对应一个实现类,比如OpenAiAdapter、QwenAdapter、LocalModelAdapter。modelType返回模型标识,比如openai、qwen、local。
在 Agent 编排服务里,通过一个工厂类按配置选择具体适配器:
@Service public class ModelFactory { private final Map<String, ChatModelAdapter> adapterMap; public ModelFactory(List<ChatModelAdapter> adapters) { this.adapterMap = adapters.stream() .collect(Collectors.toMap(ChatModelAdapter::modelType, a -> a)); } public ChatModelAdapter getAdapter(String modelType) { return adapterMap.get(modelType); } }这样如果以后要接入新模型,只需要新增一个适配器实现类,不用改编排服务的主逻辑。
6.2 超时、重试和限流
模型接口是最不稳定的外部依赖。必须设置超时时间,我一般会这样设:
- 连接超时:5 秒
- 读取超时:30 到 60 秒
- 重试次数:1 到 2 次
- 重试间隔:指数退避,比如 1 秒、2 秒、4 秒
为什么不能无限重试?因为大模型接口并发压力大,如果系统里面多个用户同时触发重试,很容易把服务拖垮。重试只适合临时网络抖动,对持续超时没有帮助。
限流也是必须的。建议在网关层对每个用户 ID 做 QPS 限制,在模型接入层对每个 API Key 做并发限制。双层限流可以防止单用户刷接口拖垮整个平台。
6.3 结构化输出解析
模型输出并不总是稳定格式。当你要求模型输出 JSON 时,它有可能输出多余的 Markdown 标记、注释,或者把 JSON 包在代码块里。
所以在ModelService里,必须有一段容错解析逻辑:
- 优先尝试直接解析 JSON
- 如果失败,去掉开头和结尾的代码块标记
- 如果还失败,用正则提取第一个
{到最后一个}之间的内容 - 仍然失败时返回给模型一段纠错 Prompt,让它重新生成
这一步非常重要。工具调用决策一旦依赖模型输出格式,解析就必须足够健壮。我在实测时发现,很多“Agent 不工作”的问题,最终都出在格式解析上,而不是模型本身能力不行。
7. 批量任务与异步处理
Agent 平台真正从 Demo 走向生产,一定会遇到批量需求:给一批用户发送定制报告、对一批历史工单做自动分类、定时拉取数据后生成摘要。这些任务不能在 HTTP 请求线程里同步处理,必须交给消息队列和任务服务。
7.1 为什么不建议直接用并发循环
有些初学者看到批量任务,第一反应是开一个 for 循环,里面用线程池并发调用模型。这种方式在小数据量时能跑,但面临几个问题:
- 进程重启后,任务丢失
- 中途失败,不知道从哪个继续
- 没有统一的进度记录
- 多个实例部署时,线程池各自为政,可能重复消费
所以只要任务量超过几十条,就应该走队列和任务表。
7.2 任务表的核心字段
一个批量任务拆成两层:任务批次和原子任务。
- 任务批次:记录这次批量任务的类型、总量、成功数、失败数、开始时间、结束时间
- 原子任务:记录每一条数据的输入、输出、状态、失败原因
原子任务表至少要有这些字段:
| 字段 | 作用 |
|---|---|
| task_id | 批次 ID |
| item_id | 原子任务 ID |
| status | WAITING / RUNNING / SUCCESS / FAILED |
| input_data | 输入参数,JSON 格式 |
| output_data | 输出结果,JSON 格式 |
| error_msg | 失败原因 |
| retry_count | 重试次数 |
| next_retry_time | 下次重试时间 |
为什么要把输入输出存成 JSON?因为批量任务的输入类型可能很多,用独立的业务字段不灵活。JSON 把所有可能情况都装下了,查询具体字段时可以再用数据库函数解析,或者结合实际需求单独提取列。
7.3 批量任务消费流程
任务服务的消费流程可以按这个步骤设计:
- 接收任务批次消息
- 把每条数据拆成原子任务,写入任务表,状态 WAITING
- 消费者按一定数量批量拉取 WAITING 任务
- 调用模型服务或工具服务处理每条任务
- 成功后更新原子任务状态为 SUCCESS
- 失败时根据重试次数决定直接失败还是延迟重试
- 每完成一个原子任务,就在 Redis 里更新该批次的进度
进度信息可以用INCR原子递增来实现。前端定时拉取批次进度,就能展示“已完成 30/100”这类信息。
批量任务不要追求“一次处理完”,要能随时中断、恢复、重跑。把状态设计好了,出现失败时直接查数据库就能知道卡在哪。
8. 生产化落地与边界条件
跑通核心链路之后,还要处理一些容易影响稳定性和合规性的问题。下面这几点,是很多人第一次做 Agent 平台时容易忽略的。
8.1 内容安全与模型输出过滤
Agent 虽然不像普通聊天产品那样面向海量 C 端用户,但在业务系统里也存在内容安全要求。我的做法是在模型接入层和网关层各做一道过滤:
- 网关层:对用户输入做长度限制、频率限制、敏感词预检
- 模型接入层:对模型返回内容做合规过滤,确保不包含违规信息
如果这是企业内部系统,还需要考虑数据权限。用户可能有权查询订单,但未必有权查询财务数据。Agent 在使用工具时,必须把用户 ID 作为参数传给业务工具服务,由工具服务判断权限,不能让 Agent 通过拼接参数绕过权限。
8.2 审计日志
Agent 平台的审计日志比普通 CRUD 系统更重要。因为模型是不可完全预测的,一旦发生误操作或错误回答,必须能追溯当时的输入、输出、工具调用和决策依据。
建议在两个节点记录日志:
- 请求开始:用户 ID、会话 ID、输入内容、请求时间
- 请求结束:输出内容、工具调用列表、每个工具调用耗时、模型名称、token 消耗、错误信息
日志不要只打到大盘,建议通过消息队列异步写入专门的日志表或对象存储。如果直接在请求链路里写日志表,高峰期会对数据库产生额外压力。
8.3 部署与资源建议
AgentMesh 类项目如果用 Docker Compose 部署,可以从这样划分:
- 基础组件:Nacos、MySQL、Redis、RabbitMQ
- 应用服务:agent-gateway、agent-server、agent-tool-server、agent-model-server、agent-session-server
- 可选组件:日志收集、监控告警、对象存储
内存紧张时,优先保障 Nacos、Redis 和 agent-server。agent-server 是 Agent 编排的核心,承担的线程和对象最多。agent-model-server 如果并发不高,可以先跟 agent-server 共用实例,但生产环境不建议。
模型服务如果使用本地模型,需要单独部署在 GPU 机器上,与应用服务分离。不要把推理服务和应用服务混布,因为 CPU 和 GPU 的资源抢占会互相影响。
8.4 现有微服务项目怎么接入
如果你已经有一个普通微服务项目,比如基于若依微服务版本改造过的系统,不需要把整个项目推倒重来。常见做法是新增两个模块:
- agent-tool:把自己项目里的业务接口封装成 Agent 可调用的工具
- agent-server:部署 Agent 编排服务,主要负责判断何时调用工具
这么做的好处是业务域代码不用大改,Agent 服务只依赖工具接口,不会侵入原有数据库和事务边界。但要注意,工具接口的入参出参最好设计成通用 JSON,而不是原有系统的内部 DTO。因为 Agent 调用工具时参数通常来自模型生成,如果内部 DTO 太严格,解析和兜底成本会很高。
9. 常见报错与排查链路
最后整理一份排错顺序。无论你遇到什么报错,先别急着改代码,按这个链路走,大概率能定位问题。
9.1 请求发出去没有响应
- 先看 agent-gateway 日志,确认请求是否到达网关
- 再看 Nacos,确认服务是否注册成功
- 查看 agent-server 日志,确认请求是否进入编排服务
- 查看模型接入日志,确认 LLM 调用是否超时
- 最后看 Redis,确认缓存和会话状态是否正常
这一类问题最常见的根因是:服务之间通过 Feign 调用时有超时或序列化错误,但上层日志没有打印完整堆栈。先把日志级别调成 DEBUG,再复现一次。
9.2 模型返回格式解析失败
- 查看模型原始输出,确认输出是否包含无关内容
- 确认 Prompt 中是否有明确的输出格式要求
- 确认解析逻辑是否覆盖了代码块标记情况
- 尝试用更严格的输出约束方式,比如要求模型只输出 JSON,不要附加解释
很多模型在温度较高时会输出多余解释,把 temperature 调低到 0.1 到 0.3,格式稳定性会明显提升。
9.3 工具调用提示 not found
- 确认工具实现的
getName()返回值是否唯一 - 确认工具类是否被 Spring 扫描并在启动时完成注册
- 确认模型决策时传入的工具描述列表是否来自同一个注册表
- 建议启动时打印工具注册列表,和模型能看到的列表做比对
这种问题大多数不是模型能力问题,而是注册和描述两边信息不一致。
9.4 批量任务积压并且速度越来越慢
- 先看任务表,确认失败任务是不是一直在重试
- 再看模型接口的限流情况,确认是否触发了 API 限流
- 看消费者线程池配置,确认并发线程数和任务量是否匹配
- 看数据库连接池,确认是否连接耗尽
批量任务的并发并不是越大越好。模型接口有 QPS 限制,数据库有连接上限,线程池有 CPU 瓶颈。建议从 5 个并发开始,逐步增加,观察拐点在哪里。
做 AgentMesh 这类项目时,最容易被干扰的是“大模型很厉害”这个预期。实际落地时,模型能力当然重要,但决定平台稳不稳的,往往是会话状态是否完整、工具调用是否可靠、任务失败能否恢复这些工程细节。先把单条链路跑通,再慢慢把批量和并发做起来,这个项目的学习价值才能真正体现出来。