news 2026/9/29 8:17:40

0基础学会Agent Harness工程(前置知识二):用Python从ReAct到Agent Loop的Tool Calling配置骨架

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
0基础学会Agent Harness工程(前置知识二):用Python从ReAct到Agent Loop的Tool Calling配置骨架

1. 从一次“看起来很简单”的任务说起

假设你让 AI 帮你做这件事:看看当前目录有哪些文件,找到项目入口,然后用两三句话说明这个项目怎么启动。听起来不难对吧?但如果你只调用一次模型,它大概率会给你一段“看起来合理”的猜测——比如“通常入口是 main.py,运行 python main.py 即可”。问题是,它根本没看过你的目录,不知道你的项目用的是 src/ 布局,也不知道入口其实写在 pyproject.toml 的 [project.scripts] 里。

这就是本篇要解决的核心问题:模型不能一次决定完整任务。原因不是它不会规划,而是开放任务里的关键事实还没出现。模型在看到目录之前,不知道 README.md 是否存在;看到目录之后,也可能发现真正的入口藏在配置文件里。任务不是“一次猜对五步”,而是“每获得一份新事实,就重新判断下一步”。

Agent Harness 工程要做的,就是把这个“重新判断”的过程变成可运行、可控制、可回填的循环。本篇是前置知识二,聚焦零基础读者用 Python 搭建 Agent Harness 的 Tool Calling 配置骨架:从 ReAct 的推理-行动循环讲到 Agent Loop 的落地,交付可复制的 config.toml 与 settings.json 骨架、TaoToken 统一 Key/API 通道接入 AI 工具的配置片段,并给出运行验证动作,确认循环能正确调用工具并回填结果。适合刚接触 Agent、想搞懂“工具调用到底怎么接进循环”的开发者。

2. 先搞懂 ReAct、Tool Calling、Agent Loop 的分工

很多教程把这三个词混着用,初学者容易当成三个名字。它们确实协作,但分别处于不同层。

ReAct 是方法范式。它回答的是:为什么推理与行动要交错,外部反馈怎样更新后续判断。ReAct 论文关注的核心问题是,语言模型的推理能力和行动能力过去经常被分开研究——只推理时模型困在自身信息里,只行动时又缺少对目标的高层判断。ReAct 让 reasoning traces 与 task-specific actions 交错出现,使行动取得的外部信息支持后续推理。

Tool Calling 是结构化交接。它回答的是:模型怎样把“我想调用某个工具”表达成程序可以解析的数据。相比让模型在自然语言里写“请执行 ls”,结构化调用会明确工具名、参数和调用标识。但要注意,Tool Calling 只表达调用意图——模型返回tool_name = "list_directory",不代表目录已经读取。只有 Harness 找到 handler、通过权限检查并实际执行后,环境才会产生 observation。

Agent Loop 是运行控制结构。它回答的是:谁保存当前状态,谁调用模型,谁执行工具,谁回填结果,以及何时继续或停止。Loop 把模型调用、工具执行和 observation 回填接成可重复过程,通常属于 Harness,而不是模型参数的一部分。

三者是组合关系,不是同义关系。一个系统可以有 Tool Calling 却没有 Agent Loop——模型产生一次工具调用,程序执行后直接把结果返回用户,不再询问模型。一个系统也可以有循环却不采用 ReAct——程序固定调用模型三次做同一个改写任务,有 for 循环却没有动态选择 action。真正要检查的是:模型是否获得了新状态,并据此控制后续过程。

3. TaoToken 前置:统一 Key 与 API 通道

在写循环之前,先把模型通道准备好。TaoToken 提供统一的 API 通道,让你用一套 Key 接入多种模型,省去为每个工具单独配置的麻烦。官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api 。

你需要先拿到 API Key。进入控制台创建:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,然后在 API Keys 页面生成密钥:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。生成后复制保存,后面配置里会用到。

