Compare commits

..

30 Commits

Author SHA1 Message Date
zhayujie
5958b69ec9 feat: release 2.0.4 2026-03-22 20:49:41 +08:00
zhayujie
7d4e2cb39a docs: update comments 2026-03-22 19:07:19 +08:00
zhayujie
a483ec0cea feat: optimize weixin channel qr code generate 2026-03-22 18:20:10 +08:00
zhayujie
c1421e0874 feat: support weixin channel in scripts 2026-03-22 16:29:12 +08:00
zhayujie
ce89869c3c feat: support weixin channel 2026-03-22 15:52:13 +08:00
zhayujie
b8b57e34ff fix: auto-repair messages 2026-03-21 14:20:22 +08:00
zhayujie
bc7f627253 fix(wecom_bot): compat with old websocket-client 2026-03-21 14:03:17 +08:00
zhayujie
652156e398 feat: make run.sh executable 2026-03-20 17:56:10 +08:00
zhayujie
9febb071c6 fix: run.sh get pid bug 2026-03-20 17:51:04 +08:00
zhayujie
7d0e1568ac fix: feishu msg and log encoding 2026-03-19 17:07:39 +08:00
zhayujie
b4e711f411 feat: add request header 2026-03-19 17:06:05 +08:00
zhayujie
1b5be1b981 fix: remove feishu_bot_name in run.sh 2026-03-19 14:55:12 +08:00
zhayujie
49d8707c58 refactor: simplify run.sh by extracting shared logic and eliminating duplication 2026-03-19 11:07:16 +08:00
zhayujie
9192f6f7f7 feat: add MiniMax-M2.7 and glm-5-turbo 2026-03-19 10:46:13 +08:00
zhayujie
05022e3745 fix: add log 2026-03-18 23:09:27 +08:00
zhayujie
5356e9ddeb docs: adjust docs order 2026-03-18 21:55:09 +08:00
zhayujie
52acf76e2c docs: update jp docs 2026-03-18 21:01:02 +08:00
zhayujie
40cdbd3b45 Merge pull request #2710 from eltociear/add-ja-doc
docs: add Japanese documents
2026-03-18 19:28:04 +08:00
Ikko Ashimine
5487c0befe docs: add Japanese documents 2026-03-18 19:13:39 +09:00
zhayujie
8bb16c48c0 docs: update install cmd 2026-03-18 16:11:35 +08:00
zhayujie
c6384363f9 feat: workspace volume in docker deploy 2026-03-18 16:03:03 +08:00
zhayujie
8993e8ad3e feat: release 2.0.3 2026-03-18 15:40:49 +08:00
zhayujie
289989d9f7 feat: release 2.0.3 2026-03-18 15:10:21 +08:00
zhayujie
dc2ae0e6f1 feat: support gpt-5.4-mini and gpt-5.4-nano 2026-03-18 14:55:29 +08:00
zhayujie
9c966c152d feat: enhance AGENT.md update prompts to encourage proactive evolution 2026-03-18 12:10:45 +08:00
zhayujie
4efae41048 feat: support coding plan 2026-03-18 11:59:22 +08:00
zhayujie
b8437032e9 fix: optimize image recognition prompts 2026-03-18 10:10:23 +08:00
zhayujie
2d339ca81b Merge branch 'master' of github.com:zhayujie/chatgpt-on-wechat 2026-03-17 23:03:05 +08:00
zhayujie
d53abc9696 docs: update README.md 2026-03-17 23:02:41 +08:00
zhayujie
446c886d38 Merge pull request #2706 from zhayujie/feat-web-files
feat: support files upload in web console and office parsing
2026-03-17 21:22:38 +08:00
129 changed files with 6009 additions and 772 deletions

160
README.md
View File

