æ¬ææ¡£è¯Šç»éè¿°ãäžçå éšã项ç®åŠäœä»äžå¡å¶åºŠè®Ÿè®¡å°ä»£ç å®ç°ç»èïŒå®æŽå€ç倿å€Agentåäœçä»»å¡ååäžæµèœ¬ãè¿æ¯äžäžªå¶åºŠåçAIå€Agentæ¡æ¶ïŒèéäŒ ç»çèªç±è®šè®ºåŒåäœç³»ç»ã
ææ¡£æŠè§åŸ
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
äžå¡å±ïŒåžåœå¶åºŠ (Imperial Governance Model)
ââ åæå¶è¡¡ïŒçäž â 倪å â äžä¹Š â éšäž â å°ä¹Š â å
éš
ââ å¶åºŠçºŠæïŒäžå¯è¶çº§ãç¶æäž¥æ Œéè¿ãéšäžå¿
审议
ââ 莚éä¿éïŒå¯å°é©³åå·¥ã宿¶å¯è§æµã玧æ¥å¯å¹²é¢
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ææ¯å±ïŒOpenClawå€AgentçŒæ (Multi-Agent Orchestration)
ââ ç¶ææºïŒ9äžªç¶æïŒPending â Taizi â Zhongshu â Menxia â Assigned â Doing/Next â Review â Done/CancelledïŒ
ââ æ°æ®èåïŒflow_log + progress_log + session JSONL â unified activity stream
ââ æéç©éµïŒäž¥æ Œçsubagentè°çšæéæ§å¶
ââ è°åºŠå±ïŒèªåšæŽŸåãè¶
æ¶éè¯ãåæ»å级ãèªåšåæ»
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
è§æµå±ïŒReact çæ¿ + 宿¶API (Dashboard + Real-time Analytics)
ââ ä»»å¡çæ¿ïŒ10䞪è§åŸé¢æ¿ïŒå
šéš/æç¶æ/æéšéš/æäŒå
级çïŒ
ââ æŽ»åšæµïŒ59æ¡/ä»»å¡çæ··åæŽ»åšè®°åœïŒæèè¿çšãå·¥å
·è°çšãç¶æèœ¬ç§»ïŒ
ââ åšçº¿ç¶æïŒAgent 宿¶èç¹æ£æµ + å¿è·³åéæºå¶
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
äŒ ç»çå€Agentæ¡æ¶ïŒåŠCrewAIãAutoGenïŒéçš**"èªç±åäœ"æš¡åŒ**ïŒ
- Agentèªäž»éæ©åäœå¯¹è±¡
- æ¡æ¶ä» æäŸéä¿¡éé
- èŽšéæ§å¶å®å šäŸèµAgentæºèœ
- é®é¢ïŒå®¹æåºç°Agentçžäºå¶é åæ°æ®ãéå€å·¥äœãæ¹æ¡èŽšéæ ä¿é
äžçå éšéçš**"å¶åºŠååäœ"æš¡åŒ**ïŒæš¡ä»¿å€ä»£åžåœå®åäœç³»ïŒ
çäž
(User)
â
â
倪å (Taizi)
[忣å®ãæ¶æ¯æ¥å
¥æ»èŽèŽ£]
ââ è¯å«ïŒè¿æ¯æšæè¿æ¯é²èïŒ
ââ æ§è¡ïŒçŽæ¥åå€é²è || 建ç«ä»»å¡â蜬äžä¹Š
ââ æéïŒåªèœè°çš äžä¹Šç
â
â
äžä¹Šç (Zhongshu)
[è§åå®ãæ¹æ¡èµ·èæ»èŽèŽ£]
ââ æ¥æšååæéæ±
ââ æè§£äžºåä»»å¡ïŒtodosïŒ
ââ è°çšéšäžç审议 OR å°ä¹Šçåšè¯¢
ââ æéïŒåªèœè°çš éšäž + å°ä¹Š
â
â
éšäžç (Menxia)
[审议å®ãèŽšéææ¡äºº]
ââ 审æ¥äžä¹Šæ¹æ¡ïŒå¯è¡æ§ã宿޿§ãé£é©ïŒ
ââ åå¥ OR å°é©³ïŒå«ä¿®æ¹å»ºè®®ïŒ
ââ è¥å°é©³ â è¿åäžä¹Šä¿®æ¹ â éæ°å®¡è®®ïŒæå€3蜮ïŒ
ââ æéïŒåªèœè°çš å°ä¹Š + åè°äžä¹Š
â
(â
åå¥)
â
â
å°ä¹Šç (Shangshu)
[掟åå®ãæ§è¡æ»ææ¥]
ââ æ¥å°å奿¹æ¡
ââ åææŽŸåç»åªäžªéšéš
ââ è°çšå
éšïŒç€Œ/æ·/å
µ/å/å·¥/åïŒæ§è¡
ââ çæ§åéšè¿åºŠ â æ±æ»ç»æ
ââ æéïŒåªèœè°çš å
éšïŒäžèœè¶æè°äžä¹ŠïŒ
â
ââ ç€Œéš (Libu) - ææ¡£çŒå¶å®
ââ æ·éš (Hubu) - æ°æ®åæå®
ââ å
µéš (Bingbu) - 代ç å®ç°å®
ââ åéš (Xingbu) - æµè¯å®¡æ¥å®
ââ å·¥éš (Gongbu) - åºç¡è®Ÿæœå®
ââ åéš (Libu_hr) - 人åèµæºå®
â
(åéšå¹¶è¡æ§è¡)
â
å°ä¹ŠçÂ·æ±æ»
ââ æ¶éå
éšç»æ
ââ ç¶æèœ¬äžº Review
ââ åè°äžä¹Šç蜬æ¥çäž
â
â
äžä¹Šç·åå¥
ââ æ±æ»ç°è±¡ãç»è®ºã建议
ââ ç¶æèœ¬äžº Done
ââ åå€é£ä¹Šæ¶æ¯ç»çäž
| ä¿éæºå¶ | å®ç°ç»è | 鲿€ææ |
|---|---|---|
| å¶åºŠæ§å®¡æ ž | éšäžçå¿ å®¡è®®ææäžä¹Šæ¹æ¡ïŒäžå¯è·³è¿ | 鲿¢Agentè¡ä¹±æ§è¡ïŒç¡®ä¿æ¹æ¡å ·æå¯è¡æ§ |
| åæå¶è¡¡ | æéç©éµïŒè°èœè°è°äž¥æ Œå®ä¹ | 鲿¢æå滥çšïŒåŠå°ä¹Šè¶æè°äžä¹Šæ¹æ¹æ¡ïŒ |
| å®å šå¯è§æµ | ä»»å¡çæ¿10äžªé¢æ¿ + 59æ¡æŽ»åš/ä»»å¡ | 宿¶çå°ä»»å¡å¡åšåªãè°åšå·¥äœãå·¥äœç¶æåŠäœ |
| 宿¶å¯å¹²é¢ | çæ¿å äžé® stop/cancel/resume/advance | çŽ§æ¥æ åµïŒåŠåç°Agentèµ°éæ¹åïŒèœç«å³çº æ£ |
stateDiagram-v2
[*] --> Pending: çäžäžæš
Pending --> Taizi: å€ªåæ¥æš
Taizi --> Zhongshu: 倪å蜬亀äžä¹Š
Zhongshu --> Menxia: äžä¹Šæäº€å®¡è®®
Menxia --> Zhongshu: éšäžå°é©³(å¯å€æ¬¡)
Menxia --> Assigned: éšäžåå¥
Assigned --> Doing: å°ä¹ŠæŽŸåæ§è¡
Doing --> Review: åéšå®æ
Review --> Done: çäžåŸ¡æ¹éè¿
Review --> Menxia: çäžèŠæ±ä¿®æ¹
Done --> [*]
Doing --> [*]: æåšåæ¶
Review --> [*]: äžå¡ç»æ¢
â çæ³è·¯åŸïŒæ 黿»ïŒ4-5倩宿ïŒ
DAY 1:
10:00 - çäžé£ä¹ŠïŒ"䞺äžçå
éšçŒå宿Žèªåšåæµè¯æ¹æ¡"
å€ªåæ¥æšãstate = Taizi, org = 倪å
èªåšæŽŸå taizi agent â å€çæ€æšæ
10:30 - 倪å忣宿¯ãå€å®äžºãå·¥äœæšæãïŒéé²èïŒ
å»ºä»»å¡ JJC-20260228-E2E
flow_log è®°åœïŒ"çäž â 倪åïŒäžæš"
state: Taizi â Zhongshu, org: 倪å â äžä¹Šç
èªåšæŽŸå zhongshu agent
DAY 2:
09:00 - äžä¹Šçæ¥æšãåŒå§è§å
æ±æ¥è¿å±ïŒ"åææµè¯éæ±ïŒæè§£äžºåå
/éæ/E2Eäžå±"
progress_log è®°åœïŒ"äžä¹Šç åŒ äžïŒåéæ±"
15:00 - äžä¹Šçå®ææ¹æ¡
todos å¿«ç
§ïŒéæ±åæâ
ãæ¹æ¡è®Ÿè®¡â
ãåŸ
审议ð
flow_log è®°åœïŒ"äžä¹Šç â éšäžçïŒæ¹æ¡æäº€å®¡è®®"
state: Zhongshu â Menxia, org: äžä¹Šç â éšäžç
èªåšæŽŸå menxia agent
DAY 3:
09:00 - éšäžçåŒå§å®¡è®®
è¿åºŠæ±æ¥ïŒ"ç°åšå®¡æ¥æ¹æ¡ç宿޿§åé£é©"
14:00 - éšäžçå®¡è®®å®æ¯
å€å®ïŒ"æ¹æ¡å¯è¡ïŒäœçŒºå€± _infer_agent_id_from_runtime åœæ°çæµè¯"
è¡äžºïŒâ
åå¥ (垊修æ¹å»ºè®®)
flow_log è®°åœïŒ"éšäžç â å°ä¹ŠçïŒâ
åå¥éè¿ïŒ5æ¡å»ºè®®ïŒ"
state: Menxia â Assigned, org: éšäžç â å°ä¹Šç
OPTIONALïŒäžä¹Šçæ¶å°å»ºè®®ïŒäž»åšäŒåæ¹æ¡
èªåšæŽŸå shangshu agent
DAY 4:
10:00 - å°ä¹Šçæ¥å°åå¥
åæïŒ"该æµè¯æ¹æ¡åºæŽŸç»å·¥éš+åéš+瀌éšåå宿"
flow_log è®°åœïŒ"å°ä¹Šç â å
éšïŒæŽŸåæ§è¡ïŒå
µååäœïŒ"
state: Assigned â Doing, org: å°ä¹Šç â å
µéš+åéš+瀌éš
èªåšæŽŸå bingbu/xingbu/libu äžäžªagentïŒå¹¶è¡ïŒ
DAY 4-5:
(åéšå¹¶è¡æ§è¡)
- å
µéš(bingbu)ïŒå®ç° pytest + unittest æµè¯æ¡æ¶
- åéš(xingbu)ïŒçŒåæµè¯èŠçææå
³é®åœæ°
- 瀌éš(libu)ïŒæŽçæµè¯ææ¡£åçšäŸè¯Žæ
宿¶æ±æ¥ïŒhourly progressïŒïŒ
- å
µéšïŒ"â
å·²å®ç° 16 䞪åå
æµè¯"
- åéšïŒ"ð æ£åšçŒåéææµè¯ïŒ8/12 宿ïŒ"
- 瀌éšïŒ"çåŸ
å
µéšå®æå忥å"
DAY 5:
14:00 - åéšå®æ
state: Doing â Review, org: å
µéš â å°ä¹Šç
å°ä¹Šçæ±æ»ïŒ"æææµè¯å·²å®æïŒéè¿ç 98.5%"
蜬åäžä¹Šç
15:00 - äžä¹Šçåå¥çäž
state: Review â Done
æš¡æ¿åå€é£ä¹ŠïŒå«æç»ææéŸæ¥åæ»ç»
â æ«æè·¯åŸïŒå«å°é©³åéè¯ïŒ6-7倩ïŒ
DAY 2 åäž
DAY 3 [å°é©³åºæ¯]ïŒ
14:00 - éšäžçå®¡è®®å®æ¯
å€å®ïŒ"æ¹æ¡äžå®æŽïŒçŒºå°æ§èœæµè¯ + ååæµè¯"
è¡äžºïŒð« å°é©³
review_round += 1
flow_log è®°åœïŒ"éšäžç â äžä¹ŠçïŒð« å°é©³ïŒéè¡¥å
æ§èœæµè¯ïŒ"
state: Menxia â Zhongshu # è¿åäžä¹Šä¿®æ¹
èªåšæŽŸå zhongshu agentïŒéæ°è§åïŒ
DAY 3-4ïŒ
16:00 - äžä¹Šçæ¶å°å°é©³éç¥ïŒå€éagentïŒ
åææ¹è¿æè§ïŒè¡¥å
æ§èœæµè¯æ¹æ¡
progressïŒ"å·²æŽåæ§èœæµè¯éæ±ïŒä¿®æ£æ¹æ¡åŠäž..."
flow_log è®°åœïŒ"äžä¹Šç â éšäžçïŒä¿®è®¢æ¹æ¡ïŒç¬¬2蜮审议ïŒ"
state: Zhongshu â Menxia
èªåšæŽŸå menxia agent
18:00 - éšäžçéæ°å®¡è®®
å€å®ïŒ"â
æ¬æ¬¡éè¿"
flow_log è®°åœïŒ"éšäžç â å°ä¹ŠçïŒâ
åå¥éè¿ïŒç¬¬2蜮ïŒ"
state: Menxia â Assigned â Doing
åç»åçæ³è·¯åŸ...
DAY 7ïŒå
šéšå®æïŒæ¯çæ³è·¯åŸæ1-2倩ïŒ
{
"id": "JJC-20260228-E2E", // ä»»å¡å
šå±å¯äžID (JJC-æ¥æ-åºå·)
"title": "䞺äžçå
éšçŒå宿Žèªåšåæµè¯æ¹æ¡",
"official": "äžä¹Šä»€", // èŽèŽ£å®è
"org": "äžä¹Šç", // åœåèŽèŽ£éšéš
"state": "Assigned", // åœåç¶æïŒè§ _STATE_FLOWïŒ
// ââââ 莚éäžçºŠæ ââââ
"priority": "normal", // äŒå
级ïŒcritical/high/normal/low
"block": "æ ", // åœå黿»åå ïŒåŠ"çåŸ
å·¥éšåéŠ"ïŒ
"reviewRound": 2, // éšäžå®¡è®®ç¬¬å 蜮
"_prev_state": "Menxia", // è¥è¢« stopïŒè®°åœä¹åç¶æçšäº resume
// ââââ äžå¡äº§åº ââââ
"output": "", // æç»ä»»å¡ææïŒURL/æä»¶è·¯åŸ/æ»ç»ïŒ
"ac": "", // Acceptance CriteriaïŒéªæ¶æ åïŒ
"priority": "normal",
// ââââ æµèœ¬è®°åœ ââââ
"flow_log": [
{
"at": "2026-02-28T10:00:00Z",
"from": "çäž",
"to": "倪å",
"remark": "äžæšïŒäžºäžçå
éšçŒå宿Žèªåšåæµè¯æ¹æ¡"
},
{
"at": "2026-02-28T10:30:00Z",
"from": "倪å",
"to": "äžä¹Šç",
"remark": "忣âäŒ æš"
},
{
"at": "2026-02-28T15:00:00Z",
"from": "äžä¹Šç",
"to": "éšäžç",
"remark": "è§åæ¹æ¡æäº€å®¡è®®"
},
{
"at": "2026-03-01T09:00:00Z",
"from": "éšäžç",
"to": "äžä¹Šç",
"remark": "ð« å°é©³ïŒéè¡¥å
æ§èœæµè¯"
},
{
"at": "2026-03-01T15:00:00Z",
"from": "äžä¹Šç",
"to": "éšäžç",
"remark": "ä¿®è®¢æ¹æ¡ïŒç¬¬2蜮审议ïŒ"
},
{
"at": "2026-03-01T20:00:00Z",
"from": "éšäžç",
"to": "å°ä¹Šç",
"remark": "â
åå¥éè¿ïŒç¬¬2蜮ïŒ5æ¡å»ºè®®å·²é纳ïŒ"
}
],
// ââââ Agent 宿¶æ±æ¥ ââââ
"progress_log": [
{
"at": "2026-02-28T10:35:00Z",
"agent": "zhongshu", // æ±æ¥agent
"agentLabel": "äžä¹Šç",
"text": "å·²æ¥æšãåææµè¯éæ±ïŒæå®äžå±æµè¯æ¹æ¡...",
"state": "Zhongshu", // æ±æ¥æ¶çç¶æå¿«ç
§
"org": "äžä¹Šç",
"tokens": 4500, // èµæºæ¶è
"cost": 0.0045,
"elapsed": 120,
"todos": [ // åŸ
åä»»å¡å¿«ç
§
{"id": "1", "title": "éæ±åæ", "status": "completed"},
{"id": "2", "title": "æ¹æ¡è®Ÿè®¡", "status": "in-progress"},
{"id": "3", "title": "await审议", "status": "not-started"}
]
},
// ... æŽå€ progress_log æ¡ç® ...
],
// ââââ è°åºŠå
æ°æ® ââââ
"_scheduler": {
"enabled": true,
"stallThresholdSec": 180, // åæ»è¶
è¿180ç§èªåšå级
"maxRetry": 1, // èªåšéè¯æå€1次
"retryCount": 0,
"escalationLevel": 0, // 0=æ å级 1=éšäžåè° 2=å°ä¹Šåè°
"lastProgressAt": "2026-03-01T20:00:00Z",
"stallSince": null, // äœæ¶åŒå§åæ»
"lastDispatchStatus": "success", // queued|success|failed|timeout|error
"snapshot": {
"state": "Assigned",
"org": "å°ä¹Šç",
"note": "review-before-approve"
}
},
// ââââ çåœåšæ ââââ
"archived": false, // æ¯åŠåœæ¡£
"now": "éšäžçåå¥ïŒç§»äº€å°ä¹Šç掟å", // åœå宿¶ç¶ææè¿°
"updatedAt": "2026-03-01T20:00:00Z"
}| å¥çºŠ | å«ä¹ | è¿ååæ |
|---|---|---|
| äžå¯è¶çº§ | 倪ååªèœè°äžä¹ŠïŒäžä¹Šåªèœè°éšäž/å°ä¹ŠïŒå éšäžèœå¯¹å€è°çš | è¶ æè°çšè¢«æç»ïŒç³»ç»èªåšæŠæª |
| ç¶æååéè¿ | Pending â Taizi â Zhongshu â ... â DoneïŒäžèœè·³è¿æåé | åªèœéè¿ review_action(reject) è¿åäžäžæ¥ |
| éšäžå¿ 审 | ææäžä¹Šæåºçæ¹æ¡éœèŠéšäžçå®¡è®®ïŒæ æ³è·³è¿ | äžä¹ŠäžèœçŽæ¥èœ¬å°ä¹ŠïŒéšäžå¿ å ¥ |
| äžæŠDoneæ æ¹ | ä»»å¡è¿å ¥Done/Cancelledåäžèœåä¿®æ¹ç¶æ | è¥éä¿®æ¹éèŠå建æ°ä»»å¡æåæ¶åéæ°å»º |
| task_idå¯äžæ§ | JJC-æ¥æ-åºå· å šå±å¯äžïŒåäžå€©åäžä»»å¡äžéå€å»º | çæ¿é²éïŒèªåšå»é |
| èµæºæ¶èéæ | æ¯æ¬¡è¿å±æ±æ¥éœèŠäžæ¥ tokens/cost/elapsed | äŸ¿äºææ¬æ žç®åæ§èœäŒå |
_STATE_FLOW = {
'Pending': ('Taizi', 'çäž', '倪å', 'åŸ
å€çæšæèœ¬äº€å€ªå忣'),
'Taizi': ('Zhongshu','倪å', 'äžä¹Šç', '倪å忣宿¯ïŒèœ¬äžä¹Šçèµ·è'),
'Zhongshu': ('Menxia', 'äžä¹Šç', 'éšäžç', 'äžä¹Šçæ¹æ¡æäº€éšäžç审议'),
'Menxia': ('Assigned','éšäžç', 'å°ä¹Šç', 'éšäžçåå¥ïŒèœ¬å°ä¹Šç掟å'),
'Assigned': ('Doing', 'å°ä¹Šç', 'å
éš', 'å°ä¹ŠçåŒå§æŽŸåæ§è¡'),
'Next': ('Doing', 'å°ä¹Šç', 'å
éš', 'åŸ
æ§è¡ä»»å¡åŒå§æ§è¡'),
'Doing': ('Review', 'å
éš', 'å°ä¹Šç', 'åéšå®æïŒè¿å
¥æ±æ»'),
'Review': ('Done', 'å°ä¹Šç', '倪å', 'å
šæµçšå®æïŒåå¥å€ªå蜬æ¥çäž'),
}æ¯äžªç¶æèªåšå
³è Agent IDïŒè§ _STATE_AGENT_MAPïŒïŒ
_STATE_AGENT_MAP = {
'Taizi': 'taizi',
'Zhongshu': 'zhongshu',
'Menxia': 'menxia',
'Assigned': 'shangshu',
'Doing': None, # ä» org æšæïŒå
éšä¹äžïŒ
'Next': None, # ä» org æšæ
'Review': 'shangshu',
'Pending': 'zhongshu',
}åœä»»å¡ç¶æèœ¬ç§»æ¶ïŒéè¿ handle_advance_state() æå®¡æ¹ïŒïŒåå°èªåšæ§è¡æŽŸåïŒ
1. ç¶æèœ¬ç§»è§ŠåæŽŸå
ââ æ¥è¡š _STATE_AGENT_MAP åŸå°ç®æ agent_id
ââ è¥æ¯ Doing/NextïŒä» task.org æ¥è¡š _ORG_AGENT_MAP æšæå
·äœéšéšagent
ââ è¥æ æ³æšæåè·³è¿æŽŸåïŒåŠ Done/CancelledïŒ
2. æé æŽŸåæ¶æ¯ïŒé对æ§ä¿äœ¿Agentç«å³å·¥äœïŒ
ââ taizi: "ð çäžæšæéèŠäœ å€ç..."
ââ zhongshu: "ð æšæå·²å°äžä¹ŠçïŒè¯·èµ·èæ¹æ¡..."
ââ menxia: "ð äžä¹Šçæ¹æ¡æäº€å®¡è®®..."
ââ shangshu: "ð® éšäžçå·²åå¥ïŒè¯·æŽŸåæ§è¡..."
ââ å
éš: "ð 请å€çä»»å¡..."
3. åå°åŒæ¥æŽŸåïŒéé»å¡ïŒ
ââ spawn daemon thread
ââ æ è®° _scheduler.lastDispatchStatus = 'queued'
ââ æ£æ¥ Gateway è¿çšæ¯åŠåŒå¯
ââ è¿è¡ openclaw agent --agent {id} -m "{msg}" --deliver --timeout 300
ââ éè¯æå€2次ïŒå€±èŽ¥éŽé5ç§éé¿ïŒ
ââ æŽæ° _scheduler ç¶æåé误信æ¯
ââ flow_log è®°åœæŽŸåç»æ
4. 掟åç¶æèœ¬ç§»
ââ success: ç«å³æŽæ° _scheduler.lastDispatchStatus = 'success'
ââ failed: è®°åœå€±èŽ¥åå ïŒAgent è¶
æ¶äžäŒ block çæ¿
ââ timeout: æ è®° timeoutïŒå
è®žçšæ·æåšéè¯ / å级
ââ gateway-offline: Gateway æªå¯åšïŒè·³è¿æ€æ¬¡æŽŸåïŒåç»å¯éè¯ïŒ
ââ error: åŒåžžæ
åµïŒè®°åœå æ äŸè°è¯
5. å°èŸŸç®æ Agentçå€ç
ââ Agent ä»é£ä¹Šæ¶æ¯æ¶å°éç¥
ââ éè¿ kanban_update.py äžçæ¿äº€äºïŒæŽæ°ç¶æ/è®°åœè¿å±ïŒ
ââ 宿工äœå忬¡è§Šå掟åå°äžäžäžªAgent
{
"agents": [
{
"id": "taizi",
"label": "倪å",
"allowAgents": ["zhongshu"]
},
{
"id": "zhongshu",
"label": "äžä¹Šç",
"allowAgents": ["menxia", "shangshu"]
},
{
"id": "menxia",
"label": "éšäžç",
"allowAgents": ["shangshu", "zhongshu"]
},
{
"id": "shangshu",
"label": "å°ä¹Šç",
"allowAgents": ["libu", "hubu", "bingbu", "xingbu", "gongbu", "libu_hr"]
},
{
"id": "libu",
"label": "瀌éš",
"allowAgents": []
},
// ... å
¶ä»å
éšåæ · allowAgents = [] ...
]
}åš dispatch_for_state() ä¹å€ïŒè¿æäžå¥é²åŸ¡æ§çæéæ£æ¥ïŒ
def can_dispatch_to(from_agent, to_agent):
"""æ£æ¥ from_agent æ¯åŠææè°çš to_agentã"""
cfg = read_json(DATA / 'agent_config.json', {})
agents = cfg.get('agents', [])
from_record = next((a for a in agents if a.get('id') == from_agent), None)
if not from_record:
return False, f'{from_agent} äžååš'
allowed = from_record.get('allowAgents', [])
if to_agent not in allowed:
return False, f'{from_agent} æ æè°çš {to_agent}ïŒå
讞å衚ïŒ{allowed}ïŒ'
return True, 'OK'| åºæ¯ | è¯·æ± | ç»æ | çç± |
|---|---|---|---|
| æ£åžž | äžä¹Šç â éšäžç审议 | â å 讞 | éšäžåšäžä¹Šç allowAgents äž |
| è¿å | äžä¹Šç â å°ä¹Šçæ¹æ¹æ¡ | â æç» | äžä¹Šåªèœè°éšäž/å°ä¹ŠïŒäžèœæå·¥æ¹å°ä¹Šå·¥äœ |
| è¿å | å·¥éš â å°ä¹Šç "æå®æäº" | â æ¹ç¶æ | éè¿ flow_log å progress_logïŒäžæ¯è·šAgentè°çšïŒ |
| è¿å | å°ä¹Šç â äžä¹Šç "éæ°æ¹æ¹æ¡" | â æç» | å°ä¹Šäžåšéšäž/äžä¹Šç allowAgents äž |
| 鲿§ | Agent 䌪é å ¶ä»agent掟å | â æŠæª | API å±éªè¯ HTTP è¯·æ±æ¥æº/çŸå |
åœä»»å¡æ§è¡æ¶ïŒæäžå±æ°æ®æºïŒ
1ïžâ£ flow_log
ââ 纯粹记åœç¶æèœ¬ç§»ïŒZhongshu â MenxiaïŒ
ââ æ°æ®æºïŒä»»å¡ JSON ç flow_log åæ®µ
ââ æ¥èªïŒAgent éè¿ kanban_update.py flow åœä»€äžæ¥
2ïžâ£ progress_log
ââ Agent ç宿¶å·¥äœæ±æ¥ïŒææ¬è¿å±ãtodoså¿«ç
§ãèµæºæ¶èïŒ
ââ æ°æ®æºïŒä»»å¡ JSON ç progress_log åæ®µ
ââ æ¥èªïŒAgent éè¿ kanban_update.py progress åœä»€äžæ¥
ââ åšæïŒéåžžæ¯30åéæå
³é®èç¹äžæ¥1次
3ïžâ£ session JSONLïŒæ°å¢ïŒïŒ
ââ Agent çå
éšæèè¿çšïŒthinkingïŒãå·¥å
·è°çšïŒtool_resultïŒã对è¯åå²ïŒuserïŒ
ââ æ°æ®æºïŒ~/.openclaw/agents/{agent_id}/sessions/*.jsonl
ââ æ¥èªïŒOpenClawæ¡æ¶èªåšè®°åœïŒAgentæ éäž»åšæäœ
ââ åšæïŒæ¶æ¯çº§å«ïŒç²åºŠæç»
è¿å»ïŒåªé flow_log + progress_log å±ç°è¿å±ïŒ
- â çäžå°Agentçå ·äœæèè¿çš
- â çäžå°æ¯æ¬¡å·¥å ·è°çšçç»æ
- â çäžå°AgentäžéŽç对è¯åå²
- â Agent 衚ç°åº"é»çç¶æ"
äŸåŠïŒprogress_log è®°åœ"æ£åšåæéæ±"ïŒäœçšæ·çäžå°å°åºåæäºä»ä¹ã
åš get_task_activity() äžæ°å¢èåé»èŸïŒ40è¡ïŒïŒ
def get_task_activity(task_id):
# ... åé¢ä»£ç åäž ...
# ââ èå Agent Session 掻åšïŒthinking / tool_result / userïŒââ
session_entries = []
# 掻è·ä»»å¡ïŒå°è¯æ task_id 粟确å¹é
if state not in ('Done', 'Cancelled'):
if agent_id:
entries = get_agent_activity(
agent_id, limit=30, task_id=task_id
)
session_entries.extend(entries)
# ä¹ä»çžå
³Agentè·å
for ra in related_agents:
if ra != agent_id:
entries = get_agent_activity(
ra, limit=20, task_id=task_id
)
session_entries.extend(entries)
else:
# 已宿任å¡ïŒåºäºå
³é®è¯å¹é
title = task.get('title', '')
keywords = _extract_keywords(title)
if keywords:
for ra in related_agents[:5]:
entries = get_agent_activity_by_keywords(
ra, keywords, limit=15
)
session_entries.extend(entries)
# å»éïŒéè¿ at+kind å»éé¿å
éå€ïŒ
existing_keys = {(a.get('at', ''), a.get('kind', '')) for a in activity}
for se in session_entries:
key = (se.get('at', ''), se.get('kind', ''))
if key not in existing_keys:
activity.append(se)
existing_keys.add(key)
# éæ°æåº
activity.sort(key=lambda x: x.get('at', ''))
# è¿åæ¶æ è®°æ°æ®æ¥æº
return {
'activity': activity,
'activitySource': 'progress+session', # æ°æ è®°
# ... å
¶ä»å段 ...
}ä» JSONL äžæåçæ¡ç®ïŒç»äžèœ¬æ¢äžºçæ¿æŽ»åšæ¡ç®ïŒ
def _parse_activity_entry(item):
"""å° session jsonl ç message ç»äžè§£ææçæ¿æŽ»åšæ¡ç®ã"""
msg = item.get('message', {})
role = str(msg.get('role', '')).strip().lower()
ts = item.get('timestamp', '')
# ð§ Assistant è§è² - Agentæèè¿çš
if role == 'assistant':
entry = {
'at': ts,
'kind': 'assistant',
'text': '...äž»åå€...',
'thinking': 'ð Agentèèå°...', # å
éšæç»ŽéŸ
'tools': [
{'name': 'bash', 'input_preview': 'cd /src && npm test'},
{'name': 'file_read', 'input_preview': 'dashboard/server.py'},
]
}
return entry
# ð§ Tool Result - å·¥å
·è°çšç»æ
if role in ('toolresult', 'tool_result'):
entry = {
'at': ts,
'kind': 'tool_result',
'tool': 'bash',
'exitCode': 0,
'output': 'â All tests passed (123 tests)',
'durationMs': 4500 # æ§è¡æ¶é¿
}
return entry
# ð€ User - 人工åéŠæå¯¹è¯
if role == 'user':
entry = {
'at': ts,
'kind': 'user',
'text': '请å®ç°æµè¯çšäŸçåŒåžžå€ç'
}
return entryå䞪任å¡ç59æ¡æŽ»åšæµïŒJJC-20260228-E2E 瀺äŸïŒïŒ
kind count 代衚äºä»¶
ââââââââââââââââââââââââââââââââââââââââââââââââ
flow 10 ç¶æèœ¬ç§»éŸïŒPendingâTaiziâZhongshuâ...ïŒ
progress 11 Agentå·¥äœæ±æ¥ïŒ"æ£åšåæ"ã"已宿"ïŒ
todos 11 åŸ
åä»»å¡å¿«ç
§ïŒè¿åºŠæŽæ°æ¶æ¯æ¡ïŒ
user 1 çšæ·åéŠïŒåŠ"éèŠè¡¥å
æ§èœæµè¯"ïŒ
assistant 10 Agentæèè¿çšïŒð reasoning chainïŒ
tool_result 16 å·¥å
·è°çšè®°åœïŒbashè¿è¡ç»æãAPIè°çšç»æïŒ
ââââââââââââââââââââââââââââââââââââââââââââââââ
æ»è®¡ 59 宿Žå·¥äœèœšè¿¹
çæ¿å±ç€ºæ¶ïŒçšæ·å¯ä»¥ïŒ
- ð çæµèœ¬éŸäºè§£ä»»å¡åšåªäžªé¶æ®µ
- ð ç progress äºè§£Agent宿¶è¯Žäºä»ä¹
- â ç todos äºè§£ä»»å¡æè§£å宿è¿åºŠ
- ð ç assistant/thinking äºè§£Agentçæèè¿çš
- ð§ ç tool_result äºè§£æ¯æ¬¡å·¥å ·è°çšçç»æ
- ð€ ç user äºè§£æ¯åŠæäººå·¥å¹²é¢
_scheduler = {
# é
çœ®åæ°
'enabled': True,
'stallThresholdSec': 180, # åæ»å€ä¹
åèªåšå级ïŒé»è®€180ç§ïŒ
'maxRetry': 1, # èªåšéè¯æ¬¡æ°ïŒ0=äžéè¯ïŒ1=éè¯1次ïŒ
'autoRollback': True, # æ¯åŠèªåšåæ»å°å¿«ç
§
# è¿è¡æ¶ç¶æ
'retryCount': 0, # åœåå·²éè¯å 次
'escalationLevel': 0, # 0=æ å级 1=éšäžåè° 2=å°ä¹Šåè°
'stallSince': None, # äœæ¶åŒå§åæ»çæ¶éŽæ³
'lastProgressAt': '2026-03-01T...', # æåäžæ¬¡è·åŸè¿å±çæ¶éŽ
'lastEscalatedAt': '2026-03-01T...',
'lastRetryAt': '2026-03-01T...',
# 掟å远螪
'lastDispatchStatus': 'success', # queued|success|failed|timeout|gateway-offline|error
'lastDispatchAgent': 'zhongshu',
'lastDispatchTrigger': 'state-transition',
'lastDispatchError': '', # éè¯¯å æ ïŒåŠæïŒ
# å¿«ç
§ïŒçšäºèªåšåæ»ïŒ
'snapshot': {
'state': 'Assigned',
'org': 'å°ä¹Šç',
'now': 'çåŸ
掟å...',
'savedAt': '2026-03-01T...',
'note': 'scheduled-check'
}
}æ¯ 60 ç§è¿è¡äžæ¬¡ handle_scheduler_scan(threshold_sec=180)ïŒ
FOR EACH ä»»å¡:
IF state in (Done, Cancelled, Blocked):
SKIP # ç»æäžå€ç
elapsed_since_progress = NOW - lastProgressAt
IF elapsed_since_progress < stallThreshold:
SKIP # æè¿æè¿å±ïŒæ éå€ç
# ââ åæ»å€çé»èŸ ââ
IF retryCount < maxRetry:
â
æ§è¡ãéè¯ã
- increment retryCount
- dispatch_for_state(task, new_state, trigger='taizi-scan-retry')
- flow_log: "åæ»180ç§ïŒè§Šåèªåšéè¯ç¬¬N次"
- NEXT task
IF escalationLevel < 2:
â
æ§è¡ãå级ã
- nextLevel = escalationLevel + 1
- target_agent = menxia (if L=1) else shangshu (if L=2)
- wake_agent(target_agent, "ð¬ ä»»å¡åæ»ïŒè¯·ä»å
¥åè°æšè¿")
- flow_log: "å级è³{target_agent}åè°"
- NEXT task
IF escalationLevel >= 2 AND autoRollback:
â
æ§è¡ãèªåšåæ»ã
- restore task to snapshot.state
- retryCount = 0
- escalationLevel = 0
- dispatch_for_state(task, snapshot.state, trigger='taiji-auto-rollback')
- flow_log: "è¿ç»åæ»ïŒèªåšåæ»å°{snapshot.state}"
åºæ¯ïŒäžä¹ŠçAgentè¿çšåŽ©æºïŒä»»å¡å¡åš Zhongshu
T+0:
äžä¹Šçæ£åšè§åæ¹æ¡
lastProgressAt = T
dispatch status = success
T+30:
Agent è¿çšæå€åŽ©æºïŒæè¶
蜜æ ååºïŒ
lastProgressAt ä»ç¶ = TïŒæ²¡ææ°ç progressïŒ
T+60:
scheduler_scan æ«äžéïŒåç°ïŒ
elapsed = 60 < 180ïŒè·³è¿
T+180:
scheduler_scan æ«äžéïŒåç°ïŒ
elapsed = 180 >= 180ïŒè§Šåå€ç
â
é¶æ®µ1ïŒéè¯
- retryCount: 0 â 1
- dispatch_for_state('JJC-20260228-E2E', 'Zhongshu', trigger='taizi-scan-retry')
- æŽŸåæ¶æ¯åéå°äžä¹ŠçïŒå€éagentæéå¯ïŒ
- flow_log: "åæ»180ç§ïŒèªåšéè¯ç¬¬1次"
T+ 240:
äžä¹Šç Agent æ¢å€ïŒææå·¥éå¯ïŒïŒæ¶å°éè¯æŽŸå
æ±æ¥è¿å±ïŒ"å·²æ¢å€ïŒç»§ç»è§å..."
lastProgressAt æŽæ°äžº T+240
retryCount é眮䞺 0
â é®é¢è§£å³
T+360 (è¥ä»æªæ¢å€):
scheduler_scan 忬¡æ«ïŒåç°ïŒ
elapsed = 360 >= 180, retryCount å·²ç» = 1
â
é¶æ®µ2ïŒå级
- escalationLevel: 0 â 1
- wake_agent('menxia', "ð¬ ä»»å¡JJC-20260228-E2Eåæ»ïŒäžä¹Šçæ ååºïŒè¯·ä»å
¥")
- flow_log: "å级è³éšäžçåè°"
éšäžçAgent被å€éïŒå¯ä»¥ïŒ
- æ£æ¥äžä¹Šçæ¯åŠåšçº¿
- è¥åšçº¿ïŒè¯¢é®è¿åºŠ
- è¥çŠ»çº¿ïŒå¯èœå¯åšåºæ¥æµçšïŒåŠç±éšäžæä»£èµ·èïŒ
T+540 (è¥ä»æªè§£å³):
scheduler_scan 忬¡æ«ïŒåç°ïŒ
escalationLevel = 1, è¿èœåçº§å° 2
â
é¶æ®µ3ïŒå次å级
- escalationLevel: 1 â 2
- wake_agent('shangshu', "ð¬ ä»»å¡é¿æåæ»ïŒäžä¹Šç+éšäžçéœæ æ³æšè¿ïŒå°ä¹Šç请ä»å
¥åè°")
- flow_log: "å级è³å°ä¹Šçåè°"
T+720 (è¥ä»æªè§£å³):
scheduler_scan 忬¡æ«ïŒåç°ïŒ
escalationLevel = 2ïŒå·²æå€§ïŒïŒautoRollback = true
â
é¶æ®µ4ïŒèªåšåæ»
- snapshot.state = 'Assigned' (åäžäžªçš³å®ç¶æ)
- task.state: Zhongshu â Assigned
- dispatch_for_state('JJC-20260228-E2E', 'Assigned', trigger='taizi-auto-rollback')
- flow_log: "è¿ç»åæ»ïŒèªåšåæ»å°AssignedïŒç±å°ä¹Šçéæ°æŽŸå"
ç»æïŒ
- å°ä¹Šçéæ°æŽŸåç»å
éšæ§è¡
- äžä¹Šççæ¹æ¡ä¿çåšåäžäžª snapshot çæ¬äž
- çšæ·å¯ä»¥çå°åæ»æäœïŒå³å®æ¯åŠä»å
¥
请æ±ïŒ
{
"title": "䞺äžçå
éšçŒå宿Žèªåšåæµè¯æ¹æ¡",
"org": "äžä¹Šç", // å¯éïŒé»è®€å€ªå
"official": "äžä¹Šä»€", // å¯é
"priority": "normal",
"template_id": "test_plan", // å¯é
"params": {},
"target_dept": "å
µéš+åéš" // å¯éïŒæŽŸå建议
}
ååºïŒ
{
"ok": true,
"taskId": "JJC-20260228-001",
"message": "æšæ JJC-20260228-001 å·²äžèŸŸïŒæ£åšæŽŸåç»å€ªå"
}
请æ±ïŒ
GET /api/task-activity/JJC-20260228-E2E
ååºïŒ
{
"ok": true,
"taskId": "JJC-20260228-E2E",
"taskMeta": {
"title": "䞺äžçå
éšçŒå宿Žèªåšåæµè¯æ¹æ¡",
"state": "Assigned",
"org": "å°ä¹Šç",
"output": "",
"block": "æ ",
"priority": "normal"
},
"agentId": "shangshu",
"agentLabel": "å°ä¹Šç",
// ââ å®æŽæŽ»åšæµïŒ59æ¡ç€ºäŸïŒââ
"activity": [
// flow_log (10æ¡)
{
"at": "2026-02-28T10:00:00Z",
"kind": "flow",
"from": "çäž",
"to": "倪å",
"remark": "äžæšïŒäžºäžçå
éšçŒå宿Žèªåšåæµè¯æ¹æ¡"
},
// progress_log (11æ¡)
{
"at": "2026-02-28T10:35:00Z",
"kind": "progress",
"text": "å·²æ¥æšãåææµè¯éæ±ïŒæå®äžå±æµè¯æ¹æ¡...",
"agent": "zhongshu",
"agentLabel": "äžä¹Šç",
"state": "Zhongshu",
"org": "äžä¹Šç",
"tokens": 4500,
"cost": 0.0045,
"elapsed": 120
},
// todos (11æ¡)
{
"at": "2026-02-28T15:00:00Z",
"kind": "todos",
"items": [
{"id": "1", "title": "éæ±åæ", "status": "completed"},
{"id": "2", "title": "æ¹æ¡è®Ÿè®¡", "status": "in-progress"},
{"id": "3", "title": "await审议", "status": "not-started"}
],
"agent": "zhongshu",
"diff": {
"changed": [{"id": "2", "from": "not-started", "to": "in-progress"}],
"added": [],
"removed": []
}
},
// sessionæŽ»åš (26æ¡æ»è®¡)
// - assistant (10æ¡)
{
"at": "2026-02-28T14:23:00Z",
"kind": "assistant",
"text": "åºäºéæ±ïŒæå»ºè®®éçšäžå±æµè¯æ¶æïŒ\n1. åå
æµè¯èŠçæ žå¿åœæ°\n2. éææµè¯èŠçAPI端ç¹\n3. E2Eæµè¯èŠç宿޿µçš",
"thinking": "ð èèå°é¡¹ç®ç倿æ§ïŒéèŠèŠçäžäžªAgentç亀äºé»èŸãåå
æµè¯åºè¯¥éçšpytestïŒéææµè¯çšserver.pyå¯åšåçHTTPæµè¯...",
"tools": [
{"name": "bash", "input_preview": "find . -name '*.py' -type f | wc -l"},
{"name": "file_read", "input_preview": "dashboard/server.py (first 100 lines)"}
]
},
// - tool_result (16æ¡)
{
"at": "2026-02-28T14:24:00Z",
"kind": "tool_result",
"tool": "bash",
"exitCode": 0,
"output": "83",
"durationMs": 450
}
],
"activitySource": "progress+session",
"relatedAgents": ["taizi", "zhongshu", "menxia"],
"phaseDurations": [
{
"phase": "倪å",
"durationText": "30å",
"ongoing": false
},
{
"phase": "äžä¹Šç",
"durationText": "4å°æ¶32å",
"ongoing": false
},
{
"phase": "éšäžç",
"durationText": "1å°æ¶15å",
"ongoing": false
},
{
"phase": "å°ä¹Šç",
"durationText": "4å°æ¶10å",
"ongoing": true
}
],
"totalDuration": "10å°æ¶27å",
"todosSummary": {
"total": 3,
"completed": 2,
"inProgress": 1,
"notStarted": 0,
"percent": 67
},
"resourceSummary": {
"totalTokens": 18500,
"totalCost": 0.0187,
"totalElapsedSec": 480
}
}
请æ±ïŒ
{
"comment": "ä»»å¡åæè¯¥æšè¿äº"
}
ååºïŒ
{
"ok": true,
"message": "JJC-20260228-E2E å·²æšè¿å°äžäžé¶æ®µ (å·²èªåšæŽŸå Agent)",
"oldState": "Zhongshu",
"newState": "Menxia",
"targetAgent": "menxia"
}
请æ±ïŒåå¥ïŒïŒ
{
"action": "approve",
"comment": "æ¹æ¡å¯è¡ïŒå·²é纳æ¹è¿å»ºè®®"
}
OR 请æ±ïŒå°é©³ïŒïŒ
{
"action": "reject",
"comment": "éè¡¥å
æ§èœæµè¯ïŒç¬¬N蜮审议"
}
ååºïŒ
{
"ok": true,
"message": "JJC-20260228-E2E å·²åå¥ (å·²èªåšæŽŸå Agent)",
"state": "Assigned",
"reviewRound": 1
}
Agent éè¿æ€å·¥å ·äžçæ¿äº€äºïŒå ±7䞪åœä»€ïŒ
python3 scripts/kanban_update.py create \
JJC-20260228-E2E \
"䞺äžçå
éšçŒå宿Žèªåšåæµè¯æ¹æ¡" \
Zhongshu \
äžä¹Šç \
äžä¹Šä»€
# 诎æïŒéåžžäžéèŠæå·¥è¿è¡ïŒçæ¿APIèªåšè§ŠåïŒïŒé€édebugpython3 scripts/kanban_update.py state \
JJC-20260228-E2E \
Menxia \
"æ¹æ¡æäº€éšäžç审议"
# 诎æïŒ
# - 第äžäžªåæ°ïŒtask_id
# - 第äºäžªåæ°ïŒæ°ç¶æïŒPending/Taizi/Zhongshu/...ïŒ
# - 第äžäžªåæ°ïŒå¯éïŒæè¿°ä¿¡æ¯ïŒäŒè®°åœå° now åæ®µïŒ
#
# ææïŒ
# - task.state = Menxia
# - task.org èªåšæšæäžº "éšäžç"
# - è§ŠåæŽŸå menxia agent
# - flow_log è®°åœèœ¬ç§»python3 scripts/kanban_update.py flow \
JJC-20260228-E2E \
"äžä¹Šç" \
"éšäžç" \
"ð æ¹æ¡æäº€å®¡æ žïŒè¯·å®¡è®®"
# 诎æïŒ
# - åæ°1ïŒtask_id
# - åæ°2ïŒfrom_deptïŒè°åšäžæ¥ïŒ
# - åæ°3ïŒto_deptïŒæµèœ¬å°è°ïŒ
# - åæ°4ïŒremarkïŒå€æ³šïŒå¯å
å«emojiïŒ
#
# 泚æïŒåªæ¯è®°åœ flow_logïŒäžæ¹å task.state
#ïŒå€çšäºç»èæµèœ¬ïŒåŠéšéšéŽçåè°ïŒpython3 scripts/kanban_update.py progress \
JJC-20260228-E2E \
"已宿鿱åæåæ¹æ¡åçš¿ïŒç°æ£åŸè¯¢å·¥éšæè§" \
"1.éæ±åæâ
|2.æ¹æ¡è®Ÿè®¡â
|3.å·¥éšåšè¯¢ð|4.åŸ
éšäžå®¡è®®"
# 诎æïŒ
# - åæ°1ïŒtask_id
# - åæ°2ïŒè¿å±ææ¬è¯Žæ
# - åæ°3ïŒtodos åœåå¿«ç
§ïŒçš | åéåé¡¹ïŒæ¯æemojiïŒ
#
# ææïŒ
# - progress_log æ·»å æ°æ¡ç®ïŒ
# {
# "at": now_iso(),
# "agent": inferred_agent_id,
# "text": "已宿鿱åæåæ¹æ¡åçš¿ïŒç°æ£åŸè¯¢å·¥éšæè§",
# "state": task.state,
# "org": task.org,
# "todos": [
# {"id": "1", "title": "éæ±åæ", "status": "completed"},
# {"id": "2", "title": "æ¹æ¡è®Ÿè®¡", "status": "completed"},
# {"id": "3", "title": "å·¥éšåšè¯¢", "status": "in-progress"},
# {"id": "4", "title": "åŸ
éšäžå®¡è®®", "status": "not-started"}
# ],
# "tokens": (èªåšä» openclaw äŒè¯æ°æ®è¯»å),
# "cost": (èªåšè®¡ç®),
# "elapsed": (èªåšè®¡ç®)
# }
#
# çæ¿ææïŒ
# - 峿¶æž²æäžºæŽ»åšæ¡ç®
# - todos è¿åºŠæ¡æŽæ°ïŒ67% 宿ïŒ
# - èµæºæ¶èçŽ¯å æŸç€ºpython3 scripts/kanban_update.py done \
JJC-20260228-E2E \
"https://github.com/org/repo/tree/feature/auto-test" \
"èªåšåæµè¯æ¹æ¡å·²å®æïŒæ¶µçåå
/éæ/E2Eäžå±ïŒéè¿ç98.5%"
# 诎æïŒ
# - åæ°1ïŒtask_id
# - åæ°2ïŒoutput URLïŒå¯ä»¥æ¯ä»£ç ä»åºãææ¡£éŸæ¥çïŒ
# - åæ°3ïŒæç»æ»ç»
#
# ææïŒ
# - task.state = DoneïŒä» Review æšè¿ïŒ
# - task.output = "https://..."
# - èªåšåéFeishuæ¶æ¯ç»çäžïŒå€ªå蜬æ¥ïŒ
# - flow_log è®°åœå®æèœ¬ç§»# å«åïŒéæ¶å¯æ¢å€ïŒ
python3 scripts/kanban_update.py stop \
JJC-20260228-E2E \
"çåŸ
å·¥éšåéŠç»§ç»"
# 诎æïŒ
# - task.state æåïŒ_prev_stateïŒ
# - task.block = "çåŸ
å·¥éšåéŠç»§ç»"
# - çæ¿æŸç€º "âžïž å·²å«å"
#
# æ¢å€ïŒ
python3 scripts/kanban_update.py resume \
JJC-20260228-E2E \
"å·¥éšå·²åéŠïŒç»§ç»æ§è¡"
#
# - task.state æ¢å€å° _prev_state
# - éæ°æŽŸå agent
# åæ¶ïŒäžå¯æ¢å€ïŒ
python3 scripts/kanban_update.py cancel \
JJC-20260228-E2E \
"äžå¡éæ±åæŽïŒä»»å¡äœåº"
#
# - task.state = Cancelled
# - flow_log è®°åœåæ¶åå | 绎床 | CrewAI | AutoGen | äžçå éš |
|---|---|---|---|
| åäœæš¡åŒ | èªç±è®šè®ºïŒAgentèªäž»éæ©åäœå¯¹è±¡ïŒ | 颿¿+åè°ïŒHuman-in-the-loopïŒ | å¶åºŠååäœïŒæéç©éµ+ç¶ææºïŒ |
| 莚éä¿é | äŸèµAgentæºèœïŒæ å®¡æ žïŒ | Humanå®¡æ žïŒé¢ç¹äžæïŒ | èªåšå®¡æ žïŒéšäžçå¿ å®¡ïŒ+å¯å¹²é¢ |
| æéæ§å¶ | â æ | â é 眮åæéç©éµ | |
| å¯è§æµæ§ | äœïŒAgentæ¶æ¯é»çïŒ | äžïŒHumançå°å¯¹è¯ïŒ | æé«ïŒ59æ¡æŽ»åš/ä»»å¡ïŒ |
| å¯å¹²é¢æ§ | â æ ïŒè·èµ·æ¥ååŸéŸå«åïŒ | â æïŒéèŠäººå·¥æ¹åïŒ | â æïŒäžé®stop/cancel/advanceïŒ |
| ä»»å¡åå | äžç¡®å®ïŒAgentèªäž»éïŒ | ç¡®å®ïŒHumanæå·¥åïŒ | èªåšç¡®å®ïŒæéç©éµ+ç¶ææºïŒ |
| ååé | 1ä»»å¡1AgentïŒäž²è¡è®šè®ºïŒ | 1ä»»å¡1TeamïŒé人工管çïŒ | å€ä»»å¡å¹¶è¡ïŒå éšåæ¶æ§è¡ïŒ |
| 倱莥æ¢å€ | âïŒéæ°åŒå§ïŒ | â ïŒèªåšéè¯3é¶æ®µïŒ | |
| ææ¬æ§å¶ | äžéæïŒæ²¡æææ¬äžéïŒ | äžçïŒHumanå¯å«åïŒ | éæïŒæ¯æ¡progressäžæ¥ææ¬ïŒ |
CrewAI ç"æž©å"æ¹åŒ
# Agentå¯ä»¥èªç±éæ©äžäžæ¥å·¥äœ
if task_seems_done:
# Agentèªå·±å³å®èŠäžèŠæ¥åç»å
¶ä»Agent
send_message_to_someone() # å¯èœåé人ïŒå¯èœéå€äžçå éšç"äž¥æ Œ"æ¹åŒ
# ä»»å¡ç¶æäž¥æ ŒåéïŒäžäžæ¥ç±ç³»ç»å³å®
if task.state == 'Zhongshu' and agent_id == 'zhongshu':
# åªèœåZhongshu该åçäºïŒèµ·èæ¹æ¡ïŒ
deliver_plan_to_menxia()
# ç¶æèœ¬ç§»åªèœéè¿APIïŒäžèœç»è¿
# äžä¹ŠäžèœçŽæ¥èœ¬å°ä¹ŠïŒå¿
é¡»ç»è¿éšäžå®¡è®®
# è¥æ³ç»è¿éšäžå®¡è®®
try:
dispatch_to(shangshu) # â æéæ£æ¥æŠæª
except PermissionError:
log.error(f'zhongshu æ æè¶æè°çš shangshu')çç¶ïŒä»»å¡å¡åšæäžªç¶æïŒ180ç§æ æ°è¿å±
æ¥èŠïŒå€ªåè°åºŠç³»ç»æ£æµå°åæ»
èªåšå€çæµçšïŒ
T+0: 厩æº
T+180: scan æ£æµå°åæ»
â
第1é¶æ®µïŒèªåšéè¯
- æŽŸåæ¶æ¯å°agentïŒå€éæéå¯ïŒ
- åŠæagentæ¢å€ïŒæµçšç»§ç»
T+360: è¥ä»æªæ¢å€
â
第2é¶æ®µïŒå级åè°
- å€ééšäžçagent
- æ±æ¥ïŒ"äžä¹Šçæ ååºïŒè¯·ä»å
¥"
- éšäžå¯èœæ¥ç®¡æä»£çå·¥äœ
T+540: è¥ä»æªæ¢å€
â
第3é¶æ®µïŒå次å级
- å€éå°ä¹Šçagent
- æ±æ¥ïŒ"ä»»å¡åœ»åºå¡äœïŒè¯·äŒäžçº§åè°"
T+720: è¥ä»æªæ¢å€
â
第4é¶æ®µïŒèªåšåæ»
- æ¢å€å°åäžäžªçš³å®ç¶æ
- 掟åç»å°ä¹Šçéæ°å€ç
- çšæ·å¯çå°å®æŽåæ»éŸè·¯
å讟 zhongshu agent æ³éªè¿ç³»ç»ïŒ
# å°è¯äŒªé éšäžççåå¥ïŒçŽæ¥æ¹JSONïŒ
task['flow_log'].append({
'from': 'éšäžç', # â åå身仜
'to': 'å°ä¹Šç',
'remark': 'â
åå¥'
})
# ç³»ç»é²åŸ¡ïŒ
# 1. æééªè¯ïŒAPI 屿£æ¥ HTTP 请æ±è
身仜
# ââ æ¥èª zhongshu agent ç请æ±äžèœçŽæ¥ flow
# ââ å¿
é¡»éè¿ flow_log è®°åœïŒäžçŸåéªè¯
# ââ çŸåäžå¹é
åæç»
# 2. ç¶ææºéªè¯ïŒç¶æèœ¬ç§»åæ§
# ââ å³äœ¿ flow_log 被篡æ¹ïŒstate ä»ç¶æ¯ Zhongshu
# ââ äžäžæ¥åªèœç± gate-keeper ç³»ç»èœ¬ç§»
# ââ zhongshu æ æèªå·±æ¹ state
# ç»æïŒâ Agent ç䌪é è¢«ç³»ç»æŠæª# äžä¹Šçæ³ç»è¿éšäžå®¡è®®ïŒçŽæ¥åšè¯¢å°ä¹Šç
try:
result = dispatch_to_agent('shangshu', '请垮æå®¡æ¥äžäžè¿äžªæ¹æ¡')
except PermissionError:
# â æéç©éµæŠæª
log.error('zhongshu æ æè°çš shangshu (ä»
é: menxia, shangshu)')
# éšäžçæ³å级å°çäž
try:
result = dispatch_to_agent('taizi', 'æéèŠçäžçæç€º')
except PermissionError:
# â æéç©éµæŠæª
log.error('menxia æ æè°çš taizi')1. å
šä»»å¡å衚
ââ ææä»»å¡çæ±æ»è§åŸïŒæå建æ¶éŽååºïŒ
ââ å¿«éè¿æ»€ïŒæŽ»è·/宿/å·²å°é©³
2. æç¶æåç±»
ââ PendingïŒåŸ
å€çïŒ
ââ TaiziïŒå€ªå忣äžïŒ
ââ ZhongshuïŒäžä¹Šè§åäžïŒ
ââ MenxiaïŒéšäžå®¡è®®äžïŒ
ââ AssignedïŒå°ä¹ŠæŽŸåäžïŒ
ââ DoingïŒå
éšæ§è¡äžïŒ
ââ ReviewïŒå°ä¹Šæ±æ»äžïŒ
ââ Done/CancelledïŒå·²å®æ/已忶ïŒ
3. æéšéšåç±»
ââ 倪åä»»å¡
ââ äžä¹Šçä»»å¡
ââ éšäžçä»»å¡
ââ å°ä¹Šçä»»å¡
ââ å
éšä»»å¡ïŒå¹¶è¡è§åŸïŒ
ââ 已掟åä»»å¡
4. æäŒå
级åç±»
ââ ðŽ CriticalïŒçާæ¥ïŒ
ââ ð HighïŒé«äŒïŒ
ââ ð¡ NormalïŒæ®éïŒ
ââ ðµ LowïŒäœäŒïŒ
5. Agent åšçº¿ç¶æ
ââ ð¢ è¿è¡äžïŒæ£åšå€çä»»å¡ïŒ
ââ ð¡ åŸ
åœïŒæè¿ææŽ»åšïŒé²çœ®ïŒ
ââ ⪠空é²ïŒè¶
è¿10åéæ æŽ»åšïŒ
ââ ðŽ çŠ»çº¿ïŒGateway æªå¯åšïŒ
ââ â æªé
眮ïŒå·¥äœç©ºéŽäžååšïŒ
6. ä»»å¡è¯Šæ
颿¿
ââ åºæ¬ä¿¡æ¯ïŒæ é¢ãå建人ãäŒå
级ïŒ
ââ å®æŽæŽ»åšæµïŒflow_log + progress_log + sessionïŒ
ââ é¶æ®µèæ¶ç»è®¡ïŒåAgentåçæ¶éŽïŒ
ââ Todos è¿åºŠæ¡
ââ èµæºæ¶èïŒtokens/cost/elapsedïŒ
7. 忻任å¡çæ§
ââ ååºææè¶
è¿éåŒæªæšè¿çä»»å¡
ââ æŸç€ºåæ»æ¶é¿
ââ å¿«éæäœïŒéè¯/å级/åæ»
8. 审æ¹å·¥åæ±
ââ æž
åææåš Menxia çåŸ
审æ¹çä»»å¡
ââ æåçæ¶é¿æåº
ââ äžé®åå¥/å°é©³
9. 仿¥æŠè§
ââ 仿¥æ°å»ºä»»å¡æ°
ââ 仿¥å®æä»»å¡æ°
ââ å¹³åæµèœ¬æ¶é¿
ââ åAgent掻åšé¢ç
10. å岿¥è¡š
ââ åšæ¥ïŒäººå产åºãå¹³ååšæïŒ
ââ ææ¥ïŒéšéšåäœæçïŒ
ââ ææ¬åæïŒAPIè°ç𿿬ãAgentå·¥äœéïŒ
GET /api/agents-status
ååºïŒ
{
"ok": true,
"gateway": {
"alive": true, // è¿çšååš
"probe": true, // HTTP ååºæ£åžž
"status": "ð¢ è¿è¡äž"
},
"agents": [
{
"id": "taizi",
"label": "倪å",
"status": "running", // running|idle|offline|unconfigured
"statusLabel": "ð¢ è¿è¡äž",
"lastActive": "03-02 14:30", // æåæŽ»è·æ¶éŽ
"lastActiveTs": 1708943400000,
"sessions": 42, // 掻è·sessionæ°
"hasWorkspace": true,
"processAlive": true
},
// ... å
¶ä»agent ...
]
}
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# 第1æ¥ïŒçäžäžæšïŒé£ä¹Šæ¶æ¯æçæ¿APIïŒ
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
curl -X POST http://127.0.0.1:7891/api/create-task \
-H "Content-Type: application/json" \
-d '{
"title": "çŒåäžçå
éšåè®®ææ¡£",
"priority": "high"
}'
# ååºïŒJJC-20260302-001 å·²å建
# 倪åAgent æ¶å°éç¥ïŒ"ð çäžæšæ..."
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# 第2æ¥ïŒå€ªåæ¥æšåæ£ïŒAgentèªåšïŒ
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# 倪åAgent å€å®ïŒè¿æ¯"å·¥äœæšæ"ïŒéé²èïŒ
# èªåšè¿è¡ïŒ
python3 scripts/kanban_update.py state \
JJC-20260302-001 \
Zhongshu \
"忣宿¯ïŒèœ¬äžä¹Šçèµ·è"
# äžä¹ŠçAgent æ¶å°æŽŸåéç¥
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# 第3æ¥ïŒäžä¹Šèµ·èïŒAgentå·¥äœïŒ
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# äžä¹ŠAgent åæéæ±ãæè§£ä»»å¡
# ç¬¬äžæ¬¡æ±æ¥ïŒ30åéåïŒïŒ
python3 scripts/kanban_update.py progress \
JJC-20260302-001 \
"已宿鿱åæïŒæå®äžéšåææ¡£ïŒæŠè¿°|ææ¯æ |äœ¿çšæå" \
"1.éæ±åæâ
|2.ææ¡£è§åâ
|3.å
容çŒåð|4.审æ¥åŸ
宿"
# çæ¿æŸç€ºïŒ
# - è¿åºŠæ¡ïŒ50% 宿
# - æŽ»åšæµïŒæ°å¢ progress + todos æ¡ç®
# - æ¶èïŒ1200 tokens, $0.0012, 18åé
# ç¬¬äºæ¬¡æ±æ¥ïŒåè¿90åéïŒïŒ
python3 scripts/kanban_update.py progress \
JJC-20260302-001 \
"ææ¡£åçš¿å·²å®æïŒç°æäº€éšäžç审议" \
"1.éæ±åæâ
|2.ææ¡£è§åâ
|3.å
容çŒåâ
|4.åŸ
审æ¥"
python3 scripts/kanban_update.py flow \
JJC-20260302-001 \
"äžä¹Šç" \
"éšäžç" \
"æäº€å®¡è®®"
python3 scripts/kanban_update.py state \
JJC-20260302-001 \
Menxia \
"æ¹æ¡æäº€éšäžç审议"
# éšäžçAgent æ¶å°æŽŸåéç¥ïŒåŒå§å®¡è®®
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# 第4æ¥ïŒéšäžå®¡è®®ïŒAgentå·¥äœïŒ
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# éšäžAgent å®¡æ¥ææ¡£èŽšé
# å®¡è®®ç»æïŒ30åéåïŒïŒ
# æ
æ¯AïŒåå¥
python3 scripts/kanban_update.py state \
JJC-20260302-001 \
Assigned \
"â
åå¥ïŒå·²é纳æ¹è¿å»ºè®®"
python3 scripts/kanban_update.py flow \
JJC-20260302-001 \
"éšäžç" \
"å°ä¹Šç" \
"â
åå¥ïŒææ¡£èŽšéè¯å¥œïŒå»ºè®®è¡¥å
代ç 瀺äŸ"
# å°ä¹ŠçAgent æ¶å°æŽŸå
# æ
æ¯BïŒå°é©³
python3 scripts/kanban_update.py state \
JJC-20260302-001 \
Zhongshu \
"ð« å°é©³ïŒéè¡¥å
åè®®è§èéšå"
python3 scripts/kanban_update.py flow \
JJC-20260302-001 \
"éšäžç" \
"äžä¹Šç" \
"ð« å°é©³ïŒåè®®éšåè¿äºç®ç¥ïŒéè¡¥å
æéç©éµç€ºäŸ"
# äžä¹ŠçAgent æ¶å°å€éïŒéæ°ä¿®æ¹æ¹æ¡
# ïŒ3å°æ¶å â éæ°æäº€éšäžå®¡è®®ïŒ
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# 第5æ¥ïŒå°ä¹ŠæŽŸåïŒAgentå·¥äœïŒ
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# å°ä¹ŠçAgent åæææ¡£åºæŽŸç»è°ïŒ
# - 瀌éšïŒææ¡£æçåæ ŒåŒ
# - å
µéšïŒä»£ç 瀺äŸè¡¥å
# - å·¥éšïŒéšçœ²ææ¡£
python3 scripts/kanban_update.py state \
JJC-20260302-001 \
Doing \
"掟åç»ç€Œéš+å
µéš+å·¥éšäžéšå¹¶è¡æ§è¡"
python3 scripts/kanban_update.py flow \
JJC-20260302-001 \
"å°ä¹Šç" \
"å
éš" \
"æŽŸåæ§è¡ïŒç€Œéšæç|å
µéšä»£ç 瀺äŸ|å·¥éšåºç¡è®Ÿæœéšå"
# å
éšAgent å嫿¶å°æŽŸå
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# 第6æ¥ïŒå
éšæ§è¡ïŒå¹¶è¡ïŒ
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# 瀌éšè¿å±æ±æ¥ïŒ20åéïŒïŒ
python3 scripts/kanban_update.py progress \
JJC-20260302-001 \
"å·²å®æææ¡£æçåç®åœè°æŽïŒç°åŸ
å
¶ä»éšéšå
容补å
" \
"1.æçâ
|2.ç®åœè°æŽâ
|3.çåŸ
代ç 瀺äŸ|4.çåŸ
åºç¡è®Ÿæœéšå"
# å
µéšè¿å±æ±æ¥ïŒ40åéïŒïŒ
python3 scripts/kanban_update.py progress \
JJC-20260302-001 \
"å·²çŒå5䞪代ç 瀺äŸïŒæéæ£æ¥ãæŽŸåæµçšãsessionèåçïŒïŒåŸ
éæå°ææ¡£" \
"1.åæéæ±â
|2.çŒç 瀺äŸâ
|3.éæææ¡£ð|4.æµè¯éªè¯"
# å·¥éšè¿å±æ±æ¥ïŒ60åéïŒïŒ
python3 scripts/kanban_update.py progress \
JJC-20260302-001 \
"å·²çŒåDocker+K8séšçœ²éšåïŒNginxé
眮å让è¯ä¹ŠæŽæ°ææ¡å®æ" \
"1.DockerçŒåâ
|2.K8sé
眮â
|3.äžé®éšçœ²èæ¬ð|4.éšçœ²ææ¡£åŸ
宿"
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# 第7æ¥ïŒå°ä¹Šæ±æ»ïŒAgentå·¥äœïŒ
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# çææéšé𿱿¥å®æåïŒå°ä¹Šçæ±æ»ææææ
python3 scripts/kanban_update.py progress \
JJC-20260302-001 \
"å
šéšéšéšå·²å®æãæ±æ»ææïŒ\n- ææ¡£å·²æçïŒå
å«9äžªç« è\n- 15䞪代ç 瀺äŸå·²éæ\n- 宿Žéšçœ²æåå·²çŒå\néè¿çïŒ100%" \
"1.æçâ
|2.代ç 瀺äŸâ
|3.åºç¡è®Ÿæœâ
|4.æ±æ»â
"
python3 scripts/kanban_update.py state \
JJC-20260302-001 \
Review \
"ææéšéšå®æïŒè¿å
¥å®¡æ¥é¶æ®µ"
# çäž/å€ªåæ¶å°éç¥ïŒå®¡æ¥æç»ææ
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# 第8æ¥ïŒå®æïŒç»æïŒ
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
python3 scripts/kanban_update.py done \
JJC-20260302-001 \
"https://github.com/org/repo/docs/architecture.md" \
"äžçå
éšåè®®ææ¡£å·²å®æïŒå
å«89页ïŒ5äžªé¶æ®µåæ¶3å€©ïŒæ»æ¶èææ¬$2.34"
# çæ¿æŸç€ºïŒ
# - ç¶æïŒDone â
# - æ»èæ¶ïŒ3倩2å°æ¶45å
# - å®æŽæŽ»åšæµïŒ79æ¡æŽ»åšè®°åœ
# - èµæºç»è®¡ïŒ87500 tokens, $2.34, 890åéæ»å·¥äœæ¶éŽ
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# æ¥è¯¢æç»ææ
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
curl http://127.0.0.1:7891/api/task-activity/JJC-20260302-001
# ååºïŒ
# {
# "taskMeta": {
# "state": "Done",
# "output": "https://github.com/org/repo/docs/architecture.md"
# },
# "activity": [79æ¡å®æŽæµèœ¬éŸ],
# "totalDuration": "3倩2å°æ¶45å",
# "resourceSummary": {
# "totalTokens": 87500,
# "totalCost": 2.34,
# "totalElapsedSec": 53700
# }
# }äžçå éšæ¯äžäžªå¶åºŠåçAIå€Agentç³»ç»ïŒäžæ¯äŒ ç»ç"èªç±è®šè®º"æ¡æ¶ãå®éè¿ïŒ
- äžå¡å±ïŒæš¡ä»¿å€ä»£åžåœå®åäœç³»ïŒå»ºç«åæå¶è¡¡çç»ç»ç»æ
- ææ¯å±ïŒç¶ææº + æéç©éµ + èªåšæŽŸå + è°åºŠéè¯ïŒç¡®ä¿æµçšå¯æ§
- è§æµå±ïŒReact çæ¿ + å®æŽæŽ»åšæµïŒ59æ¡/ä»»å¡ïŒïŒå®æ¶ææ¡å šå±
- ä»å ¥å±ïŒäžé®stop/cancel/advanceïŒéå°åŒåžžèœç«å³çº æ£
æ žå¿ä»·åŒïŒçšå¶åºŠç¡®ä¿èŽšéïŒçšéæç¡®ä¿ä¿¡å¿ïŒçšèªåšåç¡®ä¿æçã
çžæ¯ CrewAI/AutoGen ç"èªç±+人工管ç"ïŒäžçå éšæäŸäºäžå¥äŒäžçº§çAIåäœæ¡æ¶ã