如果你只是想先验证模型能不能正常对话,可以直接用模型对话页面测试:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。这一步能帮你排除“是 Key 问题还是代码问题”。

注意:API Key 不要硬编码进提交到 Git 的代码里。用环境变量或本地配置文件,并在 .gitignore 里排除。

4. 可复制配置:config.toml 与 settings.json 骨架

下面给出两个配置文件骨架。config.toml 用于 Python 侧读取模型通道和循环参数,settings.json 用于工具与运行时的结构化设置。你可以直接复制后按需修改。

4.1 config.toml

# config.toml - Agent Harness 基础配置 [model] # TaoToken 统一 API 端点 base_url = "https://taotoken.net/api" # 从环境变量读取,避免硬编码 api_key_env = "TAOTOKEN_API_KEY" # 模型名称,按你实际使用的填写 model = "claude-sonnet-4-20250514" # 单次请求超时(秒) timeout = 60 [loop] # 最大循环轮次,防止无限调用 max_turns = 8 # 单轮最大工具调用数 max_tool_calls_per_turn = 4 # 是否在工具失败时回填 observation feed_error_as_observation = true [tools.bash] enabled = true # 允许执行的命令白名单前缀,最小示例 allowed_prefixes = ["ls", "cat", "pwd", "find"] # 单次命令超时(秒) timeout = 15 # 输出截断长度(字符) max_output_chars = 4000

4.2 settings.json

{ "runtime": { "state_file": "./.agent_state.json", "log_level": "info", "log_tool_calls": true }, "tools": { "bash": { "handler": "handlers.bash_handler", "description": "执行受限的 shell 命令并返回输出", "parameters": { "type": "object", "properties": { "command": { "type": "string", "description": "要执行的命令,例如 ls -la" } }, "required": ["command"] } } }, "stop_conditions": { "on_final_answer": true, "on_max_turns": true, "on_tool_error_limit": 3 } }

这两个文件的分工是:config.toml 管通道和循环参数,settings.json 管工具描述和停止条件。工具描述里的 parameters 就是给模型看的 Tool Schema,模型据此生成结构化调用。

4.3 接入片段:读取配置并初始化客户端

import os import json import tomllib from openai import OpenAI def load_config(path="config.toml"): with open(path, "rb") as f: return tomllib.load(f) def load_settings(path="settings.json"): with open(path, "r", encoding="utf-8") as f: return json.load(f) cfg = load_config() settings = load_settings() client = OpenAI( base_url=cfg["model"]["base_url"], api_key=os.environ[cfg["model"]["api_key_env"]], ) # 把 settings.json 里的工具描述转成 API 需要的 tools 数组 tools = [] for name, spec in settings["tools"].items(): tools.append({ "type": "function", "function": { "name": name, "description": spec["description"], "parameters": spec["parameters"], }, })

这段代码做完三件事:读配置、建客户端、把工具描述转成模型可见的 tools 数组。注意 tools 不是 Python 函数本身,而是模型可见的能力说明。

5. 运行验证:确认循环能调用工具并回填结果

配置就绪后,写一个最小 agent_loop() 验证反馈链是否闭合。核心是四步:调用模型、判断是否有 tool_calls、执行工具、把结果回填到 messages。

import json def bash_handler(command: str) -> str: import subprocess try: result = subprocess.run( command, shell=True, capture_output=True, text=True, timeout=15 ) out = result.stdout or result.stderr return out[:4000] except Exception as e: return f"ERROR: {e}" def agent_loop(user_goal: str): messages = [ {"role": "system", "content": "你是一个会使用工具的助手。"}, {"role": "user", "content": user_goal}, ] for turn in range(cfg["loop"]["max_turns"]): resp = client.chat.completions.create( model=cfg["model"]["model"], messages=messages, tools=tools, ) msg = resp.choices[0].message messages.append(msg) # 没有工具调用,说明正常完成 if not msg.tool_calls: return msg.content # 有工具调用,逐个执行并回填 for call in msg.tool_calls: args = json.loads(call.function.arguments) result = bash_handler(args["command"]) messages.append({ "role": "tool", "tool_call_id": call.id, "content": result, }) return "达到最大轮次,已停止。" print(agent_loop("查看当前目录有哪些 Python 文件,并说明入口"))

