从裸调API到Claude Code插件:同一Agent三版演进与选型指南

发布时间:2026/10/31 3:49:45
从裸调API到Claude Code插件:同一Agent三版演进与选型指南

同一个需求,我前前后后写了三版 Agent,从最裸的直接调 API,到套一层 AgentCore 做编排,最后干脆塞进 Claude Code 做成插件。三版跑下来,代码量差了将近十倍,但真正让我省心的那一版,反而是看起来最"重"的插件形态。这篇就把这三条路各自的取舍、踩过的坑、以及什么场景该选哪条,完整摊开讲一遍。

如果你正在纠结"我到底该直接调 API 还是上框架",或者已经写过一版 Agent 但总觉得哪里别扭,那这篇应该能帮你少走点弯路。我会尽量把每一版的真实代码结构、参数配置、以及那些文档里不会写的坑都讲清楚,让你看完能直接对着抄。

1. 先想清楚:同一个 Agent 为什么值得写三遍

很多人看到"同一类 Agent 做三遍"第一反应是重复劳动。但如果你真的把一个 Agent 从裸 API 一路做到插件形态,就会发现这三遍其实是在回答三个完全不同的问题:第一遍回答"这个能力到底能不能跑通",第二遍回答"跑通之后怎么稳定地编排和复用",第三遍回答"怎么让它无缝融进我每天的工作流"。这三个问题的答案,决定了你最终该停在哪一层。

1.1 三版形态的本质差异

先把三版的定位摆清楚,不然后面容易混。

版本形态核心职责典型代码量适合谁
V1直接调 API验证能力、跑通链路50~150 行探索期、一次性任务
V2AgentCore 编排多步骤、多工具、可复用500~1500 行有稳定业务流、要复用
V3Claude Code 插件融入日常工作流视插件复杂度而定高频使用、要交互体验

V1 的本质是"我手动把 prompt 拼好,发出去,拿结果"。V2 的本质是"我把决策逻辑交给一个编排层,它来决定调哪个工具、走哪一步"。V3 的本质是"我不再单独启动一个程序,而是让 Agent 变成我编辑器里的一个命令"。

这三者的差别不是简单的"封装程度",而是控制权在谁手里。V1 控制权完全在你,V2 控制权部分交给框架,V3 控制权交给宿主环境(编辑器)加你的插件逻辑。理解这一点,后面所有的取舍就都顺了。

1.2 一个真实的需求场景

我这次的 Agent 需求很具体:给它一段技术文档或者一个代码仓库路径,让它自动提取关键信息、生成结构化的摘要、并且能根据我的追问继续深挖。说白了就是一个"文档/代码理解助手"。

这个需求看起来简单,但它天然包含几个环节:读取输入、理解内容、生成结构化输出、支持多轮追问。这几个环节正好对应了三种形态的适用边界——如果只是"读一次、出一次结果",V1 足够;如果要"多轮、多工具、可复用",得上 V2;如果要"我随手在编辑器里就能唤起",那就是 V3。

我一开始也觉得自己想多了,直接调 API 不就完了。但真做下来才发现,每一版暴露出来的问题都不一样,而这些问题是只有真正动手才会撞上的。

2. V1 直接调 API:最快跑通,也最快撞墙

V1 是我推荐所有人起步的地方,因为它能让你在半小时内看到结果,建立正反馈。但它的天花板也很明显,而且这个天花板往往在你以为"快做完了"的时候突然出现。

2.1 最小可运行版本长什么样

直接调 API 的核心就三件事:拼请求、发请求、解析响应。以常见的对话式接口为例,一个最小版本大概是这样:

import requests def ask_agent(user_input, api_key, base_url, model): headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } payload = { "model": model, "messages": [ {"role": "system", "content": "你是一个技术文档理解助手,负责提取关键信息并结构化输出。"}, {"role": "user", "content": user_input} ], "temperature": 0.3 } resp = requests.post(f"{base_url}/chat/completions", headers=headers, json=payload, timeout=60) resp.raise_for_status() return resp.json()["choices"][0]["message"]["content"]

这段代码没有任何花哨的东西,但它已经能跑通"输入文档、输出摘要"这个核心链路了。我建议你第一版就写到这个程度,先别急着加工具调用、加记忆、加多轮。

这里有个细节值得说:temperature我设成了 0.3。做信息提取类任务时,温度太高会让模型"发挥",把原文没有的东西编进去。0.3 是我实测下来在"稳定"和"不死板"之间的一个平衡点。如果你做的是纯抽取任务,可以压到 0.1;如果是需要一点归纳总结的,0.3 到 0.5 都行。

2.2 那些让你卡半天的报错

V1 阶段最容易撞的坑,几乎全在"请求根本没发出去"或者"发出去了但被拒"这一类。我把高频的几个列一下,都是我自己踩过的。

