尧图精选

自主学习智能体实战:用TaoToken统一Key打通工具调用与记忆闭环

🕒 发布时间:2026/10/1 6:58:31 📁 来源:尧图网络
1. 从零跑通自主学习智能体为什么卡在工具调用与记忆闭环很多人第一次尝试搭一个“自主学习智能体”跑通一次对话就以为成功了结果第二天再问它昨天做过什么它一脸茫然让它调用一个搜索工具它把参数拼错重试三次后直接放弃。问题不在模型不够聪明而在于工具调用和记忆读写这两条链路没有真正闭环。所谓自主学习智能体指的是能在执行任务过程中记录经验、在下一次任务里复用经验的 Agent。它和普通聊天机器人的区别在于普通机器人每次对话都是白纸而自主学习智能体需要把“我调用了哪个工具、参数是什么、结果如何、下次该怎么改”写进记忆并在后续任务中读出来。这个读写循环一旦断开Agent 就退化成一次性脚本。我见过最常见的三种断法。第一种是工具注册表写死在代码里新增一个工具要改三处Agent 根本不知道有哪些工具可用。第二种是记忆只存在内存里进程一重启全丢所谓“自我迭代”无从谈起。第三种是模型侧和工具侧用的 Key 不统一调用工具时鉴权失败Agent 收到 401 后不会自我修正只会重复报错。这篇内容面向想从零跑通一个可自我迭代 Agent 的开发者。我会给出可复制的 TaoToken 统一 Key 配置、工具注册与记忆读写示例并附上一轮自主任务执行的验证动作与日志检查点。你跟着做能拿到一个最小可用的闭环Agent 自己决定调哪个工具、把结果写进记忆、下一轮读出来继续用。核心检索词先明确自主学习智能体、工具调用、记忆闭环、统一 Key 配置。适合谁适合已经会写 Python、调过至少一个 LLM API、但还没把 Agent 的“学习”环节跑通的开发者。不需要你懂强化学习也不需要你部署向量数据库先用文件加 JSON 把闭环跑起来。我试过用三四个不同的 Key 分别管对话、管工具、管记忆结果是配置文件里到处是密钥换一个环境就要改半天。后来统一到一个 Key 上工具调用和记忆读写走同一个鉴权入口排障时只看一个地方效率高很多。下面从 TaoToken 的前置配置开始。2. TaoToken 统一 Key 前置配置与工具注册表初始化TaoToken 在这里的角色是统一入口你用同一个 Key 去访问模型对话、工具调用和记忆相关的接口不用为每个能力单独申请凭证。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 注意 API 地址不带 UTM 参数配置时别把推广参数拼进去。前置准备分三步。第一步在控制台创建一个 API Key路径是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 创建后复制保存它只显示一次。第二步确认你要用的模型 ID可以在模型对话页 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里先手动试一次确认模型能正常返回。第三步把 Key 写进环境变量不要硬编码在代码里。工具注册表是自主学习智能体的“能力清单”。我建议用一个 JSON 文件描述每个工具的名称、描述、参数 schema 和调用地址。Agent 在规划阶段读这个文件就知道自己有哪些工具可用。下面是一个最小注册表示例保存为 tools_registry.json{ tools: [ { name: web_search, description: 根据关键词搜索网页返回前三条摘要, parameters: { type: object, properties: { query: { type: string, description: 搜索关键词 } }, required: [query] }, endpoint: https://taotoken.net/api/tools/web_search }, { name: memory_write, description: 把一条经验写入长期记忆, parameters: { type: object, properties: { key: { type: string }, value: { type: string } }, required: [key, value] }, endpoint: https://taotoken.net/api/memory/write }, { name: memory_read, description: 按 key 读取长期记忆, parameters: { type: object, properties: { key: { type: string } }, required: [key] }, endpoint: https://taotoken.net/api/memory/read } ] }注意 endpoint 里的路径是示例结构实际调用时以接入文档为准文档入口在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。注册表的作用是让 Agent 在运行时动态发现工具而不是把工具名写死在 prompt 里。这样你新增一个工具只改 JSON不改代码。环境变量配置建议用 .env 文件配合 python-dotenv 读取TAOTOKEN_API_KEYsk-你的key TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_MODEL_ID你的模型ID读取代码import os from dotenv import load_dotenv load_dotenv() API_KEY os.getenv(TAOTOKEN_API_KEY) BASE_URL os.getenv(TAOTOKEN_BASE_URL) MODEL_ID os.getenv(TAOTOKEN_MODEL_ID) assert API_KEY, 缺少 TAOTOKEN_API_KEY assert BASE_URL, 缺少 TAOTOKEN_BASE_URL assert MODEL_ID, 缺少 TAOTOKEN_MODEL_ID这三件套——Base URL、Key、Model ID——是后面所有请求的基础。如果你用的是 Claude Code 或 Cline 这类工具配置项名称可能不同但本质都是这三样。比如 Claude Code 的 settings 里需要填 ANTHROPIC_BASE_URL、ANTHROPIC_API_KEY、ANTHROPIC_MODEL对应关系是一样的。Cline 的 MCP 配置里则是 baseUrl、apiKey、modelId。Codex 的 auth.json 里是 base_url、api_key、model。不管哪个工具缺一个都会报鉴权或模型找不到的错。工具注册表初始化完成后Agent 的“能力边界”就确定了。接下来要解决的是记忆读写。记忆闭环的关键是每次工具调用后把“调用意图、参数、结果、是否成功”写进记忆下一轮任务开始时先读记忆看有没有可复用的经验。下面给出可复制的配置和代码。3. 可复制的工具调用与记忆读写配置片段这一节给出能直接跑的配置和代码。先看记忆存储的结构。我用一个 JSON 文件做最小实现路径放在项目根目录的 memory_store.json。每条记忆包含 key、value、timestamp、source_task 四个字段。key 用“工具名:参数摘要”的格式方便检索。{ memories: [ { key: web_search:python asyncio, value: 搜索到三条结果其中第二条提到 asyncio.gather 的异常处理要用 return_exceptionsTrue, timestamp: 2026-02-20T10:00:00Z, source_task: task_001 } ] }记忆读写的封装代码import json import os from datetime import datetime, timezone MEMORY_FILE memory_store.json def load_memory(): if not os.path.exists(MEMORY_FILE): return {memories: []} with open(MEMORY_FILE, r, encodingutf-8) as f: return json.load(f) def save_memory(memory): with open(MEMORY_FILE, w, encodingutf-8) as f: json.dump(memory, f, ensure_asciiFalse, indent2) def write_memory(key, value, source_task): memory load_memory() memory[memories].append({ key: key, value: value, timestamp: datetime.now(timezone.utc).isoformat(), source_task: source_task }) save_memory(memory) def read_memory(key_prefix): memory load_memory() return [m for m in memory[memories] if m[key].startswith(key_prefix)]工具调用的封装统一走 TaoToken 的 API 入口带上同一个 Keyimport requests def call_tool(tool_name, params): registry json.load(open(tools_registry.json, encodingutf-8)) tool next((t for t in registry[tools] if t[name] tool_name), None) if not tool: return {error: f工具 {tool_name} 未注册} headers { Authorization: fBearer {API_KEY}, Content-Type: application/json } payload {tool: tool_name, parameters: params} resp requests.post(tool[endpoint], headersheaders, jsonpayload, timeout30) if resp.status_code ! 200: return {error: fHTTP {resp.status_code}, body: resp.text} return resp.json()把工具调用和记忆写入串起来形成一个“执行-记录”函数def execute_and_remember(task_id, tool_name, params): result call_tool(tool_name, params) key f{tool_name}:{json.dumps(params, ensure_asciiFalse)[:50]} if error in result: write_memory(key, f失败: {result[error]}, task_id) else: write_memory(key, f成功: {json.dumps(result, ensure_asciiFalse)[:200]}, task_id) return result这段代码的要点是无论成功还是失败都写记忆。失败的经验同样有价值Agent 下一轮读到“上次这个参数失败了”就会尝试换参数。这就是“自我迭代”的最小实现。如果你用 Cline 的 MCP 配置对应的 settings 片段长这样{ mcpServers: { taotoken-tools: { command: python, args: [tool_server.py], env: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: sk-你的key, TAOTOKEN_MODEL_ID: 你的模型ID } } } }注意这里三件套齐全Base URL、Key、Model ID。少任何一个MCP 服务启动后调用工具都会失败。Claude Code 的 settings.json 类似字段名换成 ANTHROPIC_ 前缀。Codex 的 auth.json 则是{ base_url: https://taotoken.net/api, api_key: sk-你的key, model: 你的模型ID }配置写完后先别急着跑完整任务。用一个最小验证请求确认链路通。下面进入验证环节。4. 验证请求与成功结果一轮自主任务执行的日志检查点验证分两步。第一步单独验证模型对话能通。第二步跑一轮带工具调用和记忆写入的自主任务检查日志。先验证模型对话import requests def chat_once(prompt): headers { Authorization: fBearer {API_KEY}, Content-Type: application/json } payload { model: MODEL_ID, messages: [{role: user, content: prompt}] } resp requests.post(f{BASE_URL}/chat/completions, headersheaders, jsonpayload, timeout60) return resp.status_code, resp.json() status, data chat_once(用一句话说明什么是工具调用) print(status) print(data)期望结果status 为 200data 里有 choices 字段choices[0].message.content 是一句正常的中文回答。如果 status 是 401说明 Key 不对如果是 404说明 Base URL 或路径不对如果返回体里没有 choices说明模型 ID 可能写错或者请求体格式不对。第二步跑一轮自主任务。任务描述“搜索 Python asyncio 的异常处理方式把结论写入记忆然后读出来确认。”执行代码def run_autonomous_task(task_id, goal): print(f[{task_id}] 目标: {goal}) # 1. 先读记忆看有没有可复用经验 existing read_memory(web_search:python asyncio) print(f[{task_id}] 读到 {len(existing)} 条历史记忆) # 2. 调用搜索工具 search_result execute_and_remember(task_id, web_search, {query: python asyncio 异常处理}) print(f[{task_id}] 搜索返回: {json.dumps(search_result, ensure_asciiFalse)[:200]}) # 3. 把结论写入记忆 conclusion asyncio.gather 默认遇到异常会取消其他任务需要 return_exceptionsTrue 才能收集所有结果 execute_and_remember(task_id, memory_write, {key: asyncio:异常处理, value: conclusion}) # 4. 读出来确认 read_back read_memory(asyncio:异常处理) print(f[{task_id}] 读回记忆: {json.dumps(read_back, ensure_asciiFalse)}) return read_back run_autonomous_task(task_001, 搜索并记忆 asyncio 异常处理)日志检查点有四个。第一个检查点读到 0 条历史记忆说明这是首次执行记忆文件为空正常。第二个检查点搜索返回里应该有实际内容如果返回{error: HTTP 401}说明工具调用的鉴权失败回去检查 Key 是否和模型对话用的是同一个。第三个检查点读回记忆时应该能看到刚才写入的 conclusion如果为空说明 memory_write 没成功检查 endpoint 和参数格式。第四个检查点memory_store.json 文件里应该新增了两条记录一条是 web_search 的一条是 memory_write 的。成功的结果是控制台打印出搜索摘要memory_store.json 里能看到两条新记忆再次运行同一个任务时第一个检查点会显示读到 1 条历史记忆说明记忆闭环生效了。这就是“自主学习”的最小证据Agent 第二次执行时能读到第一次的经验。如果你在这一步看到reading choices相关的报错通常是因为返回体解析时直接取了 choices[0]但实际返回结构不同。先打印完整 resp.json() 看结构再调整取值路径。如果看到local proxy failed说明请求根本没发出去检查 BASE_URL 是否被本地代理拦截或者环境变量里有没有残留的代理配置。验证通过后下面把常见的坑列出来方便你对照排查。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth排障的核心思路是先确认请求发出去了没有再确认鉴权过了没有最后确认返回体解析对不对。下面按报错类型逐一对照。401 Unauthorized。这是最常见的。原因有三种Key 没读到、Key 写错、Key 和 Base URL 不匹配。排查动作在代码里打印API_KEY[:8]和BASE_URL确认环境变量加载成功。如果用的是 Claude Code 或 Cline检查 settings 里的字段名是否拼错比如把ANTHROPIC_API_KEY写成ANTHROPIC_KEY。三件套里 Key 是最容易出错的建议复制后先粘到模型对话页手动试一次。local proxy failed。这个报错说明请求在本地就被拦截了根本没到服务端。常见原因是系统里配了 HTTP_PROXY 或 HTTPS_PROXY 环境变量requests 库自动走了代理。排查动作在代码开头加os.environ.pop(HTTP_PROXY, None)和os.environ.pop(HTTPS_PROXY, None)或者显式设置proxies{http: None, https: None}。另外检查 BASE_URL 是否被误写成带端口号的本地地址。reading choices 相关报错。典型表现是KeyError: choices或TypeError: NoneType object is not subscriptable。原因是返回体结构和预期不一致。排查动作先打印resp.status_code和resp.text看服务端到底返回了什么。如果是 200 但没有 choices可能是模型 ID 不对服务端返回了错误信息但状态码仍是 200。如果是 401返回体里是错误详情自然没有 choices。所以看到这个报错先回头看状态码。OAuth 相关报错。如果你用的是 Claude Code 或 Codex 这类带 OAuth 流程的工具可能会看到OAuth token expired或invalid_grant。原因是工具尝试用 OAuth 登录而不是 API Key。排查动作在配置里显式指定用 API Key 模式Claude Code 里设置ANTHROPIC_API_KEY并确保没有同时配置 OAuth 相关字段。Codex 的 auth.json 里只保留 base_url、api_key、model 三个字段删掉其他认证相关配置。模型找不到。报错通常是model not found或invalid model。原因是 Model ID 写错或者你的账号没有该模型的权限。排查动作去模型对话页手动选一次模型确认能正常返回然后把页面显示的模型 ID 原样复制到配置里。工具注册表读不到。报错是工具 xxx 未注册。原因是 tools_registry.json 路径不对或者 JSON 格式有误。排查动作用json.load加载后打印len(registry[tools])确认工具数量。如果路径是相对路径注意工作目录是否和预期一致。记忆写入后读不到。原因是写入和读取用的 key 不一致或者文件被覆盖。排查动作写入后立即打印 memory_store.json 的完整内容确认记录存在。读取时用startswith做前缀匹配避免 key 拼写差异导致匹配失败。上面这些错大部分在第一次配置时都会遇到至少一个。我的建议是先用最小请求验证模型对话再验证单个工具调用最后跑完整任务。每步都打印状态码和返回体不要等整个任务跑完才看日志。这样定位问题快很多。排障过程中如果需要查接口细节接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API Key 管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。这两个页面建议收藏配置和排障时反复用得到。6. 从最小闭环到持续迭代下一步怎么扩展最小闭环跑通后你已经有了一个能读记忆、能调工具、能把结果写回记忆的 Agent。接下来扩展的方向有三个。第一个方向是记忆检索从前缀匹配升级到语义检索。现在的 read_memory 用 startswith 做匹配只能按 key 前缀找。你可以把记忆向量化用余弦相似度找最相关的几条。但注意这一步不是必须的很多场景下前缀匹配已经够用。先跑通再优化。第二个方向是工具注册表从静态 JSON 升级到动态发现。现在新增工具要改 JSON 文件未来可以让 Agent 自己根据任务需求生成工具描述并注册。这需要模型有较强的结构化输出能力建议先用固定 schema 约束输出格式。第三个方向是任务循环从单轮升级到多轮。现在的 run_autonomous_task 只执行一轮你可以加一个循环读记忆、规划、调工具、写记忆、判断是否完成、未完成则继续。判断完成的条件可以用模型自己评估也可以设最大轮数防止死循环。如果你打算长期跑编码类或 Agent 类任务可以了解 Coding Plan入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。它适合需要持续调用模型、频繁读写记忆的场景。如果只是验证模型能力模型对话页就够用。最后给一个实用技巧每次任务执行前把 memory_store.json 备份一份命名带上时间戳。这样当 Agent 写入错误记忆导致后续任务跑偏时你可以回滚到上一个正常状态。记忆是自主学习智能体的核心资产别让它被一次错误写入污染。跑通闭环只是开始管好记忆才是长期可用的关键。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →