Java/Go后端手撸原生Agent(第二篇):Pydantic结构化输出改造,工程化解决工具死循环问题
上篇链接Java/Go后端手撸原生Agent”我“Java/Go后端开发者、有点时间想自己琢磨想入门Agent但不想堆砌框架、希望理解底层原理的研发正文前言上篇文章我们基于纯Python原生代码不依赖LangChain/LangGraph从零搭建了一套基础ReAct智能体实现了工具抽象、短期记忆、裸HTTP调用大模型。上篇完整工程环境配置、LLM客户端、工具基类、文本解析ReAct、主循环调度。本文作为续集完成两大核心工程级升级抛弃脆弱的文本分割解析使用Pydantic实现结构化JSON输出对标厂商标准Function Call修复原生Agent高频Bug工具重复调用死循环采用状态机工程化方案。前置说明完全复用上篇所有基础文件env_loader.py、memory.py、tools/工具包、llm_client.py基础版本新增文件agent/schema.pyPydantic实体、agent/structured_parser.py结构化解析器删除旧文本解析器parser.py核心改造main.py主循环、消息组装规范、死循环防护逻辑。一、拓展1Pydantic结构化输出改造替代文本ReAct解析1.1 改造思路后端视角文本分割解析硬伤LLM输出换行、注释、markdown会直接解析崩溃容错极低Pydantic等价Java POJO / Go Struct强类型校验、自动序列化/反序列化开启模型response_formatjson_object强制返回标准JSON对齐OpenAI/通义/DeepSeek原生Function Call协议注部分模型可能不支持该字段双分支实体工具调用动作、任务完成最终回答二选一输出。1.2 新建结构化模型 agent/schema.pyfrompydanticimportBaseModel,FieldfromtypingimportDict,Union# 场景1需要调用工具classToolAction(BaseModel):thought:strField(description推理思考过程)action:strField(description工具名称无工具固定填 FINISH)params:DictField(description工具入参json对象无参数传空{})# 场景2任务结束直接返回最终答案classFinishResponse(BaseModel):final_answer:strField(description无需调用工具整理后给用户的最终回答)# 联合类型LLM输出二选一AgentOutputToolAction|FinishResponse1.3 改造LLM客户端 llm_client.py支持JSON强输出新增json_mode参数控制结构化输出开关底层透传response_formatfrompydanticimportBaseModelfromenv_loaderimportLLMConfigimportrequestsclassResponseFormat(BaseModel):type:strclassChatRequest(BaseModel):model:strmessages:list[dict[str,str]]temperature:floatresponse_format:ResponseFormat|NoneNonedefchat_completion(messages:list[dict[str,str]],json_mode:boolFalse# 是否开启结构化输出)-str:headers{Authorization:fBearer{LLMConfig.API_KEY},Content-Type:application/json}reqChatRequest(modelLLMConfig.MODEL_NAME,messagesmessages,temperature0.1)# 开启json强制输出ifjson_mode:req.response_formatResponseFormat(typejson_object)bodyreq.model_dump()resprequests.post(f{LLMConfig.BASE_URL}/chat/completions,headersheaders,jsonbody)resp.raise_for_status()returnresp.json()[choices][0][message][content]#测试入口if__name____main__:reschat_completion([{role:user,content:你好}],json_modeTrue)print(res)1.4 结构化解析器 agent/structured_parser.py替代旧文本解析器自动JSON反序列化为Pydantic实体自带参数校验importjsonfromagent.schemaimportAgentOutput,ToolAction,FinishResponseclassStructuredParser:staticmethoddefparse_json(raw_json_str:str)-AgentOutput|None: 解析大模型返回的JSON字符串自动区分 ToolAction / FinishResponse try:datajson.loads(raw_json_str)ifactionindata:returnToolAction(**data)eliffinal_answerindata:returnFinishResponse(**data)else:returnNoneexceptExceptionase:print(f结构化解析失败{str(e)}原始输出{raw_json_str})returnNone1.5 规范消息组装角色隔离修复字符串拼接Bug上篇存在致命不规范写法将system规则全部历史对话拼接为一段字符串塞进单条system消息角色边界丢失模型极易混淆user/工具观测数据。工程标准写法第一条消息固定全局System规则记忆内每条对话独立追加区分user/assistant/system(工具观测)# 标准消息组装代码messages[{role:system,content:SYSTEM_PROMPT}]# 第二步追加所有历史对话、工具观测记录每条独立role隔离messages.extend(memory.get_raw_dict_list())同步微调短期记忆agent/memory.py保证输出标准OpenAI消息结构fromtypingimportTypedDictclassMessage(TypedDict):role:strcontent:strclassShortMemory:def__init__(self):self.history:list[Message][]defadd_user(self,content:str):用户的输入self.history.append({role:user,content:content})defadd_assistant(self,content:str):self.history.append({role:assistant,content:content})defadd_observation(self,content:str):工具返回的观察结果self.history.append({role:system,content:content})defget_all(self)-list[Message]:returnself.history.copy()defget_raw_dict_list(self)-list[dict[str,str]]:# 直接返回原生dict数组适配LLM入参return[{role:m[role],content:m[content]}forminself.history]1.6 重写系统提示词约束模型输出行为不再混入对话上下文仅定义全局规则、工具、输出格式上下文完全交给messages数组管理SYSTEM_PROMPT 你是支持工具调用的智能助手必须仅输出纯JSON禁止额外文字、Markdown、换行注释。 可用工具 calculator数学计算器参数expr为数学表达式例{expr:(10020)*5} 严格遵守执行规则 1. 首次缺少数值时调用calculator获取计算结果 2. 一旦收到system角色的工具观测结果已算出数字**禁止再次调用任何工具**必须直接输出final_answer总结答案 3. 两种输出格式严格二选一 - 需要调用工具时{thought:推理过程,action:工具名称,params:{expr:表达式}} - 已有工具计算结果、无需工具{final_answer:把计算结果整理成自然语言回答用户} 二、线上运行Bug复现工具无限重复调用死循环2.1 问题现象执行计算(100 20) * 5模型第一次调用计算器拿到结果后无视提示词约束持续重复调用同一工具耗尽最大循环次数才终止不会输出final_answer。完整日志特征第一轮用户提问 → 调用计算器 → 存入system观测记录第二轮上下文携带观测结果模型偶尔输出final_answer但代码未立刻return循环继续后续轮次上下文叠加历史assistant回答模型逻辑混乱反复执行工具直到max_loop5结束。2.2 根因工程层面非单纯提示词问题核心逻辑漏洞解析到FinishResponse仅写入记忆无return终止循环无显式任务状态标记全靠模型输出控制流程模型不稳定直接失效缺少多层前置拦截防护仅靠提示词约束容错率极低未检测记忆中已存在最终回答仍继续执行LLM推理。三、工程化根治方案状态机3.1 设计思路后端标准状态机思想新增任务状态枚举显式区分运行/完成状态驱动流程命中FinishResponse立即return终止整个函数循环耗尽兜底逻辑读取已生成的历史回答避免无结果返回可不做真实遇到这样说明我们的处理流程还是有问题。3.2 完整重写 run_agent 主循环 main.pyfromagent.memoryimportShortMemoryfromenumimportEnumfromagent.schemaimportFinishResponse,ToolActionfromagent.structured_parserimportStructuredParserfromllm_clientimportchat_completionfromtools.base_toolimportBaseToolfromtools.calculatorimportCalcTool# 1. 新增状态枚举工程化状态机替代隐式类型判断classAgentTaskState(Enum):RUNNINGrunningFINISHEDfinished# 注册所有可用工具tool_list:list[BaseTool][CalcTool()]tool_map{t.name:tfortintool_list}# 系统提示词规定ReAct输出格式SYSTEM_PROMPT 你是支持工具调用的智能助手必须仅输出纯JSON禁止额外文字、Markdown、换行注释。 可用工具 calculator数学计算器参数expr为数学表达式例{expr:(10020)*5} 严格遵守执行规则 1. 首次缺少数值时调用calculator获取计算结果 2. 一旦收到system角色的工具观测结果已算出数字**禁止再次调用任何工具**必须直接输出final_answer总结答案 3. 两种输出格式严格二选一 - 需要调用工具时{thought:推理过程,action:工具名称,params:{expr:表达式}} - 已有工具计算结果、无需工具{final_answer:把计算结果整理成自然语言回答用户} defrun_agent(user_query:str):memoryShortMemory()memory.add_user(user_query)max_loop5foriinrange(max_loop):# 标准规范消息组装固定system在前记忆消息在后 # 第一步根系统提示词全局规则、工具定义、输出JSON约束独立system消息messages[{role:system,content:SYSTEM_PROMPT}]# 第二步追加所有历史对话、工具观测记录每条独立role隔离messages.extend(memory.get_raw_dict_list())# 调试打印完整上下文print( 当前完整上下文 )formsginmessages:print(msg)# 调用LLM开启结构化JSON输出llm_raw_jsonchat_completion(messages,json_modeTrue)parse_resStructuredParser.parse_json(llm_raw_json)ifparse_resisNone:returnf模型输出格式解析失败原始内容{llm_raw_json}# 分支1任务完成直接返回最终答案ifisinstance(parse_res,FinishResponse):memory.add_assistant(parse_res.final_answer)task_stateAgentTaskState.FINISHEDreturnparse_res.final_answer# 分支2执行工具调用流程ifisinstance(parse_res,ToolAction):tool_nameparse_res.action tool_paramsparse_res.paramsprint(f【推理思考】{parse_res.thought})print(f【工具调用】name{tool_name}, params{tool_params})tooltool_map.get(tool_name)ifnottool:obsf异常不存在工具{tool_name}else:obstool.run(tool_params)print(f【工具返回结果】{obs}\n)# 工具观测存入记忆下一轮循环自动拼入messagesmemory.add_observation(obs)returnf达到最大循环次数{max_loop}任务未完成if__name____main__:answerrun_agent(计算 (100 20) * 5)print(最终回答:,answer)四、核心修复点总结修复致命逻辑缺陷命中FinishResponse直接return杜绝循环继续执行消息分层规范独立system全局规则多角色分离历史消息对齐大模型标准对话协议显式状态机管控AgentTaskState枚举统一管理任务流转方便后续扩展超时、中断、分支流程Pydantic结构化替代文本解析消除LLM输出不规则导致的解析崩溃问题。五、修复后预期运行效果 当前完整上下文 {role: system, content: \n你是支持工具调用的智能助手必须仅输出纯JSON禁止额外文字、Markdown、换行注释。\n可用工具\ncalculator数学计算器参数expr为数学表达式例{expr:(10020)*5}\n\n严格遵守执行规则\n1. 首次缺少数值时调用calculator获取计算结果\n2. 一旦收到system角色的工具观测结果已算出数字**禁止再次调用任何工具**必须直接输出final_answer总结答案\n3. 两种输出格式严格二选一\n- 需要调用工具时{thought:推理过程,action:工具名称,params:{expr:表达式}}\n- 已有工具计算结果、无需工具{final_answer:把计算结果整理成自然语言回答用户}\n} {role: user, content: 计算 (100 20) * 5} 【推理思考】用户需要计算数学表达式 (100 20) * 5我需要调用计算器工具来获取结果。 【工具调用】namecalculator, params{expr: (100 20) * 5} 【工具返回结果】计算结果: (100 20) * 5 600 当前完整上下文 {role: system, content: \n你是支持工具调用的智能助手必须仅输出纯JSON禁止额外文字、Markdown、换行注释。\n可用工具\ncalculator数学计算器参数expr为数学表达式例{expr:(10020)*5}\n\n严格遵守执行规则\n1. 首次缺少数值时调用calculator获取计算结果\n2. 一旦收到system角色的工具观测结果已算出数字**禁止再次调用任何工具**必须直接输出final_answer总结答案\n3. 两种输出格式严格二选一\n- 需要调用工具时{thought:推理过程,action:工具名称,params:{expr:表达式}}\n- 已有工具计算结果、无需工具{final_answer:把计算结果整理成自然语言回答用户}\n} {role: user, content: 计算 (100 20) * 5} {role: system, content: 计算结果: (100 20) * 5 600} 最终回答: 计算结果是 600仅执行一轮工具调用直接输出最终回答不会出现多轮重复调用、耗尽循环的问题。六、后续拓展自动基于Pydantic模型生成Function Call标准工具描述Schema对接模型原生工具调用接口新增文件读取工具FileReadTool实现代码读取Agent接入Chroma向量数据库实现长期记忆RAG封装AgentEngine类面向对象工程化重构拆分日志、异常、指标模块。

