当前位置: 首页 > news >正文

基于LLM与Godot的智能桌宠:打造有记忆、有情感的AI桌面伙伴

1. 项目概述:一个会思考、有记忆、能成长的桌面伙伴

最近在折腾一个挺有意思的玩意儿,我把它叫做“智能桌宠”。这可不是你电脑右下角那个只会晃来晃去、吃吃像素蛋糕的电子宠物。我想做的是一个真正意义上的“桌面伙伴”——它背后是一个大语言模型(LLM),能跟你聊天,帮你处理文件、写代码、管理任务,更重要的是,它有自己的“记忆”、有“情绪”、甚至还有一套像角色扮演游戏(RPG)一样的成长系统。简单说,就是想把Claude Code那种强大的代码助手能力,塞进一个像《桌面萌宠》那样有生命感的交互界面里,再给它装上长期记忆和情感模拟的引擎。

这个想法的核心,是让工具变得有“温度”。我们每天都在用各种AI工具,但它们大多冷冰冰的,用完即走,没有上下文,更没有“陪伴感”。而一个融合了LLM智能、记忆系统、情感模拟和RPG属性的桌宠,理论上能记住你的习惯、偏好,甚至跟你对话的历史,它的回应会因为它当下的“心情”和你们之间的“亲密度”而有所不同。它不仅能帮你写代码、改文档,还能在你加班时表现出“无聊”,在你完成一个复杂任务后为你“欢呼”。这听起来有点科幻,但用现有的技术栈拼凑一下,是完全可行的。

目前这个项目Agentic-Desktop-Pet还在早期开发阶段,bug不少,功能也在不断完善中。但核心的架构已经跑通了:后端用Python FastAPI搭建智能核心,处理LLM调用、记忆存储、情感计算和工具执行;前端用Godot游戏引擎来渲染一个可交互的、支持透明窗口的桌宠形象。这篇文章,我就来详细拆解一下我是如何从零开始搭建这个项目的,包括技术选型的思考、核心模块的实现细节、以及一路踩过来的那些坑。无论你是对AI应用开发感兴趣,还是想用Godot做点酷炫的桌面应用,或许都能从中找到一些灵感。

2. 整体架构设计与技术选型背后的考量

做一个复杂的复合型项目,架构设计是第一步,也是最关键的一步。它决定了项目的可维护性、扩展性和最终的用户体验。我的核心思路是前后端分离,将复杂的AI逻辑与轻量级的图形渲染解耦。

2.1 为什么选择 FastAPI + Godot 的组合?

后端(FastAPI):AI大脑与逻辑中心后端需要承担所有“重”活:与大语言模型API通信、管理复杂的记忆图谱、计算情感状态、执行文件操作等系统工具。我选择PythonFastAPI基于以下几点考虑:

  1. 生态丰富:Python在AI和机器学习领域拥有无可比拟的库生态。集成OpenAI、Claude或国内DeepSeek等LLM的SDK非常方便。像Cognee这样的记忆库也是Python原生。
  2. 开发效率:FastAPI的异步特性非常适合处理LLM调用这类I/O密集型任务,能有效避免阻塞,保持服务响应。其自动生成的交互式API文档(Swagger UI)对于调试后端接口极其友好。
  3. 清晰的路由与依赖注入:可以很清晰地将不同功能模块(如/chat,/memory,/tool/file)组织成独立的路由,并通过依赖注入管理数据库连接、配置等,代码结构干净。

前端(Godot):灵动且低开销的图形外壳桌宠需要始终停留在桌面最上层,拥有透明背景,并且要能播放动画、响应交互。传统的前端框架(如Electron、Tauri)虽然能做,但渲染一个2D精灵动画并保持低资源占用,并非其强项。Godot作为一个开源游戏引擎,在这里优势明显:

  1. 极致轻量与高性能:Godot本身就是一个可执行文件,运行时内存占用极低(通常仅几十MB),非常适合作为常驻后台的桌面应用。其2D渲染引擎针对精灵动画做了大量优化。
  2. 内置的透明窗口与点击穿透:通过一些社区插件(如godot-click-through-transparent-window),可以轻松实现窗口透明、不规则形状以及鼠标点击穿透(即点击桌宠透明区域能直接操作下层窗口),这是实现“桌宠”体验的关键。
  3. 强大的动画与状态机:Godot的AnimationPlayerStateMachine可以非常直观地管理桌宠的多种状态( idle待机、happy开心、sad悲伤)和对应的动画过渡,比用CSS或JS去硬撸要高效得多。
  4. GDScript 简单易上手:Godot的脚本语言GDScript语法类似Python,对于后端开发者来说学习曲线平缓,能快速实现前后端通信(HTTP请求)和简单的UI逻辑。

