当时看 Manus 的演示,很多人心里都会冒出一个念头:这才是我想要的 AI。它不再只是聊天框里一句一句地挤答案,而是像一位远程实习生一样,自己打开网页、读取文件、整理信息,最后交给你一份完整结果。这种体验确实让 Agent 第一次在大众层面变得可感知。
但热闹过去之后,真正在做工程的人会发现,问题并没有消失,反而更清楚了:演示环境里能跑通的 Agent,到了真实业务里为什么动不动就断?任务一复杂为什么就开始乱调用工具?角色一多为什么状态就失控?于是就有了这篇文章想聊的话题——林俊旸和 Manus,双双回到原点。这里的“原点”不是退步,而是回到 Agent 工程最朴素的地基:任务规划、工具权限、状态管理、结果验证、成本控制和可观测性。
这篇文章不讨论人物八卦,也不为任何产品站台。我会从工程视角拆解一个问题:一个 Agent 从“拿得出手的 Demo”到“能上生产的服务”,到底要补哪些课?并且我们不用任何复杂的框架,就用 Python + FastAPI + OpenAI 兼容接口,从零搭一个最小但结构完整的 Agent 服务,把整个循环跑通。
如果你正在做 AI Agent、正在被“效果不稳定”和“流程不可控”折磨,这篇文章应该能给你一个相对清晰的坐标系。
1. 回到哪个“原点”:从 Demo 到工程
1.1 Agent 演示为什么让人觉得震撼
传统聊天机器人给人的感觉是“有问有答”,但 Agent 给人的感觉是“能办事”。为什么会有这种差异?核心在于 Agent 引入了工具调用和任务循环。
一个聊天机器人只做一件事:根据用户输入生成文字。而一个 Agent 可以:
- 根据任务目标拆解步骤;
- 选择合适工具;
- 读取工具返回结果;
- 根据新信息继续决策;
- 直到任务完成或主动请求人工介入。
Manus 当初最打动人的点,就是它把这条链路以相对流畅的交互方式展示了出来。用户不需要理解背后的规划逻辑,只需要看到它一步步把任务做完。这种“把复杂度藏起来”的产品体验,是 Agent 概念破圈的真正原因。
1.2 Demo 与可用之间的差距到底在哪
如果说 Demo 是“在受控环境里证明路径可行”,那生产系统就是“在不受控环境里保证结果可用”。两者之间差着好几层工程问题。
第一层是稳定性。演示时任务通常是精心挑选的,可能在固定网站、固定格式、固定网络环境下运行。真实业务里,输入千奇百怪,工具返回时好时坏,模型偶尔抽风,任何一个环节波动都可能导致 Agent 中断。
第二层是可控性。演示时你可以接受 Agent 自由发挥,但生产环境必须知道它下一步要做什么、为什么这样做、如果做错了怎么回滚。这要求 Agent 的每一步决策都有迹可循。
第三层是成本。一次复杂任务可能需要调用几十次模型接口,如果任务量大,成本会指数级上升。演示时不需要关心 token 钱,生产环境每一分钱都是成本。
第四层是安全。给 Agent 开放工具时,它可能访问到不该访问的数据,也可能执行不该执行的命令。演示环境无所谓,生产环境这就是事故。
所以你会发现,Manus 和很多 Agent 产品带来的最大价值,不是“实现了 AGI”,而是把 Agent 这个概念从论文推进到了产品验证阶段。而当行业开始认真做产品时,所有人不约而同回到了工程原点。
1.3 我的判断
从开发者的视角看,Agent 的下半场拼的不是谁的演示更炫,而是谁能在工程层面把不确定性控制住。这篇文章就围绕这个判断展开。
我会用最朴素的工程手段实现一个 Agent 服务,不用 LangChain、不用 CrewAI,甚至连异步框架都不上。因为很多 Agent 框架把复杂度遮蔽掉了,你很难理解一个任务到底是怎么被执行的。当你亲手把循环写出来,很多“为什么效果不稳定”的问题会立刻有答案。
2. 再谈 Agent 核心概念:别把 Demo 当产品
2.1 Agent 到底是什么
先给一个简单但不失准确的定义:
Agent 是一个能感知环境、做出决策、执行动作,并基于执行结果继续调整的 AI 系统。
这里的关键词是“循环”。传统 AI 应用是一次性输入输出,Agent 则是多次“输入-决策-动作-观察-再决策”的循环。
一个最基本的 Agent 至少包含五个部分:
- 大语言模型:负责推理和决策;
- 任务目标:用户当前想要完成的事情;
- 工具集:Agent 可以调用的外部能力;
- 状态管理:记录历史信息、中间结果和当前进度;
- 执行验证:确认结果是否满足要求,决定继续还是结束。
缺少任何一部分,Agent 都会变成“看起来很聪明但无法闭环”的玩具。
2.2 Agent、工作流、RAG 的区别
很多读者容易把 Agent、工作流和 RAG 混在一起,这里用一个表格做区分。
| 概念 | 核心特征 | 适合场景 | 典型问题 |
|---|---|---|---|
| 工作流 | 固定流程,步骤预先编排 | 流程稳定、输入变化小 | 无法处理未知分支 |
| RAG | 检索增强生成,先查后答 | 知识库问答、文档辅助 | 只读,不能执行动作 |
| Agent | 动态规划,工具调用,循环决策 | 多步骤、需要外部动作 | 稳定性、成本、安全 |
简单来说,工作流是“唱本子”,RAG 是“查资料”,Agent 是“边想边做”。三者并不互斥,实际项目里经常组合使用:先用 RAG 检索资料,再用 Agent 决定调用哪些工具,最后用固定工作流兜底。
2.3 关键机制:工具调用与 ReAct 循环
在 OpenAI 的工具调用(Function Calling)出现之前,开发者要让模型使用工具,通常靠提示词约定 JSON 输出格式,非常脆弱。Function Calling 的意义在于,模型在生成时会被要求从给定工具列表中选择一个,并输出结构化参数,由程序解析后执行。
而 ReAct 循环是一个经典的 Agent 思维框架:模型先推理(Reason)当前状态,然后决定执行什么动作(Act),观察结果后再推理下一步。名字虽然看起来高大上,本质就是“想一下、做一下、看一眼结果、再想”。
我建议每个 Agent 开发者都亲手实现一次 ReAct 循环。因为只有当你自己解析 tool_calls 时,才会理解为什么 Agent 会“卡住”。
2.4 为什么“回到原点”是回归工程
回到原点的意思,不是放弃语言模型,而是认识到模型只是 Agent 的一个零件。真正决定一个 Agent 能不能用的,是围绕模型搭建的工程系统。
- 模型决定“聪明程度”;
- 工程决定“可靠程度”。
大多数失败的 Agent 项目,不是模型不够聪明,而是工程结构不够稳。工具没有权限控制、状态没有持久化、失败没有重试、结果没有校验,最后看起来就像“一个聪明人做着不靠谱的事”。
3. 环境准备与最小场景设计
3.1 场景:技术报告分析助手
为了不过度抽象,我们设计一个真实感比较强的场景:一个“技术报告分析助手”,用户提交报告文件路径和统计请求,Agent 读取本地文件、提取关键指标、做必要计算,最后给出结论。
这个场景具备 Agent 的典型要素:
- 需要读取工具;
- 需要计算工具;
- 需要根据文件内容决定下一步;
- 需要生成最终结论。
同时它又足够安全,不涉及网络请求,也不会触发危险命令,适合作为入门示例。
3.2 环境准备
示例使用 Python 3.10 以上版本,建议先创建虚拟环境。
mkdir agent-origin-demo && cd agent-origin-demo python3 -m venv venv source venv/bin/activate pip install -U pip本文不指定第三方库的具体版本,以实际安装时为准。核心依赖如下:
fastapi uvicorn openai pydantic其中 openai 库用于调用 LLM 服务的 OpenAI 兼容接口。如果你使用的是本地 Ollama,只需要把 base_url 指向本机服务即可,代码不用改。
3.3 目录结构
推荐按模块拆分,不要让 Agent 逻辑和 API 层耦合在一个文件里。
agent-origin-demo/ ├── requirements.txt ├── tools.py # 工具函数定义与实现 ├── llm_client.py # LLM 客户端封装 ├── agent.py # Agent 核心循环 ├── api.py # FastAPI 服务入口 ├── workspace/ # 工作目录,存放报告文件 │ └── report.md └── run.py # 本地直接运行的入口这样的结构足够简单,也能看出分层思想。如果以后要加数据库、任务队列,可以继续往里扩展。
4. 核心流程拆解
4.1 任务拆解:从用户 Query 到步骤
用户提交一个自然语言请求后,Agent 第一件事不是执行,而是理解任务。在 ReAct 循环里,这一步通常由模型在第一次决策时完成。
例如用户说“读取 reports/demo.md 并汇总其中性能指标”,模型需要意识到:
- 先调用 read_report 工具读取文件;
- 观察返回内容;
- 判断是否有指标数据;
- 如果有,调用 aggregate_metrics 完成计算;
- 生成最终结论。
这个拆解过程不需要预先编写,而是模型基于工具描述和用户目标动态生成。这就是 Agent 与固定工作流的本质区别。
4.2 工具设计:给 Agent 最小可用能力
工具是 Agent 的“手”,设计工具时必须克制。一个常见错误是给 Agent 塞了太多工具,导致它频繁选错。
工具描述非常关键。模型只能通过 description 理解工具用途,所以描述要清晰、具体,最好包含使用场景示例。
另一个关键点是工具参数需要有约束。比如 path 参数必须给定,values 参数必须是数字数组,不能放任模型自由发挥。
4.3 执行循环:决策—调用—观察
Agent 的核心循环可以浓缩成一句话:把模型当成“指挥官”,把工具当成“士兵”,每轮先让指挥官给指令,执行完士兵汇报结果,指挥官再决定下一步。
这个循环有几个容易踩的坑:
- 没有设置最大步数,模型陷入死循环;
- 工具调用后没有把结果传回模型,模型无法继续;
- 解析 tool_calls 时崩溃,导致整个任务失败;
- 模型返回空内容,但 Agent 仍然继续。
后面示例代码会逐一处理这些问题。
4.4 结果确认与人工介入
在演示环境里,Agent 执行完就可以返回结果。但生产环境中,越关键的操作越需要人工确认。
一个合理的策略是:对于只读工具,Agent 可以自动执行;对于写操作、删除操作、支付操作,必须设置审批节点。这个策略在后面的最佳实践部分会详细展开。
在最小示例中,我们先不做人工审批,但会在代码结构上预留判断入口。
5. 完整示例代码实现
5.1 工具实现
文件路径:tools.py
# -*- coding: utf-8 -*- """ 工具函数定义与实现。 每个工具都尽量保持单一职责,避免一个函数做太多事情。 """ import json from pathlib import Path BASE_DIR = Path.cwd() / "workspace" def read_report(path: str) -> dict: """ 读取 workspace 目录下的报告文件。 为了安全,限制只能读取 workspace 内的文件,防止路径穿越。 """ try: target = (BASE_DIR / path).resolve() if not str(target).startswith(str(BASE_DIR.resolve())): return {"error": "路径越界,拒绝访问"} if not target.exists(): return {"error": f"文件不存在: {path}"} content = target.read_text(encoding="utf-8") return {"path": str(target), "content": content[:2000]} except Exception as exc: return {"error": f"读取文件失败: {exc}"} def aggregate_metrics(values: list) -> dict: """计算一组数值指标的汇总信息。""" if not values: return {"sum": 0, "count": 0, "avg": 0} total = sum(values) count = len(values) return { "sum": total, "count": count, "avg": round(total / count, 2), } TOOL_DEFINITIONS = [ { "type": "function", "function": { "name": "read_report", "description": "读取工作目录中的报告文件,返回文件内容。适用于需要分析报告文本的场景。", "parameters": { "type": "object", "properties": { "path": { "type": "string", "description": "报告文件的相对路径,例如 report.md" } }, "required": ["path"] } } }, { "type": "function", "function": { "name": "aggregate_metrics", "description": "计算一组数值指标的总和、数量与平均值。适用于需要对报告中的指标做统计的场景。", "parameters": { "type": "object", "properties": { "values": { "type": "array", "items": {"type": "number"}, "description": "数值列表" } }, "required": ["values"] } } } ] def dispatch_tool(name: str, arguments: dict) -> dict: """ 工具分发器:根据工具名称调用对应函数。 不要直接把用户输入拼进工具名,防止注入。 """ if name == "read_report": return read_report(arguments.get("path", "")) if name == "aggregate_metrics": values = arguments.get("values", []) return aggregate_metrics(values) return {"error": f"未知工具: {name}"}这里特别说明一下路径安全。Agent 调用工具时,参数来自模型,而模型可能被用户输入引导。所以工具内部必须做路径校验。示例使用的是一种简单的白名单校验,能挡住../路径穿越,但不等于绝对安全,生产环境建议把工作目录放到独立沙箱里。
5.2 LLM 客户端封装
文件路径:llm_client.py
# -*- coding: utf-8 -*- """ LLM 客户端封装,统一管理模型调用。 支持 OpenAI 官方接口,也支持任何兼容 OpenAI 协议的本地服务。 """ import os from openai import OpenAI class LLMClient: def __init__(self): self.client = OpenAI( base_url=os.getenv("LLM_BASE_URL"), api_key=os.getenv("LLM_API_KEY", "not-needed"), ) # 这里的默认模型名只是一个占位示例,请根据实际服务修改 self.model = os.getenv("LLM_MODEL", "gpt-4o-mini") def chat(self, messages: list, tools: list = None): """ 调用模型对话接口。 tools 为空时不传,避免部分服务不支持。 """ kwargs = { "model": self.model, "messages": messages, } if tools: kwargs["tools"] = tools response = self.client.chat.completions.create(**kwargs) return response.choices[0].message使用环境变量可以避免把密钥写进代码。如果你的模型服务不需要密钥,例如本地 Ollama,可以设置LLM_API_KEY=not-needed,并将LLM_BASE_URL指向本地地址。
5.3 Agent 核心循环
文件路径:agent.py
# -*- coding: utf-8 -*- """ 最小 ReAct Agent 循环。 核心思路:模型决策 -> 程序执行工具 -> 返回结果 -> 模型继续决策 """ import json from llm_client import LLMClient from tools import TOOL_DEFINITIONS, dispatch_tool MAX_STEPS = 5 SYSTEM_PROMPT = """ 你是一个技术报告分析助手。 你需要根据用户的问题,逐步调取工具获取信息,并最终给出分析结论。 每一步都要清楚:你当前需要什么信息,调用哪个工具,然后根据结果继续。 不要臆造不存在的文件或指标,所有结论必须基于工具返回的真实内容。 """ def run_agent(user_query: str, max_steps: int = MAX_STEPS) -> str: llm = LLMClient() messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_query}, ] for step in range(max_steps): # 1. 让模型决策 assistant_message = llm.chat(messages, tools=TOOL_DEFINITIONS) # 2. 记录模型回复 assistant_payload = { "role": "assistant", "content": assistant_message.content or "", } # 3. 如果模型要求调用工具 if assistant_message.tool_calls: tool_calls = [] for tc in assistant_message.tool_calls: tool_calls.append({ "id": tc.id, "type": "function", "function": { "name": tc.function.name, "arguments": tc.function.arguments, }, }) assistant_payload["tool_calls"] = tool_calls messages.append(assistant_payload) # 4. 没有工具调用,说明模型认为可以结束了 if not assistant_message.tool_calls: return assistant_message.content or "(模型未返回内容)" # 5. 逐个执行工具,并把结果返回给模型 for tc in assistant_message.tool_calls: name = tc.function.name try: arguments = json.loads(tc.function.arguments or "{}") except json.JSONDecodeError: arguments = {} result = dispatch_tool(name, arguments) messages.append({ "role": "tool", "tool_call_id": tc.id, "content": json.dumps(result, ensure_ascii=False), }) return "已达最大步数,任务未能完成。请简化问题或检查工具配置。"这个循环看起来简单,但已经覆盖了 Agent 最核心的执行链路。你没有依赖任何 Agent 框架,所以每一步都清清楚楚。
5.4 FastAPI 服务入口
文件路径:api.py
# -*- coding: utf-8 -*- """ FastAPI 服务入口:把 Agent 能力包装成 HTTP 接口。 """ from fastapi import FastAPI, HTTPException from pydantic import BaseModel from agent import run_agent app = FastAPI(title="Minimal Agent Demo") class AgentRequest(BaseModel): query: str class AgentResponse(BaseModel): ok: bool result: str @app.post("/agent/run", response_model=AgentResponse) def agent_run(req: AgentRequest): if not req.query.strip(): raise HTTPException(status_code=400, detail="query 不能为空") try: result = run_agent(req.query) except Exception as exc: # 这里只做最小异常处理,生产环境应该记录完整 traceback raise HTTPException(status_code=500, detail=f"Agent 执行失败: {exc}") return AgentResponse(ok=True, result=result)5.5 本地直接运行入口
文件路径:run.py
# -*- coding: utf-8 -*- """ 本地直接运行入口,方便在没有启动 HTTP 服务时快速调试。 """ from agent import run_agent if __name__ == "__main__": query = "读取 report.md 并汇总其中所有性能指标" result = run_agent(query) print(result)6. 运行结果与效果验证
6.1 准备测试文件
在 workspace 目录下创建report.md:
# 2025 年第一季度系统性能报告 - 接口平均响应时间: 120ms - 接口最大响应时间: 350ms - 每日请求总量: 860000 - 错误率: 0.02% ## 结论 系统整体运行稳定,响应时间符合 SLO 目标。这个文件内容很简单,但足够让 Agent 执行一次“读取 + 提取 + 汇总”的完整链路。
6.2 启动服务
先配置环境变量。
export LLM_BASE_URL="https://api.openai.com/v1" export LLM_API_KEY="你的密钥" export LLM_MODEL="gpt-4o-mini"如果你使用 Ollama,大致是这样:
export LLM_BASE_URL="http://localhost:11434/v1" export LLM_API_KEY="not-needed" export LLM_MODEL="qwen2.5:7b"然后启动服务:
uvicorn api:app --host 0.0.0.0 --port 8000看到类似下面的日志说明启动成功:
INFO: Uvicorn running on http://0.0.0.0:80006.3 发送测试请求
用 curl 调用接口:
curl -X POST http://127.0.0.1:8000/agent/run \ -H "Content-Type: application/json" \ -d '{"query": "读取 report.md 并汇总其中所有性能指标"}'如果一切正常,返回结果大致是这样的结构:
{ "ok": true, "result": "根据报告内容,我提取到以下性能指标:平均响应时间 120ms,最大响应时间 350ms,每日请求量 860000,错误率 0.02%。对平均响应时间、最大响应时间和每日请求量进行汇总:三个指标合计为 860470,平均为 286823.33。错误率属于比例指标,不参与算术汇总。整体来看,系统运行稳定。" }注意:真实结果会因模型能力不同而略有差异,但结构应该一致。
6.4 如何判断 Agent 是否成功
判断标准不只是“有没有返回文字”,而是:
- 是否读取了文件;
- 是否提取了正确的指标;
- 是否调用了 aggregate_metrics;
- 最终结论是否基于工具返回的真实数据;
- 是否没有臆造文件内容。
如果你的 Agent 没有调用工具,而是直接编造了一份报告,那说明提示词或工具描述需要调整。这是 Agent 调试中最常见的问题。
7. 常见问题与排查思路
以下是 Agent 开发中最常见的问题和排查方法。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 模型不调用工具,直接生成答案 | 工具描述不清晰,或模型能力不足 | 打印 messages 中模型返回内容 | 优化工具 description,换用支持 Function Calling 的模型 |
| 工具调用参数解析失败 | 模型返回的 arguments 不是合法 JSON | 捕获 JSONDecodeError 并打印原始内容 | 在解析失败时把错误信息返回模型,让它重新生成 |
| Agent 陷入死循环 | 没有设置最大步数,或工具结果无法帮助模型决策 | 检查每一步 tool result 是否包含关键信息 | 设置 max_steps,并在结果里增加“下一步建议” |
| 返回内容与文件实际内容不一致 | 上下文被截断,或模型过度推理 | 检查传给模型的文件内容是否完整 | 扩大内容截断长度,或增加“只基于事实回答”指令 |
| 路径权限被拒绝 | 路径校验规则过于严格 | 查看错误信息中的路径对比日志 | 调整工作目录白名单 |
| 调用本地模型报错 | 模型服务不支持 Function Calling | 查看模型服务日志 | 换用支持工具调用的模型,例如 qwen2.5 系列 |
| 接口响应超时 | Agent 循环耗时太长 | 为 HTTP 请求配置超时时间 | 使用异步任务 + 轮询或 WebSocket 返回进度 |
这里最容易被忽视的是第一条。很多时候不是代码有问题,而是模型根本不理解工具是干什么的。工具描述必须写得像“给一个新同事的说明书”,而不是一行干巴巴的注释。
8. 从 Demo 到生产的工程建议
8.1 评测先行
Agent 最怕“感觉好用,但说不出哪里好”。一定要为每个任务准备评测集,至少包含:
- 正常输入;
- 边界输入;
- 工具失败输入;
- 恶意输入。
每次改动 Prompt、工具或模型后,都跑一遍评测集,记录成功率和失败原因。没有评测的 Agent 优化,基本等于碰运气。
8.2 工具权限最小化
给 Agent 的工具列表越短越好。只开放完成当前任务必要的能力,不要一上来就给它 Shell、数据库删除权限、支付接口。
工具内部要做二次校验。就像示例里的文件路径白名单一样,看起来多写几行代码,但能挡住大多数误操作。
8.3 状态持久化与恢复
Demo 里 Agent 跑完就结束了,但生产环境任务可能持续几分钟甚至几小时。你需要把任务状态持久化到数据库,包括:
- 当前步骤;
- 已产生的中间结果;
- 已经调用过的工具;
- 剩余步骤数。
否则服务一重启,任务就丢了。这个模块通常被称为“任务状态表”,是 Agent 工程化和玩具 Demo 的分水岭。
8.4 可观测性
生产环境的 Agent 必须可观测。建议把每一步的关键信息输出到日志:
- 当前任务 ID;
- 模型输入输出 token 数;
- 工具名称与参数;
- 工具返回结果;
- 耗时;
- 错误信息。
有了这些日志,你才能回答“它刚才是怎么想的”“它为什么调用了这个工具”。
8.5 成本控制
Agent 的 token 消耗会比普通对话高出一个量级。每一轮工具调用,都会把工具结果和历史消息重新发送给模型。优化思路包括:
- 控制历史消息长度,只保留与当前任务相关的片段;
- 工具结果设置截断;
- 使用模型缓存功能;
- 设置单任务成本上限。
在示例代码里我已经对文件内容做了截断,这就是一种非常朴素的成本控制。
8.6 人工审批节点
不是所有步骤都适合让 Agent 自动完成。建议把工具分为“自动执行”和“需审批执行”两类。例如:
- 读取文件:自动;
- 执行计算:自动;
- 发送消息:审批;
- 修改数据:审批;
- 删除资源:禁止。
审批节点会让 Agent 看起来没那么“自动”,但换来了安全边界。在真实业务里,安全永远优先于体验。
8.7 输出校验与兜底
模型输出的内容不一定符合要求。生产级 Agent 应该在返回最终结果前增加一个校验环节,例如:
- 是否包含必填字段;
- 是否引用了不存在的文件;
- 是否与工具返回数据矛盾。
校验不通过时,可以重新让模型生成,也可以直接返回“需要人工处理”。不要总指望模型一次做对。
8.8 团队协作与代码组织
Agent 项目的代码组织,不应该因为“它是 AI 项目”就特殊化。工具模块、提示词模块、状态管理模块、API 层,都要像普通后端服务一样分层管理。
提示词建议单独抽成文件或配置中心,而不是散落在代码里。这样产品同学可以调,开发同学可以追踪版本,模型升级时也能快速对比效果。
9. 总结与下一步实践
回到标题说的“回到原点”。Manus 和林俊旸这两个名字,被放在一起讨论时,人们看到的往往是光环、争议和情绪。但作为开发者,我更愿意把这个话题理解成一次提醒:Agent 的价值不在于演示时的那几分钟,而在于它能否在真实业务场景里稳定、安全、低成本地完成任务。
这篇文章里,我们用不到两百行代码实现了一个最小 Agent 服务。它没有复杂框架,也没有分布式架构,但它把 Agent 最核心的执行链路完整跑通了:模型决策、工具调用、结果回传、循环终止。我的建议是,你先不要急着给项目引入重型框架,而是把这段代码在本地跑通,仔细观察模型每一步的返回,尤其是 tool_calls 的结构。当你亲手处理过一次 JSON 解析错误、看过一次模型不调用工具的死循环,你对 Agent 的理解会立刻不一样。
下一步可以往这几个方向深入:给 Agent 增加持久化存储,让它支持长时间任务;引入人工审批节点,让写操作先经过管理员确认;建立评测集和回归测试,每次修改之后都用数据说话;尝试接入不同的本地模型,对比它们在工具调用上的表现差异。
Agent 的原点,从来不是某一个模型或某一个产品,而是工程。谁能把不确定性控制住,谁就能把 Demo 变成真正的生产力。