支持私有化部署
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


剖析 LLM Agent 定义及返回结构体定义

发布日期:2025-06-21 13:45:56 浏览次数: 1517
作者:虎sir的AI技术博客

微信搜一搜,关注“虎sir的AI技术博客”

推荐语

深入解析LLM Agent的核心结构与返回机制,从代码层面揭示其设计精髓。

核心内容:
1. AgentFunction类型别名的精妙设计与实际应用
2. Response和Result模型的结构解析与使用场景
3. 商用代码示例中的关键实现细节剖析

杨芳贤
53AI创始人/腾讯云(TVP)最具价值专家
先从一段商用代码来开始本次的讨论:
from typing import ListCallableUnionOptionalfrom openai.types.chat import ChatCompletionMessagefrom openai.types.chat.chat_completion_message_tool_call import (    ChatCompletionMessageToolCall,    Function,)# Third-party importsfrom pydantic import BaseModel
AgentFunction = Callable[[], Union[str"Agent"dict]]
class Agent(BaseModel):    name: str = "Agent"    model: str = "gpt-4o"    instructions: Union[strCallable[[], str]] = "You are a helpful agent."    functions: List[AgentFunction] = []    tool_choice: str = None    parallel_tool_calls: bool = True

class Response(BaseModel):    messages: List = []    agent: Optional[Agent] = None    context_variables: dict = {}    class Result(BaseModel):    """    Encapsulates the possible return values for an agent function.
    Attributes:        value (str): The result value as a string.        agent (Agent): The agent instance, if applicable.        context_variables (dict): A dictionary of context variables    """    value: str = ""    agent: Optional[Agent] = None    context_variables: dict = {}
首先分析下这行代码:
AgentFunction = Callable[[], Union[str"Agent"dict]]
这是一个 类型别名(Type Alias),用于定义一个函数的签名,在 Python 的类型提示(Type Hints)中非常常见。我们来逐步分析它为什么这样定义、有什么好处,以及是否可以改进。

📌 一、逐部分解释

✅ Callable[[], ...]

  • 表示这个函数 没有参数
  • 第一个 [] 表示参数列表(这里是空的),也可以写成 Callable[[int, str], ...] 等表示有参数的函数。
  • 第二个元素是返回值类型。

✅ Union[str, "Agent", dict]

  • 表示这个函数可以返回以下任意一种类型:
    • str
      :字符串;
    • "Agent"
      :字符串形式的类名(延迟求值,因为可能尚未定义);
    • dict
      :字典。

使用 "Agent" 而不是 Agent 是为了防止在类定义之前就引用它(比如在类内部定义函数时),Python 3.7+ 支持 from __future__ import annotations 后可以直接写 Agent

🧠 二、为什么要这样定义?

✅ 1. 提高可读性

将函数签名抽象为一个名字 AgentFunction,可以让其他开发者更容易理解它的用途和结构。

例如:

def my_func() -> AgentFunction:    ...
比直接写:
def my_func() -> Callable[[], Union[str"Agent"dict]]:    ...
更简洁易懂。

✅ 2. 统一接口规范

如果你有很多函数或方法返回类似的结构(如状态、子代理、配置等),统一使用 AgentFunction 可以帮助你确保接口一致性。


✅ 3. 支持多种行为

允许函数返回不同类型的值,适用于如下场景:

🔍 三、实际应用场景举例

假设你正在构建一个 Agent 框架,每个 agent 都可以决定下一步要做什么:

def decide_next_action() -> AgentFunction:    if should_stop():        return "STOP"    elif need_delegate():        return AnotherAgent()    else:        return {"action""continue""data": ...}

🛠️ 四、是否有改进空间?

虽然当前定义已经很清晰,但根据你的具体需求,也可以考虑以下几点改进:


✅ 改进 1:使用 Protocol 定义更严格的函数接口(Python 3.8+)

如果你希望对函数的输入输出做更精确控制,可以用 Protocol

from typing import Protocolclass AgentFunction(Protocol):    def __call__(self) -> Union[str"Agent"dict]:        ...

这可以让你用类型检查器验证某个函数是否符合该协议。


✅ 改进 2:使用枚举代替字符串,提升语义清晰度(推荐)