401 类错误:最常见的就是密钥不对或者格式不对。报错信息里通常会带一段类似incorrect api key provided: sk-xxxx的内容。这时候别急着怀疑密钥本身,先检查三件事:密钥有没有多余空格、请求头里的Bearer后面是不是只有一个空格、以及你用的 base_url 和密钥是不是同一家的。我遇到过密钥是 A 平台的、base_url 填了 B 平台的,报错就是 401,查了半天才发现是复制粘贴串了。

400 类错误:这类里最典型的是上下文超长。报错会明确告诉你"maximum context length is XXXXX tokens"。这时候要么截断输入,要么换更大上下文的模型。我的经验是,做文档理解时不要一股脑把整篇文档塞进去,先做一次分块,按段落或按标题切,再逐块处理。这样既避免超长,又能让每块的处理质量更稳定。

超时和连接问题:timeout一定要设,而且不要设太短。文档理解类任务,模型思考时间长,我一般设 60 到 120 秒。设太短会出现"请求发出去了但没等到结果"的情况,你还以为是代码 bug。

提示:V1 阶段所有报错都优先怀疑"请求参数"而不是"模型能力"。90% 的失败都是参数问题,不是模型不行。

2.3 V1 的天花板在哪

V1 跑通之后,你会很快遇到三个绕不过去的问题。

第一是多轮对话的状态管理。每次请求你都得把历史消息重新拼进去,拼着拼着就乱了,尤其是当你要做"追问深挖"的时候,历史消息会越来越长,很快就撞上上下文限制。

第二是工具调用。当你的 Agent 需要"读文件""查数据库""调另一个接口"时,V1 里你得手动写一堆 if-else 来判断该调哪个,代码迅速变成一坨。

第三是复用。你写好的这套逻辑,想换个场景用,基本得复制粘贴改一遍,改着改着就分叉了。

这三个问题,正是 V2 要解决的。所以别觉得 V1 白写了,它帮你验证了"能力可行",这是后面所有工作的前提。

3. V2 上 AgentCore:编排能力上来了,复杂度也上来了

从 V1 到 V2,最大的变化是你不再手动管理"下一步做什么",而是把这件事交给一个编排层。AgentCore 这类框架的核心价值,就是把"决策—调用工具—观察结果—再决策"这个循环抽象出来,你只需要定义好工具和提示词。

3.1 AgentCore 到底帮你做了什么

用一句话概括:它把 Agent 的"思考循环"标准化了。在 V1 里,这个循环是你用代码手写的;在 V2 里,框架帮你跑这个循环,你只需要提供两样东西——工具定义和系统提示。

具体来说,AgentCore 这类框架通常帮你处理了这几件事:

  • 工具注册与调度:你声明有哪些工具、每个工具接受什么参数,框架负责在合适的时机调用它们。
  • 循环控制:模型说"我要调工具 A",框架执行 A,把结果喂回模型,再让模型决定下一步,直到模型说"我完成了"。
  • 上下文管理:把工具调用结果、历史消息按框架约定的格式拼进上下文。
  • 错误重试:工具调用失败时的重试和降级逻辑。

听起来很美好,但这里有个关键认知:框架帮你省的是"样板代码",不是"设计工作"。你依然要想清楚工具有哪些、边界在哪、失败了怎么办。框架只是让这些逻辑写起来更规整。

3.2 工具定义是 V2 的核心工作量

V2 里我花时间最多的地方,不是写业务逻辑,而是设计工具。工具设计得好不好,直接决定 Agent 聪不聪明。

我当时的工具大概有这么几个:

tools = [ { "name": "read_file", "description": "读取指定路径的文件内容,支持文本和代码文件", "parameters": { "type": "object", "properties": { "path": {"type": "string", "description": "文件的绝对路径"} }, "required": ["path"] } }, { "name": "search_in_repo", "description": "在代码仓库中按关键词搜索,返回匹配的文件和行号", "parameters": { "type": "object", "properties": { "keyword": {"type": "string"}, "file_pattern": {"type": "string", "description": "如 *.py,可选"} }, "required": ["keyword"] } }, { "name": "summarize_chunk", "description": "对一段文本生成结构化摘要", "parameters": { "type": "object", "properties": { "content": {"type": "string"}, "focus": {"type": "string", "description": "关注点,如'接口定义'或'错误处理'"} }, "required": ["content"] } } ]

这里有几个我踩过坑才明白的点。

description 是给模型看的,不是给人看的。你写工具描述时,要假设读者是一个"很聪明但完全不了解你系统"的人。描述里要写清楚:这个工具做什么、什么时候该用、参数是什么含义。我一开始描述写得很简略,结果模型经常在不需要的时候乱调工具,或者该调的时候不调。把描述写详细之后,命中率明显提升。

