feat: release 2.0.3

This commit is contained in:
zhayujie
2026-03-18 15:40:49 +08:00
parent 289989d9f7
commit 8993e8ad3e
11 changed files with 121 additions and 79 deletions

View File

@@ -102,9 +102,9 @@ bash <(curl -fsSL https://cdn.link-ai.tech/code/cow/run.sh)
项目支持国内外主流厂商的模型接口,可选模型及配置说明参考:[模型说明](#模型说明)。
> Agent模式下推荐使用以下模型可根据效果及成本综合选择MiniMax-M2.5、glm-5、kimi-k2.5、qwen3.5-plus、claude-sonnet-4-6、gemini-3.1-pro-preview、gpt-5.4
> Agent模式下推荐使用以下模型可根据效果及成本综合选择MiniMax-M2.5、glm-5、kimi-k2.5、qwen3.5-plus、claude-sonnet-4-6、gemini-3.1-pro-preview、gpt-5.4、gpt-5.4-mini
同时支持使用 **LinkAI平台** 接口,可灵活切换 OpenAI、Claude、Gemini、DeepSeek、Qwen、Kimi 等多种常用模型并支持知识库、工作流、插件等Agent能,参考 [接口文档](https://docs.link-ai.tech/platform/api)。
同时支持使用 **LinkAI平台** 接口,支持上述全部模型并支持知识库、工作流、插件等Agent能,参考 [接口文档](https://docs.link-ai.tech/platform/api)。
### 2.环境安装
@@ -226,8 +226,9 @@ nohup python3 app.py & tail -f nohup.out
执行后程序运行于服务器后台,可通过 `ctrl+c` 关闭日志,不会影响后台程序的运行。使用 `ps -ef | grep app.py | grep -v grep` 命令可查看运行于后台的进程,如果想要重新启动程序可以先 `kill` 掉对应的进程。 日志关闭后如果想要再次打开只需输入 `tail -f nohup.out`
此外,项目`scripts` 目录下有一键运行、关闭程序的脚本供使用。 运行后默认channel为web通过可以通过修改配置文件进行切换
此外,项目根目录下的 `run.sh` 脚本支持一键启动和管理服务,包括 `./run.sh start``./run.sh stop``./run.sh restart``./run.sh logs` 等命令,执行 `./run.sh help` 可查看全部用法
> 如果需要通过浏览器访问Web控制台请确保服务器的 `9899` 端口已在防火墙或安全组中放行建议仅对指定IP开放以保证安全。
### 3.Docker部署
@@ -257,17 +258,7 @@ sudo docker compose up -d # 若docker-compose为 1.X 版本,则执行
sudo docker logs -f chatgpt-on-wechat
```
**(3) 插件使用**
如果需要在docker容器中修改插件配置可通过挂载的方式完成将 [插件配置文件](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/plugins/config.json.template)
重命名为 `config.json`,放置于 `docker-compose.yml` 相同目录下,并在 `docker-compose.yml` 中的 `chatgpt-on-wechat` 部分下添加 `volumes` 映射:
```
volumes:
- ./config.json:/app/plugins/config.json
```
**注**使用docker方式部署的详细教程可以参考[docker部署CoW项目](https://www.wangpc.cc/ai/docker-deploy-cow/)
> 如果需要通过浏览器访问Web控制台请确保服务器的 `9899` 端口已在防火墙或安全组中放行建议仅对指定IP开放以保证安全。
## 模型说明
@@ -613,7 +604,7 @@ API Key创建在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
</details>
<details>
<summary>Coding Plan (编程包月套餐)</summary>
<summary>Coding Plan</summary>
Coding Plan 是各厂商推出的编程包月套餐,所有厂商均可通过 OpenAI 兼容方式接入:

View File

@@ -5,22 +5,37 @@ services:
container_name: chatgpt-on-wechat
security_opt:
- seccomp:unconfined
ports:
- "9899:9899"
environment:
CHANNEL_TYPE: 'web'
OPEN_AI_API_KEY: 'YOUR API KEY'
MODEL: ''
PROXY: ''
SINGLE_CHAT_PREFIX: '["bot", "@bot"]'
SINGLE_CHAT_REPLY_PREFIX: '"[bot] "'
GROUP_CHAT_PREFIX: '["@bot"]'
GROUP_NAME_WHITE_LIST: '["ChatGPT测试群", "ChatGPT测试群2"]'
IMAGE_CREATE_PREFIX: '["画", "看", "找"]'
CONVERSATION_MAX_TOKENS: 1000
SPEECH_RECOGNITION: 'False'
CHARACTER_DESC: '你是基于大语言模型的AI智能助手旨在回答并解决人们的任何问题并且可以使用多种语言与人交流。'
EXPIRES_IN_SECONDS: 3600
USE_GLOBAL_PLUGIN_CONFIG: 'True'
MODEL: 'MiniMax-M2.5'
MINIMAX_API_KEY: ''
ZHIPU_AI_API_KEY: ''
ARK_API_KEY: ''
MOONSHOT_API_KEY: ''
DASHSCOPE_API_KEY: ''
CLAUDE_API_KEY: ''
CLAUDE_API_BASE: 'https://api.anthropic.com/v1'
OPEN_AI_API_KEY: ''
OPEN_AI_API_BASE: 'https://api.openai.com/v1'
GEMINI_API_KEY: ''
GEMINI_API_BASE: 'https://generativelanguage.googleapis.com'
VOICE_TO_TEXT: 'openai'
TEXT_TO_VOICE: 'openai'
VOICE_REPLY_VOICE: 'False'
SPEECH_RECOGNITION: 'True'
GROUP_SPEECH_RECOGNITION: 'False'
USE_LINKAI: 'False'
AGENT: 'True'
LINKAI_API_KEY: ''
LINKAI_APP_CODE: ''
FEISHU_APP_ID: ''
FEISHU_APP_SECRET: ''
DINGTALK_CLIENT_ID: ''
DINGTALK_CLIENT_SECRET: ''
WECOM_BOT_ID: ''
WECOM_BOT_SECRET: ''
AGENT: 'True'
AGENT_MAX_CONTEXT_TOKENS: 40000
AGENT_MAX_CONTEXT_TURNS: 20
AGENT_MAX_STEPS: 15

View File

@@ -56,9 +56,13 @@ python3 app.py
nohup python3 app.py & tail -f nohup.out
```
<Tip>
如果在服务器上部署,需要在防火墙或安全组中放行 `9899` 端口才能通过浏览器访问 Web 控制台建议仅对指定IP开放以保证安全。
</Tip>
## Docker 部署
使用 Docker 部署无需下载源码和安装依赖。Agent 模式下更推荐使用源码部署以获得更多系统访问能力。
使用 Docker 部署无需下载源码和安装依赖。Agent模式下更推荐使用源码部署以获得更多系统访问能力。
<Note>
需要安装 [Docker](https://docs.docker.com/engine/install/) 和 docker-compose。
@@ -84,6 +88,10 @@ sudo docker compose up -d
sudo docker logs -f chatgpt-on-wechat
```
<Tip>
如果在服务器上部署,需要在防火墙或安全组中放行 `9899` 端口才能通过浏览器访问 Web 控制台建议仅对指定IP开放以保证安全。
</Tip>
## 核心配置项
```json

View File

@@ -55,6 +55,12 @@ Web 控制台对话界面支持文件和图片上传,可直接发送文件给
- **图片识别**将图片识别Image Vision从 Skill 重构为内置 Tool新增独立的图片视觉提供方Vision Provider配置提升稳定性和可维护性 ([a50fafa](https://github.com/zhayujie/chatgpt-on-wechat/commit/a50fafa), [3b8b562](https://github.com/zhayujie/chatgpt-on-wechat/commit/3b8b562))
- **网页抓取**将网页抓取Web Fetch从 Skill 重构为内置 Tool支持远程文档文件PDF、Word、Excel、PPT的下载和解析 ([ccb9030](https://github.com/zhayujie/chatgpt-on-wechat/commit/ccb9030), [fa61744](https://github.com/zhayujie/chatgpt-on-wechat/commit/fa61744))
## 🐳 Docker 部署优化
- **配置模板对齐**`docker-compose.yml` 环境变量与 `config-template.json` 对齐,补充完整的模型 API Key 和 Agent 等配置项
- **Web 控制台端口映射**:新增 `9899` 端口映射Docker 部署后可通过浏览器访问 Web 控制台
- **配置热更新**:各模型 Bot 的 API Key 和 API Base 改为实时读取,通过 Web 控制台修改配置后无需重启即可生效
## ⚡ 性能优化
- **启动加速**:飞书通道采用懒加载方式导入依赖,避免 4-10 秒的启动延迟 ([924dc79](https://github.com/zhayujie/chatgpt-on-wechat/commit/924dc79))

View File

@@ -30,11 +30,20 @@ user_session = dict()
class ClaudeAPIBot(Bot, OpenAIImage):
def __init__(self):
super().__init__()
self.api_key = conf().get("claude_api_key")
self.api_base = conf().get("claude_api_base") or "https://api.anthropic.com/v1"
self.proxy = conf().get("proxy", None)
self.sessions = SessionManager(BaiduWenxinSession, model=conf().get("model") or "text-davinci-003")
@property
def api_key(self):
return conf().get("claude_api_key")
@property
def api_base(self):
return conf().get("claude_api_base") or "https://api.anthropic.com/v1"
@property
def proxy(self):
return conf().get("proxy", None)
def reply(self, query, context=None):
# acquire reply content
if context and context.type:

View File

@@ -35,10 +35,14 @@ class DashscopeBot(Bot):
super().__init__()
self.sessions = SessionManager(DashscopeSession, model=conf().get("model") or "qwen-plus")
self.model_name = conf().get("model") or "qwen-plus"
self.api_key = conf().get("dashscope_api_key")
if self.api_key:
os.environ["DASHSCOPE_API_KEY"] = self.api_key
self.client = dashscope.Generation
api_key = conf().get("dashscope_api_key")
if api_key:
os.environ["DASHSCOPE_API_KEY"] = api_key
@property
def api_key(self):
return conf().get("dashscope_api_key")
@staticmethod
def _is_multimodal_model(model_name: str) -> bool:

View File

@@ -24,13 +24,17 @@ class DoubaoBot(Bot):
"temperature": conf().get("temperature", 0.8),
"top_p": conf().get("top_p", 1.0),
}
self.api_key = conf().get("ark_api_key")
self.base_url = conf().get("ark_base_url", "https://ark.cn-beijing.volces.com/api/v3")
# Ensure base_url does not end with /chat/completions
if self.base_url.endswith("/chat/completions"):
self.base_url = self.base_url.rsplit("/chat/completions", 1)[0]
if self.base_url.endswith("/"):
self.base_url = self.base_url.rstrip("/")
@property
def api_key(self):
return conf().get("ark_api_key")
@property
def base_url(self):
url = conf().get("ark_base_url", "https://ark.cn-beijing.volces.com/api/v3")
if url.endswith("/chat/completions"):
url = url.rsplit("/chat/completions", 1)[0]
return url.rstrip("/")
def reply(self, query, context=None):
# acquire reply content

View File

@@ -28,21 +28,18 @@ class GoogleGeminiBot(Bot):
def __init__(self):
super().__init__()
self.api_key = conf().get("gemini_api_key")
# 复用chatGPT的token计算方式
self.sessions = SessionManager(ChatGPTSession, model=conf().get("model") or "gpt-3.5-turbo")
self.model = conf().get("model") or "gemini-pro"
if self.model == "gemini":
self.model = "gemini-pro"
# 支持自定义API base地址
self.api_base = conf().get("gemini_api_base", "").strip()
if self.api_base:
# 移除末尾的斜杠
self.api_base = self.api_base.rstrip('/')
logger.info(f"[Gemini] Using custom API base: {self.api_base}")
else:
self.api_base = "https://generativelanguage.googleapis.com"
@property
def api_key(self):
return conf().get("gemini_api_key")
@property
def api_base(self):
base = conf().get("gemini_api_base", "").strip()
if base:
return base.rstrip('/')
return "https://generativelanguage.googleapis.com"
def reply(self, query, context: Context = None) -> Reply:
session_id = None

View File

@@ -24,21 +24,19 @@ class MinimaxBot(Bot):
"temperature": conf().get("temperature", 0.3),
"top_p": conf().get("top_p", 0.95),
}
# Use unified key name: minimax_api_key
self.api_key = conf().get("minimax_api_key")
if not self.api_key:
# Fallback to old key name for backward compatibility
self.api_key = conf().get("Minimax_api_key")
if self.api_key:
logger.warning("[MINIMAX] 'Minimax_api_key' is deprecated, please use 'minimax_api_key' instead")
# REST API endpoint
# Use Chinese endpoint by default, users can override in config
# International users should set: "minimax_api_base": "https://api.minimax.io/v1"
self.api_base = conf().get("minimax_api_base", "https://api.minimaxi.com/v1")
self.sessions = SessionManager(MinimaxSession, model=const.MiniMax)
@property
def api_key(self):
key = conf().get("minimax_api_key")
if not key:
key = conf().get("Minimax_api_key")
return key
@property
def api_base(self):
return conf().get("minimax_api_base", "https://api.minimaxi.com/v1")
def reply(self, query, context: Context = None) -> Reply:
# acquire reply content
logger.info("[MINIMAX] query={}".format(query))

View File

@@ -26,8 +26,14 @@ class ModelScopeBot(Bot):
"temperature": conf().get("temperature", 0.3), # 如果设置,值域须为 [0, 1] 我们推荐 0.3,以达到较合适的效果。
"top_p": conf().get("top_p", 1.0), # 使用默认值
}
self.api_key = conf().get("modelscope_api_key")
self.base_url = conf().get("modelscope_base_url", "https://api-inference.modelscope.cn/v1/chat/completions")
@property
def api_key(self):
return conf().get("modelscope_api_key")
@property
def base_url(self):
return conf().get("modelscope_base_url", "https://api-inference.modelscope.cn/v1/chat/completions")
"""
需要获取ModelScope支持API-inference的模型名称列表请到魔搭社区官网模型中心查看 https://modelscope.cn/models?filter=inference_type&page=1。
或者使用命令 curl https://api-inference.modelscope.cn/v1/models 对模型列表和ID进行获取。查看commend/const.py文件也可以获取模型列表。

View File

@@ -26,13 +26,17 @@ class MoonshotBot(Bot):
"temperature": conf().get("temperature", 0.3),
"top_p": conf().get("top_p", 1.0),
}
self.api_key = conf().get("moonshot_api_key")
self.base_url = conf().get("moonshot_base_url", "https://api.moonshot.cn/v1")
# Ensure base_url does not end with /chat/completions (backward compat)
if self.base_url.endswith("/chat/completions"):
self.base_url = self.base_url.rsplit("/chat/completions", 1)[0]
if self.base_url.endswith("/"):
self.base_url = self.base_url.rstrip("/")
@property
def api_key(self):
return conf().get("moonshot_api_key")
@property
def base_url(self):
url = conf().get("moonshot_base_url", "https://api.moonshot.cn/v1")
if url.endswith("/chat/completions"):
url = url.rsplit("/chat/completions", 1)[0]
return url.rstrip("/")
def reply(self, query, context=None):
# acquire reply content