从0实现OnCall基于Python语言框架

news2026/3/18 16:27:21
Step01第一步做的事情先把 Python 版 OnCall 的后端外壳搭起来。也就是说先验证了一件最关键的事这个项目能不能先以 Python 服务的形式真正跑起来并且具备最基础的对外通信能力。只有这一步成立后面接模型、接 RAG、接工具调用才有意义。从工程角度看完成的是一个最小可运行骨架。创建了基础目录明确了入口文件、路由文件、请求响应结构和 SSE 工具函数这些最核心的位置。这一步的价值不在于功能多而在于它把项目从“想法”变成了“有结构的代码工程”后面继续扩展时就不会一开始全堆在一个文件里。在服务层面把 FastAPI 服务真正启动起来了并且有了main.py作为统一入口。这说明 Python 版 OnCall 已经不再停留在方案阶段而是有了一个可以启动、可以监听端口、可以对外提供接口的实际运行体。在接口层面固定了两个最重要的入口一个是普通的/api/chat一个是流式输出的/api/chat_stream。这一步非常关键因为它相当于先把未来系统最核心的对外协议钉住了。后面不管你内部是 mock、是真实模型、还是再加上 RAG 和工具前端调用方式都可以尽量保持不变这样整个迁移过程会稳定很多。在功能层面虽然现在还没有接入真实模型但已经让/api/chat能正常接收请求并返回一段 mock 内容了。这说明请求解析、参数校验、路由分发和 JSON 响应这一整条基础链路是通的。换句话说已经证明前端发一个聊天请求Python 后端能接住并给出结构化结果这件事是成立的。更重要的是/api/chat_stream做通了也就是把 SSE 流式输出先跑起来了。这一步对 OnCall 这种项目尤其关键因为后面无论是模型逐 token 输出还是工具调用过程中的中间状态返回都很可能依赖流式机制。现在先把start、token、done这种基本事件流走通相当于把后面最容易出问题的一层提前验证掉了。最后通过实际请求验证了当前这套最小骨架是有效的。也就是说/health、/api/chat和/api/chat_stream都已经不是理论存在而是已经经过真实测试、能够返回结果的接口。这代表第一步已经形成了一个完整闭环服务能启动请求能进来结果能出去流式输出也能工作。所以如果把第一步浓缩成一句话来说那就是已经从零搭出了 Python 版 OnCall 的最小后端运行底座并验证了普通 HTTP 和 SSE 两条最基础的通信链路都能正常工作。这一步虽然还没有真正实现 AI 能力但它已经把后面所有开发要依赖的地基先打稳了。Step02第二步做的事情核心不是新增更多接口而是把第一步那个能跑的 demo 外壳进一步整理成一个有分层的后端结构。如果说第一步解决的是Python 服务能不能启动、接口能不能通那么第二步解决的就是代码以后还能不能继续扩展而不至于越来越乱。第二步最重要的变化是把原来直接写在路由里的 mock 聊天逻辑抽离出来放进一个单独的ChatService里。这样一来路由层就不再负责具体怎么生成回复而只负责接收请求、调用服务、返回结果。这个拆分的意义非常大因为后面你要接真实模型、接 RAG、接工具调用其实都应该发生在 service 层而不是堆在 API 路由函数里。除了ChatService第二步还引入了一个最简单的SessionStore也就是会话存储。它的作用是先把同一个session_id下的历史消息保存起来让系统开始具备最基础的多轮对话记忆”能力。虽然这个版本只是内存存储进程一重启就会丢失但对现阶段来说已经足够了因为它先帮你验证了一件事Python 版 OnCall 不只是单轮问答接口而是已经开始具备会话状态这个后续智能体系统必需的基础能力。从项目结构上看第二步的意义在于开始引入真正的分层。原来项目里只有入口、路由、schema 和 SSE 工具逻辑基本都贴在接口附近到了第二步项目开始出现services/和infra/memory/这样的目录这代表你已经不再把所有东西都放在 API 层而是把“业务逻辑”和“底层存储”往不同层拆开了。虽然现在规模还很小但这种结构一旦立住后面继续接入模型、Milvus、Redis、工具系统时整个工程就会顺很多。第二步还带来了一个很重要的效果就是开始从“接口 mock”过渡到“业务 mock”。第一步的 mock 更像是在证明接口通不通而第二步的 mock 已经开始模拟真实业务行为了。因为现在回复内容不只是简单返回一段固定文本而是可以结合当前会话历史数量来构造结果这说明系统内部已经开始有状态、有上下文而不是纯静态输出。从演进路径上来说第二步其实是在给第三步做准备。因为一旦ChatService已经存在下一步你要做的事情就非常清晰了把ChatService里原本的 mock 拼字符串逻辑替换成真实的 LLM 调用再往后可以在这个 service 里继续插入 RAG 检索、工具调用、提示词构建等能力。也就是说第二步本身做的事情不一定最“炫”但它是整个 Python 版 OnCall 从“能跑”走向“可持续开发”的真正起点。所以如果把第二步浓缩成一句话它做的事情就是把第一步跑通的最小聊天接口进一步整理成“路由层 服务层 会话存储层”的结构让 Python 版 OnCall 开始具备最基础的业务分层和多轮会话能力。Step03第三步做的事情核心就是把前面那个“结构已经搭好、但回复还是假的聊天系统”正式升级成了一个会真实调用大模型的聊天系统。如果说前两步是在搭外壳、理结构那么第三步就是第一次把“智能能力内核”真正接进来让 Python 版 OnCall 不再只是返回本地拼出来的 mock 文本而是开始根据用户输入向真实模型发请求并拿回结果。这一阶段最重要的变化是你新增了一个独立的模型适配层。也就是说模型调用不再直接散落在路由或 service 代码里而是被封装进了infra/llm/openai_compatible.py这样的文件中专门负责和 OpenAI 兼容接口通信。这一步的意义非常大因为它把“业务逻辑”和“模型供应商实现细节”分开了。以后无论你换模型地址、换模型名字甚至换一家兼容 OpenAI 协议的服务商都主要改这一层而不用把整个项目都改一遍。与此同时你还补上了配置层也就是config.py和.env这部分。这样模型的base_url、api_key、model名称这些信息就不再写死在代码里而是统一从配置读取。这一步虽然看起来不像“功能开发”但它实际上是在把项目从“临时试验代码”往“真正可持续维护的工程”推进。因为只要配置和代码分离了后面开发、测试、部署就会清晰很多。在业务层面第三步真正改造的是ChatService。前面第二步里ChatService只是基于 session 和用户输入拼一个 mock 回复到了第三步它开始承担更真实的职责了先读取当前session_id对应的历史消息再加上 system prompt把这些内容组织成模型需要的messages格式然后调用 LLM 适配器获取真实回答最后再把用户消息和模型回复一起写回会话历史。也就是说从这一刻开始你的系统已经不只是“能保存上下文”而是“会带着上下文去和模型对话”。第三步还把流式接口也从假流升级成了真流。前面/api/chat_stream是本地逐字输出一段 заранее 准备好的字符串本质上只是模拟 SSE而现在它已经变成真正调用模型的流式接口持续接收模型返回的增量内容再通过start、token、done这些事件发给前端。这一步非常关键因为它说明你的 OnCall 不只是支持“普通调用模型”而是已经具备了后续 Agent、工具调用、RAG 流式展示这些高级能力所依赖的基础输出模式。从整体结果上看第三步完成后系统内部第一次形成了一个真实的 AI 闭环用户请求进入路由路由调用ChatServiceChatService组织消息并调用模型模型返回真实内容再把结果写进 session history最后由接口返回给用户。这和前两步最大的区别在于前两步解决的是“程序骨架和工程结构对不对”而第三步解决的是“这个系统是不是真的开始像一个 AI 系统那样工作了”。所以如果把第三步浓缩成一句话它做的事情就是把 Python 版 OnCall 从一个“有结构的 mock 聊天后端”升级成了一个“有结构、可配置、支持上下文、支持流式输出的真实大模型聊天后端”。这一阶段做完以后后面的第四步就顺理成章了因为你已经有了模型调用能力接下来只需要在模型调用之前插入检索逻辑就可以把系统继续升级成带 RAG 的 OnCall。Step04第四步做的事情核心就是把第三步那个“已经能真实调用大模型聊天”的系统进一步升级成了一个带知识库检索能力的 RAG 系统。如果说第三步解决的是“模型能不能真正回答”那么第四步解决的就是“模型能不能在回答之前先去查本地知识再基于知识回答”。这一步做完之后Python 版 OnCall 就不再只是一个通用聊天后端而是开始具备“面向特定知识库做增强问答”的能力了。这一阶段最重要的新增能力是文件上传和知识入库链路。也就是说系统不再只接收用户的聊天请求还新增了一个上传入口用来接收本地文档。上传之后文件不会只是简单保存下来而是会继续经过一条完整的数据处理流程先读取文件内容再对文本做切分然后调用 embedding 模型把每个文本片段转成向量最后把这些片段连同向量一起写入 Milvus。这个过程的意义在于它把原本“人类可读的文档”转成了“系统可以检索的知识单元”。为了让这条链路成立第四步还引入了 embedding 层和向量存储层。embedding 层负责把文本变成向量也就是把自然语言内容转换成模型可以用于相似度检索的数值表示。向量存储层则负责把这些向量和对应的文本片段保存起来并在后续查询时根据用户问题去做相似度搜索。你这里用的是 Milvus本地开发阶段走的是 Milvus Lite所以这一步实际上也意味着你的项目已经从“只有大模型接口”扩展成了“模型 向量库”的双基础设施架构。第四步另一个关键变化是在聊天链路里插入了检索逻辑。也就是说ChatService不再只是把历史消息和当前用户问题直接送给大模型而是多了一步当use_ragtrue时先用用户问题生成 query embedding再到 Milvus 里查找最相关的知识片段把这些片段组织成上下文信息然后再一起送给模型。这样一来模型的回答就不再只依赖自身训练时学到的通用知识而是会优先基于你本地上传的知识内容来回答。从行为上看这一步带来的变化非常明显。开启 RAG 时模型回答会明显引用知识库中的具体信息并且返回结果里会带上references和trace表示它命中了哪些知识片段、检索到了多少条结果。关闭 RAG 时模型就退回到自身的通用能力只能给出更泛化、更经验性的回答。你实际做的对照测试已经很好地证明了这一点在开启 RAG 时回答会直接贴着你上传的 runbook 内容走关闭 RAG 时回答就变成了一个泛化的 5xx 排障思路。这说明第四步并不是“看起来像加了知识库”而是知识检索真的已经在回答流程里生效了。从工程结构上说第四步不只是“多加了几个功能”而是让整个项目开始出现了更完整的 AI 后端形态。原来系统里只有 API、service、session、LLM 适配器到了第四步项目里又增加了 loader、splitter、embedding adapter、Milvus store、RAG service、ingestion service 这些模块。也就是说Python 版 OnCall 已经开始从一个普通聊天服务演进成一个真正意义上的“知识增强智能体后端”。如果把第四步浓缩成一句话它做的事情就是为 Python 版 OnCall 建立了一条从文件上传、知识入库到检索增强回答的完整 RAG 闭环。这一步完成之后你已经具备了两种能力一种是纯模型问答另一种是基于本地知识库的增强问答。也正因为这一步已经完成接下来的第五步才会很自然地变成“接入工具调用”因为现在系统已经有了聊天能力也有了知识能力下一步就该给它加上“主动调用外部工具”的执行能力了。Step05第五步做的事情核心就是在前面已经具备“普通聊天”和“RAG 检索增强问答”能力的基础上再往系统里加入了一层工具调用能力。如果说前四步的系统本质上还是“用户问模型答”那么第五步之后系统开始具备一种新的行为模式模型不再只是单纯根据上下文生成答案而是可以在判断“需要外部实时信息”时先请求调用某个工具等工具返回结果后再基于这个结果组织最终回答。也就是说系统第一次拥有了“借助外部能力完成回答”的执行型能力。这一阶段最重要的新增内容是你建立了一个最小的工具系统。它并不是一开始就接入日志、告警、数据库这些复杂工具而是先用get_current_time这个非常简单的工具打通了整条工具调用闭环。这样做的重点并不在于“获取时间”这个功能本身而在于验证整个协议和流程是否成立模型能不能识别自己需要一个工具后端能不能执行这个工具工具执行结果能不能被重新送回模型以及模型能不能基于工具结果生成自然语言答案。也就是说第五步的真正价值在于“让系统第一次学会工具调用”而不是某一个具体工具有多复杂。为了让这件事成立第五步还新增了工具注册中心也就是ToolRegistry。这一步的意义在于你不再是零散地写几个 Python 函数而是开始把工具当成一种正式的系统能力来管理。注册中心一方面负责把工具的名字、描述和参数 schema 转换成模型能读懂的格式另一方面负责在模型返回工具调用请求后根据工具名找到对应的工具实现并执行。这样一来系统里第一次出现了一种“工具协议层”后面继续扩展日志工具、告警工具、内部文档工具时都会沿用这个统一框架而不是各写各的。与此同时第五步也真正改造了模型调用层。原来第三步和第四步中的 LLM 适配器本质上只需要处理“发消息给模型再收回文本答案”这类单轮行为到了第五步LLM 适配器开始支持tools和tool_calls这些结构化字段。也就是说模型返回的结果已经不再只是“纯文本”而可能是一种“我要调用某个工具”的结构化请求。这代表你的系统开始从“文本交互型”演进到“文本 结构化动作请求”混合型交互这一步其实非常关键因为它是从普通聊天系统走向 Agent 系统的第一个真实转折点。在业务层面第五步最核心的变化发生在ChatService。前面第四步中ChatService的职责主要是拼好历史消息和 RAG 上下文然后把它们送给模型再拿回答案。而到了第五步ChatService里新增了一层最小的 tool loop也就是一个小型循环机制先发起第一轮模型请求如果模型直接给出最终答案就结束如果模型返回tool_calls那就进入工具执行逻辑执行完成后把工具结果作为新的消息继续发给模型再让模型基于工具结果生成最终自然语言回答。这个循环虽然现在还比较简单但它已经让系统开始具备“思考是否要借助外部工具”的能力这本质上已经是一个最小 Agent 行为了。第五步还对流式接口做了升级。虽然这一版还没有做到“工具调用过程与模型 token 真正完全混合式的实时流”但它已经能把工具相关的过程通过 SSE 事件流发给前端比如tool_call、tool_result然后再继续输出最终答案的 token。这意味着你的前端以后不只是能显示“模型在回答什么”还可以显示“模型刚刚调用了哪个工具、拿到了什么结果”。从 OnCall 场景来说这一点非常重要因为在一个运维、排障、分析类系统中用户往往不仅关心最终答案还关心这个答案是怎么来的调用了哪些外部能力这样系统的行为才更可解释、更可信。从整体效果上说第五步完成后系统已经不再只是一个“会聊天、会查知识库”的后端了而是开始具备最基础的“执行能力”。前四步解决的更多是知识和表达问题系统能不能基于上下文和知识回答用户而第五步开始解决的是动作问题系统能不能在需要外部实时事实时不靠猜而是主动请求工具来获取信息。这就是为什么第五步虽然只先接了一个简单的时间工具但它在系统演进上的意义其实非常大因为它第一次让 Python 版 OnCall 从“增强问答系统”走向了“最小可执行智能体系统”。如果把第五步浓缩成一句话它做的事情就是在现有聊天与 RAG 系统之上建立了一个最小可用的工具调用闭环让模型能够根据需要触发工具、获取外部信息并基于工具结果生成最终回答。也正因为这一步已经完成后面的第六步就会非常自然你不需要再重新设计工具框架而只需要沿着这一套机制继续增加更符合 OnCall 场景的工具比如日志查询、告警查询、内部文档查询、数据库只读查询等。换句话说第五步真正搭好的不只是一个时间工具而是整个 OnCall 后续工具体系的底座。Step06第 6 步做的事情核心就是把第 5 步那个“只有一个时间工具的最小工具调用系统”扩展成了一个更贴近 OnCall 场景的多工具系统。如果说第 5 步是在验证“模型会不会调用工具”那么第 6 步解决的就是“模型能不能在不同的运维排障场景里选择合适的工具来辅助分析问题”。这一步做完以后系统已经不只是会聊天、会查知识库、会查时间而是开始具备了面向真实 OnCall 问题调用多种外部能力的基础。这一阶段最重要的变化是新增了三个真正有业务意义的工具内部知识库查询工具、告警查询工具和日志查询工具。内部知识库工具的作用是让模型在用户明确要求“查知识库”“查 runbook”“查内部文档”时主动调用现有的知识检索能力而不是仅仅依赖主链路里的use_ragtrue开关。告警工具的作用是让模型能够针对“当前有哪些 firing 告警”“gateway 最近的 critical 告警有哪些”这类问题主动获取结构化告警数据。日志工具则是让模型可以针对“最近有没有 timeout”“有没有 5xx 相关 ERROR 日志”这类问题主动查询日志并基于日志做进一步总结。也就是说第 6 步让工具系统开始从演示性质走向真正的运维场景应用。为了支撑这些工具第 6 步并没有推翻第 5 步已经做好的工具调用框架而是在原有基础上把工具注册中心扩展成一个更完整的多工具管理层。工具注册中心现在不再只是登记一个get_current_time而是统一管理多个工具并且为每个工具提供名字、描述、参数 schema 和统一的返回结构。这样一来系统已经开始形成一个比较稳定的工具协议模型根据描述选择工具后端按统一规则执行工具工具再按统一格式返回结果最后模型基于这个结果生成自然语言回答。这说明你的系统已经不再是“临时加几个函数”而是在形成一个真正可以持续扩展的工具体系。在业务流程上第 6 步本质上还是沿用了第 5 步的 tool loop只不过现在这个 loop 开始服务于更复杂的场景了。也就是说ChatService的核心逻辑没有推翻重写而是在原有“模型先决定是否调用工具”的基础上继续支持更多工具种类并适当放宽了工具循环轮次限制以适应一个回答里可能涉及多次工具请求的情况。这个变化的意义在于你已经不再处在“验证框架能不能跑”的阶段而是进入了“框架已成立开始往里面填真正的业务能力”的阶段。从系统行为上看第 6 步带来了一个很明显的升级。以前系统主要有三种能力普通聊天、大模型问答、RAG 检索增强回答而现在又多了一层“面向 OnCall 的工具型分析能力”。这意味着用户问问题时系统不只是被动地根据知识生成答案而是会开始主动判断这个问题更适合查知识库还是查告警还是查日志。比如在“5xx 告警应该先检查什么”这种问题上它会倾向于查内部知识库在“gateway 现在有哪些 firing 告警”这种问题上它会选择查告警在“最近有没有 timeout ERROR 日志”这种问题上它会选择查日志。这种“按问题类型动态选择工具”的行为就是第 6 步最本质的成果。这一阶段的另一个重要变化是系统的可解释性进一步增强了。因为每一次工具调用都会在trace里留下记录包括模型选择了哪个工具、传了什么参数、工具返回了什么结果。流式接口也继续沿用了第 5 步的事件机制可以把tool_call、tool_result和最终的 token 输出逐步发给前端。这使得系统不只是“给答案”而且能展示答案是如何来的。对于 OnCall 场景来说这一点非常重要因为用户往往不只关心结论也关心系统到底查了哪些信息、依据了哪些数据做判断。从工程角度来说第 6 步最大的价值不在于某一个具体工具而在于你已经搭好了一个可以持续扩展的 OnCall 工具体系底座。虽然目前alerts和logs还是 mock 数据但这一步已经证明多工具注册、模型选工具、工具执行、结果回填、trace 记录、SSE 输出这整套机制都已经成立。也就是说后面你要做的已经不再是“怎么设计工具调用框架”而是“把 mock 工具替换成真实数据源”。这会让后续开发轻松很多。如果把第 6 步浓缩成一句话它做的事情就是在现有聊天、RAG 和单工具调用能力之上建立了一个面向 OnCall 场景的多工具系统让模型能够根据问题类型主动调用知识库、告警和日志工具来辅助分析。也正因为这一步已经完成下一步才会很自然地进入两个方向中的一个。一个方向是把alerts和logs从 mock 替换成真实后端数据源另一个方向是进一步做多步工具协同也就是更接近 ReAct 或 Plan-Execute-Replan 的执行模式。换句话说第 6 步真正完成的不只是 3 个工具而是整个 OnCall 智能体执行能力的第一层业务化落地。Step07第 7 步做的事情核心就是把第 6 步中仍然停留在 mock 阶段的工具系统开始真正接入真实业务后端。如果说第 6 步解决的是“模型能不能在 OnCall 场景里选择合适的工具”那么第 7 步解决的就是“这些工具拿到的数据是不是真的来自外部系统而不是写死在代码里的假数据”。这一点非常关键因为它标志着系统开始从“可演示的智能助手”迈向“具备真实业务接入能力的智能助手”。这一阶段最重要的工作是把query_alerts从第 6 步中的 mock 告警数据替换成了一个真实的 HTTP 后端适配器。也就是说告警工具不再直接读代码里写好的几条静态告警而是通过新增的alerts_client去访问真实的告警接口再把接口返回的数据做规范化处理然后交还给工具层。这个改动的意义非常大因为它让“工具”这层和“真实后端接口”这层被正式分开了。以后无论你接的是 Prometheus、Alertmanager、Grafana还是公司内部告警平台你都不需要推翻工具框架只需要替换或扩展底层的 backend 适配器。在实现层面第 7 步并没有推翻第 5、6 步已经搭好的工具调用架构而是沿用了原有的ToolRegistry ChatService tool loop机制只把query_alerts的底层数据来源换成了真实 Prometheus 接口。这说明前面几步搭建的架构已经经得住扩展不需要每次引入真实系统时都重写一遍业务逻辑。换句话说第 7 步验证的不只是“能不能查真实告警”也间接证明了你前面搭好的工具分层和执行流程是合理的。为了完成这一步你还补上了本地 Prometheus 环境。也就是说第 7 步不只是写代码层面的工作还包括真实运行环境的搭建和验证。你本地起了一个最小 Prometheus 实例配置了最基本的规则文件并用一条始终成立的测试告警规则确保/api/v1/alerts这个真实接口能返回 firing 告警。这样做的价值在于你不是“假设自己接上了真实告警系统”而是真正在本机造出了一个可访问、可验证、可用于联调的真实告警源。这一步把“真实后端接入”从概念变成了一个可以重复验证的本地开发能力。从系统行为上看第 7 步完成后query_alerts已经不再是一个“模型以为自己在查告警其实只是代码里拿几条假数据”的工具而是真的能调用 Prometheus/api/v1/alerts把返回的告警信息结构化处理后交给模型。模型再根据这些真实告警信息生成自然语言总结。这一点从你后面的测试结果已经很清楚了模型不仅成功触发了query_alerts还从真实返回中读到了PrometheusAlwaysFiring这条告警并给出了带有状态、级别、描述和时间信息的总结。这说明第 7 步的核心目标已经达成——工具系统开始消费真实系统数据而不是内部 mock 数据。这一阶段还有一个非常重要的意义就是它验证了工具系统在“真实接口条件下”依然能稳定工作。第 6 步里工具调用闭环虽然已经跑通但那是在 mock 数据条件下完成的风险比较低到了第 7 步工具开始真正面临 HTTP 超时、接口结构不一致、字段归一化、环境依赖这些现实问题。而你通过alerts_client的封装把这些外部复杂性都隔离在工具层下面没有把它们污染到ChatService或上层路由。这说明你的项目架构已经具备了承载真实外部系统接入的能力而不仅仅是写几个 demo 用的工具。从项目整体演进上看第 7 步是一个很关键的转折点。前面第 4 步和第 5 步更多是在打能力基础RAG、工具调用协议、SSE 事件流第 6 步是在这个基础上做出多工具系统而第 7 步第一次让系统真正连接到了外部真实服务。也就是说从这一步开始OnCall 不再只是“模拟一个会查知识和查告警的助手”而是在逐步成为一个“能接入真实监控系统并进行辅助分析的助手”。如果把第 7 步浓缩成一句话它做的事情就是在第 6 步多工具系统的基础上把query_alerts从 mock 数据升级为真实 Prometheus 告警接口接入并完成了本地真实告警环境的搭建与验证。也正因为第 7 步已经完成后面的下一步就变得很自然了。既然真实告警接口已经接通那么接下来最合理的方向就是继续把query_logs也从 mock 替换成真实日志后端。到那时你的系统就会同时拥有真实知识库、真实告警和真实日志三个核心 OnCall 能力整体完成度会再上一个台阶。Step08基于MCP的日志接入原始方案是Python 里的query_alerts、query_logs各自对应一个backend client然后直接去请求某个HTTP API比如/alerts、/logs。这种方式的特点是简单、透明、好调试。一眼就能看清哪个工具请求哪个地址、传什么参数、返回什么 JSON、在哪里做字段归一化。对从零重写、先把链路跑通这个阶段来说这是最稳的路线。它的代价是耦合比较强Prometheus、Loki、Elastic、内部日志平台各自接口格式都不一样要一个个写适配器。基于MCP的实现是先读取cls_mcp_url再用NewSSEMCPClient建一个MCP 客户端随后执行Initialize再通过GetTools从远端 MCP Server 动态拿到一组工具。也就是说这段代码不是“应用自己查日志”而是“应用去连一个 MCP 服务MCP 服务再把日志能力暴露成工具”。一个是直连具体后端API一个是先接统一协议层再有协议层提供工具。理解MCP以前接告警、接日志、接数据库每种能力都要自己写一套工具描述 参数 schema HTTP 调用 结果转换现在如果后端已经有一个 MCP Server这些工具可以由 MCP Server 统一暴露出来客户端只需要连接、发现工具、调用工具。第 8 步做的核心事情是把原来第 6 步里还停留在 mock 阶段的query_logs工具正式升级成了基于 MCP 腾讯云 CLS 的真实日志查询能力。如果说第 7 步解决的是“真实告警怎么接进来”那么第 8 步解决的就是“真实日志怎么接进来”。这一步完成之后OnCall 就不再只是能查知识库、查告警还真正具备了查线上日志的能力。这一阶段一开始并不是继续走我最早设计的logs_client.py - /logs这种普通 HTTP API 路线而是根据你贴出来的原 Go 项目代码改成了更贴近原项目结构的MCP 接入方案。因为 Go 版日志能力本来就不是直接写一个/logs接口去查而是通过cls_mcp_url去连接腾讯云 CLS 的 MCP 服务再从 MCP server 暴露出来的工具里拿日志查询能力。所以第 8 步本质上不是“补一个日志接口”而是把 Python 项目的日志工具层改造成了一个MCP client。为了完成这一点第 8 步里首先做的是梳理并确认腾讯云 CLS MCP server 暴露出来的真实工具集合。你通过 Go 侧和 Python 侧分别确认了工具名包括SearchLog、TextToSearchLogQuery、GetRegionCodeByName、GetTopicInfoByName等。这一步非常关键因为它让后面的 Python 代码不再是凭猜测写参数而是按真实工具名、真实 schema 去接。也就是说第 8 步不是盲目接 MCP而是先把 MCP server 的工具生态摸清楚再往 Python 里落。接着第 8 步在工程层面新增了一套MCP 日志后端。你在 Python 项目里新增了app/infra/mcp/cls_mcp_client.py把日志查询从“普通 HTTP backend”改成了“通过 SSE 连接 MCP server再调用工具”的模式。同时app/tools/logs.py也被改成依赖这个 MCP backend而不是再从 mock 数据里读日志。换句话说从这一刻开始query_logs这个工具虽然名字没变但底层实现已经彻底换成了真实的 CLS 日志查询。为了让这套 MCP 日志链路真正能跑起来第 8 步里还完成了本地自托管cls-mcp-server的搭建。中间经历了几轮排错先是腾讯云托管 SSE 地址缺少有效密钥后来又改成本地启动cls-mcp-server接着又遇到 Node 版本过低导致cls-mcp-serverlatest无法启动最后通过升级 Node 环境解决。也就是说第 8 步不仅仅是代码改造还包含了一次真实外部服务运行环境的搭建。你最终让本地机器可以正常跑起腾讯云 CLS 的 MCP server并让 Python 项目连到本地http://127.0.0.1:3000/sse这个自托管地址。在和腾讯云 CLS 联调的过程中第 8 步还完成了一系列日志查询链路的拆解和校准。一开始的失败不是来自 Python 主工程而是来自细节有时候是地域码不对有时候是TopicId必填但没传有时候是GetTopicInfoByName的参数名大小写不对有时候是腾讯云 CLS 服务还没开通或者 Topic、TopicId 还没准备好。通过这些排查第 8 步最后把一条完整的链路跑顺了先根据“北京”拿到ap-beijing再根据 Topic 名gateway拿到真实TopicId再调用SearchLog去查日志。也就是说这一步实际上已经把 CLS 日志查询所需的上下文补全逻辑理顺了。为了验证这条链路不是停留在“能连上工具”第 8 步还专门做了真实日志写入和真实日志检索。你在 CLS 中创建了gatewayTopic并通过 Python 脚本把几条测试日志真正写了进去字段里包括level、service、service_name、message。这一步很重要因为它把“日志工具能调用”进一步升级成了“日志工具能查到自己刚写进去的真实日志”。而且你还对 Topic 的索引配置做了确认确保日志不是仅仅写进去而是可检索的。在调通检索的过程中第 8 步还顺手解决了一个很实际的问题如何找到真正能命中的 CQL 查询语句。一开始完全依赖TextToSearchLogQuery生成的 Query但它生成的查询语句和 CLS 当前 Topic 的索引能力并不总是完全匹配。于是你通过单独的调试脚本一条一条试不同的 Query包括*、timeout、gateway、service_name:gateway、service:gateway、service_name:gateway AND timeout等最后确认这些写死的查询都能够命中真实日志。这一步的价值在于它把日志查询从“理论上应该能查到”变成了“已经用实际查询验证过能命中”。在此基础上第 8 步最后做的是把验证通过的查询逻辑回收到主工程。也就是说不再只是scripts/test_cls_mcp_call.py这种单独调试脚本能查到日志而是把成功的Region - TopicId - SearchLog逻辑收回app/infra/mcp/cls_mcp_client.py并最终通过/api/chat的主链路验证成功。你最后那条curl返回里模型成功调用了query_logs而tool_result返回的正是从 CLS 查回来的两条 timeout ERROR 日志这说明第 8 步已经从“联调成功”走到了“主业务接口成功”。所以如果把第 8 步整体概括一下它做的不是单一一件事而是完整完成了以下闭环基于原 Go 项目的接入思路把 Python 版的query_logs从 mock 改造成 MCP 日志工具搭建并启动本地自托管 CLS MCP server打通 Python 到 MCP再到腾讯云 CLS 的真实日志查询链路创建 Topic、写入测试日志、验证索引最后在/api/chat主接口里成功查回真实日志。如果用一句话来总结第 8 步就是第 8 步完成了 Python OnCall 项目中真实日志能力的落地将query_logs从 mock 升级为基于 MCP 腾讯云 CLS 的真实日志查询并完成了从本地自托管 MCP、CLS Topic/索引、日志写入到主链路/api/chat的端到端验证。Step09你说得对这次我按纯分段文字来总结第 9 步不再用分点结构。第 9 步做的核心事情是把系统从“能分别调用多个工具”推进到了“能把多个工具按固定顺序串起来完成一次初步故障分析”。在前面的步骤里项目已经具备了真实知识库、真实告警和真实日志这三类能力但它们本质上还是分散的能力点用户问什么系统就调某一个工具去处理。第 9 步解决的问题不是再接一个新工具而是让系统第一次具备“把多种真实数据源组织成一条分析链”的能力。这一阶段新增了一个专门用于故障初步分析的接口也就是/api/triage。这个接口不是普通聊天接口而是一个面向 OnCall 场景的专用分析入口。它接收服务名、故障现象、是否查询知识库等参数然后系统会自动按固定顺序执行分析动作。也就是说第 9 步第一次把“问答”变成了“流程”让系统不再只是回答一句话而是能够围绕一个故障现象自动去看告警、看日志、看知识库再把这些结果组织起来。为了支撑这个新接口第 9 步新增了app/schemas/triage.py、app/services/incident_analysis_service.py和app/api/routes/triage.py这几个关键文件。其中最重要的是IncidentAnalysisService。它不是单个工具的封装而是一个多步分析服务负责把多个工具串起来。这个服务会根据用户输入的故障现象自动推断一个默认日志关键字例如用户提到 timeout、超时、5xx、502、504 之类的现象时系统会优先联想到 timeout 相关日志然后再去调用日志工具。它同时还会决定是否查询知识库并把最后查回来的各类结果整理成一份统一的分析结论。第 9 步建立起来的分析流程是固定顺序的。它会先查告警再查日志再查知识库最后输出综合结论。告警部分复用了第 7 步接入的真实 Prometheus 能力日志部分复用了第 8 步接入的真实 CLS MCP 日志能力知识库部分复用了第 4 步做好的 RAG 检索能力。也就是说第 9 步并没有重新发明底层能力而是把前面几步已经做好的真实能力第一次整合到了一个统一流程里。这一阶段还有一个很重要的产物就是 triage 的输出不再只是工具原始结果而是一份已经被整理过的故障分析报告。返回结果里包含summary、alerts、logs、docs和trace。其中summary是对告警、日志、知识库结果的综合解释alerts是真实告警列表logs是真实日志列表docs是知识库命中的文档片段而trace则记录了整个分析过程中每一步具体调用了什么工具、传了什么参数、返回了多少条结果。这说明第 9 步不只是把多个工具拼在一起还保留了一条清晰可追踪的分析过程。在第 9 步初版跑通之后你又发现了一个真实问题那就是如果直接按servicegateway去查告警而 Prometheus 当前没有带这个标签的 firing 告警那么 triage 会直接得到空结果。为了解决这个问题第 9 步后来又做了一次优化加入了告警 fallback 逻辑。现在的策略是先查指定服务的告警如果没有命中再回退去查全局 firing alerts然后再从这些全局告警里筛一遍看有没有和目标服务相关的内容。为了支持这个优化IncidentAnalysisService里补充了服务匹配和二次筛选的逻辑同时在trace中清楚记录了primary和fallback_global两段查询过程还在返回里增加了alert_meta说明这次分析有没有触发回退。从实际验证结果来看第 9 步已经完成了它最重要的目标。现在/api/triage已经能够稳定执行多步协同分析能够自动串联告警、日志和知识库查询也能够在响应中返回结构化的分析结果和 trace。你已经看到这个接口成功执行了真实的query_alerts、query_logs和query_internal_docs并且告警 fallback 逻辑也已经真正生效。也就是说第 9 步当前已经不是“想法”或者“设计图”而是一个真正能运行的多步分析接口。当然这一阶段也暴露出了两个结果质量问题。一个是日志结果容易因为测试数据时间窗口太短而变成空另一个是知识库里还残留着早期的测试文本导致 triage 有时会命中无关内容。这两个问题你现在已经决定先不处理因为它们影响的是分析质量而不是第 9 步是否成立。换句话说第 9 步的流程能力已经建立起来了只是数据质量还有后续优化空间。如果要用一句完整的话来概括第 9 步那么可以这样说第 9 步完成了 Python OnCall 的第一版多步协同分析能力通过新增/api/triage接口把真实告警、真实日志和知识库检索按固定顺序组织成一次故障初步分析流程并在此基础上补上了告警查询的 fallback 回退逻辑使系统从“能分别调用多个工具”进化到了“能自动完成一次结构化故障分析”。Step10第 10 步做的核心事情是把第 9 步已经实现的“固定流程多步分析”进一步升级成了一个会根据当前证据动态决定下一步动作的调查器。如果说第 9 步解决的是“把查告警、查日志、查知识库按固定顺序串起来”那么第 10 步解决的就是“系统能不能根据前一步查到的结果自己决定下一步到底该查什么”。所以第 10 步的本质不是再新增一个工具而是让系统从固定流程走向轻量 Agent。这一阶段最重要的变化是新增了一个新的接口/api/investigate。这个接口和第 9 步的/api/triage不同/api/triage更像是一个固定模板分析器收到请求后总是按既定顺序去查告警、查日志、查知识库。而/api/investigate更像一个调查循环它会先规划下一步动作再执行工具再根据执行结果继续规划下一步直到达到最大步数或者系统判断当前证据已经足够。这意味着第 10 步第一次把系统推进到了“plan / execute / finish”这种更接近 Agent 的工作方式。为了支撑这套能力第 10 步新增并完善了一组专门的文件。首先增加了app/schemas/investigate.py用来定义调查接口的请求结构例如service、symptom、max_steps、use_rag这些字段。然后新增了app/api/routes/investigate.py把调查器正式暴露成 HTTP 接口。更核心的是新增了app/services/investigation_service.py和app/services/planner_service.py。其中InvestigationService负责整个调查循环的运行PlannerService负责决定下一步动作。除此之外还新增了app/prompts/investigation_prompts.py专门用来约束 Planner 的输出格式和决策边界。第 10 步一开始并不是直接上完全自由的大模型 Agent而是先做了一个规则式 Planner的版本。这个版本的重点是先把“调查循环”本身搭起来。它会根据当前 evidence 和 trace 去判断下一步是查告警、查日志、查知识库还是直接 finish。虽然一开始这个 Planner 还是规则驱动的但它已经实现了第 10 步最关键的结构变化系统不再只是固定顺序执行而是进入了一个每一步都要重新决定下一步的循环。在规则式版本跑通之后第 10 步又继续做了增强版也就是把PlannerService从纯规则逻辑升级成了LLM Planner 优先、规则兜底的形式。这一步的意义非常大因为它让“下一步调查什么”不再完全由你预先写死而是开始由模型根据当前 evidence 来决定。为了避免模型随意发挥这一版又特别加入了严格的 JSON 输出约束。模型只能输出固定动作query_alerts、query_logs、query_internal_docs或finish并且必须带上arguments和reason。这样既保留了 LLM 决策的灵活性又没有失去系统可控性。第 10 步里InvestigationService的作用也比第 9 步更复杂了。它不仅仅是顺序调三个工具而是会维护一个持续更新的evidence和trace。每次 Planner 产出动作之后系统都会执行对应工具把结果放进evidence再把这一步的计划和执行情况写进trace。下一轮 Planner 再根据更新后的 evidence 决定新的动作。这样一来系统就真正有了“边查边想”的味道而不是一次性跑完一个固定模板。从实际验证结果来看第 10 步已经经历了两个清晰阶段。第一阶段是规则式调查器跑通了也就是说/api/investigate已经能以plan - execute - finish的形式工作并且会把告警、日志、知识库的结果按步骤累积到 evidence 中。第二阶段是增强版成功接入了 LLM Planner。增强版的测试结果里你已经看到 Planner 不再只是按固定模板走而是会根据前一步没查到 timeout 日志继续尝试改查5xx相关日志再在日志仍没有结果时转去查知识库最后主动 finish。这说明第 10 步增强版已经具备了真正“根据当前证据调整策略”的能力。这一阶段输出结构也更接近真正的调查器了。返回结果中除了summary之外还包含evidence和trace。summary是最后整理出来的阶段性调查结论evidence保存了调查过程中累计到的告警、日志和知识库证据trace则详细记录了每一步计划了什么、执行了什么、为什么这么做。这使得第 10 步的系统不只是会给结果而且会给出一条完整的调查轨迹。从项目演进角度来看第 10 步最大的价值是让系统从“多工具系统”正式迈向了“轻量 Agent 系统”。在第 9 步之前系统虽然已经能用多个真实工具但工具之间的协作关系是你提前设定好的而到了第 10 步系统已经开始具备根据当前上下文动态决定下一步动作的能力。虽然它还没有完全达到你最开始理解的 Plan-Execute-Replan 那种复杂程度但已经非常接近真正 Agent 的雏形了。如果用一句完整的话来总结第 10 步那么可以这样说第 10 步完成了 Python OnCall 项目中“轻量调查器”的落地通过新增/api/investigate接口引入PlannerService InvestigationService的 plan / execute 循环机制并进一步将 Planner 从规则式升级为 LLM 驱动、规则兜底的结构化决策器使系统能够根据当前证据动态调整下一步调查动作。Step11第 11 步做的核心事情是把第 10 步已经具备的“多步调查能力”进一步升级成了一个真正意义上的Plan-Execute-Replan 工作流。如果说第 10 步解决的是“系统能不能根据当前证据决定下一步动作”那么第 11 步解决的就是“系统能不能先形成一个阶段性计划执行以后再根据新结果主动调整计划”。所以第 11 步的本质不再只是一个会循环执行的调查器而是一个具备计划、执行、重规划三段结构的 AI Ops 调查流程。这一阶段最重要的新能力是新增了一个独立的接口/api/plan_execute。这个接口和前面的/api/triage、/api/investigate都不一样。/api/triage是固定流程分析器/api/investigate是按当前证据逐步决定下一步的轻量调查器而/api/plan_execute则是在调查开始之前先形成一个“初始计划”再按计划执行并在每一步执行结束后根据新证据决定是继续原计划、替换后续计划还是直接结束。这意味着第 11 步第一次把系统推进到了真正接近你最开始提到的Plan-Execute-Replan形式。为了实现这条链路第 11 步新增了app/schemas/plan_execute.py、app/prompts/plan_execute_prompts.py、app/services/plan_execute_service.py和app/api/routes/plan_execute.py这几个关键文件。其中plan_execute.py这个 route 把新的工作流正式暴露成 HTTP 接口plan_execute.py对应的 schema 负责规范请求参数比如服务名、症状、最大轮数、是否使用知识库等而最核心的实现全部集中在PlanExecuteService中。除此之外这一步还在 prompt 层面新增了专门的计划提示词和重规划提示词也就是INITIAL_PLAN_SYSTEM_PROMPT和REPLAN_SYSTEM_PROMPT。这说明第 11 步不只是新增了一个服务而是把“初始规划”和“执行后重规划”明确拆成了两个不同阶段。第 11 步的第一部分能力是让系统具备了先生成初始计划的能力。系统现在不再是一上来就直接查工具而是会先根据service和symptom生成一个 JSON 格式的初始调查计划。这个计划里包含goal和steps。goal表示这轮调查的目标是什么steps则是一组准备执行的动作每个动作都包含title、action、arguments和reason。而且这些动作并不是随便生成的自始至终都被严格约束在固定集合里也就是query_alerts、query_logs、query_internal_docs和finish。这意味着第 11 步虽然开始引入计划能力但系统仍然保持着很强的可控性。第 11 步的第二部分能力是把初始计划真正执行起来。PlanExecuteService会维护一组pending_steps也就是当前待执行的计划步骤。每一轮执行时系统会从当前剩余计划中取出第一步记录到trace里作为phaseplan然后调用对应工具执行再把执行结果记录成phaseexecute。与此同时它还会把执行到的告警、日志、知识库结果写入evidence。所以第 11 步并不是“只会生成计划”而是已经把“计划”真正接到了“执行”上形成了一条完整的执行链。第 11 步最关键、也最区别于第 10 步的部分是Replan。也就是在每轮执行之后系统不会机械地继续跑剩余步骤而是会把“刚才执行了什么”“刚才查到了什么”“当前还剩哪些步骤”“当前 evidence 长什么样”重新整理出来再交给一个专门的重规划器判断。这个重规划器会输出三种决策之一continue、replan或finish。如果是continue就说明剩余计划还有价值可以继续执行如果是replan就说明当前计划不够好了需要替换成新的后续计划如果是finish就说明当前证据已经足够或者继续查下去价值不大可以结束本轮调查。正是这一层让第 11 步真正具备了“重规划”的能力。从你实际跑出来的结果看第 11 步已经把这三段结构完整体现出来了。返回中已经有initial_plan说明初始计划阶段成立了trace里不仅有phaseplan和phaseexecute还有phasereplan说明执行后重规划阶段也已经落地了。而且这次的重规划并不只是形式上的记录而是真的在改变后续路径。初始计划里原本的重点是查gateway告警、查gateway日志、查知识库、结束调查但执行到日志为空之后系统并没有机械地照着原计划往下走而是主动生成了一个新的调查方向转去查下游服务日志。这一点非常关键因为它说明第 11 步不是“先写个计划再死板执行”而是已经具备了“根据执行结果改计划”的行为。这一阶段还做了一个非常重要的工程设计就是LLM 优先、规则兜底。无论是初始计划还是重规划都优先尝试通过 LLM 按严格 JSON 输出。如果 LLM 输出格式错误、动作非法、或者根本没法解析系统就会自动退回到保守规则策略。这一点非常重要因为它避免了第 11 步一旦引入大模型就变得极不稳定。也就是说第 11 步虽然开始把 AI 决策引入到更上层的工作流里但仍然保留了足够强的工程兜底能力这样接口不会因为模型一次输出歪掉而直接不可用。第 11 步的返回结果结构也比前面更完整。现在/api/plan_execute的返回里不只有summary还有initial_plan、evidence和trace。initial_plan体现的是系统最开始打算怎么调查evidence保存的是执行过程中真正收集到的告警、日志和知识库内容trace则记录了整个过程里每一轮的计划、执行和重规划决策。这样一来第 11 步的系统不仅能给用户一个结论还能完整展示“我是怎么得到这个结论的”。这使得它已经非常接近一个真正可解释的 AI Ops 调查器。当然从这次的实际结果里也能看出第 11 步虽然流程已经成立但证据质量仍然受当前真实数据环境限制。当前告警为空、日志为空、知识库命中的还是测试文本所以系统即使已经会计划、会执行、会重规划最后得到的证据仍然不够强。这说明第 11 步现在已经不是“框架没搭起来”的问题而是“框架搭起来之后数据质量如何继续提升”的问题。换句话说第 11 步的工作流能力已经完成后续更多是质量优化而不是结构重建。如果用一句完整的话来总结第 11 步那么可以这样说第 11 步完成了 Python OnCall 项目中Plan-Execute-Replan工作流的第一版落地通过新增/api/plan_execute接口让系统具备了先生成初始调查计划、按计划执行工具、再根据执行结果动态重规划后续步骤的能力并最终输出带初始计划、执行轨迹、重规划记录和证据统计的完整调查结果。全程按照GPT的提示实现先复现之后再来梳理这个过程。原项目来自小林Coding但是只有基于Java和Go的实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2423453.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…