游乐游手机版
首页/AI教程/文章详情

AI认知架构从模型到落地RAG与Dify沙箱及提示词编排实战

时间:2026-06-03 18:32
agent的全景认知和一个node沙盒小工具 一、AI 认知模型全景视图 先说几个核心判断。你看下面这个分层,基本把当前 AI 应用的核心要素都串起来了——从底层大模型到上层应用,每一层都是绕不开的关卡。 ┌──────────────────────────────────────────────

agent的全景认知和一个node沙盒小工具

一、AI 认知模型全景视图

先说几个核心判断。你看下面这个分层,基本把当前 AI 应用的核心要素都串起来了——从底层大模型到上层应用,每一层都是绕不开的关卡。

┌─────────────────────────────────────────────────────────────┐│应用层 (Application)││Manus · ClawBot· 垂直领域 Agent│├─────────────────────────────────────────────────────────────┤│编排层 (Orchestration)││Dify(低代码) · LangChain(编程框架) · LangGraph(状态机)│├─────────────────────────────────────────────────────────────┤│记忆层 (Memory) ││Vector DB · RAG · 上下文压缩 · 长期记忆机制 · Milvus│├─────────────────────────────────────────────────────────────┤│模型层 (Foundation Model) ││GPT-4o · Claude · DeepSeek · 多模态大模型│└─────────────────────────────────────────────────────────────┘

可以说,未来的 AI 应用,不再是“一个模型单打独斗”的玩法,而是“记忆 + 规划 + 工具”这套认知架构在比拼。对于我们这些从业者来说,真正值得下功夫的地方,就是借助已有的AI能力,去搭建像 ClawBot 这样的应用,解决从模型到落地之间的“最后一公里”问题。

二、认识模型:从 DeepSeek 的开放平台开始

要动手,得先熟悉几个“菜市场”在哪里。这里介绍两个常用的模型平台入口:

  1. DeepSeek 开放平台(api-docs.deepseek.com/zh-cn/):参数比较少,对初学者很友好,适合上手感受一下模型API的调用方式。
  2. 阿里云百炼:功能和交互跟 OpenAI 比较像,模型种类丰富,支持自定义调试,文档也详细。如果你需要做高定制、多Agent协作的复杂场景,这个会更顺手。

使用这些平台,通常需要一个 Base API 地址和对应的 API Key(密钥)。然后根据你用的框架或编排工具,选择对应的模型名。比如 DeepSeek 就分 Chat 模型和 Reasoner 模型,各有侧重。

除了模型选择,还有个关键参数——温度(Temperature)。可以把它理解成模型的“创意旋钮”或者“发散程度调节器”,核心是控制概率分布的尖锐度。

温度值特征典型应用输出特点
0 - 0.3确定性/保守代码生成、数学计算、知识问答、JSON事实准确、逻辑严谨,可能机械、缺乏变化
0.4 - 0.7平衡通用对话、写作辅助、商业文案正确性与自然流畅度兼顾
0.8 - 1.2创造性创意写作、头脑风暴、诗歌、角色扮演词汇丰富、想象力强,可能跑题
> 1.5混乱/发散艺术实验、随机灵感、对抗性测试可能荒诞、跳跃、非逻辑

三、现代模型进化的四大共同趋势

如果你一直在关注模型更新,会发现尽管各家有各家的法宝,但有几个方向几乎是共识:

  1. Agentic 能力(自主袋里):所有模型都在强化工具调用链、多步骤规划、环境交互能力。Gartner 甚至把 Agentic AI 列为 2025 年顶级战略技术趋势,预计年增长率 40%。
  2. 推理架构的显性化:不再把思考过程藏在黑盒里,而是提供“思考模式”(Thinking/Deep Think)开关,让你在速度和深度之间做权衡。这其实是从简单的模式匹配,转向了结构化的推理。
  3. 上下文窗口的军备竞赛:从 Kimi 的 256K,到 GPT-5 的 400K,再到 Claude 的 1M,目标都很明确——让模型能一次性处理“整本书”或“整个代码库”,从而降低对 RAG 精度的依赖。
  4. 垂直专业化与成本分层:同一模型家族内部开始分化,有标准版(推理强)、Mini 版(成本低)、Nano 版(延迟低),背后是自动路由系统为用户匹配最优性价比。

总结下来,当下主流模型主要在工具调用、推理架构、上下文、成本四个方向优化。下面咱们重点聊聊 AI 工具调用——为什么大厂都在抢,它到底由哪些方案组成。

对于生产级的模型落地,记忆层和编排层是绕不过去的两道坎。即便你没有直接用 Milvus 或 LangChain,系统里也必然要实现这两层的等效功能,哪怕是用代码硬编码。这些工具是思想的具体实现,我们可以不用,但思路得吃透。

四、记忆层:解决“模型天生健忘”的硬伤

核心问题在于,基础大模型是无状态(Stateless)的——每次 API 请求都是独立的,它不会记得5分钟前的对话,更记不住你上周提过的业务数据。

所以落地时必须面对几个实际问题:

  • 上下文稀缺:就算 GPT-5 支持 400K tokens,全量塞进去既贵又慢,而且跨会话的记忆就别想了。
  • 知识过时:模型训练数据有截止日期,新知识得靠 RAG 接入实时知识库。
  • 个性化缺失:没有记忆层,每次对话都是从零开始,没法“越用越懂你”。

大量上下文塞给模型也会带来麻烦:一是干扰多,结论不精确;二是 token 消耗大、分析时间长;三是很难对企业内部资源做有效整合。

来看一个简单的对比,就能明白“玩具级”和“生产级”的差距在哪:

简单玩具生产级落地
直接塞历史消息到 messages 参数向量数据库(Milvus/Pinecone)存储海量文档
用全局变量存临时状态长期记忆机制(如 MemGPT)管理记忆优先级
不管 token 消耗上下文压缩(RAG 重排序、摘要机制)控制成本

说白了,记忆层就是让模型从“考试机器”变成“业务专家”的基础设施。没有它,模型只能回答通用问题。下面重点讲 RAG 模式和历史管理方案,而后者在各个编排工具里都有内置方案,这里不做深究。

4.1 什么是 RAG(Retrieval Augmented Generation)

