☰
Agent-Reach实战:用Python和CLI让AI Agent真正触达外部世界
2026/10/6 9:46:05 网站建设 项目流程

1. 从"Agent-Reach"这个名字说起:它到底想解决什么问题

第一次看到"Agent-Reach"这个项目名,我的直觉是:这大概率是一个让 AI Agent 具备"触达能力"的工具。Reach 这个词在工程语境里通常有两层含义,一层是"触达外部资源",另一层是"扩展作用半径"。结合关键词里的 CLI、AI Agent、Python,基本可以判断这是一个用命令行驱动、让 Agent 能够真正去操作外部系统的项目,而不是又一个停留在对话框里聊天的玩具。

我接触过不少号称"AI Agent 框架"的东西,绝大多数最后都卡在同一个地方:模型能想,但手伸不出去。它能告诉你"你应该去查一下数据库里昨天的订单",但它自己查不了;它能规划出"先登录后台,再导出报表,再发邮件"的流程,但每一步都要人来点。Agent-Reach 这类项目的价值,恰恰在于把"想"和"做"之间那根断掉的线接上,让 Agent 通过 CLI 这个最朴素也最通用的接口,去触达文件系统、数据库、第三方服务、甚至浏览器。

这篇文章适合三类人看。第一类是已经在用 Python 写 Agent、但苦于工具调用层太薄的开发者;第二类是刚入门 AI Agent、想知道"一个能干活儿的 Agent 到底长什么样"的学习者;第三类是做自动化、RPA、运维脚本,想看看 AI 能不能接管一部分重复劳动的工程师。我会围绕 Agent-Reach 这个核心,把 CLI 与 Agent 的结合方式、Python 侧的落地细节、并发与稳定性、以及实际搭建中那些文档里不会写的坑,全部摊开讲一遍。

需要先说明的是,由于项目正文和关键词字段为空,以下关于 Agent-Reach 的具体实现细节,是我基于"一个以 CLI 为核心触达手段的 Python AI Agent 项目"这一合理推断,结合当前主流 Agent 架构的常见实践进行的补全。凡是我推断的部分,我都会明确标注出来,你可以对照真实项目做校正。

2. 为什么 CLI 是 Agent 触达外部世界的最优解之一

2.1 CLI 的本质:一个稳定到几乎不会变的契约

很多人一提到让 Agent 操作外部系统,第一反应是接 API。API 当然好,结构化、有文档、返回 JSON。但现实是,你手边大量的系统根本没有像样的 API,或者 API 要申请权限、要走审批、要付费。这时候 CLI 就成了那个"永远都在"的兜底方案。

CLI 的本质是什么?是标准输入、标准输出、退出码这三样东西构成的一个极简契约。一个命令git status,你给它一个工作目录,它给你一段文本和一个退出码。这个契约几十年没变过,未来大概率也不会变。对 Agent 来说,这意味着它不需要理解每个系统的内部实现,只需要知道"执行什么命令、怎么解析输出、退出码代表成功还是失败"。

我打个比方。API 像是去一家正规餐厅点菜,菜单清晰、服务规范,但你没预约就进不去。CLI 像是自家厨房,锅碗瓢盆都在那儿,你想炒什么自己动手,虽然要自己洗菜切菜,但门槛低、随时可用。Agent-Reach 选择 CLI 作为核心触达手段,本质上是在赌"通用性"而不是"精致度",这个取舍在工程上是成立的。

2.2 Agent 调用 CLI 的三种典型模式

在实际项目里,Agent 和 CLI 的结合方式我见过三种,各有适用场景。

第一种是固定命令模板。开发者预先写好一批命令模板,Agent 只负责填参数。比如python export_report.py --date {date} --type {type},Agent 的任务就是把{date}和{type}填对。这种方式最安全,因为命令结构是锁死的,Agent 没有发挥空间,也就没有闯祸空间。适合生产环境里那些"绝对不能出错"的操作。

第二种是命令生成。Agent 根据任务描述,自己拼出一条命令。比如用户说"把 logs 目录下三天前的日志删掉",Agent 生成find ./logs -mtime +3 -delete。这种方式灵活,但风险高,因为 Agent 可能生成一条rm -rf /级别的命令。所以必须配合白名单和沙箱。

