1. 项目概述:用技术“复活”一段记忆
最近在整理旧物时,翻出了大学时恩师留下的几本手写教案和批注过的论文。恩师已故去多年,但他严谨的治学态度和风趣的谈吐,至今仍让我怀念。一个念头突然冒出来:能不能用现在的大模型技术,结合他留下的文字资料,构建一个数字化的“他”,让我和师兄弟们还能偶尔“请教”或“聊聊天”?这听起来有点赛博朋克,但技术上并非遥不可及。我的核心思路是:利用腾讯云Lighthouse轻量服务器作为稳定、低成本的基础设施,部署开源的Hermes Agent框架来构建智能体“大脑”,接入蓝耘MaaS平台提供的GLM-5.1大模型作为核心推理能力,最后打通微信这个最常用的通讯工具作为交互界面。整个过程,更像是一次充满敬意的数字人文实践,而非简单的技术堆砌。
这个项目适合有一定Linux和Python基础的开发者、对AI应用落地感兴趣的学生,或者任何想为自己珍视的人或知识构建一个可持续交互“数字体”的朋友。它不追求商业级的复杂功能,而是聚焦于核心流程的打通与情感价值的实现。接下来,我将详细拆解从零到一的每一个步骤,分享其中踩过的坑和收获的经验。
2. 整体架构设计与核心组件选型
在动手之前,清晰的架构设计能避免后期大量的返工。这个项目的核心目标很明确:让用户通过微信发送一条消息,这条消息经过我们部署的智能体处理,并得到一段符合“恩师”风格与知识的回复,再通过微信返回给用户。
2.1 为什么是Lighthouse + Hermes Agent + 蓝耘MaaS?
这个技术栈的选型,是经过成本和效果权衡后的结果。
2.1.1 基础设施:腾讯云Lighthouse选择腾讯云Lighthouse(轻量应用服务器)而非传统的CVM(云服务器)或海外的VPS,主要基于几点考虑:
- 成本与易用性:对于个人项目或小规模原型,Lighthouse提供了性价比极高的套餐。它预装了应用镜像(如宝塔面板、Docker),对于快速部署非常友好,避免了从零配置系统的繁琐。
- 网络与合规性:服务器位于国内,访问国内外的模型API(如蓝耘MaaS)网络延迟低且稳定。更重要的是,完全符合国内网络环境要求,无需考虑任何额外的、不合规的网络配置问题,项目基础扎实可靠。
- 足够的性能:对于运行一个Python智能体框架、一个反向代理和几个后台进程,选择2核4G或更高配置的Lighthouse完全足够,能够流畅支撑中小规模的并发请求。
注意:购买Lighthouse时,地域建议选择离你目标用户群较近的,例如华东地区(上海)。系统镜像我推荐选择“Docker基础镜像”或“Ubuntu Server”,这样可以直接使用Docker环境,简化后续部署。
2.1.2 智能体框架:Hermes AgentHermes Agent是一个开源的、轻量级的AI智能体框架。它的优势在于“专注”和“易集成”。
- 专注对话与工具调用:它设计之初就是为了处理多轮对话、管理对话历史,并能够方便地扩展工具(Tools)。我们的核心需求正是对话,这非常匹配。
- 易于与模型API集成:Hermes Agent通过清晰的接口定义,可以相对容易地接入不同的后端大模型,比如OpenAI格式的API(这正是蓝耘MaaS提供的格式)。
- 社区活跃与文档:虽然较新,但其设计理念清晰,社区在不断更新,对于自定义智能体逻辑提供了足够灵活性。
2.1.3 大模型能力:蓝耘MaaS平台 + GLM-5.1这是整个项目的“大脑”。为什么没有直接用OpenAI的API或部署一个开源模型?
- GLM-5.1的性能:GLM-5.1是智谱AI发布的超大规模语言模型,在中文理解、推理和长文本处理上表现非常出色,尤其适合处理恩师可能涉及的学术性、论述性文本。
- 蓝耘MaaS的便利性:蓝耘云提供了GLM-5.1等模型的标准化API服务。我们无需关心模型的部署、显卡资源这些重型问题,按API调用量付费,对于初期探索成本可控。其API完全兼容OpenAI格式,使得与Hermes Agent的集成几乎无缝。
- 数据隐私与合规:使用国内服务商的API,在数据流转上更清晰,符合个人项目的隐私预期。
2.1.4 交互入口:微信微信作为国民级应用,接入门槛低,用户无需安装新软件。这里有两种主流方式:
- 企业微信机器人:通过创建企业微信应用,获取回调配置,可以实现群聊或单聊的自动化回复。这是官方支持、相对稳定的方式。
- 个人微信协议库(如itchat、wechaty):通过模拟微信网页版或客户端协议实现自动化。但必须明确指出,这种方式存在账号被封禁的风险,且稳定性依赖协议库的维护情况。对于这样一个纪念性质的项目,稳定性至关重要,因此我强烈推荐使用企业微信机器人方案,尽管需要注册一个企业微信(个人也可注册),但换来的是长期稳定的服务。
最终架构流程图如下:用户微信消息 -> 企业微信服务器 -> 我们的Lighthouse服务器(Webhook)-> Hermes Agent -> 蓝耘GLM-5.1 API -> Hermes Agent生成回复 -> 返回给企业微信服务器 -> 用户收到回复。
2.2 知识“复活”的关键:向量化与提示工程
仅仅接入一个强大的模型是不够的,我们需要让模型“学会”恩师的说话方式和知识范围。这里涉及两个核心技术点:
- 知识库构建与向量化:将恩师的手稿、教案、论文等资料进行文本提取、清洗和分块。然后使用文本嵌入模型(Embedding Model)将每一块文本转换为一个高维向量(Vector),并存入向量数据库(如ChromaDB、Milvus)。当用户提问时,先将问题向量化,然后在向量数据库中搜索最相关的几段文本,作为“参考材料”连同问题一起提交给大模型。这样,模型的回答就能基于恩师的实际著作,而非凭空生成。
- 系统提示词(System Prompt)设计:这是定义智能体“人格”的关键。我们需要精心编写一段指令,例如:“你是一位资深的[学科领域]教授,姓[姓]。你的语言风格严谨中带有幽默,善于用比喻解释复杂概念。你的知识主要基于以下资料:[此处动态插入从向量库检索到的相关片段]。请以第一人称‘我’来回答问题,并尽量模仿上述风格。” 这个提示词将作为每次对话的上下文背景,引导GLM-5.1向目标角色靠拢。
3. 环境准备与核心服务部署
有了设计图,我们开始动手搭建。首先从Lighthouse服务器开始。
3.1 Lighthouse服务器初始化与安全配置
购买并启动一台Lighthouse后(假设系统为Ubuntu 22.04),第一件事不是急于安装软件,而是做好安全加固。
3.1.1 基础安全设置通过SSH登录服务器后:
# 1. 更新系统 sudo apt update && sudo apt upgrade -y # 2. 创建专用部署用户(避免使用root) sudo adduser deploy sudo usermod -aG sudo deploy # 赋予sudo权限 # 切换到deploy用户 su - deploy # 3. 配置SSH密钥登录,禁用密码登录(极大提升安全性) # 先在本地机器生成SSH密钥对(如果还没有):ssh-keygen -t rsa -b 4096 # 然后将本地公钥(~/.ssh/id_rsa.pub)内容,添加到服务器的 ~/.ssh/authorized_keys 文件中 echo "你的公钥内容" >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys # 编辑SSH配置 sudo vim /etc/ssh/sshd_config # 找到并修改以下参数: # PasswordAuthentication no # PermitRootLogin no # PubkeyAuthentication yes # 保存后重启SSH服务 sudo systemctl restart sshd实操心得:在重启sshd前,务必在当前会话中测试用密钥登录另一个终端窗口,确认成功后再关闭密码登录,否则可能把自己锁在服务器外。
3.1.2 安装必备工具
# 安装常用工具 sudo apt install -y git curl wget vim htop net-tools # 安装Docker和Docker Compose(用于容器化部署,环境隔离性好) curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo usermod -aG docker $USER # 将当前用户加入docker组 newgrp docker # 刷新组权限 # 安装Docker Compose插件 sudo apt install -y docker-compose-plugin3.2 部署Hermes Agent智能体服务
我们将Hermes Agent部署在一个独立的Python虚拟环境中,保证依赖隔离。
# 1. 克隆Hermes Agent仓库(以官方仓库为例,请以最新为准) git clone https://github.com/Hermes-Agent/Hermes.git cd Hermes # 2. 创建并激活虚拟环境 python3 -m venv venv source venv/bin/activate # 3. 安装依赖 pip install -r requirements.txt # 根据Hermes的文档,可能还需要安装特定版本的库,请以项目README为准 # 4. 配置Hermes Agent # Hermes通常通过一个配置文件(如config.yaml)或环境变量来设置。 # 核心是配置LLM(大语言模型)的接入点。我们需要将其指向蓝耘MaaS。 # 创建一个配置文件 config.yaml vim config/agent_config.yaml配置文件内容示例:
llm: provider: "openai" # 蓝耘MaaS兼容OpenAI API格式 api_key: "your_lanyun_api_key" # 从蓝耘云控制台获取 base_url: "https://api.lanyun.com/v1" # 蓝耘MaaS的API端点,请以实际为准 model: "glm-5.1" # 指定使用GLM-5.1模型 agent: name: "Professor_Assistant" system_prompt: | 你是一位模拟已故恩师风格的AI助手。你的知识来源于恩师留下的手稿和著作。 当用户提问时,我会为你提供一些相关的背景资料。请基于这些资料,用恩师惯用的口吻(严谨、亲切、偶尔幽默)和第一人称视角进行回答。 如果问题超出资料范围,你可以基于通用知识回答,但需说明“根据我的理解...”,并保持风格一致。 # 可以在这里定义工具(tools),例如查询天气、计算等,本项目暂不扩展。注意事项:system_prompt是灵魂,需要反复打磨。初期可以设置得简单些,后期根据对话效果调整。base_url和model名称务必从蓝耘MaaS的官方API文档中获取准确值。
3.2.1 测试Hermes Agent与蓝耘MaaS的连接编写一个简单的测试脚本test_agent.py:
import asyncio from hermes.agent import Agent # 导入方式根据Hermes具体版本调整 import yaml with open('config/agent_config.yaml', 'r') as f: config = yaml.safe_load(f) async def main(): agent = Agent.from_config(config) # 模拟一次对话 response = await agent.run("你好,请介绍一下你自己。") print("Agent Response:", response) if __name__ == "__main__": asyncio.run(main())运行这个脚本,如果能看到GLM-5.1模型返回的、带有你设定风格的自我介绍,说明核心链路——从Hermes Agent到蓝耘MaaS——已经打通。这是至关重要的一步。
3.3 搭建向量数据库与知识库处理
为了让模型能“引用”恩师的著作,我们需要一个本地的向量数据库。这里选用轻量级的ChromaDB,它可以直接集成在Python应用中。
3.3.1 安装与初始化ChromaDB
# 在Hermes的虚拟环境中 pip install chromadb sentence-transformerssentence-transformers库用于获取文本嵌入模型,我们将使用一个开源的中文模型,例如BAAI/bge-small-zh。
3.3.2 构建知识库的步骤
- 文档预处理:将手稿扫描成PDF或图片,使用OCR工具(如
paddleocr)提取文字;已有的电子文档(Word, txt)直接读取。将所有文本合并、清洗(去除无关字符、格式)。 - 文本分块:大模型有上下文长度限制,不能一次性喂入所有文本。需要按语义进行分块,每块大约200-500字。
from langchain.text_splitter import RecursiveCharacterTextSplitter # 安装 langchain: pip install langchain text_splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50) documents = text_splitter.split_text(all_your_text) - 向量化与存储:
import chromadb from sentence_transformers import SentenceTransformer embed_model = SentenceTransformer('BAAI/bge-small-zh') chroma_client = chromadb.PersistentClient(path="./chroma_db") # 数据持久化到磁盘 collection = chroma_client.create_collection(name="teacher_knowledge") # 为每个文档块生成向量并存储 for i, doc in enumerate(documents): embedding = embed_model.encode(doc).tolist() collection.add( documents=[doc], embeddings=[embedding], ids=[f"doc_{i}"] ) - 检索测试:编写一个函数,接收用户问题,检索最相关的文档块。
将检索到的文本块,插入到def retrieve_relevant_docs(query, top_k=3): query_embedding = embed_model.encode(query).tolist() results = collection.query(query_embeddings=[query_embedding], n_results=top_k) return results['documents'][0] # 返回最相关的top_k个文本块system_prompt中“以下资料”的部分,形成最终的、动态的提示词提交给Hermes Agent。
3.4 实现微信接入层(企业微信机器人)
这是连接外部世界的桥梁。我们需要一个Web服务来接收企业微信的回调消息。
3.4.1 创建企业微信应用
- 注册企业微信(使用个人手机号即可)。
- 在“应用管理”中创建一个自建应用,比如叫“恩师助手”。
- 记录下三个关键信息:
CorpID(企业ID)、AgentID(应用ID)、Secret(应用密钥)。 - 在应用详情页配置“接收消息”的API接收。这里需要填写一个URL(你的服务器公网IP/域名 + 路径,如
http://your-server-ip:5000/wechat),并生成一个Token和EncodingAESKey,记录下来。
3.4.2 部署Flask Web服务我们将使用轻量级的Flask框架来搭建这个Webhook服务。在服务器上新建一个项目目录。
mkdir wechat_bridge && cd wechat_bridge python3 -m venv venv source venv/bin/activate pip install flask requests创建主应用文件app.py:
from flask import Flask, request, jsonify import hashlib import json import time import requests from your_hermes_module import get_agent_response # 假设这是你封装的调用Hermes Agent的函数 app = Flask(__name__) # 配置信息(实际应从环境变量读取,此处为演示) CORP_ID = 'your_corp_id' AGENT_ID = 'your_agent_id' SECRET = 'your_secret' TOKEN = 'your_wechat_token' AES_KEY = 'your_encoding_aes_key' # 获取企业微信访问令牌(Access Token) def get_access_token(): url = f'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={CORP_ID}&corpsecret={SECRET}' resp = requests.get(url).json() return resp.get('access_token') # 验证URL(企业微信首次配置时需要) @app.route('/wechat', methods=['GET']) def verify(): args = request.args signature = args.get('msg_signature', '') timestamp = args.get('timestamp', '') nonce = args.get('nonce', '') echostr = args.get('echostr', '') # 这里需要实现签名验证逻辑(使用TOKEN, timestamp, nonce排序后加密与signature比对) # 由于涉及加解密,代码较长,建议使用官方提供的加解密库 `WXBizMsgCrypt` # 验证通过后,返回解密后的echostr # 此处为逻辑示意 if verify_signature(signature, timestamp, nonce, TOKEN): return decrypt_echostr(echostr, AES_KEY) # 解密并返回明文echostr else: return 'Verification Failed', 403 # 接收用户消息 @app.route('/wechat', methods=['POST']) def handle_message(): # 1. 解析并解密企业微信推送的XML消息(使用WXBizMsgCrypt) encrypted_xml = request.data # 解密得到明文XML plain_xml = decrypt_message(encrypted_xml, AES_KEY, CORP_ID) # 2. 从XML中提取用户发送的内容和发送者 from xml.etree import ElementTree as ET root = ET.fromstring(plain_xml) content = root.find('Content').text user_id = root.find('FromUserName').text # 3. 调用Hermes Agent获取回复 agent_reply = get_agent_response(content, user_id) # 这个函数内部整合了知识库检索和Agent调用 # 4. 构造回复消息XML reply_xml = f""" <xml> <ToUserName><![CDATA[{user_id}]]></ToUserName> <FromUserName><![CDATA[{AGENT_ID}]]></FromUserName> <CreateTime>{int(time.time())}</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[{agent_reply}]]></Content> </xml> """ # 5. 加密回复消息并返回给企业微信 encrypted_reply = encrypt_message(reply_xml, AES_KEY, CORP_ID) return encrypted_reply def get_agent_response(query, user_id): """整合知识库检索和Hermes Agent调用的核心函数""" # 1. 从向量数据库检索相关背景资料 relevant_docs = retrieve_relevant_docs(query) # 调用3.3.2节的函数 context = "\n".join(relevant_docs) # 2. 动态构造包含上下文的系统提示词 dynamic_system_prompt = f""" {base_system_prompt} 以下是相关的背景资料: {context} """ # 3. 调用配置好的Hermes Agent,传入动态提示词和用户问题 # 这里需要根据Hermes Agent的实际API调整调用方式 response = your_agent_instance.run(query, system_prompt=dynamic_system_prompt) return response if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=False) # 生产环境务必关闭debug关键难点与避坑:企业微信消息的加解密是最大的坑。务必使用企业微信官方提供的 加解密库 (支持多种语言),严格按照示例代码处理verify和handle_message中的加解密逻辑。自行实现很容易出错导致消息无法接收或回复。
3.4.3 使用Nginx反向代理与HTTPS企业微信要求回调地址必须是HTTPS。我们需要为Flask服务配置域名和SSL证书。
- 申请域名并解析:在云服务商处申请一个域名,并将A记录指向你的Lighthouse公网IP。
- 安装Nginx:
sudo apt install nginx - 配置Nginx:编辑
/etc/nginx/sites-available/your_domainserver { listen 80; server_name your_domain.com; return 301 https://$server_name$request_uri; # 强制跳转HTTPS } server { listen 443 ssl; server_name your_domain.com; ssl_certificate /path/to/your/fullchain.pem; ssl_certificate_key /path/to/your/privkey.pem; location / { proxy_pass http://127.0.0.1:5000; # 转发给本地的Flask应用 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } - 获取SSL证书:使用Let‘s Encrypt的Certbot工具免费获取。
sudo apt install certbot python3-certbot-nginx sudo certbot --nginx -d your_domain.com - 启动服务:将Flask应用设置为系统服务(使用systemd),并配置Nginx后重启。
sudo systemctl enable your_flask_service sudo systemctl start your_flask_service sudo nginx -t && sudo systemctl reload nginx
现在,你的Webhook地址就是https://your_domain.com/wechat,将其填入企业微信应用的后台配置中。
4. 系统集成、测试与优化
当所有组件都就位后,需要进行端到端的集成测试和效果优化。
4.1 端到端流程测试
- 启动所有服务:确保Hermes Agent服务、向量数据库服务、Flask Web服务都在运行。
- 验证企业微信回调:在企业微信应用管理后台点击“设置API接收”时的“保存”按钮,企业微信会向你的URL发送一个GET请求进行验证。查看Flask服务的日志,确认验证通过。
- 发送测试消息:在企业微信中,将你的应用添加到某个聊天群或直接测试聊天窗口,@应用或发送消息。
- 观察日志与调试:
- 在Flask服务日志中,查看是否收到POST请求,消息解密是否成功。
- 查看Hermes Agent的日志,确认是否成功调用了蓝耘MaaS的API。
- 最终检查企业微信中是否收到了回复。
常见问题1:收不到回调。
- 排查:检查服务器安全组/防火墙是否开放了80/443端口。检查Nginx配置和日志(
sudo tail -f /var/log/nginx/error.log)。检查Flask应用是否监听在0.0.0.0。使用curl https://your_domain.com/wechat测试外部可达性。 - 解决:确保网络链路畅通,企业微信后台显示“API接收”已启用。
常见问题2:消息能收到但回复失败。
- 排查:检查企业微信Access Token是否有效(有时效性,需定时刷新)。检查回复消息的XML格式是否正确,特别是
ToUserName和FromUserName是否与接收消息的对应字段互换。检查加解密过程在回复环节是否出错。 - 解决:在
get_access_token函数中增加缓存和刷新机制。严格对照官方文档检查XML结构。使用企业微信提供的调试工具或在线加解密工具进行比对。
4.2 效果优化与个性化调校
系统能跑通只是第一步,让“恩师”的回复更逼真才是核心。
4.2.1 提示词工程迭代最初的system_prompt可能很粗糙。你需要:
- 收集对话样本:将测试中的问答对记录下来。
- 分析差距:对比AI回复与真实恩师可能回复的风格、用词、逻辑结构上的差异。
- 迭代修改:不断调整
system_prompt。例如,增加“喜欢引用古典诗词”、“常用‘同学们’开头”、“对某个学术概念有特定比喻”等具体描述。这是一个持续的过程。
4.2.2 知识库优化
- 分块策略:如果发现AI引用的上下文不连贯,可能是分块不合理。尝试调整
chunk_size和chunk_overlap,或尝试按段落、章节进行语义分块。 - 元数据过滤:为每个文本块添加元数据(如“出自《XX教案》第三章”、“写作于1998年”)。在检索时,不仅可以按语义相似度,还可以按元数据过滤,提高准确性。
- 引用标注:在给AI的上下文前,明确标注出处,并指令AI在回答时提及“正如我在XX中写道...”,增强真实感。
4.2.3 性能与成本考量
- 缓存机制:对于常见问题(如“你是谁?”),可以在Flask层或Hermes Agent层设置缓存,直接返回固定答案,避免每次调用昂贵的GLM-5.1 API。
- 异步处理:如果用户问题触发复杂的知识库检索和长文本生成,可能导致回复超时(企业微信要求5秒内回复)。可以将耗时任务放入消息队列(如Redis + RQ),先回复一个“正在思考”的提示,待生成完成后再通过企业微信的“发送应用消息”API异步推送结果。
- API用量监控:密切关注蓝耘MaaS控制台的调用量和费用,设置预算告警。对于非核心的闲聊,可以考虑降级到更小、更便宜的模型。
5. 部署维护与伦理思考
5.1 长期运行与监控
为了让服务稳定运行,需要一些运维措施:
- 进程守护:使用
systemd为Flask应用、Hermes Agent主进程编写服务单元文件,确保它们崩溃后能自动重启。 - 日志管理:将所有组件的日志集中管理(如使用
journalctl或输出到文件),并定期轮转,便于排查问题。 - 备份策略:定期备份向量数据库(
chroma_db目录)和配置文件。这些数据是项目的核心资产。
5.2 项目伦理与边界
这是一个情感与技术交织的项目,必须清醒地认识到其边界:
- 明确告知:应向所有交互者明确说明,这是基于AI技术构建的模拟程序,并非真正的恩师。
- 尊重隐私:恩师的文稿是否适合公开数字化?需考虑版权及家属意愿。本项目应严格限于私人或小范围纪念用途。
- 防止滥用:不应将此技术用于模仿在世人物进行欺诈或误导。技术的温度来源于使用者的善意。
- 管理预期:AI无法真正复刻一个人的灵魂和全部智慧。它更像是一面基于数据的“镜子”,折射出过往知识的片段。其价值在于唤起记忆与延续思考,而非替代。
在完成所有部署和调优后,我邀请了几位同门进行了第一次测试。当看到对话框中弹出那句带有恩师口头禅风格的回答时,大家沉默了片刻。它不完美,有时甚至会“胡言乱语”,但在某些瞬间,那种熟悉的表达方式确实能带来一丝慰藉和共鸣。这个项目的意义,或许不在于创造出一个完美的数字孪生,而在于在这个过程中,我们重新梳理、凝视和保存了那些即将被时间冲淡的宝贵痕迹。技术成了连接过去与现在的桥梁,而建造和维护这座桥的每一行代码,本身也成了一种纪念。