资讯详情

AI Agent思维链(CoT)工程架构实战

📅 2026/10/22 21:23:03 | 华诺云谱 👁 阅读
AI Agent思维链(CoT)工程架构实战
做AI Agent开发这几年我越来越觉得“思维链Chain of Thought简称CoT”不是一种可选的提示词技巧而是一套需要认真对待的工程架构。Agent能不能稳定干活通常不取决于模型基础能力有多强而取决于它“想”的过程能不能被约束成一条可执行、可检查、可回退的推理链。这篇文章会从Agent的视角重新理解CoT它到底解决什么问题、和普通问答里的CoT有什么区别、怎么设计Prompt、怎么搭一个最小可运行的Agent循环最后是我在真实项目里踩过的一组坑。如果你正准备把大模型接到工具调用、数据库查询、浏览器操作上或者正被Agent反复“一本正经地胡说八道”搞到头大那么这篇应该能帮到你。我不打算做理论综述所有内容都以可复现和可排错为目标。1. 先想清楚思维链在Agent里到底扮演什么角色1.1 没有显式思维链时Agent为什么总在关键环节翻车先看一个底层事实大模型本质上是“预测下一个token”的语言模型。你给它一个问题它最容易走的路是“根据参数里的记忆直接生成一个看起来像答案的文本”而不是“先识别问题需要什么信息再决定要不要调用外部工具”。在普通问答场景里这种直觉式回答偶尔能蒙对但在Agent场景里这就是灾难。我举一个最典型的场景。用户问“帮我查一下北京明天的降雨概率再决定我要不要带伞。”如果Agent在Prompt里没有强制输出思维链模型很可能直接回答“建议带伞因为明天可能有雨”。它跳过了查天气这个动作。你问它依据是什么它还能给你编出一套“低压系统影响”之类的理由。这就是没有CoT的Agent它把“回答问题”理解成“写作文”而不是“执行任务”。用生活里的例子类比一个不写行动清单的实习生你让他确认明天要不要带伞他凭印象说“带伞吧天阴了”而不是先打开天气App。思维链的作用就是逼他把“打开天气App”、“查降雨概率”、“大于某个阈值则带伞”这几步写在纸上。对模型来说这些写在纸上的文字不是装饰而是让下一步调用工具、读取结果、再决策有了上下文锚点。另一个容易被忽略的问题即使模型第一次正确调用了工具拿到工具返回结果之后如果没有显式CoT它大概率不知道“我现在已经走到哪一步了”。工具返回的是一个结构化文本或一段JSON模型需要把它和原始问题、已执行的步骤连接起来。没有思维链这个连接就是断的模型会在“下一步该干什么”上反复横跳。1.2 Agent里的CoT不是内心独白而是一份可审计的决策日志很多教程会把CoT讲成“让模型把思考过程写下来”听起来很像内心独白。但在Agent工程里我更喜欢把它理解成“决策日志”。这个日志需要满足三个硬性要求。第一它能约束行为。模型先写“我需要城市A的天气然后调用weather工具”写完之后下一步就更容易真的去调用weather而不是绕过工具直接给结论。这相当于给自己下了一个很具体的行动指令。第二它能定位错误。当Agent最后结果错了你可以把整条思维链翻出来看是“最初的目标理解错了”还是“工具参数传错了”还是“观察结果被忽略了”。没有这条日志你面对的就是一个黑盒只能靠重新生成碰运气。第三它能建立人机信任。用户在等待Agent执行多步任务时如果只看到一个转圈图标体验是很差的。但如果你能展示“正在确认目标城市 → 正在查询天气 → 正在对比降雨概率 → 生成结论”用户会觉得系统的每一步都有据可循。哪怕最终结论是“不用带伞”用户也能理解为什么。所以我很反对把思维链做得越隐蔽越好的说法。在Agent里CoT应该是一个一等公民。设计Agent系统时你得把“Thought / Action / Observation”三段式当作日志规范来定义而不是让模型自由发挥写一段散文。1.3 从普通CoT到Agent级CoT多了一步工具交互的认知闭环普通问答里的CoT本质上是一条直线问题 → 逐步推理 → 答案。模型一口气把推理写完整个过程不接触外部世界。Agent里的CoT不是直线而是一个带“工具交互”的循环。业内常用“ReAct”这个词来描述这种范式Reasoning推理和 Acting行动交替进行。每一步的格式不是“推理到答案”而是“思考 → 行动 → 观察 → 再思考”。模型必须先想这一步要做什么然后实际调一个工具拿到观察结果后再更新思考继续下一步。举一个实际例子用户要“统计某公司官网今天发布的新闻标题含几个关键词”。普通CoT会直接编一个统计结果。ReAct风格CoT会走这样一条链路Thought需要先访问官网新闻列表页找到今天的发布日期范围。Action调用fetch_page参数是新闻列表URL。Observation拿到页面HTML发现新闻标题共15条其中3条在今天发布。Thought这3条标题里需要统计包含“产品”二字的数量继续提取正文或标题过滤。Action调用filter_titles参数是刚才观察到的3条标题。Observation返回1条匹配“产品”。Thought信息完整可以汇总成最终答案。Actionfinal_answer输出最终结论。这条链路里每一个Thought都依赖上一步Observation每一个Action都必须有明确的输入来源。这已经不是普通CoT“想清楚就能答”的逻辑而是一种“想到哪、做到哪、看到哪、再想到哪”的闭环。可以这么理解普通CoT是给模型一张草稿纸Agent级CoT是给模型一张工作流工单。工单上每一步都要签收不能跳步也不能凭记忆补结论。这也是为什么后文所有的Prompt模板和工程代码都围绕这个“循环闭环”来做。2. 怎么设计Agent的思维链三套可以直接抄的Prompt模板2.1 单轮任务型先拆解再执行的CoT模板如果你的Agent暂时不需要复杂工具只是想让模型“别瞎猜”可以用最简单的“先拆解再执行”模板。这个模板特别适合文档问答、基于固定知识库的回复、单次信息提取。系统提示词可以这么写你是一名严谨的任务执行助手。面对用户问题时你不允许直接凭印象回答。你必须先输出内部思考过程然后给出结论。 思考过程要求 1. 复述用户的核心目标。 2. 列出要给出可靠答案所必需的信息项。 3. 判断这些信息是否已经从用户消息中获得。如果缺失必须明确说明缺失项并询问用户或转入指定的查证工具。 4. 只有信息完整时才基于已有信息组织最终回答。 输出格式 { thought: 这里写你的推理依据, answer: 这里写最终答案, need_more_info: false }这个模板的精髓在于“先列信息项再判断是否足够”。我见过很多Agent在没有这步时模型会把“我猜测”“大概率”“可能”这些话混进答案看起来像在推理其实是在编。加了信息完整性判断后模型会主动说“缺少XX我不能确定”这比硬答好得多。实际使用中你还可以再加一条规则“最终答案必须引用thought中提到的信息项编号例如‘基于信息项1和2’。”这能降低模型答非所问的概率。这个模板虽然简单但它是后面所有复杂CoT的基础。2.2 多步工具调用型ReAct风格的思考-行动-观察模板到了工具调用场景不能再只用“思考答案”了。你需要的是“思考动作观察”交替循环。下面这套模板我在好几个项目里复用过稳定性和可解析性都很好。你是一名任务规划Agent。你只能使用以下工具获取外部信息 - search(query): 搜索公开资料 - weather(city): 查询城市天气 - calc(expression): 计算数学表达式 你必须严格按照以下JSON格式每次只输出一条动作 {thought: 你对当前任务的分析和依据, action: 工具名, action_input: 传给工具的具体参数} 规则 1. 每轮只能输出一个动作动作必须是上述工具之一。 2. 思考必须与动作匹配先说明为什么要调用该工具再给出调用参数。 3. action_input不能凭空生成必须来自用户原始问题或最近一次工具观察结果。 4. 当你已经获得足够信息时action使用final_answeraction_input里写最终回答。 5. 如果缺少必要参数action使用ask_clarificationaction_input里写你要问用户的问题。这里有几个值得解释的设计细节。“每轮只能输出一个动作”是我吃过亏后加上的。早期我让模型一次输出“计划多个调用”看起来效率高但实际运行时只要其中一个工具参数出错整个批次就报废而且很难定位是哪一步错。单步输出虽然多几次请求但你能在每一步中断、校验、修正对工程稳定性太重要了。“action_input不能凭空生成”这条规则能挡住大部分幻觉。模型很容易在thought里写“城市天气很重要”然后action_input里填一个用户根本没提过的城市。加了这条之后模型会倾向于输出ask_clarification而不是乱猜。在实际使用时你可以把工具参数schema直接拼到工具名后面比如weather(city: string, date: string)。模型看到参数约束后生成格式会明显更规整。2.3 长链路规划型计划-执行-反思模板如果任务要跑很多步比如“查近一周新闻 → 筛选和某产品相关的内容 → 生成汇总报告”那么单轮思考-行动循环还不够。你需要让模型先把完整计划写出来再逐步执行并且每步结束后做一次“反思”判断计划是否需要调整。模板可以设计成“阶段状态”结构你是一个长期任务规划Agent。对于复杂任务你必须按三阶段执行 阶段一规划 输出JSON {phase: plan, plan: [步骤1, 步骤2, 步骤3]} 阶段二执行 每执行一步输出 {phase: act, step_index: 1, thought: 这一步的推理依据, action: 工具名, action_input: 参数} 阶段三反思 每执行完一步后先不要急着执行下一步。输出 {phase: reflect, progress: 已完成步骤1/3, next: 下一步计划, needs_replan: true 或 false} 如果needs_replan为true必须重新进入规划阶段输出新的plan并说明为什么调整。这个模板的本质是把“计划”和“执行”分离。模型在规划阶段负责想清楚总体路径在反思阶段负责检查路径是否还成立。这个结构的优势是即使中途工具返回了意料之外的结果模型也不会硬着头皮按原计划走而是会“意识到计划失效重新规划”。我用这个模板做过一个“多源资料调研Agent”。它需要先搜索A话题背景再搜索B产品评价最后对比输出。如果中间某个搜索返回的数据很少模型会在反思阶段说“信息不足需要调整关键词重新搜索”而不是拿残缺信息硬写结论。这种“计划-执行-反思”的CoT结构是长任务稳定性的关键。3. 把一个CoT Agent跑起来最小工程实现与关键细节3.1 最小Agent循环框架怎么搭理论讲完直接上代码。下面是一个极简但完整的CoT Agent循环。为了不绑定具体厂商SDK我用通用HTTP请求调用一个大模型API工具也做了简化。import json import requests SYSTEM_PROMPT 你是一名任务规划Agent。你只能使用以下工具获取外部信息 - weather(city): 查询城市天气 - search(query): 搜索公开资料 - calc(expression): 计算数学表达式 你必须严格按照以下JSON格式每次只输出一条动作 {thought: 你对当前任务的分析和依据, action: 工具名, action_input: 传给工具的具体参数} 规则 1. 每轮只能输出一个动作。 2. action_input不能凭空生成必须来自用户原始问题或上一次工具观察结果。 3. 当你已经获得足够信息时使用action为final_answeraction_input里写最终回答。 .strip() def call_llm(messages): resp requests.post( https://api.example-model.com/v1/chat/completions, headers{Authorization: Bearer YOUR_API_KEY}, json{ model: your-chat-model, messages: messages, temperature: 0.2, max_tokens: 800, }, timeout30, ) resp.raise_for_status() return resp.json()[choices][0][message][content] def execute_action(action: str, action_input: str) - str: if action weather: # 这里简写真实项目里会按照城市名去访问天气服务 return f{action_input}晴23~31℃降雨概率10% if action search: return f关于「{action_input}」的搜索结果找到3条相关条目待进一步筛选。 if action calc: # 警告生产环境不要直接用eval这里只演示结构 try: return str(eval(action_input)) except Exception as e: return f计算失败{e} return f未知工具{action}然后写Agent主循环def extract_json(text: str): decoder json.JSONDecoder() start text.find({) while start ! -1: try: obj, _ decoder.raw_decode(text, start) return obj except json.JSONDecodeError: start text.find({, start 1) return None def agent_loop(user_query: str, max_steps: int 8): messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_query}, ] for step in range(max_steps): raw call_llm(messages) parsed extract_json(raw) if not parsed: messages.append({ role: user, content: 上一步输出无法解析请严格只输出一个JSON对象。, }) continue thought parsed.get(thought, ) action parsed.get(action, ) action_input parsed.get(action_input, ) if action final_answer: return action_input, step 1 observation execute_action(action, action_input) # 把模型这一步的输出和工具观察都塞回上下文 messages.append({role: assistant, content: raw}) messages.append({ role: user, content: f工具观察{observation}\n请基于观察继续思考和行动。, }) return 达到最大步数未得到最终结论, max_steps这个循环里最关键的是“把模型原始输出和工具观察都塞回上下文”。很多新手会把CoT只当作“生成一次答案前的思考”但Agent级CoT必须把每一轮thought、action、observation都保留模型才知道自己刚才做过什么、看到了什么。这也是上一节说“闭环”落到代码里的样子。3.2 思维链输出格式的解析与清洗上面代码里我用了一个自定义的extract_json函数而不是直接json.loads(raw)。原因很简单模型经常会不好好按格式输出它在JSON前后加解释文字是常态。比如输出好的我来思考一下。{thought: 需要先查询城市天气, action: weather, action_input: 北京} 最终我会给出建议。如果直接json.loads肯定报错。extract_json的思路是找到第一个{然后用raw_decode尝试解析失败就找下一个{。这样能容忍很多“夹带私货”的输出。但这段实现仍有一个边界情况如果action_input里本身包含大括号比如“搜索城市空气质量{良好}”那么raw_decode会正确按JSON字符串规则解析不会因为内部大括号而提前截断。这正是比简单括号计数更稳的原因。在真实项目里我还会加一层“清洗”把输出的全角引号替换成半角引号。如果解析失败尝试用正则匹配action: *([^])和action_input: *([^]*)直接抽字段。如果模型把JSON放进了Markdown代码块先把首尾的json和剥掉。不要低估这个解析模块的重要性。CoT Agent跑不稳一半以上的情况是“模型生成了正确意图但格式歪了程序识别不了”。一个健壮的解析器比一百句“请严格输出”都管用。3.3 控制推理成本什么时候打断思考、什么时候停止CoT很费token。同样的任务不做思维链可能200 token做了完整CoT可能800到1000 token。加上Agent会跑多轮成本会成倍上涨。所以不能放任模型无限思考下去必须设置明确的停止条件。我常用的控制手段有四个。第一限制max_steps。就像上面的max_steps 8超出直接中断并返回“未完成”。一个正常任务通常在3到6步内能完成超过8步要么是任务太复杂需要重新拆解要么是模型在绕圈。第二限制单轮输出长度。我在调用大模型API时把max_tokens设在600到1000之间。这个长度足以容纳一条thought、action和action_input但不会让模型写成长篇小作文。给太长反而会让它啰嗦。第三重复检测。记录最近几轮thought的哈希或前几十个字符如果发现模型连续两轮输出了几乎一样的思考应当强制打断。你可以往消息列表里追加一条“你已经重复思考同一件事请直接给出最终答案或换一个工具。”这往往能把模型从循环里拉出来。第四问题路由。不是所有问题都需要CoT。比如“11等于几”“怎么用Python打印hello world”这种直接答就行。我建议在系统Prompt里加一个前置判断模型先输出{need_tools: false}或{need_tools: true}。只有need_tools为true时才启用完整CoT循环。这个开关能省下大量无谓请求。3.4 安全与可用性取舍如何不让思维链喧宾夺主CoT虽然好用但它把“模型内部推理文本”暴露在了消息流里。这就带来两个需要处理的问题安全问题和体验问题。先说体验。如果你把原始thought直接展示给用户用户会看到模型说“这一步我不太确定”“这个工具参数可能有问题”之类的话这会影响信任感。实际上用户只需要知道执行进度和最终结论不需要看完整推理链。我的做法是在展示层做“进度摘要”。解析出action后根据动作类型显示友好文案查询天气时显示“正在查询天气…”搜索时显示“正在检索相关资料…”。最终答案只渲染final_answer的action_input。这样既保留了CoT带来的可解释性又不会让用户陷进流水账。再说安全。模型在thought里很可能会复述用户输入里的敏感信息比如手机号、账号ID。如果这些日志被写进第三方日志平台或在界面上暴露就是数据泄露风险。我在Prompt里会加一条“思考过程中不要原样复述敏感字段用脱敏代号代替。”同时在日志输出时对明显长数字串做掩码处理。还有一个很隐晦的坑用户看到thought里写了“我需要调用删库工具”会误以为系统已经删库了。实际上可能只是模型在思考一个假设。为避免这种误判对外展示的进度信息要明确区分“计划中”“执行中”“已完成”。不要让用户把“想”当成“做”。4. 我在真实项目里踩过的CoT Agent的坑排查手册4.1 思维链很漂亮但工具调用一塌糊涂我见过最多的一类失败模型的thought写得头头是道但action_input传得乱七八糟。比如用户说“帮我查下北京明天天气”模型thought写“用户需要北京的天气信息目标城市是北京明天是周四”然后action调用weatheraction_input却填了Wednesday或者填了城市拼音和全称混用的字符串导致下游服务识别不了。根本原因是模型对“工具参数格式”没有概念。它只负责用自然语言思考不负责严格格式化。解决办法是在Prompt里把每个工具的输入约束写清楚并且加一条硬性规则“action_input必须匹配工具的JSON Schema如果缺少关键参数不要编造改为ask_clarification。”还有一个很有效的技巧是让模型在thought里先复述“参数来源”。例如“params城市用户问题中的北京日期用户问题中的明天。”然后再写action_input。这样模型等于在内部做了一次“变量赋值”传错参的概率会明显下降。4.2 Agent陷入“反复思考-不行动”死循环怎么破第二种常见问题是“光想不动”。模型每一轮都在输出thought说“这个问题比较复杂需要更多信息”但就是不调用任何工具也不给final_answer。一次跑了十轮全是类似的话。我排查下来多数原因是系统提示里没有说清楚“下一步必须行动”。我后来在每一轮的用户消息末尾都会追加一句“请先输出一个action不要只输出思考如果信息足够直接final_answer。”这句话看似简单但对模型的约束效果非常明显。再配合代码层的重复检测把thought做一次简单归一化统计连续重复次数。如果同一句话出现两次就强制注入一条“你正在重复相同思考请选择一个新工具或输出final_answer”。这不是惩罚模型而是通过外部控制把Agent的状态拉回来。4.3 思维链外显带来的信息泄露与误判有一次我把CoT日志直接打到了调试面板上测试人员顺手截图给产品看结果产品指着某个thought问“这里说用户账号可能有异常到底有没有异常你们查了吗”其实模型只是在推理“要不要检查账号状态”并不是真的发现了异常。从那时起我就坚决把内部CoT和用户可见信息彻底分开。处理方法是在线调试日志只保留action和observation的关键字段thougt做脱敏或完全隐藏生产环境日志可以完整保存但必须走严格的访问权限控制。前端展示的进度状态是“动作 结果摘要”不是模型原始思考。这样既不影响排错也不会引起产品、用户或法务层面的误会。还有一类泄露更容易忽略模型在thought里复述了某个需要保密的查询条件外部接口可能通过工具调用把这段自然语言传给搜索引擎。所以工具调用参数也要做脱敏检查尤其是包含长数字、邮箱、账号标识的字段尽量用占位符替换。4.4 常见问题速查表下面这个表是我做多个CoT Agent后整理的排错速查清单。遇到问题先对照这张表定位能省不少时间。症状典型原因处理办法JSON解析失败模型在JSON前后添加解释性文字或Markdown代码块使用raw_decode或正则提取JSON同时重试提示“只输出JSON”action_input凭空编造没有约束参数来源在Prompt中强制“参数必须来自用户或上一次观察”缺参时输出ask_clarification连续多轮重复相同thought缺少状态感知或没有强制行动记录thought归一化结果重复两次后注入打断消息不调用工具直接final_answer模型认为“凭记忆可答”或没理解工具必要性增加“若任务需要外部信息必须先调用工具”的规则最终答案没有引用工具结果Observation没有进入上下文或模型忽略把工具观察作为用户消息追加要求最终回答必须基于最近一次观察敏感信息出现在日志或界面内部CoT与用户展示未隔离内部展示只保留动作摘要日志字段脱敏Prompt中禁用敏感原文复述长任务中途跑偏缺少反思和重规划机制使用“计划-执行-反思”三阶段模板每步检查needs_replan表里的每一行都不是理论而是“现象-原因-解法”的直接对应。真正排查时你还需要结合具体日志看是“模型错”还是“代码错”把模型原始返回打印出来先确认它到底输出了什么再决定是调Prompt还是修解析逻辑。最后再分享一点很个人的体会。我最早做Agent时以为思维链写得越详细越好后来发现不是。CoT真正的作用是“把判断过程暴露给系统”而不是“把模型想事情的过程变成小作文”。一套好的CoT设计往往需要针对你的工具、数据和容错方式反复调不存在万能模板。如果你刚开始尝试建议先跑通一个“最简单版本”一个大模型API、三个工具、一个循环把CoT模板固定下来然后让它在真实任务上犯错。观察它在哪一步偏了再往Prompt里加约束。等流程稳定了再从单链路CoT升级到多路径CoT。这个过程比直接套用复杂框架要可靠得多。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