体验完阿里「悟空」之后,我花 2 小时用 Python 撸了个 AI Agent 🔥

前两天阿里发了个大招------「悟空」,号称全球首个企业级 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_urlapi_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,就能搞出功能差不多的东西。

核心就三步:

  1. 定义工具(Function Calling 格式)
  2. 写个 ReAct 循环(AI 决策 → 执行工具 → 反馈结果 → 继续决策)
  3. 处理边界情况(截断、去重、重试)

代码已经全部贴在文章里了,复制就能跑。觉得有用的话点个赞,我后续会写多 Agent 协作和 MCP 集成的实战。


用到的模型:Claude Sonnet 4.6 / GPT-5.4 mini / GPT-5.2-Codex,通过 ofox.ai 聚合接口统一调用

相关推荐
万里沧海寄云帆2 小时前
pytorch+cpu版本对Intel Ultra 9 275HX性能的影响
人工智能·pytorch·python
weiwuxian2 小时前
别再拿 Demo 糊弄老板了:企业级 RAG 架构的“填坑”指南 🚀
aigc
java资料站2 小时前
python爬虫入门
python
1941s2 小时前
Google Agent Development Kit (ADK) 指南 第二章:环境搭建与快速开始
人工智能·python·adk·google agent
天下无贼2 小时前
【Python】2026版——FastAPI 框架快速搭建后端服务
后端·python·aigc
橙序员小站2 小时前
当所有人都在做 Agent,我想聊聊被遗忘的基础设施
后端·开源·aigc
小蚂蚁i2 小时前
LangChain 完全学习手册:看完就能上手
后端·python·ai编程
Aawy1203 小时前
Python生成器(Generator)与Yield关键字:惰性求值之美
jvm·数据库·python
爱吃的小肥羊3 小时前
ChatGPT、Claude、Gemini,到底该给谁交钱?这是我的深度测评
aigc·openai·ai编程