1. 从一次“AI 助手卡死”说起:mini-cc 的 Agent 循环到底在循环什么
先还原一个我真实遇到的场景。本地跑着一个基于 mini-cc 思路写的 AI 助手,输入“帮我看看 package.json 里的项目名,顺便把 version 也读出来”,结果终端里日志刷了十几屏,最后停在一句达到最大迭代次数上。任务没完成,Token 倒是烧了不少。这就是 Agent 循环没设计好时的典型表现——它确实在“思考”,但思考的方向跑偏了。
mini-cc 的 Agent 循环,说白了就是让大模型自己决定“下一步干什么”。它不是一个单纯的while(true),而是感知 → 思考 → 行动 → 再感知的闭环。用户输入进来,QueryEngine 构建上下文,调用 LLM,LLM 返回的内容里可能带工具调用(读文件、跑命令、写文件),执行完把结果再喂回去,直到 LLM 说“不用调工具了,这就是答案”。
这套机制适合谁?适合正在做本地 AI 助手、想理解 LLM 调用链路、准备自己搭一个 coding agent 的开发者。你不需要从零造轮子,但需要搞清楚 QueryEngine 和 Agent 循环怎么协作,以及 LLM 请求到底发到哪里、用什么 Key、返回怎么解析。
我试过把这块拆开看,核心其实就三件事:循环控制(什么时候继续、什么时候停)、上下文管理(每轮塞什么进去)、LLM 通道(请求发给谁、怎么鉴权)。前两件是 mini-cc 自己的工程逻辑,第三件就是 TaoToken 要解决的问题——给你一个统一的 Key 和 API 通道,不用在多个模型供应商之间来回切换配置。
下面我会按“先跑通再优化”的顺序,把 settings.json / config.toml 配置骨架、TaoToken 接入步骤、循环触发与日志验证动作全部拆开。你跟着做,能在一个本地 mini-cc 项目里看到 Agent 循环真实跑起来的样子。
2. TaoToken 前置:统一 Key 与 API 通道,让 QueryEngine 只认一个出口
在讲配置之前,先把 TaoToken 的定位说清楚。mini-cc 的 QueryEngine 里有一个LLMProvider抽象,它不关心你用的是哪家模型,只关心三件事:Base URL、API Key、Model ID。TaoToken 就是把这个出口统一掉——你拿一个 Key,配一个 Base URL,就能在 mini-cc 里调用不同模型,不用改 QueryEngine 的代码。
官网入口在这里:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。API 地址是 https://taotoken.net/api ,注意这个不加 UTM 参数,配置里直接写这个。
你需要提前准备的东西不多:
- 一个 TaoToken 账号,登录后进控制台创建 API Key;
- 本地已经有一个 mini-cc 项目(或者你按它的结构新建一个 TypeScript 项目);
- Node.js 18+ 环境,能跑
npm或pnpm。
拿 Key 的路径:进控制台 → API Keys → 新建 → 复制。这个 Key 只显示一次,建议直接写进本地.env或者配置文件,别提交到 Git。
模型对话调试入口:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,你可以先在网页里发一条消息,确认 Key 和通道是通的,再往 mini-cc 里接。
这里有个容易踩的坑:很多人把 Base URL 写成https://taotoken.net/api/v1或者带斜杠的版本,结果 mini-cc 里请求 404。正确做法是Base URL 只写到/api,具体路径由 SDK 或你的 provider 拼接。比如 OpenAI 兼容的调用,最终请求是https://taotoken.net/api/v1/chat/completions,但配置里只写https://taotoken.net/api。
另外,TaoToken 不是“中转”概念,它是一个统一的模型调用通道。你在 mini-cc 里配置一次,QueryEngine 的provider.chat(context)就走这个通道,换模型只改 Model ID,不改代码。这对 Agent 循环特别重要——循环里每一轮都要调 LLM,如果每次换模型都要改 provider 初始化逻辑,工程上会很乱。
Coding Plan 入口:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,如果你打算长期跑 Agent 循环、做本地 coding agent,这个比按量调用更划算,后面 §6 会再提。
3. 可复制配置:settings.json 与 config.toml 骨架,直接填 Key 就能跑
这一节是全文最核心的部分。mini-cc 的配置分两层:项目级 settings.json管 Agent 循环参数和工具注册,用户级 config.toml管 LLM 通道和 Key。两者配合,QueryEngine 启动时先读 config.toml 初始化 provider,再读 settings.json 决定循环上限、记忆策略、工具白名单。
先看config.toml,放在~/.mini-cc/config.toml(Windows 是C:\Users\你的用户名\.mini-cc\config.toml):
# ~/.mini-cc/config.toml # TaoToken 统一通道配置,QueryEngine 启动时读取 [llm] provider = "openai-compatible" base_url = "https://taotoken.net/api" api_key = "sk-你的TaoTokenKey" model = "claude-sonnet-4-20250514" timeout_seconds = 120 max_retries = 2 [llm.headers] X-Client = "mini-cc-agent-loop" [agent] max_iterations = 5 tool_timeout_seconds = 300 memory_short_term_limit = 50 memory_compress_threshold = 50注意base_url只写到/api,model填你在 TaoToken 控制台看到的模型 ID。max_iterations = 5对应 QueryEngine 里的硬上限,防止死循环。tool_timeout_seconds = 300是 BashTool 的兜底超时。
再看项目级settings.json,放在项目根目录.mini-cc/settings.json:
{ "agent": { "name": "mini-cc-local", "systemPromptTemplate": "你是一个专业的 AI 编程助手。可用工具: {{tools}}。工具调用格式: <function_calls><invoke name=\"工具名称\"><parameter name=\"参数名\">参数值</parameter></invoke></function_calls>", "maxIterations": 5, "recursionMode": "recursive" }, "tools": { "enabled": ["FileReadTool", "FileWriteTool", "BashTool", "GetCurrentTime"], "timeouts": { "FileReadTool": 120, "FileWriteTool": 120, "BashTool": 300 } }, "memory": { "shortTermLimit": 50, "compressThreshold": 50, "summaryModel": "claude-sonnet-4-20250514" }, "logging": { "level": "debug", "loopTrace": true, "logFile": ".mini-cc/logs/agent-loop.log" } }这两个文件的分工要记清楚:config.toml是通道层,管 Key、Base URL、Model ID;settings.json是循环层,管迭代次数、工具、记忆、日志。QueryEngine 初始化时,先loadConfig()读 toml,再loadSettings()读 json,然后new QueryEngine(provider, toolRegistry, memory)。
如果你用的是 Cline MCP 或者 Claude Code 的配置习惯,三件套要写全:Base URL + Key + Model ID。缺一个都会在启动时报错。比如 Cline 的 MCP 配置里,baseUrl写https://taotoken.net/api,apiKey写你的 Key,model写模型 ID。Codex 的auth.json同理,OPENAI_BASE_URL指向 TaoToken,OPENAI_API_KEY填 Key。
配置写完后,在项目里跑一次初始化:
cd your-mini-cc-project npm install node -e "const {loadConfig}=require('./src/config'); console.log(loadConfig())"如果输出里能看到base_url: 'https://taotoken.net/api'和你的模型 ID,说明配置读取没问题。这一步别跳过,很多“循环跑不起来”的问题,其实是配置根本没加载。
4. 验证请求:触发 Agent 循环并看日志确认 LLM 调用链路
配置就绪后,下一步是让 Agent 循环真正跑一轮,并通过日志确认 LLM 请求确实走了 TaoToken 通道。mini-cc 的 QueryEngine 里,run(prompt)是入口,我建议你先用一个最简单的任务触发,比如“读取 package.json 并告诉我项目名”。
在项目里新建test-loop.ts:
import { QueryEngine } from './src/application/QueryEngine'; import { loadConfig } from './src/config'; import { ToolRegistry } from './src/tools/ToolRegistry'; import { MemoryManager } from './src/memory/MemoryManager'; import { OpenAICompatibleProvider } from './src/providers/OpenAICompatibleProvider'; async function main() { const config = loadConfig(); const provider = new OpenAICompatibleProvider({ baseUrl: config.llm.base_url, apiKey: config.llm.api_key, model: config.llm.model, timeout: config.llm.timeout_seconds, }); const toolRegistry = new ToolRegistry(); toolRegistry.registerDefaults(); const memory = new MemoryManager({ shortTermLimit: config.agent.memory_short_term_limit, }); const engine = new QueryEngine(provider, toolRegistry, memory); const result = await engine.run('读取 package.json,告诉我项目名称和版本号'); console.log('最终结果:', result); } main().catch(console.error);跑之前确认logging.loopTrace = true,这样每轮循环都会写日志。执行:
npx ts-node test-loop.ts正常输出会分几段。第一轮:LLM 返回工具调用FileReadTool,参数{"path": "package.json"}。日志里能看到:
[AgentLoop] iteration=1 [LLMRequest] url=https://taotoken.net/api/v1/chat/completions model=claude-sonnet-4-20250514 [LLMResponse] tool_calls=[FileReadTool] [ToolExec] FileReadTool path=package.json duration=12ms第二轮:工具结果喂回 LLM,LLM 返回最终答案,日志里tool_calls=[],循环结束。最终控制台打印:
最终结果: 根据 package.json,项目名称是 my-project,版本号是 1.0.0。如果你看到[LLMRequest] url=https://taotoken.net/api/v1/chat/completions,说明请求确实走了 TaoToken 通道。如果 url 里出现别的域名,说明config.toml没生效,回去检查base_url是不是被环境变量覆盖了。
再验证一个多轮场景:输入“给 package.json 加一个 test 脚本,然后跑一下”。日志里应该看到至少三轮:FileReadTool→FileWriteTool→BashTool。每轮iteration递增,直到max_iterations或任务完成。如果第三轮之后还在循环,检查maxIterations是不是设太大,或者工具描述是不是让模型误解了。
模型对话验证入口:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,你可以先在网页里发同样的 prompt,对比返回的工具调用格式和 mini-cc 日志里的是否一致。不一致的话,多半是 system prompt 里的工具描述格式没对齐。
5. 常见错排查:401、local proxy failed、reading choices、OAuth 逐个对照
Agent 循环跑不起来,报错通常集中在几个地方。我按真实遇到的频率排一下,你对照日志逐条查。
401 Unauthorized。日志里出现[LLMRequest] status=401,说明 Key 不对或没带上。检查三处:config.toml里api_key是不是复制完整(有的 Key 带前缀,别漏);环境变量TAOTOKEN_API_KEY是不是覆盖了配置文件;请求头里Authorization: Bearer sk-xxx有没有拼错。TaoToken 的 Key 在控制台创建后只显示一次,如果丢了就重新建一个。
local proxy failed / connection refused。这个报错说明 mini-cc 尝试连的地址不对。常见原因是base_url写成了https://taotoken.net而漏了/api,或者写成了http://而不是https://。正确写法是https://taotoken.net/api。另外检查本地有没有设置HTTP_PROXY/HTTPS_PROXY环境变量,如果有,先unset再跑。
reading 'choices' of undefined。这是解析 LLM 返回时最常见的错。QueryEngine 里response.choices[0].message拿不到,说明返回体结构不对。两种可能:一是请求根本没成功,返回的是错误 JSON,没有choices字段;二是 Model ID 写错了,TaoToken 返回了错误提示。先看日志里[LLMResponse]的原始 body,如果是{"error": "model not found"},就去控制台核对模型 ID。
OAuth / token expired。如果你用的是 Claude Code 的 OAuth 流程,但配置里又写了 TaoToken 的 Key,两者会冲突。mini-cc 的 provider 初始化时,优先读config.toml的api_key,如果同时存在 OAuth token,可能触发鉴权混乱。解决办法:在config.toml里显式写auth_mode = "api_key",并确保没有残留的 OAuth 缓存文件。
循环不终止 / 达到最大迭代次数。日志里iteration=5后返回 error,说明模型一直在调工具但没收敛。检查工具描述是不是太模糊,比如GetCurrentTime描述写成“获取时间”,模型可能反复调。改成“获取当前系统时间和时区信息,返回精确的本地时间”。另外看memory_short_term_limit是不是太小,导致上下文丢失,模型每轮都“重新开始”。
工具执行超时。日志里[ToolExec] timeout,说明 BashTool 跑太久。settings.json里BashTool的 timeout 默认 300 秒,如果命令确实需要更久,调大这个值,但更推荐把长任务拆成多轮。FileReadTool / FileWriteTool 的 120 秒一般够用。
排查顺序建议:先看[LLMRequest]的 url 和 status,确认通道通;再看[LLMResponse]的 body,确认返回结构;最后看[ToolExec]的 duration 和 error,确认工具层没问题。三层都过了,循环基本就能稳定跑。
6. 语义一致 CTA:把 Agent 循环接进你的本地工作流
到这里,mini-cc 的 Agent 循环和 QueryEngine 协作机制基本拆完了。你手上应该有了可复制的config.toml和settings.json,知道怎么触发循环、怎么看日志、怎么排查 401 和 reading choices 这类错。
接下来看你的使用场景。如果你只是偶尔调试模型、验证 prompt 和工具调用格式,用模型对话入口就够了:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,在网页里直接发消息,对比返回结构。
如果你打算长期跑本地 coding agent、让 Agent 循环在项目里持续工作,建议看 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,它更适合高频调用场景,不用每次担心按量计费。
接入文档在这里:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,里面有完整的 Base URL、鉴权方式、模型列表和错误码说明。控制台入口:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,管理 Key 和查看调用量都在这里。
最后给一个实用技巧:在 mini-cc 的QueryEngine.run()里加一行日志,把每轮的iteration、toolCalls.length、context.tokenCount打出来。跑一段时间后你会发现,大部分任务在 2-3 轮内结束,超过 4 轮的基本是 prompt 太模糊或者工具描述有问题。这个日志比任何调试器都直观。