Skip to content

About

约 700 行的多 Agent 框架:类型化工具调用、Swarm handoff 路由、map-reduce 团队、护栏与记忆,MockLLM 驱动全测试

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

agent-mesh

轻量多 Agent 框架:约 700 行实现多 Agent 系统的核心原语——类型化工具调用、Swarm 式 handoff 路由、map-reduce 团队、护栏、记忆。零框架依赖,全部 11 个测试由确定性 MockLLM 驱动,不碰网络。

tests Python License: MIT

设计立场

LangChain 解决的是"什么都有",agent-mesh 想回答的是"Agent 循环的本质是什么"。整个框架只有五个概念:LLM 接口、工具注册表、Agent 循环、handoff、编排器。读懂这 700 行,任何 Agent 框架(OpenAI Swarm / LangGraph / Claude Agent SDK)的骨架都是它的超集。

确定性演示(MockLLM 驱动,可断言路由)

route: triage -> billing | handoffs: 1
answer: A-77 已发货、可退款,已为你提交退款申请。

triage agent 调用 transfer_to_billing → Swarm 切换到 billing agent → billing 调用 lookup_order 工具 → 观察结果 → 生成最终回答。整条链路的每一步都在测试里被断言。

五个核心原语

模块 是什么 关键设计
tools.py @tool 装饰器 类型注解 + docstring 自动生成 JSON Schema(与 OpenAI tools API 同契约);工具异常被封装成 error 字符串返回给模型,让它观察-重试而不是崩溃
agent.py Agent 循环 reason → tool_calls → observe → repeat;max_turns 防死循环;transcript 保持 OpenAI 消息格式,可直接喂给下一个 agent
handoff.py + swarm.py Swarm 路由 handoff 本身是一个终端工具:执行即跳出循环,由 Swarm 读取路由;只扫描本轮新增 transcript,避免历史 handoff 被重复消费;max_handoffs 防乒乓
guardrails.py 护栏管道 block(拒绝)/ transform(改写)两种语义:正则拦截注入、信用卡号脱敏、长度上限,可自由组合
memory.py 会话记忆 滑动窗口 + 超窗折叠为运行摘要(summarizer 注入式,测试用桩)

另有 Team(map-reduce):planner 拆任务 → N 个 worker 并行执行(接口同构,换 asyncio.gather 即真并行)→ reducer 汇总。

🚀 快速开始

uv sync && uv run pytest     # 11 passed,零网络

# 接真实模型(任何 OpenAI 兼容端点:OpenAI / DeepSeek / vLLM / Ollama)
export OPENAI_API_KEY=sk-...
export AGENT_MESH_MODEL=deepseek-chat
uv run python examples/customer_support.py "我想退款,订单 A-77"

三十行搭一个客服 swarm:

from agent_mesh import Agent, OpenAICompatibleLLM, Swarm, tool
from agent_mesh.tools import ToolRegistry

llm = OpenAICompatibleLLM()

@tool
def lookup_order(order_id: str) -> str:
    """Look up an order by id."""
    return orders.get(order_id, "not found")

billing_reg = ToolRegistry()
billing_reg.register(lookup_order)
triage_reg = ToolRegistry()

triage = Agent("triage", "判断问题类型并移交给 billing 或 tech", llm,
               tools=triage_reg, handoffs=["billing", "tech"])
billing = Agent("billing", "账务客服:先查单再答复", llm, tools=billing_reg)
tech = Agent("tech", "技术支持", llm)

result = Swarm([triage, billing, tech], entry="triage").run("A-77 想退款")
print(result.route, result.final_text)

为什么测试可以不用真 LLM

MockLLM 回放脚本化响应并记录每次调用的消息与工具列表——所以测试能断言的是框架行为本身:工具 schema 是否正确生成、异常是否被封装、handoff 后 transcript 是否完整传递、护栏是否按序生效。LLM 的"聪明"是端点的事,框架的正确性是自己的事。OpenAICompatibleLLM 用同一个接口(complete(messages, tools))对接真实端点,API key 只从环境变量读取。

常见问题(FAQ)

Q:MockLLM 是什么?为什么要用它做测试?