参数类型要严格。能用string就别用object,能用枚举就别用自由文本。参数越自由,模型填错的概率越高。

工具粒度要适中。太粗(一个工具干十件事)模型不好用,太细(一个工具只干一件小事)模型要调很多次,又慢又容易出错。我的经验是,一个工具对应一个"语义完整的动作",比如"读文件"是一个动作,"在仓库里搜索"是一个动作,但"读文件并摘要"就不该是一个工具,那是两个动作。

3.3 编排带来的新问题:不可控与难调试

V2 跑起来之后,能力确实强了,但新的问题也来了。

第一个问题是不可控。模型有时候会做出你意想不到的决策,比如反复调同一个工具、或者跳过某个关键步骤。这时候你得靠系统提示去约束它,但约束太死又会让它变笨。这个平衡很难调。

第二个问题是难调试。V1 里出错了,你看一眼请求响应就知道问题在哪。V2 里出错了,你得看整个循环的轨迹——模型想了什么、调了什么工具、工具返回了什么、模型又怎么反应的。这个轨迹如果不打日志,根本没法查。

我的做法是给每一步都打结构化日志:

import logging logging.basicConfig(level=logging.INFO) def log_step(step_type, content): logging.info(f"[{step_type}] {content}")

然后在循环的每个关键节点调用它。这样出问题时,我能完整复现模型的决策路径。这个习惯帮我省了大量排查时间。

第三个问题是成本。V2 因为要跑多轮循环,token 消耗比 V1 高不少。尤其是工具返回结果很长的时候,这些结果都要进上下文,很快就堆起来了。我的应对是给工具返回做截断,只返回最相关的部分,而不是把整个文件内容都塞回去。

3.4 什么时候 V2 是过度设计

这里我要泼一盆冷水:不是所有 Agent 都需要 V2。如果你的任务就是"输入一段文本、输出一个结果",中间不需要调外部工具、不需要多轮决策,那 V2 纯属给自己找麻烦。

我判断的标准很简单:如果这个任务用一段固定的流程就能描述清楚,那就不需要 AgentCore。比如"读文档→分块→逐块摘要→合并",这是一个固定流程,用普通代码串起来就行,不需要让模型来决定下一步。只有当"下一步做什么"真的需要根据中间结果动态判断时,V2 才有价值。

我当时上 V2,是因为我的需求里有"根据追问深挖"这个环节,深挖的方向是不确定的,这才需要编排。如果你的需求是固定的,老老实实用 V1 加一点流程代码,比上框架省心得多。

4. V3 做成 Claude Code 插件:从"能用"到"顺手"

V2 解决了能力问题,但还有个体验问题没解决:我每次用都得切到终端、启动脚本、粘贴内容。用了几次之后我就烦了,因为我的工作流本来就在编辑器里,为什么要跳出去?

这就是 V3 的动机——把 Agent 变成编辑器里的一个命令。

4.1 插件形态到底改变了什么

做成插件之后,最大的变化不是功能,而是交互路径。以前是"打开终端→运行脚本→输入→等结果→复制结果",现在是"在编辑器里选中内容→敲一个命令→结果直接出现在旁边"。这个路径短了,使用频率就上去了。

Claude Code 这类工具的插件机制,本质上是让你把自定义逻辑挂到它的命令体系上。你可以定义一个命令,当用户在编辑器里触发它时,它接收当前上下文(比如选中的文本、当前文件路径),然后执行你的逻辑。

这里要澄清一个常见误解:插件不是把 Agent 重写一遍,而是给已有的 Agent 逻辑套一个交互外壳。我 V3 里的核心逻辑,其实复用了 V2 的一部分,只是把入口从"命令行参数"换成了"编辑器上下文"。

4.2 插件开发里最容易忽略的细节

做插件时,我踩的坑和写普通程序完全不一样,因为你要和宿主环境打交道。

第一个坑是上下文获取。插件能拿到什么上下文,取决于宿主提供了什么。选中的文本、当前文件、光标位置,这些不一定都能拿到。我一开始想当然地以为能拿到"当前打开的所有文件",结果发现只能拿到当前激活的那个。所以设计插件功能时,要先确认宿主的能力边界,别基于假设去设计。

第二个坑是异步和超时。编辑器插件通常有响应时间要求,如果你的 Agent 要跑十几秒,界面会卡住或者超时。我的做法是把耗时操作放到后台,先给用户一个"处理中"的反馈,处理完再回填结果。这个体验细节,直接决定了插件好不好用。

第三个坑是错误处理。插件里出错,用户看到的往往是一个很模糊的提示。所以你得把错误信息包装得友好一点,比如"读取文件失败,请检查路径"而不是直接抛一个堆栈。