相关新闻

AutoVLA论文阅读笔记

AutoVLA论文阅读笔记

论文:https://arxiv.org/pdf/2506.13757 代码: 1、为什么要做这个研究(理论走向和目前缺陷) ? 之前的vla模型要么结构复杂,要么梯度断连,自回归式的输出路点虽然优雅,但是llm天然不太适合这种精…

2026/7/30 11:44:49 阅读更多 →
基于大模型的智能留学咨询系统设计与优化

基于大模型的智能留学咨询系统设计与优化

1. 项目概述:留学咨询大模型的智能办理系统这个项目本质上是一个基于大语言模型的智能留学咨询系统,核心目标是通过AI技术实现留学申请流程的自动化处理。不同于传统留学中介的人工服务模式,我们采用大模型作为底层引擎,结合业务规…

2026/7/30 9:07:44 阅读更多 →
项目开发必备:如何系统管理前置条件(Prerequisites)

项目开发必备:如何系统管理前置条件(Prerequisites)

1. 项目概述"0.Prerequisites"这个标题看似简单,却蕴含着项目开发中最容易被忽视的关键环节。作为从业十余年的老手,我见过太多项目因为前期准备不足而中途夭折的案例。这个编号为0的阶段,实际上决定了整个项目的成败基础。在技术领…

