低代码开发 AI Agent Harness Engineering:Coze/Dify 平台的高级玩法与局限性

摘要:在 AI 大模型时代,如何高效构建、部署和管理 AI Agent 已成为开发者关注的核心问题。低代码 AI Agent 开发平台如 Coze(字节跳动)和 Dify(开源)的出现,极大降低了 AI 应用的开发门槛。本文将深入探讨 AI Agent Harness Engineering(AI 代理工程化)的核心概念,详细解析 Coze 和 Dify 平台的高级玩法,并客观分析其局限性,帮助开发者在实际项目中做出明智的技术选型。


目录

  1. 核心概念解析
  2. 问题背景与挑战
  3. AI Agent 的技术架构深度剖析
  4. Coze 平台高级实战指南
  5. Dify 平台高级实战指南
  6. 平台局限性与技术陷阱
  7. 最佳实践与避坑指南
  8. 行业发展与未来趋势
  9. 总结与展望

核心概念解析

在深入探讨平台之前,我们首先需要厘清几个核心概念,建立统一的认知语境。

什么是 AI Agent?

AI Agent(人工智能代理)是指能够感知环境、做出决策并执行动作的智能系统。与传统的单次调用大模型不同,AI Agent 具备记忆能力工具使用能力规划能力

核心概念可以用一个简洁的公式来表达:

A g e n t = L L M + M e m o r y + T o o l s + P l a n n i n g Agent = LLM + Memory + Tools + Planning Agent=LLM+Memory+Tools+Planning

让我们拆解这个公式:

  1. LLM (Large Language Model): 作为 Agent 的“大脑”,负责理解、推理和生成内容。
  2. Memory (记忆): 分为短期记忆(上下文窗口)和长期记忆(向量数据库),让 Agent 能够“记住”历史交互。
  3. Tools (工具): 让 Agent 能够调用外部能力,如搜索、计算器、代码解释器、API 等。
  4. Planning (规划): 让 Agent 能够将复杂任务分解为子任务,并按步骤执行。

什么是 Harness Engineering?

“Harness Engineering”( harness 本意为“马具、挽具”,引申为“驾驭、利用”)在这里指的是系统性地工程化 AI Agent 的开发、测试、部署和监控的方法论。它不仅仅是写 Prompt,而是涵盖了从需求分析到运维监控的全生命周期。

Harness Engineering 的核心要素包括:

  • Prompt Engineering 2.0: 从单一 Prompt 到结构化的 Prompt 系统。
  • Workflow Orchestration: 工作流编排,将复杂任务拆解为可执行的步骤。
  • RAG (Retrieval-Augmented Generation): 检索增强生成,让 Agent 能够利用私有知识库。
  • Evaluation & Observability: 评估与可观测性,如何量化 Agent 的表现并监控其运行状态。
  • Safety & Guardrails: 安全与护栏,确保 Agent 的输出符合预期且安全。

Coze vs Dify:平台定位对比

在开始实战之前,我们先从宏观层面对比一下这两个平台的定位:

维度 Coze (扣子) Dify
开发公司 字节跳动 Dify.AI (初创公司)
开源属性 闭源 SaaS 核心代码开源 (GitHub)
部署方式 仅云端 云端 SaaS + 私有化部署
核心优势 与字节生态深度集成(抖音、飞书),Bot 市场丰富 高度可定制,支持自托管,LLM 供应商中立
目标用户 从普通用户到专业开发者 开发者、企业 IT 团队
价格策略 免费 (目前),有使用配额 免费版 + 专业版 + 企业版

问题背景与挑战

从“玩具”到“生产级”:AI Agent 开发的痛点

在 2023 年初,当我们谈论 AI Agent 时,很多人可能会想到 LangChain 写的几百行代码 Demo。但当我们真正试图将 Agent 投入生产环境时,会遇到一系列棘手的问题:

1. Prompt 的脆弱性 (Prompt Fragility)

“昨天还运行得好好的,今天怎么就不行了?”这是很多开发者的心声。Prompt 对 wording 极度敏感,稍微调整几个词,输出质量可能天差地别。

2. 状态管理的复杂性

传统的 Web 开发是无状态的,但 Agent 是有状态的。如何管理对话历史?如何在多轮对话中保持一致性?当用户量上来时,内存管理成为巨大挑战。

3. 工具调用的不可靠性

让 Agent 调用工具听起来很美好,但实际中:API 报错了怎么办?返回格式不对怎么处理?需要重试吗?这些边缘 Case 处理起来极其繁琐。

4. 可观测性的缺失