RAG(检索增强生成),意思就是把信息检索和文本生成结合起来,让大模型在生成答案时能参考外部知识库里的相关信息。它的效果好不好,取决于三个核心阶段:

  1. 建立索引:把知识文档解析、切片并向量化。
  2. 检索召回:根据用户查询,从向量存储里匹配出最相关的知识片段。
  3. 生成答案:大模型结合召回片段和用户查询,生成最终答案。

在 LangChain 的官方定义里,RAG 架构有三种模式,是层层递进的。

4.2 RAG 流程一:建立索引(Indexing)

  1. Loader(加载):通过文档加载器读取文件。
  2. Split(拆分):文本拆分器把大型文档切成更小的块,方便索引和塞进模型有限的上下文窗口。如果文档超过约 42000 字符,对多数模型来说就太长了;就算能塞进去,极长的输入也很难定位关键信息。所以必须先切块,再嵌入和存储。
  3. 存储:通过向量存储和嵌入模型,把切好的内容向量化后存入数据库(比如 Milvus),方便后续检索。

文本怎么变成向量的呢?简单说,就是通过 Embeddings 模型把文本转成向量,再写到 Milvus 这样的向量数据库里。

举个例子:

Dimension of embeddings: 1536Input: 风急天高猿啸哀, embedding is: [-0.0016666285653348784, 0.008690492014557004, ...]Input: 渚清沙白鸟飞回, embedding is: [0.018255604113922633, 0.030631669725945727, ...]Input: 无边落木萧萧下, embedding is: [-0.01270165436681136, 0.011355212676752505, ...]Input: 不尽长江滚滚来, embedding is: [0.003449439128962428, 0.02667092110022496, ...]

4.3 RAG 流程二:检索与生成 —— Agent 调用向量数据库

检索的本质:语义匹配

当用户问:“公司的年假怎么休?”

  • 传统搜索:只能找到包含“年假”关键词的文档,但可能漏掉“带薪休假”、“年休假”这些同义表达。
  • 向量检索
    • 把问题转成向量,比如 [0.023, -0.151, 0.892, ...](768维或1536维)。
    • 在 Milvus 里计算余弦相似度,找到向量距离最近的文本块。
    • 返回 Top 5 最相关的段落,哪怕里面没出现“年假”两个字,语义相关的内容也能找出来。
把检索封装成“工具”(Tool)

Agent 默认是没有查库能力的,需要把检索包装成工具(Tool)才能调用。在提示词里说明:当问题属于某类时,就用向量数据库做相似度搜索,返回相关片段;Agent 整合搜索结果后,由大模型生成答案。向量库在这里还能加筛选条件,百万级数据场景下作用很明显。

完成上面两步,就得到了一个基础的 RAG 检索流程,在 LangChain 官网被称为 2-Step RAG。

另外还有两种进阶模式:

  1. Agentic RAG(智能体检索增强生成):让大模型自己判断是否需要查文档、调数据库或调网络 API,然后再决定调用哪些工具。
  2. Hybrid RAG(混合检索增强生成):在标准模式基础上加了多重校验:
    • 先评估问题是否合理,不合理就改写后重查;
    • 再评估检索结果是否满意,不满意就重新检索;
    • 最后让 AI 自己判断错误从哪里来,并从对应节点重新开始。

4.4 三种 RAG 模式概览

方案 A:标准模式(2-Step RAG)

“先查资料,再回答”——简单直接。

工作流程:

  1. 用户提问:“今年的差旅报销标准是什么?”
  2. 系统自动在知识库找到《财务制度》第 3 章相关内容。
  3. AI 基于该内容生成回答,并标注来源。

核心价值:

  • :固定流程,一次检索加一次生成,响应通常在 2 秒内。
  • 省成本:步骤简单,调用 AI 次数少。
  • :结果可预测,不会“乱翻书”。
方案 B:智能模式(Agentic RAG)

“让 AI 自己决定要不要查资料”。

工作流程:

  1. 用户复杂提问:“对比我们公司和竞争对手在华东区的定价策略差异,并给出建议。”
  2. AI 自主分析:“需要分三步:先查内部定价表(工具1),再查竞品公开信息(工具2),最后做对比分析。”
  3. AI 按需调用多个工具,层层推进,最终生成报告。

核心价值:

  • 灵活:复杂问题能“见招拆招”。
  • 多工具协作:可以查文档、调用计算器、访问数据库、抓取实时网页等。
  • 类人思维:先收集信息再下结论。
方案 C:质控模式(Hybrid RAG)

“查完还要验,错了就重来”——一个带质检的严谨方案。

工作流程:在标准模式基础上加入三重校验:

  1. 问题优化:用户问得模糊?先改写成更精准的问题(比如把“那个谁”改成“张三”)。
  2. 检索验证:查到内容是否相关?如果不相关,自动换关键词重查。
  3. 答案核查:生成的回答是否符合事实?如果有矛盾,要么重新生成,要么标注“存疑”。

核心价值:

  • 风险可控:每步都有校验,减少用错误信息作答。
  • 自我修正:发现答非所问或信息不足时,可以自主优化流程。
  • 审计友好:全程可追溯,知道参考了哪些资料、为什么这么回答。

五、编排层

模型和记忆都有了,但还得有人来决定:什么时候调模型、什么时候查库、什么时候调工具、顺序怎么排——这一层就是编排层。就像前面提到的 RAG 生成,什么时候去查询向量数据库,如何分析问题,如何评估检索结果是否满意?这些都需要借助编排工具来实现。当然,像 Manus 或者 ClawBot(OpenClaw),他们并没有直接使用这些现成的编排工具,而是自己搭建了一套。

5.1 主流编排工具对比

下面是当前比较主流的四类编排工具,兼顾了可视化/低代码和编码/框架两种路线,方便你根据团队习惯选型。

工具类型GitHub Stars优点缺点
Dify可视化 / 低代码平台128k工作流 + RAG + Agent 一体;可视化画布、Prompt IDE;50+ 内置工具;Docker 一键部署、多模型支持;LLMOps 与观测完善自托管需自行维护;企业级能力依赖商业版;偏“平台”思维,深度定制要理解其抽象
LangChain编程框架(Python/JS)126k生态最大、文档与社区成熟;可编程、灵活度高;与 LangGraph、LangSmith 等配套;模型/向量库/工具集成丰富学习曲线陡;版本迭代快、API 易变;偏开发者,无开箱即用的可视化编排
AutoGen多智能体编程框架54.2k多智能体对话与协作原生支持;微软背书;支持 MCP、AgentTool 等;提供 AutoGen Studio 可做低代码原型官方建议新人关注 Microsoft Agent Framework;维护重心可能向新框架倾斜;纯代码编排需一定 Python 能力
Coze Studio可视化 Agent 开发平台19.7k脱胎于字节 Coze,一站式可视化;插件、知识库、工作流、API/SDK 齐全;Go 后端 + React/TS 前端,DDD 架构,便于二次开发相对较新,生态小于 Dify/LangChain;需自建并配置模型;公有云能力与商业版有差异