@@ -4,19 +4,19 @@
<a href="https://github.com/zhayujie/chatgpt-on-wechat/releases/latest"><img src="https://img.shields.io/github/v/release/zhayujie/chatgpt-on-wechat" alt="Latest release"></a>
<a href="https://github.com/zhayujie/chatgpt-on-wechat/blob/master/LICENSE"><img src="https://img.shields.io/github/license/zhayujie/chatgpt-on-wechat" alt="License: MIT"></a>
<a href="https://github.com/zhayujie/chatgpt-on-wechat"><img src="https://img.shields.io/github/stars/zhayujie/chatgpt-on-wechat?style=flat-square" alt="Stars"></a> <br/>
[中文] | [<a href="docs/en/README.md">English</a>]
[中文] | [<a href="docs/en/README.md">English</a>] | [<a href="docs/ja/README.md">日本語</a>]
</p>
**CowAgent** 是基于大模型的超级AI助理能够主动思考和任务规划、操作计算机和外部资源、创造和执行Skills、拥有长期记忆并不断成长。CowAgent 支持灵活切换多种模型,能处理文本、语音、图片、文件等多模态消息,可接入网页、飞书、钉钉、企微智能机器人、QQ、企微自建应用、微信公众号中使用7*24小时运行于你的个人电脑或服务器中。
**CowAgent** 是基于大模型的超级AI助理能够主动思考和任务规划、操作计算机和外部资源、创造和执行Skills、拥有长期记忆并不断成长比OpenClaw更轻量和便捷。CowAgent 支持灵活切换多种模型,能处理文本、语音、图片、文件等多模态消息,可接入微信、飞书、钉钉、企微智能机器人、QQ、企微自建应用、微信公众号、网页中使用7*24小时运行于你的个人电脑或服务器中。
<p align="center">
<a href="https://cowagent.ai/">🌐 官网</a> &nbsp;·&nbsp;
<a href="https://docs.cowagent.ai/">📖 文档中心</a> &nbsp;·&nbsp;
<a href="https://docs.cowagent.ai/guide/quick-start">🚀 快速开始</a>
<a href="https://docs.cowagent.ai/guide/quick-start">🚀 快速开始</a> &nbsp;·&nbsp;
<a href="https://link-ai.tech/cowagent/create">☁️ 在线体验</a>
</p>
# 简介
> 该项目既是一个可以开箱即用的超级AI助理也是一个支持高扩展的Agent框架可以通过为项目扩展大模型接口、接入渠道、内置工具、Skills系统来灵活实现各种定制需求。核心能力如下
@@ -26,8 +26,7 @@
-**技能系统:** 实现了Skills创建和运行的引擎内置多种技能并支持通过自然语言对话完成自定义Skills开发
-**多模态消息:** 支持对文本、图片、语音、文件等多类型消息进行解析、处理、生成、发送等操作
-**多模型接入:** 支持OpenAI, Claude, Gemini, DeepSeek, MiniMax、GLM、Qwen、Kimi、Doubao等国内外主流模型厂商
-**多端部署:** 支持运行在本地计算机或服务器,可集成到网页、飞书、钉钉、微信公众号、企业微信应用中使用
-**知识库:** 集成企业知识库能力让Agent成为专属数字员工基于[LinkAI](https://link-ai.tech)平台实现
-**多端部署:** 支持运行在本地计算机或服务器,可集成到微信、飞书、钉钉、企业微信、QQ、微信公众号、网页中使用
## 声明
@@ -37,9 +36,11 @@
## 演示
使用说明(Agent模式)[CowAgent介绍](https://docs.cowagent.ai/intro/features)
- 使用说明(Agent模式)[CowAgent介绍](https://docs.cowagent.ai/intro/features)
DEMO视频(对话模式)https://cdn.link-ai.tech/doc/cow_demo.mp4
- 免部署在线体验:[CowAgent](https://link-ai.tech/cowagent/create)
- DEMO视频(对话模式)https://cdn.link-ai.tech/doc/cow_demo.mp4
## 社区
@@ -51,9 +52,9 @@ DEMO视频(对话模式)https://cdn.link-ai.tech/doc/cow_demo.mp4
# 企业服务
<a href="https://link-ai.tech" target="_blank"><img width="720" src="https://cdn.link-ai.tech/image/link-ai-intro.jpg"></a>
<a href="https://link-ai.tech" target="_blank"><img width="650" src="https://cdn.link-ai.tech/image/link-ai-intro.jpg"></a>
> [LinkAI](https://link-ai.tech/) 是面向企业和开发者的一站式AI智能体平台聚合多模态大模型、知识库、Agent 插件、工作流等能力,支持一键接入主流平台并进行管理支持SaaS、私有化部署等多种模式。
> [LinkAI](https://link-ai.tech/) 是面向企业和个人的一站式AI智能体平台聚合多模态大模型、知识库、技能、工作流等能力支持一键接入主流平台并管理支持SaaS、私有化部署等多种模式,可免部署在线运行[CowAgent助理](https://link-ai.tech/cowagent/create)
>
> LinkAI 目前已在智能客服、私域运营、企业效率助手等场景积累了丰富的AI解决方案在消费、健康、文教、科技制造等各行业沉淀了大模型落地应用的最佳实践致力于帮助更多企业和开发者拥抱 AI 生产力。
@@ -65,16 +66,16 @@ DEMO视频(对话模式)https://cdn.link-ai.tech/doc/cow_demo.mp4
# 🏷 更新日志
>**2026.03.22** [2.0.4版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/2.0.4),新增个人微信通道(微信扫码即用)、新增 MiniMax-M2.7 和 GLM-5-Turbo 模型、run.sh 脚本重构、日文文档及多项修复。
>**2026.03.18** [2.0.3版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/2.0.3),新增企微智能机器人和 QQ 通道、支持Coding Plan、新增多个模型、Web端文件处理、记忆系统升级。
>**2026.02.27** [2.0.2版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/2.0.2)Web 控制台全面升级(流式对话、模型/技能/记忆/通道/定时任务/日志管理)、支持多通道同时运行、会话持久化存储、新增多个模型。
>**2026.02.13** [2.0.1版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/2.0.1),内置 Web Search 工具、智能上下文裁剪策略、运行时信息动态更新、Windows 兼容性适配,修复定时任务记忆丢失、飞书连接等多项问题。
>**2026.02.03** [2.0.0版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/2.0.0)正式升级为超级Agent助理支持多轮任务决策、具备长期记忆、实现多种系统工具、支持Skills框架新增多种模型并优化了接入渠道。
>**2025.05.23** [1.7.6版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.7.6) 优化web网页channel、新增 [AgentMesh](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/plugins/agent/README.md)多智能体插件、百度语音合成优化、企微应用`access_token`获取优化、支持`claude-4-sonnet``claude-4-opus`模型
>**2025.04.11** [1.7.5版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.7.5) 新增支持 [wechatferry](https://github.com/zhayujie/chatgpt-on-wechat/pull/2562) 协议、新增 deepseek 模型、新增支持腾讯云语音能力、新增支持 ModelScope 和 Gitee-AI API接口
更多更新历史请查看: [更新日志](https://docs.cowagent.ai/releases)
<br/>
@@ -86,7 +87,7 @@ DEMO视频(对话模式)https://cdn.link-ai.tech/doc/cow_demo.mp4
在终端执行以下命令:
```bash
bash <(curl -sS https://cdn.link-ai.tech/code/cow/run.sh)
bash <(curl -fsSL https://cdn.link-ai.tech/code/cow/run.sh)
```
脚本使用说明:[一键运行脚本](https://docs.cowagent.ai/guide/quick-start)
@@ -98,9 +99,9 @@ bash <(curl -sS 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.7、glm-5-turbo、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.环境安装
@@ -143,8 +144,8 @@ pip3 install -r requirements-optional.txt
```bash
# config.json 文件内容示例
{
"channel_type": "web", # 接入渠道类型默认为web支持修改为:feishu,dingtalk,wecom_bot,qq,wechatcom_app,wechatmp_service,wechatmp,terminal
"model": "MiniMax-M2.5", # 模型名称
"channel_type": "weixin", # 接入渠道类型默认为weixin, 支持修改为 feishu,dingtalk,wecom_bot,qq,wechatcom_app,wechatmp_service,wechatmp,terminal
"model": "MiniMax-M2.7", # 模型名称
"minimax_api_key": "", # MiniMax API Key
"zhipu_ai_api_key": "", # 智谱GLM API Key
"moonshot_api_key": "", # Kimi/Moonshot API Key
@@ -161,7 +162,7 @@ pip3 install -r requirements-optional.txt
"speech_recognition": false, # 是否开启语音识别
"group_speech_recognition": false, # 是否开启群组语音识别
"voice_reply_voice": false, # 是否使用语音回复语音
"use_linkai": false, # 是否使用LinkAI接口默认关闭设置为true后可对接LinkAI平台接口
"use_linkai": false, # 是否使用LinkAI接口默认关闭设置为true后可对接LinkAI平台模型
"agent": true, # 是否启用Agent模式启用后拥有多轮工具决策、长期记忆、Skills能力等
"agent_workspace": "~/cow", # Agent的工作空间路径用于存储memory、skills、系统设定等
"agent_max_context_tokens": 40000, # Agent模式下最大上下文tokens超出将自动丢弃最早的上下文
@@ -183,7 +184,7 @@ pip3 install -r requirements-optional.txt
<details>
<summary>2. 其他配置</summary>
+ `model`: 模型名称Agent模式下推荐使用 `MiniMax-M2.5``glm-5``kimi-k2.5``qwen3.5-plus``claude-sonnet-4-6``gemini-3.1-pro-preview`,全部模型名称参考[common/const.py](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/common/const.py)文件
+ `model`: 模型名称Agent模式下推荐使用 `MiniMax-M2.7``glm-5-turbo``kimi-k2.5``qwen3.5-plus``claude-sonnet-4-6``gemini-3.1-pro-preview`,全部模型名称参考[common/const.py](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/common/const.py)文件
+ `character_desc`普通对话模式下的机器人系统提示词。在Agent模式下该配置不生效由工作空间中的文件内容构成。
+ `subscribe_msg`订阅消息公众号和企业微信channel中请填写当被订阅时会自动回复 可使用特殊占位符。目前支持的占位符有{trigger_prefix}在程序中它会自动替换成bot的触发词。
</details>
@@ -191,9 +192,8 @@ pip3 install -r requirements-optional.txt
<details>
<summary>3. LinkAI配置</summary>
+ `use_linkai`: 是否使用LinkAI接口默认关闭设置为true后可对接LinkAI平台使用知识库、工作流、插件等能, 参考[接口文档](https://docs.link-ai.tech/platform/api/chat)
+ `use_linkai`: 是否使用LinkAI接口默认关闭设置为true后可对接LinkAI平台使用模型、知识库、工作流、插件等能, 参考[接口文档](https://docs.link-ai.tech/platform/api/chat)
+ `linkai_api_key`: LinkAI Api Key可在 [控制台](https://link-ai.tech/console/interface) 创建
+ `linkai_app_code`: LinkAI 应用或工作流的code选填普通对话模式中使用。
</details>
注:全部配置项说明可在 [`config.py`](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/config.py) 文件中查看。
@@ -223,8 +223,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部署
@@ -235,7 +236,7 @@ nohup python3 app.py & tail -f nohup.out
**(1) 下载 docker-compose.yml 文件**
```bash
wget https://cdn.link-ai.tech/code/cow/docker-compose.yml
curl -O https://cdn.link-ai.tech/code/cow/docker-compose.yml
```
下载完成后打开 `docker-compose.yml` 填写所需配置,例如 `CHANNEL_TYPE``OPEN_AI_API_KEY` 和等配置。
@@ -254,17 +255,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开放以保证安全。
## 模型说明
@@ -282,13 +273,13 @@ volumes:
"model": "gpt-5.4",
"open_ai_api_key": "YOUR_API_KEY",
"open_ai_api_base": "https://api.openai.com/v1",
"bot_type": "chatGPT"
"bot_type": "openai"
}
```
- `model`: 与OpenAI接口的 [model参数](https://platform.openai.com/docs/models) 一致,支持包括 gpt-5.4、o系列、gpt-4.1等模型Agent模式推荐使用 `gpt-5.4`
- `model`: 与OpenAI接口的 [model参数](https://platform.openai.com/docs/models) 一致,支持包括 gpt-5.4、gpt-5.4-mini、gpt-5.4-nano、o系列、gpt-4.1等模型Agent模式推荐使用 `gpt-5.4``gpt-5.4-mini`
- `open_ai_api_base`: 如果需要接入第三方代理接口,可通过修改该参数进行接入
- `bot_type`: 使用OpenAI相关模型时无需填写。当使用第三方代理接口接入Claude等非OpenAI官方模型时该参数设为 `chatGPT`
- `bot_type`: 使用OpenAI相关模型时无需填写。当使用第三方代理接口接入Claude等非OpenAI官方模型时该参数设为 `openai`
</details>
<details>
@@ -300,16 +291,15 @@ volumes:
```json
{
"model": "gpt-5.4-mini",
"use_linkai": true,
"linkai_api_key": "YOUR API KEY",
"linkai_app_code": "YOUR APP CODE"
"linkai_api_key": "YOUR API KEY"
}
```
+ `use_linkai`: 是否使用LinkAI接口默认关闭设置为true后可对接LinkAI平台的智能体,使用知识库、工作流、数据库、MCP插件等丰富的Agent能
+ `use_linkai`: 是否使用LinkAI接口默认关闭设置为true后可对接LinkAI平台的模型,并使用知识库、工作流、数据库、插件等丰富的Agent
+ `linkai_api_key`: LinkAI平台的API Key可在 [控制台](https://link-ai.tech/console/interface) 中创建
+ `linkai_app_code`: LinkAI智能体 (应用或工作流) 的code选填普通对话模式可用。智能体创建可参考 [说明文档](https://docs.link-ai.tech/platform/quick-start)
+ `model`: model字段填写空则直接使用智能体的模型可在平台中灵活切换[模型列表](https://link-ai.tech/console/models)中的全部模型均可使用
+ `model`: [模型列表](https://link-ai.tech/console/models)中的全部模型均可使用
</details>
<details>
@@ -319,24 +309,24 @@ volumes:
```json
{
"model": "MiniMax-M2.5",
"model": "MiniMax-M2.7",
"minimax_api_key": ""
}
```
- `model`: 可填写 `MiniMax-M2.5、MiniMax-M2.1、MiniMax-M2.1-lightning、MiniMax-M2、abab6.5-chat`
- `model`: 可填写 `MiniMax-M2.7、MiniMax-M2.5、MiniMax-M2.1、MiniMax-M2.1-lightning、MiniMax-M2、abab6.5-chat`
- `minimax_api_key`MiniMax平台的API-KEY在 [控制台](https://platform.minimaxi.com/user-center/basic-information/interface-key) 创建
方式二OpenAI兼容方式接入配置如下
```json
{
"bot_type": "chatGPT",
"model": "MiniMax-M2.5",
"bot_type": "openai",
"model": "MiniMax-M2.7",
"open_ai_api_base": "https://api.minimaxi.com/v1",
"open_ai_api_key": ""
}
```
- `bot_type`: OpenAI兼容方式
- `model`: 可填 `MiniMax-M2.5、MiniMax-M2.1、MiniMax-M2.1-lightning、MiniMax-M2`,参考[API文档](https://platform.minimaxi.com/document/%E5%AF%B9%E8%AF%9D?key=66701d281d57f38758d581d0#QklxsNSbaf6kM4j6wjO5eEek)
- `model`: 可填 `MiniMax-M2.7、MiniMax-M2.5、MiniMax-M2.1、MiniMax-M2.1-lightning、MiniMax-M2`,参考[API文档](https://platform.minimaxi.com/document/%E5%AF%B9%E8%AF%9D?key=66701d281d57f38758d581d0#QklxsNSbaf6kM4j6wjO5eEek)
- `open_ai_api_base`: MiniMax平台API的 BASE URL
- `open_ai_api_key`: MiniMax平台的API-KEY
</details>
@@ -348,24 +338,24 @@ volumes:
```json
{
"model": "glm-5",
"model": "glm-5-turbo",
"zhipu_ai_api_key": ""
}
```
- `model`: 可填 `glm-5、glm-4.7、glm-4-plus、glm-4-flash、glm-4-air、glm-4-airx、glm-4-long` 等, 参考 [glm系列模型编码](https://bigmodel.cn/dev/api/normal-model/glm-4)
- `model`: 可填 `glm-5-turbo、glm-5、glm-4.7、glm-4-plus、glm-4-flash、glm-4-air、glm-4-airx、glm-4-long` 等, 参考 [glm系列模型编码](https://bigmodel.cn/dev/api/normal-model/glm-4)
- `zhipu_ai_api_key`: 智谱AI平台的 API KEY在 [控制台](https://www.bigmodel.cn/usercenter/proj-mgmt/apikeys) 创建
方式二OpenAI兼容方式接入配置如下
```json
{
"bot_type": "chatGPT",
"model": "glm-5",
"bot_type": "openai",
"model": "glm-5-turbo",
"open_ai_api_base": "https://open.bigmodel.cn/api/paas/v4",
"open_ai_api_key": ""
}
```
- `bot_type`: OpenAI兼容方式
- `model`: 可填 `glm-5、glm-4.7、glm-4-plus、glm-4-flash、glm-4-air、glm-4-airx、glm-4-long`
- `model`: 可填 `glm-5-turbo、glm-5、glm-4.7、glm-4-plus、glm-4-flash、glm-4-air、glm-4-airx、glm-4-long`
- `open_ai_api_base`: 智谱AI平台的 BASE URL
- `open_ai_api_key`: 智谱AI平台的 API KEY
</details>
@@ -387,7 +377,7 @@ volumes:
方式二OpenAI兼容方式接入配置如下
```json
{
"bot_type": "chatGPT",
"bot_type": "openai",
"model": "qwen3.5-plus",
"open_ai_api_base": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"open_ai_api_key": "sk-qVxxxxG"
@@ -416,7 +406,7 @@ volumes:
方式二OpenAI兼容方式接入配置如下
```json
{
"bot_type": "chatGPT",
"bot_type": "openai",
"model": "kimi-k2.5",
"open_ai_api_base": "https://api.moonshot.cn/v1",
"open_ai_api_key": ""
@@ -486,8 +476,8 @@ API Key创建在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
{
"model": "deepseek-chat",
"open_ai_api_key": "sk-xxxxxxxxxxx",
"open_ai_api_base": "https://api.deepseek.com/v1",
"bot_type": "chatGPT"
"open_ai_api_base": "https://api.deepseek.com/v1",
"bot_type": "openai"
}
```
@@ -542,7 +532,7 @@ API Key创建在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
方式二OpenAI兼容方式接入配置如下
```json
{
"bot_type": "chatGPT",
"bot_type": "openai",
"model": "ERNIE-4.0-Turbo-8K",
"open_ai_api_base": "https://qianfan.baidubce.com/v2",
"open_ai_api_key": "bce-v3/ALTxxxxxxd2b"
@@ -578,7 +568,7 @@ API Key创建在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
方式二OpenAI兼容方式接入配置如下
```json
{
"bot_type": "chatGPT",
"bot_type": "openai",
"model": "4.0Ultra",
"open_ai_api_base": "https://spark-api-open.xf-yun.com/v1",
"open_ai_api_key": ""
@@ -610,6 +600,23 @@ API Key创建在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
- `text_to_image`: 图像生成模型,参考[模型列表](https://www.modelscope.cn/models?filter=inference_type&page=1)
</details>
<details>
<summary>Coding Plan</summary>
Coding Plan 是各厂商推出的编程包月套餐,所有厂商均可通过 OpenAI 兼容方式接入:
```json
{
"bot_type": "openai",
"model": "模型名称",
"open_ai_api_base": "厂商 Coding Plan API Base",
"open_ai_api_key": "YOUR_API_KEY"
}
```
目前支持阿里云、MiniMax、智谱GLM、Kimi、火山引擎等厂商各厂商详细配置请参考 [Coding Plan 文档](https://docs.cowagent.ai/models/coding-plan)。
</details>
## 通道说明
@@ -618,7 +625,24 @@ API Key创建在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
支持同时可接入多个通道,配置时可通过逗号进行分割,例如 `"channel_type": "feishu,dingtalk"`
<details>
<summary>1. Web</summary>
<summary>1. Weixin - 微信</summary>
接入个人微信,扫码登录即可使用,支持文本、图片、语音、文件等消息收发。
```json
{
"channel_type": "weixin"
}
```
启动后终端会显示二维码,使用微信扫码授权即可,也可以在 Web 控制台的「通道」页面中扫码接入。登录凭证会自动保存至 `~/.weixin_cow_credentials.json`,下次启动无需重新扫码,如需重新登录删除该文件后重启即可。
详细步骤和参数说明参考 [微信接入](https://docs.cowagent.ai/channels/weixin)
</details>
<details>
<summary>2. Web</summary>
项目启动后会默认运行Web控制台配置如下
@@ -635,7 +659,7 @@ API Key创建在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
</details>
<details>
<summary>2. Feishu - 飞书</summary>
<summary>3. Feishu - 飞书</summary>
飞书支持两种事件接收模式WebSocket 长连接(推荐)和 Webhook。
@@ -671,7 +695,7 @@ API Key创建在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
</details>
<details>
<summary>3. DingTalk - 钉钉</summary>
<summary>4. DingTalk - 钉钉</summary>
钉钉需要在开放平台创建智能机器人应用,将以下配置填入 `config.json`
@@ -686,7 +710,7 @@ API Key创建在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
</details>
<details>
<summary>4. WeCom Bot - 企微智能机器人</summary>
<summary>5. WeCom Bot - 企微智能机器人</summary>
企微智能机器人使用 WebSocket 长连接模式,无需公网 IP 和域名,配置简单:
@@ -702,7 +726,7 @@ API Key创建在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
</details>
<details>
<summary>5. QQ - QQ 机器人</summary>
<summary>6. QQ - QQ 机器人</summary>
QQ 机器人使用 WebSocket 长连接模式,无需公网 IP 和域名,支持 QQ 单聊、群聊和频道消息:
@@ -718,7 +742,7 @@ QQ 机器人使用 WebSocket 长连接模式,无需公网 IP 和域名,支
</details>
<details>
<summary>6. WeCom App - 企业微信应用</summary>
<summary>7. WeCom App - 企业微信应用</summary>
企业微信自建应用接入需在后台创建应用并启用消息回调,配置示例:
@@ -738,7 +762,7 @@ QQ 机器人使用 WebSocket 长连接模式,无需公网 IP 和域名,支
</details>
<details>
<summary>7. WeChat MP - 微信公众号</summary>
<summary>8. WeChat MP - 微信公众号</summary>
本项目支持订阅号和服务号两种公众号,通过服务号(`wechatmp_service`)体验更佳。
@@ -773,7 +797,7 @@ QQ 机器人使用 WebSocket 长连接模式,无需公网 IP 和域名,支
</details>
<details>
<summary>8. Terminal - 终端</summary>
<summary>9. Terminal - 终端</summary>
修改 `config.json` 中的 `channel_type` 字段:

View File

@@ -44,6 +44,11 @@ class ChatService:
if agent is None:
raise RuntimeError("Failed to initialise agent for the session")
# Pass context metadata to model for downstream API requests
if hasattr(agent, 'model'):
agent.model.channel_type = channel_type or ""
agent.model.session_id = session_id or ""
# State shared between the event callback and this method
state = _StreamState()

View File

@@ -32,18 +32,21 @@ class EmbeddingProvider(ABC):
class OpenAIEmbeddingProvider(EmbeddingProvider):
"""OpenAI embedding provider using REST API"""
def __init__(self, model: str = "text-embedding-3-small", api_key: Optional[str] = None, api_base: Optional[str] = None):
def __init__(self, model: str = "text-embedding-3-small", api_key: Optional[str] = None,
api_base: Optional[str] = None, extra_headers: Optional[dict] = None):
"""
Initialize OpenAI embedding provider
Args:
model: Model name (text-embedding-3-small or text-embedding-3-large)
api_key: OpenAI API key
api_base: Optional API base URL
extra_headers: Optional extra headers to include in API requests
"""
self.model = model
self.api_key = api_key
self.api_base = api_base or "https://api.openai.com/v1"
self.extra_headers = extra_headers or {}
# Validate API key
if not self.api_key or self.api_key in ["", "YOUR API KEY", "YOUR_API_KEY"]:
@@ -59,7 +62,8 @@ class OpenAIEmbeddingProvider(EmbeddingProvider):
url = f"{self.api_base}/embeddings"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
"Authorization": f"Bearer {self.api_key}",
**self.extra_headers,
}
data = {
"input": input_data,
@@ -134,7 +138,8 @@ def create_embedding_provider(
provider: str = "openai",
model: Optional[str] = None,
api_key: Optional[str] = None,
api_base: Optional[str] = None
api_base: Optional[str] = None,
extra_headers: Optional[dict] = None
) -> EmbeddingProvider:
"""
Factory function to create embedding provider
@@ -147,10 +152,11 @@ def create_embedding_provider(
model: Model name (default: text-embedding-3-small)
api_key: API key (required)
api_base: API base URL
extra_headers: Optional extra headers to include in API requests
Returns:
EmbeddingProvider instance
Raises:
ValueError: If provider is unsupported or api_key is missing
"""
@@ -158,4 +164,4 @@ def create_embedding_provider(
raise ValueError(f"Unsupported embedding provider: {provider}. Use 'openai' or 'linkai'.")
model = model or "text-embedding-3-small"
return OpenAIEmbeddingProvider(model=model, api_key=api_key, api_base=api_base)
return OpenAIEmbeddingProvider(model=model, api_key=api_key, api_base=api_base, extra_headers=extra_headers)

View File

@@ -76,11 +76,15 @@ class MemoryManager:
linkai_key = os.environ.get('LINKAI_API_KEY')
linkai_base = os.environ.get('LINKAI_API_BASE', 'https://api.link-ai.tech')
if linkai_key:
from common.utils import get_cloud_headers
cloud_headers = get_cloud_headers(linkai_key)
cloud_headers.pop("Authorization", None)
self.embedding_provider = create_embedding_provider(
provider="linkai",
model=self.config.embedding_model,
api_key=linkai_key,
api_base=f"{linkai_base}/v1"
api_base=f"{linkai_base}/v1",
extra_headers=cloud_headers,
)
except Exception as e:
from common.log import logger

View File

@@ -376,7 +376,7 @@ def _build_workspace_section(workspace_dir: str, language: str) -> List[str]:
"",
"以下文件在会话启动时**已经自动加载**到系统提示词的「项目上下文」section 中,你**无需再用 read 工具读取它们**",
"",
"- ✅ `AGENT.md`: 已加载 - 你的人格和灵魂设定。当用户修改你的名字、性格或交流风格时,用 `edit` 更新此文件",
"- ✅ `AGENT.md`: 已加载 - 你的人格和灵魂设定。当你的名字、性格或交流风格发生变化时,主动用 `edit` 更新此文件",
"- ✅ `USER.md`: 已加载 - 用户的身份信息。当用户修改称呼、姓名等身份信息时,用 `edit` 更新此文件",
"- ✅ `RULE.md`: 已加载 - 工作空间使用指南和规则",
"",
@@ -423,7 +423,8 @@ def _build_context_files_section(context_files: List[ContextFile], language: str
]
if has_agent:
lines.append("如果存在 `AGENT.md`,请体现其中定义的人格语气避免僵硬、模板化的回复;遵循其指导,除非有更高优先级的指令覆盖它")
lines.append("**`AGENT.md` 是你的灵魂文件**:严格体现其中定义的人格语气和设定,避免僵硬、模板化的回复。")
lines.append("当用户通过对话透露了对你性格、风格、职责、能力边界的新期望,你应该主动用 `edit` 更新 AGENT.md 以反映这些演变。")
lines.append("")
# 添加每个文件的内容

View File

@@ -609,14 +609,14 @@ class AgentStreamExecutor:
"arguments": ""
}
if "id" in tc_delta:
if tc_delta.get("id"):
tool_calls_buffer[index]["id"] = tc_delta["id"]
if "function" in tc_delta:
func = tc_delta["function"]
if "name" in func:
if func.get("name"):
tool_calls_buffer[index]["name"] = func["name"]
if "arguments" in func:
if func.get("arguments"):
tool_calls_buffer[index]["arguments"] += func["arguments"]
# Preserve _gemini_raw_parts for Gemini thoughtSignature round-trip
@@ -720,9 +720,9 @@ class AgentStreamExecutor:
)
else:
if retry_count >= max_retries:
logger.error(f"❌ LLM API error after {max_retries} retries: {e}")
logger.error(f"❌ LLM API error after {max_retries} retries: {e}", exc_info=True)
else:
logger.error(f"❌ LLM call error (non-retryable): {e}")
logger.error(f"❌ LLM call error (non-retryable): {e}", exc_info=True)
raise
# Parse tool calls

View File

@@ -18,6 +18,107 @@ from typing import Dict, List, Set
from common.log import logger
_SYNTH_TOOL_ERR = (
"Error: Missing tool_result adjacent to tool_use (session repair). "
"The conversation history was inconsistent; continue from here."
)
def _repair_tool_use_adjacency(messages: List[Dict]) -> int:
"""
Anthropic requires: after assistant content with tool_use, the next message
must be user content listing tool_result for every tool_use id (same user msg).
Valid histories satisfy this at every such assistant; the loop only mutates
when that condition fails (broken persistence, bad trims, etc.).
"""
def _synth_block(tid: str) -> Dict:
return {
"type": "tool_result",
"tool_use_id": tid,
"content": _SYNTH_TOOL_ERR,
"is_error": True,
}
repairs = 0
i = 0
while i < len(messages):
msg = messages[i]
if msg.get("role") != "assistant":
i += 1
continue
content = msg.get("content", [])
if not isinstance(content, list):
i += 1
continue
required = [
b.get("id")
for b in content
if isinstance(b, dict) and b.get("type") == "tool_use" and b.get("id")
]
if not required:
i += 1
continue
req_set = set(required)
if i + 1 >= len(messages):
messages.append({
"role": "user",
"content": [_synth_block(tid) for tid in required],
})
logger.warning(
"⚠️ Appended synthetic tool_result after trailing assistant tool_use"
)
repairs += 1
break
nxt = messages[i + 1]
if nxt.get("role") != "user":
messages.insert(
i + 1,
{"role": "user", "content": [_synth_block(tid) for tid in required]},
)
logger.warning(
"⚠️ Inserted synthetic tool_result user after tool_use "
f"(next role={nxt.get('role')!r})"
)
repairs += 1
i += 2
continue
nc = nxt.get("content", [])
if not isinstance(nc, list):
messages.insert(
i + 1,
{"role": "user", "content": [_synth_block(tid) for tid in required]},
)
repairs += 1
i += 2
continue
present = {
b.get("tool_use_id")
for b in nc
if isinstance(b, dict) and b.get("type") == "tool_result" and b.get("tool_use_id")
}
if req_set <= present:
i += 1
continue
missing = [tid for tid in required if tid not in present]
nxt["content"] = [_synth_block(tid) for tid in missing] + nc
logger.warning(
"⚠️ Prepended synthetic tool_result for Anthropic adjacency "
f"(missing_ids={missing})"
)
repairs += len(missing)
i += 1
return repairs
# ------------------------------------------------------------------ #
# Claude-format sanitizer (used by agent_stream)
@@ -28,33 +129,21 @@ def sanitize_claude_messages(messages: List[Dict]) -> int:
Validate and fix a Claude-format message list **in-place**.
Fixes handled:
- Trailing assistant message with tool_use but no following tool_result
- Anthropic adjacency: assistant tool_use must be immediately followed by
user message(s) containing matching tool_result blocks
- Leading orphaned tool_result user messages
- Mid-list tool_result blocks whose tool_use_id has no matching
tool_use in any preceding assistant message
Returns the number of messages / blocks removed.
Returns: number of removals plus adjacency repair operations (inserts/prepends).
"""
if not messages:
return 0
removed = 0
# 1. Remove trailing incomplete tool_use assistant messages
while messages:
last = messages[-1]
if last.get("role") != "assistant":
break
content = last.get("content", [])
if isinstance(content, list) and any(
isinstance(b, dict) and b.get("type") == "tool_use"
for b in content
):
logger.warning("⚠️ Removing trailing incomplete tool_use assistant message")
messages.pop()
removed += 1
else:
break
# 1. Adjacency repair (Anthropic: tool_result must be in the next user message)
adj_repairs = _repair_tool_use_adjacency(messages)
# 2. Remove leading orphaned tool_result user messages
while messages:
@@ -136,9 +225,15 @@ def sanitize_claude_messages(messages: List[Dict]) -> int:
if pass_removed == 0:
break
# 4. Removals above can break adjacency; re-run repair only if something was removed.
if removed:
adj_repairs += _repair_tool_use_adjacency(messages)
if removed:
logger.info(f"🔧 Message validation: removed {removed} broken message(s)")
return removed
if adj_repairs:
logger.info(f"🔧 Message validation: adjacency repairs={adj_repairs}")
return removed + adj_repairs
# ------------------------------------------------------------------ #

View File

@@ -35,7 +35,7 @@ class Vision(BaseTool):
name: str = "vision"
description: str = (
"Analyze an image (local file or URL) using Vision API. "
"Analyze a local image or image URL (jpg/jpeg/png) using Vision API. "
"Can describe content, extract text, identify objects, colors, etc. "
"Requires OPENAI_API_KEY or LINKAI_API_KEY."
)
@@ -82,7 +82,7 @@ class Vision(BaseTool):
if not question:
return ToolResult.fail("Error: 'question' parameter is required")
api_key, api_base = self._resolve_provider()
api_key, api_base, extra_headers = self._resolve_provider()
if not api_key:
return ToolResult.fail(
"Error: No API key configured for Vision.\n"
@@ -98,7 +98,7 @@ class Vision(BaseTool):
return ToolResult.fail(f"Error: {e}")
try:
return self._call_api(api_key, api_base, model, question, image_content)
return self._call_api(api_key, api_base, model, question, image_content, extra_headers)
except requests.Timeout:
return ToolResult.fail(f"Error: Vision API request timed out after {DEFAULT_TIMEOUT}s")
except requests.ConnectionError:
@@ -107,22 +107,26 @@ class Vision(BaseTool):
logger.error(f"[Vision] Unexpected error: {e}", exc_info=True)
return ToolResult.fail(f"Error: Vision API call failed - {e}")
def _resolve_provider(self) -> Tuple[Optional[str], str]:
"""Resolve API key and base URL. Priority: conf() > env vars."""
def _resolve_provider(self) -> Tuple[Optional[str], str, dict]:
"""Resolve API key, base URL and extra headers. Priority: conf() > env vars."""
api_key = conf().get("open_ai_api_key") or os.environ.get("OPENAI_API_KEY")
if api_key:
api_base = (conf().get("open_ai_api_base") or os.environ.get("OPENAI_API_BASE", "")).rstrip("/") \
or "https://api.openai.com/v1"
return api_key, self._ensure_v1(api_base)
return api_key, self._ensure_v1(api_base), {}
api_key = conf().get("linkai_api_key") or os.environ.get("LINKAI_API_KEY")
if api_key:
api_base = (conf().get("linkai_api_base") or os.environ.get("LINKAI_API_BASE", "")).rstrip("/") \
or "https://api.link-ai.tech"
logger.debug("[Vision] Using LinkAI API (OPENAI_API_KEY not set)")
return api_key, self._ensure_v1(api_base)
from common.utils import get_cloud_headers
extra = get_cloud_headers(api_key)
extra.pop("Authorization", None)
extra.pop("Content-Type", None)
return api_key, self._ensure_v1(api_base), extra
return None, ""
return None, "", {}
@staticmethod
def _ensure_v1(api_base: str) -> str:
@@ -197,7 +201,7 @@ class Vision(BaseTool):
return path
def _call_api(self, api_key: str, api_base: str, model: str,
question: str, image_content: dict) -> ToolResult:
question: str, image_content: dict, extra_headers: dict = None) -> ToolResult:
payload = {
"model": model,
"messages": [
@@ -215,6 +219,7 @@ class Vision(BaseTool):
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
**(extra_headers or {}),
}
resp = requests.post(

View File

@@ -225,10 +225,8 @@ class WebSearch(BaseTool):
api_base = conf().get("linkai_api_base", "https://api.link-ai.tech")
url = f"{api_base.rstrip('/')}/v1/plugin/execute"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
}
from common.utils import get_cloud_headers
headers = get_cloud_headers(api_key)
payload = {
"code": "web-search",

10
app.py
View File

@@ -78,7 +78,13 @@ class ChannelManager:
if first_start:
PluginManager().load_plugins()
if conf().get("use_linkai"):
# Cloud client is optional. It is only started when
# use_linkai=True AND cloud_deployment_id is set.
# By default neither is configured, so the app runs
# entirely locally without any remote connection.
if conf().get("use_linkai") and (
os.environ.get("CLOUD_DEPLOYMENT_ID") or conf().get("cloud_deployment_id")
):
try:
from common import cloud_client
threading.Thread(
@@ -229,6 +235,8 @@ def _clear_singleton_cache(channel_name: str):
const.DINGTALK: "channel.dingtalk.dingtalk_channel.DingTalkChanel",
const.WECOM_BOT: "channel.wecom_bot.wecom_bot_channel.WecomBotChannel",
const.QQ: "channel.qq.qq_channel.QQChannel",
const.WEIXIN: "channel.weixin.weixin_channel.WeixinChannel",
"wx": "channel.weixin.weixin_channel.WeixinChannel",
}
module_path = cls_map.get(channel_name)
if not module_path:

View File

@@ -106,7 +106,7 @@ class AgentLLMModel(LLMModel):
return configured_bot_type
if not model_name or not isinstance(model_name, str):
return const.CHATGPT
return const.OPENAI
if model_name in self._MODEL_BOT_TYPE_MAP:
return self._MODEL_BOT_TYPE_MAP[model_name]
if model_name.lower().startswith("minimax") or model_name in ["abab6.5-chat"]:
@@ -116,11 +116,11 @@ class AgentLLMModel(LLMModel):
if model_name in [const.MOONSHOT, "moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k"]:
return const.MOONSHOT
if model_name in [const.DEEPSEEK_CHAT, const.DEEPSEEK_REASONER]:
return const.CHATGPT
return const.OPENAI
for prefix, btype in self._MODEL_PREFIX_MAP:
if model_name.startswith(prefix):
return btype
return const.CHATGPT
return const.OPENAI
@property
def bot(self):
@@ -152,12 +152,20 @@ class AgentLLMModel(LLMModel):
# Only pass max_tokens if it's explicitly set
if request.max_tokens is not None:
kwargs['max_tokens'] = request.max_tokens
# Extract system prompt if present
system_prompt = getattr(request, 'system', None)
if system_prompt:
kwargs['system'] = system_prompt
# Pass context metadata to bot
channel_type = getattr(self, 'channel_type', None)
if channel_type:
kwargs['channel_type'] = channel_type
session_id = getattr(self, 'session_id', None)
if session_id:
kwargs['session_id'] = session_id
response = self.bot.call_with_tools(**kwargs)
return self._format_response(response)
else:
@@ -195,10 +203,13 @@ class AgentLLMModel(LLMModel):
if system_prompt:
kwargs['system'] = system_prompt
# Pass channel_type for linkai tracking
# Pass context metadata to bot
channel_type = getattr(self, 'channel_type', None)
if channel_type:
kwargs['channel_type'] = channel_type
session_id = getattr(self, 'session_id', None)
if session_id:
kwargs['session_id'] = session_id
stream = self.bot.call_with_tools(**kwargs)
@@ -375,9 +386,10 @@ class AgentBridge:
logger.warning(f"[AgentBridge] Failed to attach context to scheduler: {e}")
break
# Pass channel_type to model so linkai requests carry it
# Pass context metadata to model for downstream API requests
if context and hasattr(agent, 'model'):
agent.model.channel_type = context.get("channel_type", "")
agent.model.session_id = session_id or ""
# Store session_id on agent so executor can clear DB on fatal errors
agent._current_session_id = session_id

View File

@@ -13,7 +13,7 @@ from voice.factory import create_voice
class Bridge(object):
def __init__(self):
self.btype = {
"chat": const.CHATGPT,
"chat": const.OPENAI,
"voice_to_text": conf().get("voice_to_text", "openai"),
"text_to_voice": conf().get("text_to_voice", "google"),
"translate": conf().get("translate", "baidu"),

View File

@@ -39,6 +39,10 @@ def create_channel(channel_type) -> Channel:
elif channel_type == const.QQ:
from channel.qq.qq_channel import QQChannel
ch = QQChannel()
elif channel_type in (const.WEIXIN, "wx"):
from channel.weixin.weixin_channel import WeixinChannel
ch = WeixinChannel()
channel_type = const.WEIXIN
else:
raise RuntimeError
ch.channel_type = channel_type

View File

@@ -454,7 +454,7 @@ class FeiShuChanel(ChatChannel):
can_reply = is_group and msg and hasattr(msg, 'msg_id') and msg.msg_id
# Build content JSON
content_json = json.dumps(reply_content) if content_key is None else json.dumps({content_key: reply_content})
content_json = json.dumps(reply_content, ensure_ascii=False) if content_key is None else json.dumps({content_key: reply_content}, ensure_ascii=False)
logger.debug(f"[FeiShu] Sending message: msg_type={msg_type}, content={content_json[:200]}")
if can_reply:

View File

@@ -23,6 +23,7 @@ from channel.qq.qq_message import QQMessage
from common.expired_dict import ExpiredDict
from common.log import logger
from common.singleton import singleton
from common.ws_client_compat import websocket_app_run_forever
from config import conf
# Rich media file_type constants
@@ -210,7 +211,7 @@ class QQChannel(ChatChannel):
def run_forever():
try:
self._ws.run_forever(ping_interval=0, reconnect=0)
websocket_app_run_forever(self._ws, ping_interval=0, reconnect=0)
except (SystemExit, KeyboardInterrupt):
logger.info("[QQ] WebSocket thread interrupted")
except Exception as e:

View File

@@ -5,7 +5,7 @@
// =====================================================================
// Version — update this before each release
// =====================================================================
const APP_VERSION = 'v2.0.2';
const APP_VERSION = 'v2.0.4';
// =====================================================================
// i18n
@@ -51,6 +51,11 @@ const I18N = {
channels_empty: '暂未接入任何通道', channels_empty_desc: '点击右上角「接入通道」按钮开始配置',
channels_disconnect_confirm: '确认断开该通道?配置将保留但通道会停止运行。',
channels_connected: '已接入', channels_connecting: '接入中...',
weixin_scan_title: '微信扫码登录', weixin_scan_desc: '请使用微信扫描下方二维码',
weixin_scan_loading: '正在获取二维码...', weixin_scan_waiting: '等待扫码...',
weixin_scan_scanned: '已扫码,请在手机上确认', weixin_scan_expired: '二维码已过期,正在刷新...',
weixin_scan_success: '登录成功,正在启动通道...', weixin_scan_fail: '获取二维码失败',
weixin_qr_tip: '二维码约2分钟后过期',
tasks_title: '定时任务', tasks_desc: '查看和管理定时任务',
tasks_coming: '即将推出', tasks_coming_desc: '定时任务管理功能即将在此提供',
logs_title: '日志', logs_desc: '实时日志输出 (run.log)',
@@ -97,6 +102,11 @@ const I18N = {
channels_empty: 'No channels connected', channels_empty_desc: 'Click the "Connect" button above to get started',
channels_disconnect_confirm: 'Disconnect this channel? Config will be preserved but the channel will stop.',
channels_connected: 'Connected', channels_connecting: 'Connecting...',
weixin_scan_title: 'WeChat QR Login', weixin_scan_desc: 'Scan the QR code below with WeChat',
weixin_scan_loading: 'Loading QR code...', weixin_scan_waiting: 'Waiting for scan...',
weixin_scan_scanned: 'Scanned, please confirm on your phone', weixin_scan_expired: 'QR code expired, refreshing...',
weixin_scan_success: 'Login successful, starting channel...', weixin_scan_fail: 'Failed to load QR code',
weixin_qr_tip: 'QR code expires in ~2 minutes',
tasks_title: 'Scheduled Tasks', tasks_desc: 'View and manage scheduled tasks',
tasks_coming: 'Coming Soon', tasks_coming_desc: 'Scheduled task management will be available here',
logs_title: 'Logs', logs_desc: 'Real-time log output (run.log)',
@@ -992,7 +1002,6 @@ let configProviders = {};
let configApiBases = {};
let configApiKeys = {};
let configCurrentModel = '';
let configHasBotType = false;
let cfgProviderValue = '';
let cfgModelValue = '';
@@ -1052,7 +1061,6 @@ function initConfigView(data) {
configApiBases = data.api_bases || {};
configApiKeys = data.api_keys || {};
configCurrentModel = data.model || '';
configHasBotType = !!data.has_bot_type;
const providerEl = document.getElementById('cfg-provider');
const providerOpts = Object.entries(configProviders).map(([pid, p]) => ({ value: pid, label: p.label }));
@@ -1214,8 +1222,10 @@ function saveModelConfig() {
const updates = { model: model };
const p = configProviders[cfgProviderValue];
updates.use_linkai = (cfgProviderValue === 'linkai');
if (configHasBotType) {
updates.bot_type = (cfgProviderValue === 'linkai') ? '' : cfgProviderValue;
if (cfgProviderValue === 'linkai') {
updates.bot_type = '';
} else {
updates.bot_type = cfgProviderValue;
}
if (p && p.api_base_key) {
const base = document.getElementById('cfg-api-base').value.trim();
@@ -1583,6 +1593,8 @@ function loadChannelsView() {
}
function renderActiveChannels() {
stopWeixinQrPoll();
stopWeixinStatusPoll();
const container = document.getElementById('channels-content');
container.innerHTML = '';
closeAddChannelPanel();
@@ -1608,17 +1620,30 @@ function renderActiveChannels() {
card.id = `channel-card-${ch.name}`;
const fieldsHtml = buildChannelFieldsHtml(ch.name, ch.fields || []);
const hasFields = (ch.fields || []).length > 0;
const weixinWaiting = ch.name === 'weixin' && ch.login_status && ch.login_status !== 'logged_in';
let statusDot, statusText;
if (weixinWaiting) {
statusDot = 'bg-amber-400 animate-pulse';
statusText = ch.login_status === 'scanned'
? `<span class="text-xs text-primary-500">${t('weixin_scan_scanned')}</span>`
: `<span class="text-xs text-amber-500">${t('weixin_scan_waiting')}</span>`;
} else {
statusDot = 'bg-primary-400';
statusText = `<span class="text-xs text-primary-500">${t('channels_connected')}</span>`;
}
card.innerHTML = `
<div class="flex items-center gap-4 mb-5">
<div class="flex items-center gap-4${hasFields || weixinWaiting ? ' mb-5' : ''}">
<div class="w-10 h-10 rounded-xl bg-${ch.color}-50 dark:bg-${ch.color}-900/20 flex items-center justify-center flex-shrink-0">
<i class="fas ${ch.icon} text-${ch.color}-500 text-base"></i>
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2">
<span class="font-semibold text-slate-800 dark:text-slate-100">${escapeHtml(label)}</span>
<span class="w-2 h-2 rounded-full bg-primary-400"></span>
<span class="text-xs text-primary-500">${t('channels_connected')}</span>
<span class="w-2 h-2 rounded-full ${statusDot}"></span>
${statusText}
</div>
<p class="text-xs text-slate-500 dark:text-slate-400 mt-0.5 font-mono">${escapeHtml(ch.name)}</p>
</div>
@@ -1630,7 +1655,14 @@ function renderActiveChannels() {
${t('channels_disconnect')}
</button>
</div>
<div class="space-y-4">
${weixinWaiting ? `<div id="weixin-active-qr" class="flex flex-col items-center py-2">
<button onclick="showWeixinActiveQr()"
class="px-4 py-2 rounded-lg bg-primary-500 hover:bg-primary-600 text-white text-sm font-medium
cursor-pointer transition-colors duration-150">
${t('weixin_scan_title')}
</button>
</div>` : ''}
${hasFields ? `<div class="space-y-4">
${fieldsHtml}
<div class="flex items-center justify-end gap-3 pt-1">
<span id="ch-status-${ch.name}" class="text-xs text-primary-500 opacity-0 transition-opacity duration-300"></span>
@@ -1639,10 +1671,14 @@ function renderActiveChannels() {
cursor-pointer transition-colors duration-150 disabled:opacity-50 disabled:cursor-not-allowed"
id="ch-save-${ch.name}">${t('channels_save')}</button>
</div>
</div>`;
</div>` : ''}`;
container.appendChild(card);
bindSecretFieldEvents(card);
if (weixinWaiting) {
startWeixinActiveStatusPoll();
}
});
}
@@ -1774,6 +1810,9 @@ function openAddChannelPanel() {
const activeNames = new Set(channelsData.filter(c => c.active).map(c => c.name));
const available = channelsData.filter(c => !activeNames.has(c.name));
const content = document.getElementById('channels-content');
if (activeNames.size === 0 && content) content.classList.add('hidden');
if (available.length === 0) {
panel.innerHTML = `<div class="bg-white dark:bg-[#1A1A1A] rounded-xl border border-slate-200 dark:border-white/10 p-6 text-center">
<p class="text-sm text-slate-500 dark:text-slate-400">${currentLang === 'zh' ? '所有通道均已接入' : 'All channels are already connected'}</p>
@@ -1828,14 +1867,18 @@ function openAddChannelPanel() {
}
function closeAddChannelPanel() {
stopWeixinQrPoll();
const panel = document.getElementById('channels-add-panel');
if (panel) {
panel.classList.add('hidden');
panel.innerHTML = '';
}
const content = document.getElementById('channels-content');
if (content) content.classList.remove('hidden');
}
function onAddChannelSelect(chName) {
stopWeixinQrPoll();
const fieldsContainer = document.getElementById('add-channel-fields');
const actions = document.getElementById('add-channel-actions');
@@ -1845,6 +1888,16 @@ function onAddChannelSelect(chName) {
return;
}
if (chName === 'weixin') {
actions.classList.add('hidden');
fieldsContainer.innerHTML = `
<div id="weixin-qr-panel" class="flex flex-col items-center py-4">
<p class="text-sm text-slate-500 dark:text-slate-400 mb-4">${t('weixin_scan_loading')}</p>
</div>`;
startWeixinQrLogin();
return;
}
const ch = channelsData.find(c => c.name === chName);
if (!ch) return;
@@ -1900,6 +1953,172 @@ function submitAddChannel() {
});
}
// =====================================================================
// WeChat QR Login
// =====================================================================
let _weixinQrPollTimer = null;
let _weixinStatusPollTimer = null;
function stopWeixinStatusPoll() {
if (_weixinStatusPollTimer) {
clearTimeout(_weixinStatusPollTimer);
_weixinStatusPollTimer = null;
}
}
function startWeixinActiveStatusPoll() {
stopWeixinStatusPoll();
_weixinStatusPollTimer = setTimeout(() => {
fetch('/api/channels').then(r => r.json()).then(data => {
if (data.status !== 'success') return;
const wx = (data.channels || []).find(c => c.name === 'weixin');
if (!wx || !wx.active) return;
if (wx.login_status === 'logged_in') {
channelsData = data.channels;
renderActiveChannels();
} else {
const ch = channelsData.find(c => c.name === 'weixin');
if (ch) ch.login_status = wx.login_status;
startWeixinActiveStatusPoll();
}
}).catch(() => { startWeixinActiveStatusPoll(); });
}, 3000);
}
function showWeixinActiveQr() {
const container = document.getElementById('weixin-active-qr');
if (!container) return;
container.innerHTML = `
<div id="weixin-qr-panel" class="flex flex-col items-center py-2">
<p class="text-sm text-slate-500 dark:text-slate-400 mb-4">${t('weixin_scan_loading')}</p>
</div>`;
stopWeixinStatusPoll();
startWeixinQrLogin();
}
function stopWeixinQrPoll() {
if (_weixinQrPollTimer) {
clearTimeout(_weixinQrPollTimer);
_weixinQrPollTimer = null;
}
}
function startWeixinQrLogin() {
stopWeixinQrPoll();
fetch('/api/weixin/qrlogin')
.then(r => r.json())
.then(data => {
const panel = document.getElementById('weixin-qr-panel');
if (!panel) return;
if (data.status !== 'success') {
panel.innerHTML = `<p class="text-sm text-red-500">${t('weixin_scan_fail')}: ${data.message || ''}</p>`;
return;
}
renderWeixinQr(data.qr_image || data.qrcode_url, 'waiting');
if (data.source === 'channel') {
startWeixinActiveStatusPoll();
} else {
pollWeixinQrStatus();
}
})
.catch(() => {
const panel = document.getElementById('weixin-qr-panel');
if (panel) panel.innerHTML = `<p class="text-sm text-red-500">${t('weixin_scan_fail')}</p>`;
});
}
function renderWeixinQr(qrcodeUrl, status) {
const panel = document.getElementById('weixin-qr-panel');
if (!panel) return;
let statusText = t('weixin_scan_waiting');
let statusColor = 'text-slate-500 dark:text-slate-400';
if (status === 'scanned') {
statusText = t('weixin_scan_scanned');
statusColor = 'text-primary-500';
} else if (status === 'expired') {
statusText = t('weixin_scan_expired');
statusColor = 'text-amber-500';
} else if (status === 'confirmed') {
statusText = t('weixin_scan_success');
statusColor = 'text-primary-500';
}
panel.innerHTML = `
<div class="flex flex-col items-center">
<p class="text-sm font-medium text-slate-700 dark:text-slate-200 mb-1">${t('weixin_scan_title')}</p>
<p class="text-xs text-slate-400 dark:text-slate-500 mb-4">${t('weixin_scan_desc')}</p>
<div class="bg-white p-3 rounded-xl shadow-sm border border-slate-100 dark:border-slate-700 mb-3">
<img src="${escapeHtml(qrcodeUrl)}" alt="QR Code" class="w-52 h-52" style="image-rendering: pixelated;"/>
</div>
<p class="text-xs ${statusColor} mb-1">${statusText}</p>
<p class="text-xs text-slate-400 dark:text-slate-500">${t('weixin_qr_tip')}</p>
</div>`;
}
function pollWeixinQrStatus() {
_weixinQrPollTimer = setTimeout(() => {
fetch('/api/weixin/qrlogin', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'poll' })
})
.then(r => r.json())
.then(data => {
const panel = document.getElementById('weixin-qr-panel');
if (!panel) { stopWeixinQrPoll(); return; }
if (data.status !== 'success') {
pollWeixinQrStatus();
return;
}
const qrStatus = data.qr_status;
if (qrStatus === 'confirmed') {
renderWeixinQr('', 'confirmed');
panel.innerHTML = `
<div class="flex flex-col items-center py-4">
<div class="w-12 h-12 rounded-full bg-primary-50 dark:bg-primary-900/30 flex items-center justify-center mb-3">
<i class="fas fa-check text-primary-500 text-lg"></i>
</div>
<p class="text-sm font-medium text-primary-600 dark:text-primary-400">${t('weixin_scan_success')}</p>
</div>`;
connectWeixinAfterQr();
} else if (qrStatus === 'expired' && (data.qr_image || data.qrcode_url)) {
renderWeixinQr(data.qr_image || data.qrcode_url, 'waiting');
pollWeixinQrStatus();
} else if (qrStatus === 'scaned') {
const img = panel.querySelector('img');
const currentSrc = img ? img.src : '';
renderWeixinQr(currentSrc, 'scanned');
pollWeixinQrStatus();
} else {
pollWeixinQrStatus();
}
})
.catch(() => {
pollWeixinQrStatus();
});
}, 2000);
}
function connectWeixinAfterQr() {
fetch('/api/channels', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'connect', channel: 'weixin', config: {} })
})
.then(r => r.json())
.then(data => {
if (data.status === 'success') {
const ch = channelsData.find(c => c.name === 'weixin');
if (ch) ch.active = true;
setTimeout(() => renderActiveChannels(), 1500);
}
})
.catch(() => {});
}
// =====================================================================
// Scheduler View
// =====================================================================

View File

@@ -353,13 +353,15 @@ class WebChannel(ChatChannel):
# 打印可用渠道类型提示
logger.info(
"[WebChannel] 全部可用通道如下,可修改 config.json 配置文件中的 channel_type 字段进行切换,多个通道用逗号分隔:")
logger.info("[WebChannel] 1. web - 网页")
logger.info("[WebChannel] 2. terminal - 终端")
logger.info("[WebChannel] 3. feishu - 飞书")
logger.info("[WebChannel] 4. dingtalk - 钉钉")
logger.info("[WebChannel] 5. wechatcom_app - 企微自建应用")
logger.info("[WebChannel] 6. wechatmp - 个人公众号")
logger.info("[WebChannel] 7. wechatmp_service - 企业公众号")
logger.info("[WebChannel] 1. weixin - 微信")
logger.info("[WebChannel] 2. web - 网页")
logger.info("[WebChannel] 3. terminal - 终端")
logger.info("[WebChannel] 4. feishu - 飞书")
logger.info("[WebChannel] 5. dingtalk - 钉钉")
logger.info("[WebChannel] 6. wecom_bot - 企微智能机器人")
logger.info("[WebChannel] 7. wechatcom_app - 企微自建应用")
logger.info("[WebChannel] 8. wechatmp - 个人公众号")
logger.info("[WebChannel] 9. wechatmp_service - 企业公众号")
logger.info("[WebChannel] ✅ Web控制台已运行")
logger.info(f"[WebChannel] 🌐 本地访问: http://localhost:{port}")
logger.info(f"[WebChannel] 🌍 服务器访问: http://YOUR_IP:{port} (请将YOUR_IP替换为服务器IP)")
@@ -380,6 +382,7 @@ class WebChannel(ChatChannel):
'/chat', 'ChatHandler',
'/config', 'ConfigHandler',
'/api/channels', 'ChannelsHandler',
'/api/weixin/qrlogin', 'WeixinQrHandler',
'/api/tools', 'ToolsHandler',
'/api/skills', 'SkillsHandler',
'/api/memory', 'MemoryHandler',
@@ -497,7 +500,7 @@ class ConfigHandler:
const.DOUBAO_SEED_2_PRO, const.DOUBAO_SEED_2_CODE,
const.CLAUDE_4_6_SONNET, const.CLAUDE_4_6_OPUS, const.CLAUDE_4_5_SONNET,
const.GEMINI_31_FLASH_LITE_PRE, const.GEMINI_31_PRO_PRE, const.GEMINI_3_FLASH_PRE,
const.GPT_54, const.GPT_5, const.GPT_41, const.GPT_4o,
const.GPT_54, const.GPT_54_MINI, const.GPT_54_NANO, const.GPT_5, const.GPT_41, const.GPT_4o,
const.DEEPSEEK_CHAT, const.DEEPSEEK_REASONER,
]
@@ -551,12 +554,12 @@ class ConfigHandler:
"api_base_default": "https://generativelanguage.googleapis.com",
"models": [const.GEMINI_31_FLASH_LITE_PRE, const.GEMINI_31_PRO_PRE, const.GEMINI_3_FLASH_PRE],
}),
("chatGPT", {
("openai", {
"label": "OpenAI",
"api_key_field": "open_ai_api_key",
"api_base_key": "open_ai_api_base",
"api_base_default": "https://api.openai.com/v1",
"models": [const.GPT_54, const.GPT_5, const.GPT_41, const.GPT_4o],
"models": [const.GPT_54, const.GPT_54_MINI, const.GPT_54_NANO, const.GPT_5, const.GPT_41, const.GPT_4o],
}),
("deepseek", {
"label": "DeepSeek",
@@ -625,8 +628,7 @@ class ConfigHandler:
"use_agent": use_agent,
"title": title,
"model": local_config.get("model", ""),
"bot_type": local_config.get("bot_type", ""),
"has_bot_type": "bot_type" in local_config,
"bot_type": "openai" if local_config.get("bot_type") == "chatGPT" else local_config.get("bot_type", ""),
"use_linkai": bool(local_config.get("use_linkai", False)),
"channel_type": local_config.get("channel_type", ""),
"agent_max_context_tokens": local_config.get("agent_max_context_tokens", 50000),
@@ -671,9 +673,6 @@ class ConfigHandler:
file_cfg = json.load(f)
else:
file_cfg = {}
if "bot_type" in applied and "bot_type" not in file_cfg:
del applied["bot_type"]
local_config.pop("bot_type", None)
file_cfg.update(applied)
with open(config_path, "w", encoding="utf-8") as f:
json.dump(file_cfg, f, indent=4, ensure_ascii=False)
@@ -689,6 +688,12 @@ class ChannelsHandler:
"""API for managing external channel configurations (feishu, dingtalk, etc)."""
CHANNEL_DEFS = OrderedDict([
("weixin", {
"label": {"zh": "微信", "en": "WeChat"},
"icon": "fa-comment",
"color": "emerald",
"fields": [],
}),
("feishu", {
"label": {"zh": "飞书", "en": "Feishu"},
"icon": "fa-paper-plane",
@@ -754,6 +759,20 @@ class ChannelsHandler:
}),
])
@staticmethod
def _get_weixin_login_status() -> str:
try:
import sys
app_module = sys.modules.get('__main__') or sys.modules.get('app')
mgr = getattr(app_module, '_channel_mgr', None) if app_module else None
if mgr:
ch = mgr.get_channel("weixin")
if ch and hasattr(ch, 'login_status'):
return ch.login_status
except Exception:
pass
return "unknown"
@staticmethod
def _mask_secret(value: str) -> str:
if not value or len(value) <= 8:
@@ -793,14 +812,17 @@ class ChannelsHandler:
"value": display_val,
"default": f.get("default", ""),
})
channels.append({
ch_info = {
"name": ch_name,
"label": ch_def["label"],
"icon": ch_def["icon"],
"color": ch_def["color"],
"active": ch_name in active_channels,
"fields": fields_out,
})
}
if ch_name == "weixin" and ch_name in active_channels:
ch_info["login_status"] = self._get_weixin_login_status()
channels.append(ch_info)
return json.dumps({"status": "success", "channels": channels}, ensure_ascii=False)
except Exception as e:
logger.error(f"[WebChannel] Channels API error: {e}")
@@ -1020,6 +1042,157 @@ class ChannelsHandler:
}, ensure_ascii=False)
class WeixinQrHandler:
"""Handle WeChat QR code login from the web console.
GET /api/weixin/qrlogin → fetch a new QR code
POST /api/weixin/qrlogin → poll QR status or start channel after login
"""
_qr_state = {}
@staticmethod
def _qr_to_data_uri(data: str) -> str:
"""Generate a QR code as a PNG data URI."""
try:
import qrcode as qr_lib
import io
import base64
qr = qr_lib.QRCode(error_correction=qr_lib.constants.ERROR_CORRECT_L, box_size=6, border=2)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
buf = io.BytesIO()
img.save(buf, format="PNG")
b64 = base64.b64encode(buf.getvalue()).decode("ascii")
return f"data:image/png;base64,{b64}"
except ImportError:
return ""
@staticmethod
def _get_running_channel():
try:
import sys
app_module = sys.modules.get('__main__') or sys.modules.get('app')
mgr = getattr(app_module, '_channel_mgr', None) if app_module else None
if mgr:
return mgr.get_channel("weixin")
except Exception:
pass
return None
def GET(self):
web.header('Content-Type', 'application/json; charset=utf-8')
try:
running_ch = self._get_running_channel()
if running_ch and hasattr(running_ch, '_current_qr_url') and running_ch._current_qr_url:
qr_image = self._qr_to_data_uri(running_ch._current_qr_url)
return json.dumps({
"status": "success",
"qrcode_url": running_ch._current_qr_url,
"qr_image": qr_image,
"source": "channel",
})
from channel.weixin.weixin_api import WeixinApi, DEFAULT_BASE_URL
base_url = conf().get("weixin_base_url", DEFAULT_BASE_URL)
api = WeixinApi(base_url=base_url)
qr_resp = api.fetch_qr_code()
qrcode = qr_resp.get("qrcode", "")
qrcode_url = qr_resp.get("qrcode_img_content", "")
if not qrcode:
return json.dumps({"status": "error", "message": "No QR code returned"})
qr_image = self._qr_to_data_uri(qrcode_url)
WeixinQrHandler._qr_state = {
"qrcode": qrcode,
"qrcode_url": qrcode_url,
"base_url": base_url,
}
return json.dumps({"status": "success", "qrcode_url": qrcode_url, "qr_image": qr_image})
except Exception as e:
logger.error(f"[WebChannel] WeixinQr GET error: {e}")
return json.dumps({"status": "error", "message": str(e)})
def POST(self):
web.header('Content-Type', 'application/json; charset=utf-8')
try:
body = json.loads(web.data())
action = body.get("action", "poll")
if action == "poll":
return self._poll_status()
elif action == "refresh":
return self.GET()
else:
return json.dumps({"status": "error", "message": f"unknown action: {action}"})
except Exception as e:
logger.error(f"[WebChannel] WeixinQr POST error: {e}")
return json.dumps({"status": "error", "message": str(e)})
def _poll_status(self):
state = WeixinQrHandler._qr_state
qrcode = state.get("qrcode", "")
base_url = state.get("base_url", "")
if not qrcode:
return json.dumps({"status": "error", "message": "No active QR session"})
from channel.weixin.weixin_api import WeixinApi, DEFAULT_BASE_URL
api = WeixinApi(base_url=base_url or DEFAULT_BASE_URL)
try:
status_resp = api.poll_qr_status(qrcode, timeout=10)
except Exception as e:
return json.dumps({"status": "error", "message": str(e)})
qr_status = status_resp.get("status", "wait")
if qr_status == "confirmed":
bot_token = status_resp.get("bot_token", "")
bot_id = status_resp.get("ilink_bot_id", "")
result_base_url = status_resp.get("baseurl", base_url)
user_id = status_resp.get("ilink_user_id", "")
if not bot_token or not bot_id:
return json.dumps({"status": "error", "message": "Login confirmed but missing token"})
cred_path = os.path.expanduser(
conf().get("weixin_credentials_path", "~/.weixin_cow_credentials.json")
)
from channel.weixin.weixin_channel import _save_credentials
_save_credentials(cred_path, {
"token": bot_token,
"base_url": result_base_url,
"bot_id": bot_id,
"user_id": user_id,
})
conf()["weixin_token"] = bot_token
conf()["weixin_base_url"] = result_base_url
WeixinQrHandler._qr_state = {}
logger.info(f"[WebChannel] WeChat QR login confirmed: bot_id={bot_id}")
return json.dumps({
"status": "success",
"qr_status": "confirmed",
"bot_id": bot_id,
})
if qr_status == "expired":
new_resp = api.fetch_qr_code()
new_qrcode = new_resp.get("qrcode", "")
new_qrcode_url = new_resp.get("qrcode_img_content", "")
new_qr_image = self._qr_to_data_uri(new_qrcode_url)
WeixinQrHandler._qr_state["qrcode"] = new_qrcode
WeixinQrHandler._qr_state["qrcode_url"] = new_qrcode_url
return json.dumps({
"status": "success",
"qr_status": "expired",
"qrcode_url": new_qrcode_url,
"qr_image": new_qr_image,
})
return json.dumps({"status": "success", "qr_status": qr_status})
def _get_workspace_root():
"""Resolve the agent workspace directory."""
from common.utils import expand_path

View File

@@ -26,6 +26,7 @@ from channel.wecom_bot.wecom_bot_message import WecomBotMessage
from common.expired_dict import ExpiredDict
from common.log import logger
from common.singleton import singleton
from common.ws_client_compat import websocket_app_run_forever
from config import conf
WECOM_WS_URL = "wss://openws.work.weixin.qq.com"
@@ -119,7 +120,7 @@ class WecomBotChannel(ChatChannel):
def run_forever():
try:
self._ws.run_forever(ping_interval=0, reconnect=0)
websocket_app_run_forever(self._ws, ping_interval=0, reconnect=0)
except (SystemExit, KeyboardInterrupt):
logger.info("[WecomBot] WebSocket thread interrupted")
except Exception as e:

View File

View File

@@ -0,0 +1,385 @@
"""
Weixin HTTP JSON API client.
Implements the ilink bot protocol:
- getUpdates (long-poll)
- sendMessage
- getUploadUrl
- getConfig
- sendTyping
- QR login (get_bot_qrcode / get_qrcode_status)
CDN media upload with AES-128-ECB encryption.
"""
import base64
import hashlib
import os
import random
import struct
import time
import uuid
import requests
from common.log import logger
DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com"
CDN_BASE_URL = "https://novac2c.cdn.weixin.qq.com/c2c"
DEFAULT_LONG_POLL_TIMEOUT = 35
DEFAULT_API_TIMEOUT = 15
QR_POLL_TIMEOUT = 35
BOT_TYPE = "3"
def _random_wechat_uin() -> str:
val = random.randint(0, 0xFFFFFFFF)
return base64.b64encode(str(val).encode("utf-8")).decode("utf-8")
def _build_headers(token: str = "") -> dict:
headers = {
"Content-Type": "application/json",
"AuthorizationType": "ilink_bot_token",
"X-WECHAT-UIN": _random_wechat_uin(),
}
if token:
headers["Authorization"] = f"Bearer {token}"
return headers
def _ensure_trailing_slash(url: str) -> str:
return url if url.endswith("/") else url + "/"
class WeixinApi:
"""Stateless HTTP client for the Weixin ilink bot API."""
def __init__(self, base_url: str = DEFAULT_BASE_URL, token: str = "",
cdn_base_url: str = CDN_BASE_URL):
self.base_url = base_url
self.token = token
self.cdn_base_url = cdn_base_url
def _post(self, endpoint: str, body: dict, timeout: int = DEFAULT_API_TIMEOUT) -> dict:
url = _ensure_trailing_slash(self.base_url) + endpoint
headers = _build_headers(self.token)
try:
resp = requests.post(url, json=body, headers=headers, timeout=timeout)
resp.raise_for_status()
return resp.json()
except requests.exceptions.Timeout:
logger.debug(f"[Weixin] API timeout: {endpoint}")
return {"ret": 0, "msgs": []}
except Exception as e:
logger.error(f"[Weixin] API error {endpoint}: {e}")
raise
# ── getUpdates (long-poll) ─────────────────────────────────────────
def get_updates(self, get_updates_buf: str = "", timeout: int = DEFAULT_LONG_POLL_TIMEOUT) -> dict:
return self._post("ilink/bot/getupdates", {
"get_updates_buf": get_updates_buf,
}, timeout=timeout + 5)
# ── sendMessage ────────────────────────────────────────────────────
def send_text(self, to: str, text: str, context_token: str) -> dict:
return self._post("ilink/bot/sendmessage", {
"msg": {
"from_user_id": "",
"to_user_id": to,
"client_id": uuid.uuid4().hex[:16],
"message_type": 2, # BOT
"message_state": 2, # FINISH
"item_list": [{"type": 1, "text_item": {"text": text}}],
"context_token": context_token,
}
})
def send_image_item(self, to: str, context_token: str,
encrypt_query_param: str, aes_key_b64: str,
ciphertext_size: int, text: str = "") -> dict:
items = []
if text:
items.append({"type": 1, "text_item": {"text": text}})
items.append({
"type": 2,
"image_item": {
"media": {
"encrypt_query_param": encrypt_query_param,
"aes_key": aes_key_b64,
"encrypt_type": 1,
},
"mid_size": ciphertext_size,
}
})
return self._send_items(to, context_token, items)
def send_file_item(self, to: str, context_token: str,
encrypt_query_param: str, aes_key_b64: str,
file_name: str, file_size: int, text: str = "") -> dict:
items = []
if text:
items.append({"type": 1, "text_item": {"text": text}})
items.append({
"type": 4,
"file_item": {
"media": {
"encrypt_query_param": encrypt_query_param,
"aes_key": aes_key_b64,
"encrypt_type": 1,
},
"file_name": file_name,
"len": str(file_size),
}
})
return self._send_items(to, context_token, items)
def send_video_item(self, to: str, context_token: str,
encrypt_query_param: str, aes_key_b64: str,
ciphertext_size: int, text: str = "") -> dict:
items = []
if text:
items.append({"type": 1, "text_item": {"text": text}})
items.append({
"type": 5,
"video_item": {
"media": {
"encrypt_query_param": encrypt_query_param,
"aes_key": aes_key_b64,
"encrypt_type": 1,
},
"video_size": ciphertext_size,
}
})
return self._send_items(to, context_token, items)
def _send_items(self, to: str, context_token: str, items: list) -> dict:
return self._post("ilink/bot/sendmessage", {
"msg": {
"from_user_id": "",
"to_user_id": to,
"client_id": uuid.uuid4().hex[:16],
"message_type": 2,
"message_state": 2,
"item_list": items,
"context_token": context_token,
}
})
# ── getUploadUrl ───────────────────────────────────────────────────
def get_upload_url(self, filekey: str, media_type: int, to_user_id: str,
rawsize: int, rawfilemd5: str, filesize: int,
aeskey: str,
thumb_rawsize: int = 0, thumb_rawfilemd5: str = "",
thumb_filesize: int = 0) -> dict:
body = {
"filekey": filekey,
"media_type": media_type,
"to_user_id": to_user_id,
"rawsize": rawsize,
"rawfilemd5": rawfilemd5,
"filesize": filesize,
"aeskey": aeskey,
}
if thumb_rawsize > 0:
body["thumb_rawsize"] = thumb_rawsize
body["thumb_rawfilemd5"] = thumb_rawfilemd5
body["thumb_filesize"] = thumb_filesize
else:
body["no_need_thumb"] = True
return self._post("ilink/bot/getuploadurl", body)
# ── getConfig / sendTyping ─────────────────────────────────────────
def get_config(self, user_id: str, context_token: str = "") -> dict:
return self._post("ilink/bot/getconfig", {
"ilink_user_id": user_id,
"context_token": context_token,
}, timeout=10)
def send_typing(self, user_id: str, typing_ticket: str, status: int = 1) -> dict:
return self._post("ilink/bot/sendtyping", {
"ilink_user_id": user_id,
"typing_ticket": typing_ticket,
"status": status,
}, timeout=10)
# ── QR Login ───────────────────────────────────────────────────────
def fetch_qr_code(self) -> dict:
url = _ensure_trailing_slash(self.base_url) + f"ilink/bot/get_bot_qrcode?bot_type={BOT_TYPE}"
resp = requests.get(url, timeout=15)
resp.raise_for_status()
return resp.json()
def poll_qr_status(self, qrcode: str, timeout: int = QR_POLL_TIMEOUT) -> dict:
url = (_ensure_trailing_slash(self.base_url) +
f"ilink/bot/get_qrcode_status?qrcode={requests.utils.quote(qrcode)}")
headers = {"iLink-App-ClientVersion": "1"}
try:
resp = requests.get(url, headers=headers, timeout=timeout)
resp.raise_for_status()
return resp.json()
except requests.exceptions.Timeout:
return {"status": "wait"}
# ── AES-128-ECB helpers ─────────────────────────────────────────────
def _aes_ecb_encrypt(data: bytes, key: bytes) -> bytes:
from Crypto.Cipher import AES
pad_len = 16 - (len(data) % 16)
padded = data + bytes([pad_len] * pad_len)
cipher = AES.new(key, AES.MODE_ECB)
return cipher.encrypt(padded)
def _aes_ecb_decrypt(data: bytes, key: bytes) -> bytes:
from Crypto.Cipher import AES
cipher = AES.new(key, AES.MODE_ECB)
decrypted = cipher.decrypt(data)
pad_len = decrypted[-1]
if pad_len > 16:
return decrypted
return decrypted[:-pad_len]
def _file_md5(file_path: str) -> str:
h = hashlib.md5()
with open(file_path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
def _md5_bytes(data: bytes) -> str:
return hashlib.md5(data).hexdigest()
def upload_media_to_cdn(api: WeixinApi, file_path: str, to_user_id: str,
media_type: int) -> dict:
"""
Upload a local file to the Weixin CDN.
Args:
api: WeixinApi instance
file_path: local file path
to_user_id: target user id
media_type: 1=IMAGE, 2=VIDEO, 3=FILE
Returns:
dict with keys: encrypt_query_param, aes_key_b64, ciphertext_size, raw_size
"""
aes_key = os.urandom(16)
aes_key_hex = aes_key.hex()
with open(file_path, "rb") as f:
raw_data = f.read()
raw_size = len(raw_data)
raw_md5 = _md5_bytes(raw_data)
encrypted = _aes_ecb_encrypt(raw_data, aes_key)
cipher_size = len(encrypted)
filekey = uuid.uuid4().hex
thumb_rawsize = 0
thumb_rawfilemd5 = ""
thumb_filesize = 0
if media_type == 1: # IMAGE - generate a tiny thumbnail
try:
from PIL import Image
import io
img = Image.open(file_path)
img.thumbnail((100, 100))
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=60)
thumb_raw = buf.getvalue()
thumb_rawsize = len(thumb_raw)
thumb_rawfilemd5 = _md5_bytes(thumb_raw)
thumb_encrypted = _aes_ecb_encrypt(thumb_raw, aes_key)
thumb_filesize = len(thumb_encrypted)
except Exception as e:
logger.warning(f"[Weixin] Thumbnail generation failed, skipping: {e}")
resp = api.get_upload_url(
filekey=filekey,
media_type=media_type,
to_user_id=to_user_id,
rawsize=raw_size,
rawfilemd5=raw_md5,
filesize=cipher_size,
aeskey=aes_key_hex,
thumb_rawsize=thumb_rawsize,
thumb_rawfilemd5=thumb_rawfilemd5,
thumb_filesize=thumb_filesize,
)
upload_param = resp.get("upload_param", "")
if not upload_param:
raise RuntimeError(f"[Weixin] getUploadUrl returned no upload_param: {resp}")
cdn_url = api.cdn_base_url + "?" + upload_param
put_resp = requests.put(cdn_url, data=encrypted, headers={
"Content-Type": "application/octet-stream",
"Content-Length": str(cipher_size),
}, timeout=60)
put_resp.raise_for_status()
# Upload thumbnail if we have one
thumb_upload_param = resp.get("thumb_upload_param", "")
if thumb_upload_param and thumb_filesize > 0:
thumb_cdn_url = api.cdn_base_url + "?" + thumb_upload_param
try:
requests.put(thumb_cdn_url, data=thumb_encrypted, headers={
"Content-Type": "application/octet-stream",
"Content-Length": str(thumb_filesize),
}, timeout=30)
except Exception as e:
logger.warning(f"[Weixin] Thumbnail upload failed (non-fatal): {e}")
return {
"encrypt_query_param": upload_param,
"aes_key_b64": base64.b64encode(aes_key).decode("utf-8"),
"ciphertext_size": cipher_size,
"raw_size": raw_size,
}
def download_media_from_cdn(cdn_base_url: str, encrypt_query_param: str,
aes_key: str, save_path: str) -> str:
"""
Download and decrypt a media file from Weixin CDN.
Args:
cdn_base_url: CDN base URL
encrypt_query_param: encrypted query parameter from message
aes_key: hex or base64 encoded AES key
save_path: path to save decrypted file
Returns:
save_path on success
"""
url = cdn_base_url + "?" + encrypt_query_param
resp = requests.get(url, timeout=60)
resp.raise_for_status()
# Determine key format (hex string or base64)
try:
key_bytes = bytes.fromhex(aes_key)
if len(key_bytes) != 16:
raise ValueError()
except (ValueError, TypeError):
key_bytes = base64.b64decode(aes_key)
if len(key_bytes) != 16:
raise ValueError(f"Invalid AES key length: {len(key_bytes)}")
decrypted = _aes_ecb_decrypt(resp.content, key_bytes)
os.makedirs(os.path.dirname(save_path), exist_ok=True)
with open(save_path, "wb") as f:
f.write(decrypted)
return save_path

View File

@@ -0,0 +1,603 @@
"""
Weixin channel implementation.
Uses HTTP long-poll (getUpdates) to receive messages and sendMessage to reply.
Login via QR code scan through the ilink bot API.
"""
import json
import os
import threading
import time
import uuid
import requests
from bridge.context import Context, ContextType
from bridge.reply import Reply, ReplyType
from channel.chat_channel import ChatChannel, check_prefix
from channel.weixin.weixin_api import (
WeixinApi, upload_media_to_cdn,
DEFAULT_BASE_URL, CDN_BASE_URL,
)
from channel.weixin.weixin_message import WeixinMessage
from common.expired_dict import ExpiredDict
from common.log import logger
from common.singleton import singleton
from config import conf
MAX_CONSECUTIVE_FAILURES = 3
BACKOFF_DELAY = 30
RETRY_DELAY = 2
SESSION_EXPIRED_ERRCODE = -14
TEXT_CHUNK_LIMIT = 4000
def _load_credentials(cred_path: str) -> dict:
"""Load saved credentials from JSON file."""
try:
if os.path.exists(cred_path):
with open(cred_path, "r") as f:
return json.load(f)
except Exception as e:
logger.warning(f"[Weixin] Failed to load credentials: {e}")
return {}
def _save_credentials(cred_path: str, data: dict):
"""Save credentials to JSON file."""
os.makedirs(os.path.dirname(cred_path), exist_ok=True)
with open(cred_path, "w") as f:
json.dump(data, f, indent=2)
try:
os.chmod(cred_path, 0o600)
except Exception:
pass
@singleton
class WeixinChannel(ChatChannel):
LOGIN_STATUS_IDLE = "idle"
LOGIN_STATUS_WAITING = "waiting_scan"
LOGIN_STATUS_SCANNED = "scanned"
LOGIN_STATUS_OK = "logged_in"
def __init__(self):
super().__init__()
self.api = None
self._stop_event = threading.Event()
self._poll_thread = None
self._context_tokens = {} # user_id -> context_token
self._received_msgs = ExpiredDict(60 * 60 * 7.1)
self._get_updates_buf = ""
self._credentials_path = ""
self.login_status = self.LOGIN_STATUS_IDLE
self._current_qr_url = ""
conf()["single_chat_prefix"] = [""]
# ── Lifecycle ──────────────────────────────────────────────────────
def startup(self):
base_url = conf().get("weixin_base_url", DEFAULT_BASE_URL)
cdn_base_url = conf().get("weixin_cdn_base_url", CDN_BASE_URL)
token = conf().get("weixin_token", "")
self._credentials_path = os.path.expanduser(
conf().get("weixin_credentials_path", "~/.weixin_cow_credentials.json")
)
if not token:
creds = _load_credentials(self._credentials_path)
token = creds.get("token", "")
if creds.get("base_url"):
base_url = creds["base_url"]
if not token:
logger.info("[Weixin] No token found, starting QR login...")
self.login_status = self.LOGIN_STATUS_WAITING
login_result = self._qr_login(base_url)
if not login_result:
self.login_status = self.LOGIN_STATUS_IDLE
err = "[Weixin] QR login failed. Set weixin_token in config or run login again."
logger.error(err)
self.report_startup_error(err)
return
token = login_result["token"]
base_url = login_result.get("base_url", base_url)
self.api = WeixinApi(base_url=base_url, token=token, cdn_base_url=cdn_base_url)
self.login_status = self.LOGIN_STATUS_OK
logger.info(f"[Weixin] 微信通道已启动,凭证保存在 {self._credentials_path}"
f"如需重新扫码登录请删除该文件后重启")
self.report_startup_success()
self._stop_event.clear()
self._poll_loop()
def stop(self):
logger.info("[Weixin] stop() called")
self._stop_event.set()
def _relogin(self) -> bool:
"""Re-login after session expiry. Returns True on success."""
base_url = self.api.base_url if self.api else DEFAULT_BASE_URL
if os.path.exists(self._credentials_path):
try:
os.remove(self._credentials_path)
except Exception:
pass
self.login_status = self.LOGIN_STATUS_WAITING
result = self._qr_login(base_url)
if not result:
self.login_status = self.LOGIN_STATUS_IDLE
return False
self.api = WeixinApi(
base_url=result.get("base_url", base_url),
token=result["token"],
cdn_base_url=self.api.cdn_base_url if self.api else CDN_BASE_URL,
)
self.login_status = self.LOGIN_STATUS_OK
self._context_tokens.clear()
return True
# ── QR Login ───────────────────────────────────────────────────────
@staticmethod
def _print_qr(qrcode_url: str):
"""Print QR code to terminal for scanning."""
print("\n" + "=" * 60)
print(" 请使用微信扫描二维码登录 (二维码约2分钟后过期)")
print("=" * 60)
try:
import qrcode as qr_lib
qr = qr_lib.QRCode(error_correction=qr_lib.constants.ERROR_CORRECT_L, box_size=1, border=1)
qr.add_data(qrcode_url)
qr.make(fit=True)
qr.print_ascii(invert=True)
except ImportError:
print(f"\n 二维码链接: {qrcode_url}")
print(" (安装 'qrcode' 包可在终端显示二维码)\n")
def _notify_cloud_qrcode(self, qrcode_url: str):
"""Send QR code URL to cloud console when running in cloud mode."""
if not self.cloud_mode:
return
try:
from common import cloud_client
client = getattr(cloud_client, "chat_client", None)
if client and getattr(client, "client_id", None):
client.send_channel_qrcode("weixin", qrcode_url)
except Exception as e:
logger.warning(f"[Weixin] Failed to notify cloud QR code: {e}")
def _notify_cloud_connected(self):
"""Send connected status to cloud console when login succeeds."""
if not self.cloud_mode:
return
try:
from common import cloud_client
client = getattr(cloud_client, "chat_client", None)
if client and getattr(client, "client_id", None):
client.send_channel_status("weixin", "connected")
except Exception as e:
logger.warning(f"[Weixin] Failed to notify cloud connected: {e}")
def _qr_login(self, base_url: str) -> dict:
"""Perform interactive QR code login. Returns dict with token/base_url or empty dict."""
api = WeixinApi(base_url=base_url)
try:
qr_resp = api.fetch_qr_code()
except Exception as e:
logger.error(f"[Weixin] Failed to fetch QR code: {e}")
return {}
qrcode = qr_resp.get("qrcode", "")
qrcode_url = qr_resp.get("qrcode_img_content", "")
if not qrcode:
logger.error("[Weixin] No QR code returned from server")
return {}
self._current_qr_url = qrcode_url
logger.info(f"[Weixin] QR code URL: {qrcode_url}")
self._print_qr(qrcode_url)
self._notify_cloud_qrcode(qrcode_url)
print(" 等待扫码...\n")
scanned_printed = False
while not self._stop_event.is_set():
try:
status_resp = api.poll_qr_status(qrcode)
except Exception as e:
logger.error(f"[Weixin] QR status poll error: {e}")
return {}
status = status_resp.get("status", "wait")
if status == "wait":
pass
elif status == "scaned":
self.login_status = self.LOGIN_STATUS_SCANNED
if not scanned_printed:
print(" 已扫码,请在手机上确认...")
scanned_printed = True
elif status == "expired":
print(" 二维码已过期,正在刷新...")
try:
qr_resp = api.fetch_qr_code()
qrcode = qr_resp.get("qrcode", "")
qrcode_url = qr_resp.get("qrcode_img_content", "")
scanned_printed = False
self._current_qr_url = qrcode_url
logger.info(f"[Weixin] New QR code: {qrcode_url}")
self._print_qr(qrcode_url)
self._notify_cloud_qrcode(qrcode_url)
except Exception as e:
logger.error(f"[Weixin] QR refresh failed: {e}")
return {}
elif status == "confirmed":
bot_token = status_resp.get("bot_token", "")
bot_id = status_resp.get("ilink_bot_id", "")
result_base_url = status_resp.get("baseurl", base_url)
user_id = status_resp.get("ilink_user_id", "")
if not bot_token or not bot_id:
logger.error("[Weixin] Login confirmed but missing token/bot_id")
return {}
self._current_qr_url = ""
print(f"\n ✅ 微信登录成功bot_id={bot_id}")
logger.info(f"[Weixin] Login confirmed: bot_id={bot_id}")
self._notify_cloud_connected()
creds = {
"token": bot_token,
"base_url": result_base_url,
"bot_id": bot_id,
"user_id": user_id,
}
_save_credentials(self._credentials_path, creds)
logger.info(f"[Weixin] Credentials saved to {self._credentials_path}")
return {"token": bot_token, "base_url": result_base_url}
self._stop_event.wait(1)
logger.info("[Weixin] QR login cancelled by stop event")
self._current_qr_url = ""
return {}
# ── Long-poll loop ─────────────────────────────────────────────────
def _poll_loop(self):
"""Main long-poll loop: getUpdates -> parse -> produce."""
logger.info("[Weixin] Starting long-poll loop")
consecutive_failures = 0
while not self._stop_event.is_set():
try:
resp = self.api.get_updates(self._get_updates_buf)
ret = resp.get("ret", 0)
errcode = resp.get("errcode", 0)
is_error = (ret != 0) or (errcode != 0)
if is_error:
if errcode == SESSION_EXPIRED_ERRCODE or ret == SESSION_EXPIRED_ERRCODE:
logger.error("[Weixin] Session expired (errcode -14), starting re-login...")
if self._relogin():
logger.info("[Weixin] Re-login successful, resuming long-poll")
self._get_updates_buf = ""
consecutive_failures = 0
continue
else:
logger.error("[Weixin] Re-login failed, will retry in 5 minutes")
self._stop_event.wait(300)
continue
consecutive_failures += 1
errmsg = resp.get("errmsg", "")
logger.error(f"[Weixin] getUpdates error: ret={ret} errcode={errcode} "
f"errmsg={errmsg} ({consecutive_failures}/{MAX_CONSECUTIVE_FAILURES})")
if consecutive_failures >= MAX_CONSECUTIVE_FAILURES:
consecutive_failures = 0
self._stop_event.wait(BACKOFF_DELAY)
else:
self._stop_event.wait(RETRY_DELAY)
continue
consecutive_failures = 0
# Update sync cursor
new_buf = resp.get("get_updates_buf", "")
if new_buf:
self._get_updates_buf = new_buf
# Process messages
msgs = resp.get("msgs", [])
for raw_msg in msgs:
try:
self._process_message(raw_msg)
except Exception as e:
logger.error(f"[Weixin] Failed to process message: {e}", exc_info=True)
except Exception as e:
if self._stop_event.is_set():
break
consecutive_failures += 1
logger.error(f"[Weixin] getUpdates exception: {e} "
f"({consecutive_failures}/{MAX_CONSECUTIVE_FAILURES})")
if consecutive_failures >= MAX_CONSECUTIVE_FAILURES:
consecutive_failures = 0
self._stop_event.wait(BACKOFF_DELAY)
else:
self._stop_event.wait(RETRY_DELAY)
logger.info("[Weixin] Long-poll loop ended")
def _process_message(self, raw_msg: dict):
"""Parse a single inbound message and produce to the handling queue."""
msg_type = raw_msg.get("message_type", 0)
if msg_type != 1: # Only process USER messages (type=1)
return
msg_id = str(raw_msg.get("message_id", raw_msg.get("seq", "")))
if self._received_msgs.get(msg_id):
return
self._received_msgs[msg_id] = True
from_user = raw_msg.get("from_user_id", "")
context_token = raw_msg.get("context_token", "")
if context_token and from_user:
self._context_tokens[from_user] = context_token
cdn_base_url = self.api.cdn_base_url if self.api else CDN_BASE_URL
try:
wx_msg = WeixinMessage(raw_msg, cdn_base_url=cdn_base_url)
except Exception as e:
logger.error(f"[Weixin] Failed to parse WeixinMessage: {e}", exc_info=True)
return
logger.info(f"[Weixin] Received: from={from_user} ctype={wx_msg.ctype} "
f"content={str(wx_msg.content)[:50]}")
# File cache logic
from channel.file_cache import get_file_cache
file_cache = get_file_cache()
session_id = from_user
if wx_msg.ctype == ContextType.IMAGE:
if hasattr(wx_msg, "image_path") and wx_msg.image_path:
file_cache.add(session_id, wx_msg.image_path, file_type="image")
logger.info(f"[Weixin] Image cached for session {session_id}")
return
if wx_msg.ctype == ContextType.FILE:
wx_msg.prepare()
file_cache.add(session_id, wx_msg.content, file_type="file")
logger.info(f"[Weixin] File cached for session {session_id}: {wx_msg.content}")
return
if wx_msg.ctype == ContextType.TEXT:
cached_files = file_cache.get(session_id)
if cached_files:
refs = []
for fi in cached_files:
ftype, fpath = fi["type"], fi["path"]
if ftype == "image":
refs.append(f"[图片: {fpath}]")
elif ftype == "video":
refs.append(f"[视频: {fpath}]")
else:
refs.append(f"[文件: {fpath}]")
wx_msg.content = wx_msg.content + "\n" + "\n".join(refs)
file_cache.clear(session_id)
context = self._compose_context(
wx_msg.ctype,
wx_msg.content,
isgroup=False,
msg=wx_msg,
no_need_at=True,
)
if context:
self.produce(context)
# ── _compose_context ───────────────────────────────────────────────
def _compose_context(self, ctype: ContextType, content, **kwargs):
context = Context(ctype, content)
context.kwargs = kwargs
if "channel_type" not in context:
context["channel_type"] = self.channel_type
if "origin_ctype" not in context:
context["origin_ctype"] = ctype
cmsg = context["msg"]
context["session_id"] = cmsg.from_user_id
context["receiver"] = cmsg.other_user_id
if ctype == ContextType.TEXT:
img_match_prefix = check_prefix(content, conf().get("image_create_prefix"))
if img_match_prefix:
content = content.replace(img_match_prefix, "", 1)
context.type = ContextType.IMAGE_CREATE
else:
context.type = ContextType.TEXT
context.content = content.strip()
return context
# ── Send reply ─────────────────────────────────────────────────────
def send(self, reply: Reply, context: Context):
receiver = context.get("receiver", "")
msg = context.get("msg")
context_token = self._get_context_token(receiver, msg)
if not context_token:
logger.error(f"[Weixin] No context_token for receiver={receiver}, cannot send")
return
if reply.type == ReplyType.TEXT:
self._send_text(reply.content, receiver, context_token)
elif reply.type in (ReplyType.IMAGE_URL, ReplyType.IMAGE):
self._send_image(reply.content, receiver, context_token)
elif reply.type == ReplyType.FILE:
self._send_file(reply.content, receiver, context_token)
elif reply.type in (ReplyType.VIDEO, ReplyType.VIDEO_URL):
self._send_video(reply.content, receiver, context_token)
else:
logger.warning(f"[Weixin] Unsupported reply type: {reply.type}, fallback to text")
self._send_text(str(reply.content), receiver, context_token)
def _get_context_token(self, receiver: str, msg=None) -> str:
"""Get the context_token for a receiver, required for all sends."""
if msg and hasattr(msg, "context_token") and msg.context_token:
return msg.context_token
return self._context_tokens.get(receiver, "")
def _send_text(self, text: str, receiver: str, context_token: str):
if len(text) <= TEXT_CHUNK_LIMIT:
try:
self.api.send_text(receiver, text, context_token)
logger.debug(f"[Weixin] Text sent to {receiver}, len={len(text)}")
except Exception as e:
logger.error(f"[Weixin] Failed to send text: {e}")
return
chunks = self._split_text(text, TEXT_CHUNK_LIMIT)
for i, chunk in enumerate(chunks):
try:
self.api.send_text(receiver, chunk, context_token)
logger.debug(f"[Weixin] Text chunk {i+1}/{len(chunks)} sent to {receiver}, len={len(chunk)}")
except Exception as e:
logger.error(f"[Weixin] Failed to send text chunk {i+1}/{len(chunks)}: {e}")
break
if i < len(chunks) - 1:
time.sleep(0.5)
@staticmethod
def _split_text(text: str, limit: int) -> list:
"""Split text into chunks, preferring to break at paragraph or line boundaries."""
if len(text) <= limit:
return [text]
chunks = []
while text:
if len(text) <= limit:
chunks.append(text)
break
cut = text.rfind("\n\n", 0, limit)
if cut <= 0:
cut = text.rfind("\n", 0, limit)
if cut <= 0:
cut = limit
chunks.append(text[:cut])
text = text[cut:].lstrip("\n")
return chunks
def _send_image(self, img_path_or_url: str, receiver: str, context_token: str):
local_path = self._resolve_media_path(img_path_or_url)
if not local_path:
self._send_text("[Image send failed: file not found]", receiver, context_token)
return
try:
result = upload_media_to_cdn(self.api, local_path, receiver, media_type=1)
self.api.send_image_item(
to=receiver,
context_token=context_token,
encrypt_query_param=result["encrypt_query_param"],
aes_key_b64=result["aes_key_b64"],
ciphertext_size=result["ciphertext_size"],
)
logger.info(f"[Weixin] Image sent to {receiver}")
except Exception as e:
logger.error(f"[Weixin] Image send failed: {e}")
self._send_text("[Image send failed]", receiver, context_token)
def _send_file(self, file_path_or_url: str, receiver: str, context_token: str):
local_path = self._resolve_media_path(file_path_or_url)
if not local_path:
self._send_text("[File send failed: file not found]", receiver, context_token)
return
try:
result = upload_media_to_cdn(self.api, local_path, receiver, media_type=3)
self.api.send_file_item(
to=receiver,
context_token=context_token,
encrypt_query_param=result["encrypt_query_param"],
aes_key_b64=result["aes_key_b64"],
file_name=os.path.basename(local_path),
file_size=result["raw_size"],
)
logger.info(f"[Weixin] File sent to {receiver}")
except Exception as e:
logger.error(f"[Weixin] File send failed: {e}")
self._send_text("[File send failed]", receiver, context_token)
def _send_video(self, video_path_or_url: str, receiver: str, context_token: str):
local_path = self._resolve_media_path(video_path_or_url)
if not local_path:
self._send_text("[Video send failed: file not found]", receiver, context_token)
return
try:
result = upload_media_to_cdn(self.api, local_path, receiver, media_type=2)
self.api.send_video_item(
to=receiver,
context_token=context_token,
encrypt_query_param=result["encrypt_query_param"],
aes_key_b64=result["aes_key_b64"],
ciphertext_size=result["ciphertext_size"],
)
logger.info(f"[Weixin] Video sent to {receiver}")
except Exception as e:
logger.error(f"[Weixin] Video send failed: {e}")
self._send_text("[Video send failed]", receiver, context_token)
@staticmethod
def _resolve_media_path(path_or_url: str) -> str:
"""Resolve a file path or URL to a local file path. Downloads if needed."""
if not path_or_url:
return ""
local_path = path_or_url
if local_path.startswith("file://"):
local_path = local_path[7:]
if local_path.startswith(("http://", "https://")):
try:
resp = requests.get(local_path, timeout=60)
resp.raise_for_status()
ct = resp.headers.get("Content-Type", "")
ext = ".bin"
if "jpeg" in ct or "jpg" in ct:
ext = ".jpg"
elif "png" in ct:
ext = ".png"
elif "gif" in ct:
ext = ".gif"
elif "webp" in ct:
ext = ".webp"
elif "mp4" in ct:
ext = ".mp4"
elif "pdf" in ct:
ext = ".pdf"
tmp_path = f"/tmp/wx_media_{uuid.uuid4().hex[:8]}{ext}"
with open(tmp_path, "wb") as f:
f.write(resp.content)
return tmp_path
except Exception as e:
logger.error(f"[Weixin] Failed to download media: {e}")
return ""
if os.path.exists(local_path):
return local_path
logger.warning(f"[Weixin] Media file not found: {local_path}")
return ""

View File

@@ -0,0 +1,200 @@
"""
Weixin ChatMessage implementation.
Parses WeixinMessage from the getUpdates API into the unified ChatMessage format.
"""
import os
import uuid
from bridge.context import ContextType
from channel.chat_message import ChatMessage
from channel.weixin.weixin_api import download_media_from_cdn, CDN_BASE_URL
from common.log import logger
from common.utils import expand_path
from config import conf
# MessageItemType constants from the Weixin protocol
ITEM_TEXT = 1
ITEM_IMAGE = 2
ITEM_VOICE = 3
ITEM_FILE = 4
ITEM_VIDEO = 5
def _get_tmp_dir() -> str:
ws_root = expand_path(conf().get("agent_workspace", "~/cow"))
tmp_dir = os.path.join(ws_root, "tmp")
os.makedirs(tmp_dir, exist_ok=True)
return tmp_dir
class WeixinMessage(ChatMessage):
"""Message wrapper for Weixin channel."""
def __init__(self, msg: dict, cdn_base_url: str = CDN_BASE_URL):
super().__init__(msg)
self.msg_id = str(msg.get("message_id", msg.get("seq", uuid.uuid4().hex[:8])))
self.create_time = msg.get("create_time_ms", 0)
self.context_token = msg.get("context_token", "")
self.is_group = False # Weixin plugin only supports direct chat
self.is_at = False
from_user_id = msg.get("from_user_id", "")
to_user_id = msg.get("to_user_id", "")
self.from_user_id = from_user_id
self.from_user_nickname = from_user_id
self.to_user_id = to_user_id
self.to_user_nickname = to_user_id
self.other_user_id = from_user_id
self.other_user_nickname = from_user_id
self.actual_user_id = from_user_id
self.actual_user_nickname = from_user_id
item_list = msg.get("item_list", [])
# Parse items: find text and media
text_body = ""
media_item = None
media_type = None
ref_text = ""
for item in item_list:
itype = item.get("type", 0)
if itype == ITEM_TEXT:
text_item = item.get("text_item", {})
text_body = text_item.get("text", "")
ref = item.get("ref_msg")
if ref:
ref_title = ref.get("title", "")
ref_mi = ref.get("message_item", {})
ref_body = ""
if ref_mi.get("type") == ITEM_TEXT:
ref_body = ref_mi.get("text_item", {}).get("text", "")
if ref_title or ref_body:
parts = [p for p in [ref_title, ref_body] if p]
ref_text = f"[引用: {' | '.join(parts)}]\n"
# If ref is a media item, treat it as the media to download
if ref_mi.get("type") in (ITEM_IMAGE, ITEM_VIDEO, ITEM_FILE):
media_item = ref_mi
media_type = ref_mi.get("type")
elif itype == ITEM_VOICE:
voice_item = item.get("voice_item", {})
voice_text = voice_item.get("text", "")
if voice_text:
text_body = voice_text
else:
# Voice without transcription - download the audio
media_item = item
media_type = ITEM_VOICE
elif itype in (ITEM_IMAGE, ITEM_VIDEO, ITEM_FILE):
if not media_item:
media_item = item
media_type = itype
# Determine ctype and content
if media_item and not text_body:
self._setup_media(media_item, media_type, cdn_base_url)
elif media_item and text_body:
# Text + media: download media, attach as file ref in text
self.ctype = ContextType.TEXT
media_path = self._download_media(media_item, media_type, cdn_base_url)
if media_path:
if media_type == ITEM_IMAGE:
text_body += f"\n[图片: {media_path}]"
elif media_type == ITEM_VIDEO:
text_body += f"\n[视频: {media_path}]"
else:
text_body += f"\n[文件: {media_path}]"
self.content = ref_text + text_body
else:
self.ctype = ContextType.TEXT
self.content = ref_text + text_body
def _setup_media(self, item: dict, media_type: int, cdn_base_url: str):
"""Set up message as a media type, with lazy download via _prepare_fn."""
if media_type == ITEM_IMAGE:
self.ctype = ContextType.IMAGE
image_path = self._download_media(item, ITEM_IMAGE, cdn_base_url)
if image_path:
self.content = image_path
self.image_path = image_path
else:
self.ctype = ContextType.TEXT
self.content = "[Image download failed]"
elif media_type == ITEM_VIDEO:
self.ctype = ContextType.FILE
save_path = os.path.join(_get_tmp_dir(), f"wx_{self.msg_id}.mp4")
self.content = save_path
def _download():
path = self._download_media(item, ITEM_VIDEO, cdn_base_url)
if path:
self.content = path
self._prepare_fn = _download
elif media_type == ITEM_FILE:
self.ctype = ContextType.FILE
file_name = item.get("file_item", {}).get("file_name", f"wx_{self.msg_id}")
save_path = os.path.join(_get_tmp_dir(), file_name)
self.content = save_path
def _download():
path = self._download_media(item, ITEM_FILE, cdn_base_url)
if path:
self.content = path
self._prepare_fn = _download
elif media_type == ITEM_VOICE:
self.ctype = ContextType.VOICE
save_path = os.path.join(_get_tmp_dir(), f"wx_{self.msg_id}.silk")
self.content = save_path
def _download():
path = self._download_media(item, ITEM_VOICE, cdn_base_url)
if path:
self.content = path
self._prepare_fn = _download
def _download_media(self, item: dict, media_type: int, cdn_base_url: str) -> str:
"""Download media from CDN, returns local file path or empty string."""
type_key_map = {
ITEM_IMAGE: "image_item",
ITEM_VIDEO: "video_item",
ITEM_FILE: "file_item",
ITEM_VOICE: "voice_item",
}
key = type_key_map.get(media_type, "")
info = item.get(key, {})
media = info.get("media", {})
encrypt_param = media.get("encrypt_query_param", "")
# aes_key can be in image_item.aeskey (hex) or media.aes_key (b64)
aes_key = info.get("aeskey", "") or media.get("aes_key", "")
if not encrypt_param or not aes_key:
logger.warning(f"[Weixin] Missing CDN params for media download (type={media_type})")
return ""
ext_map = {ITEM_IMAGE: ".jpg", ITEM_VIDEO: ".mp4", ITEM_FILE: "", ITEM_VOICE: ".silk"}
ext = ext_map.get(media_type, "")
if media_type == ITEM_FILE:
ext = os.path.splitext(info.get("file_name", ""))[1] or ".bin"
save_path = os.path.join(_get_tmp_dir(), f"wx_{self.msg_id}{ext}")
try:
download_media_from_cdn(cdn_base_url, encrypt_param, aes_key, save_path)
logger.info(f"[Weixin] Media downloaded: {save_path}")
return save_path
except Exception as e:
logger.error(f"[Weixin] Media download failed: {e}")
return ""

View File

@@ -3,6 +3,18 @@ Cloud management client for connecting to the LinkAI control console.
Handles remote configuration sync, message push, and skill management
via the LinkAI socket protocol.
NOTE: By default, no cloud-related config is enabled. The application runs
entirely locally without connecting to any remote service. The cloud client
is only activated when BOTH of the following conditions are met:
1. ``use_linkai`` is set to True in config (checked in app.py before
importing this module).
2. ``cloud_deployment_id`` (or env CLOUD_DEPLOYMENT_ID) is non-empty
(checked in app.py and again in the ``start()`` function below).
If either condition is missing, this module is never loaded and the
program continues as a purely local application.
"""
from bridge.context import Context, ContextType
@@ -201,27 +213,36 @@ class CloudClient(LinkAIClient):
def _handle_channel_create(self, channel_type: str, data: dict):
local_config = conf()
self._set_channel_credentials(local_config, channel_type,
data.get("appId"), data.get("appSecret"))
cred_changed = self._set_channel_credentials(
local_config, channel_type, data.get("appId"), data.get("appSecret"))
self._add_channel_type(local_config, channel_type)
self._save_config_to_file(local_config)
if self.channel_mgr:
if not self.channel_mgr:
return
existing_ch = self.channel_mgr.get_channel(channel_type)
if existing_ch and not cred_changed:
logger.info(f"[CloudClient] Channel '{channel_type}' already running with same config, "
"skip restart, reporting status only")
threading.Thread(
target=self._do_add_channel, args=(channel_type,), daemon=True
target=self._report_channel_startup, args=(channel_type,), daemon=True
).start()
return
threading.Thread(
target=self._do_add_channel, args=(channel_type,), daemon=True
).start()
def _handle_channel_update(self, channel_type: str, data: dict):
local_config = conf()
enabled = data.get("enabled", "Y")
self._set_channel_credentials(local_config, channel_type,
data.get("appId"), data.get("appSecret"))
cred_changed = self._set_channel_credentials(
local_config, channel_type, data.get("appId"), data.get("appSecret"))
if enabled == "N":
self._remove_channel_type(local_config, channel_type)
else:
# Ensure channel_type is persisted even if this channel was not
# previously listed (e.g. update used as implicit create).
self._add_channel_type(local_config, channel_type)
self._save_config_to_file(local_config)
@@ -233,9 +254,17 @@ class CloudClient(LinkAIClient):
target=self._do_remove_channel, args=(channel_type,), daemon=True
).start()
else:
threading.Thread(
target=self._do_restart_channel, args=(self.channel_mgr, channel_type), daemon=True
).start()
existing_ch = self.channel_mgr.get_channel(channel_type)
if existing_ch and not cred_changed:
logger.info(f"[CloudClient] Channel '{channel_type}' already running with same config, "
"skip restart, reporting status only")
threading.Thread(
target=self._report_channel_startup, args=(channel_type,), daemon=True
).start()
else:
threading.Thread(
target=self._do_restart_channel, args=(self.channel_mgr, channel_type), daemon=True
).start()
def _handle_channel_delete(self, channel_type: str, data: dict):
local_config = conf()
@@ -243,11 +272,27 @@ class CloudClient(LinkAIClient):
self._remove_channel_type(local_config, channel_type)
self._save_config_to_file(local_config)
if channel_type in ("weixin", "wx"):
self._remove_weixin_credentials()
if self.channel_mgr:
threading.Thread(
target=self._do_remove_channel, args=(channel_type,), daemon=True
).start()
@staticmethod
def _remove_weixin_credentials():
"""Remove the weixin token credentials file so next connect triggers QR login."""
cred_path = os.path.expanduser(
conf().get("weixin_credentials_path", "~/.weixin_cow_credentials.json")
)
try:
if os.path.exists(cred_path):
os.remove(cred_path)
logger.info(f"[CloudClient] Removed weixin credentials: {cred_path}")
except Exception as e:
logger.warning(f"[CloudClient] Failed to remove weixin credentials: {e}")
# ------------------------------------------------------------------
# channel credentials helpers
# ------------------------------------------------------------------
@@ -322,7 +367,7 @@ class CloudClient(LinkAIClient):
self.channel_mgr.add_channel(channel_type)
logger.info(f"[CloudClient] Channel '{channel_type}' added successfully")
except Exception as e:
logger.error(f"[CloudClient] Failed to add channel '{channel_type}': {e}")
logger.error(f"[CloudClient] Failed to add channel '{channel_type}': {e}", exc_info=True)
self.send_channel_status(channel_type, "error", str(e))
return
self._report_channel_startup(channel_type)
@@ -334,12 +379,31 @@ class CloudClient(LinkAIClient):
except Exception as e:
logger.error(f"[CloudClient] Failed to remove channel '{channel_type}': {e}")
def send_channel_qrcode(self, channel_type: str, qrcode_url: str):
"""Report QR code URL for a channel that requires scan-to-login."""
if self.client_id:
from linkai.api.client.client import ClientMsgType
msg = self._build_package(ClientMsgType.CHANNEL_STATUS)
msg["data"]["channelType"] = channel_type
msg["data"]["status"] = "qrcode"
msg["data"]["qrcodeUrl"] = qrcode_url
self._send_package(msg)
logger.info(f"[CloudClient] Sent QR code status for '{channel_type}'")
def _report_channel_startup(self, channel_type: str):
"""Wait for channel startup result and report to cloud."""
ch = self.channel_mgr.get_channel(channel_type)
if not ch:
self.send_channel_status(channel_type, "error", "channel instance not found")
return
if channel_type in ("weixin", "wx") and hasattr(ch, "login_status"):
login_status = getattr(ch, "login_status", "")
if login_status in ("waiting_scan", "scanned", "idle"):
logger.info(f"[CloudClient] Channel '{channel_type}' is waiting for QR login, "
"skip reporting connected")
return
success, error = ch.wait_startup(timeout=3)
if success:
logger.info(f"[CloudClient] Channel '{channel_type}' connected, reporting status")
@@ -602,6 +666,9 @@ def build_website_prompt(workspace_dir: str) -> list:
]
def start(channel, channel_mgr=None):
if not get_deployment_id():
return
global chat_client
chat_client = CloudClient(api_key=conf().get("linkai_api_key"), host=conf().get("cloud_host", ""), channel=channel)
chat_client.channel_mgr = channel_mgr

View File

@@ -1,6 +1,7 @@
# 厂商类型
OPEN_AI = "openAI"
CHATGPT = "chatGPT"
OPENAI = "openai"
CHATGPT = "chatGPT" # legacy alias for OPENAI, kept for backward compatibility
BAIDU = "baidu"
XUNFEI = "xunfei"
CHATGPTONAZURE = "chatGPTOnAzure"
@@ -68,6 +69,8 @@ GPT_5 = "gpt-5"
GPT_5_MINI = "gpt-5-mini"
GPT_5_NANO = "gpt-5-nano"
GPT_54 = "gpt-5.4" # GPT-5.4 - Agent recommended model
GPT_54_MINI = "gpt-5.4-mini"
GPT_54_NANO = "gpt-5.4-nano"
O1 = "o1-preview"
O1_MINI = "o1-mini"
WHISPER_1 = "whisper-1"
@@ -89,14 +92,16 @@ QWEN35_PLUS = "qwen3.5-plus" # Qwen3.5 Plus - Omni model (MultiModalConversatio
QWQ_PLUS = "qwq-plus"
# MiniMax
MINIMAX_M2_5 = "MiniMax-M2.5" # MiniMax M2.5 - Latest
MINIMAX_M2_1 = "MiniMax-M2.1" # MiniMax M2.1 - Agent推荐模型
MINIMAX_M2_7 = "MiniMax-M2.7" # MiniMax M2.7 - Latest
MINIMAX_M2_5 = "MiniMax-M2.5" # MiniMax M2.5
MINIMAX_M2_1 = "MiniMax-M2.1" # MiniMax M2.1
MINIMAX_M2_1_LIGHTNING = "MiniMax-M2.1-lightning" # MiniMax M2.1 极速版
MINIMAX_M2 = "MiniMax-M2" # MiniMax M2
MINIMAX_ABAB6_5 = "abab6.5-chat" # MiniMax abab6.5
# GLM (智谱AI)
GLM_5 = "glm-5" # 智谱 GLM-5 - Latest
GLM_5_TURBO = "glm-5-turbo" # 智谱 GLM-5-Turbo - Latest
GLM_5 = "glm-5" # 智谱 GLM-5
GLM_4 = "glm-4"
GLM_4_PLUS = "glm-4-plus"
GLM_4_flash = "glm-4-flash"
@@ -153,7 +158,7 @@ MODEL_LIST = [
GPT_4o, GPT_4O_0806, GPT_4o_MINI,
GPT_41, GPT_41_MINI, GPT_41_NANO,
GPT_5, GPT_5_MINI, GPT_5_NANO,
GPT_54,
GPT_54, GPT_54_MINI, GPT_54_NANO,
O1, O1_MINI,
# DeepSeek
@@ -163,10 +168,10 @@ MODEL_LIST = [
QWEN, QWEN_TURBO, QWEN_PLUS, QWEN_MAX, QWEN_LONG, QWEN3_MAX, QWEN35_PLUS,
# MiniMax
MiniMax, MINIMAX_M2_5, MINIMAX_M2_1, MINIMAX_M2_1_LIGHTNING, MINIMAX_M2, MINIMAX_ABAB6_5,
MiniMax, MINIMAX_M2_7, MINIMAX_M2_5, MINIMAX_M2_1, MINIMAX_M2_1_LIGHTNING, MINIMAX_M2, MINIMAX_ABAB6_5,
# GLM
ZHIPU_AI, GLM_5, GLM_4, GLM_4_PLUS, GLM_4_flash, GLM_4_LONG, GLM_4_ALLTOOLS,
ZHIPU_AI, GLM_5_TURBO, GLM_5, GLM_4, GLM_4_PLUS, GLM_4_flash, GLM_4_LONG, GLM_4_ALLTOOLS,
GLM_4_0520, GLM_4_AIR, GLM_4_AIRX, GLM_4_7,
# Kimi
@@ -188,3 +193,4 @@ FEISHU = "feishu"
DINGTALK = "dingtalk"
WECOM_BOT = "wecom_bot"
QQ = "qq"
WEIXIN = "weixin"

View File

@@ -115,3 +115,22 @@ def expand_path(path: str) -> str:
expanded = os.path.join(home, path[2:])
return expanded
def get_cloud_headers(api_key: str) -> dict:
"""
Build standard headers for LinkAI API requests,
including client_id when available.
"""
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}",
}
try:
from linkai import LinkAIClient
client_id = LinkAIClient.fetch_client_id()
if client_id:
headers["X-Client-Id"] = client_id
except Exception:
pass
return headers

View File

@@ -0,0 +1,17 @@
import inspect
from typing import Any
def websocket_app_run_forever(ws: Any, **kwargs: Any) -> None:
"""
Call WebSocketApp.run_forever; strip reconnect= if the installed
websocket-client is too old (reconnect was added in a later 1.x release).
"""
if "reconnect" in kwargs:
try:
params = inspect.signature(ws.run_forever).parameters
except (TypeError, ValueError):
params = {}
if "reconnect" not in params:
kwargs = {k: v for k, v in kwargs.items() if k != "reconnect"}
ws.run_forever(**kwargs)

View File

@@ -1,6 +1,6 @@
{
"channel_type": "web",
"model": "MiniMax-M2.5",
"channel_type": "weixin",
"model": "MiniMax-M2.7",
"minimax_api_key": "",
"zhipu_ai_api_key": "",
"ark_api_key": "",

View File

@@ -20,7 +20,7 @@ available_setting = {
"proxy": "", # openai使用的代理
# chatgpt模型 当use_azure_chatgpt为true时其名称为Azure上model deployment名称
"model": "gpt-3.5-turbo", # 可选择: gpt-4o, pt-4o-mini, gpt-4-turbo, claude-3-sonnet, wenxin, moonshot, qwen-turbo, xunfei, glm-4, minimax, gemini等模型全部可选模型详见common/const.py文件
"bot_type": "", # 可选配置使用兼容openai格式的三方服务时候需填"chatGPT"。bot具体名称详见common/const.py文件列出的bot_type如不填根据model名称判断
"bot_type": "", # 可选配置使用兼容openai格式的三方服务时候需填"openai"(历史值"chatGPT"仍兼容)。bot具体名称详见common/const.py文件如不填根据model名称判断
"use_azure_chatgpt": False, # 是否使用azure的chatgpt
"azure_deployment_id": "", # azure 模型部署名称
"azure_api_version": "", # azure api版本
@@ -153,10 +153,15 @@ available_setting = {
# 企微智能机器人配置(长连接模式)
"wecom_bot_id": "", # 企微智能机器人BotID
"wecom_bot_secret": "", # 企微智能机器人长连接Secret
# 微信配置
"weixin_token": "", # 微信登录后获取的bot_token留空则启动时自动扫码登录
"weixin_base_url": "https://ilinkai.weixin.qq.com", # Weixin ilink API base URL
"weixin_cdn_base_url": "https://novac2c.cdn.weixin.qq.com/c2c", # CDN base URL
"weixin_credentials_path": "~/.weixin_cow_credentials.json", # credentials file path
# chatgpt指令自定义触发词
"clear_memory_commands": ["#清除记忆"], # 重置会话指令,必须以#开头
# channel配置
"channel_type": "", # 通道类型,支持多渠道同时运行。单个: "feishu",多个: "feishu, dingtalk" 或 ["feishu", "dingtalk"]。可选值: web,feishu,dingtalk,wecom_bot,wechatmp,wechatmp_service,wechatcom_app
"channel_type": "", # 通道类型,支持多渠道同时运行。单个: "feishu",多个: "feishu, dingtalk" 或 ["feishu", "dingtalk"]。可选值: web,feishu,dingtalk,wecom_bot,weixin,wechatmp,wechatmp_service,wechatcom_app
"web_console": True, # 是否自动启动Web控制台默认启动。设为False可禁用
"subscribe_msg": "", # 订阅消息, 支持: wechatmp, wechatmp_service, wechatcom_app
"debug": False, # 是否开启debug模式开启后会打印更多日志
@@ -382,7 +387,8 @@ def load_config():
"wechatcomapp_agent_id": "WECHATCOMAPP_AGENT_ID",
"wechatcomapp_secret": "WECHATCOMAPP_SECRET",
"qq_app_id": "QQ_APP_ID",
"qq_app_secret": "QQ_APP_SECRET"
"qq_app_secret": "QQ_APP_SECRET",
"weixin_token": "WEIXIN_TOKEN",
}
injected = 0
for conf_key, env_key in _CONFIG_TO_ENV.items():

View File

@@ -25,11 +25,11 @@ WORKDIR ${BUILD_PREFIX}
ADD docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh \
&& mkdir -p /home/noroot \
&& groupadd -r noroot \
&& useradd -r -g noroot -s /bin/bash -d /home/noroot noroot \
&& chown -R noroot:noroot /home/noroot ${BUILD_PREFIX} /usr/local/lib
&& mkdir -p /home/agent/cow \
&& groupadd -r agent \
&& useradd -r -g agent -s /bin/bash -d /home/agent agent \
&& chown -R agent:agent /home/agent ${BUILD_PREFIX} /usr/local/lib
USER noroot
USER agent
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -5,22 +5,39 @@ 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'
CHANNEL_TYPE: 'weixin'
MODEL: 'MiniMax-M2.7'
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
volumes:
- ./cow:/home/agent/cow

View File

@@ -127,7 +127,7 @@ Agent可根据智能体的名称和描述进行决策并通过 app_code 调
在命令行中执行:
```bash
bash <(curl -sS https://cdn.link-ai.tech/code/cow/run.sh)
bash <(curl -fsSL https://cdn.link-ai.tech/code/cow/run.sh)
```
详细说明及后续程序管理参考:[项目启动脚本](https://github.com/zhayujie/chatgpt-on-wechat/wiki/CowAgentQuickStart)
@@ -137,8 +137,8 @@ bash <(curl -sS https://cdn.link-ai.tech/code/cow/run.sh)
Agent模式推荐使用以下模型可根据效果及成本综合选择
- **MiniMax**: `MiniMax-M2.5`
- **GLM**: `glm-5`
- **MiniMax**: `MiniMax-M2.7`
- **GLM**: `glm-5-turbo`
- **Kimi**: `kimi-k2.5`
- **Doubao**: `doubao-seed-2-0-code-preview-260215`
- **Qwen**: `qwen3.5-plus`

74
docs/channels/weixin.mdx Normal file
View File

@@ -0,0 +1,74 @@
---
title: 微信
description: 将 CowAgent 接入个人微信
---
> 接入个人微信,扫码登录即可使用,支持文本、图片、语音、文件、视频等消息的收发。
## 一、配置和运行
### 方式一Web 控制台接入
启动 Cow 项目后打开 Web 控制台 (本地链接为: http://127.0.0.1:9899/ ),选择 **通道** 菜单,点击 **接入通道**,选择 **微信**,点击接入后按照提示扫码登录。
<img src="https://cdn.link-ai.tech/doc/20260322195114.png" width="800" />
### 方式二:配置文件接入
在 `config.json` 中设置 `channel_type` 为 `weixin`
```json
{
"channel_type": "weixin"
}
```
启动程序后,终端会显示二维码,使用微信扫码授权即可完成登录。
<img src="https://cdn.link-ai.tech/doc/20260322195509.png" width="800" />
<Note>
1. 兼容历史配置:`channel_type` 设为 `wx` 同样可以启用微信通道。
2. 注意微信客户端需要更新至 8.0.69 版本或以上
</Note>
## 二、使用说明
微信扫码并进行授权确认后,即可完成接入并开始对话。接入微信后会在对话中创建出一个机器人助理,不会对已有账号的正常使用有任何影响。
> 你可以通过搜索"微信ClawBot"随时找到这个机器人,还可以修改这个机器人的头像、备注等信息,将机器人置顶在消息列表等。
<img src="https://cdn.link-ai.tech/doc/83ae8251d896219fde4803f4205205be.jpg" width="250" />
## 三、登录说明
### 扫码登录
首次启动时,终端会显示一个二维码(有效期约 2 分钟)。使用微信扫描二维码并在手机上确认后即可完成登录。
- 二维码过期后会自动刷新并重新显示
- `requirements.txt` 中已默认包含 `qrcode` 依赖,安装后可在终端直接渲染二维码图案
### 凭证保存
登录成功后,凭证会自动保存至 `~/.weixin_cow_credentials.json`,下次启动时无需重新扫码。
如需重新登录,删除该凭证文件后重启程序即可。
### Session 过期
当微信 session 过期时errcode -14程序会自动清除旧凭证并重新发起扫码登录无需手动干预。
## 四、功能说明
| 功能 | 支持情况 |
| --- | --- |
| 单聊 | ✅ |
| 文本消息 | ✅ 收发 |
| 图片消息 | ✅ 收发 |
| 文件消息 | ✅ 收发 |
| 视频消息 | ✅ 收发 |
| 语音消息 | ✅ 接收 (自带语音识别) |

View File

@@ -59,7 +59,8 @@
"group": "安装部署",
"pages": [
"guide/quick-start",
"guide/manual-install"
"guide/manual-install",
"guide/upgrade"
]
}
]
@@ -80,7 +81,8 @@
"models/gemini",
"models/openai",
"models/deepseek",
"models/linkai"
"models/linkai",
"models/coding-plan"
]
}
]
@@ -153,6 +155,7 @@
{
"group": "接入渠道",
"pages": [
"channels/weixin",
"channels/web",
"channels/feishu",
"channels/dingtalk",
@@ -171,6 +174,8 @@
"group": "发布记录",
"pages": [
"releases/overview",
"releases/v2.0.4",
"releases/v2.0.3",
"releases/v2.0.2",
"releases/v2.0.1",
"releases/v2.0.0"
@@ -224,7 +229,8 @@
"en/models/gemini",
"en/models/openai",
"en/models/deepseek",
"en/models/linkai"
"en/models/linkai",
"en/models/coding-plan"
]
}
]
@@ -297,6 +303,7 @@
{
"group": "Platforms",
"pages": [
"en/channels/weixin",
"en/channels/web",
"en/channels/feishu",
"en/channels/dingtalk",
@@ -315,6 +322,7 @@
"group": "Release Notes",
"pages": [
"en/releases/overview",
"en/releases/v2.0.4",
"en/releases/v2.0.2",
"en/releases/v2.0.1",
"en/releases/v2.0.0"
@@ -323,6 +331,156 @@
]
}
]
},
{
"language": "ja",
"tabs": [
{
"tab": "紹介",
"groups": [
{
"group": "概要",
"pages": [
"ja/intro/index",
"ja/intro/architecture",
"ja/intro/features"
]
}
]
},
{
"tab": "クイックスタート",
"groups": [
{
"group": "インストール",
"pages": [
"ja/guide/quick-start",
"ja/guide/manual-install",
"ja/guide/upgrade"
]
}
]
},
{
"tab": "モデル",
"groups": [
{
"group": "モデル設定",
"pages": [
"ja/models/index",
"ja/models/minimax",
"ja/models/glm",
"ja/models/qwen",
"ja/models/kimi",
"ja/models/doubao",
"ja/models/claude",
"ja/models/gemini",
"ja/models/openai",
"ja/models/deepseek",
"ja/models/linkai",
"ja/models/coding-plan"
]
}
]
},
{
"tab": "ツール",
"groups": [
{
"group": "ツールシステム",
"pages": [
"ja/tools/index"
]
},
{
"group": "内蔵ツール",
"pages": [
"ja/tools/read",
"ja/tools/write",
"ja/tools/edit",
"ja/tools/ls",
"ja/tools/bash",
"ja/tools/send",
"ja/tools/memory",
"ja/tools/env-config",
"ja/tools/browser"
]
},
{
"group": "オプションツール",
"pages": [
"ja/tools/web-search",
"ja/tools/scheduler"
]
}
]
},
{
"tab": "スキル",
"groups": [
{
"group": "スキルシステム",
"pages": [
"ja/skills/index",
"ja/skills/skill-creator"
]
},
{
"group": "内蔵スキル",
"pages": [
"ja/skills/image-vision",
"ja/skills/linkai-agent",
"ja/skills/web-fetch"
]
}
]
},
{
"tab": "メモリ",
"groups": [
{
"group": "メモリシステム",
"pages": [
"ja/memory"
]
}
]
},
{
"tab": "チャネル",
"groups": [
{
"group": "プラットフォーム",
"pages": [
"ja/channels/weixin",
"ja/channels/web",
"ja/channels/feishu",
"ja/channels/dingtalk",
"ja/channels/wecom-bot",
"ja/channels/qq",
"ja/channels/wecom",
"ja/channels/wechatmp"
]
}
]
},
{
"tab": "リリース",
"groups": [
{
"group": "リリースノート",
"pages": [
"ja/releases/overview",
"ja/releases/v2.0.4",
"ja/releases/v2.0.3",
"ja/releases/v2.0.2",
"ja/releases/v2.0.1",
"ja/releases/v2.0.0"
]
}
]
}
]
}
]
}

View File

@@ -4,15 +4,16 @@
<a href="https://github.com/zhayujie/chatgpt-on-wechat/releases/latest"><img src="https://img.shields.io/github/v/release/zhayujie/chatgpt-on-wechat" alt="Latest release"></a>
<a href="https://github.com/zhayujie/chatgpt-on-wechat/blob/master/LICENSE"><img src="https://img.shields.io/github/license/zhayujie/chatgpt-on-wechat" alt="License: MIT"></a>
<a href="https://github.com/zhayujie/chatgpt-on-wechat"><img src="https://img.shields.io/github/stars/zhayujie/chatgpt-on-wechat?style=flat-square" alt="Stars"></a> <br/>
[<a href="https://github.com/zhayujie/chatgpt-on-wechat/blob/master/README.md">中文</a>] | [English]
[<a href="https://github.com/zhayujie/chatgpt-on-wechat/blob/master/README.md">中文</a>] | [English] | [<a href="https://github.com/zhayujie/chatgpt-on-wechat/blob/master/docs/ja/README.md">日本語</a>]
</p>
**CowAgent** is an AI super assistant powered by LLMs, capable of autonomous task planning, operating computers and external resources, creating and executing Skills, and continuously growing with long-term memory. It supports flexible model switching, handles text, voice, images, and files, and can be integrated into Web, Feishu, DingTalk, WeCom Bot, WeCom App, and WeChat Official Account — running 7×24 hours on your personal computer or server.
**CowAgent** is an AI super assistant powered by LLMs, capable of autonomous task planning, operating computers and external resources, creating and executing Skills, and continuously growing with long-term memory. It supports flexible model switching, handles text, voice, images, and files, and can be integrated into WeChat, Web, Feishu, DingTalk, WeCom Bot, WeCom App, and WeChat Official Account — running 7×24 hours on your personal computer or server.
<p align="center">
<a href="https://cowagent.ai/">🌐 Website</a> &nbsp;·&nbsp;
<a href="https://docs.cowagent.ai/en/intro/index">📖 Docs</a> &nbsp;·&nbsp;
<a href="https://docs.cowagent.ai/en/guide/quick-start">🚀 Quick Start</a>
<a href="https://docs.cowagent.ai/en/guide/quick-start">🚀 Quick Start</a> &nbsp;·&nbsp;
<a href="https://link-ai.tech/cowagent/create">☁️ Try Online</a>
</p>
## Introduction
@@ -24,7 +25,7 @@
-**Skills System**: Implements a Skills creation and execution engine with multiple built-in skills, and supports custom Skills development through natural language conversation.
-**Multimodal Messages**: Supports parsing, processing, generating, and sending text, images, voice, files, and other message types.
-**Multiple Model Support**: Supports OpenAI, Claude, Gemini, DeepSeek, MiniMax, GLM, Qwen, Kimi, Doubao, and other mainstream model providers.
-**Multi-platform Deployment**: Runs on local computers or servers, integrable into Web, Feishu, DingTalk, WeChat Official Account, and WeCom applications.
-**Multi-platform Deployment**: Runs on local computers or servers, integrable into WeChat, Web, Feishu, DingTalk, WeChat Official Account, and WeCom applications.
-**Knowledge Base**: Integrates enterprise knowledge base capabilities via the [LinkAI](https://link-ai.tech) platform.
## Disclaimer
@@ -33,6 +34,10 @@
2. Agent mode consumes more tokens than normal chat mode. Choose models based on effectiveness and cost. Agent has access to the host OS — please deploy in trusted environments.
3. CowAgent focuses on open-source development and does not participate in, authorize, or issue any cryptocurrency.
## Demo
Try online (no deployment needed): [CowAgent](https://link-ai.tech/cowagent/create)
## Changelog
> **2026.02.27:** [v2.0.2](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/2.0.2) — Web console overhaul (streaming chat, model/skill/memory/channel/scheduler/log management), multi-channel concurrent running, session persistence, new models including Gemini 3.1 Pro / Claude 4.6 Sonnet / Qwen3.5 Plus.
@@ -56,7 +61,7 @@ Full changelog: [Release Notes](https://docs.cowagent.ai/en/releases/overview)
The project provides a one-click script for installation, configuration, startup, and management:
```bash
bash <(curl -sS https://cdn.link-ai.tech/code/cow/run.sh)
bash <(curl -fsSL https://cdn.link-ai.tech/code/cow/run.sh)
```
After running, the Web service starts by default. Access `http://localhost:9899/chat` to chat.
@@ -102,7 +107,7 @@ nohup python3 app.py & tail -f nohup.out
### Docker Deployment
```bash
wget https://cdn.link-ai.tech/code/cow/docker-compose.yml
curl -O https://cdn.link-ai.tech/code/cow/docker-compose.yml
# Edit docker-compose.yml with your config
sudo docker compose up -d
sudo docker logs -f chatgpt-on-wechat
@@ -116,8 +121,8 @@ Supports mainstream model providers. Recommended models for Agent mode:
| Provider | Recommended Model |
| --- | --- |
| MiniMax | `MiniMax-M2.5` |
| GLM | `glm-5` |
| MiniMax | `MiniMax-M2.7` |
| GLM | `glm-5-turbo` |
| Kimi | `kimi-k2.5` |
| Doubao | `doubao-seed-2-0-code-preview-260215` |
| Qwen | `qwen3.5-plus` |
@@ -128,6 +133,28 @@ Supports mainstream model providers. Recommended models for Agent mode:
For detailed configuration of each model, see the [Models documentation](https://docs.cowagent.ai/en/models/index).
### Coding Plan
Coding Plan is a monthly subscription package offered by various providers, ideal for high-frequency Agent usage. All providers can be accessed via OpenAI-compatible mode:
```json
{
"bot_type": "openai",
"model": "MODEL_NAME",
"open_ai_api_base": "PROVIDER_CODING_PLAN_API_BASE",
"open_ai_api_key": "YOUR_API_KEY"
}
```
- `bot_type`: Must be `openai`
- `model`: Model name supported by the provider
- `open_ai_api_base`: Provider's Coding Plan API Base (different from standard pay-as-you-go)
- `open_ai_api_key`: Provider's Coding Plan API Key
> Note: Coding Plan API Base and API Key are usually separate from standard pay-as-you-go ones. Please obtain them from each provider's platform.
Supported providers include Alibaba Cloud, MiniMax, Zhipu GLM, Kimi, Volcengine, and more. For detailed configuration of each provider, see the [Coding Plan documentation](https://docs.cowagent.ai/en/models/coding-plan).
<br/>
## Channels
@@ -136,6 +163,7 @@ Supports multiple platforms. Set `channel_type` in `config.json` to switch:
| Channel | `channel_type` | Docs |
| --- | --- | --- |
| WeChat | `weixin` | [WeChat Setup](https://docs.cowagent.ai/en/channels/weixin) |
| Web (default) | `web` | [Web Channel](https://docs.cowagent.ai/en/channels/web) |
| Feishu | `feishu` | [Feishu Setup](https://docs.cowagent.ai/en/channels/feishu) |
| DingTalk | `dingtalk` | [DingTalk Setup](https://docs.cowagent.ai/en/channels/dingtalk) |

View File

@@ -0,0 +1,72 @@
---
title: WeChat
description: Connect CowAgent to personal WeChat
---
> Connect CowAgent to your personal WeChat. Simply scan a QR code to log in — no public IP required. Supports text, image, voice, file, and video messages.
## 1. Configuration
### Option A: Web Console
Start the program and open the Web console (local access: http://127.0.0.1:9899). Go to the **Channels** tab, click **Connect Channel**, select **WeChat**, and follow the prompts to scan the QR code.
### Option B: Config File
Set `channel_type` to `weixin` in your `config.json`:
```json
{
"channel_type": "weixin"
}
```
After starting the program, a QR code will be displayed in the terminal. Scan it with WeChat and confirm on your phone to complete login.
<Note>
For backward compatibility, setting `channel_type` to `wx` also activates the WeChat channel.
</Note>
## 2. Parameters
| Parameter | Description | Default |
| --- | --- | --- |
| `channel_type` | Set to `weixin` or `wx` | — |
Login credentials are automatically saved to `~/.weixin_cow_credentials.json`. To force a re-login, delete this file and restart.
## 3. Login
### QR Code Login
On first startup, a QR code is displayed in the terminal (valid for approximately 2 minutes). Scan it with WeChat and confirm on your phone.
- The QR code automatically refreshes when it expires
- The `qrcode` dependency is already included in `requirements.txt`, enabling QR code rendering directly in the terminal
### Credential Persistence
After successful login, credentials are saved to `~/.weixin_cow_credentials.json`. Subsequent startups will reuse the saved credentials without requiring a new scan.
To force a re-login, delete the credentials file and restart the program.
### Session Expiry
When the WeChat session expires (errcode -14), the program automatically clears old credentials and initiates a new QR login — no manual intervention required.
## 4. Supported Features
| Feature | Status |
| --- | --- |
| Direct Messages | ✅ |
| Text Messages | ✅ Send & Receive |
| Image Messages | ✅ Send & Receive |
| File Messages | ✅ Send & Receive |
| Video Messages | ✅ Send & Receive |
| Voice Messages | ✅ Receive |
## 5. Notes
1. Ensure network access to `ilinkai.weixin.qq.com`.
2. Media files (images, files, videos) are transferred via CDN with AES-128-ECB encryption, handled automatically by the program.
3. A stable network connection is recommended to avoid frequent disconnections that would require re-scanning.

View File

@@ -67,7 +67,7 @@ Docker deployment does not require cloning source code or installing dependencie
**1. Download config**
```bash
wget https://cdn.link-ai.tech/code/cow/docker-compose.yml
curl -O https://cdn.link-ai.tech/code/cow/docker-compose.yml
```
Edit `docker-compose.yml` with your configuration.

View File

@@ -10,7 +10,7 @@ Supports Linux, macOS, and Windows. Requires Python 3.7-3.12 (3.9 recommended).
## Install Command
```bash
bash <(curl -sS https://cdn.link-ai.tech/code/cow/run.sh)
bash <(curl -fsSL https://cdn.link-ai.tech/code/cow/run.sh)
```
The script automatically performs these steps:

View File

@@ -7,7 +7,7 @@ description: CowAgent - AI Super Assistant powered by LLMs
**CowAgent** is an AI super assistant powered by LLMs with autonomous task planning, long-term memory, skills system, multimodal messages, multiple model support, and multi-platform deployment.
CowAgent can proactively think and plan tasks, operate computers and external resources, create and execute Skills, and continuously grow with long-term memory. It supports flexible switching between multiple models, handles text, voice, images, files and other multimodal messages, and can be integrated into web, Feishu, DingTalk, WeCom, and WeChat Official Account. It runs 7x24 hours on your personal computer or server.
CowAgent can proactively think and plan tasks, operate computers and external resources, create and execute Skills, and continuously grow with long-term memory. It supports flexible switching between multiple models, handles text, voice, images, files and other multimodal messages, and can be integrated into WeChat, web, Feishu, DingTalk, WeCom, and WeChat Official Account. It runs 7x24 hours on your personal computer or server.
<Card title="GitHub" icon="github" href="https://github.com/zhayujie/chatgpt-on-wechat">
github.com/zhayujie/chatgpt-on-wechat
@@ -31,8 +31,8 @@ CowAgent can proactively think and plan tasks, operate computers and external re
<Card title="Multiple Model Support" icon="microchip" href="/en/models/index">
Supports mainstream model providers including OpenAI, Claude, Gemini, DeepSeek, MiniMax, GLM, Qwen, Kimi, Doubao, and more.
</Card>
<Card title="Multi-platform Deployment" icon="server" href="/en/channels/web">
Runs on local computers or servers, integrable into web, Feishu, DingTalk, WeChat Official Account, and WeCom applications.
<Card title="Multi-platform Deployment" icon="server" href="/en/channels/weixin">
Runs on local computers or servers, integrable into WeChat, web, Feishu, DingTalk, WeChat Official Account, and WeCom applications.
</Card>
</CardGroup>
@@ -41,7 +41,7 @@ CowAgent can proactively think and plan tasks, operate computers and external re
Run the following command in your terminal for one-click install, configuration, and startup:
```bash
bash <(curl -sS https://cdn.link-ai.tech/code/cow/run.sh)
bash <(curl -fsSL https://cdn.link-ai.tech/code/cow/run.sh)
```
By default, the Web service starts after running. Access `http://localhost:9899/chat` to chat in the web interface.

View File

@@ -0,0 +1,139 @@
---
title: Coding Plan
description: Coding Plan model configuration
---
> Coding Plan is a monthly subscription package offered by various providers, ideal for high-frequency Agent usage. CowAgent supports all Coding Plan providers via OpenAI-compatible mode.
<Note>
Coding Plan API Base and API Key are usually separate from the standard pay-as-you-go ones. Please obtain them from each provider's platform.
</Note>
## General Configuration
All providers can be accessed via the OpenAI-compatible protocol, and can be quickly configured through the web console. Set the model provider to **OpenAI**, select a custom model and enter the model code, then fill in the corresponding provider's API Base and API Key:
<img src="https://cdn.link-ai.tech/doc/20260318113134.png" width="800"/>
You can also configure directly in `config.json`:
```json
{
"bot_type": "openai",
"model": "MODEL_NAME",
"open_ai_api_base": "PROVIDER_CODING_PLAN_API_BASE",
"open_ai_api_key": "YOUR_API_KEY"
}
```
| Parameter | Description |
| --- | --- |
| `bot_type` | Must be `openai` (OpenAI-compatible mode) |
| `model` | Model name supported by the provider |
| `open_ai_api_base` | Provider's Coding Plan API Base URL |
| `open_ai_api_key` | Provider's Coding Plan API Key |
---
## Alibaba Cloud
```json
{
"bot_type": "openai",
"model": "qwen3.5-plus",
"open_ai_api_base": "https://coding.dashscope.aliyuncs.com/v1",
"open_ai_api_key": "YOUR_API_KEY"
}
```
| Parameter | Description |
| --- | --- |
| `model` | `qwen3.5-plus`, `qwen3-max-2026-01-23`, `qwen3-coder-next`, `qwen3-coder-plus`, `glm-5`, `glm-4.7`, `kimi-k2.5`, `MiniMax-M2.5` |
| `open_ai_api_base` | `https://coding.dashscope.aliyuncs.com/v1` |
| `open_ai_api_key` | Coding Plan specific key (not shared with pay-as-you-go) |
Reference: [Quick Start](https://help.aliyun.com/zh/model-studio/coding-plan-quickstart?spm=a2c4g.11186623.help-menu-2400256.d_0_2_1.70115203zi5Igc), [Model List](https://help.aliyun.com/zh/model-studio/coding-plan)
---
## MiniMax
```json
{
"bot_type": "openai",
"model": "MiniMax-M2.5",
"open_ai_api_base": "https://api.minimaxi.com/v1",
"open_ai_api_key": "YOUR_API_KEY"
}
```
| Parameter | Description |
| --- | --- |
| `model` | `MiniMax-M2.5`, `MiniMax-M2.5-highspeed`, `MiniMax-M2.1`, `MiniMax-M2` |
| `open_ai_api_base` | China: `https://api.minimaxi.com/v1`; Global: `https://api.minimax.io/v1` |
| `open_ai_api_key` | Coding Plan specific key (not shared with pay-as-you-go) |
Reference: [China Key](https://platform.minimaxi.com/docs/coding-plan/quickstart), [Model List](https://platform.minimaxi.com/docs/guides/pricing-coding-plan), [Global Key](https://platform.minimax.io/docs/coding-plan/quickstart)
---
## Zhipu GLM
```json
{
"bot_type": "openai",
"model": "glm-4.7",
"open_ai_api_base": "https://open.bigmodel.cn/api/coding/paas/v4",
"open_ai_api_key": "YOUR_API_KEY"
}
```
| Parameter | Description |
| --- | --- |
| `model` | `glm-5`, `glm-4.7`, `glm-4.6`, `glm-4.5`, `glm-4.5-air` |
| `open_ai_api_base` | China: `https://open.bigmodel.cn/api/coding/paas/v4`; Global: `https://api.z.ai/api/coding/paas/v4` |
| `open_ai_api_key` | Shared with standard API |
Reference: [China Quick Start](https://docs.bigmodel.cn/cn/coding-plan/quick-start), [Global Quick Start](https://docs.z.ai/devpack/quick-start)
---
## Kimi
```json
{
"bot_type": "openai",
"model": "kimi-for-coding",
"open_ai_api_base": "https://api.kimi.com/coding/v1",
"open_ai_api_key": "YOUR_API_KEY"
}
```
| Parameter | Description |
| --- | --- |
| `model` | `kimi-for-coding` |
| `open_ai_api_base` | `https://api.kimi.com/coding/v1` |
| `open_ai_api_key` | Coding Plan specific key (not shared with pay-as-you-go) |
Reference: [Key & Docs](https://www.kimi.com/code/docs/)
---
## Volcengine
```json
{
"bot_type": "openai",
"model": "Doubao-Seed-2.0-Code",
"open_ai_api_base": "https://ark.cn-beijing.volces.com/api/coding/v3",
"open_ai_api_key": "YOUR_API_KEY"
}
```
| Parameter | Description |
| --- | --- |
| `model` | `Doubao-Seed-2.0-Code`, `Doubao-Seed-2.0-pro`, `Doubao-Seed-2.0-lite`, `Doubao-Seed-Code`, `MiniMax-M2.5`, `Kimi-K2.5`, `GLM-4.7`, `DeepSeek-V3.2` |
| `open_ai_api_base` | `https://ark.cn-beijing.volces.com/api/coding/v3` |
| `open_ai_api_key` | Shared with standard API |
Reference: [Quick Start](https://www.volcengine.com/docs/82379/1928261?lang=zh)

View File

@@ -8,7 +8,7 @@ Use OpenAI-compatible configuration:
```json
{
"model": "deepseek-chat",
"bot_type": "chatGPT",
"bot_type": "openai",
"open_ai_api_key": "YOUR_API_KEY",
"open_ai_api_base": "https://api.deepseek.com/v1"
}
@@ -17,6 +17,6 @@ Use OpenAI-compatible configuration:
| Parameter | Description |
| --- | --- |
| `model` | `deepseek-chat` (DeepSeek-V3), `deepseek-reasoner` (DeepSeek-R1) |
| `bot_type` | Must be `chatGPT` (OpenAI-compatible mode) |
| `bot_type` | Must be `openai` (OpenAI-compatible mode) |
| `open_ai_api_key` | Create at [DeepSeek Platform](https://platform.deepseek.com/api_keys) |
| `open_ai_api_base` | DeepSeek platform BASE URL |

View File

@@ -5,22 +5,22 @@ description: Zhipu AI GLM model configuration
```json
{
"model": "glm-5",
"model": "glm-5-turbo",
"zhipu_ai_api_key": "YOUR_API_KEY"
}
```
| Parameter | Description |
| --- | --- |
| `model` | Options include `glm-5`, `glm-4.7`, `glm-4-plus`, `glm-4-flash`, `glm-4-air`, etc. See [model codes](https://bigmodel.cn/dev/api/normal-model/glm-4) |
| `model` | Options include `glm-5-turbo`, `glm-5`, `glm-4.7`, `glm-4-plus`, `glm-4-flash`, `glm-4-air`, etc. See [model codes](https://bigmodel.cn/dev/api/normal-model/glm-4) |
| `zhipu_ai_api_key` | Create at [Zhipu AI Console](https://www.bigmodel.cn/usercenter/proj-mgmt/apikeys) |
OpenAI-compatible configuration is also supported:
```json
{
"bot_type": "chatGPT",
"model": "glm-5",
"bot_type": "openai",
"model": "glm-5-turbo",
"open_ai_api_base": "https://open.bigmodel.cn/api/paas/v4",
"open_ai_api_key": "YOUR_API_KEY"
}

View File

@@ -6,12 +6,12 @@ description: Supported models and recommended choices for CowAgent
CowAgent supports mainstream LLMs from domestic and international providers. Model interfaces are implemented in the project's `models/` directory.
<Note>
For Agent mode, the following models are recommended based on quality and cost: MiniMax-M2.5, glm-5, kimi-k2.5, qwen3.5-plus, claude-sonnet-4-6, gemini-3.1-pro-preview
For Agent mode, the following models are recommended based on quality and cost: MiniMax-M2.7, glm-5-turbo, kimi-k2.5, qwen3.5-plus, claude-sonnet-4-6, gemini-3.1-pro-preview
</Note>
## Configuration
Configure the model name and API key in `config.json` according to your chosen model. Each model also supports OpenAI-compatible access by setting `bot_type` to `chatGPT` and configuring `open_ai_api_base` and `open_ai_api_key`.
Configure the model name and API key in `config.json` according to your chosen model. Each model also supports OpenAI-compatible access by setting `bot_type` to `openai` and configuring `open_ai_api_base` and `open_ai_api_key`.
You can also use the [LinkAI](https://link-ai.tech) platform interface to flexibly switch between multiple models with support for knowledge base, workflows, and other Agent capabilities.
@@ -19,10 +19,10 @@ You can also use the [LinkAI](https://link-ai.tech) platform interface to flexib
<CardGroup cols={2}>
<Card title="MiniMax" href="/en/models/minimax">
MiniMax-M2.5 and other series models
MiniMax-M2.7 and other series models
</Card>
<Card title="GLM (Zhipu AI)" href="/en/models/glm">
glm-5, glm-4.7 and other series models
glm-5-turbo, glm-5 and other series models
</Card>
<Card title="Qwen (Tongyi Qianwen)" href="/en/models/qwen">
qwen3.5-plus, qwen3-max and more

View File

@@ -19,7 +19,7 @@ OpenAI-compatible configuration is also supported:
```json
{
"bot_type": "chatGPT",
"bot_type": "openai",
"model": "kimi-k2.5",
"open_ai_api_base": "https://api.moonshot.cn/v1",
"open_ai_api_key": "YOUR_API_KEY"

View File

@@ -5,22 +5,22 @@ description: MiniMax model configuration
```json
{
"model": "MiniMax-M2.5",
"model": "MiniMax-M2.7",
"minimax_api_key": "YOUR_API_KEY"
}
```
| Parameter | Description |
| --- | --- |
| `model` | Options include `MiniMax-M2.5`, `MiniMax-M2.1`, `MiniMax-M2.1-lightning`, `MiniMax-M2`, etc. |
| `model` | Options include `MiniMax-M2.7`, `MiniMax-M2.5`, `MiniMax-M2.1`, `MiniMax-M2.1-lightning`, `MiniMax-M2`, etc. |
| `minimax_api_key` | Create at [MiniMax Console](https://platform.minimaxi.com/user-center/basic-information/interface-key) |
OpenAI-compatible configuration is also supported:
```json
{
"bot_type": "chatGPT",
"model": "MiniMax-M2.5",
"bot_type": "openai",
"model": "MiniMax-M2.7",
"open_ai_api_base": "https://api.minimaxi.com/v1",
"open_ai_api_key": "YOUR_API_KEY"
}

View File

@@ -16,4 +16,4 @@ description: OpenAI model configuration
| `model` | Matches the [model parameter](https://platform.openai.com/docs/models) of the OpenAI API. Supports o-series, gpt-5.4, gpt-5 series, gpt-4.1, etc. Recommended for Agent mode: `gpt-5.4` |
| `open_ai_api_key` | Create at [OpenAI Platform](https://platform.openai.com/api-keys) |
| `open_ai_api_base` | Optional. Change to use third-party proxy |
| `bot_type` | Not required for official OpenAI models. Set to `chatGPT` when using Claude or other non-OpenAI models via proxy |
| `bot_type` | Not required for official OpenAI models. Set to `openai` when using Claude or other non-OpenAI models via proxy |

View File

@@ -19,7 +19,7 @@ OpenAI-compatible configuration is also supported:
```json
{
"bot_type": "chatGPT",
"bot_type": "openai",
"model": "qwen3.5-plus",
"open_ai_api_base": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"open_ai_api_key": "YOUR_API_KEY"

View File

@@ -5,6 +5,7 @@ description: CowAgent version history
| Version | Date | Description |
| --- | --- | --- |
| [2.0.4](/en/releases/v2.0.4) | 2026.03.22 | Personal WeChat channel, new model support, Japanese docs, script refactoring and bug fixes |
| [2.0.2](/en/releases/v2.0.2) | 2026.02.27 | Web Console upgrade, multi-channel concurrency, session persistence |
| [2.0.1](/en/releases/v2.0.1) | 2026.02.27 | Built-in Web Search tool, smart context management, multiple fixes |
| [2.0.0](/en/releases/v2.0.0) | 2026.02.03 | Full upgrade to AI super assistant |

View File

@@ -0,0 +1,55 @@
---
title: v2.0.4
description: CowAgent 2.0.4 - Personal WeChat channel, new model support, Japanese docs, script refactoring and bug fixes
---
## 🔌 Personal WeChat Channel
Added personal WeChat (`weixin`) channel — the most important update in this release. Simply scan a QR code to connect CowAgent to your personal WeChat account, with support for:
- **Messaging**: Send and receive text, image, file, and video messages; receive voice messages
- **QR Code Login**: QR code displayed in terminal, scan with WeChat to log in; auto-refresh on expiry
- **Credential Persistence**: Login credentials saved to `~/.weixin_cow_credentials.json` automatically, no re-scan needed on restart
- **Session Auto-Reconnect**: Automatically clears expired credentials and re-initiates QR code login
- **Web Console Integration**: Add WeChat channel from the Web Console with synchronized QR code login flow
- **Docker & Script Support**: Both `run.sh` and `docker-compose.yml` now support the WeChat channel
Documentation: [WeChat Channel](https://docs.cowagent.ai/channels/weixin).
Related commits: [ce89869](https://github.com/zhayujie/chatgpt-on-wechat/commit/ce89869), [a483ec0](https://github.com/zhayujie/chatgpt-on-wechat/commit/a483ec0), [c1421e0](https://github.com/zhayujie/chatgpt-on-wechat/commit/c1421e0)
## 🤖 New Models
- **MiniMax-M2.7**: Added MiniMax-M2.7 model support
- **GLM-5-Turbo**: Added Zhipu glm-5-turbo model support
Related commits: [9192f6f](https://github.com/zhayujie/chatgpt-on-wechat/commit/9192f6f)
## 🔧 Script Refactoring
- **run.sh Refactoring**: Extracted shared logic and eliminated duplication, reducing from 600+ lines to 177 lines ([49d8707](https://github.com/zhayujie/chatgpt-on-wechat/commit/49d8707))
- **Executable Permission**: Fixed `run.sh` file permission issue ([652156e](https://github.com/zhayujie/chatgpt-on-wechat/commit/652156e))
## ⚡ Improvements
- **Unified Request Headers**: Added identification headers to external requests across Agent services (Chat, Embedding, Vision, WebSearch, etc.) ([b4e711f](https://github.com/zhayujie/chatgpt-on-wechat/commit/b4e711f))
- **Auto-Repair Messages**: Enhanced message protocol fault tolerance with automatic repair of malformed message sequences ([b8b57e3](https://github.com/zhayujie/chatgpt-on-wechat/commit/b8b57e3))
## 🌍 Japanese Documentation
Added complete Japanese documentation covering getting started guide, channel integration, model configuration and other major sections. Thanks [@Ikko Ashimine](https://github.com/ikoamu)
Related commits: [5487c0b](https://github.com/zhayujie/chatgpt-on-wechat/commit/5487c0b)
## 🐛 Bug Fixes
- **WeCom Bot Compatibility**: Fixed compatibility with older `websocket-client` versions, added unified WebSocket compatibility layer ([bc7f627](https://github.com/zhayujie/chatgpt-on-wechat/commit/bc7f627))
- **run.sh PID**: Fixed process PID retrieval error in `run.sh` ([9febb07](https://github.com/zhayujie/chatgpt-on-wechat/commit/9febb07))
- **Feishu Encoding**: Fixed message and log encoding issue in Feishu channel ([7d0e156](https://github.com/zhayujie/chatgpt-on-wechat/commit/7d0e156))
- **Feishu Config**: Removed redundant `feishu_bot_name` dependency in `run.sh` ([1b5be1b](https://github.com/zhayujie/chatgpt-on-wechat/commit/1b5be1b))
## 📦 Upgrade
Run `./run.sh update` for a one-click upgrade, or manually pull the latest code and restart. See [Upgrade Guide](https://docs.cowagent.ai/guide/upgrade) for details.
**Release Date**: 2026.03.22 | [Full Changelog](https://github.com/zhayujie/chatgpt-on-wechat/compare/2.0.3...master)

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。
@@ -67,7 +71,7 @@ nohup python3 app.py & tail -f nohup.out
**1. 下载配置文件**
```bash
wget https://cdn.link-ai.tech/code/cow/docker-compose.yml
curl -O https://cdn.link-ai.tech/code/cow/docker-compose.yml
```
打开 `docker-compose.yml` 填写所需配置。
@@ -84,6 +88,10 @@ sudo docker compose up -d
sudo docker logs -f chatgpt-on-wechat
```
<Tip>
如果在服务器上部署,需要在防火墙或安全组中放行 `9899` 端口才能通过浏览器访问 Web 控制台建议仅对指定IP开放以保证安全。
</Tip>
## 核心配置项
```json

View File

@@ -10,7 +10,7 @@ description: 使用脚本一键安装和管理 CowAgent
## 安装命令
```bash
bash <(curl -sS https://cdn.link-ai.tech/code/cow/run.sh)
bash <(curl -fsSL https://cdn.link-ai.tech/code/cow/run.sh)
```
脚本自动执行以下流程:

52
docs/guide/upgrade.mdx Normal file
View File

@@ -0,0 +1,52 @@
---
title: 更新升级
description: CowAgent 的升级方式说明
---
## 脚本升级(推荐)
如果使用 `run.sh` 管理服务,执行以下命令即可一键升级:
```bash
./run.sh update
```
该命令会自动完成以下流程:
1. 停止当前运行的服务
2. 拉取最新代码
3. 重新检查依赖
4. 启动服务
## 手动升级
在项目根目录下执行:
```bash
git pull
pip3 install -r requirements.txt
```
更新完成后重启服务:
```bash
# 如果使用 run.sh 管理
./run.sh restart
# 如果使用 nohup 直接运行
kill $(ps -ef | grep app.py | grep -v grep | awk '{print $2}')
nohup python3 app.py & tail -f nohup.out
```
## Docker 升级
在 `docker-compose.yml` 所在目录下执行:
```bash
sudo docker compose pull
sudo docker compose up -d
```
<Tip>
升级前建议备份 `config.json` 配置文件。Docker 环境下如需保留数据,可通过 volume 挂载持久化工作空间目录。
</Tip>

View File

@@ -3,15 +3,20 @@ title: 项目介绍
description: CowAgent - 基于大模型的超级AI助理
---
<img src="https://cdn.link-ai.tech/doc/78c5dd674e2c828642ecc0406669fed7.png" alt="CowAgent" width="500px"/>
<img src="https://cdn.link-ai.tech/doc/78c5dd674e2c828642ecc0406669fed7.png" alt="CowAgent" width="450px"/>
**CowAgent** 是基于大模型的超级AI助理能够主动思考和任务规划、操作计算机和外部资源、创造和执行Skills、拥有长期记忆并不断成长。
CowAgent 支持灵活切换多种模型,能处理文本、语音、图片、文件等多模态消息,可接入网页、飞书、钉钉、企业微信应用、微信公众号中使用7×24小时运行于你的个人电脑或服务器中。
CowAgent 支持灵活切换多种模型,能处理文本、语音、图片、文件等多模态消息,可接入微信、飞书、钉钉、企业微信应用、微信公众号、网页中使用7×24小时运行于你的个人电脑或服务器中。
<Card title="GitHub" icon="github" href="https://github.com/zhayujie/chatgpt-on-wechat">
github.com/zhayujie/chatgpt-on-wechat
</Card>
<CardGroup cols={2}>
<Card title="GitHub" icon="github" href="https://github.com/zhayujie/chatgpt-on-wechat">
开源代码仓库,欢迎 Star 和贡献
</Card>
<Card title="免部署在线体验" icon="cloud" href="https://link-ai.tech/cowagent/create">
无需安装,立即在线体验 CowAgent
</Card>
</CardGroup>
## 核心能力
@@ -31,8 +36,8 @@ CowAgent 支持灵活切换多种模型,能处理文本、语音、图片、
<Card title="多模型接入" icon="microchip" href="/models/index">
支持 OpenAI, Claude, Gemini, DeepSeek, MiniMax, GLM, Qwen, Kimi, Doubao 等国内外主流模型厂商。
</Card>
<Card title="多端部署" icon="server" href="/channels/web">
支持运行在本地计算机或服务器,可集成到网页、飞书、钉钉、微信公众号、企业微信应用中使用。
<Card title="多端部署" icon="server" href="/channels/weixin">
支持运行在本地计算机或服务器,可集成到微信、网页、飞书、钉钉、微信公众号、企业微信应用中使用。
</Card>
</CardGroup>
@@ -41,10 +46,10 @@ CowAgent 支持灵活切换多种模型,能处理文本、语音、图片、
在终端执行以下命令,即可一键安装、配置、启动 CowAgent
```bash
bash <(curl -sS https://cdn.link-ai.tech/code/cow/run.sh)
bash <(curl -fsSL https://cdn.link-ai.tech/code/cow/run.sh)
```
运行后默认会启动 Web 服务,通过访问 `http://localhost:9899/chat` 在网页端对话
运行后默认会启动 Web 控制台,通过访问 `http://localhost:9899` 可以在网页端进行对话、配置、应用通道接入等操作
<CardGroup cols={2}>
<Card title="快速开始" icon="rocket" href="/guide/quick-start">

206
docs/ja/README.md Normal file
View File

@@ -0,0 +1,206 @@
<p align="center"><img src="https://github.com/user-attachments/assets/eca9a9ec-8534-4615-9e0f-96c5ac1d10a3" alt="CowAgent" width="550" /></p>
<p align="center">
<a href="https://github.com/zhayujie/chatgpt-on-wechat/releases/latest"><img src="https://img.shields.io/github/v/release/zhayujie/chatgpt-on-wechat" alt="Latest release"></a>
<a href="https://github.com/zhayujie/chatgpt-on-wechat/blob/master/LICENSE"><img src="https://img.shields.io/github/license/zhayujie/chatgpt-on-wechat" alt="License: MIT"></a>
<a href="https://github.com/zhayujie/chatgpt-on-wechat"><img src="https://img.shields.io/github/stars/zhayujie/chatgpt-on-wechat?style=flat-square" alt="Stars"></a> <br/>
[<a href="https://github.com/zhayujie/chatgpt-on-wechat/blob/master/README.md">中文</a>] | [<a href="https://github.com/zhayujie/chatgpt-on-wechat/blob/master/docs/en/README.md">English</a>] | [日本語]
</p>
**CowAgent** はLLMを搭載したAIスーパーアシスタントです。自律的なタスク計画、コンピュータや外部リソースの操作、Skillの作成・実行、長期記憶による継続的な成長が可能です。柔軟なモデル切り替えに対応し、テキスト・音声・画像・ファイルを処理でき、WeChat、Web、Feishu飛書、DingTalk釘釘、WeCom Bot企業微信ボット、WeComアプリ、WeChat公式アカウントに統合可能で、個人のPCやサーバー上で24時間365日稼働できます。
<p align="center">
<a href="https://cowagent.ai/">🌐 ウェブサイト</a> &nbsp;·&nbsp;
<a href="https://docs.cowagent.ai/en/intro/index">📖 ドキュメント</a> &nbsp;·&nbsp;
<a href="https://docs.cowagent.ai/en/guide/quick-start">🚀 クイックスタート</a> &nbsp;·&nbsp;
<a href="https://link-ai.tech/cowagent/create">☁️ オンラインで試す</a>
</p>
## はじめに
> CowAgentは、すぐに使えるAIスーパーアシスタントであると同時に、高い拡張性を持つAgentフレームワークでもあります。新しいモデルインターフェース、チャネル、組み込みツール、Skillシステムを拡張することで、さまざまなカスタマイズニーズに柔軟に対応できます。
-**自律的タスク計画**: 複雑なタスクを理解し、自律的に実行計画を立て、目標達成までツールを呼び出しながら継続的に思考します。ツールを通じてファイル、ターミナル、ブラウザ、スケジューラなどのシステムリソースにアクセスできます。
-**長期記憶**: 会話の記憶をローカルファイルやデータベースに自動的に永続化します。コアメモリとデイリーメモリを含み、キーワード検索やベクトル検索に対応しています。
-**Skillシステム**: Skillの作成・実行エンジンを実装しており、複数の組み込みSkillを備え、自然言語での会話を通じたカスタムSkillの開発もサポートしています。
-**マルチモーダルメッセージ**: テキスト、画像、音声、ファイルなど、さまざまなメッセージタイプの解析・処理・生成・送信に対応しています。
-**複数モデル対応**: OpenAI、Claude、Gemini、DeepSeek、MiniMax、GLM、Qwen、Kimi、Doubaoなど、主要なモデルプロバイダーに対応しています。
-**マルチプラットフォームデプロイ**: ローカルPCやサーバー上で実行でき、WeChat、Web、Feishu、DingTalk、WeChat公式アカウント、WeComアプリケーションに統合可能です。
-**ナレッジベース**: [LinkAI](https://link-ai.tech) プラットフォームを通じて、企業向けナレッジベース機能を統合できます。
## 免責事項
1. 本プロジェクトは [MIT License](/LICENSE) に基づいており、技術研究・学習を目的としています。利用者は現地の法律、規制、ポリシー、企業の社則を遵守する必要があります。違法行為や権利侵害となる利用は禁止されています。
2. Agentモードは通常のチャットモードよりも多くのトークンを消費します。効果とコストに基づいてモデルを選択してください。AgentはホストOSにアクセスできるため、信頼できる環境にデプロイしてください。
3. CowAgentはオープンソース開発に注力しており、いかなる暗号通貨の発行・参加・承認も行っていません。
## デモ
オンラインで試す(デプロイ不要): [CowAgent](https://link-ai.tech/cowagent/create)
## 更新履歴
> **2026.02.27:** [v2.0.2](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/2.0.2) — Webコンソールの全面刷新ストリーミングチャット、モデル/Skill/メモリ/チャネル/スケジューラ/ログ管理、マルチチャネル同時実行、セッション永続化、Gemini 3.1 Pro / Claude 4.6 Sonnet / Qwen3.5 Plusなど新モデル追加。
> **2026.02.13:** [v2.0.1](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/2.0.1) — 組み込みWeb検索ツール、スマートコンテキストトリミング、ランタイム情報の動的更新、Windows互換性、スケジューラのメモリ喪失やFeishu接続問題などの修正。
> **2026.02.03:** [v2.0.0](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/2.0.0) — マルチステップタスク計画、長期記憶、組み込みツール、Skillフレームワーク、新モデル、チャネル最適化を備えたAIスーパーアシスタントへの全面アップグレード。
> **2025.05.23:** [v1.7.6](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.7.6) — Webチャネル最適化、AgentMeshマルチエージェントプラグイン、Baidu TTS、claude-4-sonnet/opus対応。
> **2025.04.11:** [v1.7.5](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.7.5) — wechatferryプロトコル、DeepSeekモデル、Tencent Cloud音声、ModelScope・Gitee-AI対応。
> **2024.12.13:** [v1.7.4](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.7.4) — Gemini 2.0モデル、Webチャネル、メモリリーク修正。
全更新履歴: [リリースノート](https://docs.cowagent.ai/en/releases/overview)
<br/>
## 🚀 クイックスタート
本プロジェクトは、インストール・設定・起動・管理をワンクリックで行えるスクリプトを提供しています:
```bash
bash <(curl -fsSL https://cdn.link-ai.tech/code/cow/run.sh)
```
実行後、デフォルトでWebサービスが起動します。`http://localhost:9899/chat` にアクセスしてチャットを開始できます。
スクリプトの使い方: [ワンクリックインストール](https://docs.cowagent.ai/en/guide/quick-start)
### 手動インストール
**1. プロジェクトのクローン**
```bash
git clone https://github.com/zhayujie/chatgpt-on-wechat
cd chatgpt-on-wechat/
```
**2. 依存関係のインストール**
```bash
pip3 install -r requirements.txt
pip3 install -r requirements-optional.txt # 任意ですが推奨
```
**3. 設定**
```bash
cp config-template.json config.json
```
`config.json` にモデルのAPIキーとチャネルタイプを記入してください。詳細は[設定ドキュメント](https://docs.cowagent.ai/en/guide/manual-install)を参照してください。
**4. 実行**
```bash
python3 app.py
```
サーバーでバックグラウンド実行する場合:
```bash
nohup python3 app.py & tail -f nohup.out
```
### Dockerデプロイ
```bash
curl -O https://cdn.link-ai.tech/code/cow/docker-compose.yml
# docker-compose.yml を編集して設定を記入
sudo docker compose up -d
sudo docker logs -f chatgpt-on-wechat
```
<br/>
## モデル
主要なモデルプロバイダーに対応しています。Agentモードの推奨モデル
| プロバイダー | 推奨モデル |
| --- | --- |
| MiniMax | `MiniMax-M2.7` |
| GLM | `glm-5-turbo` |
| Kimi | `kimi-k2.5` |
| Doubao | `doubao-seed-2-0-code-preview-260215` |
| Qwen | `qwen3.5-plus` |
| Claude | `claude-sonnet-4-6` |
| Gemini | `gemini-3.1-pro-preview` |
| OpenAI | `gpt-5.4` |
| DeepSeek | `deepseek-chat` |
各モデルの詳細設定については、[モデルドキュメント](https://docs.cowagent.ai/en/models/index)を参照してください。
### Coding Plan
Coding Planは各プロバイダーが提供する月額サブスクリプションパッケージで、高頻度のAgent利用に最適です。すべてのプロバイダーはOpenAI互換モードでアクセスできます
```json
{
"bot_type": "openai",
"model": "MODEL_NAME",
"open_ai_api_base": "PROVIDER_CODING_PLAN_API_BASE",
"open_ai_api_key": "YOUR_API_KEY"
}
```
- `bot_type`: `openai` を指定
- `model`: プロバイダーがサポートするモデル名
- `open_ai_api_base`: プロバイダーのCoding Plan API Base標準の従量課金とは異なります
- `open_ai_api_key`: プロバイダーのCoding Plan APIキー
> 注意Coding PlanのAPI BaseとAPIキーは、通常の従量課金のものとは別です。各プロバイダーのプラットフォームから取得してください。
対応プロバイダーには、Alibaba Cloud、MiniMax、Zhipu GLM、Kimi、Volcengineなどがあります。各プロバイダーの詳細設定については、[Coding Planドキュメント](https://docs.cowagent.ai/en/models/coding-plan)を参照してください。
<br/>
## チャネル
複数のプラットフォームに対応しています。`config.json``channel_type` を設定して切り替えます:
| チャネル | `channel_type` | ドキュメント |
| --- | --- | --- |
| WeChat | `weixin` | [WeChat設定](https://docs.cowagent.ai/ja/channels/weixin) |
| Webデフォルト | `web` | [Webチャネル](https://docs.cowagent.ai/en/channels/web) |
| Feishu飛書 | `feishu` | [Feishu設定](https://docs.cowagent.ai/en/channels/feishu) |
| DingTalk釘釘 | `dingtalk` | [DingTalk設定](https://docs.cowagent.ai/en/channels/dingtalk) |
| WeCom Bot | `wecom_bot` | [WeCom Bot設定](https://docs.cowagent.ai/en/channels/wecom-bot) |
| WeComアプリ | `wechatcom_app` | [WeCom設定](https://docs.cowagent.ai/en/channels/wecom) |
| WeChat公式アカウント | `wechatmp` / `wechatmp_service` | [WeChat公式アカウント設定](https://docs.cowagent.ai/en/channels/wechatmp) |
| ターミナル | `terminal` | — |
複数チャネルを同時に有効化できます。カンマ区切りで指定してください:`"channel_type": "feishu,dingtalk"`
<br/>
## エンタープライズサービス
<a href="https://link-ai.tech" target="_blank"><img width="720" src="https://cdn.link-ai.tech/image/link-ai-intro.jpg"></a>
> [LinkAI](https://link-ai.tech/) は、企業や開発者向けのワンストップAIエージェントプラットフォームです。マルチモーダルLLM、ナレッジベース、Agentプラグイン、ワークフローを統合しています。主要プラットフォームへのワンクリック統合、SaaSおよびプライベートデプロイに対応しています。
<br/>
## 🔗 関連プロジェクト
- [bot-on-anything](https://github.com/zhayujie/bot-on-anything): 軽量で高い拡張性を持つLLMアプリケーションフレームワーク。Slack、Telegram、Discord、Gmailなどに対応。
- [AgentMesh](https://github.com/MinimalFuture/AgentMesh): エージェントチームの協調による複雑な問題解決のためのオープンソースのマルチエージェントフレームワーク。
## 🔎 よくある質問
FAQ: <https://github.com/zhayujie/chatgpt-on-wechat/wiki/FAQs>
## 🛠️ コントリビューション
新しいチャネルの追加を歓迎します。[Feishuチャネル](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/channel/feishu/feishu_channel.py)を参考にしてください。また、新しいSkillのコントリビューションも歓迎します。[Skill Creatorドキュメント](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/skills/skill-creator/SKILL.md)を参照してください。
## ✉ お問い合わせ
PRやIssueの提出を歓迎します。🌟 Starでプロジェクトをサポートしてください。ご質問がある場合は、[FAQリスト](https://github.com/zhayujie/chatgpt-on-wechat/wiki/FAQs)を確認するか、[Issues](https://github.com/zhayujie/chatgpt-on-wechat/issues)を検索してください。
## 🌟 コントリビューター
![cow contributors](https://contrib.rocks/image?repo=zhayujie/chatgpt-on-wechat&max=1000)

View File

@@ -0,0 +1,58 @@
---
title: DingTalk
description: CowAgent を DingTalk アプリケーションに統合する
---
DingTalk オープンプラットフォームでインテリジェントロボットアプリを作成して、CowAgent を DingTalk に統合します。
## 1. アプリの作成
1. [DingTalk 開発者コンソール](https://open-dev.dingtalk.com/fe/app#/corp/app)にアクセスし、ログインして**アプリを作成**をクリックし、アプリ情報を入力します:
<img src="https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/dingtalk-create-app.png" width="800"/>
2. **アプリ機能の追加**をクリックし、**ロボット**機能を選択して**追加**をクリックします:
<img src="https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/dingtalk-add-bot.png" width="800"/>
3. ロボット情報を設定し、**公開**をクリックします。公開後、「**デバッグ**」をクリックすると自動的にテストグループチャットが作成され、クライアントで確認できます:
<img src="https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/dingtalk-config-bot.png" width="600"/>
4. **バージョン管理とリリース**をクリックし、新しいバージョンを作成して公開します:
<img src="https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/dingtalk-publish-bot.png" width="700"/>
## 2. プロジェクト設定
1. **認証情報と基本情報**をクリックし、`Client ID` と `Client Secret` を取得します:
<img src="https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/dingtalk-get-secret.png" width="700"/>
2. プロジェクトルートの `config.json` に以下の設定を追加します:
```json
{
"channel_type": "dingtalk",
"dingtalk_client_id": "YOUR_CLIENT_ID",
"dingtalk_client_secret": "YOUR_CLIENT_SECRET"
}
```
3. 依存パッケージをインストールします:
```bash
pip3 install dingtalk_stream
```
<img src="https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/dingtalk-app-config.png" width="700"/>
4. プロジェクト起動後、DingTalk 開発者コンソールに移動し、**イベントサブスクリプション**をクリックし、**接続確認済み、チャネルを確認**をクリックします。「**接続成功**」と表示されれば設定完了です:
<img src="https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/dingtalk-event-sub.png" width="700"/>
## 3. 使い方
ロボットと個別チャットするか、企業グループに追加して会話を開始します:
<img src="https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/dingtalk-hosting-demo.png" width="650"/>

View File

@@ -0,0 +1,69 @@
---
title: Feishu (Lark)
description: CowAgent を Feishu アプリケーションに統合する
---
企業向けカスタムアプリを作成して、CowAgent を Feishu に統合します。管理者権限を持つ Feishu 企業ユーザーである必要があります。
## 1. 企業カスタムアプリの作成
### 1.1 アプリの作成
[Feishu 開発者プラットフォーム](https://open.feishu.cn/app/)にアクセスし、**企業カスタムアプリを作成**をクリックして、必要な情報を入力し**作成**をクリックします:
<img src="https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/feishu-hosting-create-app.jpg" width="500"/>
### 1.2 Bot 機能の追加
**アプリ機能の追加**で、アプリに **Bot** 機能を追加します:
<img src="https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/feishu-hosting-add-bot.jpg" width="800"/>
### 1.3 アプリ権限の設定
**権限管理**をクリックし、**権限設定**の下の入力欄に以下の権限文字列を貼り付け、フィルタされたすべての権限を選択し、**一括有効化**をクリックして確認します:
```
im:message,im:message.group_at_msg,im:message.group_at_msg:readonly,im:message.p2p_msg,im:message.p2p_msg:readonly,im:message:send_as_bot,im:resource
```
<img src="https://cdn.link-ai.tech/doc/feishu-hosting-add-auth2.png" width="800"/>
## 2. プロジェクト設定
1. **認証情報と基本情報**から `App ID` と `App Secret` を取得します:
<img src="https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/feishu-hosting-appid-secret.jpg" width="800"/>
2. プロジェクトルートの `config.json` に以下の設定を追加します:
```json
{
"channel_type": "feishu",
"feishu_app_id": "YOUR_APP_ID",
"feishu_app_secret": "YOUR_APP_SECRET",
"feishu_bot_name": "YOUR_BOT_NAME"
}
```
| パラメータ | 説明 |
| --- | --- |
| `feishu_app_id` | Feishu Bot の App ID |
| `feishu_app_secret` | Feishu Bot の App Secret |
| `feishu_bot_name` | Bot 名(アプリ作成時に設定)、グループチャットで使用する際に必要 |
設定完了後、プロジェクトを起動します。
## 3. イベントサブスクリプションの設定
1. プロジェクトが正常に動作した後、Feishu 開発者プラットフォームに移動し、**イベントとコールバック**をクリックし、**ロングコネクション**モードを選択して保存をクリックします:
<img src="https://cdn.link-ai.tech/doc/202601311731183.png" width="600"/>
2. 下の**イベントを追加**をクリックし、「メッセージ受信」を検索して「**メッセージ受信 v2.0**」を選択し、確認します。
3. **バージョン管理とリリース**をクリックし、新しいバージョンを作成して**本番リリース**を申請します。Feishu クライアントで承認メッセージを確認し、承認します:
<img src="https://cdn.link-ai.tech/doc/202601311807356.png" width="600"/>
完了後、Feishu で Bot 名を検索してチャットを開始できます。

88
docs/ja/channels/qq.mdx Normal file
View File

@@ -0,0 +1,88 @@
---
title: QQ Bot
description: CowAgent を QQ Bot に接続するWebSocket ロングコネクション)
---
> QQ オープンプラットフォームの Bot API を介して CowAgent を接続し、QQ のダイレクトメッセージ、グループチャット(@bot、ギルドチャネルメッセージ、ギルド DM に対応します。パブリック IP は不要で、WebSocket ロングコネクションを使用します。
<Note>
QQ Bot は QQ オープンプラットフォームを通じて作成します。WebSocket ロングコネクションでメッセージを受信し、OpenAPI でメッセージを送信します。パブリック IP やドメインは不要です。
</Note>
## 1. QQ Bot の作成
> [QQ オープンプラットフォーム](https://q.qq.com)にアクセスし、QQ でサインインします。未登録の場合は、先に[アカウント登録](https://q.qq.com/#/register)を完了してください。
1.[QQ オープンプラットフォーム - Bot 一覧](https://q.qq.com/#/apps)に移動し、**Bot を作成**をクリックします:
<img src="https://cdn.link-ai.tech/doc/20260317162900.png" width="800"/>
2.Bot 名、アバター、その他の基本情報を入力して作成を完了します:
<img src="https://cdn.link-ai.tech/doc/20260317163005.png" width="800"/>
3.Bot 設定ページに入り、**開発管理**に移動して以下の手順を完了します:
- **AppID**Bot IDをコピーして保存します
- **AppSecret**Bot Secretを生成して保存します
<img src="https://cdn.link-ai.tech/doc/20260317164955.png" width="800"/>
## 2. 設定と起動
### 方法 A: Web コンソール
プログラムを起動し、Web コンソール(ローカルアクセス: http://127.0.0.1:9899/)を開きます。**チャネル**タブに移動し、**チャネルを接続**をクリックして **QQ Bot** を選択し、前のステップで取得した AppID と AppSecret を入力して接続をクリックします。
<img src="https://cdn.link-ai.tech/doc/20260317165425.png" width="800"/>
### 方法 B: 設定ファイル
`config.json` に以下を追加します:
```json
{
"channel_type": "qq",
"qq_app_id": "YOUR_APP_ID",
"qq_app_secret": "YOUR_APP_SECRET"
}
```
| パラメータ | 説明 |
| --- | --- |
| `qq_app_id` | QQ Bot の AppID。オープンプラットフォームの開発管理で確認できます |
| `qq_app_secret` | QQ Bot の AppSecret。オープンプラットフォームの開発管理で確認できます |
設定後、プログラムを起動します。ログに `[QQ] ✅ Connected successfully` と表示されれば接続成功です。
## 3. 使い方
QQ オープンプラットフォームで、**管理 → 利用範囲とメンバー**に移動し、「グループとメッセージリストに追加」の QR コードを QQ クライアントでスキャンして Bot とのチャットを開始します:
<img src="https://cdn.link-ai.tech/doc/20260317165947.png" width="800"/>
チャット例:
<img src="https://cdn.link-ai.tech/doc/20260317171508.png" width="800"/>
## 4. 対応機能
> 注意: グループチャットやギルドチャネルで QQ Bot を使用するには、公開審査を完了し、利用範囲の権限を設定する必要があります。
| 機能 | 状態 |
| --- | --- |
| QQ ダイレクトメッセージ | ✅ |
| QQ グループチャット(@bot | ✅ |
| ギルドチャネル(@bot | ✅ |
| ギルド DM | ✅ |
| テキストメッセージ | ✅ 送受信 |
| 画像メッセージ | ✅ 送受信(グループ・ダイレクト) |
| ファイルメッセージ | ✅ 送信(グループ・ダイレクト) |
| スケジュールタスク | ✅ 能動的プッシュユーザーあたり月4回 |
## 5. 注意事項
- **受動メッセージの制限**: QQ ダイレクトメッセージの返信は60分間有効です1メッセージあたり最大5回返信可能。グループチャットの返信は5分間有効です。
- **能動メッセージの制限**: ダイレクトメッセージとグループチャットの両方で、月あたりの能動メッセージは4件までです。スケジュールタスク機能を使用する際はこの点にご注意ください。
- **イベント権限**: デフォルトでは `GROUP_AND_C2C_EVENT`QQ グループ/ダイレクト)と `PUBLIC_GUILD_MESSAGES`(ギルド公開メッセージ)がサブスクライブされています。追加の権限が必要な場合は、オープンプラットフォームで申請してください。

75
docs/ja/channels/web.mdx Normal file
View File

@@ -0,0 +1,75 @@
---
title: Web コンソール
description: Web コンソールで CowAgent を使用する
---
Web コンソールは CowAgent のデフォルトチャネルです。起動後に自動的に開始され、ブラウザを通じて Agent とチャットしたり、モデル、Skill、メモリ、チャネルなどの設定をオンラインで管理できます。
## 設定
```json
{
"channel_type": "web",
"web_port": 9899
}
```
| パラメータ | 説明 | デフォルト値 |
| --- | --- | --- |
| `channel_type` | `web` に設定 | `web` |
| `web_port` | Web サービスのリスンポート | `9899` |
## アクセス URL
プロジェクト起動後、以下にアクセスしてください:
- ローカル: `http://localhost:9899`
- サーバー: `http://<server-ip>:9899`
<Note>
サーバーのファイアウォールとセキュリティグループで該当ポートが許可されていることを確認してください。
</Note>
## 機能
### チャット画面
ストリーミング出力に対応しており、Agent の推論プロセスやツール呼び出しをリアルタイムで表示し、Agent の意思決定を直感的に観察できます:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227180120.png" />
### モデル管理
設定ファイルを手動で編集せずに、オンラインでモデル設定を管理できます:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227173811.png" />
### Skill 管理
Agent の Skill をオンラインで閲覧・管理できます:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227173403.png" />
### メモリ管理
Agent のメモリをオンラインで閲覧・管理できます:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227173349.png" />
### チャネル管理
接続中のチャネルをオンラインで管理し、リアルタイムで接続・切断操作を行えます:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227173331.png" />
### スケジュールタスク
スケジュールタスクをオンラインで閲覧・管理できます。一回限りのタスク、固定間隔、Cron 式に対応しています:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227173704.png" />
### ログ
Agent のランタイムログをリアルタイムで確認でき、監視やトラブルシューティングに活用できます:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227173514.png" />

View File

@@ -0,0 +1,72 @@
---
title: WeChat 公式アカウント
description: CowAgent を WeChat 公式アカウントに統合する
---
CowAgent は個人サブスクリプションアカウントと企業サービスアカウントの両方に対応しています。
| 種類 | 要件 | 特徴 |
| --- | --- | --- |
| **個人サブスクリプション** | 個人で利用可能 | まずプレースホルダーの返信を送信し、ユーザーが完全な応答を取得するにはメッセージを送信する必要があります |
| **企業サービス** | カスタマーサービス API が認証済みの企業 | ユーザーに能動的に返信をプッシュできます |
<Note>
公式アカウントはサーバーおよび Docker デプロイのみサポートしており、ローカル実行モードには対応していません。拡張依存パッケージをインストールしてください: `pip3 install -r requirements-optional.txt`
</Note>
## 1. 個人サブスクリプションアカウント
`config.json` に以下の設定を追加します:
```json
{
"channel_type": "wechatmp",
"single_chat_prefix": [""],
"wechatmp_app_id": "wx73f9******d1e48",
"wechatmp_app_secret": "YOUR_APP_SECRET",
"wechatmp_aes_key": "",
"wechatmp_token": "YOUR_TOKEN",
"wechatmp_port": 80
}
```
### セットアップ手順
これらの設定は [WeChat 公式アカウントプラットフォーム](https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev)と一致している必要があります。**設定と開発 → 基本設定 → サーバー設定**に移動し、以下のように設定します:
<img src="https://cdn.link-ai.tech/doc/20260228103506.png" width="480"/>
1. プラットフォームで開発者シークレットを有効化し(`wechatmp_app_secret` に対応)、サーバー IP をホワイトリストに追加します
2. プラットフォームの設定と一致するように公式アカウントのパラメータを `config.json` に入力します
3. プログラムを起動します。ポート 80 でリスンします(権限がない場合は `sudo` を使用してください。ポート 80 を占有しているプロセスがあれば停止してください)
4. 公式アカウントプラットフォームで**サーバー設定を有効化**して送信します。正常に保存できれば設定完了です。**「サーバー URL」**は `http://{HOST}/wx` の形式で入力する必要があり、`{HOST}` にはサーバー IP またはドメインを指定できます
アカウントをフォローしてメッセージを送信すると、以下のような結果が表示されるはずです:
<img src="https://cdn.link-ai.tech/doc/20260228103522.png" width="720"/>
サブスクリプションアカウントの制限により、短い返信15秒以内は即座に返されますが、長い返信の場合はまず「考え中...」というプレースホルダーが送信され、ユーザーは任意のテキストを送信して回答を取得する必要があります。企業サービスアカウントではカスタマーサービス API を使用してこの問題を解決できます。
<Tip>
**音声認識**: WeChat 内蔵の音声認識を使用できます。公式アカウント管理ページの「設定と開発 → API 権限」で「音声認識結果の受信」を有効にしてください。
</Tip>
## 2. 企業サービスアカウント
企業サービスアカウントのセットアップ手順は個人サブスクリプションアカウントとほぼ同じですが、以下の点が異なります:
1. プラットフォームで企業サービスアカウントを登録し、WeChat 認証を完了します。**カスタマーサービス API** の権限が付与されていることを確認してください
2. `config.json` で `"channel_type": "wechatmp_service"` に設定します。その他の設定は同じです
3. 長い返信であっても、ユーザーに能動的にプッシュでき、手動での取得が不要です
```json
{
"channel_type": "wechatmp_service",
"single_chat_prefix": [""],
"wechatmp_app_id": "YOUR_APP_ID",
"wechatmp_app_secret": "YOUR_APP_SECRET",
"wechatmp_aes_key": "",
"wechatmp_token": "YOUR_TOKEN",
"wechatmp_port": 80
}
```

View File

@@ -0,0 +1,73 @@
---
title: WeCom Bot
description: CowAgent を WeCom AI Bot に接続するWebSocket ロングコネクション)
---
WeCom AI Bot を介して CowAgent を接続し、ダイレクトメッセージとグループチャットの両方に対応します。パブリック IP は不要で、WebSocket ロングコネクションを使用し、Markdown レンダリングとストリーミング出力をサポートします。
<Note>
WeCom Bot と WeCom App は異なる統合方式です。WeCom Bot は WebSocket ロングコネクションを使用するため、パブリック IP やドメインが不要で、セットアップが簡単です。
</Note>
## 1. AI Bot の作成
1. WeCom クライアントを開き、**ワークベンチ**に移動し、**AI Bot** をクリックします:
<img src="https://cdn.link-ai.tech/doc/20260316180959.png" width="800"/>
2. **Bot を作成** → **手動作成**をクリックします:
<img src="https://cdn.link-ai.tech/doc/20260316181118.png" width="600"/>
3. 右パネルの一番下までスクロールし、**API モード**を選択します:
<img src="https://cdn.link-ai.tech/doc/20260316181215.png" width="600"/>
4. Bot 名、アバター、公開範囲を設定します。**ロングコネクション**モードを選択し、**Bot ID** と **Secret** をメモしてから保存をクリックします。
## 2. 設定
### 方法 A: Web コンソール
プログラムを起動し、Web コンソール(ローカルアクセス: http://127.0.0.1:9899を開きます。**チャネル**タブに移動し、**チャネルを接続**をクリックして **WeCom Bot** を選択し、前のステップで取得した Bot ID と Secret を入力して接続をクリックします。
<img src="https://cdn.link-ai.tech/doc/20260316181711.png" width="600"/>
### 方法 B: 設定ファイル
`config.json` に以下を追加します:
```json
{
"channel_type": "wecom_bot",
"wecom_bot_id": "YOUR_BOT_ID",
"wecom_bot_secret": "YOUR_SECRET"
}
```
| パラメータ | 説明 |
| --- | --- |
| `wecom_bot_id` | AI Bot の Bot ID |
| `wecom_bot_secret` | AI Bot の Secret |
設定後、プログラムを起動します。ログに `[WecomBot] Subscribe success` と表示されれば接続成功です。
## 3. 対応機能
| 機能 | 状態 |
| --- | --- |
| ダイレクトメッセージ | ✅ |
| グループチャット(@bot | ✅ |
| テキストメッセージ | ✅ 送受信 |
| 画像メッセージ | ✅ 送受信 |
| ファイルメッセージ | ✅ 送受信 |
| ストリーミング返信 | ✅ |
| スケジュール配信 | ✅ |
## 4. 使い方
WeCom で Bot 名を検索してダイレクトメッセージを開始できます。
グループチャットで使用するには、Bot をグループに追加し、@メンションしてメッセージを送信します。
<img src="https://cdn.link-ai.tech/doc/20260316182902.png" width="800"/>

View File

@@ -0,0 +1,98 @@
---
title: WeCom
description: CowAgent を WeCom 企業アプリに統合する
---
カスタム企業アプリを通じて CowAgent を WeCom に統合し、社内従業員との1対1チャットに対応します。
<Note>
WeCom は Docker デプロイまたはサーバー上の Python デプロイのみサポートしています。ローカル実行モードには対応していません。
</Note>
## 1. 前提条件
必要なリソース:
1. パブリック IP を持つサーバー(海外サーバー、または国際 API アクセス用のプロキシを持つ国内サーバー)
2. 登録済みの WeCom アカウント(個人登録は可能ですが認証はできません)
3. 認証済みの WeCom アカウントには、対応する法人名義で届け出済みのドメインが別途必要です
## 2. WeCom アプリの作成
1. [WeCom 管理コンソール](https://work.weixin.qq.com/wework_admin/frame#profile)で、**自社情報**をクリックし、ページ下部の **Corp ID** を確認します。この ID を `wechatcom_corp_id` 設定フィールド用に保存します。
2. **アプリ管理**に切り替え、アプリを作成をクリックします:
<img src="https://cdn.link-ai.tech/doc/20260228103156.png" width="480"/>
3. アプリ作成ページで、`AgentId` と `Secret` を記録します:
<img src="https://cdn.link-ai.tech/doc/20260228103218.png" width="580"/>
4. **API 受信設定**をクリックしてアプリケーションインターフェースを設定します:
<img src="https://cdn.link-ai.tech/doc/20260228103211.png" width="520"/>
- URL の形式: `http://ip:port/wxcomapp`(認証済み企業は届け出済みドメインを使用する必要があります)
- ランダムな `Token` と `EncodingAESKey` を生成し、設定ファイル用に保存します
<Note>
プログラムがまだ起動していないため、この時点では API 受信設定を保存できません。プロジェクトが動作した後に戻って保存してください。
</Note>
## 3. 設定と起動
`config.json` に以下の設定を追加します(各パラメータと WeCom コンソールの対応関係は上のスクリーンショットを参照してください):
```json
{
"channel_type": "wechatcom_app",
"single_chat_prefix": [""],
"wechatcom_corp_id": "YOUR_CORP_ID",
"wechatcomapp_token": "YOUR_TOKEN",
"wechatcomapp_secret": "YOUR_SECRET",
"wechatcomapp_agent_id": "YOUR_AGENT_ID",
"wechatcomapp_aes_key": "YOUR_AES_KEY",
"wechatcomapp_port": 9898
}
```
| パラメータ | 説明 |
| --- | --- |
| `wechatcom_corp_id` | Corp ID |
| `wechatcomapp_token` | API 受信設定の Token |
| `wechatcomapp_secret` | アプリの Secret |
| `wechatcomapp_agent_id` | アプリの AgentId |
| `wechatcomapp_aes_key` | API 受信設定の EncodingAESKey |
| `wechatcomapp_port` | リスンポート、デフォルトは 9898 |
設定後、プログラムを起動します。ログに `http://0.0.0.0:9898/` と表示されれば、プログラムは正常に動作しています。このポートを外部に公開する必要があります(例:クラウドサーバーのセキュリティグループで許可します)。
プログラム起動後、WeCom 管理コンソールに戻って**メッセージサーバー設定**を保存します。保存が成功したら、サーバー IP を**企業の信頼済み IP** に追加する必要もあります。追加しないとメッセージの送受信ができません:
<img src="https://cdn.link-ai.tech/doc/20260228103224.png" width="520"/>
<Warning>
URL 設定のコールバックが失敗する場合や、設定がうまくいかない場合:
1. サーバーのファイアウォールが無効になっており、セキュリティグループでリスンポートが許可されていることを確認してください
2. Token、Secret Key などのパラメータ設定が一致しているか、URL の形式が正しいか慎重に確認してください
3. 認証済みの WeCom アカウントは、法人に対応する届け出済みドメインを設定する必要があります
</Warning>
## 4. 使い方
WeCom で作成したアプリ名を検索して、直接チャットを開始できます。異なるポートでリスンする複数のインスタンスを実行して、複数の WeCom アプリを作成できます:
<img src="https://cdn.link-ai.tech/doc/20260228103228.png" width="720"/>
外部の個人 WeChat ユーザーにアプリを利用してもらうには、**自社情報 → WeChat プラグイン**に移動し、招待 QR コードを共有します。スキャンしてフォローした後、個人 WeChat ユーザーがアプリとチャットできるようになります:
<img src="https://cdn.link-ai.tech/doc/20260228103232.png" width="520"/>
## FAQ
以下の依存パッケージがインストールされていることを確認してください:
```bash
pip install websocket-client pycryptodome
```

View File

@@ -0,0 +1,72 @@
---
title: WeChat
description: CowAgent を個人の WeChat に接続する
---
> 個人の WeChat に接続します。QR コードをスキャンするだけでログインでき、パブリック IP は不要です。テキスト、画像、音声、ファイル、動画メッセージの送受信に対応しています。
## 1. 設定
### 方法 A: Web コンソール
プログラムを起動し、Web コンソール(ローカルアクセス: http://127.0.0.1:9899を開きます。**チャネル**タブに移動し、**チャネルを接続**をクリックして **WeChat** を選択し、プロンプトに従って QR コードをスキャンしてください。
### 方法 B: 設定ファイル
`config.json` で `channel_type` を `weixin` に設定します:
```json
{
"channel_type": "weixin"
}
```
プログラム起動後、ターミナルに QR コードが表示されます。WeChat でスキャンし、スマートフォンで確認してログインを完了してください。
<Note>
後方互換性のため、`channel_type` を `wx` に設定しても WeChat チャネルが有効になります。
</Note>
## 2. パラメータ
| パラメータ | 説明 | デフォルト |
| --- | --- | --- |
| `channel_type` | `weixin` または `wx` を指定 | — |
ログイン認証情報は `~/.weixin_cow_credentials.json` に自動保存されます。再ログインするには、このファイルを削除してプログラムを再起動してください。
## 3. ログイン
### QR コードログイン
初回起動時に、ターミナルに QR コードが表示されます(有効期限は約 2 分。WeChat でスキャンし、スマートフォンで確認してください。
- QR コードが期限切れになると自動的に更新・再表示されます
- `qrcode` 依存関係は `requirements.txt` にデフォルトで含まれており、ターミナルに直接 QR コードを表示できます
### 認証情報の永続化
ログイン成功後、認証情報は `~/.weixin_cow_credentials.json` に保存されます。次回起動時は保存された認証情報が再利用され、再スキャンは不要です。
再ログインするには、認証情報ファイルを削除してプログラムを再起動してください。
### セッションの期限切れ
WeChat セッションが期限切れになった場合errcode -14、プログラムは自動的に古い認証情報をクリアし、新しい QR ログインを開始します。手動での操作は不要です。
## 4. 対応機能
| 機能 | 状態 |
| --- | --- |
| ダイレクトメッセージ | ✅ |
| テキストメッセージ | ✅ 送受信 |
| 画像メッセージ | ✅ 送受信 |
| ファイルメッセージ | ✅ 送受信 |
| 動画メッセージ | ✅ 送受信 |
| 音声メッセージ | ✅ 受信 |
## 5. 注意事項
1. `ilinkai.weixin.qq.com` へのネットワークアクセスが必要です。
2. メディアファイル(画像、ファイル、動画)は CDN 経由で AES-128-ECB 暗号化を使用して転送され、プログラムが自動的に処理します。
3. 頻繁な切断による再スキャンを避けるため、安定したネットワーク環境での実行を推奨します。

View File

@@ -0,0 +1,113 @@
---
title: 手動インストール
description: CowAgentの手動デプロイソースコード / Docker
---
## ソースコードによるデプロイ
### 1. プロジェクトをクローン
```bash
git clone https://github.com/zhayujie/chatgpt-on-wechat
cd chatgpt-on-wechat/
```
<Tip>
ネットワークに問題がある場合は、ミラーを使用してください: https://gitee.com/zhayujie/chatgpt-on-wechat
</Tip>
### 2. 依存パッケージをインストール
コア依存パッケージ(必須):
```bash
pip3 install -r requirements.txt
```
オプション依存パッケージ(推奨):
```bash
pip3 install -r requirements-optional.txt
```
### 3. 設定
設定テンプレートをコピーして編集します:
```bash
cp config-template.json config.json
```
`config.json` にモデルの API キー、チャネルタイプ、その他の設定を入力します。詳細は[モデルのドキュメント](/ja/models/index)を参照してください。
### 4. 実行
**ローカルで実行:**
```bash
python3 app.py
```
デフォルトではWebサービスが起動します。`http://localhost:9899/chat` にアクセスしてチャットできます。
**サーバーでバックグラウンド実行:**
```bash
nohup python3 app.py & tail -f nohup.out
```
## Docker によるデプロイ
Docker デプロイでは、ソースコードのクローンや依存パッケージのインストールは不要です。Agent モードを使用する場合は、より広範なシステムアクセスが可能なソースコードによるデプロイを推奨します。
<Note>
[Docker](https://docs.docker.com/engine/install/) と docker-compose が必要です。
</Note>
**1. 設定ファイルをダウンロード**
```bash
curl -O https://cdn.link-ai.tech/code/cow/docker-compose.yml
```
`docker-compose.yml` を編集して設定を行います。
**2. コンテナを起動**
```bash
sudo docker compose up -d
```
**3. ログを確認**
```bash
sudo docker logs -f chatgpt-on-wechat
```
## 主要な設定項目
```json
{
"channel_type": "web",
"model": "MiniMax-M2.5",
"agent": true,
"agent_workspace": "~/cow",
"agent_max_context_tokens": 40000,
"agent_max_context_turns": 30,
"agent_max_steps": 15
}
```
| パラメータ | 説明 | デフォルト値 |
| --- | --- | --- |
| `channel_type` | チャネルタイプ | `web` |
| `model` | モデル名 | `MiniMax-M2.5` |
| `agent` | Agent モードを有効化 | `true` |
| `agent_workspace` | Agent のワークスペースパス | `~/cow` |
| `agent_max_context_tokens` | 最大コンテキストトークン数 | `40000` |
| `agent_max_context_turns` | 最大コンテキストターン数 | `30` |
| `agent_max_steps` | タスクごとの最大判断ステップ数 | `15` |
<Tip>
すべての設定オプションはプロジェクトの [`config.py`](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/config.py) に記載されています。
</Tip>

View File

@@ -0,0 +1,39 @@
---
title: ワンクリックインストール
description: スクリプトによるCowAgentのワンクリックインストールと管理
---
本プロジェクトでは、ワンクリックでのインストール、設定、起動、管理を行うスクリプトを提供しています。素早くセットアップするには、スクリプトによるデプロイを推奨します。
Linux、macOS、Windowsに対応しています。Python 3.7〜3.12が必要です3.9を推奨)。
## インストールコマンド
```bash
bash <(curl -fsSL https://cdn.link-ai.tech/code/cow/run.sh)
```
スクリプトは以下の手順を自動的に実行します:
1. Python環境の確認Python 3.7以上が必要)
2. 必要なツールのインストールgit、curlなど
3. プロジェクトを `~/chatgpt-on-wechat` にクローン
4. Pythonの依存パッケージをインストール
5. AIモデルとチャネルの対話式設定
6. サービスの起動
デフォルトでは、インストール後にWebサービスが起動します。`http://localhost:9899/chat` にアクセスしてチャットを開始できます。
## 管理コマンド
インストール後、以下のコマンドでサービスを管理できます:
| コマンド | 説明 |
| --- | --- |
| `./run.sh start` | サービスを起動 |
| `./run.sh stop` | サービスを停止 |
| `./run.sh restart` | サービスを再起動 |
| `./run.sh status` | 実行状態を確認 |
| `./run.sh logs` | リアルタイムログを表示 |
| `./run.sh config` | 再設定 |
| `./run.sh update` | プロジェクトコードを更新 |

52
docs/ja/guide/upgrade.mdx Normal file
View File

@@ -0,0 +1,52 @@
---
title: アップデート
description: CowAgent のアップグレード方法
---
## スクリプトによるアップグレード(推奨)
`run.sh` でサービスを管理している場合、以下のコマンドでワンクリックアップグレードできます:
```bash
./run.sh update
```
このコマンドは以下のフローを自動的に実行します:
1. 現在実行中のサービスを停止
2. 最新コードをプル
3. 依存関係を再チェック
4. サービスを起動
## 手動アップグレード
プロジェクトのルートディレクトリで以下を実行します:
```bash
git pull
pip3 install -r requirements.txt
```
更新完了後、サービスを再起動します:
```bash
# run.sh で管理している場合
./run.sh restart
# nohup で直接実行している場合
kill $(ps -ef | grep app.py | grep -v grep | awk '{print $2}')
nohup python3 app.py & tail -f nohup.out
```
## Docker アップグレード
`docker-compose.yml` があるディレクトリで以下を実行します:
```bash
sudo docker compose pull
sudo docker compose up -d
```
<Tip>
アップグレード前に `config.json` 設定ファイルのバックアップを推奨します。Docker 環境でデータを保持する場合は、volume マウントでワークスペースディレクトリを永続化できます。
</Tip>

View File

@@ -0,0 +1,77 @@
---
title: アーキテクチャ
description: CowAgent 2.0 のシステムアーキテクチャとコア設計
---
CowAgent 2.0 は、シンプルなチャットボットから、自律的な思考、タスク計画、長期記憶、Skill の拡張性を備えた Agent アーキテクチャのスーパーインテリジェントアシスタントへと進化しました。
## システムアーキテクチャ
CowAgent のアーキテクチャは以下のコアモジュールで構成されています:
<img src="https://cdn.link-ai.tech/doc/68ef7b212c6f791e0e74314b912149f9-sz_5847990.png" alt="CowAgent Architecture" />
### コアモジュール
| モジュール | 説明 |
| --- | --- |
| **Channels** | メッセージの受信と送信を行うメッセージチャネル層。Web、Feishu飛書、DingTalk釘釘、WeCom企業微信、WeChat公式アカウントなどをサポート |
| **Agent Core** | タスク計画、記憶システム、Skill エンジンを含む Agent エンジン |
| **Tools** | Agent が OS リソースにアクセスするためのツール層。10 以上の組み込みツール |
| **Models** | 主要な LLM への統一アクセスを提供するモデル層 |
## Agent モードのワークフロー
Agent モードが有効な場合、CowAgent は以下のワークフローで自律的な Agent として動作します:
1. **メッセージ受信** — チャネルを通じてユーザーの入力を受信
2. **意図の理解** — タスク要件とコンテキストを分析
3. **タスク計画** — 複雑なタスクを複数のステップに分解
4. **ツール呼び出し** — 各ステップに適切なツールを選択・実行
5. **記憶の更新** — 重要な情報を長期記憶に保存
6. **結果の返却** — 実行結果をユーザーに送信
## ワークスペースのディレクトリ構成
Agent のワークスペースはデフォルトで `~/cow` にあり、システムプロンプト、記憶ファイル、Skill ファイルを格納しています:
```
~/cow/
├── system.md # Agent システムプロンプト
├── user.md # ユーザープロフィール
├── memory/ # 長期記憶ストレージ
│ ├── core.md # コアメモリ
│ └── daily/ # デイリーメモリ
└── skills/ # カスタム Skill
├── skill-1/
└── skill-2/
```
シークレットキーはセキュリティのため `~/.cow` ディレクトリに別途保存されます:
```
~/.cow/
└── .env # Skill 用のシークレットキー
```
## コア設定
`config.json` で Agent モードのパラメータを設定します:
```json
{
"agent": true,
"agent_workspace": "~/cow",
"agent_max_context_tokens": 40000,
"agent_max_context_turns": 30,
"agent_max_steps": 15
}
```
| パラメータ | 説明 | デフォルト値 |
| --- | --- | --- |
| `agent` | Agent モードの有効化 | `true` |
| `agent_workspace` | ワークスペースのパス | `~/cow` |
| `agent_max_context_tokens` | 最大コンテキストトークン数 | `40000` |
| `agent_max_context_turns` | 最大コンテキストターン数 | `30` |
| `agent_max_steps` | タスクあたりの最大判断ステップ数 | `15` |

105
docs/ja/intro/features.mdx Normal file
View File

@@ -0,0 +1,105 @@
---
title: 機能詳細
description: CowAgent の長期記憶、タスク計画、Skill システムの詳細
---
## 1. 長期記憶
記憶システムにより、Agent は重要な情報を長期にわたって記憶できます。ユーザーが好みや決定、重要な事実を共有すると、Agent は自発的に情報を保存し、会話が一定の長さに達すると自動的に要約を抽出します。記憶はコアメモリとデイリーメモリに分かれており、キーワード検索とベクトル検索の両方をサポートするハイブリッド検索が可能です。
初回起動時、Agent はユーザーに重要な情報を自発的に尋ね、ワークスペース(デフォルト `~/cow`)に記録します。これには Agent の設定、ユーザーの身元情報、記憶ファイルが含まれます。
その後の長期的な会話において、Agent は必要に応じてインテリジェントに記憶を保存・取得し、自身の設定やユーザーの好み、記憶ファイルを継続的に更新し、経験と教訓を要約します。これにより、真に自律的な思考と継続的な成長を実現しています。
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260203000455.png" width="800" />
</Frame>
## 2. タスク計画とツール活用
ツールは Agent がオペレーティングシステムのリソースにアクセスするための中核です。Agent はタスク要件に基づいてインテリジェントにツールを選択・呼び出し、ファイルの読み書き、コマンド実行、スケジュールタスクなどを実行します。組み込みツールはプロジェクトの `agent/tools/` ディレクトリに実装されています。
**主なツール:** ファイルの読み書き・編集、Bash ターミナル、ファイル送信、スケジューラ、記憶検索、Web 検索、環境設定など。
### 2.1 ターミナルとファイルアクセス
OS のターミナルとファイルシステムへのアクセスは、最も基本的かつ中核的な機能です。多くの他のツールや Skill はこの機能の上に構築されています。ユーザーはモバイルデバイスから Agent とやり取りし、パソコンやサーバーのリソースを操作できます:
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202181130.png" width="800" />
</Frame>
### 2.2 プログラミング能力
プログラミングとシステムアクセスを組み合わせることで、Agent は完全な **Vibecoding ワークフロー** を実行できます。情報検索、アセット生成、コーディング、テスト、デプロイ、Nginx 設定、公開まで、すべてスマートフォンからの一つのコマンドで実行可能です:
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260203121008.png" width="800" />
</Frame>
### 2.3 スケジュールタスク
`scheduler` ツールにより動的なスケジュールタスクが可能で、**ワンタイムタスク、固定間隔、Cron 式**をサポートしています。タスクは**固定メッセージ送信**または **Agent 動的タスク**実行としてトリガーできます:
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202195402.png" width="800" />
</Frame>
### 2.4 環境変数管理
Skill が必要とするシークレットキーは環境変数ファイルに保存され、`env_config` ツールによって管理されます。会話を通じてシークレットを更新でき、セキュリティ保護とマスキング機能が組み込まれています:
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202234939.png" width="800" />
</Frame>
## 3. Skill システム
Skill システムは Agent に無限の拡張性を提供します。各 Skill は説明ファイル、実行スクリプト任意、リソース任意で構成され、特定のタイプのタスクを完了する方法を記述します。Skill により Agent は複雑なワークフローの指示に従い、ツールを呼び出し、サードパーティシステムと連携できます。
- **組み込み Skill** プロジェクトの `skills/` ディレクトリにあり、Skill クリエイター、画像認識、LinkAI Agent、Web フェッチなどが含まれます。組み込み Skill は依存条件API キー、システムコマンドなど)に基づいて自動的に有効化されます。
- **カスタム Skill** ユーザーが会話を通じて作成し、ワークスペース(`~/cow/skills/`)に保存されます。あらゆる複雑なビジネスプロセスやサードパーティ連携を実装できます。
### 3.1 Skill の作成
`skill-creator` Skill により、会話を通じて Skill を素早く作成できます。ワークフローを Skill としてコード化するよう Agent に依頼したり、API ドキュメントやサンプルを送信して Agent に直接連携を完成させることができます:
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202202247.png" width="800" />
</Frame>
### 3.2 Web 検索と画像認識
- **Web 検索:** 組み込みの `web_search` ツールで、複数の検索エンジンをサポートします。`BOCHA_API_KEY` または `LINKAI_API_KEY` を設定して有効化してください。
- **画像認識:** 組み込みの `openai-image-vision` Skill で、`gpt-4.1-mini`、`gpt-4.1` などのモデルをサポートします。`OPENAI_API_KEY` が必要です。
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202213219.png" width="800" />
</Frame>
### 3.3 サードパーティナレッジベースとプラグイン
`linkai-agent` Skill により、[LinkAI](https://link-ai.tech/) 上のすべての Agent を Skill として利用でき、マルチ Agent による意思決定が可能になります。
設定方法:`env_config` で `LINKAI_API_KEY` を設定し、`skills/linkai-agent/config.json` に Agent の説明を追加します:
```json
{
"apps": [
{
"app_code": "G7z6vKwp",
"app_name": "LinkAI Customer Support",
"app_description": "Select only when the user needs help with LinkAI platform questions"
},
{
"app_code": "SFY5x7JR",
"app_name": "Content Creator",
"app_description": "Use only when the user needs to create images or videos"
}
]
}
```
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202234350.png" width="750" />
</Frame>

68
docs/ja/intro/index.mdx Normal file
View File

@@ -0,0 +1,68 @@
---
title: はじめに
description: CowAgent - LLM を活用した AI スーパーアシスタント
---
<img src="https://cdn.link-ai.tech/doc/78c5dd674e2c828642ecc0406669fed7.png" alt="CowAgent" width="600px"/>
**CowAgent** は、自律的なタスク計画、長期記憶、Skill システム、マルチモーダルメッセージ、複数モデル対応、マルチプラットフォームデプロイを備えた、LLM を活用した AI スーパーアシスタントです。
CowAgent は自ら思考しタスクを計画し、コンピュータや外部リソースを操作し、Skill を作成・実行し、長期記憶により継続的に成長します。複数モデルの柔軟な切り替えをサポートし、テキスト、音声、画像、ファイルなどのマルチモーダルメッセージを処理でき、WeChat、Web、Feishu飛書、DingTalk釘釘、WeCom企業微信、WeChat公式アカウントに統合できます。お使いのパソコンやサーバー上で24時間365日稼働します。
<Card title="GitHub" icon="github" href="https://github.com/zhayujie/chatgpt-on-wechat">
github.com/zhayujie/chatgpt-on-wechat
</Card>
## コア機能
<CardGroup cols={2}>
<Card title="自律タスク計画" icon="brain" href="/ja/intro/architecture">
複雑なタスクを理解し、自律的に実行計画を立て、目標が達成されるまで思考とツール呼び出しを続けます。ツールを通じてファイルシステム、ターミナル、ブラウザ、スケジューラなどのシステムリソースにアクセスできます。
</Card>
<Card title="長期記憶" icon="database" href="/ja/memory">
会話の記憶をローカルファイルやデータベースに自動的に永続化します。コアメモリとデイリーメモリを含み、キーワード検索とベクトル検索に対応しています。
</Card>
<Card title="Skill システム" icon="puzzle-piece" href="/ja/skills/index">
Skill の作成・実行エンジンを実装し、組み込み Skill を搭載。自然言語の会話を通じてカスタム Skill の開発もサポートしています。
</Card>
<Card title="マルチモーダルメッセージ" icon="image" href="/ja/channels/web">
テキスト、画像、音声、ファイルなどのメッセージタイプの解析、処理、生成、送信をサポートします。
</Card>
<Card title="複数モデル対応" icon="microchip" href="/ja/models/index">
OpenAI、Claude、Gemini、DeepSeek、MiniMax、GLM、Qwen、Kimi、Doubao など、主要なモデルプロバイダーをサポートしています。
</Card>
<Card title="マルチプラットフォームデプロイ" icon="server" href="/ja/channels/weixin">
ローカルコンピュータやサーバー上で動作し、WeChat、Web、Feishu飛書、DingTalk釘釘、WeChat公式アカウント、WeCom企業微信アプリケーションに統合できます。
</Card>
</CardGroup>
## クイック体験
ターミナルで以下のコマンドを実行すると、ワンクリックでインストール、設定、起動ができます:
```bash
bash <(curl -fsSL https://cdn.link-ai.tech/code/cow/run.sh)
```
デフォルトでは実行後に Web サービスが起動します。`http://localhost:9899/chat` にアクセスして Web インターフェースでチャットできます。
<CardGroup cols={2}>
<Card title="クイックスタート" icon="rocket" href="/ja/guide/quick-start">
インストールと実行の完全ガイド
</Card>
<Card title="アーキテクチャ" icon="sitemap" href="/ja/intro/architecture">
CowAgent システムアーキテクチャ設計
</Card>
</CardGroup>
## 免責事項
1. 本プロジェクトは [MIT License](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/LICENSE) に基づき、技術研究および学習を目的としています。利用者は現地の法律、規制、ポリシー、および企業の社内規程を遵守する必要があります。違法行為や権利侵害につながる利用は禁止されています。
2. Agent モードは通常のチャットモードよりも多くのトークンを消費します。効果とコストを考慮してモデルを選択してください。Agent はホスト OS にアクセスできるため、デプロイには十分注意してください。
3. CowAgent はオープンソース開発に注力しており、いかなる暗号通貨の発行、認可、参加も行っておりません。
## コミュニティ
WeChat でアシスタントを追加して、オープンソースコミュニティに参加しましょう:
<img width="140" src="https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/open-community.png" />

66
docs/ja/memory.mdx Normal file
View File

@@ -0,0 +1,66 @@
---
title: 記憶
description: CowAgent 長期記憶システム
---
記憶システムにより、Agent は重要な情報を長期にわたって記憶し、継続的に経験を蓄積し、ユーザーの好みを理解し、真に自律的な思考と継続的な成長を実現できます。
## 記憶の種類
### コア記憶 (MEMORY.md)
`~/cow/MEMORY.md` に保存され、長期的なユーザーの好み、重要な決定、主要な事実など、時間が経っても薄れない情報を含みます。毎回の会話ターンでバックグラウンド知識としてシステムプロンプトに自動的に注入されます。
### 日次記憶 (memory/YYYY-MM-DD.md)
`~/cow/memory/` ディレクトリに保存され、日付で命名されます(例:`2026-03-08.md`)。日々の会話の要約と主要なイベントを記録します。空ファイルの生成を避けるため、最初の書き込み時にのみファイルが作成されます。
## 記憶の書き込み
Agent は以下のメカニズムにより、会話内容を日次記憶に自動的に永続化します:
- **コンテキストトリミング時** — 会話ターン数またはトークン数が設定上限を超えた場合、コンテキストの古い半分が一括でトリミングされ、破棄されたコンテンツは LLM によって要約されて重要な情報として日次記憶ファイルに書き込まれます
- **毎日のスケジュール要約** — 毎日 23:55 に自動的にフル要約がトリガーされ、アクティビティが少ない日でも記憶が保存されます(内容が変更されていない場合はスキップ)
- **API コンテキストオーバーフロー時** — モデル API がコンテキストオーバーフローエラーを返した場合、緊急措置として現在の会話要約が保存されます
すべての記憶書き込みはバックグラウンドスレッドで非同期に実行されLLM の要約 + ファイル書き込み)、通常の会話応答をブロックしません。
## 初回起動
初回起動時に、Agent はユーザーに主要な情報を積極的に尋ね、ワークスペース(デフォルト `~/cow`)に保存します:
| ファイル | 説明 |
| --- | --- |
| `system.md` | Agent のシステムプロンプトと動作設定 |
| `user.md` | ユーザーの身元情報と好み |
| `MEMORY.md` | コア記憶(長期) |
| `memory/YYYY-MM-DD.md` | 日次記憶(オンデマンドで作成) |
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260203000455.png" width="800" />
</Frame>
## 記憶の検索
記憶システムはハイブリッド検索モードをサポートしています:
- **キーワード検索** — キーワードに基づいて過去の記憶をマッチング
- **ベクトル検索** — セマンティック類似性検索により、異なる表現でも関連する記憶を発見
Agent は必要に応じて会話中に自動的に記憶検索をトリガーし、関連する過去の情報をコンテキストに組み込みます。コア記憶(`MEMORY.md`)は常にシステムプロンプトに注入され、日次記憶は検索を通じてオンデマンドで読み込まれます。
## 設定
```json
{
"agent_workspace": "~/cow",
"agent_max_context_tokens": 40000,
"agent_max_context_turns": 20
}
```
| パラメータ | 説明 | デフォルト |
| --- | --- | --- |
| `agent_workspace` | ワークスペースパス、記憶ファイルはこのディレクトリ配下に保存されます | `~/cow` |
| `agent_max_context_tokens` | 最大コンテキストトークン数。超過時に半分がトリミングされ、記憶として要約されます | `40000` |
| `agent_max_context_turns` | 最大コンテキストターン数。超過時に半分がトリミングされ、記憶として要約されます | `20` |

17
docs/ja/models/claude.mdx Normal file
View File

@@ -0,0 +1,17 @@
---
title: Claude
description: Claudeモデルの設定
---
```json
{
"model": "claude-sonnet-4-6",
"claude_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `claude-sonnet-4-6`、`claude-opus-4-6`、`claude-sonnet-4-5`、`claude-sonnet-4-0`、`claude-3-5-sonnet-latest`などから選択可能。[公式モデル一覧](https://docs.anthropic.com/en/docs/about-claude/models/overview)を参照 |
| `claude_api_key` | [Claude Console](https://console.anthropic.com/settings/keys)で作成 |
| `claude_api_base` | 任意。デフォルトは`https://api.anthropic.com/v1`。サードパーティプロキシを使用する場合に変更 |

View File

@@ -0,0 +1,139 @@
---
title: Coding Plan
description: Coding Planモデルの設定
---
> Coding Planは各プロバイダーが提供する月額サブスクリプションパッケージで、高頻度のAgent利用に最適です。CowAgentはOpenAI互換モードにより、すべてのCoding Planプロバイダーをサポートしています。
<Note>
Coding PlanのAPI BaseとAPI Keyは、通常の従量課金制のものとは別になっています。各プロバイダーのプラットフォームから取得してください。
</Note>
## 共通設定
すべてのプロバイダーはOpenAI互換プロトコルでアクセスでき、Webコンソールから素早く設定できます。モデルプロバイダーを**OpenAI**に設定し、カスタムモデルを選択してモデルコードを入力し、対応するプロバイダーのAPI BaseとAPI Keyを入力してください:
<img src="https://cdn.link-ai.tech/doc/20260318113134.png" width="800"/>
`config.json`で直接設定することも可能です:
```json
{
"bot_type": "openai",
"model": "MODEL_NAME",
"open_ai_api_base": "PROVIDER_CODING_PLAN_API_BASE",
"open_ai_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `bot_type` | `openai`を指定OpenAI互換モード |
| `model` | プロバイダーがサポートするモデル名 |
| `open_ai_api_base` | プロバイダーのCoding Plan API Base URL |
| `open_ai_api_key` | プロバイダーのCoding Plan API Key |
---
## 阿里云
```json
{
"bot_type": "openai",
"model": "qwen3.5-plus",
"open_ai_api_base": "https://coding.dashscope.aliyuncs.com/v1",
"open_ai_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `qwen3.5-plus`、`qwen3-max-2026-01-23`、`qwen3-coder-next`、`qwen3-coder-plus`、`glm-5`、`glm-4.7`、`kimi-k2.5`、`MiniMax-M2.5` |
| `open_ai_api_base` | `https://coding.dashscope.aliyuncs.com/v1` |
| `open_ai_api_key` | Coding Plan専用キー従量課金とは共有不可 |
参考: [クイックスタート](https://help.aliyun.com/zh/model-studio/coding-plan-quickstart?spm=a2c4g.11186623.help-menu-2400256.d_0_2_1.70115203zi5Igc)、[モデル一覧](https://help.aliyun.com/zh/model-studio/coding-plan)
---
## MiniMax
```json
{
"bot_type": "openai",
"model": "MiniMax-M2.5",
"open_ai_api_base": "https://api.minimaxi.com/v1",
"open_ai_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `MiniMax-M2.5`、`MiniMax-M2.5-highspeed`、`MiniMax-M2.1`、`MiniMax-M2` |
| `open_ai_api_base` | 中国: `https://api.minimaxi.com/v1`、グローバル: `https://api.minimax.io/v1` |
| `open_ai_api_key` | Coding Plan専用キー従量課金とは共有不可 |
参考: [中国キー](https://platform.minimaxi.com/docs/coding-plan/quickstart)、[モデル一覧](https://platform.minimaxi.com/docs/guides/pricing-coding-plan)、[グローバルキー](https://platform.minimax.io/docs/coding-plan/quickstart)
---
## 智谱 GLM
```json
{
"bot_type": "openai",
"model": "glm-4.7",
"open_ai_api_base": "https://open.bigmodel.cn/api/coding/paas/v4",
"open_ai_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `glm-5`、`glm-4.7`、`glm-4.6`、`glm-4.5`、`glm-4.5-air` |
| `open_ai_api_base` | 中国: `https://open.bigmodel.cn/api/coding/paas/v4`、グローバル: `https://api.z.ai/api/coding/paas/v4` |
| `open_ai_api_key` | 標準APIと共有 |
参考: [中国クイックスタート](https://docs.bigmodel.cn/cn/coding-plan/quick-start)、[グローバルクイックスタート](https://docs.z.ai/devpack/quick-start)
---
## Kimi
```json
{
"bot_type": "openai",
"model": "kimi-for-coding",
"open_ai_api_base": "https://api.kimi.com/coding/v1",
"open_ai_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `kimi-for-coding` |
| `open_ai_api_base` | `https://api.kimi.com/coding/v1` |
| `open_ai_api_key` | Coding Plan専用キー従量課金とは共有不可 |
参考: [キー & ドキュメント](https://www.kimi.com/code/docs/)
---
## 火山引擎
```json
{
"bot_type": "openai",
"model": "Doubao-Seed-2.0-Code",
"open_ai_api_base": "https://ark.cn-beijing.volces.com/api/coding/v3",
"open_ai_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `Doubao-Seed-2.0-Code`、`Doubao-Seed-2.0-pro`、`Doubao-Seed-2.0-lite`、`Doubao-Seed-Code`、`MiniMax-M2.5`、`Kimi-K2.5`、`GLM-4.7`、`DeepSeek-V3.2` |
| `open_ai_api_base` | `https://ark.cn-beijing.volces.com/api/coding/v3` |
| `open_ai_api_key` | 標準APIと共有 |
参考: [クイックスタート](https://www.volcengine.com/docs/82379/1928261?lang=zh)

View File

@@ -0,0 +1,22 @@
---
title: DeepSeek
description: DeepSeekモデルの設定
---
OpenAI互換の設定を使用します:
```json
{
"model": "deepseek-chat",
"bot_type": "openai",
"open_ai_api_key": "YOUR_API_KEY",
"open_ai_api_base": "https://api.deepseek.com/v1"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `deepseek-chat` (DeepSeek-V3)、`deepseek-reasoner` (DeepSeek-R1) |
| `bot_type` | `openai`を指定OpenAI互換モード |
| `open_ai_api_key` | [DeepSeek Platform](https://platform.deepseek.com/api_keys)で作成 |
| `open_ai_api_base` | DeepSeekプラットフォームのBASE URL |

17
docs/ja/models/doubao.mdx Normal file
View File

@@ -0,0 +1,17 @@
---
title: Doubao (ByteDance)
description: Doubao (火山方舟) モデルの設定
---
```json
{
"model": "doubao-seed-2-0-code-preview-260215",
"ark_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `doubao-seed-2-0-code-preview-260215`、`doubao-seed-2-0-pro-260215`、`doubao-seed-2-0-lite-260215`などから選択可能 |
| `ark_api_key` | [火山方舟 Console](https://console.volcengine.com/ark/region:ark+cn-beijing/apikey)で作成 |
| `ark_base_url` | 任意。デフォルトは`https://ark.cn-beijing.volces.com/api/v3` |

16
docs/ja/models/gemini.mdx Normal file
View File

@@ -0,0 +1,16 @@
---
title: Gemini
description: Google Geminiモデルの設定
---
```json
{
"model": "gemini-3.1-pro-preview",
"gemini_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `gemini-3.1-flash-lite-preview`、`gemini-3.1-pro-preview`、`gemini-3-flash-preview`、`gemini-3-pro-preview`などから選択可能。[公式ドキュメント](https://ai.google.dev/gemini-api/docs/models)を参照 |
| `gemini_api_key` | [Google AI Studio](https://aistudio.google.com/app/apikey)で作成 |

27
docs/ja/models/glm.mdx Normal file
View File

@@ -0,0 +1,27 @@
---
title: GLM (智谱AI)
description: 智谱AI GLMモデルの設定
---
```json
{
"model": "glm-5-turbo",
"zhipu_ai_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `glm-5-turbo`、`glm-5`、`glm-4.7`、`glm-4-plus`、`glm-4-flash`、`glm-4-air`などから選択可能。[モデルコード](https://bigmodel.cn/dev/api/normal-model/glm-4)を参照 |
| `zhipu_ai_api_key` | [智谱AI Console](https://www.bigmodel.cn/usercenter/proj-mgmt/apikeys)で作成 |
OpenAI互換の設定もサポートしています:
```json
{
"bot_type": "openai",
"model": "glm-5-turbo",
"open_ai_api_base": "https://open.bigmodel.cn/api/paas/v4",
"open_ai_api_key": "YOUR_API_KEY"
}
```

55
docs/ja/models/index.mdx Normal file
View File

@@ -0,0 +1,55 @@
---
title: モデル概要
description: CowAgentがサポートするモデルとおすすめの選択肢
---
CowAgentは国内外の主要なLLMをサポートしています。モデルインターフェースはプロジェクトの`models/`ディレクトリに実装されています。
<Note>
Agent モードでは、品質とコストのバランスから以下のモデルをおすすめします: MiniMax-M2.7、glm-5-turbo、kimi-k2.5、qwen3.5-plus、claude-sonnet-4-6、gemini-3.1-pro-preview
</Note>
## 設定
選択したモデルに応じて、`config.json`にモデル名とAPI Keyを設定してください。各モデルは`bot_type`を`openai`に設定し、`open_ai_api_base`と`open_ai_api_key`を設定することで、OpenAI互換アクセスもサポートしています。
また、[LinkAI](https://link-ai.tech)プラットフォームインターフェースを使用すると、ナレッジベース、ワークフロー、その他のAgent機能をサポートしながら、複数のモデルを柔軟に切り替えることができます。
## サポートモデル
<CardGroup cols={2}>
<Card title="MiniMax" href="/ja/models/minimax">
MiniMax-M2.7およびその他のシリーズモデル
</Card>
<Card title="GLM (智谱AI)" href="/ja/models/glm">
glm-5-turbo、glm-5およびその他のシリーズモデル
</Card>
<Card title="Qwen (通义千问)" href="/ja/models/qwen">
qwen3.5-plus、qwen3-maxなど
</Card>
<Card title="Kimi" href="/ja/models/kimi">
kimi-k2.5、kimi-k2など
</Card>
<Card title="Doubao (ByteDance)" href="/ja/models/doubao">
doubao-seedシリーズモデル
</Card>
<Card title="Claude" href="/ja/models/claude">
claude-sonnet-4-6など
</Card>
<Card title="Gemini" href="/ja/models/gemini">
gemini-3.1-pro-previewなど
</Card>
<Card title="OpenAI" href="/ja/models/openai">
gpt-5.4、gpt-4.1、oシリーズなど
</Card>
<Card title="DeepSeek" href="/ja/models/deepseek">
deepseek-chat、deepseek-reasoner
</Card>
<Card title="LinkAI" href="/ja/models/linkai">
統合マルチモデルインターフェース + ナレッジベース
</Card>
</CardGroup>
<Tip>
モデル名の完全なリストについては、プロジェクトの[`common/const.py`](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/common/const.py)ファイルを参照してください。
</Tip>

27
docs/ja/models/kimi.mdx Normal file
View File

@@ -0,0 +1,27 @@
---
title: Kimi (Moonshot)
description: Kimi (Moonshot) モデルの設定
---
```json
{
"model": "kimi-k2.5",
"moonshot_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `kimi-k2.5`、`kimi-k2`、`moonshot-v1-8k`、`moonshot-v1-32k`、`moonshot-v1-128k`から選択可能 |
| `moonshot_api_key` | [Moonshot Console](https://platform.moonshot.cn/console/api-keys)で作成 |
OpenAI互換の設定もサポートしています:
```json
{
"bot_type": "openai",
"model": "kimi-k2.5",
"open_ai_api_base": "https://api.moonshot.cn/v1",
"open_ai_api_key": "YOUR_API_KEY"
}
```

21
docs/ja/models/linkai.mdx Normal file
View File

@@ -0,0 +1,21 @@
---
title: LinkAI
description: LinkAIプラットフォームで複数モデルに統合アクセス
---
[LinkAI](https://link-ai.tech)プラットフォームでは、OpenAI、Claude、Gemini、DeepSeek、Qwen、Kimiなどのモデルを柔軟に切り替えることができ、ナレッジベース、ワークフロー、プラグイン、その他のAgent機能をサポートしています。
```json
{
"use_linkai": true,
"linkai_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `use_linkai` | `true`に設定してLinkAIインターフェースを有効化 |
| `linkai_api_key` | [LinkAI Console](https://link-ai.tech/console/interface)で作成 |
| `model` | 空のままにするとAgentのデフォルトモデルを使用。プラットフォーム上で柔軟に切り替え可能。[モデル一覧](https://link-ai.tech/console/models)のすべてのモデルをサポート |
詳細は[APIドキュメント](https://docs.link-ai.tech/platform/api)を参照してください。

View File

@@ -0,0 +1,27 @@
---
title: MiniMax
description: MiniMaxモデルの設定
---
```json
{
"model": "MiniMax-M2.7",
"minimax_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `MiniMax-M2.7`、`MiniMax-M2.5`、`MiniMax-M2.1`、`MiniMax-M2.1-lightning`、`MiniMax-M2`などから選択可能 |
| `minimax_api_key` | [MiniMax Console](https://platform.minimaxi.com/user-center/basic-information/interface-key)で作成 |
OpenAI互換の設定もサポートしています:
```json
{
"bot_type": "openai",
"model": "MiniMax-M2.7",
"open_ai_api_base": "https://api.minimaxi.com/v1",
"open_ai_api_key": "YOUR_API_KEY"
}
```

19
docs/ja/models/openai.mdx Normal file
View File

@@ -0,0 +1,19 @@
---
title: OpenAI
description: OpenAIモデルの設定
---
```json
{
"model": "gpt-5.4",
"open_ai_api_key": "YOUR_API_KEY",
"open_ai_api_base": "https://api.openai.com/v1"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | OpenAI APIの[modelパラメータ](https://platform.openai.com/docs/models)に対応。oシリーズ、gpt-5.4、gpt-5シリーズ、gpt-4.1などをサポート。Agentモードでは`gpt-5.4`を推奨 |
| `open_ai_api_key` | [OpenAI Platform](https://platform.openai.com/api-keys)で作成 |
| `open_ai_api_base` | 任意。サードパーティプロキシを使用する場合に変更 |
| `bot_type` | 公式OpenAIモデルでは不要。Claudeなど非OpenAIモデルをプロキシ経由で使用する場合は`openai`に設定 |

27
docs/ja/models/qwen.mdx Normal file
View File

@@ -0,0 +1,27 @@
---
title: Qwen (通义千问)
description: 通义千问モデルの設定
---
```json
{
"model": "qwen3.5-plus",
"dashscope_api_key": "YOUR_API_KEY"
}
```
| パラメータ | 説明 |
| --- | --- |
| `model` | `qwen3.5-plus`、`qwen3-max`、`qwen-max`、`qwen-plus`、`qwen-turbo`、`qwq-plus`などから選択可能 |
| `dashscope_api_key` | [百炼 Console](https://bailian.console.aliyun.com/?tab=model#/api-key)で作成。[公式ドキュメント](https://bailian.console.aliyun.com/?tab=api#/api)を参照 |
OpenAI互換の設定もサポートしています:
```json
{
"bot_type": "openai",
"model": "qwen3.5-plus",
"open_ai_api_base": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"open_ai_api_key": "YOUR_API_KEY"
}
```

View File

@@ -0,0 +1,24 @@
---
title: 変更履歴
description: CowAgent バージョン履歴
---
| バージョン | 日付 | 説明 |
| --- | --- | --- |
| [2.0.4](/ja/releases/v2.0.4) | 2026.03.22 | 個人WeChatチャネル追加、新モデルサポート、日本語ドキュメント、スクリプトリファクタリングおよび複数修正 |
| [2.0.2](/ja/releases/v2.0.2) | 2026.02.27 | Web Console アップグレード、マルチチャネル同時実行、セッション永続化 |
| [2.0.1](/en/releases/v2.0.1) | 2026.02.27 | 組み込み Web Search ツール、スマートコンテキスト管理、複数の修正 |
| [2.0.0](/en/releases/v2.0.0) | 2026.02.03 | AI スーパーアシスタントへの全面アップグレード |
| 1.7.6 | 2025.05.23 | Web Channel 最適化、AgentMesh プラグイン |
| 1.7.5 | 2025.04.11 | DeepSeek モデル |
| 1.7.4 | 2024.12.13 | Gemini 2.0 モデル、Web Channel |
| 1.7.3 | 2024.10.31 | 安定性の改善、データベース機能 |
| 1.7.2 | 2024.09.26 | ワンクリックインストールスクリプト、o1 モデル |
| 1.7.0 | 2024.08.02 | 讯飞 4.0 モデル、ナレッジベース参照 |
| 1.6.9 | 2024.07.19 | gpt-4o-mini、阿里音声認識 |
| 1.6.8 | 2024.07.05 | Claude 3.5、Gemini 1.5 Pro |
| 1.6.0 | 2024.04.26 | Kimi 統合、gpt-4-turbo アップグレード |
| 1.5.0 | 2023.11.10 | gpt-4-turbo、dall-e-3、tts マルチモーダル |
| 1.0.0 | 2022.12.12 | プロジェクト作成、初の ChatGPT 統合 |
完全な履歴は [GitHub Releases](https://github.com/zhayujie/chatgpt-on-wechat/releases) をご覧ください。

View File

@@ -0,0 +1,63 @@
---
title: v2.0.0
description: CowAgent 2.0 - チャットボットから AI スーパーアシスタントへの全面アップグレード
---
CowAgent 2.0 は、チャットボットから **AI スーパーアシスタント** への包括的なアップグレードです。自律的な思考とタスク計画、長期記憶、コンピューターの操作、Skill の作成と実行が可能です。
**リリース日**: 2026.02.03 | [GitHub Release](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/2.0.0)
## 主な更新内容
### Agent コア
- **複雑なタスク計画**: マルチターン推論による自律的な計画
- **長期記憶**: キーワードおよびベクトル検索による永続的な記憶
- **組み込みツール**: ファイル操作、Bash、ブラウザ、スケジューラなど 10 以上のツール
- **Web 検索**: 組み込みの `web_search` ツール、複数の検索エンジンに対応、対応する API キーを設定して使用
- **Skill システム**: 組み込みおよびカスタム Skill をサポートする Skill エンジン
- **セキュリティとコスト**: シークレット管理、プロンプト制御、トークン制限
### その他
- **チャネル**: 飞书/钉钉 WebSocket 対応、画像・ファイルメッセージ
- **モデル**: claude-sonnet-4-5、gemini-3-pro-preview、glm-4.7、MiniMax-M2.1、qwen3-max
- **デプロイ**: ワンクリックでのインストール、設定、実行、および管理スクリプト
## 長期記憶
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260203000455.png" width="800" />
</Frame>
## タスク計画とツール
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202181130.png" width="800" />
</Frame>
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260203121008.png" width="800" />
</Frame>
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202195402.png" width="800" />
</Frame>
## Skill システム
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202202247.png" width="800" />
</Frame>
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202213219.png" width="800" />
</Frame>
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202234350.png" width="750" />
</Frame>
## コントリビューション
[フィードバックの送信](https://github.com/zhayujie/chatgpt-on-wechat/issues) や [コードのコントリビューション](https://github.com/zhayujie/chatgpt-on-wechat/pulls) を歓迎します。

View File

@@ -0,0 +1,36 @@
---
title: v2.0.1
description: CowAgent 2.0.1 - 組み込み Web Search、スマートコンテキスト管理、複数の修正
---
**リリース日**: 2026.02.27 | [全変更履歴](https://github.com/zhayujie/chatgpt-on-wechat/compare/2.0.0..2.0.1)
## 新機能
- **組み込み Web Search ツール**: Web 検索を Agent の組み込みツールとして統合し、判断コストを削減 ([4f0ea5d](https://github.com/zhayujie/chatgpt-on-wechat/commit/4f0ea5d7568d61db91ff69c91c429e785fd1b1c2))
- **Claude Opus 4.6 モデル対応**: Claude Opus 4.6 モデルのサポートを追加 ([#2661](https://github.com/zhayujie/chatgpt-on-wechat/pull/2661))
- **企业微信の画像認識**: 企业微信チャネルでの画像メッセージ認識をサポート ([#2667](https://github.com/zhayujie/chatgpt-on-wechat/pull/2667))
## 改善
- **スマートコンテキスト管理**: インテリジェントなコンテキストトリミング戦略により、チャットコンテキストのオーバーフローを解決し、トークン制限超過を防止 ([cea7fb7](https://github.com/zhayujie/chatgpt-on-wechat/commit/cea7fb7490c53454602bf05955a0e9f059bcf0fd), [8acf2db](https://github.com/zhayujie/chatgpt-on-wechat/commit/8acf2dbdfe713b84ad74b761b7f86674b1c1904d)) [#2663](https://github.com/zhayujie/chatgpt-on-wechat/issues/2663)
- **ランタイム情報の動的更新**: 動的関数によるシステムプロンプト内のタイムスタンプおよびその他のランタイム情報の自動更新 ([#2655](https://github.com/zhayujie/chatgpt-on-wechat/pull/2655), [#2657](https://github.com/zhayujie/chatgpt-on-wechat/pull/2657))
- **Skill プロンプトの最適化**: Skill システムプロンプト生成を改善し、ツールの説明を簡素化して Agent のパフォーマンスを向上 ([6c21833](https://github.com/zhayujie/chatgpt-on-wechat/commit/6c218331b1f1208ea8be6bf226936d3b556ade3e))
- **GLM カスタム API Base URL**: GLM モデルのカスタム API Base URL をサポート ([#2660](https://github.com/zhayujie/chatgpt-on-wechat/pull/2660))
- **起動スクリプトの最適化**: `run.sh` スクリプトのインタラクションと設定フローを改善 ([#2656](https://github.com/zhayujie/chatgpt-on-wechat/pull/2656))
- **判断ステップのログ記録**: デバッグ用の Agent 判断ステップログを追加 ([cb303e6](https://github.com/zhayujie/chatgpt-on-wechat/commit/cb303e6109c50c8dfef1f5e6c1ec47223bf3cd11))
## バグ修正
- **Scheduler の記憶喪失**: Scheduler ディスパッチャーによる記憶喪失を修正 ([a77a874](https://github.com/zhayujie/chatgpt-on-wechat/commit/a77a8741b500a408c6f5c8868856fb4b018fe9db))
- **空のツール呼び出しと長い結果**: 空のツール呼び出しおよび過度に長いツール結果の処理を修正 ([0542700](https://github.com/zhayujie/chatgpt-on-wechat/commit/0542700f9091ebb08c1a56103b0f0f45f24aa621))
- **OpenAI Function Call**: OpenAI モデルとの Function Call 互換性を修正 ([158c87a](https://github.com/zhayujie/chatgpt-on-wechat/commit/158c87ab8b05bae054cc1b4eacdbb64fc1062ba9))
- **Claude ツール名フィールド**: Claude モデルのレスポンスから余分なツール名フィールドを削除 ([eec10cb](https://github.com/zhayujie/chatgpt-on-wechat/commit/eec10cb5db6a3d5bc12ef606606532237d2c5f6e))
- **MiniMax 推論**: MiniMax モデルの推論コンテンツ処理を最適化し、思考プロセスの出力を非表示化 ([c72cda3](https://github.com/zhayujie/chatgpt-on-wechat/commit/c72cda33864bd1542012ee6e0a8bd8c6c88cb5ed), [72b1cac](https://github.com/zhayujie/chatgpt-on-wechat/commit/72b1cacea1ba0d1f3dedacbab2e088e98fd7e172))
- **GLM 思考プロセス**: GLM モデルの思考プロセス表示を非表示化 ([72b1cac](https://github.com/zhayujie/chatgpt-on-wechat/commit/72b1cacea1ba0d1f3dedacbab2e088e98fd7e172))
- **飞书の接続と SSL**: 飞书チャネルの SSL 証明書エラーおよび接続問題を修正 ([229b14b](https://github.com/zhayujie/chatgpt-on-wechat/commit/229b14b6fcabe7123d53cab1dea39f38dab26d6d), [8674421](https://github.com/zhayujie/chatgpt-on-wechat/commit/867442155e7f095b4f38b0856f8c1d8312b5fcf7))
- **model_type バリデーション**: 非文字列の `model_type` による `AttributeError` を修正 ([#2666](https://github.com/zhayujie/chatgpt-on-wechat/pull/2666))
## プラットフォーム互換性
- **Windows 互換性**: 複数のツールモジュールにおける Windows でのパス処理、ファイルエンコーディング、および `os.getuid()` の利用不可問題を修正 ([051ffd7](https://github.com/zhayujie/chatgpt-on-wechat/commit/051ffd78a372f71a967fd3259e37fe19131f83cf), [5264f7c](https://github.com/zhayujie/chatgpt-on-wechat/commit/5264f7ce18360ee4db5dcb4ebe67307977d40014))

View File

@@ -0,0 +1,98 @@
---
title: v2.0.2
description: CowAgent 2.0.2 - Web Console アップグレード、マルチチャネル同時実行、セッション永続化
---
**リリース日**: 2026.02.27 | [全変更履歴](https://github.com/zhayujie/chatgpt-on-wechat/compare/2.0.1...master)
## ハイライト
### 🖥️ Web Console アップグレード
Web Console が全面的にアップグレードされ、ストリーミング会話出力、ツール実行と推論プロセスの視覚的表示、**モデル、Skill、記憶、チャネル、Agent 設定** のオンライン管理が可能になりました。
#### チャットインターフェース
ストリーミング出力に対応し、Agent の推論プロセスとツール呼び出しをリアルタイムに表示することで、Agent の意思決定を直感的に観察できます:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227180120.png" />
#### モデル管理
設定ファイルを手動で編集せずに、モデル設定をオンラインで管理できます:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227173811.png" />
#### Skill 管理
Agent の Skill をオンラインで表示・管理できます:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227173403.png" />
#### 記憶管理
Agent の記憶をオンラインで表示・管理できます:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227173349.png" />
#### チャネル管理
接続されたチャネルをオンラインで管理し、リアルタイムで接続・切断操作ができます:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227173331.png" />
#### スケジュールタスク
ワンタイムタスク、固定間隔、Cron 式を含むスケジュールタスクをオンラインで表示・管理できます:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227173704.png" />
#### ログ
Agent のランタイムログをリアルタイムで表示し、監視とトラブルシューティングに活用できます:
<img width="850" src="https://cdn.link-ai.tech/doc/20260227173514.png" />
関連コミット: [f1a1413](https://github.com/zhayujie/chatgpt-on-wechat/commit/f1a1413), [c0702c8](https://github.com/zhayujie/chatgpt-on-wechat/commit/c0702c8), [394853c](https://github.com/zhayujie/chatgpt-on-wechat/commit/394853c), [1c71c4e](https://github.com/zhayujie/chatgpt-on-wechat/commit/1c71c4e), [5e3eccb](https://github.com/zhayujie/chatgpt-on-wechat/commit/5e3eccb), [e1dc037](https://github.com/zhayujie/chatgpt-on-wechat/commit/e1dc037), [5edbf4c](https://github.com/zhayujie/chatgpt-on-wechat/commit/5edbf4c), [7d258b5](https://github.com/zhayujie/chatgpt-on-wechat/commit/7d258b5)
### 🔀 マルチチャネル同時実行
複数のチャネル飞书、钉钉、企业微信、Webを同時に実行できるようになりました。各チャネルは独立したスレッドで動作し、互いに干渉しません。
設定方法: `config.json` の `channel_type` にカンマ区切りで複数のチャネルを設定するか、Web Console のチャネル管理ページからリアルタイムでチャネルの接続・切断を行います。
```json
{
"channel_type": "web,feishu,dingtalk"
}
```
関連コミット: [4694594](https://github.com/zhayujie/chatgpt-on-wechat/commit/4694594), [7cce224](https://github.com/zhayujie/chatgpt-on-wechat/commit/7cce224), [7d258b5](https://github.com/zhayujie/chatgpt-on-wechat/commit/7d258b5), [c9adddb](https://github.com/zhayujie/chatgpt-on-wechat/commit/c9adddb)
### 💾 セッション永続化
セッション履歴がローカルの SQLite データベースに永続化されるようになりました。サービス再起動後も会話コンテキストが自動的に復元されます。Web Console の過去の会話も復元されます。
関連コミット: [29bfbec](https://github.com/zhayujie/chatgpt-on-wechat/commit/29bfbec), [9917552](https://github.com/zhayujie/chatgpt-on-wechat/commit/9917552), [925d728](https://github.com/zhayujie/chatgpt-on-wechat/commit/925d728)
## 新モデル
- **Gemini 3.1 Pro Preview**: `gemini-3.1-pro-preview` モデルのサポートを追加 ([52d7cad](https://github.com/zhayujie/chatgpt-on-wechat/commit/52d7cad))
- **Claude 4.6 Sonnet**: `claude-4.6-sonnet` モデルのサポートを追加 ([52d7cad](https://github.com/zhayujie/chatgpt-on-wechat/commit/52d7cad))
- **Qwen3.5 Plus**: `qwen3.5-plus` モデルのサポートを追加 ([e59a289](https://github.com/zhayujie/chatgpt-on-wechat/commit/e59a289))
- **MiniMax M2.5**: `Minimax-M2.5` モデルのサポートを追加 ([48db538](https://github.com/zhayujie/chatgpt-on-wechat/commit/48db538))
- **GLM-5**: `glm-5` モデルのサポートを追加 ([48db538](https://github.com/zhayujie/chatgpt-on-wechat/commit/48db538))
- **Kimi K2.5**: `kimi-k2.5` モデルのサポートを追加 ([48db538](https://github.com/zhayujie/chatgpt-on-wechat/commit/48db538))
- **Doubao 2.0 Code**: コーディング特化型 `doubao-2.0-code` モデルを追加 ([ab28ee5](https://github.com/zhayujie/chatgpt-on-wechat/commit/ab28ee5))
- **DashScope モデル**: 阿里云 DashScope モデル名のサポートを追加 ([ce58f23](https://github.com/zhayujie/chatgpt-on-wechat/commit/ce58f23))
## ウェブサイトとドキュメント
- **公式サイト**: [cowagent.ai](https://cowagent.ai/)
- **ドキュメント**: [docs.cowagent.ai](https://docs.cowagent.ai/)
## バグ修正
- **Gemini 钉钉画像認識**: 钉钉チャネルで Gemini が画像マーカーを処理できない問題を修正 ([05a3304](https://github.com/zhayujie/chatgpt-on-wechat/commit/05a3304)) ([#2670](https://github.com/zhayujie/chatgpt-on-wechat/pull/2670)) Thanks [@SgtPepper114](https://github.com/SgtPepper114)
- **起動スクリプトの依存関係**: `run.sh` スクリプトの依存関係インストール問題を修正 ([b6fc9fa](https://github.com/zhayujie/chatgpt-on-wechat/commit/b6fc9fa))
- **bare except の整理**: より適切な例外処理のため `bare except` を `except Exception` に置換 ([adca89b](https://github.com/zhayujie/chatgpt-on-wechat/commit/adca89b)) ([#2674](https://github.com/zhayujie/chatgpt-on-wechat/pull/2674)) Thanks [@haosenwang1018](https://github.com/haosenwang1018)

View File

@@ -0,0 +1,91 @@
---
title: v2.0.3
description: CowAgent 2.0.3 - 企業微信スマートボットとQQチャネルの追加、Webコンソールファイル処理、メモリシステムのアップグレード
---
## 🔌 新規チャネル
### 企業微信スマートボット
企業微信スマートボット(`wecom_bot`チャネルを追加しました。ストリーミングカードメッセージ出力、テキストと画像メッセージの送受信をサポートし、Webコンソールでチャネルの設定と管理が可能です。
接続ドキュメント:[企業微信スマートボット接続](https://docs.cowagent.ai/channels/wecom-bot)。
関連コミット:[d4480b6](https://github.com/zhayujie/chatgpt-on-wechat/commit/d4480b6), [a42f31f](https://github.com/zhayujie/chatgpt-on-wechat/commit/a42f31f), [4ecd4df](https://github.com/zhayujie/chatgpt-on-wechat/commit/4ecd4df), [8b45d6c](https://github.com/zhayujie/chatgpt-on-wechat/commit/8b45d6c)
### QQ チャネル
QQ 公式ボット(`qq`)チャネルを追加しました。テキストと画像メッセージの送受信をサポートし、プライベートチャットとグループチャットに対応しています。
接続ドキュメント:[QQボット接続](https://docs.cowagent.ai/channels/qq)。
関連コミット:[005a0e1](https://github.com/zhayujie/chatgpt-on-wechat/commit/005a0e1), [a4d54f5](https://github.com/zhayujie/chatgpt-on-wechat/commit/a4d54f5)
## 🖥️ Web コンソールのファイル入力・処理対応
Web コンソールのチャット画面でファイルや画像のアップロードが可能になり、Agent に直接ファイルを送信して処理できます。また、Read ツールに Office ドキュメントWord、Excel、PPTの解析機能を追加しました。
関連コミット:[30c6d9b](https://github.com/zhayujie/chatgpt-on-wechat/commit/30c6d9b)
## 🤖 新規モデル
- **GPT-5.4 シリーズ**`gpt-5.4`、`gpt-5.4-mini`、`gpt-5.4-nano` モデルのサポートを追加 ([1623deb](https://github.com/zhayujie/chatgpt-on-wechat/commit/1623deb))
- **Gemini 3.1 Flash Lite Preview**`gemini-3.1-flash-lite-preview` モデルのサポートを追加 ([ba915f2](https://github.com/zhayujie/chatgpt-on-wechat/commit/ba915f2))
## 💰 Coding Plan サポート
各ベンダーの Coding Planプログラミング月額プランへの接続をサポートしました。OpenAI 互換方式で統一的に接続できます。現在、阿里雲、MiniMax、智譜 GLM、Kimi、火山エンジンなどのベンダーに対応しています。
詳細設定は [Coding Plan ドキュメント](https://docs.cowagent.ai/models/coding-plan) を参照してください。
## 🧠 メモリシステムのアップグレード
メモリ書き込みMemory Flushのアップグレード
- LLM を使用してコンテキストウィンドウを超えた会話内容をインテリジェントに要約し、精製された日次メモリエントリを生成
- 要約はバックグラウンドスレッドで非同期実行され、応答をブロックしない
- コンテキストの一括トリミング戦略を最適化し、フラッシュ頻度を低減
- 日次定期フラッシュのフォールバック機能を追加し、低アクティビティシナリオでのメモリ損失を防止
- コンテキストメモリの損失問題を修正
関連コミット:[022c13f](https://github.com/zhayujie/chatgpt-on-wechat/commit/022c13f), [c116235](https://github.com/zhayujie/chatgpt-on-wechat/commit/c116235)
## 🔧 ツールリファクタリング
- **画像認識**画像認識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スクレイピング**Webスクレイピング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 コンソールで設定変更後、再起動不要で即時反映
- **ワークスペースの永続化**`./cow` Volume マウントを追加。Agent ワークスペースデータ(メモリ、ペルソナ、スキルなど)をホストマシンに永続化し、コンテナの再構築やアップグレードでデータが失われない
## ⚡ パフォーマンス最適化
- **起動高速化**飛書チャネルで依存関係の遅延読み込みを採用し、4-10秒の起動遅延を回避 ([924dc79](https://github.com/zhayujie/chatgpt-on-wechat/commit/924dc79))
- **チャネルの安定性**:チャネル接続の安定性を最適化し、環境変数によるチャネル設定をサポート ([f1c04bc](https://github.com/zhayujie/chatgpt-on-wechat/commit/f1c04bc), [46d97fd](https://github.com/zhayujie/chatgpt-on-wechat/commit/46d97fd))
## 🐛 バグ修正
- **bot_type 設定**Agent モードでの `bot_type` 設定の受け渡し問題を修正 ([#2691](https://github.com/zhayujie/chatgpt-on-wechat/pull/2691)) Thanks [@Weikjssss](https://github.com/Weikjssss)
- **bot_type 優先順位**Agent モードでの `bot_type` の解析優先順位を調整 ([#2692](https://github.com/zhayujie/chatgpt-on-wechat/pull/2692)) Thanks [@6vision](https://github.com/6vision)
- **智譜モデル設定**:智譜の `bot_type` 命名、Web コンソールの永続化、正規表現エスケープの問題を修正 ([#2693](https://github.com/zhayujie/chatgpt-on-wechat/pull/2693)) Thanks [@6vision](https://github.com/6vision)
- **OpenAI 互換レイヤー**`openai_compat` レイヤーによる統一エラー処理 ([#2688](https://github.com/zhayujie/chatgpt-on-wechat/pull/2688)) Thanks [@JasonOA888](https://github.com/JasonOA888)
- **OpenAI 互換移行**:全モデル Bot の `openai_compat` 移行を完了 ([#2689](https://github.com/zhayujie/chatgpt-on-wechat/pull/2689))
- **Gemini ツール呼び出し**Gemini モデルのツール呼び出しマッチング問題を修正 ([eda82ba](https://github.com/zhayujie/chatgpt-on-wechat/commit/eda82ba))
- **セッション並行処理**:セッション並行シナリオでの競合条件の問題を修正 ([9879878](https://github.com/zhayujie/chatgpt-on-wechat/commit/9879878))
- **履歴メッセージの復元**履歴セッションメッセージの不完全な問題を修正。user/assistant のテキストメッセージのみを復元し、ツール呼び出しを除外 ([b788a3d](https://github.com/zhayujie/chatgpt-on-wechat/commit/b788a3d), [a33ce97](https://github.com/zhayujie/chatgpt-on-wechat/commit/a33ce97))
- **飛書グループチャット**:飛書グループチャットシナリオでの `bot_name` 依存を削除 ([b641bff](https://github.com/zhayujie/chatgpt-on-wechat/commit/b641bff))
- **Safari 互換性**Safari ブラウザでの IME Enter キーによるメッセージ誤送信の問題を修正 ([0687916](https://github.com/zhayujie/chatgpt-on-wechat/commit/0687916))
- **Windows 互換性**Windows での bash スタイル `$VAR` 環境変数を `%VAR%` に変換する問題を修正 ([7c67513](https://github.com/zhayujie/chatgpt-on-wechat/commit/7c67513))
- **MiniMax パラメータ**MiniMax モデルの `max_tokens` 制限を追加 ([1767413](https://github.com/zhayujie/chatgpt-on-wechat/commit/1767413))
- **.gitignore 更新**Python ディレクトリの無視ルールを追加 ([#2683](https://github.com/zhayujie/chatgpt-on-wechat/pull/2683)) Thanks [@pelioo](https://github.com/pelioo)
- **AGENT.md の能動的進化**:システムプロンプトでの AGENT.md 更新ガイダンスを最適化。受動的な「ユーザーが変更した時に更新」から、会話中の性格やスタイルの変化を能動的に検出して自動更新するように改善
## 📦 アップグレード方法
ソースコードデプロイの場合は `./run.sh update` でワンクリックアップグレードできます。または手動でコードをプルして再起動してください。詳細は [アップデートドキュメント](https://docs.cowagent.ai/guide/upgrade) を参照。
**リリース日**2026.03.18 | [Full Changelog](https://github.com/zhayujie/chatgpt-on-wechat/compare/2.0.2...master)

View File

@@ -0,0 +1,55 @@
---
title: v2.0.4
description: CowAgent 2.0.4 - 個人WeChat チャネルの追加、新モデルサポート、日本語ドキュメント、スクリプトリファクタリングおよび複数の修正
---
## 🔌 個人WeChat チャネルの追加
個人WeChat`weixin`チャネルを追加しました。本バージョンの最も重要なアップデートです。QRコードをスキャンするだけで CowAgent を個人WeChatに接続でき、以下の機能をサポートします
- **メッセージ送受信**:テキスト、画像、ファイル、動画メッセージの送受信、音声メッセージの受信をサポート
- **QRコードログイン**ターミナルにQRコードを表示、WeChatでスキャンして確認するだけでログイン完了。QRコード期限切れ時は自動更新
- **認証情報の永続化**:ログイン認証情報を `~/.weixin_cow_credentials.json` に自動保存、再起動時に再スキャン不要
- **Session 自動再接続**Session 期限切れ時に旧認証情報を自動クリアし、QRコードログインを再開
- **Web コンソール接続**Web コンソールからWeChatチャネルを追加可能、QRコードログインフローを同期表示
- **Docker・スクリプト対応**`run.sh` と `docker-compose.yml` がWeChat チャネルに対応
接続ドキュメント:[WeChat 接続](https://docs.cowagent.ai/channels/weixin)。
関連コミット:[ce89869](https://github.com/zhayujie/chatgpt-on-wechat/commit/ce89869), [a483ec0](https://github.com/zhayujie/chatgpt-on-wechat/commit/a483ec0), [c1421e0](https://github.com/zhayujie/chatgpt-on-wechat/commit/c1421e0)
## 🤖 新規モデル
- **MiniMax-M2.7**MiniMax-M2.7 モデルのサポートを追加
- **GLM-5-Turbo**:智譜 glm-5-turbo モデルのサポートを追加
関連コミット:[9192f6f](https://github.com/zhayujie/chatgpt-on-wechat/commit/9192f6f)
## 🔧 スクリプトリファクタリング
- **run.sh リファクタリング**:共通ロジックを抽出し、大量の重複コードを削除。スクリプトの行数を 600+ 行から 177 行に圧縮 ([49d8707](https://github.com/zhayujie/chatgpt-on-wechat/commit/49d8707))
- **実行権限**`run.sh` ファイルの権限問題を修正 ([652156e](https://github.com/zhayujie/chatgpt-on-wechat/commit/652156e))
## ⚡ 最適化
- **リクエストヘッダー統一**Agent の各サービスChat、Embedding、Vision、WebSearch 等)の外部リクエストに統一的な識別ヘッダーを追加 ([b4e711f](https://github.com/zhayujie/chatgpt-on-wechat/commit/b4e711f))
- **メッセージ自動修復**:メッセージプロトコルのフォールトトレランスを強化し、フォーマット異常なメッセージシーケンスを自動修復 ([b8b57e3](https://github.com/zhayujie/chatgpt-on-wechat/commit/b8b57e3))
## 🌍 日本語ドキュメント
完全な日本語ドキュメントを追加しました。入門ガイド、チャネル接続、モデル設定などの主要セクションをカバーしています。Thanks [@Ikko Ashimine](https://github.com/ikoamu)
関連コミット:[5487c0b](https://github.com/zhayujie/chatgpt-on-wechat/commit/5487c0b)
## 🐛 バグ修正
- **企業微信ボット互換性**:旧バージョンの `websocket-client` との互換性問題を修正し、統一的な WebSocket 互換レイヤーを追加 ([bc7f627](https://github.com/zhayujie/chatgpt-on-wechat/commit/bc7f627))
- **run.sh PID 取得**`run.sh` でのプロセス PID 取得エラーを修正 ([9febb07](https://github.com/zhayujie/chatgpt-on-wechat/commit/9febb07))
- **飛書エンコーディング**:飛書チャネルのメッセージとログのエンコーディング問題を修正 ([7d0e156](https://github.com/zhayujie/chatgpt-on-wechat/commit/7d0e156))
- **飛書設定**`run.sh` での `feishu_bot_name` への冗長な依存を削除 ([1b5be1b](https://github.com/zhayujie/chatgpt-on-wechat/commit/1b5be1b))
## 📦 アップグレード方法
ソースコードデプロイの場合は `./run.sh update` でワンクリックアップグレードできます。または手動でコードをプルして再起動してください。詳細は [アップデートドキュメント](https://docs.cowagent.ai/guide/upgrade) を参照。
**リリース日**2026.03.22 | [Full Changelog](https://github.com/zhayujie/chatgpt-on-wechat/compare/2.0.3...master)

View File

@@ -0,0 +1,31 @@
---
title: Image Vision
description: OpenAI の Vision モデルを使用して画像を認識
---
OpenAI の GPT-4 Vision API を使用して画像の内容を分析し、画像内のオブジェクト、テキスト、色などの要素を理解します。
## 依存関係
| 依存関係 | 説明 |
| --- | --- |
| `OPENAI_API_KEY` | OpenAI API キー |
| `curl`, `base64` | システムコマンド(通常プリインストール済み) |
設定方法:
- `env_config` Tool で `OPENAI_API_KEY` を設定
- または `config.json` で `open_ai_api_key` を設定
## 対応モデル
- `gpt-4.1-mini`(推奨、コストパフォーマンスに優れる)
- `gpt-4.1`
## 使い方
設定が完了したら、Agent に画像を送信すると自動的に画像認識がトリガーされます。
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202213219.png" width="800" />
</Frame>

67
docs/ja/skills/index.mdx Normal file
View File

@@ -0,0 +1,67 @@
---
title: Skill 概要
description: CowAgent の Skill システム紹介
---
Skill は Agent に無限の拡張性を提供します。各 Skill は説明ファイル(`SKILL.md`)、実行スクリプト(任意)、リソース(任意)で構成され、特定のタスクをどのように遂行するかを記述します。
Skill と Tool の違いTool はコードで実装された原子的な操作ファイルの読み書き、コマンドの実行であるのに対し、Skill は説明ファイルに基づく高レベルなワークフローであり、複数の Tool を組み合わせて複雑なタスクを完遂できます。
## 組み込み Skill
プロジェクトの `skills/` ディレクトリに配置されており、依存条件に基づいて自動的に有効化されます:
| Skill | 説明 | 依存関係 |
| --- | --- | --- |
| [`skill-creator`](/ja/skills/skill-creator) | 会話を通じてカスタム Skill を作成 | なし |
| [`openai-image-vision`](/ja/skills/image-vision) | OpenAI の Vision モデルを使用して画像を認識 | `OPENAI_API_KEY` |
| [`linkai-agent`](/ja/skills/linkai-agent) | LinkAI プラットフォームの Agent を統合 | `LINKAI_API_KEY` |
| [`web-fetch`](/ja/skills/web-fetch) | Web ページのテキストコンテンツを取得 | `curl`(デフォルトで有効) |
## カスタム Skill
ユーザーが会話を通じて作成し、ワークスペース(`~/cow/skills/`)に保存されます。任意の複雑なビジネスプロセスやサードパーティシステムとの連携を実装できます。
## Skill の読み込み優先順位
1. **ワークスペースの Skill**(最高優先):`~/cow/skills/`
2. **プロジェクト組み込み Skill**(最低優先):`skills/`
同名の Skill は優先順位に従って上書きされます。
## Skill のファイル構成
```
skills/
├── my-skill/
│ ├── SKILL.md # Skill の説明frontmatter + 手順)
│ ├── scripts/ # 実行スクリプト(任意)
│ └── resources/ # 追加リソース(任意)
```
### SKILL.md のフォーマット
```markdown
---
name: my-skill
description: Brief description of the skill
metadata:
emoji: 🔧
requires:
bins: ["curl"]
env: ["MY_API_KEY"]
primaryEnv: "MY_API_KEY"
---
# My Skill
Detailed instructions...
```
| フィールド | 説明 |
| --- | --- |
| `name` | Skill 名。ディレクトリ名と一致する必要があります |
| `description` | Skill の説明。Agent はこれに基づいて呼び出すかどうかを判断します |
| `metadata.requires.bins` | 必要なシステムコマンド |
| `metadata.requires.env` | 必要な環境変数 |
| `metadata.always` | 常に読み込む(デフォルトは false |

View File

@@ -0,0 +1,47 @@
---
title: LinkAI Agent
description: LinkAI プラットフォームのマルチ Agent Skill を統合
---
[LinkAI](https://link-ai.tech/) プラットフォームの Agent を Skill として使用し、マルチ Agent の意思決定を行います。Agent は Agent 名と説明に基づいてインテリジェントに選択し、`app_code` を通じて対応するアプリケーションやワークフローを呼び出します。
## 依存関係
| 依存関係 | 説明 |
| --- | --- |
| `LINKAI_API_KEY` | LinkAI プラットフォームの API キー。[コンソール](https://link-ai.tech/console/interface)で作成 |
| `curl` | システムコマンド(通常プリインストール済み) |
設定方法:
- `env_config` Tool で `LINKAI_API_KEY` を設定
- または `config.json` で `linkai_api_key` を設定
## Agent の設定
`skills/linkai-agent/config.json` で利用可能な Agent を追加します:
```json
{
"apps": [
{
"app_code": "G7z6vKwp",
"app_name": "LinkAI Customer Support",
"app_description": "Select this assistant only when the user needs help with LinkAI platform questions"
},
{
"app_code": "SFY5x7JR",
"app_name": "Content Creator",
"app_description": "Use this assistant only when the user needs to create images or videos"
}
]
}
```
## 使い方
設定が完了すると、Agent はユーザーの質問に基づいて適切な LinkAI Agent を自動的に選択します。
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202234350.png" width="750" />
</Frame>

View File

@@ -0,0 +1,31 @@
---
title: Skill Creator
description: 会話を通じてカスタム Skill を作成
---
自然言語の会話を通じて、Skill の作成、インストール、更新を素早く行えます。
## 依存関係
追加の依存関係は不要で、常に利用可能です。
## 使い方
- ワークフローを Skill 化:「このデプロイプロセスから Skill を作成して」
- サードパーティ API の統合:「この API ドキュメントに基づいて Skill を作成して」
- リモート Skill のインストール「xxx Skill をインストールして」
## 作成フロー
1. 作成したい Skill を Agent に伝えます
2. Agent が自動的に `SKILL.md` の説明と実行スクリプトを生成します
3. Skill はワークスペースの `~/cow/skills/` ディレクトリに保存されます
4. 以降の会話で Agent が自動的にその Skill を認識し使用します
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202202247.png" width="800" />
</Frame>
<Tip>
詳細は [Skill Creator のドキュメント](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/skills/skill-creator/SKILL.md)をご覧ください。
</Tip>

View File

@@ -0,0 +1,31 @@
---
title: Web Fetch
description: Web ページのテキストコンテンツを取得
---
curl を使用して Web ページを取得し、読み取り可能なテキストコンテンツを抽出します。ブラウザ自動化を必要としない軽量な Web アクセス方法です。
## 依存関係
| 依存関係 | 説明 |
| --- | --- |
| `curl` | システムコマンド(通常プリインストール済み) |
この Skill は `always: true` が設定されており、システムに `curl` コマンドがあればデフォルトで有効になります。
## 使い方
Agent が URL からコンテンツを取得する必要がある場合に自動的に呼び出されます。追加の設定は不要です。
## browser Tool との比較
| 機能 | web-fetch (Skill) | browser (Tool) |
| --- | --- | --- |
| 依存関係 | curl のみ | browser-use + playwright |
| JS レンダリング | 非対応 | 対応 |
| ページ操作 | 非対応 | クリック、入力などに対応 |
| 最適な用途 | 静的ページのテキスト | 動的な Web ページ |
<Tip>
ほとんどの Web コンテンツ取得シナリオでは、web-fetch で十分です。JS レンダリングやページ操作が必要な場合にのみ browser Tool を使用してください。
</Tip>

28
docs/ja/tools/bash.mdx Normal file
View File

@@ -0,0 +1,28 @@
---
title: bash - ターミナル
description: システムコマンドの実行
---
現在の作業ディレクトリでBashコマンドを実行し、stdoutとstderrを返します。`env_config` で設定されたAPIキーは自動的に環境変数に注入されます。
## 依存関係
追加の依存関係は不要で、デフォルトで利用可能です。
## パラメータ
| パラメータ | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| `command` | string | はい | 実行するコマンド |
| `timeout` | integer | いいえ | タイムアウト(秒) |
## ユースケース
- パッケージや依存関係のインストール
- コードやテストの実行
- アプリケーションやサービスのデプロイNginx設定、プロセス管理など
- システム管理とトラブルシューティング
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260203121008.png" width="800" />
</Frame>

25
docs/ja/tools/browser.mdx Normal file
View File

@@ -0,0 +1,25 @@
---
title: browser - ブラウザ
description: Webページへのアクセスと操作
---
ブラウザを使用してWebページにアクセス・操作します。JavaScriptでレンダリングされる動的ページにも対応しています。
## 依存関係
| 依存関係 | インストールコマンド |
| --- | --- |
| `browser-use` ≥ 0.1.40 | `pip install browser-use` |
| `markdownify` | `pip install markdownify` |
| `playwright` + chromium | `pip install playwright && playwright install chromium` |
## ユースケース
- 特定のURLにアクセスしてページ内容を取得
- Webページの要素を操作クリック、入力など
- デプロイされたWebページの検証
- JSレンダリングが必要な動的コンテンツのスクレイピング
<Note>
ブラウザToolは依存関係が大きいため、不要な場合はインストールを省略できます。軽量なWebコンテンツ取得には、代わりに `web-fetch` Skillをご利用ください。
</Note>

24
docs/ja/tools/edit.mdx Normal file
View File

@@ -0,0 +1,24 @@
---
title: edit - ファイル編集
description: テキスト置換によるファイル編集
---
テキスト置換によるファイル編集を行います。`oldText` が空の場合、ファイル末尾に追記します。
## 依存関係
追加の依存関係は不要で、デフォルトで利用可能です。
## パラメータ
| パラメータ | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| `path` | string | はい | ファイルパス |
| `oldText` | string | はい | 置換対象の元テキスト(空の場合は追記) |
| `newText` | string | はい | 置換後のテキスト |
## ユースケース
- 設定ファイルの特定パラメータの変更
- コードのバグ修正
- ファイル内の特定位置へのコンテンツ挿入

View File

@@ -0,0 +1,36 @@
---
title: env_config - 環境設定
description: APIキーとシークレットの管理
---
ワークスペースの `.env` ファイルで環境変数APIキーやシークレットを管理し、会話形式で安全に更新できます。セキュリティ保護とマスキング機能を内蔵しています。
## 依存関係
| 依存関係 | インストールコマンド |
| --- | --- |
| `python-dotenv` ≥ 1.0.0 | `pip install python-dotenv>=1.0.0` |
オプション依存関係のインストールに含まれています:`pip3 install -r requirements-optional.txt`
## パラメータ
| パラメータ | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| `action` | string | はい | 操作タイプ:`get`、`set`、`list`、`delete` |
| `key` | string | いいえ | 環境変数名 |
| `value` | string | いいえ | 環境変数の値(`set` の場合のみ) |
## 使い方
設定したいキーをAgentに伝えると、自動的にこのToolが呼び出されます
- 「BOCHA_API_KEYを設定して」
- 「OPENAI_API_KEYをsk-xxxに設定して」
- 「設定済みの環境変数を表示して」
設定されたキーは `bash` Toolの実行環境に自動的に注入されます。
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202234939.png" width="800" />
</Frame>

50
docs/ja/tools/index.mdx Normal file
View File

@@ -0,0 +1,50 @@
---
title: Tools 概要
description: CowAgent 組み込みToolシステム
---
Toolは、AgentがOSリソースにアクセスするための中核機能です。Agentはタスクの要件に基づいてToolをインテリジェントに選択・呼び出し、ファイル操作、コマンド実行、Web検索、スケジュールタスクなどを実行します。Toolは `agent/tools/` ディレクトリに実装されています。
## 組み込みTool
以下のToolは追加設定なしでデフォルトで利用可能です
<CardGroup cols={2}>
<Card title="read - ファイル読み取り" icon="file" href="/ja/tools/read">
ファイル内容を読み取り、テキスト・画像・PDFに対応
</Card>
<Card title="write - ファイル書き込み" icon="pen" href="/ja/tools/write">
ファイルの作成または上書き
</Card>
<Card title="edit - ファイル編集" icon="pen-to-square" href="/ja/tools/edit">
テキスト置換によるファイル編集
</Card>
<Card title="ls - ディレクトリ一覧" icon="folder-open" href="/ja/tools/ls">
ディレクトリの内容を一覧表示
</Card>
<Card title="bash - ターミナル" icon="terminal" href="/ja/tools/bash">
システムコマンドの実行
</Card>
<Card title="send - ファイル送信" icon="paper-plane" href="/ja/tools/send">
ファイルや画像をユーザーに送信
</Card>
<Card title="memory - メモリ" icon="brain" href="/ja/tools/memory">
長期メモリの検索と読み取り
</Card>
</CardGroup>
## オプションTool
以下のToolは追加の依存関係またはAPIキーの設定が必要です
<CardGroup cols={2}>
<Card title="env_config - 環境設定" icon="key" href="/ja/tools/env-config">
APIキーとシークレットの管理
</Card>
<Card title="scheduler - スケジューラ" icon="clock" href="/ja/tools/scheduler">
スケジュールタスクの作成と管理
</Card>
<Card title="web_search - Web検索" icon="magnifying-glass" href="/ja/tools/web-search">
インターネットからリアルタイム情報を検索
</Card>
</CardGroup>

23
docs/ja/tools/ls.mdx Normal file
View File

@@ -0,0 +1,23 @@
---
title: ls - ディレクトリ一覧
description: ディレクトリの内容を一覧表示
---
ディレクトリの内容をアルファベット順にソートして一覧表示します。ディレクトリには `/` が付与され、隠しファイルも含まれます。
## 依存関係
追加の依存関係は不要で、デフォルトで利用可能です。
## パラメータ
| パラメータ | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| `path` | string | はい | ディレクトリパス。相対パスはワークスペースディレクトリを基準とします |
| `limit` | integer | いいえ | 返すエントリの最大数、デフォルト500 |
## ユースケース
- プロジェクト構造の閲覧
- 特定ファイルの検索
- ディレクトリの存在確認

36
docs/ja/tools/memory.mdx Normal file
View File

@@ -0,0 +1,36 @@
---
title: memory - メモリ
description: 長期メモリの検索と読み取り
---
メモリToolには `memory_search`(メモリ検索)と `memory_get`メモリファイル読み取りの2つのサブToolがあります。
## 依存関係
追加の依存関係は不要で、デフォルトで利用可能です。Agent Coreのメモリシステムによって管理されます。
## memory_search
キーワードとベクトルのハイブリッド検索で過去のメモリを検索します。
| パラメータ | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| `query` | string | はい | 検索クエリ |
## memory_get
特定のメモリファイルの内容を読み取ります。
| パラメータ | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| `path` | string | はい | メモリファイルの相対パス(例:`MEMORY.md`、`memory/2026-01-01.md` |
| `start_line` | integer | いいえ | 開始行番号 |
| `end_line` | integer | いいえ | 終了行番号 |
## 仕組み
Agentは以下のシナリオでメモリToolを自動的に呼び出します
- ユーザーが重要な情報を共有した場合 → メモリに保存
- 過去のコンテキストが必要な場合 → 関連するメモリを検索
- 会話が一定の長さに達した場合 → 要約を抽出して保存

24
docs/ja/tools/read.mdx Normal file
View File

@@ -0,0 +1,24 @@
---
title: read - ファイル読み取り
description: ファイル内容の読み取り
---
ファイルの内容を読み取ります。テキストファイル、PDFファイル、画像メタデータを返すなどに対応しています。
## 依存関係
追加の依存関係は不要で、デフォルトで利用可能です。
## パラメータ
| パラメータ | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| `path` | string | はい | ファイルパス。相対パスはワークスペースディレクトリを基準とします |
| `offset` | integer | いいえ | 開始行番号1始まり。負の値は末尾からの読み取り |
| `limit` | integer | いいえ | 読み取る行数 |
## ユースケース
- 設定ファイルやログファイルの閲覧
- コードファイルの読み取りと分析
- 画像・動画ファイルの情報確認

View File

@@ -0,0 +1,40 @@
---
title: scheduler - スケジューラ
description: スケジュールタスクの作成と管理
---
柔軟なスケジュール設定と実行モードを備えた、動的スケジュールタスクの作成と管理を行います。
## 依存関係
| 依存関係 | インストールコマンド |
| --- | --- |
| `croniter` ≥ 2.0.0 | `pip install croniter>=2.0.0` |
コア依存関係に含まれています:`pip3 install -r requirements.txt`
## スケジュールモード
| モード | 説明 |
| --- | --- |
| ワンタイム | 指定した時刻に1回だけ実行 |
| 固定間隔 | 一定の時間間隔で繰り返し実行 |
| Cron式 | Cron構文を使用した複雑なスケジュール定義 |
## 実行モード
- **固定メッセージ**: トリガー時にプリセットメッセージを送信
- **Agent動的タスク**: トリガー時にAgentがインテリジェントにタスクを実行
## 使い方
自然言語でスケジュールタスクを作成・管理できます:
- 「毎朝9時に天気予報を送って」
- 「2時間ごとにサーバーのステータスを確認して」
- 「明日の午後3時に会議のリマインドをして」
- 「すべてのスケジュールタスクを表示して」
<Frame>
<img src="https://cdn.link-ai.tech/doc/20260202195402.png" width="800" />
</Frame>

Some files were not shown because too many files have changed in this diff Show More