AI Chat 设计模式:7. 单例模式

本文详细介绍了C++中实现单例模式的几种方法,包括饿汉式、懒汉式(双重检查和基于静态局部变量初始化),讨论了它们的线程安全性和资源管理。此外,还强调了单例模式中拷贝构造函数和赋值操作的处理,以及如何在程序结束时回收单例对象占用的内存。

本文是该系列的第七篇,采用问答式的方式展开,问题由我提出,答案由 Chat AI 作出,灰色背景的文字则主要是我的旁白和思考。

Q.1 简单介绍一下单例模式

A.1

单例模式是一种设计模式,旨在确保类只能有一个实例,并提供全局访问点来获取该实例。这意味着无论在系统中的哪个位置,调用者获取到的都是同一个实例对象。

单例模式常用于需要限制实例个数的场景,例如数据库连接对象、线程池、日志管理器等。通过使用单例模式,可以确保所有的调用方都使用同一个实例,避免了资源的重复创建和重复占用。

实现单例模式的关键是将类的构造函数设为私有,这样就无法在类的外部直接实例化对象。然后,通过一个静态方法或静态属性来创建或获取唯一的实例对象。这个方法或属性在第一次调用时会判断实例是否已存在,如果不存在就创建一个新的实例,然后返回该实例。如果实例已存在,就直接返回该实例。

单例模式有几种常见的实现方式,包括懒汉式饿汉式、双重检查锁式等,每种方式都有其适用的场景和特点。在实际使用中,需要根据具体的需求和线程安全性的考虑选择适合的实现方式。

单例模式使用了一种特殊的语法结构,将构造函数变为私有。

Q.2 详细说说饿汉式,并使用 c++ 举例

A.2

饿汉式是单例模式的一种实现方式,它的核心思想是在类加载时就创建好实例对象,并在需要时直接返回该对象。因此,它是线程安全的,但可能会造成资源的浪费

下面是一个使用C++实现的饿汉式单例模式的示例代码:

class Singleton {
private:
    static Singleton* instance;  // 唯一实例的指针

    // 私有构造函数,防止外部实例化对象
    Singleton() {}

public:
    // 获取单例对象的静态方法
    static Singleton* getInstance() {
        return instance;
    }
};

// 在类外部初始化静态成员变量
Singleton* Singleton::instance = new Singleton();

int main() {
    Singleton* obj1 = Singleton::getInstance();
    Singleton* obj2 = Singleton::getInstance();

    // obj1和obj2指向同一个对象,说明单例模式生效
    if(obj1 == obj2) {
        cout << "obj1 and obj2 are the same instance" << endl;
    }
    else {
        cout << "obj1 and obj2 are different instances" << endl;
    }

    return 0;
}

在这个示例中,Singleton类中定义了一个名为instance的静态成员变量,用于存储唯一的实例对象。在类外部,我们对instance进行了初始化,即在程序启动时就创建了实例对象。getInstance()方法用于获取该实例对象。

main()函数中,通过调用getInstance()方法获取到两个对象obj1obj2。通过比较它们的指针地址,我们可以确认它们是否指向同一个对象,从而验证单例模式是否生效。

需要注意的是,饿汉式是一种线程安全的实现方式,因为实例对象在类加载时就已经创建好了,不会出现多线程下的竞争问题。但也因为在程序启动时就创建实例对象,可能会造成资源的浪费,特别是在实例对象占用大量资源或需要进行耗时操作的情况下。

关键词:启动时创建、线程安全、资源浪费

Q.3 好,继续说说懒汉式

A.3

懒汉式是单例模式的另一种常见实现方式。与饿汉式不同,懒汉式在第一次需要获取实例对象时才会进行实例化,避免了不必要的资源浪费。但懒汉式在多线程环境下需要注意线程安全性。

下面是一个使用C++实现的简单懒汉式单例模式的示例代码:

#include <iostream>
#include <mutex>

using namespace std;

class Singleton {
private:
    static Singleton* instance;  // 唯一实例的指针
    static mutex mtx;  // 互斥锁,用于线程安全

    // 私有构造函数,防止外部实例化对象
    Singleton() {}

public:
    // 获取单例对象的静态方法
    static Singleton* getInstance() {
        if (instance == nullptr) {
            lock_guard<mutex> lock(mtx);  // 加锁,确保线程安全

            // 双重检查,防止多个线程同时通过第一个if语句
            if (instance == nullptr) {
                instance = new Singleton();
            }
        }
        return instance;
    }
};

Singleton* Singleton::instance = nullptr;
mutex Singleton::mtx;

int main() {
    Singleton* obj1 = Singleton::getInstance();
    Singleton* obj2 = Singleton::getInstance();

    // obj1和obj2指向同一个对象,说明单例模式生效
    if (obj1 == obj2) {
        cout << "obj1 and obj2 are the same instance" << endl;
    } else {
        cout << "obj1 and obj2 are different instances" << endl;
    }

    return 0;
}

在这个示例中,Singleton类中定义了一个名为instance的静态成员变量,用于存储唯一的实例对象。同时,还定义了一个静态的互斥锁mtx,用于在线程安全的环境下保护实例的创建过程。

getInstance()方法中,首先判断instance是否为nullptr,如果是,表示还没有创建实例对象,此时通过加锁的方式避免多线程同时创建实例。在加锁后,再次检查instance是否为nullptr,这是为了避免多个线程都通过了第一个检查,争夺锁之后再次创建实例。只有在第二次检查通过时,才实例化对象。

通过使用互斥锁,可以确保懒汉式在多线程环境下的线程安全性。只有当第一个线程创建好实例对象并释放锁之后,后续的线程才能获取到创建好的对象。这样可以避免多个线程同时创建多个实例对象的问题。

需要注意的是,懒汉式在第一次获取实例对象时才进行实例化,避免了不必要的资源消耗。但由于加锁机制的存在,会有一定的性能开销。此外,在高并发情况下,可能会出现多个线程同时通过第一个if语句的情况,虽然通过双重检查可以解决这个问题,但在某些特定的情况下,可能还需要采用其他的线程安全机制来保证单例模式的正确性。

为什么是双重检查呢?我们假设没有第一个 if 条件,那么就相当于锁了getInstance 这整个方法,当多个线程调用时,效率就很低,且如果类实例已经创建了,就只是只读访问,完全没必要加锁了。

先说说 instance == nullptr 这个条件,如果不成立,则类对象一定已经创建了,如果成立,则对象一定没创建吗?答案是否定的。

我们先看第一个 if,如果不成立,则对象已经被创建了,直接返回即可。如果成立,我们假设现在有两个线程,现在线程1通过了第一个 if 条件,刚要执行 instance = new Singleton() 的时候,却突然切到线程 2 了,此时线程2中第一个条件也会成立,那就会导致 new 两次。

那怎么办呢?当我们通过第一个 if 检查后就赶紧加锁吧,只要锁住后,再判断 instance == nullptr 条件,如果依然成立,那么肯定表示这个单例对象还没有初始化,我们就可以放心创建类对象了。
现在两次检查的作用就比较清楚了。第一次检查是为了提高效率,如果对象已经创建了,就不用加锁了,直接返回;第二次检查是在加锁后进行,为了保证创建类对象的代码只有一个线程会执行。

再补充一下最简单的懒汉式代码,适用于单线程,如下。这么一回头看,是不是双重检查的因果就更明晰了。

static Singleton* getInstance() {
	if (instance == nullptr) {
        instance = new Singleton();
    }
    return instance;
}

Q.4 这种双重检查,有什么问题不

A.4

双重检查是为了在懒汉式实现单例模式时解决多个线程同时通过了第一个检查的问题。尽管双重检查可以在某种程度上提高性能和线程安全性,但它也存在以下问题:

  1. 可能导致指令重排序问题:在某些编译器和处理器架构中,为了提高性能,可能会对指令进行重排序。在没有正确的同步机制的情况下,可能会导致先给对象分配内存,再进行初始化操作,然后再将地址赋值给instance,这可能会导致其他线程获取到一个尚未正确初始化的对象。

  2. 可能引发内存可见性问题:由于多线程的执行是无序的,一个线程在初始化对象之后,其他线程可能无法立即看到最新的对象状态,从而导致获取到一个未正确初始化的对象,进而引发错误。

为了解决这些问题,可以使用C++11引入的 memory_order_acquire 和memory_order_release memory order 来对这两类问题进行指令重排保护和内存可见性保证,可以采用原子操作std::atomic_thread_fence来对指令进行序列化和内存栅栏等手段来实现线程安全的双重检查。

另外,C++11还引入了更简洁的线程安全单例模式实现方式,即基于静态局部变量的局部静态变量初始化,这种方式不需要显式的互斥锁或双重检查,由编译器保证线程安全性。示例如下:

