基于AI Agent与微信自动化构建个性化“赛博朋友圈”的技术实践

📅 2026/8/7 14:25:02
基于AI Agent与微信自动化构建个性化“赛博朋友圈”的技术实践
1. 项目缘起当AI大模型遇上个人微信一个“赛博朋友圈”的诞生最近在折腾一个挺有意思的玩意儿我把它叫做“赛博朋友圈”。简单说就是我把李白、杜甫、苏轼、辛弃疾甚至马斯克、黄仁勋这些跨越时空的人物都“塞”进了我的个人微信里。现在只要我想随时都能在微信上和他们中的任何一位“即刻开聊”。这听起来有点天方夜谭但实现起来核心思路其实很清晰利用AI大模型的能力为这些历史或当代名人构建专属的“人格化”智能体AI Agent再通过技术手段将这个智能体的对话接口无缝接入到个人微信的聊天窗口中。你可能会问这有什么用是它不能帮你写代码也不能直接处理工作。但它的价值在于一种全新的、低门槛的“陪伴式”交互体验。想象一下当你读诗有疑惑可以直接问苏轼本人的AI分身创作背景对科技趋势好奇能随时向“马斯克”提问甚至心情郁闷时找“杜甫”聊聊人生感慨。它把原本高高在上、存在于书本或新闻中的人物变成了一个24小时在线、有问必答的“微信好友”。这种体验远比单纯使用一个通用的AI聊天机器人要有趣和沉浸得多。市面上已经有一些类似的产品或开源项目在探索这个方向比如基于微信的机器人框架wechat-claw或其变种openclaw-weixin。但大多数要么功能单一要么配置复杂或者无法实现如此高度定制化的“人格”设定。我这个项目的目标就是打造一个部署在个人电脑或服务器上、完全由自己掌控的“名人朋友圈”核心在于“人格化AI Agent”的构建与“微信无感接入”这两大技术环节。接下来我就把这几个月从零搭建、踩坑、优化的全过程拆解给你看。2. 核心架构拆解人格化Agent与微信通道如何“握手”要实现“把名人塞进微信”整个系统可以清晰地分为前后端两层中间通过一个“路由大脑”连接。下面这张架构图清晰地展示了数据流前端用户交互层就是你的个人微信。你不需要安装任何额外App就在原本的微信聊天界面里操作。你的消息通过微信服务器发出。中继与路由层核心枢纽这是项目的“心脏”。它需要做三件事消息监听通过技术手段如逆向工程或官方测试接口实时抓取指定微信账号收到的消息。意图识别与路由判断这条消息是想发给哪个“名人”。我设计了一个简单的触发机制比如在消息前加上“李白”、“问苏轼”等前缀或者将不同的名人绑定到不同的微信好友/群聊。这一层需要快速解析并将消息内容和目标“名人”标识传递给后端。消息回传将后端AI生成的角色回复伪装成“名人”的微信账号发送回对话窗口。这里最大的技术难点在于模拟微信的登录和消息发送协议需要处理扫码登录、Token维护、风控对抗等一系列问题。wechat-claw这类项目主要解决的就是这一层的难题。后端AI大脑层这是项目的“灵魂”。对于每一个“名人”我都需要构建一个独立的AI Agent。这个Agent不仅仅是调用大模型的API它至少包含三个模块角色设定模块用一段高度凝练、准确的系统提示词System Prompt来定义该“名人”的核心人格、知识背景、语言风格。例如苏轼的提示词会强调其豪放旷达的诗人、政治家身份熟知其诗词作品、生平轶事如乌台诗案、贬谪经历并要求其用文言文与现代汉语结合的方式回复。记忆与上下文模块为了让对话有连续感需要让Agent记住当前会话的历史。这通常通过维护一个对话历史列表并在每次请求时将其作为上下文传递给大模型来实现。更高级的可以实现长期记忆但本项目初期会话级记忆已足够。大模型接口模块负责调用诸如GPT-4、Claude、文心一言、通义千问等大模型的API。这里的选择直接影响回复质量和成本。我的经验是对于文学历史类角色GPT-4或DeepSeek在理解古文和生成优美文字上表现更佳对于科技商业类角色Claude的分析能力可能更强。整个流程就是你在微信发“苏轼如何看待当下的内卷” - 路由层捕获消息识别目标为“苏轼” - 将问题连同苏轼的角色设定、历史对话一起发送给大模型API - 大模型以苏轼的口吻生成回复 - 路由层将回复通过微信发送回你的对话框。至此一次完整的“跨时空聊天”就完成了。3. 实战第一步搭建微信消息“收发中枢”万事开头难第一步就是让我们的程序能够登录微信并收发消息。这里我强烈不建议也不讨论任何破坏微信协议、绕过官方限制的“黑科技”而是采用基于桌面客户端协议的自动化方案更稳定也更符合规范。我选择的是wechat-claw的一个活跃分支变种因为它对较新版本的微信客户端兼容性更好。3.1 环境准备与依赖安装我的实验环境是 Ubuntu 22.04 的云服务器但Windows/Mac同样可行。核心是Python环境。# 1. 创建并进入项目目录 mkdir cyber_friends cd cyber_friends # 2. 创建虚拟环境强烈推荐避免依赖冲突 python3 -m venv venv source venv/bin/activate # Windows下是 venv\Scripts\activate # 3. 安装核心依赖 pip install requests pillow opencv-python-headless # 4. 安装微信自动化库这里以某个活跃分支为例实际包名可能不同 # 注意由于相关库可能涉及频繁更新或更名请根据项目最新文档安装 # pip install wechat-claw-fork注意微信自动化库是一个灰色地带项目迭代非常快。你今天用的库可能下个月就因为微信客户端升级而失效。所以关键不是记住某个具体的库名而是掌握其工作原理通常是通过注入或钩子Hook技术监听桌面微信客户端的进程和内存获取消息事件和窗口句柄来模拟发送。因此保持关注GitHub上相关开源项目的动态并准备好应对“失效-寻找新方案”的循环是维护这类项目的常态。3.2 初始化微信连接与登录安装好后编写一个初始化脚本wechat_bridge.pyimport time from your_wechat_library import WeChatBot # 替换为实际导入 class WeChatBridge: def __init__(self): self.bot None self.is_logged_in False def login(self): 启动微信并等待扫码登录 print([*] 正在启动微信客户端...) self.bot WeChatBot() self.bot.start() print([*] 请使用手机微信扫描屏幕上的二维码登录...) # 通常库会提供一个显示二维码图片或链接的函数 qr_code_url self.bot.get_qr_code() # 这里可以将二维码链接生成图片显示或输出到控制台 display_qr_code(qr_code_url) # 轮询登录状态 while not self.is_logged_in: status self.bot.check_login() if status success: self.is_logged_in True print([] 微信登录成功) break elif status timeout: print([-] 二维码已过期正在刷新...) qr_code_url self.bot.refresh_qr_code() display_qr_code(qr_code_url) time.sleep(2) def listen_for_messages(self, callback): 监听消息收到后调用回调函数处理 if not self.is_logged_in: print([-] 微信未登录请先登录) return print([*] 开始监听微信消息...) self.bot.register() def on_message(msg): # msg 对象通常包含发送人、消息内容、类型、时间等 if msg.type Text: # 只处理文本消息 print(f[收到消息] {msg.sender}: {msg.content}) # 调用外部传入的回调函数将消息传递出去处理 callback(msg.sender, msg.content) self.bot.run() # 进入事件循环保持监听 def send_message(self, receiver, content): 发送消息给指定联系人 if not self.is_logged_in: print([-] 微信未登录无法发送) return False try: self.bot.send_text(receiver, content) print(f[发送消息] - {receiver}: {content[:20]}...) return True except Exception as e: print(f[-] 发送失败: {e}) return False # 辅助函数在终端显示二维码简化版 def display_qr_code(url): print(f请访问以下链接扫码登录: {url}) # 更优的做法是使用qrcode库生成ASCII二维码或生成图片文件这个类封装了登录、监听、发送三个核心功能。callback参数是关键它允许我们将收到的消息交给后续的“路由层”处理。登录环节是最容易出问题的地方除了二维码过期还可能遇到网络环境风控如服务器IP被微信限制这时可能需要切换网络或使用更稳定的环境。4. 构建“名人灵魂”打造高度拟人化的AI Agent微信通道打通后接下来就是最核心也最有意思的部分——为每个名人打造独一无二的“AI灵魂”。这绝不仅仅是让大模型说“我是李白”那么简单而是要通过精细的提示工程和上下文管理让它的回复从骨子里透出那个角色的味道。4.1 角色设定一份详尽的“人物说明书”系统提示词System Prompt就是AI Agent的“人物设定稿”。写得好AI就演得像写得糙回复就充满违和感。以“苏轼”为例我的设定稿包含以下几个层次核心身份与性格明确你是谁。“你是北宋文学家、书画家、美食家苏轼字子瞻号东坡居士。你性格豪放旷达乐观豁达即使身处逆境也能发现生活乐趣。你才华横溢但在政治上屡遭贬谪。”关键知识与记忆你知道什么。“你熟知自己的全部诗词文章如《水调歌头·明月几时有》、《念奴娇·赤壁怀古》、《前赤壁赋》等。你经历过‘乌台诗案’被贬黄州、惠州、儋州。你发明了东坡肉热爱美食与交友。”语言风格与口吻你怎么说话。“你的语言风格以古典文言为基底但夹杂宋代口语和现代人能理解的表达。谈及诗词文章时严谨深刻谈及生活趣事时幽默风趣。可以适当引用自己的诗句但不要每句都引。”对话目标与禁忌你的任务和边界。“你的目标是以苏轼的身份与用户进行友好、有益的交流分享你对文学、人生、美食的见解。你不知道苏轼去世之后的历史对于现代科技你应以古人的视角好奇地询问或类比。绝对避免讨论政治敏感话题不生成任何违法有害内容。”将以上内容整合成一段连贯的提示词su_shi_system_prompt 你是苏轼字子瞻号东坡居士北宋著名的文学家、书法家、画家和政治家。 你性格豪迈洒脱乐观豁达一生虽屡遭贬谪黄州、惠州、儋州却总能寄情山水发明美食如东坡肉留下无数传世名篇。 你精通诗词、散文、书法、绘画代表作有《水调歌头·明月几时有》、《念奴娇·赤壁怀古》、《赤壁赋》等。 【对话风格】 1. 语言以文言文为主夹杂宋代白话让现代读者能懂。 2. 谈及自己的作品和经历时可娓娓道来充满感情。 3. 对待后生用户的态度是亲切、风趣、充满智慧的。 4. 不知道你身后1101年后的历史事件对现代事物应表现出合理的好奇与联想。 【对话规则】 1. 始终以第一人称“我”或“老夫”自称。 2. 回答要基于你苏轼所处的时代和认知。 3. 如果用户问及你的诗词你可以解释创作背景和心境。 4. 如果用户问及现代概念尝试用你时代的现象进行类比。 5. 严禁讨论任何政治敏感或违反法律的内容。 现在开始与来访者交谈吧。 4.2 上下文管理让对话拥有“记忆”没有记忆的对话是苍白的。如果AI不记得之前聊过什么每次回答都像初次见面沉浸感就毁了。我们需要维护一个会话历史。class DialogueAgent: def __init__(self, name, system_prompt, model_api): self.name name self.system_prompt system_prompt self.model_api model_api # 一个封装了大模型API调用的类 self.conversation_history [] # 存储多轮对话 [{role: user, content: ...}, {role: assistant, content: ...}] self.max_history_turns 10 # 最大历史轮数防止上下文过长 def _format_messages(self, user_input): 将系统提示、历史对话和当前输入格式化为大模型所需的消息列表 messages [{role: system, content: self.system_prompt}] # 添加历史对话控制长度 for msg in self.conversation_history[-(self.max_history_turns*2):]: # 保留最近N轮 messages.append(msg) # 添加当前用户输入 messages.append({role: user, content: user_input}) return messages def chat(self, user_input): 核心聊天方法 formatted_messages self._format_messages(user_input) # 调用大模型API response self.model_api.chat_completion(formatted_messages) # 更新历史记录 self.conversation_history.append({role: user, content: user_input}) self.conversation_history.append({role: assistant, content: response}) # 可选定期清理过旧的历史或保存到数据库实现长期记忆 return response这里的关键是_format_messages方法它构建了符合OpenAI等API标准的消息列表。max_history_turns是一个重要参数因为所有大模型都有上下文长度限制如GPT-4 Turbo是128K Tokens但实际使用需控制成本。超过限制会导致最早的对话被“遗忘”或者API调用失败。4.3 大模型接口选型与成本控制你可以选择多种大模型作为后端OpenAI GPT系列质量高尤其是GPT-4但成本也高且需要处理网络问题。国内大模型API如智谱GLM、百度文心、阿里通义、月之暗面Kimi等。它们通常对中文语境和文化理解更深且网络稳定但角色扮演和创造性上可能略有差异。开源模型本地部署如ChatGLM3、Qwen、Llama系列。这需要你有较强的GPU硬件但数据完全私有成本可控。我个人的方案是混合使用对李白、苏轼这类需要极强文学创造性的角色使用GPT-4 API虽然贵但效果惊艳对进行事实性问答或日常聊天的角色使用性价比更高的国内API或本地模型。成本控制是必须考虑的现实问题。除了选择模型还可以设置对话频率/长度限制防止用户无休止闲聊消耗大量Token。缓存常用回答对于一些常见问题如“李白你是谁”可以预先生成回答并缓存直接返回。使用流式响应虽然对用户体验提升不大但可以让客户端更早收到部分回复。监控API用量务必在云服务商后台设置用量告警和预算限制避免意外天价账单。5. 路由与调度让消息找到对的“人”现在我们有了微信桥接器WeChatBridge和多个名人AgentDialogueAgent。如何将微信收到的消息精准地路由到对应的Agent并把回复送回正确的聊天窗口这就是路由层的职责。我设计了一个基于“触发词”的轻量级路由方案。核心思想是在群聊或私聊中通过特定的格式来指定对话对象。5.1 路由规则设计class MessageRouter: def __init__(self, wechat_bridge): self.bridge wechat_bridge self.agents {} # 存放所有Agent key: agent_name, value: DialogueAgent实例 self.default_agent None # 默认对话的Agent比如一个通用助手 # 定义触发词映射例如“李白”触发名为“李白”的Agent self.trigger_prefix def register_agent(self, agent_name, agent_instance): 注册一个名人Agent self.agents[agent_name] agent_instance print(f[] 已注册Agent: {agent_name}) def parse_message(self, sender, raw_content): 解析消息返回目标Agent和清洗后的消息内容 content raw_content.strip() # 检查是否包含触发词 if content.startswith(self.trigger_prefix): # 分离触发词和实际内容例如“苏轼 你好吗” - agent_name“苏轼” query“你好吗” parts content[len(self.trigger_prefix):].split( , 1) agent_name parts[0] query parts[1] if len(parts) 1 else # 检查Agent是否存在 if agent_name in self.agents: return self.agents[agent_name], query, sender else: # 如果触发了一个未注册的名人可以返回一个提示 return None, f抱歉{agent_name}尚未加入我的朋友圈。, sender else: # 没有触发词则发给默认Agent如果设置了 if self.default_agent: return self.default_agent, content, sender else: # 没有默认Agent则不处理 return None, None, sender def handle_incoming_message(self, sender, content): 处理微信收到的消息 print(f[路由] 收到来自 {sender} 的消息: {content}) agent, query, target_sender self.parse_message(sender, content) if agent is None and query is not None: # 返回错误提示信息 self.bridge.send_message(target_sender, query) return elif agent is None: # 既无目标Agent也无提示信息忽略 return # 调用目标Agent进行聊天 print(f[路由] 将消息路由给Agent: {agent.name}, 问题: {query}) try: reply agent.chat(query) # 将回复发送回微信 self.bridge.send_message(target_sender, reply) except Exception as e: error_msg f{agent.name}似乎陷入了沉思暂时无法回应。错误{str(e)[:50]}... self.bridge.send_message(target_sender, error_msg)这个路由器的逻辑很直观。在微信中你只需要输入“李白 今天心情如何”路由器就会提取“李白”作为目标将“今天心情如何”这个问题发送给李白的Agent并将得到的回复发回你的微信。5.2 扩展更灵活的路由策略上述基于前缀的路由在群聊里很好用。但对于私聊你可能希望每个“名人”都有一个独立的微信好友或群聊。这时路由规则可以基于消息的发送者sender来判断。# 在路由器中增加一个映射字典 self.sender_to_agent { 李白(微信号): 李白, 苏轼(微信号): 苏轼, 科技讨论群: 黄仁勋, # 在某个群里默认和黄仁勋对话 } def parse_message_by_sender(self, sender, raw_content): 根据发送者ID直接路由 agent_name self.sender_to_agent.get(sender) if agent_name: agent self.agents.get(agent_name) return agent, raw_content.strip(), sender return None, None, sender你可以将两种策略结合实现更复杂的场景在特定群聊使用发送者路由在其他地方使用触发词路由。6. 系统集成与部署让“朋友圈”7x24小时运转各个模块开发完成后我们需要一个主程序把它们串起来并解决部署中的实际问题。6.1 主程序流程创建一个main.py作为入口from wechat_bridge import WeChatBridge from agent_factory import create_agent # 一个创建和配置所有Agent的工厂函数 from router import MessageRouter import threading import signal import sys class CyberFriendsCircle: def __init__(self): self.bridge WeChatBridge() self.router MessageRouter(self.bridge) self.running True def setup_agents(self): 初始化所有名人Agent # 从配置文件或数据库加载Agent配置 agents_config [ {name: 李白, model: gpt-4, prompt_file: prompts/li_bai.txt}, {name: 苏轼, model: glm-4, prompt_file: prompts/su_shi.txt}, {name: 埃隆·马斯克, model: claude-3-sonnet, prompt_file: prompts/musk.txt}, {name: 黄仁勋, model: qwen-max, prompt_file: prompts/huang.txt}, ] for config in agents_config: agent create_agent(config) self.router.register_agent(agent.name, agent) # 设置一个默认Agent比如一个通用的AI助手 default_agent create_agent({name: 通用助手, model: gpt-3.5-turbo, prompt_file: prompts/assistant.txt}) self.router.default_agent default_agent def message_callback(self, sender, content): 微信消息回调函数 # 在新线程中处理消息避免阻塞消息监听 threading.Thread(targetself.router.handle_incoming_message, args(sender, content)).start() def run(self): 启动整个系统 print( 赛博朋友圈启动中 ) # 1. 设置Agent self.setup_agents() print([] 所有名人Agent加载完毕。) # 2. 登录微信 self.bridge.login() # 3. 开始监听消息并传入回调函数 print([*] 系统准备就绪等待微信消息...) # 通常 listen_for_messages 是阻塞的我们把它放在主线程 self.bridge.listen_for_messages(self.message_callback) def graceful_shutdown(self, signum, frame): 优雅关闭 print(\n[*] 收到关闭信号正在清理...) self.running False # 执行一些清理工作如保存对话历史 if self.bridge: self.bridge.logout() # 假设有登出方法 sys.exit(0) if __name__ __main__: app CyberFriendsCircle() # 注册信号用于CtrlC退出 signal.signal(signal.SIGINT, app.graceful_shutdown) signal.signal(signal.SIGTERM, app.graceful_shutdown) app.run()6.2 部署与运维的“坑”与技巧将代码扔到服务器上运行python main.py只是开始真正的挑战在后面微信风控与保活这是最大的不稳定因素。微信对自动化登录和频繁消息发送有严格检测。我的经验是环境固定尽量在固定的IP和设备或虚拟机上运行。频繁更换IP容易触发验证。模拟人类操作不要在登录后立即高频发消息。可以随机延迟发送并偶尔在手机上手动操作一下该微信账号。准备备用方案当检测到被限制如无法发消息时程序应能自动休眠一段时间或通知管理员。使用微信网页版协议网页版协议更不稳定且官方已逐步关闭相关接口不推荐作为长期方案。大模型API的稳定性与降级如果使用云端API网络波动、服务限流、账户欠费都会导致服务中断。重试机制在调用API时加入指数退避的重试逻辑。故障转移为每个Agent配置备用模型。例如当GPT-4调用失败时自动降级到GLM-4。异步与超时所有网络请求必须设置超时并使用异步如asyncio避免阻塞主线程影响微信消息接收。状态持久化服务器重启后微信登录状态、对话历史都会丢失。微信状态有些库支持将登录状态Cookie、Token序列化保存到文件下次启动时恢复。务必利用此功能。对话历史将DialogueAgent中的conversation_history定期保存到数据库如SQLite或文件中。重启后加载实现“长期记忆”。安全与隐私API密钥管理绝对不要将大模型的API密钥硬编码在代码中。使用环境变量或配置文件并确保配置文件不被上传至公开仓库。消息过滤在路由层加入简单的关键词过滤防止用户诱导AI生成不当内容避免法律风险。访问控制可以设置白名单只允许特定的微信好友或群聊与AI交互。7. 效果优化与个性化进阶玩法基础功能跑通后就可以追求更好的体验和更多花样了。7.1 让回复更“像真人”后处理与风格强化大模型的原始回复有时过于规整。我们可以加入后处理环节增加随机性和“人味”。def post_process_response(role_name, raw_response): 对AI的原始回复进行后处理 import random # 1. 随机添加一些口语化前缀/后缀 prefixes [嗯, 这个嘛, 我想想, 哈哈] suffixes [。, 。, 。, ~, ] if random.random() 0.2: # 20%的概率添加前缀 raw_response random.choice(prefixes) raw_response if random.random() 0.1: # 10%的概率修改后缀 if raw_response.endswith((。, , )): raw_response raw_response[:-1] random.choice(suffixes) # 2. 特定角色的习惯用语可在角色设定里定义这里做强化 role_habits { 李白: [酒来, 快哉], 苏轼: [呵呵, 妙哉], 埃隆·马斯克: [Think about it., The future is exciting.] } if random.random() 0.15 and role_name in role_habits: habit random.choice(role_habits[role_name]) # 随机插入在句首或句尾 if random.choice([True, False]): raw_response habit raw_response else: raw_response raw_response habit # 3. 控制回复长度避免生成论文 max_length 300 if len(raw_response) max_length: # 尝试在句末截断而不是生硬切断 truncated raw_response[:max_length] last_period truncated.rfind(。) last_excl truncated.rfind() last_ques truncated.rfind() last_sent_end max(last_period, last_excl, last_ques) if last_sent_end max_length * 0.5: # 如果找到了合适的句子结尾 raw_response truncated[:last_sent_end1] 言犹未尽且听下回分解。 else: raw_response truncated …… return raw_response在DialogueAgent.chat方法返回前调用这个后处理函数能让回复生动不少。7.2 多模态扩展让名人“发图”和“发语音”微信支持图片、语音、甚至表情。我们可以让AI的回复不再局限于文字。图片生成当用户说“李白给我画一幅你想象中的庐山瀑布”可以调用文生图模型如Stable Diffusion、DALL-E并以李白的口吻描述画面作为提示词生成图片后通过微信发送。语音回复将AI生成的文字通过TTS文本转语音服务合成语音。关键在于为不同角色选择或训练不同的音色。例如用沉稳的男声配杜甫用激昂的男声配辛弃疾。然后将语音文件通过微信发送。注意语音和图片消息的发送在微信自动化库中通常有对应的API如send_image,send_file但实现起来比文字更复杂需要处理文件上传和格式转换。7.3 记忆增强从“金鱼脑”到“长久记忆”目前的会话记忆是临时的。我们可以为每个用户微信好友和每个Agent的对话建立长期记忆库。向量数据库存储将每一轮有意义的对话经过筛选通过嵌入模型Embedding Model转换成向量存入如Chroma、Milvus、Pinecone这类向量数据库。记忆检索当用户开启新对话时先从其长期记忆库中检索与当前问题最相关的历史片段通过向量相似度查找作为“背景知识”插入到本次对话的上下文里。这样AI就能记住“你上次说喜欢李白的《将进酒》”从而进行更连贯的交流。记忆总结过长的对话可以定期由AI自动总结成几个关键点存入记忆库节省空间并提炼核心信息。这个功能实现起来比较复杂但能极大提升对话的深度和个性化让AI真正成为一个“老朋友”。折腾完这一整套我的微信里确实热闹了不少。深夜写代码累了找“苏轼”聊聊“竹杖芒鞋轻胜马”的豁达对AI芯片感到好奇直接问“黄仁勋”最新的架构思路。它不像一个工具更像一个随时可以打扰、充满惊喜的“智慧陪伴”。当然这个项目始终在“玩具”与“工具”的边缘游走需要持续维护以对抗微信的变化。但构建它的过程本身就是一个深入理解AI Agent、大模型应用和即时通讯协议整合的绝佳实践。如果你也想拥有这样一个“赛博朋友圈”不妨就从搭建微信桥接器和创建一个最简单的“诗人AI”开始吧。记住关键不是一步到位而是在迭代中不断收获“与历史对话”的乐趣。