做个小结:如果想快速搭个 Demo、少写代码,优先看看 Dify 或 Coze Studio;如果追求全代码控制、对接自有系统,LangChain / LangGraph 是首选;如果要做多 Agent 协作、偏研究性质的,可以关注 AutoGen 或 LangGraph。

5.2 重点了解:Dify 与 LangChain

下面主要围绕 Dify 和 LangChain 两种编排方式展开。得益于 MCP 等协议的发展,两者其实不是互相排斥的,反而形成了很好的互补:

  • 简单到中等复杂度的 AI 应用:适合用 Dify(可视化、少写代码、快速上线)。
  • 高度复杂、强定制的应用:适合用 LangChain(全代码控制、深度集成业务、自定义链与 Agent)。

编排工具会不断更新,也总有更好的替代品出现,但编排的逻辑和相关的 AI 概念(记忆、分类、流式、并行、Agent 汇总等)是通用的。这些思想学会了,可以用在 Dify 上,也能用在其他工具上,值得长期投入。

5.3 Dify 简介:能帮我们做什么?

Dify 是个低代码/可视化的编排工具。借用官网的描述:它支持本地化部署;能用插件能力对接外部 API 和数据库;通过模板节点输出结构化的数据(比如 JSON),方便前端或下游系统处理。了解这些,你就能快速搭几类常见场景了。

5.4 Dify 场景示例

  1. 表单预填:用户进入表单页时,调用插件查询近几次提交或用户偏好,通过模板节点输出预填数据(比如 JSON),前端解析后填充表单。要点就是插件查数据加模板节点出结构化结果。
  2. 文本片段补全:根据前文内容补全后续文本。创建一个 Chatflow 并开启流式输出,用代码节点做后处理使输出更可控;用问题分类器节点将请求路由到最合适的场景;启用记忆,在分类与补全时带上对话历史,提高多轮准确性。实际做的话,可以做成“每输入一段或停顿一段时间再触发补全”,在体验和成本之间找个平衡。
  3. 长文本汇总助手:文本量大时,用多个输入节点对长文分块、并发处理,最后汇聚到一个 Agent 节点做汇总。并发不仅能缩短总耗时,还可能因为分块聚焦而提高汇总质量。

这些示例体现了 Dify 中插件、模板、流式、分类、记忆、并行与汇聚等编排思想,而在 LangChain 等框架里,这些概念也是同理的,只是实现方式从“画布拖拽”变成了“代码编排”。

5.5 LangChain 简介

LangChain 是编程优先的 AI 应用编排框架,支持 Python 和 Ja vaScript。它通过代码把模型、提示词、工具、记忆、向量库等组件组装起来,构建可复用的链(Chain)和智能体(Agent)。

跟 Dify 对比的话,Dify 是用画布和节点来完成编排,而 LangChain 是用代码来定义每一步——链式调用、条件分支、工具选择、多轮记忆都由你显式控制。适合那些需要深度定制、对接自有系统或实现复杂 Agent 逻辑的场景。

几个核心概念快速了解:

  • Chain:把提示词、模型、解析等步骤串成一条流水线(比如 RAG 链:检索 → 拼上下文 → 调用模型 → 解析输出)。
  • Agent:由模型根据当前输入决定“下一步调用哪个工具、传什么参数”,多步推理与工具调用循环进行。
  • Tool:可被 Agent 调用的能力单元(查库、调 API、执行代码等),在 LangChain 中通常封装为函数并绑定到模型。
  • Memory:在对话或链式调用中持久化或读取历史(比如缓冲记忆、摘要记忆),供多轮对话或长流程使用。

下面举几个 LangChain 的实际使用场景和思路:

  1. API 文档分析,生成 Mock 接口:用 Chain 串联:解析文档(比如 OpenAPI/Swagger)→ 提取路径、参数、响应结构 → 用 LLM 生成对应的 Mock 数据或 Mock 服务代码;可以配合模板和校验步骤,保证输出格式稳定。
  2. 低代码平台 AI 生成自定义组件:用 Agent + Tool:把“读设计稿/需求描述”、“查组件库 API”、“写代码”、“执行预览”封装成 Tool,由模型按需调用;多轮时启用 Memory 记住已选组件和约束,保证风格一致。
  3. 自动化运维部署、仓库管理:用 Agent + Tool:把执行命令、读仓库状态、发 MR、查流水线等封装为 Tool,由 Agent 根据自然语言指令选择并执行;复杂流程可以用 LangGraph 做有状态编排,或者跟现有 CI/CD 脚本对接。

延伸一下:以上场景都是“在代码里显式定义每一步”,适合对可控性、可测试性和与现有系统集成要求高的场景。如果只是快速出个 Demo 或者让业务方拖拽配置,可以先拿 Dify 搭主流程,再用 LangChain(或通过 MCP 暴露的能力)来承接复杂逻辑与自动化,两者形成互补。

六、AI 集成应用与 ClawBot 简介

前面说的模型层、记忆层、编排层都是“能力层”,最终要落到用户能直接使用的应用上。这一节就来聊聊什么是 AI 集成应用,并以 ClawBot / OpenClaw 为例,看看它们跟各层的关系、做了哪些创新,以及自研框架和 Dify/LangChain 这些流行框架的异同。

6.1 什么是 AI 集成应用

简单说,AI 集成应用就是把模型层(推理)、记忆层(RAG/长期记忆)、编排层(何时调模型、何时查库、何时调工具)以及工具能力(填表、发邮件、跑脚本、控制设备等)打包成终端用户可以直接使用的产品——比如聊天机器人、个人助理、垂直领域 Agent 等。用户不关心底层用的是什么模型、哪套向量库,只关心“能对话、能办事、能记住我”。