当 Agent 做出一个错误的决策时,你知道它“为什么”这么想吗?传统的 logging 不足以展示 Agent 的“思维链”(Chain of Thought)。

5. 评估体系的空白

如何量化一个 Agent 的好坏?准确率?响应时间?用户满意度?缺乏标准化的评估指标,使得迭代优化变得困难。

正是这些痛点,催生了 Coze、Dify 这一类低代码 AI Agent 开发平台。它们试图将上述复杂的工程问题封装起来,让开发者聚焦于业务逻辑本身。


AI Agent 的技术架构深度剖析

在开始玩平台之前,我们需要“知其然,更知其所以然”。让我们抽丝剥茧,看看一个生产级 AI Agent 系统的内部到底长什么样。

概念结构与核心要素组成

一个完整的 AI Agent 系统通常由以下几层构成:

基础设施层

工具与模型层

核心服务层

用户层
Web/APP/飞书/微信

编排层
Orchestrator

Agent 执行引擎

记忆模块
Memory

知识检索模块
RAG

工具生态
Search/Code/API

模型网关
Model Gateway

向量数据库

关系型数据库

可观测性系统
Observability

让我们详细解释每个核心组件:

1. 编排层 (Orchestrator)

这是整个系统的“总指挥”。它负责接收用户请求,管理对话会话(Session),并协调各个组件工作。在复杂场景下,它可能需要处理状态机、超时控制、fallback 策略等。

2. Agent 执行引擎

这是核心逻辑所在。它通常包含以下几个子模块:

  • Prompt 模板引擎: 动态渲染 System Prompt、Few-Shot 示例等。
  • 思维链 (CoT) 控制器: 决定是直接回答还是调用工具。
  • 输出解析器 (Output Parser): 将 LLM 的非结构化输出解析为结构化数据(如 JSON)。
3. 记忆模块 (Memory)

记忆模块决定了 Agent 的“记性”有多好。

  • 短期记忆: 通常就是 LLM 的 Context Window。
  • 长期记忆: 将历史对话Embedding后存入向量数据库,需要时进行语义检索。
  • 实体记忆: 提取对话中的关键实体(如人名、日期),存储为结构化数据。
4. RAG 模块

RAG (Retrieval-Augmented Generation) 让 Agent 能够“查阅资料”。
流程通常是:文档切分 -> 向量化 -> 索引构建 -> 语义检索 -> 重排序 (Rerank) -> Prompt 注入

概念之间的关系:ER 实体关系图

为了更清晰地展示这些概念之间的关系,我们来看一个 ER 图:

发起

包含

拥有

由...组成

使用

配置

可能触发

可能触发

查询

可以连接

USER

SESSION

MESSAGE

MEMORY

STEP

AGENT

TOOL

PROMPT_TEMPLATE

LLM_CALL

TOOL_CALL

RAG

VECTOR_STORE


Coze 平台高级实战指南

Coze (扣子) 是字节跳动推出的 AI Bot 开发平台。它的核心理念是“通过编排插件(Plugins)和工作流(Workflows)来构建 Bot”。

Coze 核心概念梳理

在 Coze 中,有几个高频词汇我们必须掌握:

  1. Bot (机器人): 最终交付给用户的产品形态。
  2. 人设 (Persona): 定义 Bot 的身份、性格和回复风格。
  3. 插件 (Plugins): Bot 可以调用的工具集(如搜索、图片生成)。
  4. 工作流 (Workflows): 可视化的逻辑编排画布,用于处理复杂任务。
  5. 知识库 (Knowledge): 让 Bot 拥有特定领域的知识。

高级玩法一:复杂工作流 (Workflow) 设计

很多初学者用 Coze 只是简单地在“人设”里写一段话,然后添加几个插件。但 Coze 的真正威力在于其 Workflow

场景:智能客服工单系统

假设我们要构建一个客服 Bot,它需要:

  1. 理解用户意图(是投诉、咨询还是报修?)。
  2. 如果是报修,收集设备型号、故障描述、联系方式。
  3. 生成一个工单 ID,并调用内部 API 创建工单。
  4. 将结果以友好的方式反馈给用户。

如果只用单纯的 Prompt + LLM,很难保证信息收集的完整性和 API 调用的稳定性。这正是 Workflow 的用武之地。

Coze Workflow 核心组件解析

