体验完阿里「悟空」之后,我花 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 聚合接口统一调用

相关推荐
Csvn21 小时前
🌟 LangChain 30 天保姆级教程 · Day 13|OutputParser 进阶!让 AI 输出自动转为结构化对象,并支持自动重试!
python·langchain
AI攻城狮1 天前
用 Obsidian CLI + LLM 构建本地 RAG:让你的笔记真正「活」起来
人工智能·云原生·aigc
cch89181 天前
Python主流框架全解析
开发语言·python
sg_knight1 天前
设计模式实战:状态模式(State)
python·ui·设计模式·状态模式·state
好运的阿财1 天前
process 工具与子agent管理机制详解
网络·人工智能·python·程序人生·ai编程
张張4081 天前
(域格)环境搭建和编译
c语言·开发语言·python·ai
weixin_423533991 天前
【Windows11离线安装anaconda、python、vscode】
开发语言·vscode·python
爱吃的小肥羊1 天前
最火的 AI 生图模型 Nano Banana Pro,国内到底怎么免费用?
aigc
Ricky111zzz1 天前
leetcode学python记录1
python·算法·leetcode·职场和发展