mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-06-02 00:57:41 +08:00
feat: add knowledge switch and cli
This commit is contained in:
@@ -318,10 +318,12 @@ class MemoryManager:
|
||||
await self._sync_file(file_path, "memory", scope, user_id)
|
||||
|
||||
# Scan knowledge directory (structured knowledge wiki)
|
||||
knowledge_dir = Path(workspace_dir) / "knowledge"
|
||||
if knowledge_dir.exists():
|
||||
for file_path in knowledge_dir.rglob("*.md"):
|
||||
await self._sync_file(file_path, "knowledge", "shared", None)
|
||||
from config import conf
|
||||
if conf().get("knowledge", True):
|
||||
knowledge_dir = Path(workspace_dir) / "knowledge"
|
||||
if knowledge_dir.exists():
|
||||
for file_path in knowledge_dir.rglob("*.md"):
|
||||
await self._sync_file(file_path, "knowledge", "shared", None)
|
||||
|
||||
self._dirty = False
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ from typing import List, Dict, Optional, Any
|
||||
from dataclasses import dataclass
|
||||
|
||||
from common.log import logger
|
||||
from config import conf
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -129,7 +130,8 @@ def build_agent_system_prompt(
|
||||
sections.extend(_build_memory_section(memory_manager, tools, language))
|
||||
|
||||
# 3.5 知识系统(结构化知识库)
|
||||
sections.extend(_build_knowledge_section(workspace_dir, language))
|
||||
if conf().get("knowledge", True):
|
||||
sections.extend(_build_knowledge_section(workspace_dir, language))
|
||||
|
||||
# 4. 工作空间(工作环境说明)
|
||||
sections.extend(_build_workspace_section(workspace_dir, language))
|
||||
|
||||
@@ -68,8 +68,11 @@ def ensure_workspace(workspace_dir: str, create_templates: bool = True) -> Works
|
||||
websites_dir = os.path.join(workspace_dir, "websites")
|
||||
os.makedirs(websites_dir, exist_ok=True)
|
||||
|
||||
knowledge_dir = os.path.join(workspace_dir, "knowledge")
|
||||
os.makedirs(knowledge_dir, exist_ok=True)
|
||||
from config import conf
|
||||
knowledge_enabled = conf().get("knowledge", True)
|
||||
if knowledge_enabled:
|
||||
knowledge_dir = os.path.join(workspace_dir, "knowledge")
|
||||
os.makedirs(knowledge_dir, exist_ok=True)
|
||||
|
||||
# 如果需要,创建模板文件
|
||||
if create_templates:
|
||||
@@ -77,14 +80,15 @@ def ensure_workspace(workspace_dir: str, create_templates: bool = True) -> Works
|
||||
_create_template_if_missing(user_path, _get_user_template())
|
||||
_create_template_if_missing(rule_path, _get_rule_template())
|
||||
_create_template_if_missing(memory_path, _get_memory_template())
|
||||
_create_template_if_missing(
|
||||
os.path.join(knowledge_dir, "index.md"),
|
||||
_get_knowledge_index_template()
|
||||
)
|
||||
_create_template_if_missing(
|
||||
os.path.join(knowledge_dir, "log.md"),
|
||||
_get_knowledge_log_template()
|
||||
)
|
||||
if knowledge_enabled:
|
||||
_create_template_if_missing(
|
||||
os.path.join(knowledge_dir, "index.md"),
|
||||
_get_knowledge_index_template()
|
||||
)
|
||||
_create_template_if_missing(
|
||||
os.path.join(knowledge_dir, "log.md"),
|
||||
_get_knowledge_log_template()
|
||||
)
|
||||
|
||||
# Only create BOOTSTRAP.md for brand new workspaces;
|
||||
# agent deletes it after completing onboarding
|
||||
|
||||
@@ -210,6 +210,10 @@ class SkillManager:
|
||||
if not include_disabled:
|
||||
entries = [e for e in entries if self.is_skill_enabled(e.skill.name)]
|
||||
|
||||
from config import conf
|
||||
if not conf().get("knowledge", True):
|
||||
entries = [e for e in entries if e.skill.name != "knowledge-wiki"]
|
||||
|
||||
return entries
|
||||
|
||||
def filter_unavailable_skills(
|
||||
|
||||
@@ -45,7 +45,8 @@
|
||||
.msg-content h1 { font-size: 1.4em; }
|
||||
.msg-content h2 { font-size: 1.25em; }
|
||||
.msg-content h3 { font-size: 1.1em; }
|
||||
.msg-content ul, .msg-content ol { margin: 0.5em 0; padding-left: 1.8em; }
|
||||
.msg-content ul { margin: 0.5em 0; padding-left: 1.8em; list-style: disc; }
|
||||
.msg-content ol { margin: 0.5em 0; padding-left: 1.8em; list-style: decimal; }
|
||||
.msg-content li { margin: 0.25em 0; }
|
||||
.msg-content pre {
|
||||
border-radius: 8px; overflow-x: auto; margin: 0.8em 0;
|
||||
|
||||
@@ -496,6 +496,10 @@ const SLASH_COMMANDS = [
|
||||
{ cmd: '/skill info ', desc: '查看技能详情' },
|
||||
{ cmd: '/skill enable ', desc: '启用技能' },
|
||||
{ cmd: '/skill disable ', desc: '禁用技能' },
|
||||
{ cmd: '/knowledge', desc: '查看知识库统计' },
|
||||
{ cmd: '/knowledge list', desc: '查看知识库文件树' },
|
||||
{ cmd: '/knowledge on', desc: '开启知识库' },
|
||||
{ cmd: '/knowledge off', desc: '关闭知识库' },
|
||||
{ cmd: '/config', desc: '查看当前配置' },
|
||||
{ cmd: '/logs', desc: '查看最近日志' },
|
||||
{ cmd: '/version', desc: '查看版本' },
|
||||
|
||||
@@ -199,6 +199,7 @@ available_setting = {
|
||||
"agent_max_context_tokens": 50000, # Agent模式下最大上下文tokens
|
||||
"agent_max_context_turns": 30, # Agent模式下最大上下文记忆轮次
|
||||
"agent_max_steps": 15, # Agent模式下单次运行最大决策步数
|
||||
"knowledge": True, # 是否开启知识库功能
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ KNOWN_COMMANDS = {
|
||||
"help", "version", "status", "logs",
|
||||
"start", "stop", "restart",
|
||||
"skill", "context", "config",
|
||||
"knowledge",
|
||||
"install-browser",
|
||||
}
|
||||
|
||||
@@ -157,6 +158,9 @@ class CowCliPlugin(Plugin):
|
||||
" /config 查看当前配置",
|
||||
" /config <key> 查看某项配置",
|
||||
" /config <key> <val> 修改配置",
|
||||
" /knowledge 查看知识库统计",
|
||||
" /knowledge list 查看知识库文件树",
|
||||
" /knowledge on|off 开启/关闭知识库",
|
||||
"",
|
||||
"💡 也可以用 cow <command> 代替 /<command>",
|
||||
]
|
||||
@@ -310,6 +314,7 @@ class CowCliPlugin(Plugin):
|
||||
"agent_max_context_tokens",
|
||||
"agent_max_context_turns",
|
||||
"agent_max_steps",
|
||||
"knowledge",
|
||||
}
|
||||
|
||||
_CONFIG_READABLE = _CONFIG_WRITABLE | {"channel_type"}
|
||||
@@ -851,6 +856,133 @@ class CowCliPlugin(Plugin):
|
||||
icon = "✅" if enabled else "⬚"
|
||||
return f"{icon} 技能 '{name}' 已{action}"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# knowledge
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _cmd_knowledge(self, args: str, e_context, **_) -> str:
|
||||
sub = args.strip().lower().split(None, 1)[0] if args.strip() else ""
|
||||
|
||||
if sub == "on":
|
||||
return self._knowledge_toggle(True)
|
||||
elif sub == "off":
|
||||
return self._knowledge_toggle(False)
|
||||
elif sub in ("list", "tree"):
|
||||
return self._knowledge_tree()
|
||||
else:
|
||||
return self._knowledge_stats()
|
||||
|
||||
def _knowledge_toggle(self, enabled: bool) -> str:
|
||||
from config import conf
|
||||
import json as _json
|
||||
|
||||
conf()["knowledge"] = enabled
|
||||
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
config_path = os.path.join(project_root, "config.json")
|
||||
try:
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
file_config = _json.load(f)
|
||||
file_config["knowledge"] = enabled
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
_json.dump(file_config, f, indent=4, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
return f"⚠️ 内存中已切换,但写入 config.json 失败: {e}"
|
||||
|
||||
status = "开启 ✅" if enabled else "关闭 ❌"
|
||||
note = "知识库将在下次对话中生效" if enabled else "知识库系统已停用,不再注入提示词和索引知识文件"
|
||||
return f"📚 知识库已{status}\n\n{note}"
|
||||
|
||||
def _knowledge_stats(self) -> str:
|
||||
from config import conf
|
||||
from common.utils import expand_path
|
||||
knowledge_dir = os.path.join(
|
||||
expand_path(conf().get("agent_workspace", "~/cow")),
|
||||
"knowledge"
|
||||
)
|
||||
if not os.path.isdir(knowledge_dir):
|
||||
return "📚 知识库目录不存在\n\n💡 开启知识库: /knowledge on"
|
||||
|
||||
enabled = conf().get("knowledge", True)
|
||||
total_files = 0
|
||||
total_bytes = 0
|
||||
cat_count = {}
|
||||
|
||||
for root, dirs, files in os.walk(knowledge_dir):
|
||||
dirs[:] = [d for d in dirs if not d.startswith(".")]
|
||||
rel_root = os.path.relpath(root, knowledge_dir)
|
||||
category = rel_root.split(os.sep)[0] if rel_root != "." else "root"
|
||||
for f in files:
|
||||
if f.endswith(".md") and f not in ("index.md", "log.md"):
|
||||
total_files += 1
|
||||
total_bytes += os.path.getsize(os.path.join(root, f))
|
||||
cat_count[category] = cat_count.get(category, 0) + 1
|
||||
|
||||
status = "✅ 已开启" if enabled else "❌ 已关闭"
|
||||
lines = [
|
||||
"📚 知识库统计",
|
||||
"",
|
||||
f"状态: {status}",
|
||||
f"页面: {total_files} 篇",
|
||||
f"大小: {total_bytes / 1024:.1f} KB",
|
||||
"",
|
||||
]
|
||||
if cat_count:
|
||||
for cat in sorted(cat_count.keys()):
|
||||
lines.append(f"- {cat}/ ({cat_count[cat]} pages)")
|
||||
lines.append("")
|
||||
|
||||
lines.append(f"路径: {knowledge_dir}")
|
||||
lines.extend([
|
||||
"",
|
||||
"━━━━━━━━━━━━━━━━━━━━━━━━━━",
|
||||
"💡 /knowledge list 查看文件树",
|
||||
"💡 /knowledge on|off 开关知识库",
|
||||
])
|
||||
return "\n".join(lines)
|
||||
|
||||
def _knowledge_tree(self) -> str:
|
||||
from config import conf
|
||||
from common.utils import expand_path
|
||||
knowledge_dir = os.path.join(
|
||||
expand_path(conf().get("agent_workspace", "~/cow")),
|
||||
"knowledge"
|
||||
)
|
||||
if not os.path.isdir(knowledge_dir):
|
||||
return "📚 知识库目录不存在\n\n💡 开启知识库: /knowledge on"
|
||||
|
||||
tree = ["knowledge/"]
|
||||
|
||||
subdirs = sorted([
|
||||
d for d in os.listdir(knowledge_dir)
|
||||
if os.path.isdir(os.path.join(knowledge_dir, d)) and not d.startswith(".")
|
||||
])
|
||||
|
||||
for i, subdir in enumerate(subdirs):
|
||||
is_last_dir = (i == len(subdirs) - 1)
|
||||
branch = "└── " if is_last_dir else "├── "
|
||||
subdir_path = os.path.join(knowledge_dir, subdir)
|
||||
md_files = sorted([
|
||||
f for f in os.listdir(subdir_path)
|
||||
if f.endswith(".md") and not f.startswith(".")
|
||||
])
|
||||
tree.append(f"{branch}{subdir}/ ({len(md_files)})")
|
||||
|
||||
child_prefix = " " if is_last_dir else "│ "
|
||||
max_show = 12
|
||||
for j, fname in enumerate(md_files[:max_show]):
|
||||
is_last_file = (j == len(md_files[:max_show]) - 1) and len(md_files) <= max_show
|
||||
fb = "└── " if is_last_file else "├── "
|
||||
name = fname.replace(".md", "")
|
||||
tree.append(f"{child_prefix}{fb}{name}")
|
||||
if len(md_files) > max_show:
|
||||
tree.append(f"{child_prefix}└── ... +{len(md_files) - max_show} more")
|
||||
|
||||
if not subdirs:
|
||||
tree.append("(空)")
|
||||
|
||||
return "```\n" + "\n".join(tree) + "\n```"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user