OpenManus:无需邀请码的通用 AI 智能体,一夜爆火的 Agent 开源实现
📖 简介
📝 详细介绍
1. 开篇
我们部门去年底接到个内部需求:给总部沉淀的 10 万条售后技术文档做一个智能问答入口。这些文档分布在几十个 SQL 库、SharePoint 和旧版 FAQ 页面里,工程师排查故障主要靠 Ctrl+F 和微信群问老员工,一次平均要花 1.5 个小时。业务方的要求很朴素:"做一个能直接问东西的框,答不上来的就转人工文档检索——别让我们用 App 再说一遍流程。"
最核心的约束是:文档内容涉及公司未公开设备参数,必须在内网部署,不能走外部 SaaS。我当时的直属上级丢给我一句话:"两周内给个原型,别让我再写 PPT 重新申请专有模型。"
2. 需求拆解
我们把业务问题拆成四条技术侧的需求:
- 数据接入:要能批量吃下 docx / pdf / SharePoint 导出的 HTML 内容,统一清洗成纯文本后走向量化流程,同一份文档更新后旧向量必须能删掉;
- 响应性能:单次提问端到端响应时间不能超过 8 秒,否则一线工程师会失去耐心直接关页面;
- 成本上限:内网 GPU 只有一张 3090,模型推理+向量化每月的 token 预算折算下来不能超过 3000 元——相当于每天能跑大约 800 次问答;
- 部署约束:必须跑在 Docker 容器里放在内网 DMZ 区域,不能对外暴露 80/443 以外的端口,也不允许有"邀请码"这类需外部鉴权的依赖。
我们本来的技术栈是 Python + llama_index,对 agent 框架完全没经验。
3. 方案设计
最开始我们拿了三个候选方案对比:
Microsoft Copilot Studio —— 评估时发现审批流程复杂,且知识库数据迁移到 Graph 后权限模型跟我们的文件目录不一致,弃掉。
Dify + 自建 Agent 工作流 —— 能快速搭 RAG,但它的工具定义是"表单式"的,也就是你回答问题的过程其实是被 Dify 的流程节点强行编排的,很死板,自定义代码块在 Docker 里还要挂载额外依赖。
OpenManus —— 看了一圈,它是 MetaGPT 团队开的源,核心亮点是"无需邀请码"(Manus 当时还要申请),模型底层可以自由切换 Qwen / DeepSeek / GPT,工具端默认就带 WebSearch 和 Python 执行的 ReAct 模式。
最终选 OpenManus 的取舍逻辑有两个:一是我要把工具调用能力和话术编排都收敛在我熟悉的那几个 Python 函数里,而不是学一套编排 UI;二是它的 agent 上下文管理逻辑足够裸,我们能把自研的知识库检索脚本直接塞进去当作一个 tool_execute 来处理。成本上,OpenManus 不是重框架,不会为了"每个工具都跑通"去额外请求接口,所以 token 开销是可控的。
4. 落地实现
4.1 数据准备:清洗与向量化
我先写了一个异步脚本把 10 万条文档里的段落拆出来,按段落级别丢进 pgvector。
# normalize_docs.py
import asyncio, hashlib
from pathlib import Path
from unstructured.partition.auto import partition
async def extract(path):
elements = partition(filename = path, strategy="hi_res")
for el in elements:
if el.text.strip():
yield {
"doc_id": hashlib.md5(path.encode()).hexdigest()[:8],
"text": el.text,
"source": str(path)
}
async def main(root="/data/raw_docs"):
for p in Path(root).rglob("*.docx"):
for blk in await extract(p):
print(blk)
# 写入 vector_db
关键点是我们把 embedding 模型换成了 BAAI/bge-m3,它在 3090 上用 ONNX 跑差不多 40ms/ 条,比 OpenAI embedding 省下走外网的带宽,而且中英混合的效果好。
4.2 构建 Agent:修改 tool_execute
OpenManus 默认启动时的工具名字和提示词不适合"内网知识库检索",我改动了它的 app/agent/openmanus.py 中的工具定义,加了可以同时查 PostgreSQL 的检索动作:
@tool
def knowledge_search(query: str, top_k: int = 3) -> str:
"""Search internal support docs for relevant paragraphs."""
embedded_query = embed(query) # bge-m3
sql = """
SELECT text, source FROM doc_chunks
ORDER BY embedding %(query_vec)s
LIMIT %(k)s
"""
with pool.connection() as conn:
rows = conn.execute(sql, {"query_vec": embedded_query, "k": top_k}).fetchall()
return json.dumps(rows, ensure_ascii=False)
在 config.toml 里锁定了模型相关配置:
[llm]
default_model = "qwen2.5:14b" # 内网 ollama
max_tokens = 4096
temperature = 0.3
[web_search]
enable = false # 不允许访问外网
这句配置是折腾了最久的地方:如果 web_search.enable 保持默认 true,agent 一旦认为文档内容不够,就会跳出内网走必应搜索,直接因为超时挂掉。
4.3 部署:Docker 与 API 封装
内网目标机器没有公网访问,我把整个镜像脱网后导入。实际用到的 Dockerfile 只有三层:
FROM python:3.11
RUN pip install openmanus pgvector psycopg[binary] accelerate -i https://pypi.tuna.tsinghua.edu.cn/simple
COPY ./internal_tools /app/internal_tools
COPY ./config.toml /root/.openmanus/config.toml
EXPOSE 8000
CMD ["uvicorn", "gateway:app", "--host", "0.0.0.0", "--port", "8000"]
然后对外暴露的就是一个标准的 FastAPI 的 /ask 端点,只接收公司内部的 SSO 头,另外加了一层简单的提示词模板,限制回答只说"基于知识库的内容"。
5. 效果与数据
上线两周,我们对一线工程师调研和汇总内部的调用日志,得到这组成果数据(其中单个 token 成本为估算值):
| 指标 | 上线前 | 上线后 | 变化 |
|---|---|---|---|
| 平均故障排查时长 | 约 1.5 小时 / 单 | 约 15 分钟 / 单 | ↓83% |
| 答案可采纳率(通过日志给 4 分以上打分) | 不足 50% | 78.4%(抽样 300 条) | ↑28 个百分点 |
| 端到端响应时间 P95 | —— | 6.2s | 满足 <8s 目标 |
| 每单平均 token 成本(含答案生成 & 检索);GPU 电费分摊已计 | 原人工折算可忽略 | 约 0.02 元 / 次 | 远低于预算 |
| 开发工时 | —— | 6 人日 | 相比外包报价省 2 周 |
最让我满意的不是成本,而是知识库覆盖不到了以后,agent 会明说"内部文档中未找到相关内容",而不是强行编一段话出来误导维修动作。
6. 踩过的坑
6.1 上下文爆掉与回复截断
现象:进行到第 12 轮对话后,返回内容开始残缺,出现"…"。
排查:打开调试发现 OpenAI 兼容接口报 400 context_length_exceeded。默认 OpenManus 是把整个历史消息发给模型,没有自动切窗口。
解决:我改用了定向记忆策略:history 只保留最近一轮完整问答,加一个不变的 system prompt,把超出 6 条后的进行摘要剪枝。具体改了 config.toml 的 max_history_messages = 6,而 agent 检索出的文档片段最长只取原文前 1200 字符。
6.2 检索召回的低级 bug:embedding 做在了子进程里
现象:用户反映搜索"扳手规格"能出结果,但搜"六角套筒"返回空。我发现日志里两条相近表达只匹配到完全无关文档。
排查:查了 pgvector 中片段——原来我把 embedding 函数挂在了 asyncio.to_thread 里,而 bge-m3 的 tokenizer 不是线程安全的,导致多次并发时偶发 encode 结果全为零向量。然后用 ORDER BY <=> 算距离时,所有零向量排在文档最后。
解决:改成进程池单线程执行 embedding 调用,并做了输入长度逐条排序。同时加了一个小的校验:如果某个 query 的 embedding 向量 L2 长度等于 0,就报出一个显眼错误。
6.3 内网 DNS 解析引起 10 秒级阻塞
现象:生产环境提问后经常等很久,但 p95 比测试环境多 2 秒。
排查:测试机用的 Docker bridge 网络,生产环境机器 hosts 里未配置 pgvector 所在的数据库域名,Python 驱动每次建连接都要卡在内网 DNS 超时上。
解决:pgvector 的连接地址直接改为 IP 加连接池预热,另外顺手把代码里的同步 SQLAlchemy 引擎换成了 psycopg_pool 异步池。
7. 复盘与扩展
这次选择 OpenManus 的最正确的点在"框架化小、自定义没有黑魔法"——我们能在 6 个工作日内完成数据清洗、代码接入和去外网依赖,换做任何以对话流编排的框架,我估计还得再花 2 周学会怎么绕过他们的后台存储。另一个对的决定:修改了它的默认工具列表,把不用的 WebSearch / Browsing 直接禁用,这让 agent 不会闲着没事自己去做网络请求。
做得不好的方面是:我们没有主动去测 长尾问题并发(比如 30 个用户同时提问),导致刚上线第三天出现过两回 PG 连接被打爆和数据库 OOM 的警告。如果重新来一次,我会在一开始就上 pgbouncer 而不是用裸的连接池撑过去。
后续扩展的方向,在基础设施架构上没有太大改动空间的话,可以横向做两件事:第一是把 OpenManus 的检索工具从"只能读片段"改成"可以写工单"——也就是让 agent 能直接在工作流里引用同部门的老工单作为案例给用户参考;第二是对知识库的片段做定时重向量化,在文档更新后自动执行一遍,让 agent 不会再基于过期的保养手册给建议。总体来说,如果你要做的不是"展示 demo"而是"真刀真枪塞进业务系统",这个框架足够轻巧,适合做基座自行演进。
AI 项目推荐
智能体- 标签
- #智能体 #通用Agent #自动化 #开源
- 浏览
- 👁️ 7
- 发布日期
- 2026-09-09