MockLLM 是一个 BaseLLM 的确定性实现(agent_mesh/llm.py):传入一组脚本化的 LLMResponse(或返回响应的可调用对象),每次 complete() 按序弹出一条,并记录每轮收到的消息数与可用工具清单供断言;脚本耗尽会直接失败。这让整个测试套件零网络、可重复——测的是框架行为(schema 生成、异常封装、transcript 传递、护栏顺序),不是模型的聪明程度。换上 OpenAICompatibleLLM 即可无缝切到真实端点,二者实现同一接口。

Q:它与 LangChain / OpenAI Swarm 是什么关系?

不是替代品,是"最小骨架"。agent-mesh 只实现五个原语(LLM 接口、工具注册表、Agent 循环、handoff、编排器),handoff 语义与 OpenAI Swarm 同构(transfer_to_X 终端工具 + 路由器读 transcript)。适合用来理解 Agent 框架的运行原理、做原型与教学;需要生态集成(向量库、RAG 组件、可视化追踪)时,LangChain/LangGraph 是更完整的选择。

Q:工具 schema 自动生成怎么用?

给一个带类型注解和 docstring 的函数加 @tool 装饰器即可:参数类型从注解映射为 JSON Schema(支持 int / float / bool / str / list / dict 六种),docstring(或 @tool(description=...))成为工具描述,带默认值的参数不进 required。用 ToolRegistry.register() 注册、schemas() 导出、dispatch() 调用;工具抛出的异常会被封装成 error: ... 字符串返回给模型,供它观察后重试。

Q:怎么接真实模型?

OpenAICompatibleLLM 对接任何 OpenAI chat 兼容端点(OpenAI / DeepSeek / Qwen / vLLM / Ollama):AGENT_MESH_MODEL、OPENAI_BASE_URL、OPENAI_API_KEY 三个环境变量分别控制模型名、端点地址与密钥(密钥只从环境读取,不写入代码)。HTTP 层用 httpx 直发、不依赖官方 SDK,httpx 是可选依赖——不装也不影响框架核心与测试运行。

Q:Team(map-reduce)的 worker 是真并行吗?

当前是串行执行——这是刻意选择,保证结果确定、断言可写。接口与 asyncio.gather 同构:需要真并行时,把 Team.run 里的 for 循环换成 gather 即可,planner / worker / reducer 的签名无需改动。

Q:会话记忆(Memory)怎么接入?

Memory 是独立组件,不与 Agent 循环自动绑定:它保留最近 window 条消息(默认 20),超窗的旧消息交给注入的 summarize 回调折叠成运行摘要(可注入真实 LLM 或测试桩),render() 返回"摘要 + 窗口"组成的消息列表。是否接入、在哪一轮接入,由你的调用链决定。

局限与已知问题(Limitations)

  • 无异步与流式支持:核心循环全部是同步阻塞调用;OpenAICompatibleLLM 用 httpx 同步 POST(超时 120 秒),不支持 stream;Team 的 worker 串行执行。需要真并行要自行改写为 asyncio。
  • 护栏与记忆不自动接线:run_pipeline() 与 Memory 是独立组件,Agent / Swarm 不会自动调用它们——输入输出护栏和会话记忆都需要你在自己的调用链里显式接入。
  • 工具参数注解仅覆盖基础类型:只映射 int / str / bool / float / list / dict 六种;Optional、Literal、dataclass、嵌套泛型等复杂注解会退化为 "string"。
  • 内置 LLM 适配只有 OpenAI 兼容一种:Anthropic 原生 API、Gemini 等需自行实现 BaseLLM.complete();HTTP 请求失败直接抛出(raise_for_status),没有重试与退避,也不统计 token 用量。
  • 规模防护是浅层的:Agent 默认 max_turns=8、Swarm 默认 max_handoffs=4,超限即返回错误文本或停止路由;没有循环检测、成本预算、人工审批等更细的防护机制。
  • 无持久化:Swarm.run 每次从空白 transcript 开始,跨次运行的会话状态不落盘;多轮对话需要你自行配合 Memory 或外部存储管理。

License

MIT

About

约 700 行的多 Agent 框架:类型化工具调用、Swarm handoff 路由、map-reduce 团队、护栏与记忆,MockLLM 驱动全测试

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages