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_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api (这个不加 UTM)。
具体操作路径:
先去控制台创建 API Key,地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 。创建完把 Key 复制出来,后面配置里要用。如果你还不确定选哪个模型,可以先去模型对话页面试一下效果,地址是 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite ,直接对话验证模型能力,确认没问题再写进配置。
注意:API Key 属于敏感凭证,不要写死在代码里提交到 Git。下面所有配置示例都用环境变量占位,你本地替换成真实值即可。
对于要长期做编码类 Agent、或者跑自动化任务的场景,可以了解一下 Coding Plan,地址是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite ,它更适合高频调用的开发工作流。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,遇到参数细节可以对照查。
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="你的真实Key"Windows 下用set TAOTOKEN_API_KEY=你的真实Key,或者写进系统环境变量。设置完可以用echo $TAOTOKEN_API_KEY确认一下。
4. 验证请求:确认工具调用链路真的通了
配置写完不代表能用,必须做一次端到端的验证。下面这段 Python 代码会发起一次带工具定义的请求,观察模型是否返回了工具调用指令。
import os import json from openai import OpenAI client = OpenAI( api_key=os.getenv("TAOTOKEN_API_KEY"), base_url="https://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( model="claude-sonnet-4-20250514", messages=messages, tools=tools, tool_choice="auto" ) msg = resp.choices[0].message print("finish_reason:", resp.choices[0].finish_reason) print("tool_calls:", json.dumps(msg.tool_calls, ensure_ascii=False, indent=2) 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_ascii=False) }) final = client.chat.completions.create( model="claude-sonnet-4-20250514", messages=messages, tools=tools ) 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_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite ,它在高频工具调用场景下更顺。如果你还在选模型阶段,先去模型对话页面 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 实测几轮,确认模型在你业务场景下的表现再定。Key 的管理和创建在控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,接入参数细节对照文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
我自己的习惯是,每加一个新工具,就先单独写一段验证代码确认模型能正确生成调用参数,再集成进主循环。这样出问题时排查范围小,不会在复杂链路里迷路。工具描述里的description和parameters值得反复打磨,它们直接决定模型调用的准确率,比调 prompt 的收益还大。