☰
从 Loop 到 Graph:用 LangGraph 搭建多 Agent 协作骨架,TaoToken 配置你跟上了吗?
2026/9/29 21:16:34 网站建设 项目流程

1. 从 Loop 到 Graph:多 Agent 协作到底变了什么

如果你最近在折腾 Agent,大概率会撞上同一个困惑:昨天还在调 Loop 的停止条件,今天满屏都在聊 Graph。LangGraph、多 Agent 协作、节点编排这些词堆在一起,很容易让人以为又要推倒重来。其实不是。Loop 解决的是「一个 Agent 怎么持续干」,Graph 解决的是「多个 Agent 怎么配合干」,它们不在一个维度上,也不存在谁替代谁。

我试过把一个单 Agent 的修 bug 循环硬改成多节点图,结果发现任务本身根本不需要并行,拆完反而更难排查。所以这篇不聊虚的架构演进史,直接落地一件事:用 LangGraph 搭一个「需求拆解 → 并行开发 → 汇总审查」的多 Agent 协作骨架,并且把模型调用通道统一收敛到 TaoToken,让你不用在多个 Key 之间来回切换。适合已经写过单 Agent Loop、想往多 Agent 走一步的开发者,也适合团队里要统一模型接入方式的同学。

整篇的节奏是:先讲清楚 Loop 和 Graph 的边界,再给可复制的config.toml和settings.json骨架,然后跑一次真实的多 Agent 调用验证,最后把常见的报错挨个排掉。代码都能直接抄,参数都有说明。

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

多 Agent 系统最先崩的地方往往不是图逻辑,而是模型接入。三个 Agent 各配一套 Key、各写一份 base_url,改一个环境就得同步改三处,迟早出错。TaoToken 在这里的作用就是把模型调用收敛成一条通道:一个 Key、一个 API 地址,LangGraph 里所有节点共用同一套客户端配置。

你需要先拿到两样东西:

  • 一个 API Key,在控制台的 API Keys 页面创建,形如sk-开头的一串字符;
  • 统一的 API 地址https://taotoken.net/api,注意这个地址不带任何查询参数,直接作为 base_url 使用。

创建 Key 的入口在这里:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite

如果你还没决定用哪个模型,可以先在模型对话页面试几条 prompt,确认模型对代码类任务的响应质量,再写进配置:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite

接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面列了兼容 OpenAI SDK 的调用方式,LangGraph 节点里直接用openai客户端就能接上,不需要额外适配层。

注意:Key 只放在环境变量或本地配置文件里,不要提交到 Git。多 Agent 项目尤其容易在节点代码里硬编码 Key,一旦仓库公开就是事故。

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

配置分两层:config.toml管模型通道和运行参数,settings.json管图结构和 Agent 角色定义。这样拆的好处是,换模型只动 toml,改协作流程只动 json,互不干扰。

3.1 config.toml:模型通道与运行参数

# config.toml [llm] # 统一走 TaoToken 通道,所有 Agent 节点共用 base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" # 从环境变量读取,不写死 default_model = "gpt-4o-mini" timeout = 60 max_retries = 3 [llm.params] temperature = 0.2 max_tokens = 2048 [graph] # 图运行相关 max_parallel_nodes = 3 # 并行开发节点上限 checkpoint_backend = "memory" # 生产可换 sqlite / postgres recursion_limit = 25 # 防止图无限循环 [agents] # 每个角色的模型可以单独覆盖,不写则用 default_model planner_model = "gpt-4o-mini" coder_model = "gpt-4o-mini" reviewer_model = "gpt-4o-mini"

recursion_limit这个参数很关键。Graph 里如果有回退边(比如审查不通过打回重写),没有上限就会一直转。25 是个保守值,复杂流程可以调到 50,但别不设。

3.2 settings.json:图结构与角色定义

{ "graph_name": "multi_agent_dev", "entry": "planner", "nodes": { "planner": { "type": "agent", "role": "需求拆解", "system_prompt": "你是需求分析师,把用户需求拆成可独立开发的子任务列表,输出 JSON 数组,每项含 id、title、desc。", "output_key": "subtasks" }, "coder": { "type": "agent", "role": "并行开发", "system_prompt": "你是开发工程师,根据分配到的子任务写代码,只输出代码块,不要解释。", "input_key": "subtasks", "output_key": "code_map", "parallel": true }, "reviewer": { "type": "agent", "role": "汇总审查", "system_prompt": "你是代码审查员,检查各子任务代码是否满足需求,输出 PASS 或 FAIL 加原因。", "input_key": "code_map", "output_key": "review_result" } }, "edges": [ { "from": "planner", "to": "coder" }, { "from": "coder", "to": "reviewer" }, { "from": "reviewer", "to": "coder", "condition": "review_result.startswith('FAIL')" } ] }

edges里最后一条就是条件回退边:审查不通过就打回 coder 重写,配合recursion_limit兜底。这套结构对应的是「扇出 → 并行 → 扇入」的经典 Graph 形态,planner 一个节点拆出多个子任务,coder 并行处理,reviewer 汇总。

3.3 环境变量与依赖安装

export TAOTOKEN_API_KEY="sk-你的key" pip install langgraph openai python-dotenv tomli

Python 3.11 以下用tomli读 toml,3.11 以上标准库tomllib直接可用。

4. 验证请求:跑通一次多 Agent 调用

配置就位后,写一个最小可运行的图。核心是把 config.toml 里的通道读进来,构造一个共享的 OpenAI 客户端,所有节点复用它。

# main.py import os, json, tomllib from openai import OpenAI from langgraph.graph import StateGraph, END from typing import TypedDict, List with open("config.toml", "rb") as f: cfg = tomllib.load(f) client = OpenAI( base_url=cfg["llm"]["base_url"], api_key=os.environ[cfg["llm"]["api_key_env"]], ) def call_llm(system: str, user: str, model: str | None = None) -> str: resp = client.chat.completions.create( model=model or cfg["llm"]["default_model"], messages=[ {"role": "system", "content": system}, {"role": "user", "content": user}, ], temperature=cfg["llm"]["params"]["temperature"], max_tokens=cfg["llm"]["params"]["max_tokens"], timeout=cfg["llm"]["timeout"], ) return resp.choices[0].message.content.strip() class DevState(TypedDict): requirement: str subtasks: List[dict] code_map: dict review_result: str def planner_node(state: DevState): raw = call_llm( "你是需求分析师,把需求拆成子任务,输出 JSON 数组,每项含 id、title、desc。", state["requirement"], ) raw = raw.replace("```json", "").replace("```", "").strip() return {"subtasks": json.loads(raw)} def coder_node(state: DevState): code_map = {} for task in state["subtasks"]: code = call_llm( "你是开发工程师,根据子任务写代码,只输出代码块。", f"子任务:{task['title']}\n说明:{task['desc']}", ) code_map[task["id"]] = code return {"code_map": code_map} def reviewer_node(state: DevState): joined = "\n\n".join(f"# {k}\n{v}" for k, v in state["code_map"].items()) result = call_llm( "你是代码审查员,检查代码是否满足需求,输出 PASS 或 FAIL 加原因。", joined, ) return {"review_result": result} def route_review(state: DevState): return END if state["review_result"].startswith("PASS") else "coder" g = StateGraph(DevState) g.add_node("planner", planner_node) g.add_node("coder", coder_node) g.add_node("reviewer", reviewer_node) g.set_entry_point("planner") g.add_edge("planner", "coder") g.add_edge("coder", "reviewer") g.add_conditional_edges("reviewer", route_review, {"coder": "coder", END: END}) app = g.compile() out = app.invoke( {"requirement": "写一个用户登录接口,包含参数校验和异常处理"}, config={"recursion_limit": cfg["graph"]["recursion_limit"]}, ) print(out["review_result"])

跑起来后你会看到 planner 先输出子任务 JSON,coder 逐个生成代码,reviewer 给出 PASS 或 FAIL。如果 FAIL,图会自动回到 coder 重写,直到 PASS 或撞上 recursion_limit。

成功的结果长这样:

PASS - 子任务1 登录参数校验:已实现 user_id 非空与长度校验 - 子任务2 异常处理:已用 try-except 包裹 - 子任务3 返回结构:已统一为 code + msg

看到这个输出,说明多 Agent 协作骨架已经通了,而且所有模型调用都走的是同一条 TaoToken 通道。

5. 本篇常见错排查

5.1 401 Unauthorized

最常见的原因是环境变量没生效。export只在当前 shell 有效,换个终端就没了。建议写进.env用python-dotenv加载,或者确认TAOTOKEN_API_KEY拼写和 config.toml 里的api_key_env完全一致。另一个坑是 Key 前后带了空格或引号,复制时容易带上。

5.2 base_url 写错导致 404

base_url必须是https://taotoken.net/api,不要在后面加/v1或/chat/completions。OpenAI SDK 会自己拼路径,你多写一段就变成/api/v1/v1/chat/completions,直接 404。这个错我踩过,排查了半天才发现是地址多了一截。

5.3 planner 返回的 JSON 解析失败

模型有时会在 JSON 外面包 ```json 代码块,或者加一句「以下是拆解结果」。代码里已经做了replace清洗,但如果模型输出格式飘得厉害,可以在 system prompt 里加一句「只输出 JSON,不要任何其他文字」,稳定性会明显提升。

5.4 图跑飞了,一直循环

两个原因:一是recursion_limit没设或设太大,二是回退边的条件写反了。检查route_review的返回值,确认 PASS 时返回 END。另外 reviewer 的 prompt 里如果没明确要求输出 PASS/FAIL,模型可能返回「通过」这种中文,startswith("PASS")就永远为 False,图会一直转。

5.5 并行节点没真正并行

LangGraph 的并行需要显式用SendAPI 或把节点设计成可并发调度的形式。上面示例里 coder 是单节点内 for 循环,属于串行。要真并行,得把每个子任务拆成独立节点,用Send分发。这是从「能跑」到「跑得快」的下一步,但先跑通串行版再优化,别一上来就上并行。

6. 把通道和骨架固定下来

多 Agent 系统真正难的不是画图,是让每个节点稳定地拿到模型能力。把 Key 和 base_url 收敛到 TaoToken 一条通道后,你换模型、加节点、调角色,都不用再动接入层。上面这套 config.toml + settings.json + LangGraph 的骨架,可以直接作为你项目的起点。

如果你后面要长期跑编码类 Agent,或者把多 Agent 接进 CI 流程,可以看下 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

先把单图跑通,再考虑双图架构和并行调度。Graph 不是银弹,能 Loop 解决的就别急着上 Graph,这句话我在项目里验证过不止一次。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询