2026/7/28 4:22:46 阅读更多 →

最新新闻

魔兽争霸III终极兼容性工具:5个技巧让经典游戏在现代电脑上完美运行

魔兽争霸III终极兼容性工具:5个技巧让经典游戏在现代电脑上完美运行

魔兽争霸III终极兼容性工具:5个技巧让经典游戏在现代电脑上完美运行 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 你是否还在为《魔兽争…

2026/7/30 11:44:40 阅读更多 →
BBWEYY 跨境电商低成本获客转化解决方案:30天搭建BBWEYY GEO与独立站获客转化闭环,含零代码SAAS、AI编程、源码定制交付

BBWEYY 跨境电商低成本获客转化解决方案:30天搭建BBWEYY GEO与独立站获客转化闭环,含零代码SAAS、AI编程、源码定制交付

跨境电商实战指南 30天搭建BBWEYY GEO与独立站获客转化闭环 从品牌诊断、站点上线到内容信源与数据复盘的执行指南 干货分享|准备快速启动跨境独立站和GEO项目的企业团队 30天不能完成所有增长工作,但可以搭好一个能够持续迭代的获客底座,…

2026/7/30 11:44:40 阅读更多 →
BBWEYY 跨境电商低成本获客转化解决方案:AI搜索时代,跨境品牌用BBWEYY GEO提升海外曝光实战,含零代码SAAS、AI编程、源码定制交付