第三种是交互式会话。Agent 启动一个长驻的 CLI 进程,通过 stdin/stdout 持续对话。比如连上一个数据库客户端,Agent 一条条发 SQL,读回结果。这种方式适合需要保持会话状态的场景,但实现复杂度最高,要处理缓冲、超时、提示符识别等问题。

Agent-Reach 如果要做成一个通用项目,我判断它大概率会同时支持前两种,第三种作为进阶能力。下面这张表可以帮你快速判断该用哪种:

模式适用场景安全等级实现难度
固定命令模板生产环境、高频重复任务高低
命令生成探索性任务、一次性操作中中
交互式会话数据库、REPL、长事务低高

2.3 一个容易被忽略的点:退出码比输出更重要

新手写 Agent 调用 CLI 时,最容易犯的错是只看 stdout,不看退出码。我踩过这个坑:一个脚本执行失败了,但它把错误信息打到了 stdout 而不是 stderr,退出码是 0,Agent 就以为成功了,继续往下走,结果后面全乱套。

正确的做法是,把退出码作为第一判据。退出码非 0,直接判定失败,把 stderr 内容作为错误上下文喂回给模型,让它决定是重试、换命令还是放弃。stdout 只在退出码为 0 时才去解析。这个顺序不能反。

提示:有些 CLI 工具在部分失败时也返回 0,比如批量处理里有一条失败但整体成功。这种情况需要在命令层面加--fail-fast之类的参数,或者让 Agent 去解析输出里的失败计数。不要假设退出码 0 就等于全部成功。

3. 用 Python 把 Agent 和 CLI 缝起来:核心代码结构

3.1 环境准备:别在 Python 版本上栽跟头

Python 这块,我强烈建议用 3.10 以上。原因很实际:3.10 引入了结构化模式匹配(match-case),写命令解析逻辑时清爽很多;而且现在主流的 Agent 相关库,对新版本 Python 的支持都更好。如果你还在 3.7、3.8 上挣扎,很多库装都装不上。

安装方式上,我个人的习惯是用虚拟环境隔离,不要往系统 Python 里塞东西。命令很简单:

python -m venv agent-reach-env source agent-reach-env/bin/activate # Windows 用 agent-reach-env\Scripts\activate pip install --upgrade pip

然后装依赖。一个典型的 Agent-Reach 类项目,核心依赖大概包括:subprocess(标准库,执行命令用)、shlex(标准库,安全拆分命令)、pydantic(做参数校验)、以及一个 LLM 客户端库。如果你要用异步并发,还得上asyncio和aiofiles。

这里有个细节值得说:subprocess是标准库,不用装,但很多人不知道它有个shell=False的默认行为。当你传一个列表["ls", "-l"]时,它是安全的;当你传一个字符串"ls -l"并设shell=True时,就等于把命令交给 shell 解释,注入风险陡增。Agent 生成的命令,永远用列表形式传,永远不要shell=True。

3.2 命令执行器的封装:把危险关进笼子

我写这类项目时,第一件事是封装一个命令执行器,把所有危险操作挡在外面。核心思路是三层过滤:白名单、参数校验、超时控制。

白名单就是只允许执行预先登记的命令。比如你只允许git、ls、cat、python这几个,那 Agent 就算生成了curl也执行不了。参数校验是检查参数里有没有危险字符,比如;、|、&&、$()这些 shell 元字符。超时控制是给每个命令设一个最大执行时间,防止 Agent 执行了一个卡死的命令把整个流程拖垮。

import subprocess import shlex ALLOWED_COMMANDS = {"git", "ls", "cat", "python", "grep"} DANGEROUS_CHARS = set(";|&$`><\n") def safe_execute(command_list, timeout=30, cwd=None): if not command_list: return {"ok": False, "error": "空命令"} base = command_list[0] if base not in ALLOWED_COMMANDS: return {"ok": False, "error": f"命令 {base} 不在白名单内"} for arg in command_list: if any(ch in DANGEROUS_CHARS for ch in arg): return {"ok": False, "error": f"参数含危险字符: {arg}"} try: result = subprocess.run( command_list, capture_output=True, text=True, timeout=timeout, cwd=cwd, shell=False, ) return { "ok": result.returncode == 0, "stdout": result.stdout, "stderr": result.stderr, "code": result.returncode, } except subprocess.TimeoutExpired: return {"ok": False, "error": "命令执行超时"} except Exception as e: return {"ok": False, "error": str(e)}