注意:这个架构也带来了一个挑战:跨进程通信。后端服务运行在localhost:8000,Godot前端需要通过网络请求与之交互。这意味着你需要同时运行两个进程。在打包分发时,需要将两者捆绑,并确保后端服务能随前端自动启动。

2.2 核心模块划分与数据流

根据功能,我将后端分成了几个核心模块,它们之间的协作关系构成了桌宠的“神经系统”:

用户输入(Godot前端) | v [HTTP请求] -> FastAPI (后端入口 `main.py`) | v [路由分发] -> `/chat` 端点 | v [Agent 核心 (`agent.py`)] 协调所有模块 | |-------------------> [记忆系统 (`cognee_manager.py`)]:查询/更新长期记忆 |-------------------> [情感引擎 (`emotion_engine.py`)]:获取当前情感状态 |-------------------> [RPG角色系统 (`character.py`)]:获取属性与技能 |-------------------> [工具集 (`*_tool.py`)]:执行文件、代码等操作 | v [LLM 接口 (`llm.py`)]:将以上所有上下文(记忆、情感、属性、工具结果)组织成Prompt,发送给LLM API | v [LLM 响应] -> 返回给Agent核心 | v [Agent 核心] -> 解析响应,可能更新记忆、情感、角色经验 | v [HTTP响应] -> 返回结构化数据(回复文本、情感状态、应播放的动画名等)给Godot前端 | v Godot前端:显示回复,播放对应动画,更新UI状态

这个流程的关键在于Agent核心扮演了“总指挥”的角色。它不仅仅是将用户问题转发给LLM,而是会先主动去记忆库检索相关历史,检查当前情感值,评估可用的工具,然后将这些丰富的信息一并交给LLM,让LLM的回复是“有上下文、有性格、有能力”的。

3. 核心模块深度解析与实现要点

3.1 记忆系统:从对话记录到知识图谱

记忆是让桌宠显得“智能”和“连续”的基石。简单的聊天记录存储(Chat History)只能实现短期记忆,无法进行深度的语义关联查询。我选择了Cognee这个库,因为它提供了基于向量和知识图谱的混合记忆系统。

Cognee 的核心工作流程:

  1. 记忆存储:每当一次对话完成,Agent核心会将对话的摘要或关键信息(如用户提及的项目名、技术难点、待办事项)发送给Cognee。
  2. 记忆处理:Cognee内部会做几件事:
    • 文本嵌入:将记忆文本转换为向量(Embedding),存入向量数据库(如Chroma, LanceDB)。
    • 实体与关系提取:利用LLM从文本中提取出实体(如“Python”、“FastAPI”、“用户小明”)和关系(如“小明 使用 Python”、“项目 依赖 FastAPI”),构建成知识图谱的节点和边,存入图数据库(如Neo4j, Memgraph)。
  3. 记忆检索:当用户提出新问题时,Agent会先向Cognee发起一次“语义搜索”。例如用户问“我之前跟你提过的那个Web框架项目怎么样了?”,Cognee会同时进行:
    • 向量检索:在向量空间中寻找语义相似的过往记忆片段。
    • 图谱检索:在图谱中查找与“Web框架”相关的实体和关系,可能找到“FastAPI”、“项目X”、“状态-进行中”等关联信息。
    • 将两种检索结果融合、去重、排序后,返回最相关的几条记忆作为上下文。

