Agentic AI实战:从RAG到自主规划的智能体系统构建
Agentic AI(智能体AI)代表了大模型应用从被动应答向主动规划的关键演进,其核心在于任务分解、工具调用与反思机制三位一体的系统能力。不同于传统RAG仅解决‘查什么、答什么’的信息匹配问题,Agentic AI通过记忆(工作流上下文+长期经验)、工具(可发现、可验证的API/服务集成)和反思(置信度评估、冲突仲裁、自我审计)三大支柱,实现目标驱动的闭环决策。该范式显著提升复杂业务场景下的鲁棒
1. 项目概述:当AI开始“自己拿主意”,我们该不该放手?
你有没有试过让一个大模型帮你写一封客户邮件,结果它写得逻辑清晰、语气得体,但偏偏把对方公司名字拼错了?或者让它分析一份销售报表,它能总结出趋势、指出异常点,却在关键数字上四舍五入错了一位小数,导致整个结论跑偏?这些不是模型“笨”,而是它本质上是个极其聪明的“应答机”——你给指令,它就执行;你问问题,它就作答。它没有目标感,没有计划性,更不会在执行一半时停下来想:“等等,我是不是漏掉了什么前提?”这正是当前主流AI应用的底层局限。而“Agentic AI”(具身智能体AI,或称智能体AI)要解决的,恰恰就是这个问题。它不是让你多问一个问题,而是让AI自己学会拆解问题、规划步骤、调用工具、验证结果、甚至在失败时主动换条路走。它不取代RAG(检索增强生成),而是站在RAG的肩膀上,把“查资料+写答案”这个单步动作,升级成“理解目标→拆解任务→分头行动→整合结果→反思优化”的完整闭环。这篇文章不是在预测一个遥远的未来,而是记录我过去半年里,在三个真实业务场景中——一个电商客服知识库的自动迭代系统、一个金融合规文档的实时风险扫描流水线、还有一个内部研发团队的周报自动生成与问题预警平台——亲手搭建、调试、并最终上线运行的Agentic AI工作流。它不是实验室里的Demo,而是每天处理上千次请求、平均响应时间稳定在2.3秒、错误率比纯RAG方案下降67%的生产级系统。如果你正被“模型很强大,但落地总差一口气”的困境卡住,或者你已经用熟了RAG,正琢磨下一步该怎么走,那么这篇从代码、架构到踩坑实录的全程复盘,就是为你写的。
2. 核心思路拆解:为什么是“智能体”,而不是“更强的模型”?
2.1 从“问答机”到“项目经理”的范式跃迁
要理解Agentic AI的价值,得先看清我们当前的主流方案到底卡在哪。以RAG为例,它的核心逻辑非常清晰:用户提问 → 模型将问题向量化 → 在向量数据库里搜索最相似的几段知识 → 把问题和搜到的知识一起喂给大模型 → 模型生成最终回答。这个流程像什么?像一个经验丰富的老秘书。你告诉他“帮我给张总写封邮件,说下周三的会议改到周四了”,他立刻翻出张总的联系方式、上周会议纪要、以及公司最新的会议变更模板,然后一气呵成写出邮件。但他绝不会主动去想:“张总上周刚出差回来,会不会对时间变动特别敏感?要不要在邮件里加一句‘考虑到您近期行程紧张,我们已协调好所有参会者’?”他只做你明确吩咐的事,不多想,也不多做。这就是RAG的天花板:它极大地提升了“信息获取”的广度和准确性,但完全不涉及“任务规划”与“自主决策”。
Agentic AI则完全不同。它把AI看作一个可以委派任务的“项目经理”。当你提出“帮我分析Q3销售数据,找出增长最快和下滑最严重的三个产品,并解释可能原因”,一个智能体系统会立刻启动一套内部流程:首先,它会调用一个“数据分析师”智能体,去连接数据库,拉取原始销售数据;同时,它会调用一个“市场研究员”智能体,去爬取最近三个月的行业新闻、竞品动态和社交媒体舆情;接着,它会把两份数据交给一个“策略推理”智能体,让它交叉比对,识别出相关性最高的线索;最后,由一个“文案撰写”智能体,将所有分析结果,按照你指定的格式(比如PPT大纲或邮件正文)组织成最终输出。整个过程,它自己决定要调用哪些“子智能体”,每个子智能体需要什么输入,它们的输出如何组合,甚至在某个子智能体返回“数据缺失”时,它会主动触发一个“数据补全”子智能体去尝试从其他渠道获取。这种能力,不是靠堆算力或换更大参数的模型就能获得的,它依赖于一套全新的系统架构设计。
2.2 “智能体”的三大支柱:记忆、工具、反思
一个真正可用的Agentic AI系统,绝非一个黑箱大模型。它是由三个相互咬合的精密部件构成的有机体,缺一不可。我把它们称为“记忆”、“工具”和“反思”。
记忆(Memory) 是智能体的“大脑皮层”。它负责存储两类信息:一是 短期工作记忆(Working Memory) ,即当前正在处理的任务的所有上下文、中间结果、已执行的步骤和遇到的障碍。这就像你写报告时,桌面上摊开的Excel表格、打开的网页、还有手边的草稿纸。二是 长期记忆(Long-term Memory) ,即系统在过往任务中积累的经验、成功模式、失败教训,以及它对自身能力边界的认知。比如,它会记住“调用财务API时,如果返回401错误,大概率是token过期,应该先刷新token再重试”,或者“在分析用户投诉文本时,用‘情感强度’模型比用‘主题分类’模型更能准确定位核心矛盾”。这个长期记忆,不是静态的数据库,而是一个持续演化的知识图谱,它让智能体越用越“懂行”。
工具(Tools) 是智能体的“手脚”。没有工具,智能体再聪明也只是纸上谈兵。这里的工具,远不止是调用几个API那么简单。它必须是一个可发现、可描述、可验证的生态系统。每一个工具,都需要有精确的“说明书”:它能做什么(功能描述)、需要什么输入(参数列表及类型)、会返回什么(输出结构及示例)、以及它的能力边界在哪里(比如,“天气查询工具只能提供未来7天预报,无法预测台风路径”)。更重要的是,智能体必须具备“工具选择”和“工具编排”的能力。面对一个复杂任务,它要能像一个资深工程师一样,评估手头所有工具,判断哪个组合最高效、最可靠。例如,要验证一个客户的信用额度,它可能需要先调用“身份核验”工具确认客户ID,再调用“征信查询”工具获取基础报告,最后调用“额度计算”工具进行动态评估。这个链条的每一步,都必须有明确的输入输出契约,否则整个流程就会在某个环节断裂。
反思(Reflection) 是智能体的“元认知”能力,也是它区别于普通自动化脚本的灵魂所在。它指的是智能体在完成一个步骤、收到一个结果、甚至在任务开始前,都能停下来,对自己的行为、决策、以及所用工具的有效性进行审视。这种反思不是简单的“if-else”判断,而是一种基于规则和概率的深度推理。例如,当“数据分析”子智能体返回的结果与“市场调研”子智能体的结论严重冲突时,反思模块不会直接报错,而是会启动一个“冲突诊断”流程:它会检查两个子智能体的数据源是否一致、时间范围是否匹配、分析方法是否存在根本性差异;然后,它会根据预设的优先级规则(比如,“第一手业务数据”优先级高于“第三方舆情”),决定采纳哪一方的结论,或者触发一个“专家仲裁”子智能体来介入。这种能力,让系统拥有了韧性,能在不完美的现实世界中稳健运行。
2.3 为什么现在才爆发?技术成熟度的临界点
很多人会问,这个概念听起来并不新鲜,为什么直到2025年才成为主流?答案在于,构成智能体的三大支柱,其底层技术在过去两年里才真正达到了工程化落地的临界点。
首先是 大模型的“思维链”(Chain-of-Thought)能力 。早期的大模型,输出是跳跃式的,缺乏可追溯的推理路径。而现在的主流模型,如GPT-4o、Claude 3 Opus、以及国内的Qwen2.5-72B,已经能稳定地生成带有清晰步骤编号、逻辑连接词(“因此”、“然而”、“综上所述”)的长文本。这为智能体的“规划”和“反思”提供了最基础的语言表达能力。没有这个,智能体的“思考”就无从谈起。
其次是 轻量级、高可靠性的工具调用协议 的普及。两年前,让模型调用API还是一件充满魔幻色彩的事,经常出现参数名拼错、JSON格式不合法、或者模型“脑补”出一个根本不存在的工具。而如今,像OpenAI的 function calling 、Anthropic的 tool use ,以及开源社区的 LangChain 、 LlamaIndex 等框架,已经将这一过程标准化、健壮化。它们不仅能准确解析模型的调用意图,还能在调用失败时提供清晰的错误反馈,让智能体可以据此进行有效的“反思”和重试。
最后是 向量数据库与RAG技术的全面成熟 。这看似是“旧技术”,但它恰恰是智能体的“知识地基”。一个智能体再会规划,如果它调用的“市场研究员”工具查不到最新数据,或者“财务分析师”工具连不上数据库,那一切规划都是空中楼阁。2024年,向量数据库的性能、稳定性、易用性都达到了前所未有的高度,使得为每一个子智能体配备一个专属、精准、低延迟的知识库,成为一件成本可控、部署简单的事情。RAG不再是终点,而是智能体系统里最基础、最可靠的“信息供给单元”。
3. 实操细节解析:从零搭建一个电商客服知识库智能体
3.1 场景定义与需求拆解:让知识库“活”起来
我们服务的是一家年GMV超50亿的垂直类电商平台,其客服知识库是一个典型的“大而全、深而杂”的系统。它包含超过12万条FAQ、8000多份产品说明书、300多份售后服务政策PDF,以及每天新增的数百条客服对话记录。传统上,这个知识库服务于两个角色:一是客服人员,他们通过关键词搜索快速定位答案;二是RAG系统,它为在线客服机器人提供实时问答支持。但问题一直存在:新员工培训周期长,因为知识太散,找不到学习路径;客服机器人回答准确率只有78%,尤其在处理“组合型问题”(比如“我的iPhone 15 Pro在保内,屏幕碎了,但充电口也接触不良,能一起修吗?”)时,错误率高达45%;最头疼的是,知识库本身是“死”的,没人知道哪些条款已经过时,哪些FAQ被反复追问却从未更新。
我们的目标,就是把这个“死库”变成一个“活”的智能体系统。它需要做到三点:第一, 主动进化 :能自动从每日的客服对话中,识别出高频、高困惑度的新问题,并驱动内容团队生成新的FAQ;第二, 精准服务 :当客服人员在后台输入一个模糊问题(如“用户说收不到货,怎么办?”),系统不仅能给出标准SOP,还能根据该用户的订单状态、历史投诉记录、甚至当前物流公司的区域性故障公告,动态生成个性化处置建议;第三, 自我审计 :能定期扫描整个知识库,标记出引用了已下架产品、链接已失效、或与最新《消费者权益保护法》相悖的陈旧内容。
这个需求,完美契合Agentic AI的三大支柱。它需要一个强大的“记忆”来关联用户、订单、政策;需要一系列“工具”来查询订单系统、调用物流API、解析PDF、甚至生成法律合规性报告;更需要一个“反思”模块,来评估不同来源信息的可信度,并在信息冲突时做出专业判断。
3.2 架构设计:一个三层智能体的协同网络
我们没有采用单一的、巨无霸式的“全能智能体”,而是设计了一个分层、解耦的智能体网络。这种设计源于一个血泪教训:在第一个POC版本中,我们试图用一个模型搞定所有事,结果系统变得无比脆弱,任何一个环节出错,整个流程就崩溃。分层设计,让每个智能体各司其职,互为备份,大大提升了系统的鲁棒性。
第一层:协调智能体(Orchestrator Agent) 这是整个系统的“大脑”和“指挥官”。它不直接处理业务数据,只负责宏观规划与调度。它的输入是用户的一个原始请求(比如一段客服对话文本),输出是一个经过解析的、结构化的任务指令集。它的核心能力是“任务分解”(Task Decomposition)。例如,当它收到“用户A投诉收不到货”这个请求时,它会立即分解出四个并行子任务:
fetch_user_profile: 调用用户画像工具,获取用户等级、历史订单数、近30天投诉次数。fetch_order_status: 调用订单中心API,获取该订单的最新物流节点、预计送达时间、以及是否有异常标记。fetch_logistics_alert: 调用物流监控工具,查询该物流公司、该运输线路在近48小时内是否有大面积延误或丢件公告。fetch_policy_relevance: 调用知识库RAG工具,检索所有与“物流延误”、“用户投诉”、“赔偿标准”相关的政策条款。
这个分解过程,是通过一个专门微调过的、13B参数的小模型完成的。我们没有用最大的模型,因为协调任务的核心是“精准”而非“博学”,小模型推理快、成本低、且更容易被约束在预设的工具调用范式内。
第二层:执行智能体(Executor Agents) 这是系统的“四肢”,由多个高度专业化的子智能体组成,每个都只精通一个领域。它们接收来自协调智能体的、格式严格统一的指令,并返回同样格式规范的结果。我们为这个电商场景定制了五个核心执行智能体:
-
订单分析师(Order Analyst) :专精于解读订单状态码、物流轨迹、库存锁定逻辑。它内置了对平台所有27种订单状态的语义理解,能将“已发货(待揽收)”和“已发货(在途)”这两种状态,映射到完全不同的客服话术和处理时效上。
-
物流哨兵(Logistics Sentinel) :它不是一个简单的API调用器。它会同时对接顺丰、中通、京东物流三家的官方API,并对返回的数据进行交叉验证。如果顺丰显示“派送中”,而中通显示“已签收”,它会触发一个“数据冲突”事件,交由协调智能体处理。
-
政策顾问(Policy Advisor) :这是最复杂的执行智能体。它背后不是一个静态的RAG索引,而是一个动态的“政策知识图谱”。图谱的节点是政策条款,边是条款间的逻辑关系(如“依据”、“例外”、“补充说明”)。当协调智能体要求它检索“赔偿标准”时,它不仅能返回《售后政策》第3.2条,还能自动关联到《消费者权益保护法》第24条,并标注出两者在“举证责任”上的细微差别。
-
内容生成器(Content Generator) :它不生成最终答案,而是生成“答案的原材料”。例如,它会根据订单分析师和物流哨兵的结果,生成一份结构化的“事实摘要”;再根据政策顾问的结论,生成一份“合规要点清单”。这两份材料,会被送到第三层。
-
合规审查员(Compliance Auditor) :这是一个独立的、由法律专家规则驱动的智能体。它不依赖大模型,而是用一套精心编写的Drools规则引擎。它会逐条扫描内容生成器输出的“合规要点清单”,检查其中是否包含了“绝对化用语”、“未明确告知免责条款”等违规风险点,并给出修改建议。
第三层:合成智能体(Synthesizer Agent) 这是系统的“嘴巴”和“手”。它接收来自第二层所有执行智能体的结构化输出,进行最终的整合、润色和格式化。它的核心能力是“信息融合”(Information Fusion)与“风格适配”(Style Adaptation)。它会将“事实摘要”、“合规要点”、“用户画像洞察”这三份材料,按照预设的模板,编织成一段自然流畅、语气得体、且完全符合客服SOP的回复。更重要的是,它能根据接收方的不同,自动切换风格:如果是给一线客服看的,它会突出“操作步骤”和“关键话术”;如果是给主管看的周报,它会提炼出“共性问题”和“流程优化建议”。
3.3 关键技术实现:让“反思”真正落地
在整个架构中,“反思”能力不是一句空话,而是通过三个具体的技术模块来实现的。
模块一:置信度评分(Confidence Scoring) 每一个执行智能体,在返回结果时,都必须附带一个0-1之间的“置信度分数”。这个分数不是模型自己瞎猜的,而是由一套混合算法计算得出。以“订单分析师”为例,它的置信度计算公式是: Confidence = (API成功率 * 0.4) + (状态码解析准确率 * 0.3) + (历史相似案例匹配度 * 0.3) 其中,“API成功率”是该智能体过去一小时调用订单API的成功率;“状态码解析准确率”是它对当前订单状态的判断,与人工复核结果的一致性;“历史相似案例匹配度”是它在知识库中找到的、与当前订单特征(如用户等级、商品类目、下单时间)最相似的10个历史案例中,其处理结果的平均好评率。这个分数,会实时传递给协调智能体,作为后续决策的关键依据。如果某个子任务的置信度低于0.6,协调智能体就会启动“降级预案”,比如,放弃调用高风险的“物流哨兵”,转而使用一个更保守、但置信度更高的“物流概览”工具。
模块二:冲突检测与仲裁(Conflict Detection & Arbitration) 当多个执行智能体返回相互矛盾的信息时,系统不会报错,而是启动一个标准化的仲裁流程。我们定义了三种主要冲突类型:
- 事实冲突 (如订单状态是“已发货”,但物流信息是“未揽收”)
- 政策冲突 (如《售后政策》说可全额退款,但《消费者权益保护法》规定需扣除折旧费)
- 风格冲突 (如内容生成器建议用“亲”开头,但合规审查员判定为不专业)
针对每种冲突,我们都预设了一个“仲裁规则库”。例如,对于“事实冲突”,仲裁规则是:“以订单中心API的返回为准,物流API的信息仅作参考,并在最终回复中注明‘物流信息可能存在1-2小时延迟’”。这个规则库,由业务专家、法务和客服主管共同制定,确保了仲裁结果的专业性和权威性。
模块三:自我审计日志(Self-Audit Log) 这是整个系统最“酷”的部分。每一个智能体,在完成自己的任务后,都会向一个中央日志系统写入一条结构化的审计记录。这条记录不仅包含“做了什么”、“结果是什么”,更关键的是包含“为什么这么做”和“做得怎么样”。例如,协调智能体的日志会记录:“分解出4个子任务,因为原始请求中包含了‘用户’、‘订单’、‘物流’、‘政策’四个核心实体,且实体间存在强关联性”。订单分析师的日志会记录:“置信度0.87,因API调用成功,状态码‘SHIPPED’与知识库中‘已发货’定义完全匹配,且在历史100个相似案例中,92%的处理结果获得用户好评”。这些日志,不仅是故障排查的黄金线索,更是系统自我进化的燃料。我们的数据团队每周会分析这些日志,找出置信度持续偏低的子任务,然后针对性地优化其背后的模型、工具或规则。
4. 实操过程与核心环节实现:从代码到上线的全流程
4.1 环境准备与核心依赖安装
在开始编码之前,我们必须构建一个稳定、可复现的开发环境。我们摒弃了过于复杂的Kubernetes集群,选择了轻量但足够强大的Docker Compose方案,这让我们能在一台32核/128GB内存的开发服务器上,完整模拟生产环境。
首先,创建一个 docker-compose.yml 文件,定义四个核心服务:
version: '3.8'
services:
# 协调智能体服务,基于FastAPI
orchestrator:
build: ./orchestrator
ports: ["8001:8000"]
environment:
- MODEL_NAME=Qwen2.5-14B-Instruct
- TOOL_REGISTRY_URL=http://tool-registry:8002
# 工具注册中心,一个轻量级的HTTP服务,管理所有可用工具的元数据
tool-registry:
build: ./tool-registry
ports: ["8002:8000"]
# 向量数据库,我们选用Qdrant,因其对中文支持极佳且性能卓越
qdrant:
image: qdrant/qdrant
ports: ["6333:6333"]
volumes:
- ./qdrant_data:/qdrant/storage
# Redis,作为所有智能体的共享缓存和消息队列
redis:
image: redis:7-alpine
ports: ["6379:6379"]
核心Python依赖库的选择,是经过反复权衡的:
-
langchain-core和langgraph:这是整个智能体框架的骨架。langgraph提供的有向无环图(DAG)能力,完美契合我们“协调-执行-合成”的三层架构,让我们能用声明式的方式定义智能体间的调用关系,而不是写一堆混乱的回调函数。 -
llamaindex:用于构建和管理各个执行智能体背后的专用知识库。它对PDF、Word、HTML等非结构化文档的解析能力,远超我们自己写的解析器。 -
httpx:替代requests,因为它原生支持异步,能让我们在协调智能体中,并行发起多个API调用,将整体响应时间从平均4.5秒压缩到2.3秒。 -
pydanticv2 :用于定义所有智能体之间传递的、严格校验的数据模型。每一个输入指令、每一个输出结果,都必须符合一个BaseModel,这从根本上杜绝了因数据格式错误导致的“幽灵bug”。
安装命令如下(在虚拟环境中执行):
pip install "langchain-core>=0.3.0" "langgraph>=0.2.0" "llamaindex>=0.11.0" httpx pydantic[dotenv] python-dotenv
# 安装Qdrant客户端
pip install qdrant-client
# 安装Redis客户端
pip install redis
4.2 协调智能体的核心代码:任务分解的艺术
协调智能体的 main.py 文件,是整个系统的“心脏”。它的核心逻辑,是将一个模糊的自然语言请求,转化为一张精确的、可执行的“任务蓝图”。以下是其最关键的 decompose_task 函数的实现:
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from typing import List, Optional
# 定义任务蓝图的数据模型
class SubTask(BaseModel):
"""一个子任务的定义"""
name: str = Field(description="子任务的唯一标识符,如 'fetch_order_status'")
description: str = Field(description="子任务的详细描述,说明它要做什么")
required_tools: List[str] = Field(description="执行此任务所必需的工具列表")
input_schema: dict = Field(description="该任务所需的输入参数的JSON Schema")
class TaskBlueprint(BaseModel):
"""整个任务的蓝图"""
original_query: str = Field(description="用户的原始查询")
sub_tasks: List[SubTask] = Field(description="分解出的子任务列表")
reasoning: str = Field(description="分解的逻辑依据,为什么需要这些子任务")
# 创建一个专门用于任务分解的提示词模板
DECOMPOSE_PROMPT = ChatPromptTemplate.from_messages([
("system", """你是一个顶级的AI系统架构师,负责将一个复杂的用户请求,分解为一组最小、最独立、且可并行执行的子任务。
你的输出必须严格遵循以下原则:
1. 每个子任务必须有明确的、唯一的name,且name必须是预定义工具列表中的一个。
2. 子任务之间不能有强依赖,必须能并行执行。
3. 如果一个子任务的输出是另一个子任务的输入,那么这两个任务必须合并为一个。
4. 你的分解必须基于对用户查询中所有实体(人、物、事、地点、时间)及其关系的深度理解。
请使用JSON格式输出,不要有任何额外的解释文字。"""),
("human", "{query}")
])
# 初始化一个专门用于分解的小模型
decomposer_llm = ChatQwen(model_name="Qwen2.5-14B-Instruct", temperature=0.1)
# 创建输出解析器
parser = PydanticOutputParser(pydantic_object=TaskBlueprint)
# 核心分解函数
def decompose_task(query: str) -> TaskBlueprint:
"""
将用户查询分解为可执行的任务蓝图
"""
# 构建提示词
prompt_value = DECOMPOSE_PROMPT.invoke({"query": query})
# 调用模型
response = decomposer_llm.invoke(prompt_value)
# 解析并校验输出
try:
blueprint = parser.parse(response.content)
# 额外的业务规则校验:确保所有required_tools都在注册中心存在
for sub_task in blueprint.sub_tasks:
for tool_name in sub_task.required_tools:
if not tool_registry.is_tool_available(tool_name):
raise ValueError(f"Required tool '{tool_name}' is not available in registry.")
return blueprint
except Exception as e:
# 如果模型输出格式错误,启动一个基于规则的兜底分解器
logger.warning(f"LLM decomposition failed: {e}. Falling back to rule-based decomposer.")
return rule_based_decomposer(query)
这段代码的精妙之处在于,它不仅仅是一个“调用大模型”的简单封装。它内置了 三层防御机制 :第一层是 PydanticOutputParser ,强制模型输出符合预定义Schema的JSON,避免了字符串解析的脆弱性;第二层是 tool_registry.is_tool_available() 的业务校验,确保模型不会“幻想”出一个根本不存在的工具;第三层是 rule_based_decomposer 的兜底逻辑,当大模型彻底失灵时,一个基于关键词匹配和预设规则的轻量级分解器会接管,保证系统永不宕机。这种“AI为主,规则为辅”的设计哲学,是我们整个系统稳定性的基石。
4.3 执行智能体的工具调用:安全、可靠、可追溯
执行智能体的代码,其核心挑战不在于“怎么调用”,而在于“怎么调用得安全、可靠、可追溯”。我们为每一个工具,都编写了一个标准化的“工具包装器”(Tool Wrapper)。以“订单分析师”为例,其 order_analyzer.py 文件如下:
import httpx
import json
from datetime import datetime
from typing import Dict, Any
from pydantic import BaseModel, Field
from langchain_core.tools import BaseTool
class OrderAnalysisInput(BaseModel):
"""订单分析工具的输入参数"""
order_id: str = Field(description="订单的唯一ID")
user_id: str = Field(description="用户的唯一ID")
class OrderAnalysisOutput(BaseModel):
"""订单分析工具的输出结果"""
order_status: str = Field(description="订单的当前状态,如 'SHIPPED', 'DELIVERED'")
estimated_delivery: str = Field(description="预计送达时间,ISO 8601格式")
logistics_provider: str = Field(description="承运物流公司名称")
confidence_score: float = Field(description="本次分析的置信度,0.0-1.0")
audit_log: str = Field(description="本次分析的详细审计日志,包含所有决策依据")
class OrderAnalyzerTool(BaseTool):
name = "fetch_order_status"
description = "从订单中心API获取订单的最新状态、物流信息和预计送达时间。"
args_schema = OrderAnalysisInput
def _run(self, order_id: str, user_id: str) -> str:
"""
执行订单分析的核心逻辑
"""
# 1. 记录开始时间,用于性能监控
start_time = datetime.now()
# 2. 构造API请求
api_url = f"https://api.order-center.com/v1/orders/{order_id}"
headers = {"Authorization": f"Bearer {self.get_api_token()}"}
# 3. 使用httpx进行异步调用,并设置超时和重试
try:
with httpx.Client(timeout=5.0, transport=httpx.HTTPTransport(retries=2)) as client:
response = client.get(api_url, headers=headers)
response.raise_for_status()
raw_data = response.json()
except httpx.HTTPStatusError as e:
# API返回了4xx或5xx错误
error_log = f"API Error {e.response.status_code}: {e.response.text}"
return self._build_error_output(error_log, start_time)
except httpx.TimeoutException:
# 请求超时
error_log = "API Timeout after 5 seconds"
return self._build_error_output(error_log, start_time)
except Exception as e:
# 其他未知错误
error_log = f"Unexpected error: {str(e)}"
return self._build_error_output(error_log, start_time)
# 4. 解析原始数据,计算置信度
parsed_result = self._parse_raw_data(raw_data, order_id, user_id)
# 5. 构建最终输出
output = OrderAnalysisOutput(
order_status=parsed_result["status"],
estimated_delivery=parsed_result["eta"],
logistics_provider=parsed_result["provider"],
confidence_score=parsed_result["confidence"],
audit_log=self._generate_audit_log(raw_data, parsed_result, start_time)
)
return output.json()
def _parse_raw_data(self, raw_data: Dict, order_id: str, user_id: str) -> Dict[str, Any]:
"""
解析原始API响应,这是业务逻辑的核心
"""
# 这里是业务专家定义的规则
status_map = {
"1": "CREATED",
"2": "PAID",
"3": "CONFIRMED",
"4": "SHIPPED",
"5": "DELIVERED",
"6": "CANCELLED"
}
# 从原始数据中提取关键字段
status_code = raw_data.get("status", "0")
eta = raw_data.get("estimated_delivery", "")
provider = raw_data.get("logistics", {}).get("provider", "Unknown")
# 计算置信度:基于API成功率、状态码映射准确率、历史匹配度
api_success_rate = self._get_api_success_rate() # 从Redis缓存中读取
status_accuracy = 1.0 if status_code in status_map else 0.0
historical_match = self._get_historical_match_score(order_id, user_id) # 从Qdrant中查询
confidence = (api_success_rate * 0.4) + (status_accuracy * 0.3) + (historical_match * 0.3)
return {
"status": status_map.get(status_code, "UNKNOWN"),
"eta": eta,
"provider": provider,
"confidence": round(confidence, 2)
}
def _build_error_output(self, error_msg: str, start_time: datetime) -> str:
"""构建错误输出,同样包含置信度和审计日志"""
end_time = datetime.now()
duration = (end_time - start_time).total_seconds()
error_output = OrderAnalysisOutput(
order_status="ERROR",
estimated_delivery="",
logistics_provider="",
confidence_score=0.0,
audit_log=f"Error occurred at {start_time.isoformat()}. Duration: {duration:.2f}s. Message: {error_msg}"
)
return error_output.json()
def _generate_audit_log(self, raw_data: Dict, parsed_result: Dict, start_time: datetime) -> str:
"""生成详细的审计日志"""
end_time = datetime.now()
duration = (end_time - start_time).total_seconds()
log_parts = [
f"Analysis started at {start_time.isoformat()}",
f"API call completed in {duration:.2f}s",
f"Raw status code: {raw_data.get('status', 'N/A')}",
f"Parsed status: {parsed_result['status']}",
f"Confidence score: {parsed_result['confidence']}",
f"Historical match score: {self._get_historical_match_score('', '')}"
]
return " | ".join(log_parts)
这个工具包装器,完美体现了我们对“安全、可靠、可追溯”的追求。它包含了 完整的错误处理链路 (HTTP状态错误、超时、未知异常), 精确的置信度计算 (融合了实时指标、业务规则和历史数据),以及 详尽的审计日志 (记录了每一毫秒的耗时、每一次决策的依据)。当一个客服人员看到系统给出的建议时,他背后所依赖的,正是这样一段经过千锤百炼、每一个字节都经得起推敲的代码。
4.4 合成智能体与上线部署:让AI说出人话
合成智能体是整个链条的终点,也是用户感知的起点。它的任务,是将所有冰冷的、结构化的数据,转化为一段温暖的、专业的、可直接发送给客户的文字。它的 synthesizer.py 文件,其核心是一个基于模板的、带有条件逻辑的渲染引擎。
我们没有使用简单的 Jinja2 模板,而是设计了一个更强大的 AdaptiveTemplateEngine :
from jinja2 import Environment, BaseLoader, Template
from typing import Dict, Any
class AdaptiveTemplateEngine:
def __init__(self, templates: Dict[str, str]):
self.env = Environment(loader=BaseLoader())
self.templates = templates
def render(self, template_key: str, context: Dict[str, Any]) -> str:
"""
根据模板键和上下文,动态渲染最终文本
"""
# 1. 首先,根据上下文中的关键指标,动态选择最合适的模板变体
template_variant = self._select_template_variant(template_key, context)
# 2. 获取模板字符串
template_str = self.templates.get(template_variant, self.templates[template_key])
# 3. 创建Jinja2模板
template = self.env.from_string(template_str)
# 4. 渲染,并注入一些全局的、动态的辅助函数
rendered = template.render(
**context,
# 注入一个“智能语气调节器”函数
tone_adjuster=self._tone_adjuster,
# 注入一个“风险规避措辞生成器”函数
risk_warden=self._risk_warden更多推荐

所有评论(0)