这段代码看着简单,但每一行都有讲究。capture_output=True把 stdout 和 stderr 分开捕获,方便后续判断。text=True让返回的是字符串而不是字节,省去解码的麻烦。shell=False是安全底线。超时用TimeoutExpired单独捕获,因为这是最常见的异常。

注意:白名单机制有个坑,就是命令的路径问题。如果 Agent 生成的是/usr/bin/git,你的白名单里只有git,就会误判。解决办法是取os.path.basename(command_list[0])再比对,或者干脆在环境变量里把 PATH 收紧。

3.3 把执行结果喂回模型:上下文怎么组织

命令执行完了,结果怎么给模型看,这一步直接决定 Agent 的后续决策质量。我的经验是,不要一股脑把 stdout 全塞进去。一个ls -R在大目录下能吐出几万行,塞进去既浪费 token 又干扰判断。

正确的做法是做截断和摘要。截断是限制返回的最大行数和最大字符数,比如只取前 100 行、前 4000 字符。摘要是在截断的基础上,告诉模型"这里被截断了,总共 N 行,只显示了前 100 行"。这样模型知道信息不完整,需要的话可以再执行更精确的命令。

def format_result_for_llm(result, max_lines=100, max_chars=4000): if not result.get("ok"): return f"命令执行失败:{result.get('error') or result.get('stderr')}" lines = result["stdout"].splitlines() total = len(lines) shown = lines[:max_lines] text = "\n".join(shown) if len(text) > max_chars: text = text[:max_chars] suffix = f"\n[输出共 {total} 行,已截断显示前 {min(total, max_lines)} 行]" if total > max_lines else "" return text + suffix

这个函数里,total和shown的对比是关键。模型看到"共 5000 行,显示前 100 行",它就知道该用grep去过滤,而不是傻乎乎地要求看全部。这其实是在用输出格式引导模型的行为,比在 prompt 里写一堆"请注意输出可能很长"要有效得多。

4. 并发这件事:AI Agent 怎么扛住同时来的多个任务

4.1 先搞清楚瓶颈在哪,别盲目上并发

"AI Agent 怎么扛并发"是个热词,但很多人一上来就想着多线程、多进程、协程全上,结果发现瓶颈根本不在代码,而在模型 API 的速率限制上。所以第一步是定位瓶颈。

Agent 处理一个任务的链路通常是:接收请求 → 调用模型做规划 → 执行 CLI 命令 → 把结果喂回模型 → 再规划 → 直到完成。这条链路里,模型调用是网络 IO,CLI 执行是本地或远程 IO,两者都是 IO 密集型。IO 密集型任务,用asyncio协程是最合适的,不需要多进程。

但如果你用的是同步的模型客户端,那协程也救不了你,因为同步调用会阻塞事件循环。这时候要么换成异步客户端,要么用线程池把同步调用包起来。我一般优先找异步客户端,实在没有再上run_in_executor。

4.2 用 asyncio 编排多个 Agent 任务

下面是一个简化的并发编排示例。核心是asyncio.gather加上信号量控制并发数,避免一次性打爆模型 API。

import asyncio async def run_agent_task(task, semaphore): async with semaphore: # 这里放你的 Agent 主循环:规划 -> 执行 -> 反馈 result = await agent_loop(task) return result async def main(tasks, max_concurrency=5): semaphore = asyncio.Semaphore(max_concurrency) coros = [run_agent_task(t, semaphore) for t in tasks] results = await asyncio.gather(*coros, return_exceptions=True) return results

Semaphore是这里的灵魂。它保证同时最多只有max_concurrency个任务在跑,其余的排队等待。这个数字怎么定?我的经验是,先看模型 API 的 RPM(每分钟请求数)限制,假设一个任务平均要调 5 次模型,那并发数大概就是 RPM 除以 5 再打个七折。比如 RPM 是 60,那并发数设 8 左右比较稳。

4.3 CLI 执行的并发陷阱:共享状态和资源竞争

并发执行 CLI 命令时,最容易出问题的是共享状态。比如两个 Agent 任务同时往同一个文件写,或者同时操作同一个 git 仓库,就会冲突。这类问题不会报错,但结果会莫名其妙地错乱,排查起来极其痛苦。

