前两篇解决了安装启动和日常使用这一篇进入真正的二次开发阶段。重点不再是把系统跑起来也不只是把任务用顺而是建立一套能够持续扩展、稳定发布、可回归验证的工程化改造路径。内容围绕代码结构、后端扩展、前端扩展、Agent 扩展、调度策略、PyQt 启动器、兼容治理、测试发布和运维安全展开目标是把 Edict 从可用系统推进到可迭代平台。目前已整理为一组连续教程分别对应部署启动、使用实战、二开扩展和封装版本使用四个方向。若希望完整了解该项目的源码运行方式、实际操作流程以及封装版本的使用方法建议结合以下文章按需阅读。文章说明【OpenClaw】Edict 三省六部制部署与启动介绍 Edict 三省六部制的基础部署方式、运行环境准备和启动流程【OpenClaw】Edict 三省六部制使用与实战流程介绍系统启动后的主要使用方式、核心流程和实战操作思路【OpenClaw】Edict 三省六部制二开与扩展介绍项目在源码层面的二次开发、扩展思路和能力接入方式AIGC工具平台-Edict 三省六部制 OpenClaw 集成封装版介绍封装后的本地程序获取、启动配置、WebUI 访问和标准使用流程文章目录根目录看板版 vs edict/ 全栈版代码结构后端扩展前端扩展Agent 扩展Worker/调度扩展PyQt 启动器扩展数据与兼容测试与发布运维与安全总结根目录看板版 vs edict/ 全栈版Edict 当前可以明显分成两条开发主线。一条是根目录看板主线另一条是edict/下的全栈主线。二开前最重要的动作不是立刻改代码而是先确定这一轮改动落在哪一条线上。两条线的职责、接口风格和演进程度并不完全一致如果在一次改造中同时重构两条线返工概率会很高。根目录看板主线更适合快速迭代常见入口集中在下面这些位置dashboard/server.py scripts/*.py data/*.json agents/*/SOUL.md edict/frontend这一条线的特点是改动直观、验证成本低、和现有看板功能结合紧适合先做面板扩展、状态扩展、任务流改造和轻量级能力增强。edict/全栈主线更偏向完整后端架构常见入口集中在下面这些位置edict/backend edict/frontend Redis Streams Postgres workers websocket这一条线更适合做长期演进例如完整服务层抽象、数据库约束、事件发布、状态持久化和接口治理。开发环境可以直接使用下面这一组命令01_setup_prod_env.bat --sync docker compose -f edict/docker-compose.yml up -d postgres redis 03_start_prod_stack.bat --open-browser --migrate启动后核心地址如下看板http://127.0.0.1:7891 后端文档http://127.0.0.1:8000/docs 网关http://127.0.0.1:18789 日志目录.runtime\prod\logs如果本次目标只是新增一个业务面板并把一份新的 JSON 数据接进看板那么根目录看板主线就足够了。如果本次目标是新增正式状态枚举、补数据库约束并让 Worker 参与状态机流转那么更适合进入edict/backend这条全栈线。代码结构进入二开阶段后最先需要建立的是代码地图。知道文件在哪不等于知道职责在哪。真正高效的做法是先把不同目录的责任边界分清楚这样改动时才知道影响范围。常用目录可以整理为下面这张代码地图agents/ Agent 人格、职责边界、输入输出规范 scripts/ 数据同步、刷新循环、辅助脚本、运维脚本 dashboard/ 看板服务、接口聚合、页面静态资源入口 edict/backend/ FastAPI 后端、服务层、模型、状态机、数据库逻辑 edict/frontend/ 前端页面、面板组件、状态管理、接口调用 data/ 看板主线依赖的 JSON 数据源 tests/ 单元测试、端到端测试、回归验证如果要快速判断改动落点可以按下面这个思路来分改页面展示先看 edict/frontend 改接口返回先看 dashboard/server.py 或 edict/backend 改任务状态先看 dashboard/server.py、store.ts、后端状态模型 改角色行为先看 agents/*/SOUL.md 和权限矩阵 改刷新数据先看 scripts/run_loop.py 和相关同步脚本需要新增一个“质量雷达”面板时不应一开始就改前端组件。更合理的顺序是先确定数据从哪里来再确定接口怎么返回最后再决定前端怎么展示。这类改动通常会同时涉及scripts/、dashboard/server.py和edict/frontend/。后端扩展新增 API、状态流转、服务层改造后端扩展最常见的三类工作是新增接口、改状态流转和调整服务层逻辑。二开时最容易出问题的地方不是代码写不出来而是前后端契约没有先固定导致后端能返回、前端却无法消费或者状态能流转、页面却不显示。正式改后端之前建议先固定三份契约任务状态契约状态枚举、合法流转、终态定义 任务数据契约任务 JSON 或数据库字段的结构语义 接口契约请求参数、响应字段、错误格式如果要在根目录看板主线中新增一个简单接口可以直接在dashboard/server.py中增加路由例如新增一个质量指标接口fromfastapiimportFastAPIfromfastapi.responsesimportJSONResponsefrompathlibimportPathimportjson appFastAPI()app.get(/api/quality-metrics)defquality_metrics():data_filePath(data/quality_metrics.json)ifnotdata_file.exists():returnJSONResponse({items:[],message:no data})withdata_file.open(r,encodingutf-8)asf:returnJSONResponse(json.load(f))如果要新增一个状态例如在Doing和Review之间插入QA看板主线通常至少要改下面这些位置scripts/kanban_update.py dashboard/server.py edict/frontend/src/store.ts edict/frontend/src/components/TaskModal.tsx如果走全栈主线则还需要改后端状态模型和服务层例如fromenumimportEnumclassTaskState(str,Enum):TAIZITaiziZHONGSHUZhongshuMENXIAMenxiaASSIGNEDAssignedDOINGDoingQAQAREVIEWReviewDONEDoneSTATE_TRANSITIONS{Doing:[QA],QA:[Review],Review:[Done],}状态流转逻辑通常还要同步进入服务层校验否则前端即使显示了新状态后端也可能拒绝推进。defcan_transition(current_state:str,next_state:str)-bool:returnnext_stateinSTATE_TRANSITIONS.get(current_state,[])新增 QA 状态示例把QA插入到执行链后不只是多一个标签。真正完整的改动应该让任务能够从Doing推进到QA前端能显示新节点详情弹窗能识别下一步动作接口和活动流也都能接受这个状态。只改某一个地方最终都会出现前后不一致的问题。前端扩展新增面板、组件通信、状态管理前端扩展最常见的是新增业务面板其次是改任务详情弹窗、扩展组件通信和调整状态管理。看板类系统的前端改造有一个明显特点就是展示逻辑和接口契约绑得很紧所以前端扩展不能脱离接口数据单独进行。新增一个业务面板一般可以按下面这条路径完成新增数据脚本 - 新增后端接口 - 新增前端 API - 新建组件 - 注册 tab - 挂载页面例如新增一个“质量雷达”面板先在scripts/下写一个数据生成脚本importjsonfrompathlibimportPath data{items:[{name:任务完成率,value:92},{name:封驳率,value:8},{name:平均处理时长,value:37}]}Path(data).mkdir(exist_okTrue)withopen(data/quality_metrics.json,w,encodingutf-8)asf:json.dump(data,f,ensure_asciiFalse,indent2)再把脚本纳入刷新循环REFRESH_SCRIPTS[sync_tasks.py,sync_agents.py,sync_quality_metrics.py,]前端接口层可以新增一个方法exportasyncfunctionqualityMetrics(){constresawaitfetch(/api/quality-metrics);if(!res.ok)thrownewError(failed to fetch quality metrics);returnres.json();}新增面板组件import { useEffect, useState } from react; import { qualityMetrics } from ../api; export default function QualityPanel() { const [data, setData] useStateany({ items: [] }); useEffect(() { qualityMetrics().then(setData).catch(() setData({ items: [] })); }, []); return ( div h3质量雷达/h3 {data.items.map((item: any) ( div key{item.name} {item.name}{item.value} /div ))} /div ); }在状态管理中注册新面板再在App.tsx挂载它。构建命令如下cdedict/frontendnpmrun build如果构建产物输出到dashboard/dist看板服务就能直接使用新的前端内容。这一类改动的关键不在于组件写得多复杂而在于链路完整。只要数据脚本能产出 JSON接口能稳定读取前端能显示结果这个面板就已经具备后续迭代空间。反过来如果一开始就堆大量图表却没有稳定数据源后续维护成本会很高。Agent 扩展SOUL.md 定制、新角色与权限矩阵Agent 扩展是 Edict 区别于普通看板系统的重要部分。新增角色并不是简单复制一个目录而是要同时定义职责边界、输入输出格式、权限范围和调度映射。只有这些部分都补齐新角色才能真正进入任务体系。新增一个 Agent通常先从SOUL.md开始。最小结构示例如下agents/audit_office/SOUL.mdSOUL.md中至少要包含职责、输入、输出和限制条件例如# 审计司 职责 负责对任务结果进行合规检查、风险审计和输出复核。 输入 接收待审计的任务说明、执行结果和相关附件。 输出 输出审计结论、问题列表和整改建议。 限制 不得直接改写业务任务结果不得越权推进终态。除了新增人格文件还要同步注册权限矩阵。常见位置包括安装脚本中的 Agent 列表、生产引导脚本中的权限配置以及看板服务中的状态与组织映射。例如AGENT_PERMS{audit_office:{allowAgents:[zhongshu,menxia,shangshu],allowTools:[review,audit]}}如果前端要显示新角色还需要在状态管理中补充部门信息exportconstDEPTS[{id:zhongshu,name:中书省},{id:menxia,name:门下省},{id:audit_office,name:审计司},];完成配置后通常需要重新 bootstrap再检查工作区是否生成、看板是否出现新部门、任务是否能派发到新角色。当系统已经具备拆解、审议和执行能力但缺少结果复核环节时就可以新增“审计司”。这个角色不直接生成任务成果而是专门负责检查结果质量和合规性。这样做的价值不是多一个名字而是把“验收”从模糊动作变成明确职责。Worker/调度扩展重试、升级、回滚策略调度扩展决定了系统在异常情况下能否稳定恢复。真正的工程化系统不能只在一切正常时运转还要考虑超时、失败、阻塞和重复执行等情况。Worker 和调度策略的价值就体现在这些边界场景中。调度扩展常见的三个方向是重试、升级和回滚。重试任务暂时失败后再次执行 升级任务长时间卡住后交给更高层级处理 回滚错误推进或错误配置后恢复到稳定状态一个简单的重试策略可以写成下面这样defshould_retry(retry_count:int,max_retry:int3)-bool:returnretry_countmax_retry升级策略通常会结合超时阈值使用defshould_escalate(wait_seconds:int,threshold:int180)-bool:returnwait_secondsthreshold如果系统已经暴露了巡检接口可以直接触发调度扫描curl-XPOST http://127.0.0.1:7891/api/scheduler-scan\-HContent-Type: application/json\-d{thresholdSec:180}回滚策略更强调可恢复性。例如模型切换后结果明显异常或者某个新状态上线后流转紊乱都应该允许快速恢复到前一个稳定配置而不是继续在错误状态上叠加修补。一条任务长时间停留在Assigned活动流没有继续推进。这时最合理的处理不是立刻手动改状态而是先由调度扫描判断是否需要重试派发或升级到协调层处理。只有在自动修复失败时才进入人工干预。PyQt 启动器扩展在二次开发阶段除了 Web 看板与后端服务扩展还可以增加一个本地桌面启动器作为项目的图形化入口。这个启动器适合本地部署、教学演示、内部交付和运维辅助场景能够把原本依赖命令行完成的启动、停止、日志查看、模型切换和数据清理操作集中到一个窗口中。当前这一部分代码由入口文件edict_launcher.py和界面主文件edict_pyqt_ui.py组成其中入口文件只负责调用主程序真正的界面、服务控制和日志逻辑都集中在edict_pyqt_ui.py中。入口文件非常轻只做一件事就是调用主界面模块中的main()。这种写法的好处是把启动入口和具体界面逻辑分开后续无论是直接运行 Python 文件还是继续打包成可执行程序都比较方便。fromedict_pyqt_uiimportmainif__name____main__:raiseSystemExit(main())桌面启动器的主窗口通常继承自QMainWindow界面层负责渲染按钮、输入框、状态标签和日志窗口同时通过信号机制把后台线程中的执行结果同步回界面。核心能力通常包括服务启停、模型切换、历史数据清理、二维码弹窗、浏览器跳转和日志滚动显示。服务启动逻辑的核心是把原来的多条命令行启动流程封装成一个图形化入口。按钮点击后先检查端口是否已被占用再进入后台线程执行完整启动流程。这样做可以避免窗口卡死也能持续看到日志输出。defstart_service(self)-None:ifself._is_port_open(self._host(),self._port()):self.log([INFO] 服务已运行无需重复启动)self._update_status()returnfrontend_portself._port()self._run_async(启动服务,lambda:self._start_stack(frontend_port))日志回显一般通过信号槽机制完成。后台线程只负责发出日志信号界面线程负责把文本写入日志框。这样一来启动服务、停止服务、切换模型和清理历史数据这些耗时操作都不会阻塞界面。def_post_log(self,text:str)-None:self.sig_log.emit(text)deflog(self,text:str)-None:ifself.txt_logsisNone:returnself.txt_logs.insertPlainText(text.rstrip()\n)self.txt_logs.moveCursor(QTextCursor.End)这类启动器还可以把模型治理能力图形化例如提供“深度求索全员切换”和“克劳德全员切换”按钮。点击后先弹出 API Key 输入框再执行模型变更脚本和配置同步脚本。defset_deepseek_for_all_agents(self)-None:keyself._prompt_api_key(深度求索密钥)ifnotkey:returnself._run_async(深度求索全员切换,lambda:self._apply_deepseek_key_and_switch(key))如果要直接运行启动器可以使用下面的命令python edict_launcher.py如果要在开发阶段直接运行界面文件也可以执行python edict_pyqt_ui.py需要把 Edict 交给不熟悉命令行的同事使用时直接分发批处理脚本并不直观。改用 PyQt 启动器后启动、停止、切换模型、清理数据和查看日志都可以在一个窗口中完成。对于教学演示、本地运维和内部交付场景这种入口形式会比纯命令行更易用。数据与兼容legacy 接口与迁移注意事项二开中最容易被忽略的一部分就是兼容治理。功能能跑并不代表可以上线尤其是旧字段、旧状态、旧任务编号风格仍然存在时。兼容问题如果前期不处理后面基本都会返工。当前兼容风险通常集中在三类。第一类是任务主键风格差异例如task_id、trace_id与JJC-*风格并存。第二类是状态命名差异例如Taizi与taizi这种大小写不一致。第三类是配置项命名差异例如数据库覆盖配置字段可能并不是直觉中的名字。针对状态命名差异最稳妥的做法是加一层标准化defnormalize_state(state:str)-str:mapping{taizi:Taizi,zhongshu:Zhongshu,menxia:Menxia,}returnmapping.get(state,state)针对任务编号差异可以统一输出层格式而不是让前端直接感知多种风格defnormalize_task_id(raw:dict)-str:returnraw.get(id)orraw.get(task_id)orraw.get(trace_id)orUNKNOWN如果全栈线涉及数据库状态枚举变更还需要配合迁移脚本而不是只改 Python 枚举否则历史数据很可能无法兼容新代码。新增QA状态后如果旧任务数据仍然只认识Doing - Review - Done那么新旧任务会同时存在不同状态路径。这个时候不做兼容层前端展示和历史查询就会出现混乱。更稳妥的做法是让旧数据仍能被识别并明确哪些任务走新路径哪些任务走旧路径。测试与发布pytest/E2E/构建/回归二开阶段如果没有测试基线改动越多系统越不稳。测试和发布不是收尾动作而是每次改造前就该纳入设计的部分。尤其是状态机、面板、调度逻辑和桌面启动器改动最需要回归验证。最小可行测试基线可以直接使用下面这一组命令python-mpytest tests python tests/test_e2e_kanban.py python-mpy_compile dashboard/server.py python-mpy_compile scripts/kanban_update.py python-mpy_compile edict_pyqt_ui.py python-mpy_compile edict_launcher.pycdedict/frontendnpmrun build每次发版前建议至少回归下面这些动作新建任务 状态流转 叫停、恢复、取消 审议准奏、封驳 模型切换 技能导入与更新 奏折归档与活动流追溯 PyQt 启动器启动、停止、日志显示、模型切换发布策略上更适合小步合并而不是一次堆很多变化。每次合并前跑自动化测试每次发布前保留前一版本数据快照和回滚方案会比“有问题再修”更稳。新增质量雷达面板后不应该只看这个面板本身是否能打开还要确认新增接口没有影响原有任务列表接口前端构建没有破坏主看板任务流转和归档仍然正常。如果还引入了 PyQt 启动器就需要补充桌面入口的启动、停止和日志展示测试避免出现 Web 正常而桌面入口失效的情况。运维与安全配置、日志、密钥、监控运维与安全决定了系统能否长期稳定运行。二开完成后如果配置混乱、日志不可读、密钥散落、监控缺失那么系统即使功能再全也很难支撑持续迭代。日常运维中至少要把下面几件事固定下来统一日志目录按服务拆分日志文件 配置项集中管理避免多处覆盖同名变量 密钥不写入仓库改用环境变量或安全配置文件 关键数据写入使用原子操作和文件锁 监控中至少覆盖服务存活、任务积压、异常重试次数 桌面启动器中的密钥输入不落盘或做脱敏处理如果要做最基础的接口监控可以直接定时检查健康接口curl-shttp://127.0.0.1:7891/healthzcurl-shttp://127.0.0.1:7891/api/live-status日志目录建议统一在运行时目录中管理.runtime\prod\logs如果改动涉及模型配置、远程 Skill 或数据库连接密钥和关键配置都不应直接硬编码在脚本中。对桌面启动器来说密钥输入框虽然方便但更适合只做临时输入不直接持久化到源码目录或明文文件中。把远程 Skill 源地址、模型访问密钥和数据库覆盖配置直接写死在脚本里短期看起来省事长期一定会成为风险点。更稳妥的做法是把这些配置抽到环境变量或专门配置文件中并在启动阶段做有效性检查。桌面启动器如果增加配置持久化能力也应先处理敏感字段脱敏和安全存储问题。总结这一篇的重点不是单独学会某个接口怎么加、某个面板怎么写也不是单纯把命令行包装成桌面窗口而是建立一套可持续迭代的方法。真正成熟的二开方式应该先定契约再选主线再做小步扩展最后用测试、发布、回滚和监控把改动闭合起来。这样做系统能力会越来越稳而不是功能越来越多、维护越来越难。如果需要把二开工作拆成一个更实际的推进顺序可以直接按下面这条路线来落地第一阶段统一状态、任务和接口契约补齐配置自检 第二阶段新增一个业务面板补一条完整数据链路 第三阶段引入或完善 PyQt 启动器形成本地图形化入口 第四阶段新增一个 Agent完成权限和派发验证 第五阶段补调度恢复、回滚方案和端到端回归 第六阶段整理发布规范、监控指标和团队开发约定到这一阶段Edict 就不再只是一个可以体验和使用的项目而是一套能够持续演进、逐步沉淀团队能力的多 Agent 平台。