
from typing import List, Callable, Union, Optional
from openai.types.chat import ChatCompletionMessage
from openai.types.chat.chat_completion_message_tool_call import (
ChatCompletionMessageToolCall,
Function,
)
# Third-party imports
from pydantic import BaseModel
AgentFunction = Callable[[], Union[str, "Agent", dict]]
class Agent(BaseModel):
name: str = "Agent"
model: str = "gpt-4o"
instructions: Union[str, Callable[[], 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]]
📌 一、逐部分解释
✅ 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 Protocol
class AgentFunction(Protocol):
def __call__(self) -> Union[str, "Agent", dict]:
...
这可以让你用类型检查器验证某个函数是否符合该协议。
✅ 改进 2:使用枚举代替字符串,提升语义清晰度(推荐)
如果你的返回值是一些固定的指令(如停止、继续、委托),建议用 Enum
替代字符串:
from enum import Enum
class Action(Enum):
CONTINUE = "continue"
STOP = "stop"
DELEGATE = "delegate"
AgentFunction = Callable[[], Union[Action, "Agent", dict]]
这样可以避免拼写错误,并增强语义表达。
✅ 改进 3:封装返回值为统一的数据结构(推荐)
你可以定义一个统一的响应结构,让逻辑更清晰:
from typing import Optional, Union
from dataclasses import dataclass
class AgentResponse:
action: str # 如 "continue", "stop"
next_agent: Optional["Agent"] = None
output: Optional[dict] = None
AgentFunction = Callable[[], AgentResponse]
这个 Agent
类定义基于 Python 的数据类(dataclass)或 Pydantic 的 BaseModel,假设是使用了 Pydantic 的 BaseModel
。它定义了一个代理(Agent)的模板,其中包含了一些属性来描述该代理的行为和功能。下面是对每个字段的分析以及潜在的改进点。
字段解析
-
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 List, Optional, Union, Callable
from pydantic import BaseModel
# 假设 AgentFunction 是已经定义好的类型别名
AgentFunction = Callable[[], Union[str, "Agent", dict]]
class Agent(BaseModel):
name: str = "Agent"
# 确认模型名称的有效性
model: str = "gpt-4" # 注意这里修正了模型名
# 提供了更详细的默认指令
instructions: Union[str, Callable[[], 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
的类型安全性。