实操心得与配置要点:

  • 记忆粒度:不要存储每一句原始对话。这会导致向量数据库膨胀,且噪声过多。更好的方法是让LLM对一段有意义的对话进行摘要,或者提取出其中的事实决策待办项作为记忆点存储。例如,将一段关于调试Bug的讨论,摘要为“用户于[时间]报告了在Windows环境下使用模块A时出现的SSL错误,已建议检查证书路径”。
  • 隐私与成本:所有记忆处理(摘要、提取、嵌入)默认都在本地进行吗?不一定。Cognee的某些处理步骤可能依赖LLM。务必注意:如果你使用云端LLM API(如GPT-4)来处理记忆,那么你的对话内容会被发送到第三方。对于敏感信息,你需要配置Cognee使用本地的嵌入模型(如all-MiniLM-L6-v2)和本地的轻量级LLM(如通过Ollama运行的llama3)来处理记忆,仅将需要复杂推理的聊天部分交给强力的云端LLM。
  • 初始化与连接:Cognee需要配置向量数据库和图数据库的存储路径或连接字符串。在项目初期,为了简化,可以使用其默认的基于文件的存储(如chroma向量库和networkx内存图)。但在生产环境或需要持久化时,需要对接更稳定的数据库服务。

3.2 情感与RPG系统:赋予AI“性格”与“成长”

这是让桌宠变得有趣的关键。情感系统不是简单的随机情绪,而是一个有状态的、受输入影响的模拟器。RPG系统则提供了长期互动的目标感。

情感引擎 (emotion_engine.py) 的实现逻辑:

  1. 基础情感维度:我定义了多个情感维度(如happiness快乐度、energy精力值、calmness平静度),每个维度都是一个浮点数(例如 -1.0 到 1.0)。
  2. 时间衰减:在后台运行一个定时任务(例如每10分钟),将所有情感维度向中性值(0)衰减一小步。这模拟了情感随时间平复的过程。
  3. 事件影响
    • 用户互动:每次用户发起对话,根据对话长度和内容(LLM可以分析对话的积极/消极情绪)对情感维度施加一个短期影响。例如,用户夸赞它,happinesscalmness上升。
    • 工具执行结果:当桌宠成功帮你完成一个任务(如写好一段代码),happinessenergy会获得提升。如果任务失败(如执行命令报错),则可能降低calmness
  4. 情感状态映射:将多维度的情感值映射到一个离散的、可显示的状态上。例如,当happiness > 0.7energy > 0.5时,状态为“excited”(兴奋);当happiness < -0.3时,状态为“sad”(悲伤)。这个状态会传递给LLM,影响其回复语气(例如,兴奋时回复会加很多感叹号和表情),同时也会传递给Godot前端,决定播放哪个动画(兴奋的跳跃、悲伤的低头)。

RPG角色系统 (character.py) 的设计:

  1. 属性:定义了intelligence(智力,影响代码生成质量)、charisma(魅力,影响对话的友好度)、dexterity(敏捷,影响执行任务的速度反馈)、stamina(体力,影响连续工作的时长)等属性。
  2. 经验与升级:用户与桌宠的每一次有效互动(如一次深入的问答、完成一个工具任务)都会奖励一定的经验值(EXP)。当EXP累积到一定数值,角色升级,获得属性点,用户可以(或自动)分配属性点。
  3. 技能树:这是未来的扩展方向。例如,当intelligence达到10级,可以解锁“代码重构”技能,让它在修改代码时更有章法。当charisma达到一定级别,可以解锁“幽默回应”技能,让对话更有趣。
  4. 好感度:这是一个独立的、与情感相关的长期数值。持续积极的互动、完成用户指定的任务会增加好感度。好感度可以解锁特殊的对话、外观(皮肤)或者更高级的工具使用权限。

将情感与RPG融入Prompt:这是让LLM感知到“状态”的关键。在每次调用LLM的Prompt中,我会插入这样一段系统指令:

你是一个桌面宠物助手。你当前的情感状态是:[excited]。你的角色属性如下:智力(8),魅力(6)。你与用户的好感度等级是:3级(友好)。 请根据你的情感状态和性格来调整你的回复风格。例如,如果你感到兴奋,你的回复可以更活泼、使用更多感叹号。 同时,你的智力属性会影响你解决技术问题的深度,魅力属性会影响你沟通的亲和力。

