Agent demo 跑通了,然后呢?聊聊多用户生产化这道没人填的坑

一、先说我遇到的问题

去年到今年,我陆陆续续写过几个 Agent 小工具。每次的体验都惊人地一致:

demo 阶段如丝般顺滑,上线阶段如鲠在喉。

跟着任何一个主流框架的 quickstart,二十行代码,一个能调工具、能多轮对话的 Agent 就跑起来了,演示效果很唬人。可一旦我想把它变成一个多人能同时用的服务,问题就接踵而至:

  • 两个用户同时发消息,会话状态开始串台------A 的上下文漏进了 B 的回复。
  • 我到底该给每个用户一个 Agent 实例,还是共享一个?共享会不会有并发安全问题?独享内存会不会爆?
  • 实例什么时候回收?空闲多久算空闲?并发上限怎么设?
  • 会话要持久化、进程重启要能恢复,这套又得自己从头搭。

我翻遍了手上几个框架的文档,发现这部分几乎都被当成"留给读者的练习":要么让你去用它收费的托管平台,要么一句"生产部署请参考最佳实践"就把你打发了。

后来我查了一圈资料才确认,这不是我一个人的错觉------Salesforce 专门写过工程博客讲他们的多租户 Agent 平台怎么扛 7000+ 并发会话不串台;2026 年初 Medium / dev.to 上一连串「Multi-Tenant AI Agent Infrastructure」实战文,焦点全是同一件事:会话状态串扰、per-user 隔离、资源池化、向量库的租户边界

「多用户会话隔离」是真实的生产难题,而大多数框架把它甩给了你。

milu 就是我为了系统性地解决这个问题,顺手把另外几个老问题(国产模型接入、工具安全、全家桶)一起做掉的产物。这篇文章主要讲讲 milu 是怎么填「多用户生产化」这道坑的,以及它的整体设计取舍。

项目地址:github.com/stephonGAO/... (MIT,pip install -U milu 即用,欢迎 star)


二、核心:AgentPool 多用户并发资源池

先看一个事实------Agent 实例是带状态的 。它持有对话历史、会话对象、MCP 连接、工具注册表、甚至"是否已经开始干活"这种运行期标记。这意味着多个用户绝对不能共享同一个 Agent 实例

那唯一安全的方案就是 per-user Agent 。但"每人一个实例"立刻带来新问题:实例怎么管理、怎么限量、怎么回收。这正是 AgentPool 干的事:

python 复制代码
from milu import AgentPool, ModelRegistry

# LLM 实例本身协程安全,可以在所有用户间共享(底层是 AsyncOpenAI)
llm = ModelRegistry.create("qwen", model="qwen3.6-plus")
pool = AgentPool.from_llm(llm)
await pool.start()

# 按 (user_id, session_id) 拿到这个用户专属的 Agent,用完自动归还
async with pool.acquire("user-1", "session-A") as h:
    async for evt in h.agent.run("你好"):
        ...

await pool.stop()

AgentPool 守着四个硬不变量,这也是它和"自己写个 dict 缓存 Agent"的本质区别:

  1. 每个 (user_id, session_id) 最多一个实例(隔离的根基);
  2. 实例总数 max_agents(LRU 淘汰,防内存爆);
  3. 并发执行的 run max_concurrent_runs(全局信号量限流);
  4. 空闲超过 idle_ttl_seconds 的实例被后台清理。

会话、记忆、知识库都按 user_id 自动派生隔离,你不用操心。

真正的难点不在池子,在"无状态化"

这里有个容易被忽略的坑。就算你给每个用户独立 Agent,如果工具内部用了模块级全局变量来存状态,照样串台。比如一个 todo 工具用模块级单例存当前计划,两个用户的 Agent 跑在同一个进程里,计划就会互相覆盖。

milu 的做法是全链路 ContextVar 无状态化 :todo 的计划存储、子代理的事件收集、父级安全模式的继承,全部通过 ContextVarAgent.run() 入口按调用注入,实现 asyncio 任务级隔离。源码里这条约束被反复强调:

新增任何"跨调用共享"的工具状态,一律用 ContextVar,切勿用模块级全局变量

这块我专门写了并发隔离的测试(test_concurrency_with_pool.pytest_todo_concurrent_isolation.pytest_subagent_concurrent_isolation.py 等),整个项目 1100+ 测试里,这部分是核心保证。

顺带一提,我之所以对"并发崩溃"这么敏感,是因为调研时看到国产框架里有公开的并发翻车案例------某框架的群聊模块多用户同时发消息时 IndexError、Code Interpreter 多用户调用有线程安全瓶颈。这些都是真实发生过的、能在 CSDN 搜到的事故。无状态化不是过度设计,是被现实教育出来的。


三、第二个动机:国产模型不该是二等公民

我日常用的是 DeepSeek 和通义,但很多框架对国产模型的支持是"靠社区零散包"或"自己配 base_url + LiteLLM 转一道"。每接一家,我都要:查它的 base_url、适配它的 thinking 模式开关、处理它内置联网搜索的参数、规避它不支持的字段......然后自己维护一张多厂商路由表。

milu 把 9 家提供商收敛到一个接口,6 家国产模型(通义、DeepSeek、Kimi、GLM、MiniMax、豆包)+ OpenAI / Gemini / Claude,切换就一行:

python 复制代码
from milu import ModelRegistry, Message, MessageRole

llm = ModelRegistry.create("deepseek")          # 换 "kimi" / "glm" / "qwen" ... 同一接口
async for chunk in llm.chat([Message(role=MessageRole.USER, content="你好")]):
    print(chunk.content or "", end="", flush=True)

API Key 从 {PROVIDER}_API_KEY 环境变量读,各家的参数差异(thinking、内置搜索、参数过滤)已经逐家适配好。

实现上一个关键决定:所有 provider 统一用 openai.AsyncOpenAI 作为 HTTP 客户端 ,靠不同 base_url + extra_body 适配各家。好处是 AsyncOpenAI 协程安全,所以一个 LLM 实例能在多用户间安全共享------这正好和上面 AgentPool 的设计咬合:共享 LLM、隔离 Agent。

还有个国内特别实在的点:内置 web_search 工具是可插拔后端 ,DDG 国内连不上,配 WEB_SEARCH_PROVIDER=bocha 就能国内直连。这种细节,没在国内踩过坑的框架不会替你想。


四、第三件事:工具执行得有真正的安全模型

Agent 一旦能调 shell_commandfile_writepython_repl,"它会不会把我电脑搞坏"就成了真问题。milu 给了四种操作模式:

python 复制代码
agent = Agent(llm, mode="manual")   # 不安全工具等人工审批
agent.set_mode("talk")              # 只读:不安全工具一律拦截
模式 行为
talk 只读,所有不安全工具调用被拦
manual 安全工具直接跑;不安全工具产出确认事件、等审批
auto(默认) 自主决策;不安全调用交 AI 安全判定器裁决(放行 / 转确认 / 拒绝)
superwork 全权限,跳过一切检查

auto 模式那个 AI 安全判定器 是对齐 Claude Code auto 模式分类器做的:安全工具走快路径零开销,其余不安全调用批量交给一个判定 LLM 三态裁决。而且------子代理会通过 ContextVar 继承父级的模式和确认回调,也就是说你没法靠"委派给子代理"来绕过审批。委派不构成安全旁路,这点我觉得挺重要。


五、剩下的就是"全家桶开箱即用"

到这里 milu 的三个核心动机讲完了:多用户生产化、国产模型一等公民、工具安全。剩下的能力是"既然都做了 Agent,该有的都给齐":

一个 Agent(llm) 默认就是完整体------内置系统提示词、20+ 工具、技能、三个子代理、会话持久化、上下文自动压缩,全都有,传显式参数才覆盖:

python 复制代码
from milu import Agent, ModelRegistry, TextDelta, AgentDone