BBWEYY 跨境电商低成本获客转化解决方案:AI搜索时代,跨境品牌用BBWEYY GEO提升海外曝光实战,含零代码SAAS、AI编程、源码定制交付

跨境电商实战指南 AI搜索时代,跨境品牌用BBWEYY GEO提升海外曝光实战 从品牌提及监测到可信信源建设的完整方法 干货分享|正在布局海外品牌、内容营销与AI搜索曝光的企业 AI搜索不会因为企业发布更多广告就自动推荐品牌,它更依赖清晰、可信…

2026/7/30 11:44:40 阅读更多 →
接口自动化测试总结

接口自动化测试总结

🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快想要在软件测试这个行业继续前行,就必须拥有核心竞争力,掌握自动化测试技术,是必不可少的一个技能。目前国内的互联网行业变化很…

2026/7/30 11:44:40 阅读更多 →
BBWEYY 跨境电商低成本获客转化解决方案:亚马逊卖家用BBWEYY独立站降低单一平台依赖实战,含零代码SAAS、AI编程、源码定制交付

BBWEYY 跨境电商低成本获客转化解决方案:亚马逊卖家用BBWEYY独立站降低单一平台依赖实战,含零代码SAAS、AI编程、源码定制交付

跨境电商实战指南 亚马逊卖家用BBWEYY独立站降低单一平台依赖实战 保留亚马逊成交优势,同时建立自主品牌、客户与复购渠道 干货分享|亚马逊精品、铺货、品牌备案与多站点卖家 独立站不是与亚马逊二选一,而是帮助卖家获得品牌表达、客户沉淀…

2026/7/30 11:44:40 阅读更多 →
视频驱动的空间智能灾害推演技术解析与应用

视频驱动的空间智能灾害推演技术解析与应用

1. 项目概述:当灾害推演遇上空间智能 去年参与某地防汛指挥系统升级时,我第一次见识到传统灾害推演的局限性——决策者们围坐在二维平面图前,对着静态的风险标识争论不休。这种脱离空间维度的推演方式,就像用象棋规则下立体围棋。…

2026/7/30 11:43:40 阅读更多 →

日新闻

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 您是否曾因Windows系统盘空间不足而烦恼?是否遇到过设…

2026/7/30 0:00:13 阅读更多 →
如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper:网页视频下载的完整实战指南 【免费下载链接】VideoDownloadHelper Chrome Extension to Help Download Video for Some Video Sites. 项目地址: https://gitcode.com/gh_mirrors/vi/VideoDownloadHelper 你是否曾经在浏览…

2026/7/30 0:00:13 阅读更多 →
“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

更多请点击: https://intelliparadigm.com 第一章:AI 教师备课辅助 AI 教师备课辅助系统正逐步成为教育数字化转型的核心支撑工具,它并非替代教师,而是通过语义理解、知识图谱与多模态生成能力,将教师从重复性劳动中解…

2026/7/30 0:00:13 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/29 22:18:20 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/29 15:00:03 阅读更多 →

月新闻