运行后你会看到 messages 逐步增长:先是用户目标,然后是 assistant 的 tool_calls,接着是 role="tool" 的 observation,再进入下一轮。如果模型不再请求工具,就返回最终回答。这就是完整的反馈链:模型提出 action → Harness 执行 → observation 回填 → 模型根据新状态继续。

验证成功的标志有三个:一是日志里能看到 tool_calls 被解析;二是 role="tool" 消息的 tool_call_id 与调用配对;三是模型在拿到目录结果后不再重复请求同一个动作。

6. 本篇常见错排查

错误一:模型重复请求同一个动作。多半是 observation 没有回填,或者回填内容不够清楚。检查 messages 里是否真的追加了 role="tool" 消息,以及 tool_call_id 是否匹配。

错误二:工具执行成功但调用 ID 丢失。模型无法确认哪份结果对应哪个 action,并行调用时更明显。确保每个 tool 消息都带上对应的 tool_call_id。

错误三:模型给出普通回答,程序却继续循环。说明退出条件没绑定“没有工具调用”的状态。检查if not msg.tool_calls这个分支是否生效。

错误四:工具失败后程序直接崩溃。说明错误没有形成受控 observation。把异常捕获后作为字符串回填,让模型看到失败并调整,而不是让进程挂掉。

错误五:API 返回 401 或连接失败。先确认环境变量 TAOTOKEN_API_KEY 已设置,再确认 base_url 是 https://taotoken.net/api 。如果还是不通,去模型对话页面单独测一次,排除是 Key 问题还是代码问题。

错误六:循环跑满 max_turns 还没结束。可能是工具返回信息不足,模型一直在试探。适当提高单次输出长度,或在 system 里明确告诉模型“拿到足够信息就给出最终回答”。

排障时如果怀疑是接入配置问题,可以对照接入文档检查:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。如果是要长期跑编码类 Agent 任务,建议了解 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它更适合持续性的编码与 Agent 场景。如果你用的是 Claude Code 这类工具,可以参考 Anthropic 接入说明:https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude-code-anthropic&utm_campaign=rewrite 。

7. 下一步:把骨架跑起来,再谈扩展

到这里,你已经有了一个能跑的最小 Agent Loop:config.toml 管通道和循环参数,settings.json 管工具描述和停止条件,agent_loop() 负责调用、执行、回填和退出。它只用一个 Bash handler,故意把其他能力拿掉,就是为了让你能清楚看到反馈链的每一环。

接下来可以做的第一件事,是把 max_turns 调小到 2,观察模型在信息不足时会不会重复请求;再把 feed_error_as_observation 打开,故意让命令失败,看模型怎么根据错误调整。这两个实验能帮你确认自己真的理解了“observation 改变后续判断”这件事。

等你确认循环稳定后,再考虑加读文件、写文件、搜索这些工具。但记住:能够执行和允许执行是两回事。字符串黑名单和简单超时都不构成完整权限模型,生产环境还需要审批、沙箱和审计。先把最小闭环跑通,比一上来堆功能更重要。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 8:15:51

CoppeliaSim 4.2 (V-REP) 添加3D轨迹:用 TaoToken 统一 Key 打通脚本配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 8:09:45

基于springboot的厨具用品线上销售平台的设计与实现

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 1. 项目背景与意义 随着互联网技术的快速发展和电子商务的普及,线上购物已成为人们日常生活中不可或缺的一部分。厨具用品作为家庭生活的重要消费品&#xf…

作者头像 李华