最近技术社区里“奥特曼最后一战:4个月后,交付AGI”的话题讨论热度很高。这类标题容易吸引眼球,但对于我们写代码的人来说,更需要关心的不是口号,而是:如果真把“4个月交付AGI”当作一个工程目标,它到底意味着什么?第一步该做什么?需要哪些模块?有哪些坑?
这篇文章我想换一个务实的角度,把“AGI交付”拆成一个可落地的技术命题:多模态 AGI 系统的最小实现路径。我们会先讲清楚 AGI、多模态 AGI 的概念边界,再拆解一个多模态智能体系统的核心模块,最后用 FastAPI + 视觉语言模型 + 工具调用,从零搭一个“多模态工单助手”雏形。整个流程我会按照 4 个月的时间线来排,适合想快速验证想法、做内部原型、或者准备智能体应用的开发者参考。
1. “4个月交付AGI”背后的技术命题
1.1 先界定概念:AGI 与多模态 AGI
AGI 是 Artificial General Intelligence 的缩写,中文常翻译为“通用人工智能”。它指的是一种能够像人类一样,在不同领域、不同任务之间灵活迁移能力的人工智能系统。与当前常见的“狭义人工智能”不同,AGI 不只会下棋、不只会翻译、不只会做图像分类,而是能理解复杂目标、拆解任务、跨模态获取信息,并在新环境中自主决策。
“多模态 AGI”是在 AGI 概念之上增加了模态维度。所谓模态,指的是信息的表达形式:文本、图片、音频、视频、传感器数据等。多模态 AGI 需要同时处理并融合多种模态的信息。举个例子,用户上传一张报错截图,再用自然语言描述“登录时出现这个提示”,多模态系统要能“看懂”图片内容,理解文字描述,结合上下文判断问题原因,最终给出处理建议。
需要说明的是,截至本文写作时,业界并没有公认的 AGI 验收标准,也没有哪家公司公开完成过严格意义上的 AGI 交付。我们现在能做的,是在限定领域内构建“接近 AGI 体验”的系统,这也是很多团队所说的“AGI 应用落地”。
1.2 为什么“交付AGI”是一个伪命题
从工程角度讲,“4个月后交付AGI”这句话最大的问题在于:AGI 不是一个可验收的软件版本,而是一个没有终结点的研究目标。
如果把它作为项目需求,我们需要回答一系列问题:
- 交付范围是什么?是文本问答、图像理解,还是包含工具调用、自主规划、多轮记忆?
- 验收标准是什么?准确率多少算达标?失败率允许是多少?
- 运行环境是什么?私有化部署还是云端 API?
- 安全边界在哪里?哪些操作允许 Agent 自主执行,哪些必须人工确认?
- 数据从哪里来?评测集如何构建?效果如何度量?
这些问题的答案,恰恰构成了一个多模态智能体系统真正的研发范围。因此,我更倾向于把这句话改写成:“在 4 个月内,交付一个具备多模态感知、任务规划、工具调用和短期记忆能力的智能体系统。”这才是一个可以排期、可以验收、可以上线的工程目标。
1.3 可落地的工程目标:交付多模态 Agent
一个可落地的多模态 Agent,通常包含四个能力层:
第一层是感知层,负责接收文本、图片、语音等输入,并转换成模型可以理解的格式。第二层是决策层,由大语言模型或视觉语言模型完成推理、分析和任务拆解。第三层是执行层,通过调用外部工具完成实际操作,比如查询数据库、发送消息、创建工单。第四层是记忆层,保存历史对话、用户偏好和任务状态,让系统具备连续服务能力。
这四个层次不是理论框架,而是直接决定代码结构的模块划分。下面我会围绕这四层,搭建一个真实可运行的多模态 Agent 原型。
2. 多模态 Agent 系统的功能拆解
2.1 感知层:多模态输入解析
感知层是所有能力的前置条件。对文本输入,处理相对简单,直接作为 prompt 的一部分传入模型即可。对图片输入,常见做法有两种:
一种是调用支持视觉理解的模型 API,把图片转为 Base64 编码后,以image_url形式传给模型。另一种是本地先用图像理解模型抽取图片信息,生成结构化的文字描述,再交给语言模型处理。第一种链路短、效果好,适合快速原型;第二种适合图片信息需要保留结构化字段、或模型不支持图片输入的场景。
实际开发中,感知层还要考虑图片大小、格式、清晰度、隐私脱敏等问题。比如用户上传的截图可能包含手机号、姓名等敏感信息,在进入模型之前就需要做脱敏处理,这是很多项目容易忽略的点。
2.2 决策层:推理与任务规划
决策层是整个 Agent 的“大脑”。当用户输入“我的订单三天了还没到账,请帮我查一下”时,模型需要理解:
- 用户意图是查询订单状态。
- 当前系统是否有相关工具可以查询。
- 如果信息不足,需要反问用户补充什么信息。
现代大模型通过指令跟随能力可以完成大部分推理,但工程上要注意两点:一是 prompt 设计要清晰,告诉模型系统有哪些工具、什么时候该调用工具、调用后如何组织最终回答;二是要限制模型的自由发挥空间,比如要求模型在调用工具时输出严格 JSON 格式,或者直接使用模型提供的 function calling 能力。
2.3 执行层:工具调用与行动闭环
工具调用是让 Agent 从“聊天”走向“办事”的关键。一个多模态 Agent 系统如果只能回答问题,价值有限;只有能调用工具完成实际操作,才具备业务闭环能力。
在代码实现上,工具调用通常包含三个步骤:
- 模型输出包含工具名和参数的结构化结果。
- 系统解析并校验参数,判断该工具是否在白名单内。
- 执行工具函数,把返回值拼接回对话上下文,让模型基于结果生成最终回复。
工具层需要特别注意权限控制和参数校验。生产环境下,Agent 调用的每一个工具都应该有明确的调用方身份、操作类型、操作对象和审计记录。敏感操作比如删除数据、发送消息、创建订单,建议增加人工确认环节,而不是让 Agent 全自动执行。
2.4 记忆层与持续学习
记忆层决定了 Agent 是否具备“连续服务”的能力。最简单的记忆是会话级记忆,把多轮对话直接拼接在请求上下文中。更复杂一些的是长期记忆,把用户画像、历史偏好、历史任务结果存入向量数据库,在每次对话前检索相关记忆片段。
我这里的设计会先采用内存级会话记忆,用消息数组维护上下文。生产环境建议引入外部的向量数据库或键值存储,再配合定时清理策略,避免上下文无限增长导致 token 成本失控。
3. 环境准备与项目结构
3.1 运行环境与依赖
本文示例以 Python 3.10+ 为基础,使用 FastAPI 提供 HTTP 服务。模型层通过 OpenAI 兼容接口调用视觉语言模型,这样不绑定特定云厂商,只要你使用的模型平台提供 OpenAI 兼容的/chat/completions接口,就可以直接接入。
依赖文件如下,示例中不锁定具体版本,请以你环境验证通过的版本为准:
# requirements.txt fastapi uvicorn openai python-dotenv pydantic requests安装命令:
pip install -r requirements.txt3.2 模型服务选型
代码中通过环境变量配置三个关键参数:
MODEL_API_BASE:模型服务的 API 地址。MODEL_API_KEY:访问密钥。MODEL_NAME:视觉语言模型名称。
你需要在.env文件中填写自己使用的模型信息。本文不指定具体模型名称,因为不同平台、不同时期的模型命名和接口可能有差异。建议选择一个支持图像输入的视觉语言模型来完成多模态部分。
3.3 项目目录设计
整个项目采用轻量结构,方便你按模块扩展:
multimodal_agent/ ├── .env.example # 环境变量示例 ├── requirements.txt # Python 依赖 ├── config.py # 配置读取 ├── tools.py # 工具函数定义 ├── agent.py # Agent 核心逻辑 ├── main.py # FastAPI 服务入口 └── test_agent.py # 简单自测脚本这样的结构划分清楚,职责明确:配置管配置,工具管工具,Agent 管流程,Main 管接口。
4. 四个月实战:搭建多模态 Agent 雏形
4.1 第一个月:场景定义与模型接入
第一个月不建议直接写代码,而是先做两件事:定义场景和准备评测数据。
场景定义要具体。以“多模态工单助手”为例,我们可以设定几类典型任务:
- 用户上传报错截图并描述问题,Agent 判断原因并给出解决步骤。
- 用户询问订单状态,Agent 调用查询工具返回结果。
- 用户提交的图片不清晰,Agent 主动要求重新上传。
评测数据不需要很多,20 到 50 条典型样本即可,关键是覆盖正常路径和失败路径。比如图片模糊、文字描述与图片无关、工具查询超时等情况都要准备。
模型接入这一步,先验证一件事:用一段最简单的代码,把一张图片和一段文字发送给模型,确认返回结果符合预期。这样可以尽早暴露模型选型、接口格式、网络连通性问题。
4.2 第二个月:工具调用与记忆
第二个月开始写核心代码。首先是配置读取模块,把环境变量统一管理起来:
# config.py import os from dotenv import load_dotenv load_dotenv() MODEL_API_BASE = os.getenv("MODEL_API_BASE", "https://your-model-provider.example.com/v1") MODEL_API_KEY = os.getenv("MODEL_API_KEY", "") MODEL_NAME = os.getenv("MODEL_NAME", "your-vision-language-model") TOOL_WHITELIST = os.getenv("TOOL_WHITELIST", "search_kb,create_order,query_order").split(",")接下来定义工具函数。这里用模拟实现演示思路,实际项目中需要把函数体替换为真实业务系统调用:
# tools.py import json def search_kb(query: str) -> str: """模拟知识库检索。""" data = { "无法登录": "请先清理浏览器缓存,再尝试重置密码。", "订单未到账": "请检查订单状态,若超过 24 小时未到账,可提交人工审核。", "图片无法识别": "请确认图片格式为 JPG/PNG,且单张图片小于 5MB。", } for key, value in data.items(): if key in query: return json.dumps({"answer": value}, ensure_ascii=False) return json.dumps({"answer": "未找到匹配知识,已转人工。"}, ensure_ascii=False) def create_order(content: str) -> str: """模拟创建工单。""" return json.dumps({"order_id": "WO20250001", "status": "created"}, ensure_ascii=False) def query_order(order_id: str) -> str: """模拟查询工单状态。""" return json.dumps({"order_id": order_id, "status": "processing"}, ensure_ascii=False)这里有一个很重要的工程点:工具函数必须返回 JSON 字符串。原因是工具返回值要拼接回模型上下文,模型对结构化的 JSON 文本理解更稳定,后续解析也方便。
4.3 第三个月:评估与护栏
第三个月的重点不是增加新功能,而是把“评估”和“护栏”补上。
Agent 核心逻辑实现如下:
# agent.py import json from openai import OpenAI from config import MODEL_API_BASE, MODEL_API_KEY, MODEL_NAME, TOOL_WHITELIST import tools client = OpenAI(base_url=MODEL_API_BASE, api_key=MODEL_API_KEY) SYSTEM_PROMPT = ( "你是一个多模态工单助手。用户会提供文字描述和可能存在的图片。" "请你先理解问题,再决定是否需要调用工具。" "需要调用工具时,请严格输出如下 JSON 格式:\n" '{"tool": "工具名", "args": {"参数": "值"}}\n' "可选工具:" + ", ".join(TOOL_WHITELIST) + "。" "不需要调用工具时,直接输出最终回复。" ) def build_messages(user_text: str, image_base64: str = None): content = [{"type": "text", "text": user_text}] if image_base64: content.append( { "type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_base64}"}, } ) return content def try_parse_tool_call(reply: str): """尝试从模型输出中解析 JSON 工具调用。""" try: return json.loads(reply) except Exception: return None def run_agent(user_text: str, image_base64: str = None, max_steps: int = 3): messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": build_messages(user_text, image_base64)}, ] for step in range(max_steps): response = client.chat.completions.create( model=MODEL_NAME, messages=messages, temperature=0.2, ) reply = response.choices[0].message.content parsed = try_parse_tool_call(reply) if parsed is None: return reply tool_name = parsed.get("tool") if tool_name not in TOOL_WHITELIST: return f"工具 {tool_name} 不在白名单中,已终止操作。" args = parsed.get("args", {}) func = getattr(tools, tool_name, None) if func is None: return f"工具 {tool_name} 不存在,已终止操作。" result = func(**args) messages.append({"role": "assistant", "content": reply}) messages.append( { "role": "user", "content": f"工具返回结果如下,请基于结果给出最终回复:\n{result}", } ) return "达到最大调用步数,已停止。"为什么要加max_steps限制?因为在实际运行中,模型可能陷入“反复调用工具”的死循环。设置最大步数,一方面保护系统资源,另一方面避免 token 成本失控。
提示词里要求模型严格输出 JSON,只是一个保守兜底方案。生产环境更推荐使用模型自带的 function calling 能力,让模型返回结构化工具调用参数,解析会更稳定。上面代码的解析方式,适合教学演示和模型功能受限时的过渡方案。
4.4 第四个月:部署、灰度与监控
最后一个月要把它变成可交付的服务。我们使用 FastAPI 封装一个/chat接口,接收文本和可选的图片文件:
# main.py import base64 from fastapi import FastAPI, File, Form, UploadFile from agent import run_agent app = FastAPI(title="多模态工单助手") @app.post("/chat") async def chat( message: str = Form(...), image: UploadFile = File(None), ): image_base64 = None if image is not None: raw = await image.read() image_base64 = base64.b64encode(raw).decode("utf-8") result = run_agent(message, image_base64) return {"reply": result}启动服务:
uvicorn main:app --reload接口自测:
curl -X POST http://127.0.0.1:8000/chat \ -F "message=用户上传了无法登录的截图,请帮忙处理" \ -F "image=@screenshot.jpg"如果一切正常,你会看到 Agent 先调用search_kb检索知识,再基于工具结果返回可读的解决建议。
4.5 核心流程梳理
整个 Agent 的完整调用链可以简化为:
- 用户上传文本 + 图片。
- 服务将文本和 Base64 图片组装为多模态消息。
- 模型判断是否需要调用工具。
- 如果不需要,直接返回回答。
- 如果需要,系统解析工具名和参数,校验白名单,执行工具函数。
- 工具结果拼回上下文,模型生成最终回复。
这个流程看起来很简洁,但每个环节都有不少细节。比如图片过大时需要压缩;模型返回的工具参数可能与函数签名不匹配,需要做兼容处理;工具执行超时后要设置兜底回复。
5. 常见问题与排查思路
在实际开发中,最常见的几个问题我整理成了一张表:
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 模型返回乱码或非 JSON | 提示词约束不足,模型没有按格式输出 | 使用 function calling 能力;增加 JSON 格式示例;对输出做容错解析 |
| 图片上传后请求失败 | 图片过大,Base64 编码后超出接口限制 | 限制图片大小,服务端压缩后再编码 |
| 工具调用总是失败 | 参数名与函数签名不一致 | 在提示词中给出参数示例,代码层做参数兼容校验 |
| 模型回答“不知道” | 工具检索结果没有正确拼入上下文 | 检查 messages 拼接逻辑,确认工具结果确实追加到了下一轮请求 |
| 运行成本快速上升 | 图片 token 消耗大,循环调用无上限 | 限制 max_steps,对图片做缩放,增加日志统计 |
| 系统被恶意利用 | 工具权限控制不足 | 增加白名单校验、身份认证和操作审计 |
排查这类问题时,我建议先看日志,再看 messages 内容。很多 Agent 问题的根源并不在模型本身,而是上下文拼接错误。你可以把每一轮的 messages 输出到日志文件,检查模型输入是否包含完整的历史信息、工具调用记录和检索结果。
6. 工程最佳实践与安全建议
6.1 配置管理与密钥保护
模型 API Key 这类敏感信息,绝对不要写死在代码里,也不要提交到 Git 仓库。本地开发用.env文件管理,生产环境优先使用配置中心或云平台的密钥管理服务。示例项目里已经通过python-dotenv读取环境变量,你把真实配置写入.env后,记得把.env加入.gitignore。
6.2 日志与可观测性
Agent 系统比普通接口更难排查问题,因为多了一步“模型决策”的过程。建议每个请求都记录以下信息:
- 用户标识和会话标识。
- 输入的文本和图片信息(注意脱敏)。
- 模型每一轮的原始输出。
- 工具调用名称、参数和返回结果。
- 耗时和 token 消耗。
- 最终回复内容。
有了这些日志,你才能在用户反馈“回答不准”的时候,快速定位是模型理解错了,还是工具返回数据有问题。
6.3 安全与合规
多模态 Agent 涉及图片上传,很容易引入个人信息和敏感数据。上线前要确认:
- 图片是否需要脱敏处理后再传给模型。
- 模型服务是否支持数据私有化或数据不落盘。
- 工具调用是否具备完整权限校验。
- 删除、转账、发消息等高危操作是否有人工审批环节。
权限控制要遵循最小权限原则,Agent 只能调用当前场景必需的工具,而不是把所有内部系统接口都暴露给模型。
6.4 性能与成本控制
多模态请求的 token 消耗通常比纯文本高很多。控制成本可以从几个角度入手:
- 上传图片前先压缩并限制分辨率。
- 对于固定模板类图片,可以先做 OCR 或图像摘要,再以文字形式交给模型。
- 对工具调用步数设置上限。
- 增加缓存,相同或相似的问题可以复用之前的回答。
性能方面,FastAPI 的异步接口适合处理 IO 密集型任务,但模型调用本身耗时较长,建议在前端或网关层设置合理的超时时间,并考虑把长耗时任务改为异步队列处理。
7. 总结与下一步
从“交付AGI”这个宏大的口号,落到一个 4 个月可执行的多模态 Agent 项目,中间隔着的不是模型能力的差距,而是工程化的完整度。我在这篇文章里分享了一个最简可行的技术路径:感知层处理多模态输入,决策层负责推理规划,执行层通过工具调用完成业务闭环,记忆层保存对话上下文。同时给出了一个可以运行的多模态工单助手示例,覆盖了从模型接入、工具调用、接口封装到安全护栏的完整链路。
如果你打算自己动手实践,我建议先不要急着扩展功能,而是按这个顺序来:第一周先用你最熟悉的模型平台跑通“图片 + 文字进、文字出”的接口;第二周给模型加一个查询工具,让它学会在需要的时候调用;第三周整理你的场景评测集;第四周再把服务部署到测试环境。
跑通第一个版本之后,再逐步增加长期记忆、权限控制、灰度发布和监控告警。你会发现,真正难的不是模型调不通,而是如何在模型输出不稳定、工具状态多样、用户需求多变的情况下,让系统依然可控、可追踪、可回滚。这也是 4 个月交付窗口里最值得投入时间打磨的部分。