跟分层的关系可以这样理解:

层次作用与“AI 集成应用”的关系
模型层提供推理与生成能力应用通过 API 调用模型,得到回复或决策
记忆层存储与检索知识、对话历史、用户偏好应用把“要记住什么”“要查什么”交给记忆层,再拼进上下文
编排层决定调用顺序、分支、工具选择应用要么使用现成编排工具(如 Dify、LangChain),要么自研编排逻辑(如 ClawBot)
应用层面向最终用户的产品形态AI 集成应用就站在这一层,整合下层能力,并通过 IM、Web、API 等渠道交付给用户

所以结论是:AI 集成应用 = 在应用层,对模型层、记忆层、编排层(及工具)做整合后的可交付产品。ClawBot / OpenClaw 正是这类产品——他们没有直接采用 Dify 或 LangChain,而是自研了编排与技能体系,从而更贴合“个人助理 + 多渠道 + 本地优先”的定位。

6.2 ClawBot / OpenClaw 简介

ClawBot(后来曾用名 Moltbot,开源版本常称 OpenClaw)是一类个人 AI 助手型集成应用,典型特点包括:

  • 可执行实际操作:不限于聊天,能运行命令、填表格、发邮件、控制浏览器与智能家居等,充当“数字个人助理”。
  • 多渠道集成:接入 WhatsApp、Telegram、iMessage、Slack、Discord 等,在用户日常使用的通讯工具里交互,实现“像和真人对话一样”的体验。
  • 对话记忆:能记住较长时间的对话内容,并支持主动提醒等能力,依赖自研或集成的记忆机制。
  • 技能系统:通过 AgentSkills 兼容的技能(或类似插件)扩展能力,用户可从 ClawdHub 等技能库发现、安装、更新技能,形成可扩展的生态。

这类产品把“模型 + 记忆 + 编排 + 工具”全部收口在一个应用里,用户看到的只是一个“助理”,背后则是模型层、记忆层、编排层的协同。

6.3 与模型层、记忆层、编排层的关系

ClawBot / OpenClaw 作为应用层产品,跟各层的关系可以概括为:

  • 模型层:通过接入 OpenAI、Claude、DeepSeek 等厂商的 API 获得推理与生成能力;应用负责组请求、解析回复。
  • 记忆层:采用自研记忆方案,比如用 Markdown 文本文件存储 AGENTS.md、SOUL.md、MEMORY.md、每日记忆日志等,实现长期记忆与人格/行为设定。部分能力在概念上与 RAG/向量库类似,但实现方式更轻量、本地化。
  • 编排层:没有用 Dify、LangChain 等现成编排平台,而是自研编排与调度——比如通过 Gateway 控制平面统一管理消息通道、工具调用与事件触发。相当于“自建了一个小型编排引擎”,以便深度贴合 IM 接入、设备控制、技能加载等需求。

也就是说:他们站在应用层,自己实现了“何时调模型、何时查记忆、何时调哪个技能/工具”的逻辑,从而在架构上与“用 Dify 画布”或“用 LangChain 写链”形成差异:更垂直、更产品化,而不是通用编排平台。

6.4 他们做了什么创新

从公开资料和社区讨论来看,几个创新点很突出:

方向说明
插件化 / 技能生态从单体演进为插件化架构,模型提供商、技能等解耦为独立模块;通过 ClawdHub 等技能注册表,用户可发现、安装、更新技能,社区可贡献新能力,形成“应用 + 技能市场”的形态。
Gateway 控制平面消息通道、工具调用、事件触发由统一 Gateway 管理,类似“总控/接线员”,便于对接多种 IM 与设备,并统一做鉴权、限流、路由。
本地优先与记忆形态本地优先策略,敏感数据可只存本地;记忆采用 Markdown 文件(如 AGENTS.md、MEMORY.md、每日日志),用户和开发者可直接查看、编辑,透明且可版本管理。
多渠道 + 7×24 待命深度集成 IM,用户在常用聊天工具里即可与 AI 交互;结合自动化与记忆,实现“随时发消息、随时执行任务”的助理体验。
自动化执行深度不限于“聊天 + 查知识”,而是真正执行操作:填表、发邮件、跑脚本、控制浏览器与智能家居等,把“工具调用”做成产品级能力。

这些创新集中在应用形态、编排自研、记忆形态、技能生态、渠道与执行深度上,跟“只接一个模型 API”或“只用通用编排平台”的路线有明显区别。

6.5 自研框架与流行框架的异同

维度ClawBot / OpenClaw 自研路线Dify / LangChain 等流行框架
编排自研:Gateway + 自建调度逻辑,不依赖 Dify/LangChain通用编排:Dify 画布、LangChain 链/Agent,可复用于多种应用
定位应用层产品:直接面向终端用户(个人助理、IM 内使用)能力层/中间层:提供编排与集成能力,由业务再封装成应用
记忆自研:Markdown 文件、本地优先,强调可控与可读通用方案:多与向量库/RAG 集成,Dify/LangChain 均支持多种记忆与检索
扩展技能/插件:ClawdHub、AgentSkills 兼容技能,围绕“一个应用”扩展Tool/插件/工作流:Dify 插件与工作流节点、LangChain Tool,围绕“编排能力”扩展
渠道与部署深度集成 IM、本地优先、设备控制等,产品形态固定渠道无关:多为 API/SDK,由接入方决定 Web/IM/内部系统等
底层逻辑相同:仍是“模型 + 记忆 + 工具调用”的认知架构,只是编排与记忆的实现方式自研相同:同样依赖模型 API、记忆/检索、工具调用,只是以通用框架形式提供

小结一下:ClawBot / OpenClaw 是应用层的 AI 集成应用,在模型层、记忆层、编排层之上做整合;创新点在于自研编排与技能体系、Gateway 控制平面、本地优先的记忆形态、以及多渠道 IM 与深度自动化执行;跟 Dify/LangChain 的异同——底层思想一致(模型+记忆+工具),但不建在现成编排平台上,而是自研框架以更好地贴合“个人助理 + 多渠道 + 本地 + 技能生态”的产品目标。

七、沙箱插件功能实现

这一节来说说 CLI 中“AI 沙箱”插件的功能和实现思路,方便对内分享。实现是基于 LangChain 的 Tool 与 Agent 调用,用自然语言驱动“生成代码 → 沙箱执行”的闭环。

