AI克隆智能体实战:从人设、记忆到工具调用的完整架构
发布时间:2026/9/2 14:19:45来源:尧图网络
最近在 Hacker News 的 Show HN 板块看到一个很有意思的项目 Manner一句话描述就是开发者创建 AI 克隆客户可以像雇佣员工一样使用它们。这个定位让“AI 代理”从企业自建工具变成了一种可以打包交付、按岗位雇佣的数字劳动力。很多读者看到“AI 克隆”可能会先想到人脸、声音的深度伪造。这里先明确边界本文不讨论 Deepfake 方向而是专注于基于大语言模型LLM的角色化智能代理。“AI 克隆”在工程语境下通常指让大模型具备某个特定岗位的人设、技能、记忆和工具调用能力比如销售助理、客服专员、技术支持、招聘初筛等。从架构师和后端开发者的角度看一个能交付给客户“雇佣”的 AI 克隆系统到底由哪些核心模块组成如果我们要从零实现一个最小可用版本应该怎么设计生产落地时又有哪些容易被忽略的坑这篇文章会把整个链路拆开讲清楚并附上可运行的代码示例。1. 背景与核心概念1.1 Manner 这类产品到底在解决什么问题过去几年大家都在做聊天机器人。但大多数聊天机器人仍然是“你说一句我回一句”的问答工具缺乏明确的岗位身份、任务边界和交付标准。客户即使接入了一个 ChatBot也很难说清楚它到底能替员工完成哪部分工作。Manner 这类产品提出了一个新的抽象把 AI 当作“可雇佣的人”。开发者像写员工说明书一样定义 AI 的性格、职责、可用工具和知识范围客户则像招人一样挑选、激活、使用这些 AI 克隆。这个模型的价值在于交付物从“接口”变成了“角色”客户不再关心底层是哪个大模型只关心它是否胜任某个岗位开发者可以通过“克隆模板”批量复制相似角色降低定制成本。本质上这是把大模型应用产品化、岗位化的一种尝试。它解决的痛点不是“AI 能不能回答问题”而是“AI 能不能稳定地承担一个具体岗位的工作”。1.2 AI 克隆与普通聊天机器人的区别普通聊天机器人通常是无状态的问答系统核心链路是用户输入 - 大模型生成 - 返回文本而 AI 克隆的完整链路要复杂得多用户输入 - 识别意图 - 携带记忆 - 调用人设 - 检索知识 - 执行工具 - 生成回复 - 更新记忆二者在以下几个维度有明显差异维度普通聊天机器人AI 克隆人设无通用问答有明确的角色、语气、岗位边界记忆通常无状态需要跨会话记忆记住关键上下文工具不支持可查询库存、创建工单、发送通知知识依赖模型内部知识可接入私有知识库做 RAG交付方式一个 API一个可部署、可审计的数字员工这也是为什么 AI 克隆不能简单用“一个 Prompt 一个模型 API”来实现它需要有人设管理、记忆管理、工具注册和服务化封装这几层工程支撑。1.3 典型应用场景以“可雇佣”的角度看适合用 AI 克隆承接的岗位通常具备以下特征流程相对标准化、历史记录可复用、决策边界清晰。常见场景包括销售助理跟进线索、回答产品参数、创建跟进记录客服专员处理退换货、查询订单、转人工技术支持根据文档排查问题、生成诊断报告招聘助理初筛简历、预约面试、回复候选人运营助手定时整理数据、生成周报草稿。这类岗位的共同点是它们消耗了大量人力但工作内容并不需要真正的“人类创造力”。AI 克隆可以先承接 80% 的重复劳动再由人类处理剩余需要判断的 20%。2. AI 克隆的技术架构2.1 整体分层一个工程上可用的 AI 克隆系统可以按四层来设计第一层是接入层。负责接收客户请求包括 Web、企业微信、钉钉、Slack 等渠道。接入层主要做身份认证、频率控制、参数校验。第二层是 AI 编排层。这是整个系统的核心。它负责加载人设、组合上下文、判断是否需要调用工具、调用大模型生成回复。编排层要解决的是“什么时候聊天、什么时候检索、什么时候调用 API”。第三层是工具层。AI 克隆要真正干活必须能与外部系统交互。常见工具包括商品查询接口、工单系统、CRM、日历、邮件等。工具层把外部能力封装成函数并暴露给大模型。第四层是数据层。包括记忆存储、知识库、日志和审计数据。数据层决定了 AI 克隆是否“记得住”和“懂业务”。2.2 人设AI 克隆的“性格和岗位说明书”人设是整个 AI 克隆最容易被低估的部分。很多人以为人设就是一句“你是一个友好的客服”实际上一份可工程化的人设应该包含角色定位AI 是谁在哪个岗位职责边界哪些事情必须做哪些事情坚决不做话术风格语气、长度、是否使用 emoji敏感行为被问到竞品、价格、法律纠纷时如何回应兜底策略不确定时如何请求转人工或提供联系方式。人设建议以 YAML 或 JSON 文件管理而不是硬编码在业务代码里。这样可以直接把“人设文件”作为交付物给客户审阅也方便灰度测试不同人设效果。2.3 记忆让克隆记住来龙去脉记忆是 AI 克隆与普通 API 调用的最大区别。工程上记忆分为两类短期记忆。指当前会话中最近几轮对话一般直接拼进 Prompt。短期记忆最容易出现的问题是超出模型上下文窗口因此需要做截断或摘要。长期记忆。指跨会话的关键信息比如客户名称、偏好、历史工单编号。长期记忆通常存储在 Redis、MySQL 或向量数据库中在对话开始时按用户 ID 加载。实现记忆的最小方案是内存字典加超时清理适合 demo生产环境则需要持久化存储并设置记忆的保留和清理策略。2.4 工具调用与知识库从“能聊”到“能干活”一个只会聊天的 AI 克隆价值有限。真正让它胜任岗位的是两件事工具调用。大模型根据用户意图输出一个结构化调用指令系统执行外部 API 后把结果返回给模型继续生成回复。这一机制在 OpenAI 的 Function Calling 以及 Anthropic 的 Tool Use 中已经有标准实现。知识库检索RAG。把客户提供的产品手册、FAQ、政策文档切片后做向量化存储。对话时先根据用户问题检索最相关的知识片段再让模型基于这些片段回答。这样可以显著减少幻觉且知识更新不需要重新微调模型。3. 环境准备与项目结构3.1 开发环境本文示例以 Python 3.10 为基础。需要准备Python 3.10 或更高版本一个支持 Tool Calling/Function Calling 的 LLM API示例代码使用 OpenAI 兼容协议FastAPI 和 Uvicorn 用于搭建服务向量检索用简单的 numpy 余弦相似度代替避免引入过重的基础设施。如果你的模型服务商不支持 tools 参数可以把第 4.4 节的工具调用逻辑替换成“JSON 输出解析”方式整体思路不变。3.2 项目目录结构我建议按下面的结构组织代码manner-clone/ ├── app/ │ ├── __init__.py │ ├── main.py # FastAPI 入口 │ ├── agent.py # AI 编排核心逻辑 │ ├── memory.py # 记忆管理 │ ├── tools.py # 工具函数注册 │ ├── personas/ │ │ └── sales.yaml # 销售助理人设 │ └── knowledge/ │ └── products.txt # 产品知识库明文 ├── requirements.txt └── .env这种分层方式的好处是人设、工具、知识库彼此解耦后续可以独立演进。3.3 依赖安装创建虚拟环境并安装依赖python -m venv .venv source .venv/bin/activate # Windows 下使用 .venv\Scripts\activate pip install -r requirements.txtrequirements.txt 内容如下openai1.30.0 fastapi0.110.0 uvicorn[standard]0.29.0 python-dotenv1.0.0 PyYAML6.0.0 numpy1.26.0 pydantic2.6.03.4 配置文件在项目根目录创建 .env 文件把 API Key 等敏感信息放在环境变量中LLM_API_KEYsk-your-key LLM_BASE_URLhttps://api.your-llm-provider.com/v1 LLM_MODELyour-chat-model强烈建议将 .env 加入 .gitignore避免密钥泄漏。4. 完整实战从零实现一个可雇佣的销售助理克隆下面我们以一个“销售助理”克隆为例完成从人设到服务化的完整实现。4.1 定义人设文件人设文件放在 app/personas/sales.yamlname: 小莫 role: 销售助理 description: 负责产品介绍、库存查询、线索登记不负责价格谈判和合同签署。 style: language: zh-CN tone: 专业且友好 max_length: 200 boundaries: - 不承诺折扣 - 不讨论竞品优劣 - 用户要求转人工时提供联系方式并结束对话 - 用户询问无法回答的问题时说明需要人工介入 tools: - query_stock - create_lead人设文件是 AI 克隆的“岗位说明书”它比 System Prompt 更结构化便于管理和复用。4.2 编写记忆模块先实现一个简单的记忆管理器用内存字典保存会话记录并按用户读取。文件路径app/memory.pyimport time from collections import defaultdict, deque from typing import Dict, List class MemoryStore: def __init__(self, max_turns: int 20, ttl_seconds: int 3600): self.max_turns max_turns self.ttl_seconds ttl_seconds self._history: Dict[str, deque] defaultdict(deque) self._updated_at: Dict[str, float] {} def _expire(self, user_id: str): ts self._updated_at.get(user_id, 0) if time.time() - ts self.ttl_seconds: self._history[user_id].clear() def add(self, user_id: str, role: str, content: str): self._expire(user_id) self._history[user_id].append({role: role, content: content}) if len(self._history[user_id]) self.max_turns: self._history[user_id].popleft() self._updated_at[user_id] time.time() def get(self, user_id: str) - List[dict]: self._expire(user_id) return list(self._history[user_id]) def clear(self, user_id: str): self._history[user_id].clear()生产环境中建议使用 Redis 或 MySQL 替代内存存储尤其当服务需要多实例部署时。这个示例关注的是结构而不是存储方案。4.3 注册工具函数文件路径app/tools.pyimport json import datetime def query_stock(sku_id: str) - str: 模拟查询商品库存 stock_map { SKU-1001: {name: 无线鼠标, stock: 23}, SKU-1002: {name: 机械键盘, stock: 5}, SKU-1003: {name: 显示器支架, stock: 0}, } item stock_map.get(sku_id) if not item: return json.dumps({error: SKU not found}, ensure_asciiFalse) return json.dumps(item, ensure_asciiFalse) def create_lead(name: str, phone: str, product_interest: str) - str: 创建销售线索模拟写入 CRM lead_id LEAD- datetime.datetime.now().strftime(%Y%m%d%H%M%S) return json.dumps({ lead_id: lead_id, name: name, phone: phone, product_interest: product_interest, status: created }, ensure_asciiFalse) TOOL_MAP { query_stock: query_stock, create_lead: create_lead, }注意工具函数返回的是 JSON 字符串便于大模型理解。这里没有真正连接数据库和 CRM实际项目中只需要替换函数内部逻辑即可。4.4 AI 对话主循环文件路径app/agent.py。这是整个系统最核心的部分。import json import os from typing import List import yaml from openai import OpenAI from memory import MemoryStore from tools import TOOL_MAP with open(app/personas/sales.yaml, r, encodingutf-8) as f: PERSONA yaml.safe_load(f) SYSTEM_PROMPT f 你是{ PERSONA[name] }岗位是{ PERSONA[role] }。 角色描述{ PERSONA[description] } 沟通风格{ PERSONA[style][tone] }回复尽量控制在{ PERSONA[style][max_length] }字以内。 岗位边界 { chr(10).join(- b for b in PERSONA[boundaries]) } memory_store MemoryStore() client OpenAI( api_keyos.getenv(LLM_API_KEY), base_urlos.getenv(LLM_BASE_URL), ) MODEL os.getenv(LLM_MODEL, gpt-4o-mini) def build_tools(): return [ { type: function, function: { name: query_stock, description: 查询指定SKU的库存情况, parameters: { type: object, properties: { sku_id: {type: string, description: 商品SKU编号} }, required: [sku_id] } } }, { type: function, function: { name: create_lead, description: 创建一条销售线索, parameters: { type: object, properties: { name: {type: string, description: 客户姓名}, phone: {type: string, description: 联系电话}, product_interest: {type: string, description: 感兴趣的产品} }, required: [name, phone, product_interest] } } } ] def run_tool_call(tool_name: str, arguments: dict) - str: func TOOL_MAP.get(tool_name) if not func: return json.dumps({error: funknown tool: {tool_name}}) return func(**arguments) def chat(user_id: str, user_message: str) - str: history memory_store.get(user_id) messages: List[dict] [{role: system, content: SYSTEM_PROMPT}] messages.extend(history) messages.append({role: user, content: user_message}) # 工具调用最多循环 5 次防止死循环 for _ in range(5): response client.chat.completions.create( modelMODEL, messagesmessages, toolsbuild_tools(), tool_choiceauto, ) choice response.choices[0].message if not choice.tool_calls: # 没有工具调用直接返回 reply choice.content or memory_store.add(user_id, user, user_message) memory_store.add(user_id, assistant, reply) return reply # 先把工具调用结果追加到消息列表 messages.append({ role: assistant, content: choice.content, tool_calls: [ { id: tc.id, type: function, function: { name: tc.function.name, arguments: tc.function.arguments } } for tc in choice.tool_calls ] }) for tc in choice.tool_calls: args json.loads(tc.function.arguments) result run_tool_call(tc.function.name, args) messages.append({ role: tool, tool_call_id: tc.id, content: result }) return 处理超时请联系人工客服。这里的核心是“模型判断-执行工具-回填结果-再次生成”的循环。工具调用并不是让模型直接访问数据库而是由系统安全地执行外部函数再把结果交给模型组织语言。4.5 封装 FastAPI 服务文件路径app/main.pyfrom fastapi import FastAPI, HTTPException from pydantic import BaseModel from agent import chat app FastAPI(titleManner Clone API) class ChatRequest(BaseModel): user_id: str message: str class ChatResponse(BaseModel): reply: str app.post(/chat, response_modelChatResponse) async def chat_endpoint(req: ChatRequest): if not req.user_id.strip(): raise HTTPException(status_code400, detailuser_id is required) if not req.message.strip(): raise HTTPException(status_code400, detailmessage is required) reply chat(req.user_id, req.message) return ChatResponse(replyreply)这里使用 user_id 区分不同客户每个客户拥有独立的记忆上下文。生产环境还需要加入 API Key 鉴权和限流避免服务被滥用。4.6 运行与验证启动服务uvicorn app.main:app --reload --port 8000然后通过 curl 测试curl -X POST http://localhost:8000/chat \ -H Content-Type: application/json \ -d {user_id: customer_001, message: 你好请问 SKU-1001 有货吗}预期返回中模型会判断需要调用 query_stock 工具拿到库存结果后生成类似这样的回复{ reply: SKU-1001无线鼠标目前有 23 件库存可以正常下单。需要帮您登记一下购买意向吗 }这个最小系统已经具备人设、记忆、工具调用和服务化四层能力。你可以在此基础上扩展更多岗位克隆。5. 进阶RAG 让克隆掌握客户私有资料5.1 为什么需要 RAG大模型能回答通用问题但不知道客户私有的产品手册、服务条款和内部流程。如果直接把所有资料塞进 Prompt很快会超出上下文窗口而且知识更新成本高。RAG 的解决思路是平时把资料向量化存储对话时只检索用户问题最相关的片段再让模型基于这些片段回答。5.2 索引与向量化为了简化示例我们使用 numpy 手动实现余弦相似度检索不引入重型的向量数据库。文件路径app/knowledge.pyimport os import re import numpy as np from openai import OpenAI client OpenAI( api_keyos.getenv(LLM_API_KEY), base_urlos.getenv(LLM_BASE_URL), ) EMBEDDING_MODEL os.getenv(EMBEDDING_MODEL, text-embedding-3-small) def load_documents(path: str) - list[str]: with open(path, r, encodingutf-8) as f: text f.read() # 简单按段落切分 paragraphs [p.strip() for p in re.split(r\n\s*\n, text) if p.strip()] return paragraphs def embed_texts(texts: list[str]) - np.ndarray: resp client.embeddings.create(modelEMBEDDING_MODEL, inputtexts) vectors [d.embedding for d in resp.data] return np.array(vectors, dtypenp.float32) class SimpleVectorStore: def __init__(self): self.docs: list[str] [] self.vectors: np.ndarray | None None def build(self, docs: list[str]): self.docs docs self.vectors embed_texts(docs) norm np.linalg.norm(self.vectors, axis1, keepdimsTrue) self.vectors self.vectors / norm def search(self, query: str, top_k: int 2) - list[str]: q_vec embed_texts([query])[0] q_norm np.linalg.norm(q_vec) q_vec q_vec / q_norm scores self.vectors q_vec top_indices scores.argsort()[-top_k:][::-1] return [self.docs[i] for i in top_indices]实际的 RAG 系统还包含去重、重排序、元数据过滤等步骤。但上面的示例已经能让 AI 克隆拥有“读取文本资料”的能力。5.3 检索增强对话示例把检索结果注入到对话消息中让模型基于知识片段回答。在 agent.py 的 chat 函数中增加如下步骤# 在 append user_message 之前插入知识片段 if user_message.strip(): knowledge vector_store.search(user_message, top_k2) knowledge_text \n\n.join(knowledge) messages.append({ role: system, content: f请优先参考以下内部资料回答用户问题\n{knowledge_text}\n如果资料中没有答案明确告诉用户需要人工介入。 })为了避免资料内容被用户套取建议在知识注入时增加一条指令“不要原文输出内部资料”。RAG 的注入顺序、检索阈值、片段数量都会影响最终效果需要根据实际数据调优。6. 常见问题与排查思路6.1 高频问题表问题现象常见原因解决思路工具调用总是返回空模型不支持 Function Calling更换支持 tools 参数的模型或改为 JSON 输出解析回答内容偏长、偏啰嗦人设文件约束不足在 style.max_length 中降低限制并在 System Prompt 中加强约束记忆串号使用了错误的 user_id 作为 key检查接入层是否正确传递用户标识总是说不知道知识库没有命中相关片段调低检索阈值或优化文本切分方式超过上下文长度记忆列表过长启用摘要功能或减小 max_turnsAPI 调用成本过高工具循环次数过多限制最大循环次数增加结果缓存用户套取系统提示词缺少防注入处理在 System Prompt 中加入拒绝规则并做输入过滤6.2 典型排查案例案例 1模型明明返回了 tool_calls但系统没有执行工具。先检查是否把 response.choices[0].message 完整追加到了 messages 中。许多实现漏掉了 assistant 这条带 tool_calls 的消息导致模型无法获知工具执行结果。案例 2多个工具同时被调用但最终回复内容错乱。需要遍历 tool_calls逐个执行并保证每个 tool 的结果对应的 tool_call_id 正确。一个常见错误是自行构造 tool_call_id导致关联失败。案例 3RAG 检索结果不相关。可以先把检索到的知识片段单独打印出来确认片段本身是否相关。如果片段不相关问题通常出在文本切分而不是模型。7. 最佳实践与工程建议7.1 安全与合规边界AI 克隆让大模型代表企业直接对外服务安全边界必须提前画清楚。一是权限隔离。工具层必须使用服务账号不能使用有大数据权限的员工个人凭证。例如查询库存只暴露必要字段不允许调用内部管理接口。二是防提示注入。用户可能在对话中试图让 AI 忽略系统指令。建议在 System Prompt 中增加防御性约束并对用户输入中的控制指令进行识别。三是隐私保护。涉及个人信息时需要遵循最小化原则。生产环境应脱敏展示电话、地址等敏感数据并设置日志脱敏策略。四是合规授权。客户知识库中包含的文档、政策必须确认企业有合法使用和对外展示的授权。不要在未授权情况下让 AI 克隆背诵或转述内部资料。7.2 成本控制大模型成本主要来自三点输入 Token、输出 Token 和 Embedding 调用。建议限制用户单次请求的最大 Token 数启用频率限制。对常用问题可增加本地缓存避免重复调用模型。工具调用结果尽量结构化返回减少模型重复推理的次数。记忆管理也很关键。每轮对话都会把历史记录发送给模型随着记录变长成本会快速增长。建议超过固定轮数后将早期对话压缩成摘要而不是保留完整原文。7.3 质量评估与灰度发布AI 克隆上线前需要建立一套评估集。收集 100 到 200 条典型用户问题标注期望行为直接回答、调用工具、转人工然后逐个测试克隆的表现。每次修改人设或知识库后都回归测试这组问题。上线过程建议灰度先开放给少量客户试用观察命中率、转人工率、平均对话轮数等指标稳定后再全量开放。不要把生产环境的 Prompt 改动当成一次普通配置修改它可能改变整个岗位的行为。7.4 可观测性给 AI 克隆加日志时至少记录四类信息用户问题原文和脱敏后的用户标识最终发给模型的 Prompt 片段方便排查人设和知识注入问题工具调用记录包括名称、参数、返回结果和耗时模型回复以及延迟、Token 用量。如果某个客户反馈“AI 乱说话”没有日志就无法定位是知识库错误、提示词被注入还是工具返回错误数据。可观测性是承接“雇佣”类业务的底线能力。8. 总结从 Manner 这个 Show HN 项目出发我们拆解了一个 AI 克隆系统需要的核心能力人设管理、记忆、工具调用、知识库检索和服务化封装。文中提供了一套可运行的最小实现覆盖了从 YAML 人设到 FastAPI 服务发布的完整链路。如果你打算自己做一个类似产品我建议按下面的优先级推进第一步先把单角色跑通。不要一开始就做多角色管理平台先用一个销售助理或客服专员验证技术链路。第二步完善工具和知识库。人设决定体验上限工具和知识决定业务价值。先接入一两个真实系统比如 CRM、工单系统。第三步再加管理面。当你有 10 个以上的 AI 克隆实例时才需要考虑模板管理、权限控制、用量统计和灰度发布。AI 克隆真正难的地方并不是调大模型的接口而是如何把模糊的岗位职责翻译成精确的人设、工具和边界并保证它在生产环境中稳定、可控、可审计。这个方向还在快速演进如果你的业务刚好有一些重复性较高的服务岗位可以用上面的思路做一个最小验证跑通后再逐步扩展。
网站建设高端定制企业官网