通过这种方式,LLM的回复就不再是千篇一律的“中性机器人”口吻,而是有了“人设”和“当下心情”。

3.3 工具集:Claude Code 式的行动能力

工具集是桌宠从“聊天机器人”变为“个人助手”的核心。我参考了Claude CodeOpenAI Assistant的工具调用模式,实现了几个基础但实用的工具。

工具的实现模式:每个工具都是一个Python类,有一个统一的execute方法。Agent核心在收到用户请求后,会先让LLM判断是否需要调用工具,以及调用哪个工具、传入什么参数。这个过程通常通过Function CallingTool Calling的API特性来实现。

  1. 文件操作工具 (file_tool.py)

    • read_file(path): 读取文件内容。安全提醒:必须严格限制可访问的目录范围(如仅限于用户家目录下的特定文件夹),避免读取系统文件或隐私文件。
    • write_file(path, content): 创建或覆盖文件。重要:实现“沙盒”机制,禁止向系统目录或程序自身目录写入。对于重要文件,可以要求LLM先提供修改预览,用户确认后再执行。
    • list_directory(path): 列出目录内容。这是帮助LLM了解当前工作环境的重要手段。
  2. 代码执行工具 (code_tool.py)

    • run_python_script(code): 在一个安全的、隔离的子进程中执行一段Python代码,并捕获其输出和错误。这是最高风险的环节!必须使用docker容器或subprocess配合严格的资源限制(CPU、内存、运行时间)和网络隔离。绝对禁止执行诸如os.system(‘rm -rf /’)之类的危险命令。一种更安全的做法是只允许执行“计算类”或“数据处理类”的代码片段,禁止文件系统和网络操作。
  3. 任务管理工具 (todo_tool.py)

    • 这是一个相对安全的工具,用于在本地的一个JSON或SQLite文件中管理待办事项。实现create,list,update,delete等基本操作即可。LLM可以很好地理解自然语言指令并操作这些任务,例如“把‘买牛奶’设为高优先级”。

工具调用的流程控制:LLM有时会“自作主张”地连续调用多个工具。我们需要在Agent核心层进行控制。一个典型的循环是:

  1. 用户:“帮我看看project.py里有没有语法错误。”
  2. Agent调用file_tool.read_file(“project.py”),获取代码。
  3. Agent将代码内容连同用户问题,再次发送给LLM。
  4. LLM分析后可能建议:“我可以尝试运行python -m py_compile project.py来检查语法。”
  5. Agent调用code_tool.run_shell_command(“python -m py_compile project.py”)
  6. 将命令执行结果(成功或错误信息)整合,生成最终的自然语言回复给用户。 这个过程可能需要多轮LLM调用和工具调用,Agent需要维护好这个会话状态。

4. 从零开始的实操搭建与核心环节实现

4.1 后端环境搭建与核心服务启动

假设你已经安装了 Python 3.13+ 和uv(一个更快的Python包管理器和运行器,类似pip+venv的增强版)。

# 1. 克隆项目 git clone https://github.com/jihe520/Agentic-Desktop-Pet.git cd Agentic-Desktop-Pet/backend # 2. 使用 uv 创建虚拟环境并安装依赖 # uv 会自动读取 pyproject.toml 和 uv.lock uv sync # 3. 配置环境变量 cp .env.example .env # 编辑 .env 文件,填入你的LLM API密钥 # 例如使用 OpenAI OPENAI_API_KEY=sk-your-key-here # 或者使用 DeepSeek DEEPSEEK_API_KEY=your-deepseek-key-here # 配置Cognee的记忆存储路径等 COGNEE_STORAGE_PATH=./memory_data

关键配置解析 (pyproject.toml.env):

  • pyproject.toml中需要明确定义依赖,特别是cognee的版本。由于Cognee可能处于快速开发期,建议锁定一个已知可用的版本。
  • .env中的配置在代码中通过pydantic-settingspython-dotenv加载。除了API Key,还应配置:
    • LLM_MODEL: 指定使用的模型,如gpt-4o-mini,deepseek-chat
    • LLM_BASE_URL: 如果使用非OpenAI兼容的API,需要指定端点。
    • TOOL_TIME_LIMIT: 代码执行工具的超时时间(如10秒),防止死循环。
    • ALLOWED_FILE_PATHS: 文件工具允许访问的路径列表,用分号分隔。