如果你的返回值是一些固定的指令(如停止、继续、委托),建议用 Enum 替代字符串:

from enum import Enumclass Action(Enum):    CONTINUE = "continue"    STOP = "stop"    DELEGATE = "delegate"AgentFunction = Callable[[], Union[Action, "Agent"dict]]

这样可以避免拼写错误,并增强语义表达。


✅ 改进 3:封装返回值为统一的数据结构(推荐)

你可以定义一个统一的响应结构,让逻辑更清晰:

from typing import OptionalUnionfrom dataclasses import dataclass@dataclassclass AgentResponse:    action: str  # 如 "continue", "stop"    next_agent: Optional["Agent"] = None    output: Optional[dict] = NoneAgentFunction = Callable[[], AgentResponse]
这样可以避免多个返回类型带来的混乱,也方便后续扩展。

这个 Agent 类定义基于 Python 的数据类(dataclass)或 Pydantic 的 BaseModel,假设是使用了 Pydantic 的 BaseModel。它定义了一个代理(Agent)的模板,其中包含了一些属性来描述该代理的行为和功能。下面是对每个字段的分析以及潜在的改进点。

字段解析

  1. name: str = "Agent"

  • 代理的名字,默认值为 "Agent"
  • 改进建议:确保名字唯一性可能是一个好主意,尤其是在有多个代理实例时。
  • model: str = "gpt-4o"

    • 代理使用的模型,默认值为 "gpt-4o"
    • 改进建议:检查模型名称是否正确(例如,是否存在拼写错误如 gpt-4o 应该是 gpt-4 或者其他有效模型名)。考虑使用枚举或者更严格的验证规则来限制允许的模型名称。
  • instructions: Union[str, Callable[[], str]] = "You are a helpful agent."

    • 指令或初始化消息,可以是字符串或一个无参函数返回字符串。
    • 改进建议:如果指令可能会根据某些条件动态生成,那么这种设计是非常灵活的。但是,如果大多数情况下都是静态文本,可能需要提供更好的默认示例或文档说明如何实现 Callable 版本。
  • functions: List[AgentFunction] = []

    • 与代理相关的功能列表。
    • 改进建议:避免直接使用可变对象作为默认参数(即空列表),因为这可能导致意外的共享状态问题。应使用 None 作为默认值,并在类内部处理初始化逻辑。
  • tool_choice: str = None

    • 工具选择,默认为 None
    • 改进建议:明确工具的选择范围或类型,可以通过枚举或者自定义类型来增强类型安全性和代码可读性。
  • parallel_tool_calls: bool = True

    • 是否并行调用工具,默认为 True
    • 改进建议:无需特别改进,但确保文档清晰地解释了这一选项的意义及其对性能的影响。

    改进后的版本

    from typing import ListOptionalUnionCallablefrom pydantic import BaseModel# 假设 AgentFunction 是已经定义好的类型别名AgentFunction = Callable[[], Union[str"Agent"dict]]class Agent(BaseModel):    name: str = "Agent"
        # 确认模型名称的有效性    model: str = "gpt-4"  # 注意这里修正了模型名
        # 提供了更详细的默认指令    instructions: Union[strCallable[[], str]] = "You are an intelligent assistant designed to help users with their queries."
        # 使用 None 代替直接定义可变默认值    functions: Optional[List[AgentFunction]] = None
        # 定义工具选择的枚举或其他限制方式    tool_choice: Optional[str] = None
        parallel_tool_calls: bool = True
        # 初始化函数,用于设置默认值    def __init__(self, **data):        super().__init__(**data)        if self.functions is None:            self.functions = []

    总结

    • 对于可能变化的数据结构(如 functions),避免直接使用可变对象作为默认参数。
    • 提高模型名称的准确性,并考虑增加校验机制。
    • 对于复杂或动态生成的属性(如 instructions),提供更加具体的指导或示例。
    • 可以通过引入枚举或其他类型约束来提高 tool_choice 的类型安全性。

    53AI,企业落地大模型首选服务商

    产品:场景落地咨询+大模型应用平台+行业解决方案

    承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

    联系我们

    售前咨询
    186 6662 7370
    预约演示
    185 8882 0121

    微信扫码

    添加专属顾问

    回到顶部

    加载中...

    扫码咨询