class Singleton {
private:
    Singleton() {}

public:
    static Singleton& getInstance() {
        static Singleton instance;
        return instance;
    }
};

在这种方式下,静态局部变量instance在第一次调用getInstance()方法时被初始化,并且C++11标准保证了静态局部变量初始化的线程安全性。

总结起来,双重检查虽然能够一定程度上提高性能和线程安全性,但是需要注意在编写时要考虑到指令重排序和内存可见性问题,同时也要注意支持多线程的特定平台上的实现。在C++11及以后的版本中,可以使用基于静态局部变量的局部静态变量初始化来实现线程安全的单例模式

补充两个点。

第一,关于“先给对象分配内存,再进行初始化操作,然后再将地址赋值给instance”,这就是 instance = new Singleton()这行代码的理想执行顺序,并不会导致其他线程获取到一个尚未正确初始化的对象。考虑如下场景,指令重排序导致双重锁定失效,(箭头表示指令的执行顺序):
在这里插入图片描述
上图中线程 1 还没有将 instance 指向构造初始化后的内存,就切换到了线程 2,此时instance 依然为 nullptr,因此线程 2 也会进入创建流程,先一步让 instance 指向了红色的内存块,随后又切换回线程 1 的指令,再次让 instance 指向了绿色的内存块。无论 instance 指针指向哪块内存,用户获得的都是一个正确初始化了的对象,而不是 AI 说的尚未正确初始化的对象,这只是导致了内存泄漏。

下面这样的指令重排序才会导致获得未初始化的对象,当刚执行到第二步,另一个线程来获取实例,会发现 instance 指针不为空,于是就认为对象已经被创建了,但其实还未进行构造初始化。
在这里插入图片描述

第二基于静态局部变量的局部静态变量初始化 这种方式,代码简单又安全,在 c++ 项目中还是比较常用的,至少我熟悉的是这种方式,除非你们的 c++ 版本还是11之前的,好吧。

Q.5 单例模式,是否需要把拷贝构造函数和拷贝赋值函数也设为私有呢

A.5

在单例模式中,将拷贝构造函数和拷贝赋值函数设为私有是一个常见的做法,旨在防止通过拷贝创建多个实例,确保只有一个实例存在。这样可以进一步加强单例模式的特性。

如果不将拷贝构造函数和拷贝赋值函数设为私有,那么可以通过拷贝创建多个实例,这可能破坏单例模式的意图。例如,如果有两个对象进行拷贝,那么就会有两个实例存在,违背了单例模式的原则。

为了避免这种情况,将拷贝构造函数和拷贝赋值函数设为私有可以有效地阻止对象的拷贝。这样一来,如果有人试图通过拷贝创建新的实例,编译器将会报错,提醒开发人员单例模式只允许存在一个实例。

以下是示例代码,演示如何将拷贝构造函数和拷贝赋值函数设为私有:

class Singleton {
private:
    static Singleton* instance;
    
    Singleton() {}  // 私有构造函数
    Singleton(const Singleton& other) {}  // 私有拷贝构造函数
    Singleton& operator=(const Singleton& other) { return *this; }  // 私有拷贝赋值函数

public:
    static Singleton* getInstance() {
        if(instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};

Singleton* Singleton::instance = nullptr;

通过将拷贝构造函数和拷贝赋值函数设为私有,确保了单例模式只存在一个实例,并且无法通过拷贝操作创建新的实例。这样可以防止意外创建多个对象,从而确保单例模式的正确性。

单例模式中,一般会将拷贝构造函数、赋值构造函数设置为私有,或者删除,防止出现多个实例。

Q.6 好的,总结一下上面几种单例模式写法的特点吧

A.6

下面是对上述几种单例模式写法的特点进行总结:

  1. 饿汉式:

    • 特点:在类加载时就创建好唯一实例,不存在多线程并发访问的安全问题。
    • 优点:实现简单,线程安全。
    • 缺点:可能造成资源浪费,因为实例在程序运行期间一直存在,无论是否被使用。
  2. 懒汉式(双重检查):

    • 特点:第一次获取实例时才进行实例化,避免了不必要的资源浪费。
    • 优点:实现较为灵活,只在需要时才创建实例;线程安全。
    • 缺点:需要解决指令重排序和内存可见性问题,实现稍复杂。
  3. 懒汉式(基于静态局部变量初始化):

    • 特点:使用静态局部变量的局部静态变量初始化,在需要时才进行实例化,并保证线程安全。
    • 优点:实现简洁,无需显式的锁或双重检查,由编译器保证线程安全性。
    • 缺点:在C++11及以后的版本才可使用。

对于饿汉式和懒汉式(双重检查),需要考虑多线程环境下的线程安全性,并采取相应的同步机制,如锁、互斥量、原子操作等,以保证单例模式的正确性。双重检查方式相对于饿汉式可以避免资源浪费,但需要在实现时注意解决指令重排序和内存可见性问题。

而懒汉式(基于静态局部变量初始化)是在C++11及以后的版本中引入的一种简洁的实现方式,由编译器保证线程安全性,无需显式的锁或双重检查。

选择哪种单例模式的实现方式,应根据具体的需求和环境来进行考虑。需要根据项目的特点和要求,综合考虑线程安全性、资源利用率以及实现复杂度等因素,选择适合的单例模式写法。

Q.7 单例模式,对象内存怎么释放呢

A.7

在单例模式中,对象的内存释放通常在程序结束时进行,因为单例模式的对象一般在整个程序执行期间都需要保持存在。因此,对象的内存释放由操作系统在程序退出时自动处理

对于懒汉式单例模式,由于对象的创建是在需要时进行的,因此对象的内存释放也应该在整个程序退出时进行。一般来说,操作系统会在程序结束时自动释放已分配的内存,包括单例对象的内存。

对于饿汉式单例模式和懒汉式(基于静态局部变量初始化)单例模式,在程序退出时,由于对象是静态变量,其内存会由操作系统自动释放。这是因为全局/静态变量的内存是在程序起始时分配的,操作系统会负责在程序结束时回收这些内存。

需要注意的是,如果在单例模式中使用了其他资源(如文件句柄、数据库连接等),则应该在程序结束前显式地释放这些资源。在单例类中提供一个公共的方法来释放资源是一个常见的做法,可供程序显式调用。

以下是示例代码,展示如何在单例模式中显式释放资源:

class Singleton {
private:
    // ...

public:
    // 获取单例实例的方法
    static Singleton& getInstance() {
        static Singleton instance;
        return instance;
    }

    // 释放资源的方法
    void releaseResources() {
        // 释放其他资源的代码
        // ...
    }
};

int main() {
    Singleton& singleton = Singleton::getInstance();

    // 使用单例对象...

    // 在程序结束前显式释放资源
    singleton.releaseResources();

    return 0;
}

在上述示例中,通过在单例类中添加一个releaseResources()方法,可以在程序结束前显式调用该方法来释放其他资源。

总结起来,在单例模式中,对象的内存释放由操作系统在程序结束时自动处理。但如果单例类中涉及到其他资源的占用,应该在程序退出前显式释放这些资源,以确保资源的正确释放。

单例模式,一般会将析构函数私有化,防止单例对象被意外释放。

Q.8 有什么办法,在程序结束时能主动回收单例对象占用的内存,而不是等操作系统来回收吗?

A.8

在一些特定的情况下,我们可能需要在程序结束时主动回收单例对象占用的内存,而不依赖于操作系统的自动回收。这可以通过调用单例类的析构函数来实现。

但是需要注意,对于单例模式而言,由于单例对象一般在整个程序运行期间都需要保持存在,主动释放单例对象占用的内存可能会导致对单例对象的再次访问时出现问题。因此,需要确保在主动释放内存后,不再对单例对象进行访问或使用

以下是一个示例代码,展示如何在程序结束时主动释放单例对象的内存:

class Singleton {
private:
    // 单例对象指针
    static Singleton* instance;

    // 私有构造函数和析构函数,确保单例对象的创建和销毁只能在内部进行
    Singleton() {}
    ~Singleton() {}

public:
    // 获取单例实例的方法
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }

    // 删除拷贝构造函数和拷贝赋值运算符,确保单例对象不可复制
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 主动释放单例对象的内存
    static void releaseInstance() {
        delete instance;
        instance = nullptr;
    }
};

// 初始化静态成员变量
Singleton* Singleton::instance = nullptr;

int main() {
    Singleton* singleton = Singleton::getInstance();

    // 使用单例对象...

    // 主动释放单例对象的内存
    Singleton::releaseInstance();

    return 0;
}

在上述示例中,我添加了一个静态指针instance作为单例对象的引用,通过调用getInstance()方法来获取单例对象。在releaseInstance()方法中,我们手动释放了该单例对象的内存,并将指针设为nullptr

需要明确,主动释放单例对象的内存是一种特殊情况,一般不建议在正常业务逻辑中使用。这样做可能会导致在之后再次使用单例对象时出现空悬指针或其他错误。在实际开发中,我们更倾向于让操作系统在程序结束时自动回收单例对象占用的内存。

Q.9 好的,我看网上有一种,在单例类中嵌套一个垃圾回收类的写法,给我说说呗

A.9

是的,你提到的在单例类中嵌套一个垃圾回收类的写法是一种常见的方式,用于在程序结束时自动释放单例对象的内存。这种方式利用了静态成员变量的析构函数在程序结束时被调用的特性

下面是一个示例代码,演示这种嵌套垃圾回收类的写法:

class Singleton {
private:
    // 垃圾回收类
    class GarbageCollector {
    public:
        ~GarbageCollector() {
            if (Singleton::instance) {
                delete Singleton::instance;
                Singleton::instance = nullptr;
            }
        }
    };

