大模型应用做多了,慢慢会发现一个尴尬的事实:模型脑子里装的东西再多,也就是个"会说话的脑袋"。你问它什么,它能答得头头是道;但你要它自己去截图看一眼报错、查一下业务数据、把结果写进表格、再发给相关的人——它就傻眼了,因为它既没有眼睛,也伸不出手。
所以这段时间我一直琢磨一件事:怎么给大模型把"眼睛"和"手"装上。眼睛负责看——截图、OCR、图像理解;手负责干——查接口、执行代码、操作页面。琢磨来琢磨去,最后选定 anythingLLM 来做整个底座,把它打造成一个能看、能查、能调接口干活的 AI Agent。今天这篇就把我的选型思路、装配过程、踩坑记录一次性讲清楚,看完你也能照着搭一套出来。
这套方案适合什么人?适合已经有一定大模型使用经验、想让模型真正介入业务流程的开发者,也适合运维、测试、运营这些想把日常重复活儿交给 Agent 的实操型同学。不需要从头写一套 Agent 框架,anythingLLM 已经把底座、对话、知识库、工具调度这些都包好了,我们只需要做"接线"的工作。
1. 底座选型:为什么是 anythingLLM
说实话,市面上能搭 Agent 的工具不少,有重型的开发框架,也有各种商业平台,但一圈比对下来,anythingLLM 在我这里赢在三个点上。
1.1 模型无关,想换大脑随时换
anythingLLM 是把"模型接入"和"应用逻辑"解耦的。它支持接本地模型运行时(比如 Ollama 这类),也支持接各种云端模型 API,而且可以同时配置多套,在不同工作区里用不同模型。
这意味着什么?意味着大脑可以随时换。今天是通用对话模型负责调度,明天我想让一个专门的轻量模型处理 OCR 结果分析,后天我发现某个模型推理能力不行,直接改配置就切走了,业务代码一点不用动。对做实际项目的人来说,这种"模型中立"太重要了,你永远不知道下一个更好的模型什么时候出来,绑定死一家就亏了。
1.2 工作区加知识库,天然的 RAG 骨架
Agent 要干活,光靠模型肚子里那点知识远远不够,它得能查项目文档、查历史数据、查团队规范。anythingLLM 的工作区(Workspace)设计在这一环非常省事:每个工作区可以上传独立文档,系统自动做向量化,回答问题时检索增强。这比我自己拿向量数据库从零搭一套 RAG 要省太多事。
我实际用下来的感受是,它的文档解析和检索效果在开源方案里属于相当能打的。我给它丢了几十页的项目运维手册、接口文档、排障 SOP,它基本都能在回答问题时引用对段落,不会答非所问。
1.3 Agent 模式自带工具调度能力
很多人不知道,anythingLLM 不是单纯的聊天问答工具,它内置了 Agent 运行模式。在这个模式下,模型不只是生成文字,而是会被赋予一组工具,它可以自己判断"这个问题需不需要调用工具""调用哪个工具""拿到结果之后怎么继续"。
这正好是我要的"手"的雏形。而且它是可视化配置的,不用像在开发框架里那样写一堆注册代码。对非专业程序员来说很友好,对专业开发者来说也能省掉大量胶水代码。
2. 整体架构:大脑、眼睛、手三段式
拿到一个项目,先不急着动手,我把整个 Agent 的能力模型拆成了三段:大脑、眼睛、手。这个拆法能让后面每一步都清楚——哪里该接什么、哪里缺什么。
2.1 大脑:负责想,不负责看和干
大脑就是大模型本身,在 anythingLLM 里就是那个对话模型。它要做的事情是意图理解、任务拆解和决策调度。
比如我跟 Agent 说:"帮我看看昨天线上那台机器为什么告警。"大脑收到这句话后要做三件事:判断"看监控截图"需要用眼睛,判断"查告警记录"需要用一只手调接口,然后决定先调哪个、拿到结果怎么综合分析。它自己不直接看图片,也不直接发 HTTP 请求,它是指挥官。
这里有个特别重要的设计理念:别让大模型亲自处理它不擅长的事。视觉推理能力再强,也不如专门的 OCR 引擎识别得快、准;写代码能力再强,也不如直接调封装好的接口稳定。所以大脑的任务是"想清楚怎么做",剩下的都交给外围模块。
2.2 眼睛:负责看,把世界变成文字
眼睛这一层,我把它定义成"一切能把视觉信息变成模型能理解的东西的模块集合"。什么东西属于眼睛?截图工具、OCR 识别服务、图片描述接口、表格识别引擎,全是眼睛。
为什么要单独做眼睛层?因为大模型的理解入口是文本。模型再强,本质上也还是对文字做概率预测。图片对它来说是一堆像素矩阵,除非是多模态模型直接看图,否则它需要眼睛先把图片"翻译"成文字,比如把一张系统监控截图翻译成"CPU 使用率 95%,内存 80%,磁盘 I/O 偏高"。
我给眼睛定的原则是:能转成结构化文字的,绝不让模型去"看图猜"。OCR 能识别的文字,直接给文字;需要描述场景的,让图像描述模型产出结构化描述;能转成表格数据的,一定要按 JSON 格式输出,方便下游工具处理。
2.3 手:负责干,连接真实世界的动作
手就是一切能改变外部状态、获取外部数据的工具集合。HTTP 请求、代码执行、数据库查询、文件写入、调用通知接口推送消息,这些都是手。
手的设计有一个度要把握:不是每个操作都要做成单独的手。有些简单操作,比如算个数、转个格式,让模型自己写代码执行就行;而有些关键操作,比如"查询订单状态""发送审批通知",这些涉及外部系统核心逻辑的,一定要封装成固定参数的接口,不能让模型自由发挥。
我搭这套东西的时候,手是最花时间的部分。原因很简单:大脑和眼睛都有现成的开源方案,但手必须针对你自己的业务场景去接。你公司的订单系统、监控系统、工单系统,和我的完全不一样。所以这一部分最需要花心思设计。
3. 给模型装上眼睛:视觉能力从哪来
开头我说了三种装眼睛的方案,这里逐个展开说,包括各自适用什么场景、有什么坑。
3.1 最省事:直接选用多模态模型
如果预算允许、数据也不太敏感,最简单的方式是直接用带视觉能力的多模态大模型。把图片作为输入的一部分直接发给模型,它看一眼就能回答"这是什么""图里写了什么"。
这个方案的优势是零额外开发,模型天生就会看图。但劣势也很明显:一是贵,图片按像素算 token,一张高清截图消耗的 token 比几千字文本还多;二是慢,图像推理比纯文本慢不少;三是它未必擅长细活,比如识别一张截图里的小字表格,多模态模型经常看走眼。
所以我的用法是:多模态模型只用于"需要理解图片语义"的场景,比如分析一张架构图、看一张产品设计稿、判断一张截图里有没有异常告警弹窗。它负责"看懂",不负责"抠字"。
3.2 最实用:OCR 引擎加结构化输出
真要处理文档、截图、发票、表格这类图片里的文字信息,老老实实用 OCR 引擎比什么都强。本地部署一个开源 OCR 引擎(类似 Tesseract 这类成熟项目),把它封装成一个 HTTP 服务,接收图片、返回文字,干净利落。
OCR 方案我在实际项目里用下来,识别印刷体、屏幕文字的效果相当好,尤其是在中文场景下,准确率能做到 95% 以上。处理扫描件、发票、代码截图、报错弹窗,都是它的拿手好戏。
但 OCR 也有自己的脾气。识别表格结构经常会乱,同一张表格,文字能认出来,但"哪行哪列"的关系容易错位。手写体就更别提了,基本不可用。解决方法是:表格类图片优先转成 Markdown 表格格式输出,手写体直接放弃,交给人工处理,不要硬上。
3.3 眼睛的核心封装:统一视觉服务
不管底层用多模态模型还是 OCR 引擎,我强烈建议你封装一个统一的视觉服务,对外只暴露一个接口:传图片进去,返回标准的 JSON 文本。接口内部再根据图片类型自动路由——是纯文字截图就走 OCR,是场景图就走多模态描述。
这个封装的收益非常大。后期想换 OCR 引擎或者换视觉模型,只需要改服务内部实现,Agent 这边完全不用动。我踩过一次不封装的坑:一开始直接在 Agent 工具里写死了调某家的 OCR 接口,后来不稳定想换一家,结果所有相关工具全要改,烦到崩溃。
下面是视觉服务的一个简单示例,用 Python 写个路由层:
from flask import Flask, request, jsonify import requests, base64 app = Flask(__name__) # 假设内部有两个引擎:ocr_engine 和 vision_engine def detect_image_type(image_bytes): # 简单判断:如果图片里像素分布比较"平"、文字密集,走 OCR # 实际项目里可以用图像统计特征,比如纹理复杂度 # 这里直接给个模拟判断,生产环境建议先跑一批样本调阈值 return "ocr" @app.route("/vision/analyze", methods=["POST"]) def analyze(): data = request.get_json() image_b64 = data.get("image") # base64 编码 img_bytes = base64.b64decode(image_b64) mode = data.get("mode", "auto") if mode == "auto": mode = detect_image_type(img_bytes) if mode == "ocr": # 调用 OCR 引擎,返回 {"text": "识别结果", "type": "ocr"} result = {"text": ocr_engine.run(img_bytes), "type": "ocr"} else: # 调用视觉模型引擎,返回 {"description": "...", "type": "vision"} result = {"description": vision_engine.describe(img_bytes), "type": "vision"} return jsonify({"code": 0, "data": result})3.4 把视觉服务挂进 anythingLLM
视觉服务写好后,要在 anythingLLM 里把它变成一个 Agent 可以直接调用的工具,配置一个自定义工具即可。工具名称我取的是"视觉分析",描述写成"输入图片路径或图片 URL,返回图片的文字内容或结构化描述,用于查看截图、识别文档、分析图像"。
描述一定要写清楚工具的适用场景和输入格式。这一点很多人忽略,其实极其重要。大模型是靠描述来决定要不要调用这个工具的,描述写得模糊,它就可能在不需要用的时候也乱调,或者在真正需要的时候想不起来。
工具配置好后,在 Agent 的模型参数里把工具开关打开。之后你可以直接发一张截图给它,它就自动调用视觉服务,然后基于识别出的文字继续跟你对话。到这步,第一只眼睛就装上了。
4. 给模型装上手:从内置工具到自定义工具
视觉搞定后,开始装手。手是 Agent 真正能改变世界的那部分,也是工程上最容易出事的地方。
4.1 anythingLLM 自带的手够不够用
anythingLLM 的 Agent 模式里预置了几个常用工具,比如网页内容阅读、网页搜索。这几个工具对很多场景已经够用了。比如让它去读一个技术文档页面、查一条公开信息,它真能自己完成"搜索-阅读-总结"的闭环。
但自带工具的问题是它们太通用,没法对接你私域的业务系统。你的内部系统不会开放给它的通用搜索工具去查,所以核心的手还是要自己做。
4.2 自己造手:把内部接口封装成 Agent 工具
给 Agent 造手,本质就是把你现有的 API 接口包装成它能理解的样子。这里最关键的不是写代码,而是做接口设计裁剪。
我家里的实践是,优先封装两类接口:查询类接口(订单、状态、日志、监控数据)和执行类接口(发消息、写记录、触发流程)。查询类优先,因为只读操作风险小;执行类接口一定要加上参数白名单和二次确认。
拿一个实际的工具来举例。我想让 Agent 能查"某电商系统的订单进度",后端有一个接口本来返回 50 个字段,但 Agent 只需要其中 5 个。于是我做了一层精简代理,只暴露几个必要参数:订单号、时间范围。返回也只保留:状态、金额、更新时间、当前节点。这样模型不需要在一堆无关字段里挑,减少出错概率。
工具返回格式建议统一用 JSON。我甚至会在 JSON 最前面加一段纯文本摘要,让模型先看到结论,再决定要不要继续追问细节。这个方法能显著减少模型因为返回内容太长而"迷失"的情况。
下面是我封装的一个查询工具的示意代码,部署成独立服务后,在 anythingLLM 里注册成工具 "订单查询":
@app.route("/agent/order_query", methods=["POST"]) def order_query(): params = request.get_json() # 参数白名单:只认这两个字段,别的全部丢弃 order_id = params.get("order_id") date_range = params.get("date_range", "7d") if not order_id: return jsonify({"code": 400, "msg": "missing order_id"}) # 调内部接口拿原始数据 raw = internal_api.fetch_order(order_id, date_range) # 裁剪字段,加摘要 summary = f"订单 {order_id} 当前状态:{raw['status']},金额:{raw['amount']},最近更新:{raw['updated_at']}" return jsonify({ "code": 0, "data": { "summary": summary, "status": raw["status"], "amount": raw["amount"], "updated_at": raw["updated_at"], "current_node": raw["current_node"] } })4.3 给手装上安全锁
手能干活,也能闯祸。我这里说三个必须加的安全措施。
第一是权限最小化。Agent 调用的接口账号,绝不使用管理员权限,只给到"能查到这个 Agent 分内事"的最小权限。曾经有人图省事,给 Agent 配了个高权限账号,结果一次工具调用参数解析异常,把线上一条测试数据给覆盖了,教训深刻。
第二是超时和重试机制。大模型的思考速度不稳定,工具返回也可能慢,一定要设置合理的超时,不能让一个工具调用永远挂着。重试次数也要限制,最多重试两次,再多就直接放弃并把异常告诉模型,让它换一种方案,而不是反复撞同一个错误。
第三是执行类操作必须二次确认。凡是会修改数据的工具,我对 Agent 的要求是:先输出将要执行的操作内容和影响范围,等用户明确说"确认"之后,才真正执行。这一步能挡住绝大多数误操作。
5. 实操过程:从零搭到全流程跑通
思路理清楚了,下面是我实际搭这套系统的完整流程,每一步都能直接照着做。
5.1 部署 anythingLLM 实例
我选择用 Docker 部署,一来干净,二来方便备份迁移。把容器跑起来后,映射好端口和存储目录就完成了基础安装。这里有个小建议:把数据目录挂载到宿主机磁盘,要不然容器一删所有配置和知识库全没了。
安装完进入管理界面,第一件事是创建管理员账号,第二件事是赶紧去设置里看一下模型连接配置。
5.2 接入大脑:配置模型
在模型配置里,我把两套模型都接上了。一套是本地部署的开源模型,主要跑日常问答和简单调度,数据不走外网,响应也快;另一套是云端的能力更强的商业模型 API,处理复杂推理和长文档总结。
这里分享一个重要心得:不同工作区可以用不同模型。我建了两个工作区,一个日常积累区用的本地模型,处理文档问答;一个任务调度区用的强模型,专门处理复杂工具调用。这样既省钱又不牺牲体验。
5.3 装眼睛:部署视觉服务并注册工具
按前面说的,我把视觉服务部署在本机另一个端口,然后用一个简单的脚本测试接口能正常返回。确认没问题后,进入 anythingLLM 的工具配置页,新增自定义工具,填上服务地址和工具描述。
注册工具后我做了一次快速验证:传了一张系统报错截图,Agent 通过视觉服务识别出错误关键字,再根据知识库里的排障文档,给出了对应的处理建议。这一条链路走通,就说明眼睛真正长上去了。
5.4 装手:注册查询类工具
我先注册的是无风险的查询类工具,也就是那个订单查询工具。在工具配置里填好接口地址、请求方式(POST)、返回格式。测试时直接让 Agent 问"查一下订单 A10086 的进度",它成功识别出需要调用工具,返回了摘要。到此手也装上了。
接下来才开始注册写操作类工具,而且我故意在 Agent 的指令里加了一条规则:"任何修改数据的操作,必须等我确认后再执行。"实测下来,这个约束对控制风险非常有效。
5.5 端到端实测:让它完成任务闭环
这里记录三个我跑通的真实场景,你可以感受一下完成度。
第一个场景是"看":给它一张新上线的页面截图,让它检查有没有明显排版问题。它自动调视觉服务识别文字元素,结合我对页面设计要求的知识库内容,列出了一个初步的问题清单。虽然不能说像资深前端那样细致,但作为初筛完全够用。
第二个场景是"查":让它统计最近一周"某分类订单"的数量和金额趋势。它先调订单查询工具拿到明细,再用内置的代码执行工具做聚合计算,最后输出了一张简洁的统计表。注意,我并没有教会它怎么调聚合函数,它是自己决定用代码工具来算的。
第三个场景是"干":让它根据查询到的订单数据,生成一份简短的周报,并调用消息推送接口发给指定群。这一步涉及查询、生成、推送三个动作,需要模型连续调度三个工具,最终完整跑通了。看到消息从群里弹出来那一刻,确实有一种"这家伙真的能干活了"的感觉。
6. 踩过的坑和排查心得
最后说说我在这套系统上踩的坑。这些东西文档里不会写,全是用时间和事故换出来的。
6.1 模型乱调工具怎么办
最大的坑是模型"工具调用幻觉":明明不需要查任何东西,它偏要调一下工具再回答;或者把参数传错,把订单号传成了日期。
排查下来,根因通常是两个:一是工具描述太宽泛,让模型觉得"啥都能查一下";二是模型能力偏弱,对工具意图判断不准确。我的解法是把工具描述写得极度具体,甚至直接在描述里写清触发条件和不触发条件。比如"订单查询:仅当用户明确提到订单号或订单进度时调用,闲聊不要调用"。改完之后乱调用率直线下降。
另一个有效手段是把思维链提示加进系统指令里,要求模型不能在调用工具前就编造结论。具体做法是给 Agent 设定"先分析需要什么信息,再决定是否调用工具"的流程。
6.2 视觉服务结果不稳定
OCR 对高清晰度截图效果很好,但遇到有背景色的页面截图,经常把背景干扰文字也识别进来。后来我在视觉服务里加了预处理逻辑:先做灰度化和二值化,再送识别引擎,准确率提升明显。
还有一次是表格截图整体错乱,后来排查发现是图片分辨率不够。这个问题的解决方案很朴素:教 Agent 在调用视觉工具之前,先确认图片是否清晰,因为很多截图工具默认缩放,导致页面文字糊成一片。让 Agent 提示用户"请截取清晰的原图"能解决一半问题。
6.3 上下文爆炸:工具返回内容太长
有一次 Agent 查询了一个大范围时间段的日志,工具一次性返回了几万行文本。模型上下文窗口直接被塞爆,后面的对话开始胡言乱语。
解决思路是限制工具返回长度。我在工具服务端做了截断策略:任何返回给模型的文本,最多保留前 N 个字符,超出部分用一句话概括"其余 N 条结果已省略,可通过指定条件缩小范围"。同时我会在工具返回里附带一个"查询建议",引导模型在数据量大时主动缩小查询范围。这样模型就不会硬着头皮处理海量返回内容了。
6.4 权限和审计不能省
前面提到的权限最小化是其一,其二是审计日志。每次 Agent 调用工具,我都会记录下时间、调用方、入参、返回状态、耗时。一开始觉得麻烦,直到有一次 Agent 误触发了一个测试环境的清理任务,靠着审计日志才追溯到是哪条指令导致的。
所以现在的做法是:所有工具服务端强制开启请求日志,至少保留 30 天。这不算什么高科技,但关键时刻能省很多排查时间。
7. 装完眼和手之后还能怎么扩展
架构搭好之后,后续扩展空间其实很大。视觉服务已经被抽象成独立模块,新接一个"自动截图"的能力,就能让 Agent 定时看完监控页面再汇报;手这边,把消息推送、日历、邮件这些常用能力封装成工具,就能组一个"能自己组织会议、发周报、跟进待办"的助理 Agent。
我个人目前的规划是:先把内部知识库继续充实,让 RAG 检索的覆盖面更全,减少 Agent 靠猜回答的情况;然后给视觉服务增加对图表的理解能力,让它能从折线图、柱状图里提取趋势信息。等这两块补齐,这套 Agent 的"可用性"会再上一个台阶。
搭这套系统给我的最大体会是:不要把注意力全放在大模型本身。模型的能力已经被卷得很高,真正的瓶颈在两侧——感知端能不能把真实世界的信息喂给它,行动端能不能让它的决策变成实际动作。眼睛和手,才是 Agent 落地过程中最能拉开差距的地方。