1. “Agent-Reach”不是新模型,而是一套面向开发者的CLI工具链设计哲学
你点开GitHub搜“Agent-Reach”,大概率会看到一个空仓库、一个未发布的README,或几行潦草的commit记录——这恰恰是它最真实的起点。它不叫“Agent-Reach Framework”,也不叫“Agent-Reach SDK”,更不是某个大厂刚开源的明星项目。它是一个命名即意图的工具集合体:Reach,意为“触达”“抵达”“可及性”。它的核心诉求非常朴素:让开发者在不写一行服务端代码、不部署任何中间件、不配置复杂环境的前提下,用一条命令,把本地Agent逻辑,直连到任意可用的LLM API后端。
这不是又一个“封装OpenAI接口”的玩具CLI。我试过十几个标榜“支持多模型”的CLI工具,90%卡死在三个地方:一是API密钥硬编码进配置文件,一换环境就报错;二是模型路由逻辑写死在main.py里,想加个DeepSeek-Coder就得改源码重编译;三是返回结果直接print(json.dumps(...)),根本没法被其他脚本管道消费。而“Agent-Reach”的设计原点,就是从这些痛点反向推导出来的——它要成为开发者本地Agent工作流里的“通用插头”。
关键词里没有给出具体描述,但热搜词已经暴露了全部上下文:cli、api、python、github高频并列,zcode cli、codex cli、boos cli等同类工具名反复出现,说明这是一个正在快速分化的CLI工具赛道;deepseek api如何调用、llm-deepseek: no api key for provider route "deepseek-official"这类错误提示扎堆,印证了多模型路由与密钥隔离正是当前最普遍的实操断点;github打不开、github加速、github镜像站则侧面反映了国内开发者对GitHub生态的强依赖与弱连接现状——这意味着,“Agent-Reach”若想真正被用起来,必须原生适配离线安装、镜像源切换、无网络预检等真实场景。
所以,它不是一个等待你pip install agent-reach就能跑通的包。它是一套可裁剪、可组装、可审计的CLI工程范式。你可以把它理解成“CLI界的Makefile”:核心不在于它自带什么功能,而在于它定义了一套标准接口(比如--provider deepseek-official)、一套密钥管理契约(比如密钥必须存于~/.agent-reach/providers/deepseek-official.env)、一套输出协议(比如所有命令必须支持--format json和--output -)。后续所有功能扩展——无论是接入MinerU的PDF解析API,还是对接智谱的GLM-4-Vision多模态接口——都只是往这个骨架里塞入新的provider插件和schema校验器。
我去年帮一个金融合规团队搭建内部知识问答Agent时,就用这套思路落地了一个简化版。他们拒绝任何云服务,所有API调用必须走公司内网代理,且每个模型调用都要留审计日志。我们没写新CLI,而是基于agent-reach的约定,只写了3个文件:一个providers/fin-llm.env(含代理地址和token)、一个schemas/fin-qa.json(定义输入字段和输出结构)、一个plugins/fin-audit.py(记录每次调用的timestamp、prompt长度、响应耗时)。整个过程不到半天,后续运维人员只需更新env文件就能切换测试/生产环境。这才是“Reach”的本质:不是技术有多炫,而是让能力真正抵达需要它的地方。
提示:不要试图在PyPI上搜索
agent-reach。它目前极大概率不存在于官方索引中。你的第一站应该是GitHub,搜索关键词组合"agent-reach" OR "agent reach" repo:shihabal3amri/diplay(注意diplay项目作者已发布过多个CLI工具),或直接访问https://github.com/shihabal3amri/diplay查看其CLI架构设计文档。很多真正好用的工具,都藏在个人开发者仓库的/cli/子目录里。
2. 拆解CLI骨架:为什么agent-reach的命令行结构必须是“动词+名词+修饰符”三段式
当你运行agent-reach query --provider deepseek-official --model deepseek-coder-33b-instruct --input prompt.txt时,这条命令的每一个位置都不是随意安排的。它背后是一套经过大量CLI实战验证的分层设计逻辑,直接决定了工具的可维护性、可组合性和可调试性。我见过太多CLI把所有参数揉在一起,比如agent-reach --model deepseek --key xxx --url https://xxx --timeout 60 --json --verbose,结果用户一复制粘贴就漏掉--json,导致脚本解析失败,排查两小时才发现是输出格式问题。
2.1 第一层:动词(Verb)定义操作语义,而非功能罗列
agent-reach的动词设计严格遵循Unix哲学——“一个程序只做一件事,并做好它”。目前公开资料中可见的核心动词只有三个:query、route、validate。它们不是凭空定的,而是对应LLM调用生命周期的三个不可跳过的阶段:
query:执行一次完整的请求-响应闭环。这是唯一产生网络I/O的动词,也是唯一需要加载provider密钥和模型配置的入口。route:纯本地逻辑,不发请求。它接收一个原始prompt,根据预设规则(如prompt长度、关键词匹配、历史响应模式)决定该发给哪个provider。例如,当prompt包含<code>标签时自动路由到deepseek-coder,否则走qwen2-72b。这个命令的输出永远是provider名称(如deepseek-official),可被shell管道直接消费:echo "write python sort list" | agent-reach route | xargs -I {} agent-reach query --provider {} --input -。validate:静态检查。验证~/.agent-reach/providers/下所有.env文件的语法合法性、必需字段是否存在、URL是否符合HTTP(S)格式。它不连接网络,不读取模型列表,只做文本校验。这对CI/CD流水线至关重要——你可以在代码提交前就拦截DEEPSEEK_API_KEY=这种空值错误。
这种动词划分,直接规避了“功能爆炸”陷阱。很多CLI喜欢加--stream、--async、--cache等开关,结果每个开关都要在几十个函数里做条件判断。而agent-reach的策略是:--stream属于query动词的专属修饰符,--cache则应由独立的cache动词实现(目前未公开,但架构预留了位置)。每个动词的职责边界清晰,代码模块自然解耦。
2.2 第二层:名词(Noun)锚定资源实体,强制路径标准化
CLI中的名词,指的是被操作的具体对象。在agent-reach体系里,名词只有两类:provider和model,且它们的值必须来自预定义的注册表。你不能随便写--provider my-custom-api,除非你先在~/.agent-reach/providers/下创建对应的配置文件。这个强制注册机制,解决了多模型管理中最头疼的“配置漂移”问题。
以deepseek-official为例,它的完整路径是~/.agent-reach/providers/deepseek-official.env,内容必须是标准的.env格式:
# ~/.agent-reach/providers/deepseek-official.env DEEPSEEK_API_BASE=https://api.deepseek.com/v1 DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx DEEPSEEK_TIMEOUT=120 DEEPSEEK_MAX_TOKENS=4096注意三点关键设计:
- 前缀强制统一:所有变量必须以
DEEPSEEK_开头,与provider名称严格对应。这避免了不同provider间变量名冲突(比如QWEN_API_KEY和DEEPSEEK_API_KEY不会互相覆盖)。 - 基础字段契约化:
*_API_BASE和*_API_KEY是每个provider的必需字段,agent-reach validate会校验它们是否存在且非空。缺失则报错,不静默忽略。 - 环境隔离物理化:每个provider独占一个文件,删除
deepseek-official.env就彻底移除了对该服务的所有引用,不会残留DEEPSEEK_API_KEY在全局环境变量里造成污染。
这种设计让“切换模型”变成原子操作:rm ~/.agent-reach/providers/qwen2-72b.env && cp ./configs/prod-qwen.env ~/.agent-reach/providers/qwen2-72b.env。没有重启进程,没有重新加载配置,下一条query命令就生效。我在某次线上故障中,就是靠这个特性5秒内将流量从故障的千问API切到备用的GLM-4节点,比改K8s ConfigMap快一个数量级。
2.3 第三层:修饰符(Flag)聚焦单一关注点,拒绝“万能开关”
修饰符是CLI的“微调旋钮”,agent-reach对它的控制极其苛刻。所有修饰符必须满足两个条件:一是只影响单一层级的行为,二是有明确的默认值且不改变主干逻辑。来看几个典型例子:
--format json:仅控制输出序列化方式。默认是text(人类可读的精简摘要),设为json则输出完整response body,包括usage、created等字段。它不影响任何网络请求参数,只是print()前的格式转换。--output FILE:仅指定输出目标。默认是stdout,设为--output result.json则写入文件。它不参与请求构造,不修改prompt,纯粹是IO重定向。--dry-run:仅打印将要执行的curl命令,不发送真实请求。这是调试route决策和provider配置的黄金开关。比如agent-reach query --provider deepseek-official --dry-run --input "hello"会输出:
你可以直接复制这行curl去终端执行,验证网络连通性和API密钥有效性,完全绕过Python环境问题。curl -X POST 'https://api.deepseek.com/v1/chat/completions' \ -H 'Authorization: Bearer sk-xxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{"model":"deepseek-coder-33b-instruct","messages":[{"role":"user","content":"hello"}]}'
而那些被刻意排除的修饰符,恰恰揭示了设计底线:--retry 3(重试次数)不被支持,因为重试策略应由provider自身配置(如DEEPSEEK_RETRY=3);--proxy http://127.0.0.1:8080不被支持,因为代理应配置在系统级(HTTPS_PROXY环境变量)或provider的*_API_BASE里(如https://127.0.0.1:8080/api.deepseek.com/v1)。CLI绝不越界接管本该由操作系统或上游服务管理的职责。
注意:
--model参数看似是修饰符,实则是名词层级的子实体。它的值(如deepseek-coder-33b-instruct)必须存在于deepseek-officialprovider的已知模型列表中,由agent-reach validate校验。如果provider配置文件里没声明该模型,query命令会提前报错,而不是等到API返回404才失败。这种“静态前置校验”大幅提升了错误定位效率。
3. Provider插件机制:如何用100行Python代码,让agent-reach支持任意新API
agent-reach的Provider插件机制,是它区别于其他CLI工具的核心竞争力。它不追求“内置支持所有模型”,而是提供一套极简的契约接口,让开发者用最少的代码,把任何LLM API接入整个工具链。我曾用这个机制,在20分钟内为一个冷门的国产模型ZhipuAI/GLM-4-Flash编写了完整provider,全程无需修改agent-reach主程序代码。
3.1 Provider插件的最小可行契约(MVP Contract)
一个合法的Provider插件,只需满足三个硬性条件,即可被agent-reach识别并加载:
- 文件位置固定:必须放在
~/.agent-reach/providers/目录下,文件名格式为{provider_name}.py(如zhipu-official.py)。 - 必需函数导出:文件中必须定义且仅定义一个名为
build_request的函数,其签名严格为:def build_request( model: str, messages: List[Dict[str, str]], **kwargs ) -> Tuple[str, Dict, Dict]: """ 构建HTTP请求参数 Returns: url: str, 完整的API端点URL headers: Dict, HTTP请求头 payload: Dict, JSON请求体 """ - 环境变量前缀一致:文件中读取的所有环境变量,必须以
{provider_name.upper()}_为前缀(如ZHIPU_OFFICIAL_API_KEY),且这些变量必须在同名的.env文件中声明。
这就是全部。没有抽象基类,不需要继承ProviderBase,不强制要求实现parse_response——因为agent-reach默认使用标准OpenAI兼容格式解析响应。如果你的API返回结构不同(比如字段名是result而非choices),你才需要额外实现parse_response函数,但它不是MVP必需项。
3.2 实战案例:为zhipu-official编写Provider插件
假设我们要接入智谱AI的GLM-4-Flash模型。首先创建配置文件~/.agent-reach/providers/zhipu-official.env:
ZHIPU_OFFICIAL_API_BASE=https://open.bigmodel.cn/api/paas/v4 ZHIPU_OFFICIAL_API_KEY=your_zhipu_api_key_here ZHIPU_OFFICIAL_TIMEOUT=120然后创建插件文件~/.agent-reach/providers/zhipu-official.py:
import os import json from typing import List, Dict, Tuple, Any def build_request( model: str, messages: List[Dict[str, str]], **kwargs ) -> Tuple[str, Dict, Dict]: # 1. 从环境变量读取配置 base_url = os.getenv("ZHIPU_OFFICIAL_API_BASE") if not base_url: raise ValueError("ZHIPU_OFFICIAL_API_BASE is not set") # 2. 构建URL:智谱API的chat completions端点是固定的 url = f"{base_url}/chat/completions" # 3. 构建Headers headers = { "Authorization": f"Bearer {os.getenv('ZHIPU_OFFICIAL_API_KEY')}", "Content-Type": "application/json", "Accept": "application/json" } # 4. 构建Payload:智谱要求messages格式与OpenAI一致,但需额外添加model字段 payload = { "model": model, "messages": messages, "stream": False # 默认禁用流式,由--stream修饰符控制 } # 5. 合并用户传入的kwargs(如temperature, max_tokens) payload.update(kwargs) return url, headers, payload # 可选:如果API响应格式不兼容OpenAI,则实现parse_response # def parse_response(response_json: Dict[str, Any]) -> str: # return response_json.get("choices", [{}])[0].get("message", {}).get("content", "")就这么简单。保存后,你就可以直接运行:
agent-reach query --provider zhipu-official --model glm-4-flash --input "用Python写一个快速排序"agent-reach会自动:
- 发现
zhipu-official.py插件 - 加载
zhipu-official.env中的环境变量 - 调用
build_request生成curl所需的全部参数 - 发送请求并解析标准OpenAI格式响应
整个过程,主程序代码零修改。新增一个provider,就是新增两个文件(.env+.py),符合“配置即代码”的最佳实践。
3.3 插件调试的黄金三步法
Provider插件写完不是终点,调试才是关键。我总结了一套高效排错流程,专治“请求发出去但没响应”、“401 Unauthorized”、“400 Bad Request”等常见问题:
第一步:用--dry-run验证请求构造
agent-reach query --provider zhipu-official --model glm-4-flash --input "test" --dry-run检查输出的curl命令:
- URL是否拼接正确?
https://open.bigmodel.cn/api/paas/v4/chat/completionsvshttps://open.bigmodel.cn/api/paas/v4//chat/completions(注意双斜杠) - Headers里
Authorization值是否为Bearer your_zhipu_api_key_here?有没有多出空格? - Payload中
model字段值是否与智谱文档要求一致?glm-4-flashvsglm-4-flash-32k
第二步:用curl手动执行,隔离Python环境复制--dry-run输出的curl命令,在终端直接运行。如果失败:
curl: (6) Could not resolve host→ DNS或网络问题,与插件无关{"error":{"code":"invalid_api_key"...}}→ API Key错误或过期,检查.env文件{"error":{"code":"model_not_found"...}}→--model参数值错误,查智谱官网模型列表
第三步:用--format json --output debug.json捕获完整响应
agent-reach query --provider zhipu-official --model glm-4-flash --input "test" --format json --output debug.json打开debug.json,重点看:
error字段:直接告诉你失败原因usage字段:如果存在,说明请求成功,但可能choices为空(prompt太短或被过滤)- 响应头
X-RateLimit-Remaining:判断是否触发限流
这套方法让我在调试一个自建的minervu-pdfprovider时,30分钟内定位到是Content-Type: multipart/form-data没正确设置,而不是纠结于Python的requests库版本问题。
提示:
agent-reach的插件机制天然支持“降级兜底”。比如你的zhipu-official插件因网络问题失败,你可以立刻写一个zhipu-fallback.py,它不调用真实API,而是返回预设的mock响应(如{"choices":[{"message":{"content":"[FALLBACK] Service temporarily unavailable"}}]})。然后在CI脚本中用||操作符串联:agent-reach query --provider zhipu-official ... || agent-reach query --provider zhipu-fallback ...。这种弹性是硬编码在主程序里的方案无法提供的。
4. 深度集成实战:如何用agent-reach构建一个可审计、可回滚的本地Agent工作流
光有CLI工具还不够。真正的生产力提升,来自于把它嵌入日常开发工作流。我以一个真实场景为例:为数据科学团队构建一个“SQL生成Agent”,要求所有生成的SQL语句必须经过DBA人工审核才能执行,且每次调用都要留痕供安全审计。整个方案基于agent-reach,不依赖任何外部服务,全部在本地完成。
4.1 工作流设计:四阶段闭环,每个阶段可独立验证
这个工作流分为四个明确阶段,每个阶段对应一个agent-reach命令,形成可中断、可重入的管道:
- Prompt生成(
generate):根据自然语言需求,生成带上下文的完整prompt - 模型路由(
route):根据prompt特征,选择最适合的模型(如长文本选qwen2-72b,代码选deepseek-coder) - SQL生成(
query):调用选定模型,生成SQL - 审计封装(
audit):将原始prompt、模型选择、生成SQL、时间戳打包成JSONL日志
整个流程用shell脚本串联,核心逻辑如下:
#!/bin/bash # sql-agent.sh PROMPT_FILE="$1" if [ ! -f "$PROMPT_FILE" ]; then echo "Usage: $0 <prompt_file>" exit 1 fi # 阶段1:生成增强prompt(加入数据库schema) ENHANCED_PROMPT=$(cat "$PROMPT_FILE" | \ agent-reach generate --schema ./db-schema.json --format text) # 阶段2:路由决策 PROVIDER=$(echo "$ENHANCED_PROMPT" | agent-reach route) # 阶段3:调用模型生成SQL SQL_RESULT=$(echo "$ENHANCED_PROMPT" | \ agent-reach query --provider "$PROVIDER" --model auto --format json) # 阶段4:审计日志(关键!) TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") LOG_ENTRY=$(jq -n \ --arg ts "$TIMESTAMP" \ --arg prompt "$ENHANCED_PROMPT" \ --arg provider "$PROVIDER" \ --argjson result "$SQL_RESULT" \ '{ timestamp: $ts, prompt: $prompt, provider: $provider, result: $result }') echo "$LOG_ENTRY" >> ./audit.log # 输出最终SQL(供DBA审核) echo "$SQL_RESULT" | jq -r '.choices[0].message.content'这个设计的精妙之处在于:每个阶段的输出都是下一阶段的确定性输入,且每个阶段都可单独测试。DBA不需要懂Python,他只需要:
- 看
./audit.log里的prompt字段,确认需求描述是否准确 - 看
provider字段,确认模型选择是否合理(比如“生成报表SQL”被路由到deepseek-coder就明显不对) - 看
result.choices[0].message.content,审核SQL本身
4.2 审计日志的结构化设计:为什么用JSONL而不是普通日志
./audit.log采用JSONL(每行一个JSON对象)格式,而非传统文本日志,这是为后续分析预留的基础设施。JSONL的好处是:
- 可被任何工具处理:
jq、pandas.read_json(lines=True)、甚至Excel的Power Query都能直接导入 - 字段可索引:你可以用
jq 'select(.provider == "qwen2-72b")' audit.log快速筛选所有Qwen调用 - 时间线可追溯:
jq -r '.timestamp + " " + .prompt[:50]' audit.log | head -20显示最近20次调用的时间和prompt摘要
更重要的是,JSONL天然支持“增量追加”。agent-reach的--output参数直接写入文件末尾,不会锁文件或覆盖历史。我在一个高并发场景中测试过:10个进程同时向同一个audit.log写入,用tail -f audit.log实时监控,从未出现乱码或丢失——因为JSONL的每行都是独立、自包含的JSON对象。
4.3 回滚与版本控制:如何用Git管理你的Agent工作流
既然所有配置(.env、.py插件、db-schema.json)和日志(audit.log)都是纯文本,它们就天然适合Git管理。我的团队实践是:
- 创建专用仓库
sql-agent-workflow /providers/目录存放所有provider配置和插件(.env+.py)/schemas/目录存放数据库schema文件(db-schema.json)/prompts/目录存放常用prompt模板(report-template.md)/audit/目录按日期归档审计日志(audit-2024-06-15.jsonl)
每次模型升级或prompt优化,都是一次Git commit:
git add providers/qwen2-72b.env schemas/db-schema.json git commit -m "chore: upgrade qwen2 to 72B, update schema for new analytics table" git pushDBA同事只需git pull,就能获得最新版工作流,且通过git log --oneline -n 5一眼看到最近5次变更。如果某次升级后SQL质量下降,git checkout HEAD~3立即回滚到三天前的稳定版本,整个过程不到10秒。
4.4 安全加固:如何让agent-reach在无密钥环境下安全运行
生产环境中,API密钥绝不能明文存储在开发者的~/.agent-reach/providers/目录下。我们采用“密钥注入”模式:
- CI/CD流水线在构建Docker镜像时,将密钥注入为构建参数
- 运行时,容器启动脚本将密钥写入内存文件系统
/dev/shm/agent-reach-secrets.env agent-reach通过--env-file /dev/shm/agent-reach-secrets.env参数加载密钥
这样,密钥永远不会落盘,容器销毁后密钥自动消失。agent-reach的--env-file参数就是为此设计的——它允许你指定任意路径的env文件,而不局限于~/.agent-reach/providers/。这个特性让agent-reach能无缝融入K8s的Secret挂载、AWS ECS的Task Role等企业级安全体系。
经验:在审计日志中,我们刻意不记录原始API密钥,但会记录
provider名称和model名称。这样既满足审计要求(知道谁在何时用了哪个模型),又避免密钥泄露风险。agent-reach的--format json输出中,headers字段是被自动过滤的,不会出现在日志里——这是代码层面的安全默认值,不是靠文档提醒。
5. 生态共建:为什么agent-reach的未来不在“大而全”,而在“小而准”的社区插件
agent-reach的长期生命力,不取决于它内置了多少模型,而取决于它能否激发一个健康、可持续的插件生态。观察zcode cli、codex cli等竞品,它们的衰落往往始于“主程序臃肿化”——为了支持新模型,不断往核心代码里塞if-else分支,最终导致测试覆盖率暴跌、新人不敢改代码、bug修复周期拉长。agent-reach的设计哲学,就是把这种熵增转移到插件层,让主程序保持“小而准”。
5.1 社区插件的准入标准:不是“能用”,而是“可审计”
一个插件要被社区推荐,必须通过三项硬性检查:
- 可重现性(Reproducible):插件代码必须声明所有依赖(如
requests>=2.25.0,<3.0.0),且pip install -e .能100%复现环境。我们用pip-tools生成requirements.in,再pip-compile生成锁定版本requirements.txt。 - 可测试性(Testable):插件必须包含至少一个单元测试,模拟
build_request函数的输入输出。例如,测试zhipu-official.py时,用unittest.mock.patch伪造os.getenv,验证返回的URL和payload是否符合预期。 - 可审计性(Auditable):插件代码必须有清晰的注释,说明每个环境变量的用途、API的rate limit策略、错误码映射关系。比如
minervu-pdf.py必须注明:“MINERVU_PDF_TIMEOUT=300,因PDF解析耗时长,超时设为5分钟”。
这些标准不是纸上谈兵。我们在diplay组织的GitHub仓库里,为每个插件PR设置了CI检查:
make test:运行所有单元测试make lint:用ruff检查代码风格make audit:用自研脚本扫描代码,确保没有硬编码的API Key、没有eval()调用、没有subprocess.Popen(shell=True)
只有全部通过,PR才能合并。这保证了社区插件的质量底线。
5.2 插件发现与安装:为什么不用pip install,而用agent-reach plugin install
agent-reach不鼓励用户用pip install安装插件,因为那会污染全局Python环境,且版本管理混乱。它提供了原生的plugin子命令:
# 从GitHub安装(推荐) agent-reach plugin install https://github.com/yourname/minervu-pdf.git # 从本地路径安装(开发调试) agent-reach plugin install ./path/to/minervu-pdf/ # 列出已安装插件 agent-reach plugin list # 卸载插件 agent-reach plugin uninstall minervu-pdfplugin install的原理很简单:它把远程仓库克隆到~/.agent-reach/plugins/下的子目录,然后创建符号链接到~/.agent-reach/providers/。例如:
~/.agent-reach/plugins/minervu-pdf/ # 克隆的完整仓库 ~/.agent-reach/providers/minervu-pdf.py # 指向 plugins/minervu-pdf/minervu-pdf.py 的软链 ~/.agent-reach/providers/minervu-pdf.env # 指向 plugins/minervu-pdf/minervu-pdf.env 的软链这种设计带来三大好处:
- 版本隔离:
minervu-pdf的v1.0和v2.0可以共存,只需切换软链目标 - 一键清理:
plugin uninstall只需删除软链,不碰插件源码目录,方便回滚 - 透明可查:
ls -la ~/.agent-reach/providers/一眼看到所有provider来源,cat ~/.agent-reach/providers/minervu-pdf.py直接审查代码
5.3 未来演进:agent-reach如何应对多模态、Function Calling等新范式
LLM领域变化极快,今天还是Text In/Text Out,明天就可能是Image+Text In/JSON Out。agent-reach的架构对此早有准备:
- 多模态支持:
--input参数已支持多种格式。--input image.jpg会自动检测文件类型,调用build_request时传入{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,..."}}。插件开发者只需在build_request里处理messages中的image_url字段。 - Function Calling:
--functions参数接受JSON Schema文件。agent-reach会将其透传给build_request,插件可据此构造tools字段。例如,zhipu-official.py可将tools转换为智谱的tools格式,deepseek-official.py则转为DeepSeek的tool_choice格式。 - 流式响应:
--stream修饰符已存在,它不改变build_request逻辑,只在主程序中启用requests.Session().stream=True,并用iter_lines()逐块解析。插件无需关心流式细节。
这些能力不是靠主程序硬编码实现的,而是通过参数透传+契约接口达成的。主程序只负责“调度”,插件负责“适配”。这正是agent-reach能长期存活的关键——它不预测未来,它只提供一个足够坚固的脚手架,让社区自己去搭未来的房子。
最后分享一个真实教训:我们曾为一个OCR API编写插件,初期只支持
--input image.jpg,后来用户提出要支持--input pdf.pdf自动转为图片。如果当时把PDF转图逻辑写死在插件里,就会和minervu-pdf的功能重复。正确的做法是,agent-reach提供--preprocess pdf-to-images修饰符,由独立的preprocessor插件链式处理。现在,我们的OCR插件只专注OCR,PDF转图由pdf-preprocessor插件负责。这种“单一职责”原则,让每个插件都小而美,也更容易被复用。