    // 单例对象指针
    static Singleton* instance;

public:
    // 获取单例实例的方法
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
            // 嵌套的垃圾回收类对象,在程序结束时自动释放单例对象的内存
            static GarbageCollector garbageCollector;
        }
        return instance;
    }

    // 删除拷贝构造函数和拷贝赋值运算符,确保单例对象不可复制
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // ...其他成员函数...
};

// 初始化静态成员变量
Singleton* Singleton::instance = nullptr;

int main() {
    Singleton* singleton = Singleton::getInstance();

    // 使用单例对象...

    return 0;  // 在程序结束时,垃圾回收类的析构函数会被自动调用,释放单例对象的内存
}

在上述示例中,Singleton类中嵌套了一个名为GarbageCollector的垃圾回收类。在getInstance()方法中的单例对象创建逻辑中,我们使用了一个static修饰的GarbageCollector对象,这保证了它只会在程序的第一次调用getInstance()时创建,并且在程序结束时自动触发析构函数。

当程序结束时,嵌套的GarbageCollector对象会被销毁,它的析构函数会检查单例对象指针是否存在,然后释放单例对象的内存,并将其指针置为空指针

使用嵌套的垃圾回收类可以避免依赖于用户调用释放方法或注册退出处理函数,使得单例对象的内存释放更加自动化和方便

如果程序以非正常的方式终止,如崩溃或强制关闭,嵌套的垃圾回收类对象的析构函数可能不会被调用,导致单例对象的内存泄漏

Q.10 画一下单例模式的类图

A.10

在这里插入图片描述