在 Coze Workflow 中,我们主要使用以下节点:

  1. Start/End: 工作流的起点和终点。
  2. LLM: 调用大语言模型。
  3. Code: 编写自定义 Python 或 JavaScript 代码。
  4. Condition: 条件判断(if-else)。
  5. API: 发送 HTTP 请求。
  6. Variable Assign: 变量赋值。
  7. Form: 结构化表单收集(这是关键!)。
代码级理解:Form 节点的力量

Coze 的 Form 节点本质上是一种 Slot Filling (槽位填充) 机制的可视化封装。

让我们用 Python 代码模拟一下 Form 节点背后的逻辑:

from typing import Dict, Any, Optional
from dataclasses import dataclass

@dataclass
class Slot:
    name: str
    description: str
    required: bool = True
    type: str = "string"
    options: Optional[list] = None

class FormFiller:
    def __init__(self, slots: list[Slot]):
        self.slots = slots
        self.collected_data: Dict[str, Any] = {}
    
    def get_missing_slots(self) -> list[Slot]:
        """获取还未填充的槽位"""
        return [slot for slot in self.slots 
                if slot.name not in self.collected_data]
    
    def generate_question(self) -> Optional[str]:
        """根据缺失的槽位生成问题"""
        missing = self.get_missing_slots()
        if not missing:
            return None
            
        next_slot = missing[0]
        # 这里实际上是 LLM 来生成自然语言问题
        # 我们这里做简化
        return f"请问您的{next_slot.description}是什么?"
    
    def fill_slot(self, slot_name: str, value: Any):
        """填充槽位"""
        # 实际上会有 LLM 做 NLU (自然语言理解) 来提取值
        self.collected_data[slot_name] = value

# --- 使用示例 ---
if __name__ == "__main__":
    # 定义我们需要收集的信息(槽位)
    slots = [
        Slot("device_type", "设备型号"),
        Slot("problem_desc", "故障描述"),
        Slot("phone", "联系电话")
    ]
    
    form = FormFiller(slots)
    
    # 模拟对话流程
    while True:
        question = form.generate_question()
        if not question:
            print("信息收集完毕!", form.collected_data)
            break
        
        print(f"Bot: {question}")
        user_input = input("You: ")
        
        # 简化:这里直接假设用户输入就是针对当前问题的答案
        # 实际 Coze 中会用 LLM 去判断用户这句话在回答哪个槽位
        current_slot = form.get_missing_slots()[0]
        form.fill_slot(current_slot.name, user_input)

关键点:Coze 的 Form 节点不仅仅是简单的问答,它还利用 LLM 做了 NLU (Natural Language Understanding)。这意味着如果用户说“我的手机是 iPhone 15,屏幕碎了,电话是 13800000000”,Coze 能一次性把三个槽位都填上,而无需机械地一个个问。

高级玩法二:利用数据库 (Database) 实现有状态应用

Coze 最近推出了 Database 功能,这是其从“对话 Bot”向“应用平台”演进的重要标志。

场景:每日打卡与积分系统

我们要做一个打卡 Bot:

  1. 用户发送“打卡”,记录当前时间。
  2. 查看“我的积分”,根据打卡天数计算。
  3. 查看“排行榜”。
核心实现思路
  1. 数据表设计: 在 Coze Database 中创建表 check_in_records,字段包括 user_id (文本), check_in_time (时间), points (数字)。
  2. Workflow 编排:
    • 首先判断用户意图(是打卡还是查询)。
    • 如果是打卡:
      • 查询数据库,看今天是否已经打过卡。
      • 如果没打,插入记录,并加 10 积分。
      • 如果打了,提示“今天已打卡”。
    • 如果是查询积分:
      • 执行 SQL SELECT SUM(points) FROM check_in_records WHERE user_id = ?
Coze 数据库操作的代码模拟

虽然 Coze 是可视化操作,但理解其背后的 SQL 逻辑至关重要:

import sqlite3
from datetime import datetime

