1. 从 Dola 说起一个数据分析 Agent 到底在做什么腾讯 PCG 大数据平台部做的 Dola是我最近反复研究的一个案例。它做的事情说起来很朴素你把个人数据表丢进去用自然语言描述需求它自己写 SQL、跑数、纠错、用 Python 画图最后给你一份分析报告。股票回测、异动归因、房价预测这类任务全程不用你写一行代码。但拆开看Dola 这类 AI 智能体Agent的本质并不神秘。它等于一个会思考的大脑大模型加上记忆系统、工具调用能力和任务规划能力。大模型负责理解你的意图、决定下一步做什么工具负责真正去执行 SQL 查询、Python 计算记忆负责让它别聊到第三轮就忘了你第一轮说过什么规划负责把帮我分析一下这个月销售异动拆成取数、对比、归因、出图若干步骤。这套链路对程序员来说价值在于它是可复制的。你不需要从零造一个 Dola但你可以用同样的架构思路给自己搭一个能跑通工具调用的智能体开发环境。而跑通环境的第一步往往卡在最不起眼的地方模型 API 的接入。多个模型、多个 Key、多个 base_url 来回切换代码里到处硬编码调试一次换一次配置非常消耗耐心。这篇就按能跟做的标准来写先讲清楚 Agent 的核心链路再给出一套统一的 Key/API 通道配置骨架含 settings.json 和 config.toml 示例最后用可复制的验证动作确认你的智能体工具调用链路真的通了。适合已经会写 Python、想动手做 Agent 但还没跑通环境的开发者。2. 前置准备用 TaoToken 统一模型接入通道在写 Agent 之前先把模型调用这层抽象出来。原因很简单Agent 开发过程中你会频繁切换模型——规划用推理强的工具调用用 function calling 稳的总结用便宜的。如果每个模型都单独配 Key 和地址代码会变得很难维护。TaoToken 在这里扮演的角色是一个统一的 API 通道。你只需要一个 Key、一个 base_url就能在同一个接口下调用不同模型Agent 代码里不用关心底层换的是哪个供应商。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 这个不加 UTM。具体操作路径先去控制台创建 API Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。创建完把 Key 复制出来后面配置里要用。如果你还不确定选哪个模型可以先去模型对话页面试一下效果地址是 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 直接对话验证模型能力确认没问题再写进配置。注意API Key 属于敏感凭证不要写死在代码里提交到 Git。下面所有配置示例都用环境变量占位你本地替换成真实值即可。对于要长期做编码类 Agent、或者跑自动化任务的场景可以了解一下 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 它更适合高频调用的开发工作流。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 遇到参数细节可以对照查。3. 可复制配置settings.json 与 config.toml 骨架Agent 项目通常有两类配置需求一类是应用层的模型参数用 JSON 存一类是工具链或 CLI 工具的配置用 TOML 存。下面给两套骨架你直接改值就能用。3.1 settings.json应用层模型配置这个文件放在项目根目录负责告诉 Agent 用哪个 base_url、哪个 Key、默认模型是谁。{ llm: { provider: taotoken, base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, default_model: claude-sonnet-4-20250514, planner_model: claude-sonnet-4-20250514, tool_model: gpt-4o-mini, timeout_seconds: 60, max_retries: 3 }, agent: { max_steps: 12, enable_reflection: true, tool_choice: auto, memory: { short_term_turns: 8, summary_threshold_tokens: 6000 } }, tools: { enabled: [sql_query, python_exec, http_fetch], approval_required: [python_exec] } }几个关键点解释一下。base_url统一指向 TaoToken 的 API 入口这样你换模型时不用改地址。api_key_env写的是环境变量名不是 Key 本身代码里用os.getenv(TAOTOKEN_API_KEY)读取。planner_model和tool_model分开配置是因为规划任务和工具调用对模型能力要求不同分开配更省钱也更稳。tool_choice设为auto表示让模型自己决定是否调用工具这是 Agent 的基础行为。3.2 config.toml工具链与 CLI 配置如果你用的是支持 TOML 配置的 Agent 框架或 CLI 工具下面这套可以直接参考。[api] base_url https://taotoken.net/api api_key ${TAOTOKEN_API_KEY} default_model claude-sonnet-4-20250514 [agent] max_iterations 12 verbose true [agent.memory] short_term_window 8 long_term_enabled true vector_store local [[tools]] name sql_query description 执行只读 SQL 查询输入为 SQL 字符串返回结果集 input_schema { type object, properties { sql { type string } }, required [sql] } [[tools]] name python_exec description 在沙箱中执行 Python 代码用于数据处理与可视化 input_schema { type object, properties { code { type string } }, required [code] }TOML 里${TAOTOKEN_API_KEY}是环境变量引用语法具体是否支持取决于你用的框架不支持的话就在启动脚本里 export 后再读取。工具定义部分我特意写了input_schema这是 function calling 的关键——模型需要知道每个工具接受什么参数才能正确生成调用指令。3.3 环境变量设置export TAOTOKEN_API_KEY你的真实KeyWindows 下用set TAOTOKEN_API_KEY你的真实Key或者写进系统环境变量。设置完可以用echo $TAOTOKEN_API_KEY确认一下。4. 验证请求确认工具调用链路真的通了配置写完不代表能用必须做一次端到端的验证。下面这段 Python 代码会发起一次带工具定义的请求观察模型是否返回了工具调用指令。import os import json from openai import OpenAI client OpenAI( api_keyos.getenv(TAOTOKEN_API_KEY), base_urlhttps://taotoken.net/api ) tools [ { type: function, function: { name: get_weather, description: 查询指定城市的天气, parameters: { type: object, properties: { city: {type: string, description: 城市名称} }, required: [city] } } } ] messages [ {role: user, content: 帮我查一下上海现在的天气} ] resp client.chat.completions.create( modelclaude-sonnet-4-20250514, messagesmessages, toolstools, tool_choiceauto ) msg resp.choices[0].message print(finish_reason:, resp.choices[0].finish_reason) print(tool_calls:, json.dumps(msg.tool_calls, ensure_asciiFalse, indent2) if msg.tool_calls else None)跑通后你应该看到finish_reason是tool_calls并且tool_calls里包含get_weather和参数{city: 上海}。这说明模型正确理解了工具定义并生成了调用指令Agent 的工具调用链路是通的。接下来把工具执行结果回传完成一次完整闭环# 模拟工具执行结果 tool_result {city: 上海, weather: 多云, temp: 28C} messages.append(msg) messages.append({ role: tool, tool_call_id: msg.tool_calls[0].id, content: json.dumps(tool_result, ensure_asciiFalse) }) final client.chat.completions.create( modelclaude-sonnet-4-20250514, messagesmessages, toolstools ) print(final.choices[0].message.content)如果这一步模型返回了类似上海现在多云气温 28 摄氏度的自然语言回复恭喜你一个最小可用的 Agent 工具调用循环就跑通了。Dola 那种复杂分析 Agent本质上就是这个循环加上更丰富的工具集、更完善的记忆和规划模块。5. 本篇常见错排查报错一401 Unauthorized。九成是 Key 没读到。先确认echo $TAOTOKEN_API_KEY有值再确认代码里用的是os.getenv而不是硬编码的空字符串。如果你在 IDE 里跑注意 IDE 可能没继承你终端里 export 的环境变量重启 IDE 或在运行配置里单独设置。报错二404 或 model not found。检查base_url是不是写成了https://taotoken.net/api/带尾斜杠有些 SDK 对尾斜杠敏感。另外确认模型名拼写正确模型名区分大小写和版本号。报错三模型不返回 tool_calls直接给了文本回复。这通常有两个原因。一是tool_choice设成了none或者没传 tools 参数二是你的工具描述太模糊模型判断不需要调用工具。把description写具体一点明确说明当用户询问天气时必须调用此工具。报错四tool_call_id 对不上。回传工具结果时tool_call_id必须和模型返回的id完全一致。如果你手动构造 messages 数组很容易在这里出错。建议直接把msg对象 append 进去而不是手动拼字典。报错五多轮对话后模型失忆。这是上下文窗口超限的典型表现。检查你的short_term_turns设置超过阈值的历史消息要做摘要压缩而不是无限往 messages 里塞。这也是为什么配置里要单独配 memory 模块。报错六工具执行超时导致整个 Agent 卡死。给每个工具调用加超时配置里的timeout_seconds就是干这个的。工具执行失败时把错误信息作为 tool 结果回传给模型让它自己决定重试还是换方案而不是直接抛异常中断。6. 下一步把最小循环扩展成真正的 Agent跑通上面的验证后你已经有了 Agent 的骨架。接下来要补的是三块规划模块让模型先拆任务再执行、记忆模块短期对话加长期向量检索、以及更丰富的工具集SQL、Python、HTTP 请求。如果你要做的偏编码类 Agent比如自动改代码、跑测试、提交 PR建议走 Coding Plan 那条线地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 它在高频工具调用场景下更顺。如果你还在选模型阶段先去模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 实测几轮确认模型在你业务场景下的表现再定。Key 的管理和创建在控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 接入参数细节对照文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。我自己的习惯是每加一个新工具就先单独写一段验证代码确认模型能正确生成调用参数再集成进主循环。这样出问题时排查范围小不会在复杂链路里迷路。工具描述里的description和parameters值得反复打磨它们直接决定模型调用的准确率比调 prompt 的收益还大。