我的做法是给每个任务分配独立的工作目录。任务开始时创建一个临时目录,所有文件操作都在里面做,任务结束再决定要不要合并回主目录。这样任务之间天然隔离,不会互相踩脚。

import tempfile import os def create_task_workspace(task_id): base = os.path.join(tempfile.gettempdir(), "agent-reach", task_id) os.makedirs(base, exist_ok=True) return base

对于必须共享的资源,比如一个数据库连接,那就得加锁。asyncio.Lock可以解决协程间的互斥,但如果跨进程,就得上文件锁或者数据库层面的锁。这块没有银弹,只能具体问题具体分析。

提示:并发数不是越高越好。我实测过一个场景,并发从 5 提到 20,吞吐量只涨了不到一倍,但错误率翻了三倍。原因是模型 API 开始限流,大量请求返回 429,重试又加剧了拥堵。找到那个"甜点"并发数,比一味堆高更有价值。

5. 从零搭一个能干活儿的 Agent:完整流程拆解

5.1 定义工具集:Agent 的手到底有几只

搭 Agent 的第一步不是写代码,是想清楚"这个 Agent 能做什么"。这就是工具集的定义。工具集不是越多越好,每多一个工具,模型的选择难度就增加一分,出错概率也上升。

我的经验是,一个垂直场景的 Agent,工具控制在 5 到 10 个之间最舒服。比如一个"代码仓库助手"Agent,工具可以是:列目录、读文件、搜索内容、执行 git 命令、运行测试、查看 diff。这六个工具覆盖了日常绝大部分操作,模型也容易记住。

每个工具的定义要包含三部分:名字、描述、参数 schema。描述尤其重要,它是模型判断"什么时候该用这个工具"的唯一依据。描述要写清楚"做什么"和"什么时候用",而不是只写"做什么"。比如"读取文件内容"就不如"读取指定文件的完整内容,当你需要查看某个文件的具体实现时使用"。

5.2 主循环:规划、执行、观察、再规划

Agent 的主循环,说白了就是一个 while 循环,直到任务完成或达到最大步数。每一轮做四件事:把当前状态给模型、模型输出下一步动作、执行动作、把结果加入状态。

async def agent_loop(task, max_steps=15): history = [{"role": "user", "content": task}] for step in range(max_steps): response = await call_llm(history, tools=TOOLS) if response.is_final: return response.content action = response.tool_call result = await execute_tool(action) history.append({"role": "assistant", "content": response.raw}) history.append({"role": "tool", "content": format_result_for_llm(result)}) return "达到最大步数,任务未完成"

max_steps是必须的保险丝。没有它,Agent 可能陷入死循环,反复执行同一个失败的命令,烧掉大量 token。15 步对大多数任务够用,复杂任务可以放宽到 30,但一定要有上限。

5.3 状态管理:别让上下文无限膨胀

主循环跑着跑着,history会越来越长,最后超出模型的上下文窗口。这时候要么报错,要么模型开始"遗忘"早期信息。解决办法是上下文压缩。

压缩的策略有几种。最简单的是滑动窗口,只保留最近 N 轮对话。但这样会丢掉早期的关键信息,比如任务目标。好一点的做法是保留第一条用户消息(任务目标)加上最近 N 轮,中间的老消息做摘要。摘要可以用模型生成,也可以简单地只保留工具调用的关键结果。

def compress_history(history, keep_recent=6): if len(history) <= keep_recent + 1: return history first = history[0] recent = history[-keep_recent:] summary = {"role": "system", "content": "早期对话已省略,任务目标保持不变。"} return [first, summary] + recent

这个函数很粗糙,但能解决 80% 的问题。真正生产环境里,摘要内容应该由模型生成,把早期执行过的关键动作和结果浓缩成几句话。

6. 实测中那些文档不会告诉你的坑

6.1 命令输出里的 ANSI 转义码