7.1 沙箱的概念:为什么需要沙箱

这里的沙箱(Sandbox)是指:在一个隔离的运行时环境里执行由 AI 生成的代码,而不是直接在宿主进程里执行

  • 为什么要隔离:用户说一句“生成一个 index.html”或者“把当前目录下所有 .txt 里的空格改成下划线”,我们会让模型生成一段 Ja vaScript 代码,再由程序执行。如果这段代码直接在宿主 Node 进程里跑,万一模型生成了 require('child_process').execSync('rm -rf /') 或者试图访问敏感环境变量,那就糟糕了。沙箱的作用就是限制执行环境:只暴露我们允许的 API(比如 createFilereadFilelistDir),不暴露完整的 requireprocess.env 等,从而把“AI 生成的代码”和“真实系统”隔离开。
  • 本项目的沙箱实现:使用了 vm2 库,把一段“沙箱全局对象”(包含 __workspacefspathcreateFilereadFile 等)注入到 VM 里,模型生成的代码只能访问这些对象;执行时用 IIFE 包裹代码,以便支持顶层的 return,执行结果或异常由宿主捕获后返回给用户。这样既能让 AI“写代码办事”,又不会越权操作宿主。

7.2 功能概述

  • 入口st sandbox <指令...>,例如 st sandbox 生成一个 index.htmlst sandbox 把当前目录下所有 .txt 里的空格改成下划线
  • 行为:根据用户指令,由调度模型决定是“执行代码/操作文件”还是“纯问答”;如果需要执行代码,则再调用一次模型,根据沙箱环境说明生成可在沙箱中运行的 Ja vaScript 代码,经过语法校验后在 vm2 沙箱中执行,最后把结果返回给用户。
  • 特殊处理:如果生成的代码会启动 HTTP 服务(比如 app.listen),进程会挂起并提示“按 Ctrl+C 停止”,避免服务立刻退出。

7.3 整体流程与 LangChain 概念对应

步骤实现LangChain 概念简述
1. 调度模型根据用户输入决定调用 sandbox 还是 direct_answerTool:把能力封装成工具,模型通过 tool_calls 选择调用;bindTools 将工具绑定到模型。
2. 生成代码沙箱工具内再调用一次模型,传入“沙箱环境说明”+ 用户指令,得到代码字符串SystemMessage / HumanMessage:构造对话;model.invoke(messages):同步调用模型。
3. 执行与回传在 vm2 中执行代码,将 stdout/返回值拼成字符串,作为 ToolMessage 回传给模型ToolMessage:工具执行结果必须用 tool_call_id 与之前的 tool_calls 对应,模型据此继续推理或结束。

多轮逻辑:命令里用 messages 数组累积 HumanMessage → AIMessage(含 tool_calls)→ ToolMessage → …,循环 model.invoke(messages) 直到模型不再发起 tool_calls,即完成一轮“调度 → 执行 → 回复”。

7.4 命令入口:调度 Agent 与工具循环

命令定义在 src/bin/command/agentCommand/sandbox.ts。核心是绑定两个 Tool,用系统提示词约束“何时调沙箱、何时直接回答”。

// 调度提示:需要动文件/跑代码 → sandbox;纯问答 → direct_answerconst DISPATCHER_SYSTEM_PROMPT = `你是调度助手:根据用户输入决定「要不要执行代码」,并只调用一个工具。规则:1. **需要执行代码/操作文件时** → 调用 `sandbox`,把用户的整句指令原样传入。2. **纯问答、不需要动文件或跑代码时** → 调用 `direct_answer`,用 `answer` 参数给出简短回答(1~3 句话)。3. 只调用一个工具,不要同时调用两个。`;const tools = [sandboxTool, answerTool];const model = createModel().bindTools(tools);const messages = [new SystemMessage(DISPATCHER_SYSTEM_PROMPT), new HumanMessage(instruction)];const response = await model.invoke(messages);// 若有 response.tool_calls,则执行对应工具,把结果 push 成 ToolMessage,再 invoke(messages),循环

  • bindTools(tools):LangChain 中把“可调用的工具列表”绑到模型上,模型输出中会包含 tool_calls(工具名 + 参数)。
  • ToolMessage:构造时需传入 tool_call_id(与 AIMessage 中某条 tool_call 的 id 一致),这样模型才能把“这条工具结果”跟“哪一次调用”对应起来。

7.5 Tools 如何写、为什么这么写

工具定义在 src/bin/langchain/tools/sandbox.ts。我们只暴露两个 Tool:direct_answer(纯问答)和 sandbox(执行代码/操作文件)。模型通过 tool_calls 选择调用哪一个,并传入对应参数。下面用实际代码说明怎么写以及为什么这么写。

7.5.1 为什么只有两个 Tool?

调度层只需要做二选一:“用户是在问问题” → 用 direct_answer 直接回答;“用户要动文件或跑代码” → 用 sandbox 生成代码并在沙箱里执行。如果只给一个“万能工具”,模型容易在纯问答时也去调沙箱,增加延迟和误执行风险;如果拆成很多小工具(比如“创建文件”“读文件”“执行命令”),调度模型要做的选择太多,容易选错。所以两个工具、职责清晰,便于在系统提示词里写清“何时调哪个”。

7.5.2 answerTool:纯问答工具

import { tool } from "@langchain/core/tools";import { z } from "zod";export const answerTool = tool((args: { answer: string }) => args.answer,{name: "direct_answer",description:"仅用于纯问答:用户只是提问、解释、闲聊,不需要创建/修改/删除文件或执行任何代码时使用。回答必须简短(一两句话到一小段),不要长篇大论。",schema: z.object({answer: z.string().describe("简短回答,控制在 1~3 句话内,不要过长"),}),});

  • name:模型在 tool_calls 里通过这个名字选中该工具;写成 direct_answer 与调度提示词里的“调用 direct_answer”一致。
  • description:最关键。LangChain 会把 description 发给模型,模型据此判断“当前用户输入是否属于纯问答”。这里明确写了“仅用于纯问答”、“不需要创建/修改/删除文件或执行任何代码”、“回答必须简短”,模型才会在用户问“什么是 RAG”时调这个工具,而不是调 sandbox。
  • schema:用 zod 声明参数。模型不会“自己发明”参数名,而是按 schema 里的字段生成 JSON;这里只有 answer 一个字符串,模型就会把要回复给用户的话放进 answer,我们直接 args.answer 返回即可。不写 schema 的话,模型不知道要传什么参数,容易不传或传错。