agent = Agent(ModelRegistry.create("deepseek"))
async for evt in agent.run("读一下 ./report.pdf 然后总结"):
    if isinstance(evt, TextDelta):
        print(evt.text, end="", flush=True)
    elif isinstance(evt, AgentDone):
        print(f"\n[共 {evt.turn_count} 轮]")

清单(都是库内置,不用再装一堆集成包):

  • 20+ 内置工具 :文件读写、shell、Python REPL、HTTP、网页正文提取、可插拔搜索、Office/PDF 文档解析、图片视觉输入、日期、结构化输出、todo、长期记忆。
  • MCP 协议:stdio / streamable HTTP / SSE 三种传输,而且 MCP 工具用"休眠池"设计------schema 不污染上下文,Agent 按需发现激活。
  • RAG 知识库 :分块向量化、余弦检索、来源路由常驻 prompt、可选每轮自动检索,kb_search / kb_ingest / kb_manage 三工具。
  • 子代理:内置调研员 / 阅读员 / 编码员三件套,上下文隔离、权限收窄、可并行。
  • 技能系统:9 个内置技能,元数据常驻、正文按需加载。
  • 定时任务 :多用户 cron 调度,能嵌在 milu chat / milu serve 里跑。
  • 内置 Web 服务milu serve 一行起多用户 SSE 流式对话 + 全功能演示前端(纯 vanilla 无构建链)。

而这一切,一个 pip install -U milu 全都装好,CLI、Web 服务、RAG、MCP 都在核心依赖里。

bash 复制代码
pip install milu
milu            # 首次运行引导你选厂商、填 Key,然后直接聊
milu serve      # 一行起多用户 Web 服务,浏览器开 http://127.0.0.1:8000


六、五分钟上手

bash 复制代码
pip install milu
milu                # 跟着引导走,零配置开聊

或者嵌进你自己的代码,三行:

python 复制代码
from milu import Agent, ModelRegistry
agent = Agent(ModelRegistry.create("deepseek"))
async for event in agent.run("现在几点?用工具查一下"):
    ...

项目还很新(v0.1.0,star 也刚起步),但 1100+ 测试、中英双语文档、Docker 部署文档都在位。如果这篇文章里有哪个点戳到你了,欢迎去 GitHub 点个 star,或者直接提 issue 拍砖------冷启动阶段,你的每一条反馈我都会认真看。

🦌 GitHub:github.com/stephonGAO/... 📦 PyPI:pip install -U milu


相关推荐
Holman1 小时前
给 Claude Code 装技能包:Skills 实战
人工智能·ai编程
SilentSamsara1 小时前
特征工程系统方法论:编码、分箱、交互特征与特征选择
开发语言·人工智能·python·机器学习·青少年编程·信息可视化·pandas
财经资讯数据_灵砚智能1 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(夜间-次晨)2026年6月8日
大数据·人工智能·python·ai·信息可视化·自然语言处理·灵砚智能
“码”力全开1 小时前
打破芯片与协议壁垒:基于 Docker+边缘计算 的企业级 AI 视频管理平台架构解析(附 GB28181/RTSP 统一接入与源码交付方案)
人工智能·docker·边缘计算
morning_judger1 小时前
Agent开发系列(十)-知识库建设(架构总览)
开发语言·人工智能
南知意-1 小时前
MonkeyCode:长亭开源的企业级AI开发平台,GitHub 3.2k Star!
人工智能·ai·开源·github·ai编程·开源项目
数字人小文1 小时前
生产环境 Agent 实战:4个真实踩坑场景
人工智能
ai产品老杨1 小时前
【架构深评】基于 Docker 与 边缘计算,如何打通 GB28181/RTSP 与 X86/ARM 异构算力的企业级 AI 视频流网关?(附源码交付)
人工智能·docker·架构
星幻元宇VR1 小时前
消防教育基地展厅设备【消防知识安全竞赛系统】
人工智能·科技·学习·安全