Spaces:
Running
Running
Agno Structured Output 集成技术文档
本文档记录了对后端生成服务(Generation Services)进行重构,集成 Agno output_schema(结构化输出)的技术细节与架构变更。
1. 为什么集成 Agno Structured Output?
在重构之前,系统面临以下挑战:
- 模型输出不稳:不同的大模型(如 GLM, Gemini 等)可能会返回带有 Markdown 标签、Python 代码块或解释性文字的非标准内容。
- 解析代码脆弱:原先使用正则表达式或简单的
json.loads来提取数据,一旦模型返回格式稍有偏差(例如将字段名space_label改为space),解析就会失败。 - 缺乏类型安全:后端逻辑直接操作不确定的字典或字符串,容易引发运行时错误。
集成 Agno 结构化输出的主要优势:
- 强类型约束:通过 Pydantic 模型(Schema)告诉 AI 需要填写的“表格”格式。
- 内建解析:Agno 在底层自动处理格式校验和重试逻辑。
- 架构一致性:核心生成逻辑统一走“Schema -> Object”链路。
2. 逻辑对比
2.1 重构前逻辑 (旧)
- 下发 Prompt:手动在提示词里写“返回 JSON,包含 a, b 字段”。
- 获取文本:从流式响应中累加
full_content字符串。 - 手动解析:用正则表达式尝试从
full_content提取 JSON 或特定字段。 - 容错处理:如果 JSON 报错,尝试一些简单的字符串截取。
- 返回结果:返回一个字段缺失风险较高的字典。
2.2 重构后逻辑 (新 - 防御性双轨制)
- 定义 Schema:在
models/generation.py中定义 Pydantic 模型(如TitleSpaceResponse)。 - 底层传递:
StreamChatService将 Schema 传给 Agno 处理。 - 核心:防御性双轨获取结果:
我们刻意保留并增强了原有的
safe_json_parse和正则表达式逻辑,形成了所谓的“防御性双轨制”:- **第一轨:Agno 结构化提取 (优先)**:直接从响应中提取 Agno 转换好的 Pydantic 对象。这是最理想的情况。
- **第二轨:手动解析与正则兜底 (核心保险)**:
- 为什么要保留?:大模型(尤其是 GLM 等)是非确定性的。即便有 Schema 约束,它们偶尔仍会由于各种原因(如 Context 干扰、模型微调差异)返回纯文本、错误的 JSON 键名或 Python 风格的赋值语句(如
title='...')。 - 作用:当 Agno 的内建解析器因为格式极其混乱而报错时,手动逻辑(
safe_json_parse+ 增强正则)会介入。这保证了即使大模型“发疯”,应用层依然能顽强地从乱码中抠出关键数据,而不是返回一个冷冰冰的错误或空结果。
- 为什么要保留?:大模型(尤其是 GLM 等)是非确定性的。即便有 Schema 约束,它们偶尔仍会由于各种原因(如 Context 干扰、模型微调差异)返回纯文本、错误的 JSON 键名或 Python 风格的赋值语句(如
- 归一化处理:针对模型输出的细微偏差(如
space_labelvsspaceLabel)或描述溢出进行最终清洗。
3. 做的主要变更
3.1 模型层 (src/models/generation.py)
- 新增了
TitleResponse,TitleSpaceResponse,TitleSpaceAgentResponse,RelatedQuestionsResponse,DailyTipResponse五大 Pydantic 模型。 - 集成了
field_validator来自动修复模型常见的表情符号返回格式问题。
3.2 基础服务层
stream_chat.py: 增加了对output_schema的流式接收与封装,通过DoneEvent将结构化对象透传给业务层。llm_utils.py: 更新了run_agent_completion工具函数,确保它在任务完成时同时返回文本和结构化对象。
3.3 业务逻辑层 (src/services/generation.py)
- 重构了 5 个核心生成函数,使其完全支持 Agno 的结构化输出。
- 为每个函数增加了防御性编程逻辑:结构化解析 -> JSON 安全解析 -> 强力正则匹配 -> 归一化清洗。
4. 集成效果
| 功能模块 | 改进点 |
|---|---|
| 标题生成 | 不再受 Markdown 块干扰,标题长度和表情更精准。 |
| 空间/Agent 识别 | 能够准确剥离描述语,确保返回的 Label 与数据库 100% 匹配。 |
| 相关问题 | 解决了模型返回纯文本列表而非 JSON 导致按钮显示不出的顽疾。 |
| 每日贴士 | 确保只返回干货文字,杜绝开场白和结束语。 |
5. 结论
通过此次集成,系统将原本依赖“运气”和“正则”的解析逻辑,进化为一套由模型驱动、由 Schema 约束、由代码兜底的稳健传输体系。这不仅大幅提升了后端生成的成功率,也为未来支持更复杂的交互功能打下了坚实的基础。