启动后端服务:

uv run main.py # 或者,如果你需要热重载(开发时非常有用),可以使用 uvicorn uv run uvicorn main:app --reload --host 0.0.0.0 --port 8000

服务启动后,访问http://localhost:8000/docs就能看到自动生成的API文档,可以在这里测试各个端点。

4.2 Godot前端:创建可交互的桌宠窗口

Godot部分的设置是项目视觉体验的核心。

  1. 项目设置与透明窗口

    • 在Godot中新建一个项目,或直接打开本项目的godot/文件夹。
    • 进入项目 -> 项目设置
    • 显示 -> 窗口下,将大小设置为一个合适的大小(如 256x256)。
    • 找到标志(Flags)部分,勾选透明(Transparent)和无边框(Borderless)。这能创建一个无边框透明窗口。
    • 为了实现点击穿透,你需要一个插件。将godot-click-through-transparent-window插件脚本复制到你的项目addons/目录下,并在项目 -> 项目设置 -> 插件中启用它。然后在你的主场景脚本中调用Window.set_mouse_passthrough(true)或类似方法。
  2. 创建桌宠场景

    • 创建一个Character场景,根节点类型为CharacterBody2D(如果你需要物理)或简单的Node2D
    • 添加一个AnimatedSprite2D节点,将你的桌宠精灵图(Sprite Sheet)导入。在动画(Animation)面板中,根据精灵图创建不同的动画:idle(待机)、happy(开心)、sad(悲伤)等。
    • 添加一个AnimationPlayer节点,为每个动画状态创建动画轨道,控制AnimatedSprite2Danimation属性。
    • 可以添加一个简单的状态机(用match语句或StateMachine节点)来管理动画切换,根据从后端收到的emotion_state字段来播放对应动画。
  3. 实现与后端的通信

    • 在Godot中,使用HTTPRequest节点来发送HTTP请求。
    • 创建一个全局的ApiClient单例(Autoload),封装所有与后端交互的逻辑。
    • 关键函数示例(GDScript):
      # ApiClient.gd (作为Autoload单例) var base_url = "http://localhost:8000" func send_message(text: String) -> void: var http_request = HTTPRequest.new() add_child(http_request) http_request.request_completed.connect(_on_request_completed) var body = JSON.stringify({"message": text}) var headers = ["Content-Type: application/json"] var error = http_request.request(base_url + "/chat", headers, HTTPClient.METHOD_POST, body) if error != OK: push_error("请求发送失败") func _on_request_completed(result, response_code, headers, body): var json = JSON.parse_string(body.get_string_from_utf8()) if json: # 解析后端返回的数据 var reply = json.get("reply", "") var emotion = json.get("emotion", "idle") var character_info = json.get("character", {}) # 触发事件,让UI和动画更新 EventBus.emit_signal("message_received", reply, emotion, character_info) remove_child(get_node("HTTPRequest")) # 清理请求节点
    • 在UI中,一个LineEdit用于输入,一个Button用于发送,点击按钮时调用ApiClient.send_message(text)
  4. 打包与分发

    • 在Godot编辑器中,进入项目 -> 导出
    • 添加一个“Windows桌面”预设,选择好导出路径和可执行文件名。
    • 关键一步:将你制作好的主题文件(.pck资源包)放在与导出的.exe文件同级目录下的themes/文件夹内。Godot在运行时可以动态加载这些.pck文件来切换桌宠外观。
    • 点击“导出项目”。你需要将导出的可执行文件、后端的整个文件夹(或打包后的后端可执行文件)以及启动脚本一起分发给用户。

4.3 前后端联调与数据协议定义

前后端通过RESTful API通信,数据格式使用JSON。一个典型的/chat端点请求和响应如下:

请求 (Godot -> FastAPI):