7.5.3 sandboxTool:沙箱执行工具

export const sandboxTool = tool(async (args: { instruction: string }) => {const { instruction } = args;// 1. 构造沙箱专用对话,让模型生成代码const messages = [new SystemMessage(SANDBOX_SYSTEM_PROMPT),new HumanMessage(instruction),];const res = await model.invoke(messages);const responseText = typeof res.content === "string" ? res.content : String(res.content ?? "");// 2. 解析代码 + 是否 HTTP 服务const { code, isHttpService } = parseCodeFromResponse(responseText);if (!code) return `未能从模型回复中解析出代码。...`;// 3. Acorn 语法校验const validation = validateSyntax(code);if (!validation.ok) return `语法校验失败:${validation.message}`;// 4. 在沙箱中执行try {const result = await runInSandbox(code);// 格式化结果,若为 HTTP 服务则加前缀供命令层挂起进程return isHttpService ? SANDBOX_HTTP_SERVICE_PREFIX + body : body;} catch (err) {return `沙箱执行错误:${msg}`;}},{name: "sandbox",description:"仅当用户需要「执行代码/操作文件」时使用:例如创建/修改/删除文件、生成 HTML、批量重命名、运行 shell 命令、读目录等。不需要动文件或跑代码时不要调用。",schema: z.object({instruction: z.string().describe("用户的自然语言指令,例如:生成一个 index.html;或:把当前目录下所有 .txt 文件名里的空格改成下划线"),}),});

  • namesandbox,与调度提示词里“调用 sandbox”一致。
  • description:明确写了“仅当用户需要执行代码/操作文件时使用”,并列举了例子(创建/修改/删除文件、生成 HTML、批量重命名、运行命令、读目录),最后强调“不需要动文件或跑代码时不要调用”。这样模型才会在用户说“生成一个 index.html”时调 sandbox,而在用户说“什么是 RAG”时不调。
  • schema:只有一个参数 instruction,类型为 string,describe 里给了示例。调度模型会把用户的整句指令原样放进 instruction,sandboxTool 内部再用这句 instruction 去调第二次模型(生成代码的那次),这样“调度”和“生成代码”就解耦了:调度只负责选工具+传参,生成代码由沙箱专用的 system prompt(SANDBOX_SYSTEM_PROMPT)约束。
  • 为什么工具内部再调一次模型? 因为“生成可在沙箱中执行的 Ja vaScript”需要另一套提示词(沙箱环境说明、默认目录、HTTP 服务标记等),跟“调度:选 sandbox 还是 direct_answer”的提示词不同。所以设计成:调度模型只做二选一并传 instruction;sandboxTool 内部用 SANDBOX_SYSTEM_PROMPT + instruction 再 invoke 一次模型,得到代码后再校验、执行、返回。这样职责清晰,也便于单独调优沙箱侧的提示词。
7.5.4 sandboxTool 内部四步(简要)
  1. 构造沙箱专用对话:SystemMessage(SANDBOX_SYSTEM_PROMPT) + HumanMessage(instruction)。SANDBOX_SYSTEM_PROMPT 由 getSandboxPromptContext("markdown") 生成,限制模型只使用沙箱内列出的 API。
  2. 解析模型回复:从回复中提取 ```js ... ``` 或裸代码,并判断是否包含 __IS_HTTP_SERVICE__ 或代码中是否有 .listen(,用于后续是否挂起进程。
  3. Acorn 语法校验:在进入 vm2 前用 acorn 做一次 parse,避免明显语法错误在沙箱里报错不明确或卡死。
  4. runInSandbox(code):调用沙箱模块执行代码;若为 HTTP 服务,在返回内容前加上 SANDBOX_HTTP_SERVICE_PREFIX,命令层检测到后挂起进程。

7.6 沙箱相关提示词:为什么要这么写

沙箱功能里用到两套提示词:一套在命令层做调度(选 sandbox 还是 direct_answer),一套在沙箱工具内部做代码生成(只生成可在沙箱中执行的 Ja vaScript)。两套职责分离,便于单独调优。下面分别看看关键内容,并说明为什么要这么写。

7.6.1 调度提示词(DISPATCHER_SYSTEM_PROMPT)

完整内容:

你是调度助手:根据用户输入决定「要不要执行代码」,并只调用一个工具。规则:1. **需要执行代码/操作文件时** → 调用 `sandbox`,把用户的整句指令原样传入。例如:创建/修改/删除文件、生成 HTML、批量重命名、列目录、运行命令等。2. **纯问答、不需要动文件或跑代码时** → 调用 `direct_answer`,用 `answer` 参数给出简短回答(1~3 句话,不要长篇大论)。3. 只调用一个工具,不要同时调用两个。不要输出过长文本。

为什么要这么写:

部分作用不这么写的后果
角色“你是调度助手”让模型只做“二选一”决策,不越权去解释或生成代码模型可能既调工具又自己啰嗦一大段,或去“猜”用户意图时输出长文
任务“根据用户输入决定要不要执行代码,并只调用一个工具”明确输出形态:只产生一次 tool_calls,且只选一个工具可能同时调 sandbox 和 direct_answer,下游难以处理;或多次调用增加延迟
规则 1“需要执行代码/操作文件时 → 调用 sandbox,整句指令原样传入”与 Tool 的 name: "sandbox" 一致;举例帮助模型判断“何时算需要动文件”模型容易在纯问答时也调 sandbox,或把指令改写成自己的话导致语义漂移
规则 2“纯问答时 → 调用 direct_answer,answer 参数 1~3 句话”与 Tool 的 name: "direct_answer" 和 schema 里的 answer 一致;约束长度便于终端展示纯问答时模型可能调 sandbox 或输出过长,影响体验和 token
规则 3“只调用一个工具,不要输出过长文本”强化“只选一个”和“不附带长文”,避免模型在 tool_calls 之外再输出大段说明容易出现“调了工具又输出好几段话”或一次多个 tool_calls

总结:调度提示词的核心是角色单一(调度)、任务清晰(二选一)、规则与 Tool 的 name/schema 一一对应,这样模型才会稳定地“只选一个工具、传对参数”。

7.6.2 沙箱环境说明(prompt.ts:SANDBOX_SCHEMA / getSandboxPromptMarkdown)

作用:在沙箱工具内部,把“沙箱里有哪些全局对象/函数、怎么用”写成 Markdown(或 JSON),通过 getSandboxPromptContext("markdown") 拼进 SANDBOX_SYSTEM_PROMPT 的前半部分,让生成代码的那次模型只使用这些 API,不写 require('fs')require('path') 等(沙箱里没有原生 require)。

为什么要这么写:

  • 按类别分块(Node 内置、异步、Web 框架、子进程、便捷 API):模型按“要找什么能力”快速定位,减少幻觉(例如去用未暴露的 API)。
  • 写清默认操作目录“默认使用当前命令行所在目录(即 __workspace);路径用相对路径”:避免模型生成绝对路径或假设别的 cwd,导致执行时报“文件不存在”。
  • 便捷 API 用表格列出(createFile、readFile、listDir、rename、remove 等):表格比大段文字更利于模型“按名选用”,且与 common.ts 里真实暴露的函数一致;对易错点(如“改文件名时用 rename,不要只 createFile 新路径”)单独写一句,减少同一内容两份文件。
  • 启动服务、修改文件名等单独强调:因为模型常犯“只生成文件不 listen”或“只 createFile 新路径不删旧文件”的错,所以在环境说明里就约束好,比只在后面“要求”里写更醒目。
7.6.3 沙箱代码生成规则(SANDBOX_SYSTEM_PROMPT 后半部分)

在“沙箱环境说明”之后,工具里拼接的要求如下,每条都说明了为什么要这么写:

---请根据用户的自然语言指令,生成一段可在上述沙箱中执行的 Ja vaScript 代码。要求:1. 只输出代码,不要解释或 markdown 标题。2. 代码中只能使用上文列出的全局对象和函数。3. 若需要返回结果给用户,请用 return 或 console.log。4. 可以包在 ```js ... ``` 里,也可以直接输出裸代码。5. 如果你认为执行后有重大错误请直接返回 "执行失败"。6. **默认操作目录**:如果用户没有指定在哪个目录操作,默认使用当前命令行所在目录(即 __workspace);路径用相对路径如 '.' 或 '文件名' 即可。7. **启动服务型项目时**:尽量在沙箱内完成,单段代码中完成应用创建、路由、app.listen 等,不要只生成文件让用户自己运行或依赖沙箱外的步骤;用 app.listen(port, () => console.log('https://localhost:' + port)) 打印地址,代码末尾不要 return。8. **若本次生成的代码会启动 HTTP 服务**(如 Koa/Express 的 app.listen),请在代码块后单独一行写 __IS_HTTP_SERVICE__,以便宿主进程挂起(用户 Ctrl+C 停止)。9. **修改文件名或格式时**:用 rename(oldPath, newPath) 重命名,或先 readFile 再 createFile 新路径后 remove 原文件;不要只 createFile 新路径而保留原文件,避免同一内容有两份。

为什么要这么写:

目的不这么写的后果
1 只输出代码,不要解释或 markdown 标题便于从回复里稳定解析出“一段代码”;解析逻辑依赖“要么 ```js 块要么裸代码”模型输出大段说明 + 代码,解析容易取错或取到解释文本
2 只能使用上文列出的全局对象和函数与 prompt 前半部分“沙箱环境说明”一致,防止用 require('fs') 等沙箱内不存在的东西生成的代码在 vm2 里报 require is not defined 或访问未暴露 API 报错
3 返回结果用 return 或 console.log宿主通过 IIFE 的返回值或 stdout 拿结果;明确两种出口,模型不会只写中间变量不输出用户看不到执行结果,或解析不到返回值
4 可以包在 ```js 里也可以裸代码解析时两种都支持(parseCodeFromResponse),给模型容错空间若强制只接受一种,模型有时会选另一种导致解析失败
5 认为有重大错误时直接返回 "执行失败"避免模型硬生成明显会报错的代码,让工具层直接返回可读提示容易得到一段必然报错的代码,用户体验差
6 默认操作目录 __workspace,路径用相对路径与 prompt 前半部分一致;和 common.ts 里 createSandbox 的 root 对齐模型生成 /tmp/xxx 或其它绝对路径,在沙箱里路径不对
7 启动服务时在沙箱内完成 listen,不要只生成文件用户说“起一个 Koa 服务”时期望直接能访问,而不是再手动运行只生成 app.js 让用户自己 node app.js,不符合“一条指令完成”的预期
8 HTTP 服务时在代码块后写 IS_HTTP_SERVICE工具和命令层用该标记判断是否挂起进程(不退出),否则服务会立刻退出启动的服务一启动就退出,用户无法访问
9 改文件名用 rename 或 读→写新→删旧与环境说明里“不要只 createFile 新路径”一致,避免同一内容两份文件用户说“把 a.txt 改成 b.txt”时生成两个文件,语义错误

总结:前半部分(prompt.ts)负责能力边界与用法约定,后半部分(工具里拼接)负责输出格式、默认目录、HTTP 服务、重命名等易错约束;两段合在一起,模型才能稳定生成“可解析、可执行、行为符合预期”的沙箱代码。

7.7 沙箱环境与执行引擎

  • 环境定义src/bin/langchain/sandbox/common.ts):createSandbox(workspaceRoot) 返回一个对象,包含 vm2 所需的 sandbox 全局变量——如 __workspacefs/path/process/os/Buffer/util/console/URLPromise/setTimeoutKoa/koaRouterchild_process.execSync/spawnSync,以及便捷方法 createFile / readFile / listDir / rename / remove / copy / move / stat / listFilesRecursive / globFiles / requireFromWorkspace 等。路径未指定时默认为当前命令行所在目录(即 __workspace)。
  • 执行src/bin/langchain/sandbox/index.ts):使用 vm2 的 VM,把上述 sandbox 对象传入;为支持“顶层 return”,将用户代码包成 IIFE 再执行:

const wrapped = `(function() {n${code}n})()`;const result = vm.run(wrapped);return result instanceof Promise ? await result : result;

这样生成的代码里可以直接 return ...,返回值会交给命令层展示;若 return 的是 Promise,会 await 后再返回。

  • 提示词与 API 说明src/bin/langchain/sandbox/prompt.ts):SANDBOX_SCHEMA 描述沙箱内所有全局对象和函数的类型、参数、示例;getSandboxPromptMarkdown() 将其转成 Markdown 表格和列表,注入到 SANDBOX_SYSTEM_PROMPT,使模型只使用这些 API 生成代码,避免使用未暴露的 Node 或浏览器 API。

