ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

ReAct模式详解:从零实现大模型Agent工具调用循环

ReAct模式详解:从零实现大模型Agent工具调用循环 ReAct 这个名字最近在 AI Agent 相关内容里出现频率非常高。注意这里说的是大模型 Agent 领域的 ReAct不是前端那个 React 框架。ReAct 的全称是 Reasoning and Acting核心思想用一句话说就是让大模型在回答之前先想Reason再做Act然后把工具返回的结果再交给模型继续思考循环往复直到问题解决。这个模式的出处是论文ReAct: Synergizing Reasoning and Acting in Language Models由 Shunyu Yao 等人提出。相比普通的“一次性问答”ReAct 把模型从“单次输出”变成了“多步决策循环”。现在很多 Agent 框架内部其实都在用类似思路只不过包装成了 LangChain Agent 或 Function Calling 的形式。这篇文章会做这几件事先拆解 ReAct 的原理和核心循环再用 Python 从零实现一个最小的 ReAct Agent随后扩展成多工具版、API 服务和批量任务脚本最后给出常见的坑和排查方式。整个过程不需要 GPU不需要复杂框架只需要一个能调用的模型接口就行。如果你正在做 AI Agent 相关开发或者准备在自己的工具里接入多步推理能力这篇文章可以直接收藏。接下来直接进入正题。1. ReAct 核心能力速览能力项说明技术名称ReActReasoning and Acting来源论文《ReAct: Synergizing Reasoning and Acting in Language Models》项目类型LLM Agent 的 Prompt 编排方法不是独立框架核心机制Thought思考- Action行动- Observation观察循环依赖要求仅需一个 LLM 接口如 OpenAI、Anthropic、本地 Ollama 等模型硬件云端 API 不需要本地 GPU本地模型需要按模型大小评估显存是否需要微调不需要纯 Prompt 方式即可实现是否支持工具调用支持可通过函数注册方式绑定搜索、计算器、数据库、代码执行器等工具是否支持 API 封装支持可以把 Agent 循环封装成独立 HTTP 服务是否支持批量任务支持按任务逐个进入循环即可注意控制并发主要优势过程可解释、可调试、能利用外部工具完成多步任务主要代价Token 消耗高、推理次数多、延迟比单轮 Prompt 高适合读者正在学习 Agent 原理、需要自己实现 Agent 循环的开发者2. 适用场景与使用边界ReAct 适合解决那些“只靠模型内部知识回答不了、或者容易出错”的任务。典型场景包括需要查询实时信息比如天气、价格、股票、新闻模型本身不知道最新数据。需要做逻辑计算模型直接算出容易出错不如把表达式丢给代码执行器或计算器。需要查找私有数据比如企业内部文档、数据库模型不能凭空知道。需要多步操作比如先查用户订单号再查物流状态最后判断是否属于延迟发货。反过来ReAct 不太适合这些场景单轮文本分类直接用 Prompt 加几个示例就够了不需要引入循环。低延迟场景比如实时对话中每一轮都要在几百毫秒内返回ReAct 的多步推理会明显拖慢速度。高并发但低成本的场景ReAct 的每一轮循环都会重复发送历史上下文Token 成本是按倍数增长的。需要绝对稳定输出的场景比如金融结算、医疗诊断等模型多步推理的不可控因素更多必须有人工复核。使用边界也要说清楚。ReAct 本质上是让模型去调用外部工具工具一旦可以执行系统命令、发送网络请求、读取本地文件就必须做权限控制。尤其要注意工具白名单机制模型只能调用注册过的函数不能任意执行代码。如果代码执行工具的权限过大模型可能因为错误的指令删除文件或污染环境。如果模型读取了外部网页或文档外部内容可能携带恶意指令也就是 Prompt Injection需要在 System Prompt 中明确要求模型忽略文本内的指令。涉及用户隐私、企业数据、版权素材时必须确认授权和合规边界测试数据要脱敏。3. ReAct 技术原理拆解ReAct 的核心不是某个开源项目而是一套与模型交互的协议。理解它只需要抓住四个部分。第一个部分是 Thought也就是模型对当前问题的思考。模型在这里分析已知信息和缺失信息决定下一步需要做什么。比如用户问“上海今天适合穿短袖吗”模型会意识到自己不知道上海实时天气于是思考应该调用天气工具。第二个部分是 Action模型基于思考选择一个工具并给出参数。这里不要求模型真的去执行什么它只负责输出“要调用哪个工具、参数是什么”真正的执行发生在外部代码中。第三个部分是 Observation外部代码拿到 Action 里的参数调用真实工具把结果返回给模型。例如天气工具返回“多云27 度”这个结果会作为一条消息拼接回对话历史。第四个部分是 Final Answer模型在观察结果足够充分之后停止行动输出最终回答。整个流程是一个 while 循环模型输出 - 解析 Action - 执行工具 - 拼接 Observation - 再问模型 - 直到出现 Final Answer。ReAct 与 Chain-of-ThoughtCoT的区别在于CoT 只是让模型多写推理步骤仍然不接触外部世界ReAct 则把推理和行动交替结合模型可以知道自己的行动结果从而修正错误。比如模型第一次调用天气工具传错了城市名观察结果会立刻告诉它参数不对它可以重新调用一次而不是将错就错。ReAct 与 Function Calling 的关系也要讲清楚。Function Calling 是模型接口层面提供的一种结构化能力模型直接返回函数名和 JSON 参数框架负责执行。ReAct 更像是 Prompt 层面的协议模型输出文本代码用正则或字符串匹配来解析工具调用。实际工程中很多 Agent 框架仍然用 ReAct 风格的思考链来组织多轮工具调用即使底层用了 Function Calling也会保留 thought 字段。对于没有 Function Calling 接口的本地模型ReAct 反而是更通用的做法。所以 ReAct 的价值在于它不绑定任何框架和模型接口只要是能正常对话的大模型都能套上这套 Prompt 协议跑起来。这也是为什么今天还要花时间手工实现一遍搞懂它之后再去看 LangChain 或其他 Agent 框架会有一种豁然开朗的感觉。4. 环境准备与前置条件因为 ReAct 是一套代码逻辑不依赖特定框架所以环境很简单。Python 3.9 以上安装 requests 库用于调用模型接口。一个可用的模型接口。可以用 OpenAI 兼容接口、Anthropic 接口也可以用本地 Ollama 启动的接口。如果用云端 API完全不需要本地 GPU如果用本地模型显存大小取决于模型参数量和量化方式。常见量化 7B 模型大概需要 6GB 到 8GB 显存13B 模型需要 12GB 以上这只是经验值实际占用还要看上下文长度和推理框架务必以本机测试为准。工具所需的依赖按需准备。比如要执行 Python 代码就用 Python 内置的 exec要查数据库就准备 SQLite 或对应驱动。磁盘空间和端口方面云端 API 场景只需要几十 KB 的代码文件本地模型场景需要预留模型文件空间和推理服务端口。如果端口冲突Ollama 默认 11434可以命令行指定其他端口。没有真实模型环境时建议用最简单的方式先验证直接使用 OpenAI 兼容接口的/chat/completions路径请求体里带上 System Prompt 和用户消息。接下来我们直接写代码。5. 从零实现一个最小 ReAct Agent先说明总体设计。一个最小 ReAct Agent 由四个部分组成模型调用函数、工具注册表、Prompt 模板、循环控制逻辑。我们不需要框架核心代码大约 100 行。5.1 工具注册函数工具就是普通的 Python 函数。为了让这个例子不依赖外部网络这里先定义两个模拟工具一个模拟天气查询一个模拟简单计算。def get_weather(city: str) - str: 模拟天气查询工具。 生产环境可替换为真实天气 API。 weather_data { beijing: 晴24 度适合短袖, shanghai: 多云27 度适合短袖, guangzhou: 阵雨30 度建议带伞 } return weather_data.get(city, f未收录 {city} 的天气数据) def calculate(expression: str) - str: 简单计算器仅限纯数字表达式。 allowed_chars set(0123456789-*/(). ) if not set(expression).issubset(allowed_chars): return 表达式不合法包含非法字符 try: result eval(expression) return str(result) except Exception as e: return f计算失败: {e}工具注册表用字典把函数名映射到真实函数。这样模型输出 Action 时直接查字典就能拿到可调用对象。TOOLS { get_weather: get_weather, calculate: calculate, }生产环境里工具名建议统一用下划线风格避免模型输出时把大写字母写错。5.2 调用模型接口这里用 OpenAI 兼容接口作为例子。无论背后是 OpenAI 官方、Azure、还是本地使用兼容层启动的模型请求格式都一致。调用chat函数时传入完整的 messages 列表即可。import requests import json # 按实际环境替换本地兼容接口可以填 http://127.0.0.1:11434/v1 API_BASE https://api.openai.com/v1 API_KEY sk-your-key MODEL_NAME gpt-4o-mini def chat(messages, max_tokens1024, temperature0.2): 调用 OpenAI 兼容的 chat 接口。 url f{API_BASE}/chat/completions headers { Authorization: fBearer {API_KEY}, Content-Type: application/json } payload { model: MODEL_NAME, messages: messages, max_tokens: max_tokens, temperature: temperature } resp requests.post(url, headersheaders, jsonpayload, timeout120) resp.raise_for_status() data resp.json() return data[choices][0][message][content]如果使用的是 Ollama 本地模型且没启用兼容接口可以改请求 Ollama 的/api/chat接口。但更推荐的模式是给 Ollama 加兼容层这样代码可以复用。5.3 ReAct Prompt 模板Prompt 模板是 ReAct 能不能跑通的关键。模型必须知道输出格式是什么以及什么时候停止循环。SYSTEM_PROMPT 你是一个能够调用工具解决任务的 AI Agent。 你可以使用的工具有 {tool_desc} 请严格按照以下格式输出每一步 Thought: 你当前的思考 Action: 工具名 Action Input: {{参数名: 参数值}} 当工具的 Observation 反馈回来之后你会继续看到新的消息。 当你已经有足够信息回答用户问题时请输出 Thought: 我已经获得答案 Final Answer: 最终答案 注意 - 每一步只能输出一个 Action。 - Action Input 必须是 JSON 格式。 - 不要编造工具只能在给定工具中选择。 - 如果不使用工具也能回答可以直接输出 Final Answer。 调用前需要把TOOLS字典转成 Prompt 里的工具描述。格式可以这样组织def build_tool_desc(): lines [] for name, func in TOOLS.items(): doc func.__doc__.strip().split(\n)[0] if func.__doc__ else lines.append(f- {name}: {doc}) return \n.join(lines)5.4 解析模型输出模型返回的文本里Action 行和 Action Input 行是关键信息。解析时不能直接json.loads整个文本因为模型可能输出多余内容。这里用正则提取。import re import json def parse_react_output(text): 从模型输出中解析 Action 和 Action Input。 如果输出包含 Final Answer则返回 answer。 final_match re.search(rFinal Answer:\s*(.), text, re.DOTALL) if final_match: return {type: answer, content: final_match.group(1).strip()} action_match re.search(rAction:\s*(\w), text) action_input_match re.search(rAction Input:\s*(\{.*\}), text, re.DOTALL) if not action_match: return {type: error, content: f无法解析模型输出: {text}} action action_match.group(1) action_input {} if action_input_match: raw action_input_match.group(1) try: action_input json.loads(raw) except json.JSONDecodeError: # 模型有时会输出单引号或换行不规范的 JSON这里做一次容错 raw raw.replace(, ) try: action_input json.loads(raw) except json.JSONDecodeError: return {type: error, content: fAction Input JSON 解析失败: {raw}} return {type: action, action: action, input: action_input}这个解析函数没有把单引号 JSON 做完整修复如果模型经常输出不规范内容后续可以再加一层正则清洗或者让模型重新格式化。5.5 主循环主循环负责把上面几个部分串起来。核心逻辑是给模型发消息解析输出如果是 Final Answer结束如果是 Action执行工具把 Observation 拼回消息继续下一轮如果解析失败把错误信息也作为 Observation 给模型让它自行修正。def run_react_agent(user_query, max_steps5): 执行 ReAct Agent 循环。 messages [ {role: system, content: SYSTEM_PROMPT.format(tool_descbuild_tool_desc())}, {role: user, content: user_query} ] for step in range(max_steps): print(f\n Step {step 1} ) model_output chat(messages, max_tokens1024, temperature0.2) print(模型输出) print(model_output) parsed parse_react_output(model_output) if parsed[type] answer: return parsed[content] if parsed[type] error: messages.append({role: assistant, content: model_output}) messages.append({role: user, content: parsed[content] 请修正你的输出格式。}) continue tool_name parsed[action] tool_input parsed[input] if tool_name not in TOOLS: observation f工具 {tool_name} 不存在可用工具{list(TOOLS.keys())} else: try: observation TOOLS[tool_name](**tool_input) except Exception as e: observation f工具执行失败: {e} print(fObservation: {observation}) messages.append({role: assistant, content: model_output}) messages.append({role: user, content: fObservation: {observation}}) return 达到最大步数任务未完成。这段代码有几个细节值得注意。第一每轮循环都需要把完整的对话历史发给模型所以模型的上下文窗口会逐步增大。第二工具执行失败不要直接把异常吞掉而是作为 Observation 传回模型让模型根据错误重新调整参数或换工具。第三max_steps必须有否则模型可能在同一个工具调用上反复打转浪费 Token 和延迟。执行一下if __name__ __main__: result run_react_agent(上海今天适合穿短袖吗) print(\n最终回答, result)如果模型按预期工作它会先调用get_weather根据 Observation 里的温度和天气情况再决定是否输出回答。这比让模型直接对着未知天气强行回答要可靠得多。6. 功能测试与效果验证写完了核心实现接下来需要验证流程是否正常。建议按以下顺序测试。6.1 工具调用测试先测试模型能否正确输出 Action。输入问题“计算 (15 27) * 4 等于多少”。如果模型输出了Action: calculate且 Action Input 是合理 JSON说明工具识别成功。如果模型直接给出答案说明它用了内部计算能力这个结果未必可靠但也不算出错。如果模型输出格式混乱就要回到 Prompt 模板补充一个 few-shot 示例。6.2 多轮工具测试再测试一个需要两次工具调用的任务“分别查询北京和广州的天气然后判断哪个城市更适合跑步。”理想流程是Thought: 我需要查询北京的天气。 Action: get_weather Action Input: {city: beijing} Observation: 晴24 度 Thought: 我再查询广州的天气。 Action: get_weather Action Input: {city: guangzhou} Observation: 阵雨30 度 Thought: 已经获得两个城市天气可以综合判断。 Final Answer: 北京更适合跑步。判断成功与否的标准是模型是否记住前面查询的结果并在最终答案中体现。如果模型在第二轮重新问用户“你要查哪个城市”说明对话历史拼接有问题或者模型没有正确理解 Observation 是属于哪个任务的。6.3 错误恢复测试故意给模型一个不存在的工具名或者让工具抛出异常观察它是否能通过 Observation 恢复。例如让模型调用一个search_web工具而工具表里没有这个函数。正常情况是模型看到 Observation 描述“该工具不存在”后换一个可用工具或者直接用已有知识回答而不是继续输出同一个不存在的 Action。这一步能同时验证循环代码的健壮性和模型的恢复能力。如果模型反复输出同一个非法工具建议在 Prompt 中明确列出可用工具并把错误信息写得更加直接。6.4 边界条件测试空输入模型应该要求用户提供更完整的信息。超长输入如果用户问题超过模型上下文调用时会直接报错。需要多步但被max_steps截断观察最终是否返回“达到最大步数”。工具参数缺失例如get_weather没有传 city 参数函数会抛 TypeErrorObservation 会把异常信息传回模型。这些测试不需要特别复杂的脚本每次打印模型输出和 Observation 就够了。建议先运行 3 到 5 个用例确认链路稳定后再扩展工具数量。7. 接口 API 与批量任务封装很多场景下ReAct Agent 不应只活在命令行脚本里而是要作为服务被其他系统调用。这里提供一个轻量封装思路。7.1 先把 Agent 循环包成函数把run_react_agent的返回值固定为字符串并提供一个run_react_agent_with_meta函数返回步骤记录方便上层系统记录日志。def run_react_agent_with_meta(user_query, max_steps5): messages [...] steps [] for step in range(max_steps): model_output chat(messages, max_tokens1024, temperature0.2) parsed parse_react_output(model_output) steps.append({step: step 1, output: model_output}) if parsed[type] answer: return {answer: parsed[content], steps: steps, success: True} if parsed[type] error: ... else: observation ... messages.append(...) return {answer: 达到最大步数任务未完成。, steps: steps, success: False}7.2 FastAPI 暴露 HTTP 接口建议用 FastAPI 做服务因为自带请求校验和文档页面。安装依赖后把上面的代码导入即可。# server.py from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class AgentRequest(BaseModel): query: str max_steps: int 5 class AgentResponse(BaseModel): answer: str success: bool steps: list app.post(/agent/run, response_modelAgentResponse) def run_agent(req: AgentRequest): result run_react_agent_with_meta(req.query, req.max_steps) return result if __name__ __main__: import uvicorn uvicorn.run(app, host127.0.0.1, port8000)启动后调用方式curl -X POST http://127.0.0.1:8000/agent/run \ -H Content-Type: application/json \ -d {query: 上海今天适合穿短袖吗, max_steps: 5}响应示例{ answer: 适合上海今天多云 27 度。, success: true, steps: [ { step: 1, output: Thought: 我需要查询上海天气。\nAction: get_weather\nAction Input: {\city\: \shanghai\} } ] }7.3 批量任务脚本批量任务的本质很简单读一批任务逐个跑 Agent 循环把结果写回文件。但要注意失败重试和日志记录。下面是一个可参考的 JSONL 批处理模板。import json import time INPUT_FILE tasks.jsonl OUTPUT_FILE results.jsonl FAILED_FILE failed.jsonl with open(INPUT_FILE, r, encodingutf-8) as f: tasks [json.loads(line) for line in f if line.strip()] for i, task in enumerate(tasks): query task.get(query) print(f处理第 {i1}/{len(tasks)} 条: {query[:30]}...) try: result run_react_agent_with_meta(query, max_steps5) output {query: query, answer: result[answer], success: result[success]} with open(OUTPUT_FILE, a, encodingutf-8) as f: f.write(json.dumps(output, ensure_asciiFalse) \n) except Exception as e: failed {query: query, error: str(e)} with open(FAILED_FILE, a, encodingutf-8) as f: f.write(json.dumps(failed, ensure_asciiFalse) \n) time.sleep(2)批量任务最容易踩的坑是 API 限流。如果一次性提交大量请求接口会返回 429。更稳妥的做法是加入重试机制def chat_with_retry(messages, max_retries3, retry_delay5): for attempt in range(max_retries): try: return chat(messages) except Exception as e: if attempt max_retries - 1: raise print(f调用失败{retry_delay} 秒后重试: {e}) time.sleep(retry_delay)8. 资源占用与性能观察ReAct 对资源的消耗不在显存而在 Token 和延迟。这里单独说一下。假设模型每轮输出 200 个 Token工具返回 50 个 Token那么执行 3 轮循环累计发送给模型的 Token 就是第一轮约 500第二轮约 750第三轮约 1000总数远高于单轮 Prompt。如果是本地 7B 模型每轮推理时间可能在 2 到 5 秒三步循环就是 10 到 15 秒用户体感非常明显。如何观察性能建议在每次chat调用前后打印时间戳并在响应里读取 Token 统计。OpenAI 兼容接口的返回体里通常有usage字段包含prompt_tokens和completion_tokens。把这些数据累加就能得到单次任务的真实 Token 开销。def chat_with_usage(messages, max_tokens1024, temperature0.2): ... data resp.json() usage data.get(usage, {}) return data[choices][0][message][content], usage如果发现 Token 消耗过高可以从几个方向入手限制最大步数把max_steps从 5 降到 3成本会显著下降。缩短工具返回值比如天气工具只返回“晴/多云/温度”不返回长句。在每轮 Observation 里只保留上一步的相关结果不要把历史 Observation 全部拼回 messages但这会牺牲部分上下文连贯性。使用更小的模型如果任务不复杂gpt-4o-mini 或本地 7B 就够用。关于本地显存占用需要明确一点显存占用主要取决于模型本身而不是 ReAct 代码。云端 API 场景不占本地显存本地模型场景建议先用nvidia-smi观察基线占用再跑一个单轮任务和一个 3 步 ReAct 任务对比。上下文越长KV Cache 占用越高显存也会随之上升。9. ReAct 常见问题与排查方法问题现象可能原因排查方式解决方案模型不输出 Action直接给答案Prompt 约束不足或模型能力较弱打印模型原始输出确认是否走了思考链在 Prompt 中加入 few-shot 示例换更大参数模型Action Input JSON 解析失败模型输出单引号、未闭合括号、注释等打印解析异常附近的原始文本加强正则匹配使用兼容层让模型直接输出结构化 JSON模型反复调用同一个工具工具结果未满足判断条件或模型陷入循环查看每轮 Observation 和 Action 是否重复添加工具调用去重缓存设置 max_steps在 Prompt 中要求“如果上次结果相同请换策略”Observation 太长导致上下文溢出工具返回大量内容比如完整网页文本查看报错信息确认是 context length 超限截断工具返回例如只保留前 500 字符增加摘要步骤工具执行报错但模型继续用错误参数模型没有充分理解 Observation 中的异常信息观察 Observation 是否包含完整错误让工具返回更明确的错误提示例如“城市不存在可选城市xxx”使用本地模型时显存不足模型过大或上下文过长导致 KV Cache 超限运行 nvidia-smi 查看显存占用换更小的量化模型减小 max_tokens关闭并行请求API 调用报 429 或 timeout限流或网络问题查看错误码和响应头增加重试和退避机制降低并发批量任务某一条卡住单个循环步数过长或 API 超时给每条任务加超时控制记录执行日志用asyncio.wait_for或设置请求 timeout把超时任务写入 failed 列表最终回答与 Observation 矛盾模型在最后一步忽略了观察结果检查最终答案前的历史消息在 Prompt 中要求“Final Answer 必须基于最近的 Observation不能凭记忆回答”排查时最实用的一招是把steps列表完整打印出来。ReAct 是可解释的每步都有 Thought、Action、Observation只要链路数据完整问题基本能定位到具体环节。10. ReAct 工程化最佳实践第一部分Prompt 设计。System Prompt 里必须写清楚工具列表、输出格式、循环结束条件。建议至少给 1 到 2 个 few-shot 示例特别是格式复杂时。模型在不同温度下的稳定性不一样Agent 循环建议 temperature 设置在 0 到 0.3 之间太高会让 Action 参数随机变化导致解析失败率上升。第二部分工具设计。工具接口要简单参数名要直观避免模型猜不到参数含义。工具返回内容建议纯文本不要返回 HTML 或复杂嵌套结构。让模型消化极度结构化的数据容易出错不如在工具代码里先做清洗。每个工具都要做好异常捕获把人类可读的错误信息返回给模型而不是让模型看到一段 Python Traceback。第三部分流程控制。一定要设置最大步数这是最基本的保护机制。建议每次调用工具前查一下缓存如果模型重复调用相同工具相同参数直接返回上一次结果。对高风险工具比如删除文件、发送邮件、执行 shell 命令在 Prompt 里要求模型必须输出“用户已确认”同时在代码里做二次校验。第四部分数据安全。不要把敏感信息直接拼进 Prompt尤其是发给外部 API 时。如果必须处理私有数据优先考虑本地模型。工具执行环境建议用 Docker 或沙箱隔离限制网络访问和文件路径。涉及人脸、声音、个人信息、版权文本时必须确认数据来源合法并获得授权。第五部分可观测性。Agent 服务上线后每一步的模型输出、工具参数、工具返回、最终答案都要有日志。最好在响应里带上任务 ID方便追踪问题。批量任务要区分成功、失败、超时三类结果失败任务自动重试重试仍失败则进入人工处理队列。11. 总结与下一步ReAct 最值得尝试的点在于它用最简单的方式让大模型突破“只靠记忆回答”的限制。你不需要理解复杂框架不一定要用 GPU不需要微调模型只要有一个对话接口几十行 Python 代码就能让模型逐步调用工具、观察结果、自我纠错最终给出更可靠的回答。建议你先做最小验证搭好模型接口跑通天气查询和计算器两个工具看模型是否能按 Thought - Action - Observation - Final Answer 的循环完成任务。最容易踩的坑是模型输出格式解析失败和无限循环前者加强 Prompt 和正则后者加 max_steps 和工具调用去重。下一步可以继续扩展的方向包括把工具换成真实搜索引擎或数据库给 Agent 加上记忆模块让它跨会话记住用户偏好把单步工具扩展成子 Agent 编排也可以研究 Reflexion、Self-Ask 等更进阶的推理增强方法。搞懂 ReAct 之后再去看 LangChain 的 Agent 源码或各大模型厂商的 Function Calling 文档理解成本都会低很多。
返回列表