class CozeDBSimulator:
    def __init__(self, db_name="coze_sim.db"):
        self.conn = sqlite3.connect(db_name)
        self._init_schema()
        
    def _init_schema(self):
        cursor = self.conn.cursor()
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS check_in_records (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_id TEXT NOT NULL,
                check_in_time TEXT NOT NULL,
                points INTEGER DEFAULT 10
            )
        """)
        self.conn.commit()
    
    def has_checked_today(self, user_id: str) -> bool:
        """检查用户今天是否已打卡"""
        today = datetime.now().strftime("%Y-%m-%d")
        cursor = self.conn.cursor()
        # 注意:这里简化了时间查询,实际需处理时区
        cursor.execute("""
            SELECT 1 FROM check_in_records 
            WHERE user_id = ? AND DATE(check_in_time) = ?
        """, (user_id, today))
        return cursor.fetchone() is not None
    
    def check_in(self, user_id: str) -> dict:
        """执行打卡操作"""
        if self.has_checked_today(user_id):
            return {"status": "fail", "msg": "今天已经打过卡啦!"}
            
        now = datetime.now().isoformat()
        cursor = self.conn.cursor()
        cursor.execute("""
            INSERT INTO check_in_records (user_id, check_in_time)
            VALUES (?, ?)
        """, (user_id, now))
        self.conn.commit()
        return {"status": "success", "msg": "打卡成功!积分 +10"}
    
    def get_total_points(self, user_id: str) -> int:
        """获取用户总积分"""
        cursor = self.conn.cursor()
        cursor.execute("""
            SELECT COALESCE(SUM(points), 0) FROM check_in_records
            WHERE user_id = ?
        """, (user_id,))
        return cursor.fetchone()[0]

# --- 测试代码 ---
db = CozeDBSimulator()
user = "user_001"

print(db.check_in(user))  # 成功
print(db.check_in(user))  # 失败,重复
print(f"当前积分: {db.get_total_points(user)}") # 10

高级玩法三:Prompt 优化与人设 (Persona) 工程

在 Coze 中,“人设与回复逻辑”是 Bot 的灵魂。这里分享几个高级 Prompting 技巧在 Coze 中的应用。

技巧 1:使用 XML 标签结构化 Prompt

Coze 的 Prompt 编辑器支持较长的文本,我们可以使用 XML 标签(Coze 官方推荐)来组织内容,这比纯文本更能让 LLM 理解结构。

# 角色
你是一位专业的技术支持工程师,名字叫 "小扣"。

# 技能
<skills>
<skill>
<name>故障排查</name>
<description>当用户描述电脑问题时,按步骤引导用户排查</description>
</skill>
<skill>
<name>情绪安抚</name>
<description>如果用户用词比较急躁,先安抚情绪再解决问题</description>
</skill>
</skills>

# 限制
<constraints>
1. 不要谈论与技术支持无关的话题。
2. 如果问题超出范围,引导用户转人工。
</constraints>
技巧 2:动态 Few-Shot Learning (少样本学习)

Coze 的变量系统非常强大。我们可以结合 知识库变量 来实现动态的 Few-Shot。

思路

  1. 在知识库中上传历史上的“优秀对话案例”。
  2. 在 Workflow 中,先根据用户的当前问题,去知识库检索最相似的 3 条历史案例。
  3. 将这 3 条案例作为 <examples> 注入到最终的 LLM Prompt 中。

Coze 平台局限性分析

尽管 Coze 非常强大,但它也不是万能的。

  1. 数据主权与私有化部署: Coze 目前是纯 SaaS 服务。对于金融、医疗等对数据敏感的行业,将数据上传到第三方云平台存在合规风险。
  2. 自定义模型的限制: Coze 主要绑定字节的豆包模型。虽然也支持 GPT-4,但在国内网络环境下配置较麻烦,且无法接入企业自训练的私有模型。
  3. Workflow 的黑盒性: 当 Workflow 变得非常复杂(几十个节点)时,调试变得极其困难。你很难看到节点之间传递的完整中间态数据,也无法像传统代码那样设置断点。
  4. 版本控制缺失: 目前 Coze 没有类似 Git 的版本管理功能。如果你修改坏了 Bot,很难一键回滚到上一个版本。团队协作时,也容易出现配置冲突。

Dify 平台高级实战指南

如果说 Coze 是“开箱即用的 iPhone”,那么 Dify 更像是“自由度极高的 Android”,或者说“可以自己拼装的 PC”。Dify 是一个开源项目,这意味着我们可以深入其源码,甚至修改它。

Dify 核心概念梳理

Dify 的界面虽然也很友好,但概念比 Coze 更技术化一些:

  1. 应用 (Application): 对应 Coze 的 Bot。分为“聊天助手”、“文本生成”、“Agent”等几种类型。
  2. 提示词编排 (Prompt Eng.): Dify 将 Prompt 拆分为“前置指令”、“上下文”、“变量”等模块。
  3. 知识库 (Dataset): 功能比 Coze 更强大,支持多种文件格式,且对文档切分(Chunking)策略有更细粒度的控制。
  4. API 发布: Dify 生来就为了集成,一键生成后端 API。
  5. 日志与标注 (Logs & Annotations): 这是 Dify 非常出彩的地方,可以查看每一次请求的详细日志,并对不好的输出进行人工标注修正。

高级玩法一:深度 RAG 系统构建

RAG 是 Dify 的杀手锏功能。大多数低代码平台的 RAG 只是“文档上传 -> 切分 -> 检索 -> 塞给 LLM”,但 Dify 提供了全流程的控制。

从 0 到 1 构建企业级知识库

让我们假设我们要把公司的 100 份 PDF 技术文档变成一个知识库。

步骤 1:文档预处理 (Document Preprocessing)

痛点:直接把 PDF 扔进去效果往往很差,因为 PDF 里可能有页眉页脚、乱码、表格识别错误。

Dify 解决方案
Dify 支持自定义 ETL Pipeline。在自部署版本中,我们可以编写预处理脚本。

这里我提供一段基于 LangChainPyMuPDF 的预处理代码(这也是 Dify 内部默认使用的逻辑之一):

import fitz  # PyMuPDF
from langchain.text_splitter import RecursiveCharacterTextSplitter
from typing import List, Dict

class PDFProcessor:
    def __init__(self, chunk_size: int = 500, chunk_overlap: int = 50):
        self.chunk_size = chunk_size
        self.chunk_overlap = chunk_overlap
        
    def extract_text_from_pdf(self, pdf_path: str) -> str:
        """提取 PDF 文本,并尝试去除页眉页脚"""
        doc = fitz.open(pdf_path)
        full_text = []
        
        for page in doc:
            # 获取页面文本块
            blocks = page.get_text("blocks")
            # 简单启发式:按位置过滤页眉页脚
            # 假设页眉在页面顶部 10%,页脚在底部 10%
            page_height = page.rect.height
            header_threshold = page_height * 0.1
            footer_threshold = page_height * 0.9
            
            filtered_blocks = [
                b[4] for b in blocks 
                if b[1] > header_threshold and b[3] < footer_threshold
            ]
            full_text.append("\n".join(filtered_blocks))
            
        return "\n\n".join(full_text)
    
    def split_text(self, text: str) -> List[Dict]:
        """切分文本"""
        text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=self.chunk_size,
            chunk_overlap=self.chunk_overlap,
            separators=["\n\n", "\n", "。", "!", "?", " ", ""],
            length_function=len
        )
        
        chunks = text_splitter.split_text(text)
        
        # 转化为 Dify 需要的格式
        return [{"content": chunk, "metadata": {"source": "my_doc"}} for chunk in chunks]

# 使用示例
processor = PDFProcessor()
raw_text = processor.extract_text_from_pdf("technical_doc.pdf")
chunks = processor.split_text(raw_text)
print(f"生成了 {len(chunks)} 个 Chunk")
步骤 2:嵌入模型 (Embedding Model) 的选择

这是很多人容易忽略但极其关键的一步。用中文语料 fine-tune 过的 Embedding 模型,效果比通用的 OpenAI Embedding 要好得多

Dify 支持多种 Embedding 模型:

  • OpenAI text-embedding-3-small
  • HuggingFace (如 shibing624/text2vec-base-chinese)
  • Cohere / Jina AI
  • 本地部署的 BGE (BAAI General Embedding)

数学原理:余弦相似度 (Cosine Similarity)

检索的核心是计算用户 Query 的向量和文档 Chunk 向量之间的相似度。最常用的度量是余弦相似度:

s i m i l a r i t y ( A , B ) = cos ⁡ ( θ ) = A ⋅ B ∥ A ∥ ∥ B ∥ = ∑ i = 1 n A i B i ∑ i = 1 n A i 2 ∑ i = 1 n B i 2 similarity(A, B) = \cos(\theta) = \frac{A \cdot B}{\|A\| \|B\|} = \frac{\sum_{i=1}^{n} A_i B_i}{\sqrt{\sum_{i=1}^{n} A_i^2} \sqrt{\sum_{i=1}^{n} B_i^2}} similarity(A,B)=cos(θ)=A∥∥BAB=i=1nAi2 i=1nBi2 i=1nAiBi

其中 A A A B B B 是两个向量。

步骤 3:检索后的重排序 (Rerank)

“向量检索”找的是“语义上相似”的内容,但这不一定是“最相关”的内容。通常的做法是:先通过向量数据库召回 Top 50,然后用一个更强的模型(Cross-Encoder)对这 50 个进行重新排序,选出 Top 5。

Dify 已经内置了 Cohere RerankBGE Reranker 的支持。这是一个能显著提升 RAG 准确率的高级功能,强烈建议开启。

高级玩法二:基于 Dify API 的二次开发

Dify 的定位不仅是一个 GUI 工具,更是一个开发平台。这是它与 Coze 最大的不同。你可以用 Dify 快速把 Agent 的逻辑原型做出来,然后通过它的 API 嵌入到你自己的业务系统中。

场景:将 Dify Agent 接入企业微信

假设我们已经在 Dify 后台配置好了一个知识库应用,现在我们要写个简单的 Python 服务来接收企业微信消息并转发给 Dify。

from flask import Flask, request, jsonify
import requests

app = Flask(__name__)

# Dify 配置
DIFY_API_URL = "https://api.dify.ai/v1/chat-messages"
DIFY_API_KEY = "your-dify-api-key-here"

@app.route('/wechat/webhook', methods=['POST'])
def wechat_webhook():
    # 1. 接收企业微信数据 (此处简化解析逻辑)
    data = request.json
    user_query = data.get('text', {}).get('content', '')
    user_id = data.get('user_id', 'anonymous')
    
    # 2. 调用 Dify API
    # Dify 使用 conversation_id 来维持多轮对话上下文
    # 这里我们简单的用 user_id 做映射,实际应落库
    conversation_id = get_conversation_id(user_id)
    
    headers = {
        "Authorization": f"Bearer {DIFY_API_KEY}",
        "Content-Type": "application/json"
    }
    
    payload = {
        "inputs": {}, # 对应 Dify 里的变量
        "query": user_query,
        "response_mode": "blocking", # 也可以用 streaming
        "conversation_id": conversation_id,
        "user": user_id,
        "files": []
    }
    
    try:
        dify_response = requests.post(DIFY_API_URL, json=payload, headers=headers)
        dify_response.raise_for_status()
        result = dify_response.json()
        
        # 保存新的 conversation_id
        if not conversation_id:
            save_conversation_id(user_id, result.get('conversation_id'))
            
        # 3. 返回给企业微信
        return jsonify({
            "msgtype": "text",
            "text": {
                "content": result.get('answer', '抱歉,我没有理解')
            }
        })
        
    except Exception as e:
        print(f"Error: {e}")
        return jsonify({"msgtype": "text", "text": {"content": "系统繁忙"}})

# 模拟的 KV 存储
conversation_store = {}

def get_conversation_id(user_id):
    return conversation_store.get(user_id)

def save_conversation_id(user_id, conv_id):
    conversation_store[user_id] = conv_id

if __name__ == '__main__':
    app.run(port=5000, debug=True)

高级玩法三:本地部署与源码定制 (Self-Hosted)

这是 Dify 最硬核的玩法。既然是开源的,我们就可以把它 Fork 过来,改成我们想要的样子。

架构解读:Dify 的代码结构

当你 clone 下 Dify 的代码后,你会看到这几个主要目录:

dify/
├── api/              # 后端服务 (Python/FastAPI)
├── web/              # 前端控制台 (React/Next.js)
├── worker/           # 异步任务队列 (Celery)
├── docker/           # Docker Compose 编排文件
└── ssrf_proxy/       # 安全代理

核心技术栈

  • Backend: FastAPI + SQLAlchemy + Celery
  • Vector DB: 支持 Weaviate, Qdrant, Milvus, PGVector 等(默认 Weaviate)
  • Frontend: Next.js + Tailwind CSS
  • ORM: PostgreSQL
实战:修改 Dify 以支持一种新的 LLM

假设公司内部有一个私有化部署的大模型,API 格式类似 OpenAI 但有点不一样。我们需要修改 Dify 的源码来适配它。

  1. 定位代码位置: 在 api/core/model_runtime/model_providers/ 下,你会看到 openaianthropic 等文件夹。
  2. 创建新的 Provider: 复制 openai 文件夹,重命名为 my_custom_llm
  3. 修改 llm.py:
# 这是一个简化的示例,位于 api/core/model_runtime/model_providers/my_custom_llm/llm.py
from typing import Optional
from core.model_runtime.entities.llm_entities import LLMResult
from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel

class MyCustomLLM(LargeLanguageModel):
    def _invoke(self, model: str, credentials: dict,
                prompt: str, streaming: bool = False) -> LLMResult:
        """
        调用自定义模型的逻辑
        """
        # 1. 从 credentials 获取 API Key 和 URL
        api_key = credentials.get('api_key')
        endpoint_url = credentials.get('endpoint_url')
        
        # 2. 构造请求 (假设内部模型有特定的 Header)
        headers = {
            "Authorization": f"Bearer {api_key}",
            "X-Internal-Request": "true", # 假设这是特殊要求
            "Content-Type": "application/json"
        }
        
        payload = {
            "model": model,
            "messages": [{"role": "user", "content": prompt}]
        }
        
        # 3. 发送请求... (代码省略)
        # response = requests.post(...)
        
        # 4. 解析返回结果并封装成 Dify 标准的 LLMResult
        # ...
        
        return LLMResult(...)

Dify 平台局限性分析

  1. 上手门槛略高: 相比于 Coze 的“小白友好”,Dify 的界面有更多的专业术语(如 Temperature、Top P),对于完全没有 LLM 背景的业务人员来说,初期学习曲线较陡。
  2. 工具生态不如 Coze 丰富: Coze 得益于字节跳动的生态,插件商城里有大量即插即用的工具(如搜索电商数据、编辑飞书文档)。Dify 的工具更多需要用户自己通过 API 或者代码编写。
  3. 运维成本: 如果选择自部署 Dify,你需要维护 PostgreSQL、Vector DB、Redis、Celery Worker 等一大堆组件。这对于小团队来说是不小的运维压力(虽然 Docker Compose 已经简化了很多)。

平台局限性与技术陷阱

无论是 Coze 还是 Dify,都不是银弹。这一章我们来聊聊那些“看起来很美,但踩进去才发现是坑”的地方。

陷阱一:Context Window 的幻觉

现象:你把一份 1000 页的文档上传到了知识库,然后问了一个非常细节的问题,Agent 回答得头头是道,但你仔细一看,发现它在一本正经地胡说八道(Hallucination)。

原因:RAG 的召回率不是 100%。如果你的文档切分得不好,或者问题太偏门,最相关的那个 Chunk 可能并没有被塞进 Prompt 里。LLM 发现上下文里没有,但它又不敢说“不知道”,于是就开始瞎编。

数学公式:检索失败的概率

假设每次检索命中正确文档的概率是 p p p,我们期望在 Top-K 中至少命中一次的概率是:

P ( 成功 ) = 1 − ( 1 − p ) k P(\text{成功}) = 1 - (1 - p)^k P(成功)=1(1p)k

如果 p = 0.3 p=0.3 p=0.3 (30% 命中率), k = 3 k=3 k=3,那么成功率只有 1 − 0.7 3 = 65.7 % 1 - 0.7^3 = 65.7\% 10.73=65.7%。这意味着每三次提问就可能有一次失败。

陷阱二:工具调用的“无限循环”

现象:你让 Agent 帮你搜索今天的天气。Agent 调用了搜索工具,返回了结果。但 Agent 看了结果后,又觉得需要再确认一下,于是又调用了一次搜索工具……然后就停不下来了。

原因:这通常是因为 Prompt 写得不好,或者 LLM 的推理能力不足。Agent 的“反思”环节出了问题。

陷阱三:成本失控

现象:刚开始玩的时候觉得免费额度用不完,结果正式上线一周后,收到了五位数的账单。

深层分析
Agent 的成本不仅是用户输入的那几个字。一个复杂的 Workflow 可能包含:

  1. 理解用户输入 -> LLM Call 1
  2. 知识库检索 -> 不算钱
  3. 根据检索结果思考 -> LLM Call 2
  4. 调用工具 -> 不算钱
  5. 根据工具返回结果生成最终答案 -> LLM Call 3

成本计算公式

C o s t = N u s e r s × C ˉ t u r n s × ( C i n p u t × T ˉ i n p u t + C o u t p u t × T ˉ o u t p u t ) × N s t e p s Cost = N_{users} \times \bar{C}_{turns} \times (C_{input} \times \bar{T}_{input} + C_{output} \times \bar{T}_{output}) \times N_{steps} Cost=Nusers×Cˉturns×(Cinput×Tˉinput+Coutput×Tˉoutput)×Nsteps

  • N u s e r s N_{users} Nusers: 用户数
  • C ˉ t u r n s \bar{C}_{turns} Cˉturns: 平均对话轮数
  • C C C: 模型单价
  • T ˉ \bar{T} Tˉ: 平均 Token 数
  • N s t e p s N_{steps} Nsteps: 平均每轮对话的 LLM 调用次数(Agent 类型的应用这个数字通常 >= 2)

最佳实践与避坑指南

1. Prompt 工程的黄金法则

  • Instruct 模式优于 Chat 模式:在可能的情况下,尽量让 Agent 按照指令输出 JSON 或 XML,而不是纯自然语言。结构化输出是可控性的基础。
  • 给 LLM 一个“逃生舱”:在 Prompt 里加上一句:“如果你不确定答案,或者信息不足,请直接说‘我不知道’或‘无法找到相关信息’,不要编造。”
  • 使用 User Feedback Loop:无论是 Coze 还是 Dify,都要开启用户反馈(点赞/点踩)。这是最便宜且最有效的数据来源。

2. RAG 优化 Checklist

如果你正在搭建知识库,请逐项检查:

  • 文档清洗:去除了 PDF 页眉页脚、水印、HTML 标签吗?
  • Chunk 策略:Chunk size 设置合理吗?(中文建议 300-800 token)
  • 混合检索:是否同时使用了“向量检索”和“关键词检索 (BM25)”?
  • Rerank:开启重排序模型了吗?
  • 召回测试:你有一个测试集来验证召回准确率吗?

3. 从 Demo 到 Production 的架构演进

阶段一:MVP (最小可行性产品)

  • 技术选型:直接用 Coze 或 Dify SaaS。
  • 目标:验证业务逻辑是否成立。

阶段二:规模化 (Scale)

  • 技术选型:迁移到 Dify 专业版或自部署。
  • 重点:加入缓存层(Redis),把相同的用户问题缓存起来,避免重复调用 LLM。

阶段三:定制化 (Custom)

  • 技术选型:基于 LangChain / LlamaIndex 自研,或者深度 Fork Dify。
  • 重点:接入私有模型,构建复杂的风控和审核体系。

行业发展与未来趋势

AI Agent 发展简史

让我们用一个表格来回顾一下这短短几年的巨变:

年份 事件 意义 代表产品/论文
2020 GPT-3 发布 展示了 In-Context Learning 的惊人能力 GPT-3 API
2022 Q4 ChatGPT 发布 AI 首次破圈,对话式交互成为范式 ChatGPT
2023 Q1 LangChain 爆火 “把 LLM 用胶水粘起来”成为工程化的第一次尝试 LangChain, AutoGPT
2023 Q3 OpenAI GPTs 发布 普通用户也能自定义 Bot,Agent 概念深入人心 GPTs, Assistants API
2023 Q4 - 2024 Coze, Dify 等平台崛起 Harness Engineering 时代到来,关注稳定性、可观测性、企业级安全 Coze, Dify, LangFlow

未来趋势:多模态 Agent 与 Agent 协作 (Multi-Agent)

目前的 Agent 大多还只是处理文本。下一阶段的两个重要方向是:

  1. 多模态 (Multi-Modal): Agent 不仅能读文字,还能看图片、听语音、甚至生成视频。
  2. Agent 协作 (Multi-Agent System): 就像人类团队一样,多个 Agent 各司其职(有的负责写代码,有的负责测试,有的负责审核),共同完成复杂任务。

架构设想图

Agent 群体

需要修改

完成

用户

调度器
Dispatcher

码农 Agent
擅长写代码

产品 Agent
擅长拆解需求

审查 Agent
擅长挑错


总结与展望

在这篇万字长文中,我们从核心概念出发,深入剖析了 AI Agent Harness Engineering 的内涵,分别实战了 Coze 的 Workflow 和 Dify 的 RAG 系统,并坦诚地讨论了当前平台的局限性。

核心观点回顾

  1. 低代码平台是生产力工具,但不是替代品:Coze 和 Dify 极大地提高了我们的开发效率,但它们不能替代你对 LLM 基本原理的理解。
  2. 没有最好的平台,只有最合适的场景
    • 如果你是个人开发者,想快速做一个 Bot 玩玩,或者重度依赖飞书/抖音生态,选 Coze
    • 如果你是企业用户,需要私有化部署,或者有大量的二次开发需求,选 Dify
  3. 工程化能力依然是核心竞争力:Prompt 写得好只是基础,能把系统搭起来、跑稳定、控住成本、持续迭代,才是高手。

AI Agent 的时代才刚刚拉开序幕。现在的低代码平台或许还很粗糙,就像 2010 年的智能手机操作系统。但正是这些不完美,孕育着巨大的创新机会。

行动建议:现在就去注册一个 Coze 或 Dify 账号,花一下午时间,把你手边的一份文档丢进去,做一个属于你自己的 Agent。当你亲手把它做出来的那一刻,你对这篇文章内容的理解将会完全不同。

祝你在 Harness AI 的道路上,玩得开心!


如果你觉得这篇文章对你有帮助,欢迎点赞、收藏,并在评论区分享你使用 Coze 或 Dify 的有趣经历。让我们一起,把 AI 的力量握在手中。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