这个坑我踩得最惨。很多 CLI 工具默认输出带颜色,那些颜色是用 ANSI 转义码实现的,比如\x1b[32m。这些码在终端里显示为颜色,但被 Agent 读到就是一堆乱码,模型看了半天不知道是什么,决策质量直线下降。

解决办法有两个。一是给命令加--no-color或--color=never参数,大部分正规 CLI 都支持。二是拿到输出后做一次清洗,用正则把 ANSI 码去掉。

import re ANSI_PATTERN = re.compile(r"\x1b\[[0-9;]*[a-zA-Z]") def strip_ansi(text): return ANSI_PATTERN.sub("", text)

清洗这一步建议放在format_result_for_llm里,所有输出统一过一遍,省得每个工具单独处理。

6.2 交互式命令的"假死"

有些命令会等待用户输入,比如git commit不带-m会打开编辑器,mysql不带参数会进入交互模式。Agent 执行这类命令时,进程会一直挂着,直到超时才被杀掉。这期间它占着资源,还让 Agent 以为任务在进行中。

预防的办法是给命令加非交互参数。git commit -m "msg"、mysql -e "SQL"、apt-get -y install,这些都是标准做法。如果某个命令实在没有非交互模式,那就得用pexpect之类的库去模拟输入,或者干脆把它排除在工具集之外。

注意:超时时间不要设太长。我见过有人设 300 秒,结果一个卡死的命令让整个任务等了五分钟。一般命令 30 秒足够,编译、安装这类重操作可以放宽到 120 秒,但要有明确的上限。

6.3 模型"幻觉"出一条不存在的命令

这是最隐蔽的坑。模型可能生成一条看起来很像那么回事、但实际不存在的命令,比如把git log --oneline记成git log --short。执行后返回"command not found",如果 Agent 不把这个当回事,继续往下走,后面就全错了。

应对策略是,把"命令不存在"和"命令执行失败"区分对待。命令不存在(退出码 127)通常意味着模型记错了,应该把错误信息明确反馈给模型,提示它"这个命令不存在,请检查拼写或换一个命令"。而命令执行失败(其他非零退出码)可能是业务逻辑问题,处理方式不同。

def classify_error(result): code = result.get("code") if code == 127: return "命令不存在,请检查命令名是否正确" if code == 126: return "命令无执行权限" if code == 1: return "命令执行失败,通常是业务逻辑错误" return "未知错误"

这个分类能让模型更快定位问题,而不是笼统地看到"失败了"就瞎猜。

6.4 路径问题:相对路径和绝对路径的坑

Agent 执行命令时,工作目录(cwd)是个容易被忽略的变量。如果 Agent 以为自己在项目根目录,实际却在别的地方,那所有相对路径都会错。我的做法是,所有命令都显式指定cwd,并且在工具描述里告诉模型"当前工作目录是 X"。这样模型生成路径时心里有数。

另外,Agent 生成的路径里如果带~,subprocess是不会自动展开的,得用os.path.expanduser处理。带空格的文件名要正确加引号,但因为我们用列表传参,其实不需要引号,反而是加了引号会被当成文件名的一部分。这些细节不注意,就会出各种"文件找不到"的怪问题。

7. 这套东西能用在哪些真实场景

7.1 自动化运维:让 Agent 接管日常巡检

运维场景是 Agent-Reach 这类项目最直接的用武之地。日常巡检无非就是查磁盘、查内存、查日志、查进程,这些全是 CLI 命令。把这些命令封装成工具,Agent 就能自己跑一遍巡检,发现异常时主动报告,甚至尝试初步修复。

我做过一个类似的:Agent 每天定时检查日志目录,发现错误日志超过阈值就自动 grep 出关键行,分析可能的原因,然后发通知。整个过程不需要人盯着,比写死脚本灵活得多,因为 Agent 能根据日志内容动态决定下一步查什么。

7.2 数据处理流水线:把零散脚本串起来

数据团队手里往往有一堆零散脚本,每个脚本干一件事,靠人工按顺序执行。Agent 可以充当这个"调度员"。你告诉它"把昨天的数据跑一遍",它自己规划:先拉数据、再清洗、再入库、再出报表,每一步调用对应的脚本,中间出错就停下来报告。

这种场景下,固定命令模板模式最合适。因为流程是确定的,Agent 的价值在于处理异常——某个脚本失败了,它能看错误信息,判断是重试还是跳过还是报警。

7.3 代码仓库助手:让 Agent 帮你做重复的代码操作

批量重命名、批量改配置、批量加注释,这些操作在 CLI 下用sed、grep、git组合就能完成,但写起来费劲。Agent 可以理解你的自然语言描述,生成对应的命令组合,执行后把 diff 给你看,你确认了再提交。

这个场景的关键是"人在回路"。Agent 生成命令后不直接执行,而是先展示,等人确认。这样既享受了 Agent 的便利,又避免了它闯祸。实现上就是在执行工具前加一个确认环节。

7.4 学习辅助:让 Agent 带你熟悉陌生工具

这个用法比较特别。当你面对一个不熟悉的 CLI 工具时,可以让 Agent 帮你探索。你说"我想知道这个工具怎么用",Agent 执行tool --help,读输出,然后给你解释。你说"试试某个功能",Agent 执行并展示结果。这比你自己翻文档快得多,因为 Agent 能根据你的具体需求动态调整。

8. 性能与稳定性:让 Agent 跑得久、不出事

8.1 重试策略:不是所有失败都值得重试

Agent 执行命令失败时,要不要重试?我的原则是:幂等操作可以重试,非幂等操作谨慎重试。查询类命令(ls、cat、grep)失败了重试没问题,因为它们不改变状态。但写操作(rm、mv、git push)失败了重试可能造成重复操作,得小心。

重试还要区分错误类型。网络超时可以重试,命令不存在重试一百次也没用。我一般给重试加两个条件:错误类型可重试,且重试次数没超上限。上限设 3 次比较合理,再多就是浪费。

RETRYABLE_ERRORS = {"超时", "连接失败", "暂时不可用"} def should_retry(error_msg, attempt, max_attempts=3): if attempt >= max_attempts: return False return any(kw in error_msg for kw in RETRYABLE_ERRORS)

8.2 日志与可观测性:出问题时你能查到什么

Agent 系统最怕的是"黑盒"。任务失败了,你不知道它中间做了什么,只能干瞪眼。所以日志必须做扎实。我的做法是,每一次模型调用、每一次命令执行、每一次状态变更,都记一条结构化日志,包含时间戳、任务 ID、步骤序号、动作类型、输入、输出、耗时。

import json import time def log_step(task_id, step, action, payload, result, duration): entry = { "ts": time.time(), "task_id": task_id, "step": step, "action": action, "payload": payload, "result_summary": str(result)[:500], "duration_ms": int(duration * 1000), } with open("agent_reach.log", "a", encoding="utf-8") as f: f.write(json.dumps(entry, ensure_ascii=False) + "\n")

有了这些日志,出问题时可以完整回放一个任务的执行过程,定位到具体哪一步出的错。这比在代码里到处打 print 强太多。

8.3 资源限制:别让一个任务拖垮整个系统

Agent 执行命令可能消耗大量资源,比如一个find /能跑很久,一个编译能占满 CPU。如果不加限制,一个失控的任务就能把整个系统拖垮。所以必须设资源上限:CPU 时间、内存、磁盘写入、执行时长,都要有上限。

在 Linux 下可以用resource模块给子进程设限制,或者用ulimit。更简单粗暴的办法是给每个任务设一个总时长上限,超了就整个任务终止。这个上限根据任务类型定,巡检类 5 分钟,编译类 30 分钟,别一刀切。

9. 我个人的一些实操体会

搭这类 Agent 系统,我最大的体会是:模型的能力不是瓶颈,工程细节才是。模型再聪明,如果命令执行器有注入漏洞,如果输出清洗没做好,如果并发控制不当,整个系统就是不可用的。反过来,一个中等能力的模型,配上扎实的工程封装,能稳定跑出很好的效果。

另一个体会是,工具的描述比工具本身重要。我花在写工具描述上的时间,往往比写工具实现还多。因为模型是靠着描述来决定用哪个工具的,描述写得含糊,模型就选错工具,后面全乱。把每个工具描述当成给一个新同事写说明书,写清楚"什么时候用、怎么用、有什么坑",效果立竿见影。

最后分享一个小技巧:给 Agent 加一个"自言自语"的环节。在执行每个工具前,让它先用一句话说明"我为什么要执行这个命令"。这句话不产生实际动作,但能让你在日志里看到它的推理过程,出问题时一眼就能看出它是在哪一步想歪的。这个成本极低,收益极高,强烈建议加上。

这套东西后续还能往几个方向扩展。一是加一个工具市场,让不同项目共享工具定义;二是把执行环境容器化,每个任务跑在独立容器里,隔离性更好;三是加一个"经验库",把成功执行过的命令模式存下来,下次遇到类似任务直接复用,减少模型调用次数。这些方向我都在陆续尝试,有新的心得再分享。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询