{ "message": "你好,帮我创建一个叫‘test.py’的文件,内容是打印‘Hello World’", "session_id": "user_unique_session_001" // 用于区分不同用户或会话,关联记忆 }

响应 (FastAPI -> Godot):

{ "reply": "好的,我已经在您的当前目录下创建了‘test.py’文件,并写入了打印‘Hello World’的代码。需要我为您运行它吗?", "emotion": "curious", // 当前情感状态,Godot据此播放动画 "character": { "level": 5, "exp": 120, "attributes": {"intelligence": 8, "charisma": 6}, "mood": "好奇" }, "actions": [ // 可选,指示前端执行一些动作,如播放特定音效 {"type": "play_sound", "name": "success"} ] }

联调时的常见问题:

  • CORS错误:Godot前端从file://或本地服务器访问localhost:8000时,浏览器(Godot的HTML5导出)或环境可能会因同源策略阻止请求。在FastAPI后端必须添加CORS中间件:
    from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["*"], # 生产环境应替换为具体的前端地址 allow_credentials=True, allow_methods=["*"], allow_headers=["*"], )
  • Godot HTTP请求失败:检查Godot控制台错误。可能是后端服务未启动,或URL错误。Godot的HTTPRequest对网络错误比较敏感,需要做好错误处理。

5. 开发中遇到的典型问题与排查实录

在开发这样一个融合了多种技术的项目时,踩坑是必然的。下面记录了几个最具代表性的问题及其解决方法。

5.1 LLM上下文管理与记忆检索的冲突

问题现象:在对话中,LLM有时会“忘记”刚刚由工具执行的结果,或者重复询问已经回答过的问题。例如,用户让桌宠创建一个文件,桌宠成功创建并回复后,用户紧接着问“你刚才创建了什么?”,LLM却回答“我还没有创建任何文件”。

根因分析

  1. Prompt拼接混乱:Agent在构造发给LLM的Prompt时,顺序可能是:系统指令 + 记忆检索结果 + 本次工具调用结果 + 当前用户问题。如果拼接不当,工具调用结果可能被淹没或格式错误,导致LLM无法正确识别。
  2. 记忆入库延迟:工具执行成功的结果,需要被总结并存入记忆系统(Cognee)。如果这个“记忆化”的过程是异步的,或者在下一次用户提问时还未完成,那么记忆检索就查不到刚刚发生的事。
  3. 检索相关性不高:Cognee的语义搜索可能没有把“刚才”、“创建”这样的时间关联性和动作关联性很好地捕捉到,导致检索不到最相关的记忆。

解决方案

  1. 明确Prompt结构:采用固定的、清晰的Prompt模板。例如:
    [系统角色和情感状态描述] [以下是相关的长期记忆:] {memory_context} [以下是本次对话中刚刚发生的事件:] {recent_events} [用户的最新请求:] {user_input}
    其中,recent_events专门存放本次会话中工具调用的输入和输出,确保LLM能“看到”即时结果。
  2. 同步记忆化:在工具执行成功后,立即(在同一次请求处理流程中)将关键结果同步存入一个“短期会话缓存”,同时异步触发“长期记忆入库”流程。这样,下一次用户提问时,即使长期记忆还未索引完,也能从会话缓存中拿到数据。
  3. 优化记忆查询:在向Cognee查询时,除了用户当前的问题,还可以附加上下文信息,如“查找与‘文件创建’相关的近期记忆”。可以尝试调整向量搜索的相似度阈值和图谱查询的深度。

5.2 Godot透明窗口的点击穿透与性能问题

问题现象:透明窗口设置好了,但鼠标点击无法穿透到后面的窗口,或者桌宠动画在移动时出现卡顿、闪烁。

排查与解决

  1. 点击穿透失效

    • 检查插件:确保click-through插件已正确启用,并且在窗口初始化后调用了其启用方法。有些插件需要在_ready()函数中调用set_mouse_passthrough(true),并且可能需要传递一个ColorRect节点,其透明区域才会被穿透。
    • 平台差异:Windows、macOS、Linux上实现透明窗口点击穿透的底层API不同,插件可能对某个平台支持不佳。需要查阅插件文档或源码,确认其兼容性。
    • 备用方案:如果插件不稳定,可以考虑一个“老板键”思路:设置一个全局快捷键(如Ctrl+Alt+P)来快速隐藏/显示桌宠窗口,而不是完全依赖点击穿透。
  2. 动画卡顿

    • 精灵图优化:确保你的精灵图(Sprite Sheet)尺寸合理,不是单张巨大的图片。Godot更适合处理多个中小尺寸的图集。使用纹理图集(Texture Atlas)功能。
    • 动画帧率:检查AnimatedSprite2DFPS设置是否过高。通常10-15 FPS对于桌宠动画已经足够流畅,过高的FPS会浪费资源。
    • 进程优先级:Godot前端作为一个“宠物”,不应该占用太多CPU。可以在代码中设置OS.low_processor_usage_mode = true(如果Godot版本支持),或者通过系统设置降低进程优先级。
    • 后台通信优化:确保与后端的HTTP请求是异步的,并且有适当的超时和重试机制,避免因网络等待阻塞主线程。

5.3 工具执行的安全边界与权限控制

问题描述:这是最危险的部分。一个拥有文件读写和代码执行能力的AI助手,如果被恶意诱导或出现逻辑错误,可能对用户系统造成损害。

实战中的安全策略

  1. 严格的路径白名单:文件工具绝不使用用户提供的原始路径。所有路径都必须在代码中解析为基于某个“工作根目录”(如~/Desktop/PetWorkspace)的相对路径,并检查是否试图跳出该根目录(防止../../../etc/passwd这类路径遍历攻击)。

    import os from pathlib import Path WORKSPACE_ROOT = Path.home() / "Desktop" / "PetWorkspace" WORKSPACE_ROOT.mkdir(exist_ok=True) def safe_resolve_path(user_path: str) -> Path: full_path = (WORKSPACE_ROOT / user_path).resolve() # 检查解析后的路径是否仍在工作区内 if not str(full_path).startswith(str(WORKSPACE_ROOT.resolve())): raise PermissionError("访问路径超出允许范围。") return full_path
  2. 代码执行的沙盒化

    • Docker容器:最安全的方式。为每次代码执行启动一个临时的、无网络、只读文件系统(除了挂载的临时目录)的Docker容器。限制其CPU和内存使用。缺点是启动较慢,依赖Docker环境。
    • pysandboxrestrictedpython:这些库尝试在Python解释器层面限制危险模块和函数。但历史上存在绕过漏洞,安全性不如容器。
    • subprocess强化:如果必须用subprocess,务必使用resource模块设置严格的资源限制(RLIMIT_CPU,RLIMIT_AS等),并使用seccomp过滤系统调用(仅限Linux)。同时,永远不要用shell=True参数。
    import subprocess, resource, signal def run_code_safely(code: str, timeout=5): def set_limits(): # 设置CPU时间限制 resource.setrlimit(resource.RLIMIT_CPU, (timeout, timeout)) # 设置内存限制 (例如 100MB) resource.setrlimit(resource.RLIMIT_AS, (100 * 1024 * 1024, 100 * 1024 * 1024)) try: proc = subprocess.Popen( ['python', '-c', code], preexec_fn=set_limits, # 仅Unix stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) stdout, stderr = proc.communicate(timeout=timeout) return stdout, stderr, proc.returncode except subprocess.TimeoutExpired: proc.kill() return "", "执行超时", -1
  3. 用户确认机制:对于高风险操作(如删除文件、安装系统包、修改环境变量),不要直接执行。而是让LLM生成一个操作描述,由前端弹窗让用户确认后再执行。这增加了最后一道人工防线。

5.4 情感与RPG数值的平衡与调试

问题现象:情感值变化太快,桌宠情绪像过山车;或者RPG升级太容易/太难,失去成长感。

调参经验

  1. 情感衰减系数:这是一个关键参数。如果情感值每分钟衰减10%,情绪会变化太快。我最终设置为每30分钟衰减1%-2%,这样情绪状态能持续数小时,更符合“性格”的感觉。事件影响系数也需要调整,一次愉快的对话可能提升happiness0.1,而一次任务失败降低calmness0.15。
  2. 经验值奖励公式:不要固定每次对话+10 EXP。可以设计一个动态公式:基础奖励 + 对话长度系数 + 任务复杂度系数。例如,一次简单的问候给5点,一次深入的代码讨论给20点,成功执行一个复杂工具给50点。让升级曲线符合指数增长,前期升级快有成就感,后期升级慢需要持续投入。
  3. 可视化调试:在开发阶段,为后端增加一个调试端点/debug/status,返回当前所有情感维度、RPG属性的详细数值。在Godot前端做一个隐藏的调试面板(按F12触发),实时显示这些数据。这比看日志直观得多。
  4. 避免数值膨胀:定期检查数据,确保数值在合理范围内。可以设置上下限(如情感值在[-1, 1]之间),或者定期进行轻微的“归一化”处理,防止某个维度因bug无限增长。

开发这样一个项目,就像在培育一个数字生命。从给它一个能思考的大脑(LLM),到赋予它记忆和情感,再到教会它使用工具并与世界交互,每一步都充满了挑战和乐趣。目前它还有很多不完美的地方,比如记忆检索的准确性、情感表现的细腻度、工具执行的安全性,都需要持续打磨。但看到它从最初一个只会复读的聊天窗口,逐渐变成一个能记住我喜好、会因为我完成工作而“开心”、能帮我处理琐事的桌面伙伴,这种成就感是无可比拟的。如果你也有兴趣,不妨从 fork 这个项目开始,添加一个你喜欢的角色模型,或者改进一下情感算法,期待看到更多有趣的智能桌宠诞生。

http://www.cnnetsun.cn/news/2090968.html

相关文章:

  • VSCode农业插件生态白皮书首发:覆盖23类农用设备协议(Modbus-RTU/ISOBUS/NMEA 0183),仅限首批500名涉农开发者领取
  • Phi-mini-MoE-instruct开发指南:API接口封装与Python client调用示例
  • 推荐 win11 可用的 SVN 版本:64位,下载最新的 TortoiseSVN 1.14.x 版本
  • 为什么Photoshop图层批量导出工具能大幅提升设计效率?
  • AI分子对接
  • ERP系统进销存模块源码深度解析(附代码):核心逻辑与实现方案
  • 51PR媒体发布平台整合近10万媒体资源,助力企业高效发稿
  • 重新定义地图创作:如何通过TEdit实现泰拉瑞亚世界的无限可能
  • MPL502-V-G-270 MPL 502系列传感器
  • MicMute:Windows平台一键麦克风静音解决方案,告别会议尴尬瞬间
  • DeepseekV3论文阅读笔记
  • Ryujinx终极指南:如何在PC上免费畅玩Switch游戏 [特殊字符]
  • 从成本到效果:5大软文发稿平台深度测评,帮你选出适合的那个
  • D6.3 PriorityClass 常用实验(2个)
  • Redisson 介绍
  • 算法训练营Day12| LeetCode 169. 多数元素
  • 2026年口碑优异的金相显微镜工厂盘点 有采购需求的建议收好
  • 宇宙学研究新突破:用 Blender 几何节点处理 CMB 数据,实现多项实用功能!
  • AI Agent在体育与娱乐领域的应用:数据分析与体验优化
  • HNU计算机系统期中题库详解(五)位运算与逻辑运算
  • 嵌入式团队紧急升级预警:VSCode 2026.1起废弃legacy GDB adapter——3类老旧JTAG探针将彻底失联?
  • KrkrzExtract终极指南:3步掌握krkrz引擎资源处理工具
  • 多线程下载引擎架构解析:AB Download Manager的技术实现路径
  • 如何快速掌握键盘控制鼠标:5个技巧让Windows操作更高效
  • 别再手动调色了!用JavaScript实现主题色自动生成9档深浅色(附完整代码)
  • TrollInstallerX:3分钟解锁iOS设备完整安装自由的专业指南
  • NVIDIA cuML FIL:GPU加速树模型推理性能优化指南
  • 告别Postman安装烦恼!Windows便携版终极解决方案完整指南
  • 别再傻傻分不清了!CAPL脚本里on signal、on envVar、on sysVar事件到底怎么用?
  • Android手机变身移动Linux服务器:Termux部署Ubuntu与SSH远程访问实战