Atomic Agents 结构化 I/O 指南:用 `BaseIOSchema` 为 Agent 定义输入输出契约 AI AgentAgent 框架MCP 服务后端【免费下载链接】atomic-agentsBuilding AI agents, atomically项目地址https://gitcode.com/gh_mirrors/at/atomic-agents点击查看免费下载BaseIOSchema是 Atomic Agents 框架中所有 Agent 输入/输出数据结构的统一基类它把 Pydantic 的强类型校验、强制文档化约束与 Instructor 的 JSON Schema 注入机制无缝衔接。本文以框架官方参考文档claude-plugin/atomic-agents/skills/framework/references/schemas.md为主线结合仓库源码与测试用例系统讲解 Schema 的定义规则、字段模式、验证器、组合与错误模式帮助读者写出能被 LLM 稳定解析、可校验、可复用的结构化数据契约。一、BaseIOSchema强制执行的规则BaseIOSchema是一个 PydanticBaseModel子类并通过元类钩子在类定义时执行检查如果类没有 docstring或 docstring 只有空白字符会立即抛出ValueError。这一设计的目的在于框架覆写了model_json_schema()把类 docstring 变成 JSON Schema 的description类名变成title而 Instructor 在构造 LLM 提示词时恰好会用到这两项因此 docstring 必须“写给模型看”而不只是写给人类看。from pydantic import Field from atomic_agents import BaseIOSchema class SearchQuery(BaseIOSchema): Parameters for a web search issued by the agent. query: str Field(..., descriptionNatural-language search query.) limit: int Field(default10, ge1, le100, descriptionMaximum results to return.)从源码看atomic-agents/atomic_agents/base/base_io_schema.py中__pydantic_init_subclass__会在每个子类定义完成后调用_validate_description()当 docstring 为空时抛出ValueError(f{cls.__name__} must have a non-empty docstring ...)。需要注意的是该方法对 Instructor 内部自动生成的 Schema 做了豁免通过cls.__module__前缀与from_streaming_response属性判断以免误伤框架自身的中间类型。model_json_schema()的重写逻辑也很明确调用父类生成 schema 后若description缺失且存在 docstring则用inspect.cleandoc()清洗缩进后写入description若title缺失则写入类名。仓库测试atomic-agents/tests/agents/test_atomic_agent.py中的test_base_io_schema_empty_docstring与test_base_io_schema_model_json_schema_no_description分别验证了这两种行为后者通过 mock 覆盖父类返回空 schema确认覆写逻辑仍会补充 description。docstring 会被传播到哪些地方BaseIOSchema.model_json_schema()的title/description并不仅仅服务于 Instructor 的提示词构造它们还参与了框架内部多个核心环节Prompt 命名atomic-agents/atomic_agents/base/base_prompt.py中BasePrompt.prompt_name取input_schema.model_json_schema()[title]prompt_description取[description]可由BasePromptConfig的title/description覆盖。也就是说类名与 docstring 直接决定 Prompt 在系统中的展示名与说明。工具定义atomic-agents/atomic_agents/agents/atomic_agent.py中_build_tools_definition()在 TOOLS 模式下通过generate_openai_schema(self.output_schema)生成发送给 LLM 的 function schema_build_schema_for_json_mode()则在 JSON 模式下把model_json_schema()序列化后拼入系统消息。默认输入输出框架内置的BasicChatInputSchema/BasicChatOutputSchema同样定义在atomic_agent.py中即为BaseIOSchema的子类分别用 docstring 描述“用户输入”与“Agent 回复”的语义。二、字段模式必填、可选、默认值与description在BaseIOSchema中定义字段的黄金法则是每个字段都必须带description否则 Instructor 没有任何文本可以用来向 LLM 解释该字段的含义模型只能靠字段名猜测解析稳定性无从谈起。参考文档给出的完整字段模式如下from typing import Optional, Literal from pydantic import Field name: str Field(..., descriptionFull legal name.) nickname: Optional[str] Field(defaultNone, descriptionPreferred nickname, if any.) count: int Field(default10, ge1, le100, descriptionItems to return (1–100).) sort: Literal[asc, desc] Field(defaultdesc, descriptionSort order.) tags: list[str] Field(default_factorylist, max_length10, descriptionTag filters (≤10).)逐项拆解必填字段Field(...)Ellipsis表示字段必填必须提供值才能通过校验同时强制要求description。可选字段Optional[str]搭配defaultNone表示该字段可以缺失。若只有Optional[str]而没有默认值字段会变成“必填但允许为 None”这通常不是开发者想要的行为详见“常见错误”一节。默认值字段int Field(default10, ge1, le100)同时声明默认值、最小值和最大值LLM 生成的值也会被 Pydantic 校验。ge/le等约束会被写入 JSON Schema进一步约束模型生成空间。闭集合Literal[asc, desc]把取值限定在两个字面量上defaultdesc给出默认行为。列表字段list[str] Field(default_factorylist, max_length10)使用default_factory保证每次实例化都得到新的空列表避免可变默认值的坑并用max_length限制元素数量上限。参考文档特别强调在闭合取值集合的场景下优先使用Literal[...]而不是Enum——生成的 JSON Schema 更扁平更利于 Instructor 处理。当然Enum在框架中同样受支持见后文“枚举”小节两者各有适用场景。三、验证器字段级与模型级字段级验证器当单个字段需要自定义规则时使用 Pydantic v2 的field_validator。下面的例子把邮箱地址强制转为小写并在缺少时抛出ValueErrorfrom pydantic import field_validator class EmailSchema(BaseIOSchema): An email address. email: str Field(..., descriptionRFC 5322 email address.) field_validator(email) classmethod def _lowercase(cls, v: str) - str: if not in v: raise ValueError(invalid email) return v.lower()模型级验证器跨字段当校验逻辑依赖多个字段的取值关系时使用model_validator(modeafter)。下面的DateRange在完整对象构建之后检查日期顺序保证end不早于startfrom pydantic import model_validator from datetime import date class DateRange(BaseIOSchema): An inclusive date range. start: date Field(..., descriptionStart date (inclusive).) end: date Field(..., descriptionEnd date (inclusive).) model_validator(modeafter) def _ordered(self) - DateRange: if self.end self.start: raise ValueError(end must be on or after start) return self校验失败后的处理链路校验失败并不会导致 Agent 直接崩溃。在 Atomic Agents 中这类错误会转化为Instructor 的重试机制最多重试max_retries次模型根据错误信息自行修正输出同时触发parse:errorhook开发者可以在 hook 中做日志记录、监控告警或自定义修复逻辑详见claude-plugin/atomic-agents/skills/framework/references/agents.md与claude-plugin/atomic-agents/skills/framework/references/hooks.md。需要特别提醒的是流式输出时验证器会在字段逐个出现的瞬间触发因此验证器应保持轻量、幂等避免昂贵计算——AtomicAgent.run_stream()生成的 partial 对象会随着字段填充反复经过校验。参考文档明确给出了这一约束仓库atomic_agent.py中run_stream/run_async_stream的实现也印证了“部分字段先填充、逐帧校验”的运行模型。四、组合、判别联合与枚举嵌套组合把子 Schema 作为字段类型即可实现嵌套结构每个层级都独立享受 docstring 注入与校验class Address(BaseIOSchema): A mailing address. street: str Field(..., descriptionStreet and number.) city: str Field(..., descriptionCity name.) country: str Field(..., descriptionISO 3166-1 alpha-2 country code.) class Person(BaseIOSchema): A person with mailing address. name: str Field(..., descriptionFull name.) address: Address Field(..., descriptionMailing address.)判别联合Discriminated Unions多态输出是结构化 Agent 的常见需求例如一条消息既可以是纯文本也可以是图片。参考文档给出的方案是在每个变体上放置一个Literal判别字段from typing import Literal, Union class TextPart(BaseIOSchema): A text message part. kind: Literal[text] text text: str Field(..., descriptionPlain-text body.) class ImagePart(BaseIOSchema): An image attachment. kind: Literal[image] image url: str Field(..., descriptionPublicly accessible image URL.) class Message(BaseIOSchema): A multimodal message part. part: Union[TextPart, ImagePart] Field(..., descriptionMessage content.)kind字段同时承担“判别标签”与“自我描述”双重职责LLM 只需选择text或imagePydantic 即可据此路由到正确的变体结构。这种模式也是后续“错误 Schema 模式”的基础。枚举当需要命名的固定取值集合时使用继承str的Enum让取值既是成员名也是可序列化的字符串值from enum import Enum class Priority(str, Enum): LOW low MEDIUM medium HIGH high class Task(BaseIOSchema): A unit of work. title: str Field(..., descriptionTask title.) priority: Priority Field(defaultPriority.MEDIUM, descriptionPriority level.)关于枚举有一个来自源码测试的重要细节Instructor 默认strictTrue这会导致枚举字段只能收到枚举实例、阻止 Pydantic 从字符串做常规强转。AtomicAgent._get_completion_kwargs()特意把strict默认值设为None让output_schema自身的 Pydantic 行为生效。atomic-agents/tests/agents/test_atomic_agent.py中的test_run_uses_pydantic_default_strictness_for_enum_output验证了默认情况下food字符串可以被正确转换为Topic.FOOD枚举实例而test_run_respects_explicit_strict_override_for_enum_output则验证了当用户在model_api_parameters中显式传入strictTrue时字符串强转会被拒绝并抛出ValidationError。这意味着在 Agent 中定义枚举字段时默认宽松转换即可正常工作无需为兼容性做额外处理。五、错误 Schema 模式用结构化替代异常当工具或 Agent 存在“合法失败”的可能性时参考文档建议不要抛异常而是把失败建模为结构化的替代输出。两种常见的形态形态一成功/失败成对 Schema通过输出联合返回适合调用方需要穷尽处理每一种情况的场景例如网关、任务编排器class SearchSuccess(BaseIOSchema): Successful search result. results: list[str] Field(..., descriptionMatching items.) class SearchFailure(BaseIOSchema): Search could not complete. error: str Field(..., descriptionHuman-readable failure reason.) code: Literal[rate_limited, no_results, upstream_error] Field( ..., descriptionMachine-readable failure code. ) class SearchOutput(BaseIOSchema): Search output — either success or typed failure. result: Union[SearchSuccess, SearchFailure] Field(..., descriptionOutcome.)code字段使用Literal限定了机器可读的错误码集合下游调用方可以据此做精确的分支路由error提供面向用户的可读原因。形态二单一 Schema 上的判别状态字段适合大多数代码路径只关心status ok的场景例如日志、简单查询包装class SearchOutput(BaseIOSchema): Search result envelope. status: Literal[ok, error] Field(..., descriptionOutcome code.) results: list[str] Field(default_factorylist, descriptionItems when statusok.) error: Optional[str] Field(defaultNone, descriptionMessage when statuserror.)两种形态的选择依据很直白需要穷尽分支用联合 Schema仅需快速判断成败用状态字段。相比抛出异常这种建模让 Agent 的输出契约自包含错误语义LLM 更容易学会“失败也是一种合法答案”同时 Pydantic 仍然全程参与校验。六、常见错误清单参考文档列出的五个高频错误每一类都能在仓库源码或测试中找到对应的反例与后果忘记 docstring框架在类定义导入时直接抛出ValueError(... must have a non-empty docstring ...)test_base_io_schema_empty_docstring就是这一行为的回归测试——空 docstring的类定义会在with pytest.raises上下文中被立即拒绝。使用普通BaseModel而非BaseIOSchema会同时失去“docstring 强制检查”和“model_json_schema()覆写”两层保障。更重要的是AtomicAgent/BasePrompt/BaseTool的泛型参数要求 Schema 必须是BaseIOSchema子类参见atomic-agents/atomic_agents/agents/atomic_agent.py的类型约束传入裸BaseModel会破坏整个结构化链路。Field()不带descriptionInstructor 就没有任何文本可以向 LLM 说明该字段的含义模型只能凭字段名与类型猜测输出命中率大幅下降。这是所有字段模式中最容易忽视、影响却最直接的一点。Optional[str]没有默认值字段会被判定为“必填但允许为 None”——请求时既不能省略、值又可能是None几乎总与开发意图相悖。正确写法是Optional[str] Field(defaultNone, ...)。过度宽泛的类型dict、Any、objectLLM 会自由生成任意结构Pydantic 无法对其做有效校验Schema 退化为“没有 Schema”。应尽量拆分为具名字段、嵌套结构或Literal限定集合。七、落地实践从 Schema 到可运行 Agent最后以一个完整的运行链路收尾参考文档中定义的SearchQuery这样的BaseIOSchema在真实项目中会同时充当 Agent 的输入与输出契约。仓库示例atomic-examples/quickstart/quickstart/2_basic_custom_chatbot.py展示了标准用法——先构造AgentConfigclient为instructor.from_openai(...)包装的客户端再用泛型AtomicAgent[InputSchema, OutputSchema]声明类型运行时通过agent.input_schema(chat_message...)实例化输入agent.run()返回的即是对应输出 Schema 的实例可直接用model_dump()序列化。而BaseIOSchema.__str__与__rich__的实现见atomic-agents/atomic_agents/base/base_io_schema.py分别把实例转为 JSON 字符串与 Rich 可渲染的 JSON 对象方便调试输出与终端展示——test_base_agent_io_str_and_rich即验证了str()输出与model_dump_json()一致。小结BaseIOSchema是 Atomic Agents 中“原子化”思想的直接载体每一个输入输出都被定义为一个自带文档、自带校验、自带 JSON Schema 导出能力的 Pydantic 模型。理解并遵守其规则——docstring 必填且写给模型、字段必带description、优先Literal与嵌套组合、用结构化错误替代异常——就能让 Agent 的每个接口都稳定、可测、可被 LLM 精确理解。本文涉及的核心源码与测试均可直接在仓库中查阅BaseIOSchema 实现、Agent 与内置 Schema、BasePrompt 的 Schema 复用、Schema 相关测试以及完整的 Quickstart 可运行示例。赞分享AI AgentAgent 框架MCP 服务后端【免费下载链接】atomic-agentsBuilding AI agents, atomically项目地址https://gitcode.com/gh_mirrors/at/atomic-agents点击查看免费下载相关推荐wvp-GB28181-pro 容器化部署教程3 步快速搭建支持直播与云台控制的 GB28181 国标视频平台wvp GB28181 pro 容器化部署教程3 步快速搭建支持直播与云台控制的 GB28181 国标视频平台 手头有一台云主机和一批海康、大华的国标摄像头后端音视频前端BentoML 输入输出类型IO Types完全指南定义 Service API 数据契约BentoML 输入输出类型IO Types完全指南定义 Service API 数据契约 本文围绕 BentoML 官方文档 iotypes.rst h模型推理服务人工智能后端大模型MLOpsLLMOpsESP-IDF 标准输入输出Standard I/O与 Console 输出配置完全指南ESP IDF 标准输入输出Standard I/O与 Console 输出配置完全指南 导读 本文基于 ESP IDF 官方文档 docs/en/api物联网嵌入式上一篇解决Layui 2.9.17与jQuery 3.7.1兼容性问题的完美指南下一篇FlowMVI与Essenty/Decompose集成构建可维护的大型应用架构的终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考