7.8 小结

模块作用
sandbox 命令解析 st sandbox <指令>,用调度 Agent + bindTools 在 sandbox / direct_answer 间选择,并循环处理 tool_calls,直到模型不再调用工具;检测 HTTP 服务标记后挂起进程。
sandboxTool根据用户指令调用模型生成沙箱代码 → Acorn 校验 → runInSandbox 执行 → 将结果或错误以 ToolMessage 形式返回。
answerTool纯问答时由模型填入 answer,原样返回给用户。
createSandbox / runInSandbox提供 vm2 沙箱全局环境(含 Node 与便捷文件 API),IIFE 包裹执行并支持 return/Promise。
prompt 与 SANDBOX_SCHEMA把沙箱能力描述成 Markdown/JSON,约束模型只生成合规、可执行的代码。

整体上,这就是一个“自然语言 → 调度 Agent(Tool 二选一)→ 沙箱工具内再调模型生成代码 → 语法校验 → vm2 执行 → 结果回传”的闭环,用到的 LangChain 概念包括 Tool、bindTools、SystemMessage/HumanMessage/AIMessage/ToolMessage、model.invoke。

八、提示词工程:如何写好提示词

前面提到的编排、RAG、沙箱等,最终都要通过“和模型说清楚要做什么”才能生效,这部分就是提示词工程。下面用通俗的方式说明其重要性,并给出一套可落地的书写原则,便于在业务里统一规范、减少无效调用。