1.“# E:\AI_System\agent\cognitive_architecture.py import os import sys import logging import json import time import abc from pathlib import Path from agent.base_module import CognitiveModule class CognitiveSystem(CognitiveModule): """核心认知系统实现""" VERSION = "1.2.0" # 默认配置参数 DEFAULT_CONFIG = { "reasoning_depth": 3, "memory_limit": 1000, "auto_reflection": True, "learning_threshold": 0.8, "error_recovery": True, "max_concurrent_tasks": 5 } def __init__(self, name: str, model_manager, config: dict = None): """ 初始化认知系统 :param name: 认知系统名称 :param model_manager: 模型管理器实例 :param config: 可选配置字典,覆盖默认配置 """ super().__init__(name) self.model_manager = model_manager # 合并默认配置和用户配置 self.config = self.DEFAULT_CONFIG.copy() if config is not None: self.config.update(config) # 验证配置有效性 self._validate_config() # 初始化系统组件 self._initialize_components() self.mode = "TASK_EXECUTION" # 默认任务执行模式 self.command_handlers = { "help": self.handle_help, "hi": self.handle_greeting, "hello": self.handle_greeting, "你好": self.handle_greeting, "在吗": self.handle_greeting, "status": self.handle_status, "mode": self.handle_mode, "models": self.handle_models, } # 初始化记忆系统 self.memory = { "short_term": [], "long_term": {}, "last_accessed": time.time() } # 初始化日志 self.logger = logging.getLogger(f"CognitiveSystem.{name}") self.logger.info(f"✅ 认知系统初始化完成 (版本 {self.VERSION})") self.logger.info(f"当前模式: {self.mode}") self.logger.debug(f"系统配置: {self.config}") def process_command(self, command: str) -> str: """处理用户命令的核心方法""" try: self.logger.info(f"🧠 处理命令: {command}") # 分割命令和参数 parts = command.split(maxsplit=1) cmd = parts[0].lower() arg = parts[1] if len(parts) > 1 else "" # 查找命令处理器 handler = self.command_handlers.get(cmd, self.handle_default) return handler(arg) except Exception as e: self.logger.error(f"命令处理失败: {str(e)}", exc_info=True) return f"❌ 处理命令时出错: {str(e)}" # 命令处理函数 def handle_greeting(self, arg: str) -> str: """处理问候命令""" return f"你好,我是{self.name}!有什么可以帮您?" def handle_help(self, arg: str) -> str: """处理帮助命令""" return """ === 高级命令系统 === 基础命令: help - 显示此帮助信息 exit/quit - 退出系统 status - 查看系统状态 mode [mode]- 切换工作模式 (reflect, task, learn) 系统控制: models - 显示已加载模型 config [key] [value] - 修改配置 多行输入: 输入多行命令时,在最后一行以 ;; 结束 """ def handle_status(self, arg: str) -> str: """处理状态查询命令""" return ( f"系统状态:\n" f"- 认知系统: {self.name} v{self.VERSION}\n" f"- 当前模式: {self.mode}\n" f"- 最后访问: {self.memory['last_accessed']}\n" f"- 短期记忆: {len(self.memory['short_term'])}/{self.config['memory_limit']} 条" ) def handle_mode(self, arg: str) -> str: """处理模式切换命令""" if not arg: return "请指定模式: reflect, task, learn" mode_map = { "reflect": "SELF_REFLECTION", "task": "TASK_EXECUTION", "learn": "LEARNING" } new_mode = mode_map.get(arg.lower(), "") if new_mode: self.set_mode(new_mode) return f"已切换到 {new_mode} 模式" return f"❌ 无效模式: {arg} (可用选项: reflect, task, learn)" def handle_models(self, arg: str) -> str: """处理模型查询命令""" try: # 获取模型注册表和已加载模型 model_registry = self.model_manager.model_registry loaded_models = self.model_manager.loaded_models # 构建模型信息列表 models_info = [] for name, path in model_registry.items(): status = "✅ 已加载" if name in loaded_models else "❌ 未加载" models_info.append(f"- {name}: {path} ({status})") return "已配置模型:\n" + "\n".join(models_info) except Exception as e: return f"❌ 获取模型信息失败: {str(e)}" def handle_default(self, command: str) -> str: """默认命令处理器""" return f"正在处理您的请求: {command}..." def _validate_config(self): """验证配置参数有效性""" if not 1 <= self.config.get("reasoning_depth", 3) <= 5: raise ValueError("推理深度必须在1-5范围内") if self.config.get("memory_limit", 1000) < 100: raise ValueError("内存限制不能小于100") def _initialize_components(self): """初始化认知系统的各个子组件""" self.logger.debug("初始化推理引擎...") self.logger.debug("初始化记忆系统...") self.logger.debug("初始化学习系统...") self.logger.debug("初始化任务调度器...") def process_stimulus(self, stimulus: dict): """处理输入刺激""" try: self.logger.debug(f"处理刺激: {stimulus}") self.memory["last_accessed"] = time.time() if self.mode == "SELF_REFLECTION": return self._process_self_reflection(stimulus) elif self.mode == "LEARNING": return self._process_learning(stimulus) else: # TASK_EXECUTION return self._process_task(stimulus) except Exception as e: self.logger.error(f"处理刺激失败: {str(e)}", exc_info=True) return {"error": f"处理失败: {str(e)}"} def generate_response(self): """生成响应(保留方法)""" return {"status": "ready", "mode": self.mode} def get_current_mode(self): """获取当前模式""" return self.mode def set_mode(self, new_mode: str): """切换模式""" valid_modes = ["SELF_REFLECTION", "TASK_EXECUTION", "LEARNING"] if new_mode in valid_modes: self.mode = new_mode self.logger.info(f"切换到 {new_mode} 模式") return {"status": "success", "new_mode": new_mode} else: self.logger.warning(f"无效模式: {new_mode}") return {"status": "error", "message": f"无效模式: {new_mode}"} def _process_task(self, stimulus: dict): """处理任务执行""" task_type = stimulus.get("type", "general") content = stimulus.get("content", {}) self.logger.info(f"处理任务: {task_type}") if task_type == "question": return {"response": f"收到问题: {content.get('text', '')}"} elif task_type == "command": return {"response": f"执行命令: {content.get('text', '')}"} else: return {"response": f"处理通用任务: {json.dumps(content)}"} def _process_self_reflection(self, stimulus: dict): """处理自我反思""" self.logger.info("执行深度反思...") return {"reflection": "反思完成", "insights": []} def _process_learning(self, stimulus: dict): """处理学习任务""" self.logger.info("执行学习任务...") return {"learning": "学习完成", "knowledge": "新知识"} def save_state(self, path: str): """保存系统状态""" state = { "version": self.VERSION, "mode": self.mode, "last_accessed": self.memory["last_accessed"] } try: with open(path, 'w') as f: json.dump(state, f) self.logger.info(f"✅ 状态已保存到 {path}") return True except Exception as e: self.logger.error(f"保存状态失败: {str(e)}", exc_info=True) return False def load_state(self, path: str): """加载系统状态""" try: with open(path, 'r') as f: state = json.load(f) self.mode = state.get("mode", "TASK_EXECUTION") self.logger.info(f"✅ 状态已从 {path} 加载") return True except Exception as e: self.logger.error(f"加载状态失败: {str(e)}", exc_info=True) return False” 2."# E:\AI_System\core\config.py import os import json import re import logging from pathlib import Path from dotenv import load_dotenv from typing import Any, Dict, Union, Optional # 配置日志系统 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger('CoreConfig') class CoreConfig: """重构的配置系统 - 修复了单例模式和加载问题""" _instance = None _initialized = False def __new__(cls): """单例模式实现""" if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._initialized = False return cls._instance def __init__(self): """初始化配置系统(确保只执行一次)""" if self._initialized: return self._initialized = True # 基础设置 self.base_dir = Path(__file__).resolve().parent.parent self.config: Dict[str, Any] = {} self.sensitive_fields = ["DB_PASSWORD", "SECRET_KEY", "API_KEY"] self.path_keys = ["LOG_DIR", "MODEL_CACHE_DIR", "MODEL_BASE_PATH"] try: # 配置加载流程 self._load_defaults() # 步骤1: 设置默认值 self._load_environment() # 步骤2: 加载环境变量 self._load_config_files() # 步骤3: 加载配置文件 self._resolve_variables() # 步骤4: 解析变量引用 self._validate_paths() # 步骤5: 验证路径 logger.info("✅ 配置系统初始化完成") except Exception as e: logger.critical(f"🔥 配置加载失败: {str(e)}", exc_info=True) raise RuntimeError(f"配置系统初始化失败: {str(e)}") from e def _load_defaults(self): """设置默认配置值""" self.config = { "LOG_DIR": str(self.base_dir / "logs"), "CONFIG_DIR": str(self.base_dir / "config"), "MODEL_CACHE_DIR": str(self.base_dir / "model_cache"), "AGENT_NAME": "小蓝", "MODEL_BASE_PATH": "E:/AI_Models", "MODEL_PATHS": { "TEXT_BASE": "E:/AI_Models/Qwen2-7B", "TEXT_CHAT": "E:/AI_Models/deepseek-7b-chat", }, "NETWORK": { "HOST": "0.0.0.0", "FLASK_PORT": 8000, }, "DIRECTORIES": { "PROJECT_ROOT": str(self.base_dir) }, "USE_GPU": True, "LOG_LEVEL": "INFO" } logger.debug("设置默认配置值") def _load_environment(self): """加载环境变量""" # 1. 加载 .env 文件 env_file = self.base_dir / '.env' if env_file.exists(): load_dotenv(env_file) logger.info(f"🌐 从 {env_file} 加载环境变量") # 2. 加载系统环境变量 for key, value in os.environ.items(): # 忽略非配置变量 if not key.startswith("AI_SYSTEM_"): continue # 转换键名格式:AI_SYSTEM_DB__HOST -> db.host config_key = key[11:].lower().replace('__', '.') # 类型转换 converted_value = self._convert_env_value(value) # 设置配置值 self._set_nested_config(config_key, converted_value) logger.debug(f"设置环境变量: {config_key} = [MASKED]" if any(s in config_key for s in self.sensitive_fields) else f"设置环境变量: {config_key} = {converted_value}") def _convert_env_value(self, value: str) -> Any: """转换环境变量的值为适当类型""" if value.lower() in ['true', 'false']: return value.lower() == 'true' elif value.isdigit(): return int(value) elif value.replace('.', '', 1).isdigit(): try: return float(value) except ValueError: return value else: return value def _set_nested_config(self, key_path: str, value: Any): """设置嵌套配置值""" keys = key_path.split('.') current = self.config for i, key in enumerate(keys): if i == len(keys) - 1: current[key] = value else: if key not in current or not isinstance(current[key], dict): current[key] = {} current = current[key] def _load_config_files(self): """加载JSON/YAML配置文件""" config_dir = Path(self.get("CONFIG_DIR", self.base_dir / "config")) config_files = [ config_dir / "config.json", config_dir / "settings.yaml" ] for file in config_files: if not file.exists(): continue try: if file.suffix == ".json": with open(file, "r") as f: new_config = json.load(f) self._merge_config(new_config) logger.info(f"📄 加载配置文件: {file}") elif file.suffix in (".yaml", ".yml"): try: import yaml with open(file, "r") as f: new_config = yaml.safe_load(f) self._merge_config(new_config) logger.info(f"📄 加载配置文件: {file}") except ImportError: logger.warning("PyYAML未安装,跳过YAML配置") except Exception as e: logger.error(f"❌ 加载配置文件失败: {file} - {str(e)}") def _merge_config(self, new_config: dict): """深度合并配置字典""" def recursive_merge(base, update): for key, value in update.items(): if isinstance(value, dict) and key in base and isinstance(base[key], dict): recursive_merge(base[key], value) else: base[key] = value recursive_merge(self.config, new_config) def _resolve_variables(self): """解析配置中的变量引用(格式:${...})""" pattern = re.compile(r'\$\{([^}]+)\}') def resolve_value(value): if isinstance(value, str): return pattern.sub(self._replace_var, value) return value def traverse(data): if isinstance(data, dict): return {k: traverse(v) for k, v in data.items()} elif isinstance(data, list): return [traverse(item) for item in data] else: return resolve_value(data) self.config = traverse(self.config) def _replace_var(self, match) -> str: """替换单个变量引用""" var_expr = match.group(1).strip() # 处理默认值语法:${VAR|default} if '|' in var_expr: var_name, default_val = var_expr.split('|', 1) var_name = var_name.strip() default_val = default_val.strip() else: var_name = var_expr default_val = None # 获取配置值 value = self.get(var_name, default_val) return str(value) if value is not None else '' def _validate_paths(self): """验证并创建缺失的关键路径""" # 检查目录路径 for key in self.path_keys: path_str = self.get(key) if path_str: try: path = Path(path_str) if not path.exists(): path.mkdir(parents=True, exist_ok=True) logger.info(f"📁 创建目录: {path}") except Exception as e: logger.error(f"❌ 创建目录失败: {key}={path_str} - {str(e)}") def get(self, key_path: str, default: Any = None) -> Any: """通过点分路径获取配置值""" keys = key_path.split('.') value = self.config for key in keys: if isinstance(value, dict) and key in value: value = value[key] else: return default return value def __getattr__(self, name: str) -> Any: """允许通过属性访问顶级配置项""" if name in self.config: return self.config[name] raise AttributeError(f"配置项 '{name}' 不存在") # 创建全局配置实例 config = CoreConfig() " 3."# E:\AI_System\core\command_listener.py import threading import logging import time import sys from queue import Queue from typing import Callable, Union, Any logger = logging.getLogger("CommandListener") class CommandListener: """改进的命令监听器 - 增强多行处理和错误恢复""" def __init__(self, command_handler: Callable[[str], Union[str, dict]], shutdown_handler: Callable[[], None]): """ 初始化命令监听器 :param command_handler: 处理命令的函数,接受命令字符串,返回字符串或字典响应 :param shutdown_handler: 系统关闭时调用的函数 """ self.command_handler = command_handler self.shutdown_handler = shutdown_handler self.command_queue = Queue() self.running = False self.input_thread = None self.processing_thread = None self.is_processing = False self.multiline_buffer = [] # 多行输入缓冲区 def start(self): """启动命令监听器""" if self.running: logger.warning("命令监听器已在运行中") return logger.info("🔊 启动命令监听器") self.running = True # 启动输入线程 self.input_thread = threading.Thread( target=self._read_console_input, daemon=True, name="CommandListener-Input" ) self.input_thread.start() # 启动命令处理线程 self.processing_thread = threading.Thread( target=self._process_commands, daemon=True, name="CommandListener-Processor" ) self.processing_thread.start() logger.info("✅ 命令监听器已就绪,输入 'help' 查看可用命令") print("> ", end='', flush=True) # 初始提示符 def stop(self): """优雅停止命令监听器""" if not self.running: return logger.info("🛑 正在停止命令监听器...") self.running = False # 清空多行缓冲区 if self.multiline_buffer: logger.debug("清理多行缓冲区") self.multiline_buffer.clear() # 等待处理完成 while self.is_processing: time.sleep(0.1) # 放入退出指令 self.command_queue.put("exit") # 等待线程结束 self.input_thread.join(timeout=1.0) self.processing_thread.join(timeout=1.0) logger.info("命令监听器已停止") def _read_console_input(self): """从控制台读取输入并处理多行逻辑""" while self.running: try: # 读取一行输入 try: line = input().strip() except EOFError: # Ctrl+D logger.info("收到EOF信号") self.command_queue.put("exit") return except KeyboardInterrupt: # Ctrl+C print() # 换行 self.command_queue.put("exit") return # 空行处理 if not line: print("> ", end='', flush=True) continue # 多行结束标记检查 if line.endswith(";;"): # 移除结束标记并添加到缓冲区 self.multiline_buffer.append(line[:-2].strip()) full_command = "\n".join(self.multiline_buffer) self.command_queue.put(full_command) self.multiline_buffer.clear() print("> ", end='', flush=True) # 重置提示符 elif self.multiline_buffer: # 多行输入中间行 self.multiline_buffer.append(line) print("... ", end='', flush=True) # 多行提示符 else: # 单行命令 self.command_queue.put(line) print("> ", end='', flush=True) except Exception as e: logger.error(f"输入读取错误: {str(e)}") time.sleep(0.5) print("> ", end='', flush=True) # 恢复提示符 def _process_commands(self): """处理队列中的命令""" while self.running: try: if not self.command_queue.empty(): self.is_processing = True command = self.command_queue.get() # 跳过空命令 if not command.strip(): self.is_processing = False continue # 安全日志记录(屏蔽敏感命令) safe_command = self._sanitize_command(command) logger.info(f"📩 收到命令: {safe_command}") # 处理系统命令 if command.lower() in ["exit", "quit"]: logger.info("🛑 收到退出命令") self.shutdown_handler() return # 处理内置命令 if command.lower() == "help": response = self._get_help_text() print(f"\n{response}") self.is_processing = False continue # 处理其他命令 try: response = self.command_handler(command) self._handle_response(response) except Exception as e: logger.error(f"命令处理错误: {str(e)}", exc_info=True) print(f"\n❌ 命令处理错误: {str(e)}") self.is_processing = False time.sleep(0.05) except Exception as e: logger.error(f"命令处理循环错误: {str(e)}", exc_info=True) self.is_processing = False time.sleep(1) def _sanitize_command(self, command: str) -> str: """屏蔽敏感命令信息""" sensitive_keywords = ["password", "secret", "key", "token"] if any(kw in command.lower() for kw in sensitive_keywords): return "[敏感命令已屏蔽]" return command def _handle_response(self, response: Any): """处理不同类型的响应""" if isinstance(response, dict): # 字典响应直接打印 print("\n" + self._format_dict_response(response)) elif isinstance(response, str): # 字符串响应格式化输出 print(f"\n📤 {response}") elif response is None: print("\nℹ️ 无响应") else: print(f"\n📤 {str(response)}") def _format_dict_response(self, response: dict) -> str: """格式化字典响应为可读字符串""" formatted = "💬 系统响应:\n" for key, value in response.items(): formatted += f" • {key}: {value}\n" return formatted def _get_help_text(self): """生成帮助文本""" help_text = """ === 高级命令系统 === 基础命令: help - 显示此帮助信息 exit/quit - 退出系统 status - 查看系统状态 mode [mode]- 切换工作模式 (reflect, task, learn) 多行输入: 输入多行命令时,在最后一行以 ;; 结束 例如: update user set name = John Doe;; """ return help_text # 便捷启动函数 def start_command_listener(command_handler: Callable[[str], Union[str, dict]], shutdown_handler: Callable[[], None]) -> CommandListener: """ 创建并启动命令监听器 :param command_handler: 处理普通命令的函数 :param shutdown_handler: 系统关闭时调用的函数 :return: CommandListener实例 """ listener = CommandListener(command_handler, shutdown_handler) listener.start() return listener " 4.我有E:\AI_System\agent\decision_system文件夹 里面有“pycache init_.py !config.yaml critical trust.py decision system.py demo.py init trust system.py trust system.py trust utils.py”“# E:\AI_System\agent\decision_system\decision_system.py class EnhancedDecisionSystem(DecisionSystem): """整合多维度思考的增强决策系统""" def __init__(self, knowledge_base, emotion_model, memory_system): super().__init__(knowledge_base, emotion_model) self.memory = memory_system self.thinking_weights = { "conscious": 0.6, "unconscious": 0.3, "subconscious": 0.1 } self.long_term_questions = {} self.insights = [] # 启动后台思考线程 self._init_background_processing() def _init_background_processing(self): """初始化后台思考线程""" import threading self.running = True # 长期思考线程 self.long_term_thread = threading.Thread(target=self._process_long_term_questions) self.long_term_thread.daemon = True self.long_term_thread.start() # 灵感处理线程 self.insight_thread = threading.Thread(target=self._process_insights) self.insight_thread.daemon = True self.insight_thread.start() def make_decision(self, input_data, context, urgency=0.5): """ 增强决策方法 - 整合多维度思考 :param urgency: 紧急程度 (0.0-1.0) """ # 1. 无意识快速反应(条件反射) if urgency > 0.7: reflex_decision = self._unconscious_reflex(input_data) if reflex_decision: self._record_decision("reflex", reflex_decision, input_data) return reflex_decision # 2. 情感分析(整合到各维度) emotion_state = self.emotion.analyze(input_data) # 3. 多维度决策整合 conscious = self._conscious_processing(input_data, context, emotion_state) subconscious = self._subconscious_influence(input_data) # 4. 整合决策(动态权重) final_decision = self._integrate_decisions( conscious, subconscious, emotion_state, urgency ) self._record_decision("integrated", final_decision, input_data) return final_decision def _unconscious_reflex(self, input_data): """无意识快速反应(保留原有紧急处理逻辑)""" # 保留原有紧急处理逻辑 if "urgent" in input_data: return self._handle_urgent(input_data, self.knowledge) # 新增记忆驱动的反射 reflex_pattern = self.memory.retrieve_reflex_pattern(input_data) if reflex_pattern: return reflex_pattern["response"] # 情感驱动的快速反应 if self.emotion.current_intensity > 0.8: return self.emotion.get_high_intensity_response() return None def _conscious_processing(self, input_data, context, emotion_state): """有意识思考(整合原有规则系统)""" # 保留原有规则处理核心 knowledge = self.knowledge.retrieve(input_data) decision = self._apply_rules(input_data, emotion_state, knowledge) # 增强风险评估 risk_assessment = self._assess_risk(decision, context) return { "decision": decision, "risk": risk_assessment["risk"], "confidence": risk_assessment["confidence"] } def _subconscious_influence(self, input_data): """潜意识影响(新增功能)""" # 1. 检查相关灵感 relevant_insights = [i for i in self.insights if i["relevance"] > 0.7] if relevant_insights: return {"type": "insight", "content": relevant_insights[0]} # 2. 检查长期思考关联 for question_id, task in self.long_term_questions.items(): if self._is_related(input_data, task["question"]): return {"type": "long_term", "content": task["partial_conclusions"]} # 3. 记忆关联 associations = self.memory.retrieve_associations(input_data) return {"type": "association", "content": associations} def pose_question(self, question, priority=0.5): """提出长期思考问题(新增功能)""" task_id = f"q_{time.time()}" self.long_term_questions[task_id] = { "question": question, "priority": priority, "start_time": time.time(), "progress": 0.0, "partial_conclusions": [] } return task_id def _process_long_term_questions(self): """处理长期思考问题(后台线程)""" while self.running: for task_id, task in list(self.long_term_questions.items()): # 更新思考进度 task["progress"] = min(1.0, task["progress"] + 0.01 * task["priority"]) # 定期产生部分结论 if random.random() < 0.1 and task["progress"] > 0.3: conclusion = self._generate_partial_conclusion(task["question"]) task["partial_conclusions"].append(conclusion) # 思考完成时产生灵感 if task["progress"] >= 1.0: insight = self._generate_insight(task["question"]) self.insights.append(insight) del self.long_term_questions[task_id] time.sleep(10) # 每10秒处理一次 def _process_insights(self): """处理灵感队列(后台线程)""" while self.running: if self.insights: insight = self.insights.pop(0) # 将重要灵感存入长期记忆 self.memory.store(insight["content"], significance=0.8) # 更新知识库 self.knowledge.update_from_insight(insight) time.sleep(5) # 原有系统方法的增强版本 def learn_from_history(self): """增强的历史学习(整合多维度反馈)""" # 分析历史决策(保留原有逻辑) super().learn_from_history() # 新增多维度反馈分析 for entry in self.decision_history[-30:]: # 分析决策中不同维度的贡献 conscious_impact = self._analyze_conscious_impact(entry) subconscious_impact = self._analyze_subconscious_impact(entry) # 动态调整权重 if conscious_impact > subconscious_impact: self.thinking_weights["conscious"] = min(0.8, self.thinking_weights["conscious"] + 0.02) else: self.thinking_weights["subconscious"] = min(0.3, self.thinking_weights["subconscious"] + 0.02) # 其他辅助方法... ”我需要把我原有的decision_system.py删掉 还是把它放在E:\AI_System\agent里面 还是只需要新建你发给我的“# agent/diagnostic_system.py import logging import psutil class DiagnosticSystem: def __init__(self): self.logger = logging.getLogger("DiagnosticSystem") def check_modules(self): """检查核心模块状态""" results = { "cognitive_system": self._check_cognitive(), "environment_interface": self._check_environment(), "affective_system": self._check_affective(), "system_resources": self._check_resources() } return results def _check_cognitive(self): try: # 伪代码:实际应检查模块导入和初始化 from .cognitive_architecture import CognitiveSystem return {"status": "✅ 正常运行", "version": CognitiveSystem.VERSION} except Exception as e: return {"status": "❌ 异常", "error": str(e)} def _check_environment(self): # 类似实现 return {"status": "✅ 正常运行"} def _check_affective(self): # 类似实现 return {"status": "✅ 正常运行"} def _check_resources(self): """检查系统资源使用情况""" return { "cpu": f"{psutil.cpu_percent()}%", "memory": f"{psutil.virtual_memory().percent}%", "gpu": self._get_gpu_status() } def _get_gpu_status(self): try: import gpustat stats = gpustat.new_query() return [{ "id": gpu.index, "utilization": gpu.utilization, "memory": f"{gpu.memory_used}/{gpu.memory_total}MB" } for gpu in stats.gpus] except ImportError: return "⚠️ gpustat 未安装" except Exception as e: return f"❌ GPU检测失败: {str(e)}" ”还是需要什么别的操作? 5.你可以把修好的文件直接发我吗?我需要能直接覆盖、替换的,请不要让我去修改 因为我不会,也不知道怎么改
08-31
import time import os import requests import json from basereal import BaseReal from logger import logger from typing import Dict, List, Optional, Callable import jwt import hashlib import hmac import base64 from datetime import datetime, timedelta import threading class ConversationHistory: """对话历史管理类""" def __init__(self, max_rounds=5): self.max_rounds = max_rounds self.history = [] def add_message(self, role: str, content: str): """添加消息到历史记录""" self.history.append({"role": role, "content": content}) # 保持最多max_rounds轮对话(每轮包含用户和AI两条消息) self.history = self.history[-(self.max_rounds*2):] def get_messages(self) -> List[Dict]: """获取完整的历史消息列表""" return self.history.copy() def clear(self): """清空历史记录""" self.history = [] class CozeClient: def __init__(self, app_id: str, private_key: str, kid: str, bot_id: str): """ 初始化Coze客户端 :param app_id: 应用ID :param private_key: 私钥 :param kid: Key ID :param bot_id: 机器人ID """ self.last_activity = {} self.app_id = app_id self.private_key = private_key self.kid = kid self.bot_id = bot_id self.access_token = None self.token_expire_time = None self.lock = threading.Lock() self.conversation_histories = {} # 用户对话历史存储 self.user_conversations = {} # {user_id: conversation_id} self.session_file = "coze_sessions.json" self._load_sessions() # 初始化时加载保存的会话 def _load_sessions(self): try: with open(self.session_file, 'r') as f: data = json.load(f) self.user_conversations = data.get("user_conversations", {}) self.conversation_histories = { uid: ConversationHistory(max_rounds=5) for uid in data.get("conversation_histories", {}) } except (FileNotFoundError, json.JSONDecodeError): self.user_conversations = {} self.conversation_histories = {} def _save_sessions(self): data = { "user_conversations": self.user_conversations, "conversation_histories": { uid: hist.get_messages() for uid, hist in self.conversation_histories.items() } } with open(self.session_file, 'w') as f: json.dump(data, f) # 在CozeClient类中添加 CONVERSATION_TIMEOUT = 1800 # 30分钟 def get_conversation_id(self, user_id: str) -> Optional[str]: conv_id = self.user_conversations.get(user_id) if conv_id: self.last_activity[user_id] = time.time() # 更新活动时间 return conv_id def _create_jwt_token(self, expire_seconds: int = 3600) -> str: """创建JWT Token用于获取Access Token""" ts = int(time.time()) exp = ts + expire_seconds header = { "alg": "RS256", "typ": "JMT", "kid": self.kid } payload = { "iss": self.app_id, "aud": "api.coze.cn", "iat": ts, "exp": exp, "jti": self._get_random_string(4) } header_b64 = self._base64_url_encode(json.dumps(header).encode('utf-8')) payload_b64 = self._base64_url_encode(json.dumps(payload).encode('utf-8')) h_and_p = f"{header_b64}.{payload_b64}" signature = self._rsa_sign(self.private_key, h_and_p) signature_b64 = self._base64_url_encode(signature) return f"{h_and_p}.{signature_b64}" def _get_access_token(self) -> str: """获取Access Token,优先使用缓存的token""" if self.access_token and self.token_expire_time and time.time() < self.token_expire_time - 10: return self.access_token with self.lock: if self.access_token and self.token_expire_time and time.time() < self.token_expire_time - 10: return self.access_token jwt_token = self._create_jwt_token(300) data = { "duration_seconds": 86399, "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer" } headers = { "Content-Type": "application/json", "Authorization": f"Bearer {jwt_token}" } response = requests.post( "https://api.coze.cn/api/permission/oauth2/token", json=data, headers=headers, timeout=20 ) if response.status_code == 200: result = response.json() self.access_token = result.get("access_token") expires_in = result.get("expires_in", 86399) self.token_expire_time = time.time() + expires_in - 10 return self.access_token else: raise Exception(f"Failed to get access token: {response.text}") def get_conversation_history(self, user_id: str, max_rounds: int = 5) -> List[Dict]: """获取用户对话历史""" if user_id not in self.conversation_histories: self.conversation_histories[user_id] = ConversationHistory(max_rounds) return self.conversation_histories[user_id].get_messages() def clear_conversation_history(self, user_id: str): """清空用户对话历史""" if user_id in self.conversation_histories: self.conversation_histories[user_id].clear() def stream_chat( self, conversation_id: Optional[str], user_id: str, messages: List[Dict], on_message: Callable[[str, Dict], None], cancellation_token=None, max_history_rounds: int = 5, nerfreal: Optional[BaseReal] = None, ): # 超时检查(添加在函数开头) if (conversation_id and time.time() - self.last_activity.get(user_id, 0) > self.CONVERSATION_TIMEOUT): self.clear_conversation_history(user_id) conversation_id = None logger.info(f"Conversation timeout, new session started") # 在函数开始时添加日志 logger.info(f"Starting chat - User: {user_id}, Existing Conversation ID: {conversation_id}") """ 流式对话(支持历史记录) :param max_history_rounds: 最大历史对话轮数 :param nerfreal: BaseReal实例,用于消息输出 """ token = self._get_access_token() # 获取历史记录并合并新消息 history = self.get_conversation_history(user_id, max_history_rounds) all_messages = history + messages url = "https://api.coze.cn/v3/chat" if conversation_id: url += f"?conversation_id={conversation_id}" data = { "bot_id": self.bot_id, "user_id": user_id, "additional_messages": all_messages, "stream": True } headers = { "Content-Type": "application/json", "Authorization": f"Bearer {token}" } # 定义包装回调以处理历史记录 full_response = "" buffer = "" def wrapped_callback(event: str, msg: dict): nonlocal full_response, buffer, conversation_id # 添加conversation_id到nonlocal #logger.debug(f"Received event: {event}, data: {json.dumps(msg, ensure_ascii=False)}") if event == "conversation.message.delta": # 从delta消息中获取会话ID(如果有) if msg.get("conversation_id"): conversation_id = msg["conversation_id"] self.user_conversations[user_id] = conversation_id self._save_sessions() # 立即保存 #logger.info(f"Updated conversation ID: {conversation_id}") #logger.info(f"Delta message - ID: {msg.get('id')}, " # f"Conversation ID: {msg.get('conversation_id')}, " # f"Content: {msg.get('content')}") if msg.get("type") == "answer" and msg.get("content_type") == "text": content = msg.get("content", "") reasoning_content = msg.get("reasoning_content", "") if reasoning_content: logger.info(f"Thinking content: {reasoning_content}") if content: buffer += content sentence_enders = ['.', '!', '?'] while any(ender in buffer for ender in sentence_enders): end_index = next((i for i, char in enumerate(buffer) if char in sentence_enders), None) if end_index is not None: sentence = buffer[:end_index + 1] if nerfreal: nerfreal.put_msg_txt(self._filter_urls(sentence)) # 优化历史记录处理 - 减少文件IO if os.path.exists('systemReplyArray.txt'): with open('systemReplyArray.txt', 'r+', encoding='utf-8') as f: previous_lines = f.read().splitlines()[:3] f.seek(0) f.write(sentence + '\n' + '\n'.join(previous_lines)) f.truncate() logger.info(f"Processed sentence: {sentence}") full_response += sentence buffer = buffer[end_index + 1:] elif event == "conversation.message": # 保存会话ID并记录完整消息 if msg.get("conversation_id") and user_id in self.user_conversations: self.user_conversations[user_id] = msg["conversation_id"] #logger.info(f"New conversation established - " # f"Conversation ID: {msg['conversation_id']}, " # f"Message ID: {msg.get('id')}") elif event == "error": error_msg = msg.get("error", "Unknown error") logger.error(f"Chat error occurred: {error_msg}") # if nerfreal: # nerfreal.put_msg_txt(f"对话出错: {error_msg}") # full_response += f"对话出错: {error_msg}" elif event == "done": if buffer: if nerfreal: nerfreal.put_msg_txt(self._filter_urls(buffer)) logger.info(f"Final buffer content: {buffer}") full_response += buffer buffer = "" # 记录完整对话历史 logger.info(f"Completed conversation - " f"Final response: {full_response}") # 将完整回复加入历史记录 if user_id in self.conversation_histories: self.conversation_histories[user_id].add_message("assistant", full_response) # 写入完整回复到文件 with open('systemReply.txt', 'w', encoding='utf-8') as f: f.write(full_response) # 调用原始回调 on_message(event, msg) try: with requests.post(url, json=data, headers=headers, stream=True, timeout=300) as response: if response.status_code != 200: wrapped_callback("error", {"error": response.text}) return for line in response.iter_lines(): if cancellation_token and cancellation_token.is_cancelled(): wrapped_callback("user_cancel", None) break if not line: continue line = line.decode('utf-8') if line.startswith("event:"): event = line[6:].strip() elif line.startswith("data:"): data = line[5:].strip() if data == "[DONE]" or data == "\"[DONE]\"": wrapped_callback("done", None) break try: msg = json.loads(data) wrapped_callback(event, msg) except json.JSONDecodeError: logger.error(f"Failed to parse message: {data}") except Exception as e: wrapped_callback("error", {"error": str(e)}) @staticmethod def _filter_urls(text: str) -> str: """过滤掉文本中的HTTP/HTTPS链接""" import re url_pattern = re.compile(r'https?://\S+') return url_pattern.sub('', text) @staticmethod def _base64_url_encode(data: bytes) -> str: """Base64 URL安全编码""" return base64.urlsafe_b64encode(data).decode('utf-8').rstrip('=') @staticmethod def _rsa_sign(private_key: str, message: str) -> bytes: """RSA签名""" from cryptography.hazmat.primitives import serialization, hashes from cryptography.hazmat.primitives.asymmetric import padding private_key_obj = serialization.load_pem_private_key( private_key.encode(), password=None ) signature = private_key_obj.sign( message.encode(), padding.PKCS1v15(), hashes.SHA256() ) return signature @staticmethod def _get_random_string(length: int) -> str: """生成随机字符串""" import random import string return ''.join(random.choices(string.ascii_letters + string.digits, k=length)) # 在模块级别创建单例客户端 _coze_client_instance = None def llm_response(message, nerfreal: BaseReal): start = time.perf_counter() global _coze_client_instance if _coze_client_instance is None: _coze_client_instance = CozeClient( app_id="1173801711558", private_key="-----BEGIN PRIVATE KEY-----\n" "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC2sewRl13lFCgB\n" "s+ypHkWelDwS4NRiiUgDngisx8EV8awSS5mnj5GX632ZFwwHqLQbeQAvKys1/fiK\n" "3nZduO6wSRKX3sqqxdWVhUcVJA2YURFLbVpkHozAJhY9s8wgug1Z7UjKgxOHupcx\n" "1Ia1aW00cM03EE97Gq1u/qCoMjJtba8f/Igwgpctfg1YKtlzTKI+2NNo9OfQaBY+\n" "WBilcTlkf8isw7zW/4g7f1/CpKPhAhsbjR5S4PPGZObi6m7Th49kUL8jbhcZGQir\n" "JsVwhqoTmFU/rTwDJKMxdYNN3Xd6r1HwNilOzJxLBp+ayhQ2FzSzrDZBWNlfnL3A\n" "eOqv2VDxAgMBAAECggEAPfIvLrnJ0wpWFFm7FY7XoVD225nTOcP9oIhdvaQPks66\n" "fwuWQov6HG5zTEzVveUUiLoq91NmV+zQ8NlEfjvd5vUn8knPIz9oT8X8l6z9VRer\n" "ywz9mLQJGn/vi6ViwfgD3emIhG4UWbHJYVKECJACQMU8t/52TLH7e4an19AJbDOx\n" "GyurdgvhtMrsKblbfMUShvDrhice5oeh2N/NNMw4TfUwIlWei4vdGS7Nh9itDSor\n" "Pxy8B9ezkkGFjskANIzwsfhvd8+c7TX6X5DnHmFVuL2M81AzcbVGPElx/GcbPkob\n" "4GyVVmzI8ugBEFqlJmGHPKOl1e4UB5mXa+ylKGmdhwKBgQDpFtL0HIldLvbHlCGO\n" "jdfSvzCPcV27koY8qYLp1ShAGx4VHlLWZOt/eV2Y0Fqq/X/QIIrCnkgzlDcWzISx\n" "itZUkl3KtoxCrzloprcc7dI3gLdAhl3vsoeF9DL/J4iemxnsBi97gU7dDuBxt8fb\n" "M0eYS43WsF8LATqCJLze0uGR3wKBgQDIpwpLGA22bfgh477y+51zdNuIFJ1Ll7qw\n" "Fbz+speWbxxnwIjRX3lR1lh3uBdmIesFFsMTPc06IM4e+bj37AyucgRy/bch6z4g\n" "L86kQCKKdGG0J+jMYdcFo5xKlYxN97hw8FQEl2JksbKkSf4fFAyWQjfLFcclT5cV\n" "Eo+JUzuXLwKBgCY8r0iKceJOdP9Shpq7HB+fa5jscQL3S3wiFq7DYAH8MNgoDFDN\n" "Z3CW+Uq7S1Rnl5MN85Vvn8qOUuczj8UMUJK5HBfIEIRT+Gf5iWp+fRDL1cQJBtnu\n" "gJrx73e6BYh3Sy5T6XAqS0SqTxl4m5mS9Pi/1DnW3xCQGAgHfNBU6dojAoGALlN6\n" "qenMyLDNGC332SvEp3J0eQ+hXWGTpbHvJ7LeEspmeYHXVNfBL+bYGBP1uwvbshoW\n" "QewD5QbL8BTh4sOqDeCfLFltnbQtbMr836k7EFJceHa6Ze208kVbAVFTynCGMfUa\n" "wNCe0/a+8vVuaYh8e3igXxARIYklraTSZPdFi9sCgYAh7RMUrRhv/AcFyJIPhfyA\n" "y9KdUSPbbGT4/JoDKNE3TO61/v/h+q6WHSruYxpReZQyJTDPprL8inCBpfPmoPXX\n" "vlDUehuRQ74xOkT4u+Xd9YYjR2V3zqthrydXrY+8aZxLi/ZumgBqRx18HxPyYkj/\n" "2ASVX3TOEGLOC8dvq706AQ==\n" "-----END PRIVATE KEY-----", kid="AT0Q-GegCst7M3PcJz_icpUwDMrdXogc5q4k2SqTAXI", bot_id="7546524863025463347" ) coze_client = _coze_client_instance user_id = "fixed_user_id_for_session" def get_real_user_id() -> str: # 示例1:从Web框架的Session获取(如Flask/Django) # return session.get("user_id") return "fixed_user_id_for_session" # 示例2:生成临时会话ID(匿名用户) #import uuid #return str(uuid.uuid4()) # 在llm_response函数中: #user_id = get_real_user_id() if user_id not in coze_client.user_conversations: coze_client.user_conversations[user_id] = None # 初始化 logger.info(f"New user session: {user_id}") conversation_id = coze_client.get_conversation_id(user_id) logger.info(f"Current conversation - User: {user_id}, ID: {conversation_id}") # 添加用户消息到历史记录(自动处理) user_message = { "role": "user", "content": message } # 发起流式对话(会自动处理历史记录) coze_client.stream_chat( conversation_id=conversation_id, user_id=user_id, messages=[user_message], # 只需要传入新消息,历史记录会自动添加 on_message=lambda event, msg: None, # 实际处理在wrapped_callback中完成 max_history_rounds=5, # 保持5轮对话历史 nerfreal=nerfreal # 传递nerfreal参数 ) end = time.perf_counter() logger.info(f"llm总耗时: {end-start}s") 我在重构这个项目, 需要把coze换成ollama的, 本地部署就行不需要联网, 配置好
10-11
你还是把这个改好了 发完整版给我吧 你的修复我看不懂 弄得我不知道是直接替换 还是让我干什么“# E:\AI_System\core\config.py import os import sys import json import logging from pathlib import Path from dotenv import load_dotenv from prettytable import PrettyTable # 临时添加项目根目录到Python路径 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) class CoreConfig: _instance = None @classmethod def get_instance(cls): """获取单例实例""" if cls._instance is None: cls._instance = cls() return cls._instance def __init__(self): """初始化配置系统""" # 设置日志 self.logger = logging.getLogger('CoreConfig') self.logger.setLevel(logging.INFO) # 确保有基本日志处理器 if not self.logger.handlers: handler = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) self.logger.addHandler(handler) # 设置基础目录 self.base_dir = Path(__file__).resolve().parent.parent self.env_prefix = "AI_SYSTEM" self.config = {} # 敏感字段列表(在日志和输出中掩码) self.sensitive_fields = ["DB_PASSWORD", "SECRET_KEY", "API_KEY", "ACCESS_TOKEN"] # 加载配置 self._load_config() # 直接调用方法,不要嵌套定义 self.logger.info("✅ 配置系统初始化完成") # 注意:所有方法都在类作用域内,不要嵌套定义 def _load_config(self): """加载所有配置""" # 1. 设置默认值 self._set_defaults() # 2. 加载配置文件 self._load_config_files() # 3. 加载环境变量 self._load_environment() # 4. 验证关键路径 self.validate_model_paths() def _mask_sensitive_value(self, key, value): """对敏感信息进行掩码处理""" if value and key in self.sensitive_fields: return "******" return value # ... 其他方法保持不变 ... def _log_sensitive_value(self, key, value): """在日志中安全地记录敏感信息""" if key in self.sensitive_fields: self.logger.info(f"🔄 环境变量覆盖: {key}=******") else: self.logger.info(f"🔄 环境变量覆盖: {key}={value}") def _set_defaults(self): """设置默认配置值""" # 系统路径配置 defaults = { "LOG_DIR": str(self.base_dir / "logs"), "CONFIG_DIR": str(self.base_dir / "config"), "MODEL_CACHE_DIR": str(self.base_dir / "model_cache"), "AGENT_NAME": "小蓝", "DEFAULT_USER": "管理员", "MAX_WORKERS": 4, "AGENT_RESPONSE_TIMEOUT": 30.0, # 模型路径配置 "MODEL_BASE_PATH": "E:/AI_Models", "TEXT_BASE": "E:/AI_Models/Qwen2-7B", "TEXT_CHAT": "E:/AI_Models/deepseek-7b-chat", "MULTIMODAL": "E:/AI_Models/deepseek-vl2", "IMAGE_GEN": "E:/AI_Models/sdxl", "YI_VL": "E:/AI_Models/yi-vl", "STABLE_DIFFUSION": "E:/AI_Models/stable-diffusion-xl-base-1", # 系统路径配置 "SYSTEM_ROOT": str(self.base_dir), "AGENT_DIR": str(self.base_dir / "agent"), "WEB_UI_DIR": str(self.base_dir / "web_ui"), "CORE_DIR": str(self.base_dir / "core"), "MODELS_DIR": str(self.base_dir / "models"), "LOGS_DIR": str(self.base_dir / "logs"), # 服务器配置 "HOST": "0.0.0.0", "FLASK_PORT": 8000, "GRADIO_PORT": 7860, # 数据库配置 "DB_HOST": "localhost", "DB_PORT": 5432, "DB_NAME": "ai_system", "DB_USER": "ai_user", "DB_PASSWORD": "", # 安全配置 "SECRET_KEY": "default-secret-key" } for key, value in defaults.items(): self.config[key] = value self.logger.debug(f"设置默认值: {key}={self._mask_sensitive_value(key, value)}") def _load_config_files(self): """加载配置文件""" # 确保配置目录存在 config_dir = self.base_dir / "config" config_dir.mkdir(exist_ok=True, parents=True) # 配置加载顺序 config_files = [ config_dir / 'default.json', config_dir / 'local.json' ] for config_file in config_files: if config_file.exists(): try: with open(config_file, 'r', encoding='utf-8') as f: config_data = json.load(f) # 掩码敏感信息 masked_data = {k: self._mask_sensitive_value(k, v) for k, v in config_data.items()} self.config.update(config_data) self.logger.info(f"📂 从 {config_file} 加载配置: {masked_data}") except Exception as e: self.logger.error(f"❌ 加载配置文件 {config_file} 错误: {str(e)}") else: self.logger.info(f"ℹ️ 配置文件不存在: {config_file},跳过") def _load_environment(self): """加载环境变量""" # 加载.env文件 env_file = self.base_dir / '.env' if env_file.exists(): try: # 加载.env文件 load_dotenv(dotenv_path=str(env_file), override=True) self.logger.info(f"🌐 从 {env_file} 加载环境变量") except Exception as e: self.logger.error(f"❌ 加载环境变量失败: {str(e)}") # 覆盖环境变量中的配置 for key in list(self.config.keys()): # 先尝试带前缀的环境变量 prefixed_key = f"{self.env_prefix}_{key}" env_value = os.getenv(prefixed_key) # 如果带前缀的环境变量不存在,尝试直接使用key if env_value is None: env_value = os.getenv(key) if env_value is not None: # 尝试转换数据类型 if env_value.lower() in ['true', 'false']: env_value = env_value.lower() == 'true' elif env_value.isdigit(): env_value = int(env_value) elif env_value.replace('.', '', 1).isdigit(): try: env_value = float(env_value) except ValueError: pass # 保持字符串 self.config[key] = env_value self._log_sensitive_value(key, env_value) def validate_model_paths(self): """验证所有模型路径是否存在""" model_keys = ["TEXT_BASE", "TEXT_CHAT", "MULTIMODAL", "IMAGE_GEN", "YI_VL", "STABLE_DIFFUSION"] results = {} for key in model_keys: path = self.get(key, "") if path: path_obj = Path(path) exists = path_obj.exists() results[key] = { "path": str(path_obj), "exists": exists } if not exists: self.logger.warning(f"⚠️ 模型路径不存在: {key} = {path}") else: results[key] = { "path": "", "exists": False } self.logger.warning(f"⚠️ 模型路径未配置: {key}") return results def get(self, key, default=None): """获取配置值""" return self.config.get(key, default) def __getitem__(self, key): """通过键访问配置值""" return self.config[key] def __contains__(self, key): """检查键是否存在""" return key in self.config def to_dict(self, mask_sensitive=True): """返回当前配置的字典表示""" if mask_sensitive: return {k: self._mask_sensitive_value(k, v) for k, v in self.config.items()} return self.config.copy() # 创建全局配置实例 config = CoreConfig.get_instance() # 测试代码 if __name__ == "__main__": # 设置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) print("=" * 50) print("配置系统测试") print("=" * 50) # 获取配置实例 config = CoreConfig.get_instance() # 打印基本配置 print(f"AGENT_NAME: {config.get('AGENT_NAME')}") print(f"SYSTEM_ROOT: {config.get('SYSTEM_ROOT')}") print(f"LOG_DIR: {config.get('LOG_DIR')}") print(f"AGENT_DIR: {config.get('AGENT_DIR')}") print(f"WEB_UI_DIR: {config.get('WEB_UI_DIR')}") print(f"DB_HOST: {config.get('DB_HOST')}") # 验证模型路径 print("\n模型路径验证结果:") for model, info in config.validate_model_paths().items(): status = "存在 ✅" if info["exists"] else "不存在 ❌" print(f"{model:20} {status} ({info['path']})") # 使用表格显示所有配置(美化输出) print("\n当前所有配置:") table = PrettyTable() table.field_names = ["配置项", "值"] table.align["配置项"] = "l" table.align["值"] = "l" # 获取掩码后的配置 masked_config = config.to_dict(mask_sensitive=True) for key, value in masked_config.items(): table.add_row([key, value]) print(table) print("\n测试完成!") ”还有这个 我不知道怎么添加,你下次最好把需要更改的文件 都弄成能直接复制粘贴的那种 你这么发 我看不明白 更改不明白 懂吗?“{ "ENV": "dev", "LOG_LEVEL": "DEBUG", "USE_GPU": false, "DEFAULT_MODEL": "minimal-model", "HOST": "0.0.0.0", "FLASK_PORT": 8000, "GRADIO_PORT": 7860, "DB_HOST": "localhost", "DB_PORT": 5432, "DB_NAME": "ai_system", "DB_USER": "ai_user" }”
08-24
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值