前两天阿里发了个大招------「悟空」,号称全球首个企业级 Agent 平台。刷了一圈掘金和知乎,全是"震撼""王炸""颠覆",搞得我以为外星科技降临了。
抱着"别人吹你信,自己试了才知道"的心态,我花了一下午研究了悟空的架构思路,然后动手用 Python 撸了个 mini Agent 框架。结论是:悟空的 CLI 化思路确实聪明,但核心技术你自己也能实现,而且没那么玄乎。
先说结论
| 维度 | 阿里悟空 | 自建 Agent |
|---|---|---|
| 上手难度 | 开箱即用,钉钉内直接用 | 需要写代码,100 行起步 |
| 灵活性 | 绑定钉钉生态 | 想接啥接啥 |
| 成本 | 企业版收费(具体没公布) | API 调用费,按量付 |
| 数据安全 | 阿里云托管 | 数据在自己手里 |
| 定制能力 | 用它的 Skill 模板 | 完全自定义 |
| 适合谁 | 不想写代码的团队 | 想掌控一切的开发者 |
如果你是开发者,自己搞一个可能更香。 下面我把完整代码和踩坑记录都放出来。
悟空到底做了什么?
抛开营销话术,悟空的核心思路其实就三件事:
1. CLI 化改造------让 AI 能"操作"而不是"模拟"
钉钉把自己的 GUI 功能拆解成了一套 CLI 指令。AI Agent 不用像 Playwright 那样模拟鼠标点击,而是直接调用命令行接口。
这个思路其实跟 MCP(Model Context Protocol)异曲同工------把工具变成函数,让 AI 直接调用。
2. Skill 市场------预制工具包
悟空的 OPT(One Person Team)方案,本质上就是把多个 AI 工具编排成一个工作流。比如跨境电商场景:选品雷达 → AI 找同款 → 供应链匹配,这些都是预制好的 Skill。
3. 沙箱隔离------企业级安全
每个 Agent 跑在独立的容器沙箱里,权限最小化。这个在企业场景确实重要,但对个人开发者来说,本地跑就完事了。
说白了,悟空 = Function Calling + 工具编排 + 企业权限管理。前两个你自己就能搞,第三个大多数场景用不上。
动手:100 行 Python 搞个 AI Agent
废话不多说,直接上代码。我要实现一个能自动执行多步任务的 Agent:给它一个目标,它自己决定调哪些工具、按什么顺序执行。
第一步:定义工具
python
import json
import requests
import subprocess
from datetime import datetime
# 工具定义------Agent 能调用的函数
TOOLS = [
{
"type": "function",
"function": {
"name": "search_web",
"description": "搜索互联网获取最新信息",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "搜索关键词"}
},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "run_command",
"description": "在终端执行命令(如 curl, python, git 等)",
"parameters": {
"type": "object",
"properties": {
"command": {"type": "string", "description": "要执行的命令"}
},
"required": ["command"]
}
}
},
{
"type": "function",
"function": {
"name": "read_file",
"description": "读取本地文件内容",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "文件路径"}
},
"required": ["path"]
}
}
},
{
"type": "function",
"function": {
"name": "write_file",
"description": "将内容写入文件",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "文件路径"},
"content": {"type": "string", "description": "文件内容"}
},
"required": ["path", "content"]
}
}
}
]
第二步:实现工具函数
python
def execute_tool(name: str, args: dict) -> str:
"""执行工具并返回结果"""
if name == "search_web":
# 这里用 DuckDuckGo 做演示,生产环境建议用 Tavily 或 Serper
try:
resp = requests.get(
"https://api.duckduckgo.com/",
params={"q": args["query"], "format": "json"},
timeout=10
)
data = resp.json()
return data.get("AbstractText") or f"搜索完成,请根据关键词 '{args['query']}' 进行分析"
except Exception as e:
return f"搜索失败: {e}"
elif name == "run_command":
try:
result = subprocess.run(
args["command"], shell=True,
capture_output=True, text=True, timeout=30
)
output = result.stdout or result.stderr
return output[:2000] # 截断避免 token 爆炸
except subprocess.TimeoutExpired:
return "命令执行超时(30s)"
elif name == "read_file":
try:
with open(args["path"], "r") as f:
return f.read()[:5000]
except FileNotFoundError:
return f"文件不存在: {args['path']}"
elif name == "write_file":
with open(args["path"], "w") as f:
f.write(args["content"])
return f"已写入 {args['path']}({len(args['content'])} 字符)"
return f"未知工具: {name}"
第三步:Agent 主循环
这是核心------一个 ReAct 风格的循环,让 AI 自己决策下一步:
python
def run_agent(task: str, model: str = "claude-sonnet-4-20250514", max_steps: int = 10):
"""
运行 AI Agent
给它一个任务,它自己决定怎么完成
"""
# 兼容 OpenAI 协议的 API(我用的 ofox.ai,改个 base_url 就行)
api_base = "https://api.ofox.ai/v1"
api_key = "你的 API Key"
messages = [
{
"role": "system",
"content": (
f"你是一个能力很强的 AI Agent。当前时间:{datetime.now().isoformat()}\n"
"你可以使用工具来完成任务。每次只调用必要的工具,完成后直接回复最终结果。\n"
"如果任务已完成,直接回复结果,不要再调用工具。"
)
},
{"role": "user", "content": task}
]
print(f"\n🎯 任务: {task}\n{'='*50}")
for step in range(max_steps):
print(f"\n--- Step {step + 1} ---")
resp = requests.post(
f"{api_base}/chat/completions",
headers={"Authorization": f"Bearer {api_key}"},
json={
"model": model,
"messages": messages,
"tools": TOOLS,
"tool_choice": "auto"
},
timeout=60
)
result = resp.json()
choice = result["choices"][0]
msg = choice["message"]
messages.append(msg)
# 如果 AI 决定调用工具
if msg.get("tool_calls"):
for call in msg["tool_calls"]:
fn_name = call["function"]["name"]
fn_args = json.loads(call["function"]["arguments"])
print(f"🔧 调用工具: {fn_name}({fn_args})")
tool_result = execute_tool(fn_name, fn_args)
print(f"📋 结果: {tool_result[:200]}...")
messages.append({
"role": "tool",
"tool_call_id": call["id"],
"content": tool_result
})
else:
# AI 认为任务完成,直接返回
print(f"\n✅ 完成!\n{msg['content']}")
return msg["content"]
print("⚠️ 达到最大步数限制")
return messages[-1].get("content", "未完成")
跑起来试试
python
if __name__ == "__main__":
# 例子1: 让 Agent 查看项目结构并生成说明
run_agent("查看当前目录的项目结构,生成一份简要的 README")
# 例子2: 让 Agent 搜索最新技术动态并写摘要
run_agent("搜索 '阿里悟空 AI Agent' 的最新信息,写一段 200 字的技术分析")
输出长这样:
css
🎯 任务: 查看当前目录的项目结构,生成一份简要的 README
==================================================
--- Step 1 ---
🔧 调用工具: run_command({'command': 'find . -maxdepth 2 -type f | head -30'})
📋 结果: ./src/main.py ./src/utils.py ./tests/test_main.py ...
--- Step 2 ---
🔧 调用工具: read_file({'path': './src/main.py'})
📋 结果: import asyncio ...
--- Step 3 ---
🔧 调用工具: write_file({'path': './README.md', 'content': '# MyProject\n...'})
📋 结果: 已写入 ./README.md(456 字符)
--- Step 4 ---
✅ 完成!
已为你生成 README.md,包含项目结构、安装方式和使用示例。
整个过程 AI 自己决定了要先看目录、再读代码、最后写文件。 这就是 Agent 的核心------不是写死流程,而是让 AI 自己规划执行路径。
进阶:加上多模型切换
悟空底层用的是通义千问,但实际做 Agent 开发你会发现,不同任务用不同模型效果差异巨大:
- 规划/推理任务:Claude Opus 4.6 或 GPT-5.4 效果最好,但贵
- 简单工具调用:GPT-5.4 mini 或 Gemini 3 Flash 就够了,便宜 10 倍
- 代码生成:GPT-5.2-Codex 专门优化过,写代码最稳
python
# 一套代码切换多个模型,只要 API 兼容 OpenAI 协议就行
MODELS = {
"planning": "claude-opus-4-6-20250901", # 复杂推理
"execution": "gpt-5.4-mini", # 日常执行
"coding": "gpt-5.2-codex", # 代码生成
"fast": "gemini-3-flash", # 快速响应
}
def run_agent(task, task_type="execution", max_steps=10):
model = MODELS.get(task_type, MODELS["execution"])
# ... 其余代码不变,model 参数传进去就行
这里有个小技巧:用兼容 OpenAI 协议的聚合接口,换模型只需要改一个 model 字段,base_url 和 api_key 都不用动。我自己用的 ofox.ai,50 多个模型一个 Key 搞定,省得每家都去申请。
踩坑记录
坑 1:tool_calls 的 arguments 是字符串不是字典
python
# ❌ 错误写法
fn_args = call["function"]["arguments"] # 这是个 JSON 字符串!
# ✅ 正确写法
fn_args = json.loads(call["function"]["arguments"])
第一次写的时候忘了 json.loads,直接把字符串当字典用,报错信息还特别迷惑------"string indices must be integers"。找了半小时才发现是这么个低级错误。
坑 2:工具结果太长会让模型"失忆"
如果工具返回了几万字的内容,塞进 messages 里会直接把上下文撑爆。模型不是崩就是忘了之前的任务。
python
# 解决方案:截断 + 摘要
def execute_tool(name, args):
result = _do_execute(name, args)
if len(result) > 3000:
result = result[:2000] + f"\n\n... 省略 {len(result)-2000} 字符 ..."
return result
坑 3:模型疯狂循环调用同一个工具
有时候模型会陷入死循环------比如一直重复搜索同一个关键词。加个简单的去重就行:
python
recent_calls = []
for step in range(max_steps):
# ... 获取 AI 响应 ...
if msg.get("tool_calls"):
call_sig = f"{fn_name}:{json.dumps(fn_args, sort_keys=True)}"
if call_sig in recent_calls[-3:]: # 最近 3 次有重复就跳过
messages.append({
"role": "tool",
"tool_call_id": call["id"],
"content": "你已经调用过这个工具了,请尝试其他方式或直接回答。"
})
continue
recent_calls.append(call_sig)
坑 4:并发请求被限流
跑多个 Agent 任务时,API 请求太密集会被限流。加个简单的指数退避:
python
import time
def api_call_with_retry(url, **kwargs):
for attempt in range(3):
resp = requests.post(url, **kwargs)
if resp.status_code == 429:
wait = 2 ** attempt
print(f"限流了,等 {wait}s 重试...")
time.sleep(wait)
continue
return resp.json()
raise Exception("重试 3 次仍然失败")
小结
阿里悟空的定位很明确------给不想写代码的企业用户做一站式 Agent 平台。但如果你是开发者,自己用 100 行 Python + 一个兼容 OpenAI 协议的 API,就能搞出功能差不多的东西。
核心就三步:
- 定义工具(Function Calling 格式)
- 写个 ReAct 循环(AI 决策 → 执行工具 → 反馈结果 → 继续决策)
- 处理边界情况(截断、去重、重试)
代码已经全部贴在文章里了,复制就能跑。觉得有用的话点个赞,我后续会写多 Agent 协作和 MCP 集成的实战。
用到的模型:Claude Sonnet 4.6 / GPT-5.4 mini / GPT-5.2-Codex,通过 ofox.ai 聚合接口统一调用