8.1 为什么提示词重要

模型本身不会“主动知道”业务规则、输出格式或边界条件,这些都需要通过输入文本(提示词)显式地告诉它。提示词写得好,模型更容易:

  • 做对事:理解任务意图、约束和优先级,减少答非所问或越权行为。
  • 出对格式:便于下游解析(如 JSON、表格、代码块),减少二次清洗。
  • 控制成本与延迟:约束长度、禁止冗长解释,有利于 token 与响应时间可控。

因此,提示词是连接“业务需求”和“模型能力”的接口;写好提示词,是落地 AI 功能时性价比很高的投入。

8.2 如何写好提示词:几条可操作原则

可以归纳为角色、任务、约束、格式、示例五类要素,按需组合即可。

要素说明示例(一句话)
角色让模型进入“谁在说话”的设定,行为更一致“你是调度助手”“你是只生成代码的沙箱执行器”
任务用一句话说清“要干什么”,避免模糊“根据用户输入决定调用 sandbox 还是 direct_answer”“生成可在沙箱中执行的 Ja vaScript”
约束明确“不能做什么”“必须怎么做”,减少越界“只输出代码,不要解释”“只能使用上文列出的全局对象”“回答控制在 1~3 句话”
格式规定输出形态,方便解析与展示“返回 JSON”“用```js ... ``` 包住代码”“若启动 HTTP 服务请在代码块后写 IS_HTTP_SERVICE”
示例给 1~2 个输入输出样例,大幅提升格式与风格一致性“输入:生成 index.html → 输出:仅一段可执行代码”

书写习惯建议:

  • 先写“角色 + 任务”,再补约束和格式,最后需要时加示例。
  • 一条提示词只服务一个目标:调度就只做调度,生成代码就只生成代码,避免混在一起导致模型“不知道该优先听哪句”。
  • 关键规则可以用加粗或编号,便于模型注意(比如“只调用一个工具”“3. 只输出代码”)。
  • 迭代优化:用真实用例跑几轮,看模型常犯的错误(漏约束、格式乱、话太多),再回头改提示词补一句、减一句。

8.3 本文中的提示词实践(对应关系)

  • 调度提示词:角色是调度助手。任务是根据用户输入决定调用 sandbox 还是 direct_answer。约束是只调用一个工具、不要长篇大论。这样模型就不会既调工具又自己啰嗦一大段。
  • 沙箱代码生成提示词:角色是沙箱内代码生成器。任务是根据用户指令生成可在给定沙箱中执行的 Ja vaScript。约束是只使用列出的 API、默认目录为 __workspace、启动服务时标注 __IS_HTTP_SERVICE__。格式是可包在 ```js ... ``` 里或裸代码。示例通过 SANDBOX_SCHEMA / Markdown 表格把“有哪些函数、怎么用”写清楚,等于给了结构化示例。
  • 温度:虽然不算一句话提示词,但属于同一类控制:通过参数告诉模型“要更确定还是更发散”。代码/结构化输出用低温度,创意类用稍高温度,本质上也是在“写好”对模型的行为约束。

8.4 参照:业务中使用的提示词示例

下面摘录并整理业务里实际用过的提示词,作为“角色 + 任务 + 约束 + 格式 + 示例”的参照,便于对照前文原则做迭代。

示例一:生成管理后台页面的 Prompt(低代码 / 运营者模块)

角色/任务:让 AI 在“30% 框架内”生成管理后台页面代码,而非凭空创新;需求边界、数据格式、交互范式由人工先锁定。

约束与格式(节选):

  • 1.1 顶部导航栏:用 el-menu,背景色 #409EFF,文字白色,选中时背景加深 10%;左侧预留项目名称插槽(slot="brand");中间菜单项通过数组 Na vItems 注入,结构 Na vItem { name, route, icon? };暴露事件 @select、@menu-click。Store 的 module 划分、state 形状、mutation 名由人工先锁定,AI 只生成具体 CRUD 逻辑。
  • 1.2 左侧列表区块:点击高亮用 class 切换(.is-active);中间内容区 flex:1 自适应。
  • 1.3 左上角按钮组:添加按钮 el-button + Plus 图标、文字“新增”;搜索框 el-input + Search 图标,v-model 绑定 keyword,触发 @search。
  • 1.4 列表实现:字段校对由人工完成;AI 直接生成固定列数的 el-table,给出 骨架,prop 与后端字段保持一致。
  • 1.5 封装 ListTable 组件:外部传入 columns: { key, label, width?, slot? }[];内部维护 rendererMap,按 column.key 映射渲染函数;插槽命名规则 table-[key],如