4.3 插件配置与调试的实操

插件的配置通常是一个清单文件,声明命令名、触发方式、入口脚本等。以常见的结构为例:

{ "name": "doc-agent", "version": "1.0.0", "commands": [ { "name": "analyze-selection", "description": "分析选中的文本并生成结构化摘要", "entry": "dist/analyze.js" } ] }

调试插件时,最有效的方法是先脱离宿主单独跑。也就是把你的入口脚本当成一个普通脚本,手动传上下文进去,确认逻辑没问题,再挂到宿主上。这样能把"逻辑 bug"和"集成 bug"分开,排查效率高很多。

另外,插件的日志一定要输出到你能看到的地方。有些宿主环境里,插件的console.log是不显示的,你得写到文件里或者用宿主提供的日志接口。我一开始就是因为看不到日志,白白多花了一个小时。

4.4 三版对比:到底该停在哪一层

到这里,三版都讲完了。我把它们放在一起对比一下,方便你做决策。

维度V1 直接调 APIV2 AgentCoreV3 Claude Code 插件
上手速度最快中等较慢
代码量最少中等视宿主而定
多轮/多工具手动实现框架支持复用 V2
交互体验差差好
复用性低高中
调试难度低高中
适合场景验证、一次性稳定业务流高频日常使用

我的建议是:从 V1 起步,按需升级,不要一步到位。先用 V1 验证能力,确认这个 Agent 真的有用;如果发现需要多轮和多工具,再上 V2;如果发现自己每天都在用、而且嫌切换麻烦,再考虑 V3。

最忌讳的是反过来——一上来就奔着插件去,结果核心逻辑还没跑通,就在集成上耗光了耐心。

5. 三版都踩过的通用坑与经验

不管哪一版,有些坑是共通的。这部分是我觉得最值钱的经验,因为它们是跨版本复用的。

5.1 密钥与配置管理

三版里我都遇到过密钥相关的问题。最典型的就是把密钥硬编码在代码里,然后不小心提交了。我的做法是统一用环境变量,并且写一个配置加载层:

import os def load_config(): return { "api_key": os.environ.get("AGENT_API_KEY"), "base_url": os.environ.get("AGENT_BASE_URL", "https://api.example.com/v1"), "model": os.environ.get("AGENT_MODEL", "default-model") }

这样切换环境时只改环境变量,不动代码。另外,密钥一定要做校验,启动时如果发现没配,直接报错退出,别等到发请求时才 401。

5.2 上下文长度管理

这是三版都会遇到的问题。我的通用策略是分块加摘要:先把长输入切成块,每块单独处理,再把各块的结果合并。合并时如果还是太长,就再做一次摘要。这个策略在文档理解和代码理解场景下都很管用。

具体分块大小,我一般按 token 数控制在模型上限的 60% 到 70%,留出空间给系统提示和输出。别卡着上限用,容易出问题。

5.3 输出格式的稳定性

让模型输出结构化内容(比如 JSON)时,最容易出现的问题是格式不稳定——有时候多一个逗号,有时候少一个字段。我的应对是:在提示词里明确给出格式示例,并且在解析时做容错。比如用正则先提取 JSON 部分,再解析,解析失败就重试一次。

import json import re def parse_json_safe(text): match = re.search(r'\{.*\}', text, re.DOTALL) if not match: return None try: return json.loads(match.group()) except json.JSONDecodeError: return None

这个函数看起来简单,但帮我省了无数次手动修格式的时间。

5.4 日志与可观测性

三版里我都坚持打日志,而且日志要包含足够的信息:输入是什么、模型返回了什么、耗时多少、有没有报错。这些日志在排查问题时是救命的。尤其是 V2 和 V3,没有日志基本没法调试。

我一般会记录这几个字段:时间戳、版本标识、输入摘要、输出摘要、耗时、错误信息。这样出问题时,我能快速定位是哪一版、哪个环节出的问题。

6. 我的最终选择与给你的建议

三版做完,我最终保留的是 V3 插件形态,但核心逻辑复用了 V2。也就是说,V3 是"V2 的逻辑 + 插件的交互"。V1 的代码我没留,但它验证了能力,这个价值是不可替代的。

如果你现在正准备做自己的 Agent,我的建议是按这个顺序来:先花半天写 V1,确认能力可行;如果发现需要多轮和多工具,再花几天上 V2;如果发现自己每天都在用,再考虑 V3。每一步都别跳,因为每一步解决的问题不一样。

最后分享一个我自己的判断标准:当你开始觉得"每次用都要切来切去很烦"的时候,就是该做插件的时候了。这个信号比任何技术指标都准,因为它是真实使用频率的体现。技术选型最终要服务于使用习惯,而不是反过来。