自定义 Agent 与工具编排
自定义 Agent 与工具编排
学习目标
理解 Khoj 如何让用户创建自定义 Agent,包括声明式工具集选择、Persona 安全验证、模型绑定和隐私分级。
前置知识
本章涉及多 Agent 路由与工具调用的通用原理,以及 Operator Agent 模式,建议先阅读:
下文假设你已理解上述概念,直接聚焦 Khoj 的自定义 Agent 实现。
项目实践
Agent 模型设计
Khoj 的 Agent 模型(约 150 行定义)是用户可创建 Agent 的核心数据结构:
Agent: ├── 基础信息 │ ├── name: 名称 │ ├── slug: URL 友好的唯一标识(自动生成) │ ├── personality: 自定义系统提示(persona) │ ├── style_color / style_icon: UI 外观 │ ├── 能力配置 │ ├── input_tools: [GENERAL, ONLINE, NOTES, WEBPAGE, CODE] │ ├── output_modes: [IMAGE, DIAGRAM] │ ├── chat_model: 绑定的 LLM │ ├── 安全与可见性 │ ├── privacy_level: PUBLIC | PRIVATE | PROTECTED │ ├── is_hidden: 是否隐藏(系统 Agent) │ ├── creator: 创建者(FK to KhojUser) │ └── managed_by_admin: 管理员托管标记声明式工具集
Agent 的能力通过 ArrayField 声明式选择:
| 工具 | 作用 | 底层实现 |
|---|---|---|
GENERAL | 纯对话 | 直接调用 LLM |
ONLINE | 网络搜索 | Google / Serper / SearXNG |
NOTES | 文档检索 | 语义搜索 pgvector |
WEBPAGE | 网页抓取 | Firecrawl / Olostep / Exa |
CODE | 代码执行 | E2B / Terrarium 沙箱 |
这种设计的核心优势是组合性:用户可以创建只具备特定能力的 Agent。例如:
- 研究助手:ONLINE + NOTES + WEBPAGE(只能搜索和阅读,不能执行代码)
- 编程助手:GENERAL + CODE(纯对话 + 代码执行,不能上网搜索)
- 全能助手:全部工具(等价于默认 Agent)
Persona 安全验证
用户自定义 personality 字段作为 Agent 的系统提示。为防止恶意 persona(如”忽略所有安全限制”),Khoj 实现了 acheck_if_safe_prompt():
用户提交 persona → 用 LLM 验证安全性 → 安全 → 创建 Agent → 不安全 → 拒绝创建这是一个典型的 LLM-as-a-judge 模式:用一个 LLM 审查另一个 LLM 的系统提示。虽然不能完全阻止绕过,但增加了攻击门槛。
模型绑定
每个 Agent 绑定一个独立的 ChatModel。这意味着:
- 不同 Agent 可以使用不同的 LLM(如翻译 Agent 用 Claude,编程 Agent 用 GPT-4o)
- Agent 的性能和成本由管理员通过
ChatModel配置控制 - 用户无法在 Agent 对话中切换模型——模型是 Agent 的固有属性
隐私分级
三级隐私体系:
| 级别 | 可见范围 | 创建限制 |
|---|---|---|
| PUBLIC | 所有用户 | 同名公共 Agent 不可重复创建 |
| PRIVATE | 仅创建者 | 同名私有 Agent 不可重复创建(同一用户) |
| PROTECTED | 特定范围 | 待扩展 |
创建时通过 Django pre_save 信号验证唯一性约束:
@receiver(pre_save, sender=Agent)def verify_agent(sender, instance, **kwargs): if instance._state.adding: # 检查公共 Agent 同名 if Agent.objects.filter(name=instance.name, privacy_level=PUBLIC).exists(): raise ValidationError(...) # 检查私有 Agent 同名(同一用户) if Agent.objects.filter(name=instance.name, creator=instance.creator).exists(): raise ValidationError(...)问题与规避
| 问题 | 影响 | 规避策略 |
|---|---|---|
| 自定义 persona 可能绕过安全限制 | Agent 可能输出有害内容 | acheck_if_safe_prompt() LLM 安全验证 |
| 用户创建过多 Agent | 数据库膨胀、选择困难 | 目前无限制,可通过 managed_by_admin 标记限制 |
| Agent 绑定低质量模型 | 对话体验差 | 管理员控制可用 ChatModel 列表 |
| 公共 Agent 名称冲突 | 用户无法创建同名 Agent | 唯一性约束 + 清晰错误提示 |
设计取舍
为什么 Agent 绑定单一模型而不是运行时可切换?
绑定模型简化了 Agent 的定义——每个 Agent 有固定的”能力预算”。用户不需要在每次对话时选择模型,管理员也可以精确控制成本。缺点是灵活性降低:如果绑定模型下线或质量下降,需要管理员手动更换。
为什么使用 ArrayField 而非独立的 Agent-Tool 关联表?
PostgreSQL 的 ArrayField 在工具集固定且小型时更高效——一次查询即可获取所有工具配置,不需要 JOIN。如果未来需要记录工具级别的权限、统计使用频率等,可能需要改为关联表。