@omnicrawl/cli-win32-ia32 0.2.8 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/omnicrawl.exe +0 -0
- package/host/omnicrawl/config/templates/config.example.toml +88 -26
- package/host/omnicrawl/config/templates/decision_models.example.toml +62 -7
- package/host/omnicrawl/config/templates/embedding_models.example.toml +31 -0
- package/host/omnicrawl/config/templates/subagents.example.toml +37 -38
- package/host/omnicrawl/extensions/onejev_worker.py +293 -0
- package/host/omnicrawl/templates/speaking-style.md +13 -0
- package/host/omnicrawl/templates/subagents/computer-use.md +75 -0
- package/host/omnicrawl/templates/system_prompt.md +5 -1
- package/host/omnicrawl-api.exe +0 -0
- package/host/omnicrawl-decision.exe +0 -0
- package/host/omnicrawl-host.exe +0 -0
- package/host/omnicrawl-mcp-server.exe +0 -0
- package/host/omnicrawl-tui.exe +0 -0
- package/host/omnicrawl.exe +0 -0
- package/host/rust/assets/config-templates/config.example.toml +88 -26
- package/host/rust/assets/config-templates/decision_models.example.toml +62 -7
- package/host/rust/assets/config-templates/embedding_models.example.toml +31 -0
- package/host/rust/assets/config-templates/subagents.example.toml +37 -38
- package/host/rust/assets/extensions/onejev_worker.py +293 -0
- package/host/rust/assets/templates/speaking-style.md +13 -0
- package/host/rust/assets/templates/subagents/computer-use.md +75 -0
- package/host/rust/assets/templates/system_prompt.md +5 -1
- package/package.json +1 -1
- package/version.json +3 -3
- package/host/omnicrawl/templates/model_reply_compaction_system.md +0 -28
- package/host/rust/assets/templates/model_reply_compaction_system.md +0 -28
|
@@ -16,7 +16,7 @@ name = "Jev 主渠道"
|
|
|
16
16
|
# 请求方式:
|
|
17
17
|
# jev —— Jev 原生接口,POST {base_url}/v1/decide,state + questions 直接进请求体。
|
|
18
18
|
# chat_completions —— OpenAI 兼容接口,POST {base_url}/chat/completions,
|
|
19
|
-
# base_url 与 [image_gen]
|
|
19
|
+
# base_url 与 [image_gen] 同口径(填到 /v1);
|
|
20
20
|
# 同一份 state + questions 作为 user 消息发出,从返回内容里解析 answers。
|
|
21
21
|
# onejev —— 本机自部署的 OneJev 服务,POST {base_url}/v1/systemone,
|
|
22
22
|
# 请求与响应形状与 jev 相同;base_url 指向本地服务(见下方 [local])。
|
|
@@ -29,6 +29,10 @@ api_key = ""
|
|
|
29
29
|
api_key_env = "JEV_API_KEY"
|
|
30
30
|
# 决策模型:jev-latest 跟随最新版本;生产环境建议钉死版本(如 jev-1.13.0)。
|
|
31
31
|
model = "jev-latest"
|
|
32
|
+
# 请求失败后的重试次数(不含首次请求):连接失败、超时与上游 5xx 按 0.2s→0.4s→0.8s(封顶 2s)
|
|
33
|
+
# 退避重试这么多次;4xx 不重试(那是请求本身的问题),读不到可用答案也不重试(不猜、不编)。
|
|
34
|
+
# 一次决策在回合路径上同步等待,因此次数要有界(上限 10);0 表示不重试。
|
|
35
|
+
retry_count = 3
|
|
32
36
|
enabled = true
|
|
33
37
|
|
|
34
38
|
# 决策模型的功能开关。开关关闭时相关功能走原路径(当前即对话模型审查与本地排序检索)。
|
|
@@ -47,10 +51,32 @@ memory_search_rerank = false
|
|
|
47
51
|
# 知识库检索使用决策模型排序:对 kb_search 同理,候选描述用标题、路径与摘要。
|
|
48
52
|
kb_search_rerank = false
|
|
49
53
|
|
|
54
|
+
# Skill 检索使用决策模型排序:开启后 skill_search 带关键词检索时先取更宽的候选池(最多 20 条),
|
|
55
|
+
# 再让决策模型按相关程度排序,按各候选项的置信度降序返回,条数仍按调用方给的 max_results。
|
|
56
|
+
# 不带关键词(按作用域列出全部)时没有「相关度」可判,不送审。决策服务不可用时回退本地排序,
|
|
57
|
+
# 检索工具不会因此失败。
|
|
58
|
+
skill_search_rerank = false
|
|
59
|
+
|
|
60
|
+
# 联网搜索使用决策模型排序:开启后 web_search 对每一路渠道结果单独送审——本地先按关键词
|
|
61
|
+
# 覆盖度圈出该路前 10 条,交决策模型按相关度排序后每路保留前 5 条。engine=mixed 时每路各自
|
|
62
|
+
# 排序、各自最多 5 条,因此返回总量随渠道数增长(N 路 × 5 条),没有固定总量上限;只有调用方
|
|
63
|
+
# 显式给了 max_results 才收窄总数。决策服务不可用时回退本地顺序,搜索不会因此失败。
|
|
64
|
+
web_search_rerank = false
|
|
65
|
+
|
|
66
|
+
# 联网搜索使用向量模型排序:与上一项并行,各自读自己的开关。它是唯一画在「向量模型」
|
|
67
|
+
# 设置页顶部的开关(其余开关都在「结构化决策模型」页),配置仍在本段。开启后用「向量模型」设置里
|
|
68
|
+
# 配置的渠道把查询与每条候选都算成向量,按余弦相似度给该路结果排序,每路仍保留前 5 条;
|
|
69
|
+
# 判定条数上限的口径与 web_search_rerank 一致。两项都开启时**向量优先**:向量不可用
|
|
70
|
+
# (未配渠道、无凭据、请求失败)才回退决策模型排序;决策模型也不可用时回退本地关键词排序,
|
|
71
|
+
# 搜索不会因此失败。向量通路不依赖决策模型服务,因此不必先配决策渠道。
|
|
72
|
+
web_search_embedding_rerank = false
|
|
73
|
+
|
|
50
74
|
# 提问由决策模型自动作答:开启后模型调用 ask_user 且带选项时,不再弹面板等你作答,
|
|
51
75
|
# 而是把提问正文、你本回合的请求与本回合已有的顾问答复交给决策模型,让它选最符合
|
|
52
|
-
#
|
|
53
|
-
#
|
|
76
|
+
# 你意图的一项。候选项末尾固定带一项「我有自己的想法...」:需要你自己填写答案时决策
|
|
77
|
+
# 模型会选它,提问就退回面板(由你选或输入自己的回答)。没有选项的提问(自由问答)
|
|
78
|
+
# 照旧交给你。决策服务不可用时回退到人工提问,不会替你瞎猜;自动作答会在会话流里
|
|
79
|
+
# 留一条提示。
|
|
54
80
|
ask_user_custody = false
|
|
55
81
|
|
|
56
82
|
# 旧一轮工具调用按需淘汰(先标注-后压缩):只裁决「刚变老」的那一批(Agent 又推进了一批
|
|
@@ -72,6 +98,34 @@ ask_user_custody = false
|
|
|
72
98
|
# 重启后按同一份投影重建同一份上下文。
|
|
73
99
|
tool_call_prune = false
|
|
74
100
|
|
|
101
|
+
# 新提问自动建议工具:开启后,你每发一条新提问,宿主先把**这一条提问的正文**(不带历史)
|
|
102
|
+
# 与一份「已启用工具」的清单交给决策模型,问它「本次该用哪些工具」。每个工具一个是否题,
|
|
103
|
+
# 概率 ≥0.5 进建议名单、<0.5 排除;另有最后一个兜底判定——本条属于普通对话、寒暄或无从
|
|
104
|
+
# 判断的异常任务时,整份建议作废(不插入任何内容)。名单非空时在提问尾部追加一行
|
|
105
|
+
# 「当前请求建议调用tool:a、b。」再发给模型。
|
|
106
|
+
#
|
|
107
|
+
# 清单就是本机工具表里全部已启用的工具(每项为工具名 + 描述首句,截断到 24 字),不做人工
|
|
108
|
+
# 取舍:关掉的工具与当前没有运行期的工具(语音合成、顾问、Windows 组)本来就不在表里。
|
|
109
|
+
# 该用专用工具还是顺手用泛化工具,由决策模型按选择要求判断——子代理、生图、语音、桌面
|
|
110
|
+
# 操作这类专用工具贴合任务特殊性;读文件、搜代码、执行命令、记待办这类通用工具几乎每轮
|
|
111
|
+
# 都能顺手用一下,除非本次任务明确围绕它展开,否则判为用不到。决策服务不可用(缺渠道/凭据、
|
|
112
|
+
# 超时、响应不可解析)时按「本次无建议」处理,提问原文照发,不会挡住你的输入。
|
|
113
|
+
tool_suggestion = false
|
|
114
|
+
|
|
115
|
+
# 新提问自动建议子代理:与上面的 `tool_suggestion` 形状相同、互相独立,可单开也可同时开。
|
|
116
|
+
# 区别在候选清单:这里送审的是**本机可调度的子代理角色**(每项为角色名 + 角色描述首句,
|
|
117
|
+
# 截断到 24 字),与 `subagent` 工具的参数枚举同源,内核不能调度的角色不会进审。
|
|
118
|
+
# 判定同样是每个角色一个是否题 + 一个兜底判定(本条是否属于无需任何子代理的普通对话),
|
|
119
|
+
# 概率阈值都是 0.5。名单非空时在提问尾部追加一行「当前请求建议使用子代理:explore、plan。」。
|
|
120
|
+
#
|
|
121
|
+
# 子代理个个都是一次带独立上下文的子回合,开销明显高于主模型自己动手,因此判定要求特别
|
|
122
|
+
# 强调:只有能拆成相互独立、可并行推进的子任务,或需要隔离视角核查与隔离改动时才算用得到;
|
|
123
|
+
# 单步问答、小改动、串行依赖的活一律判为用不到。
|
|
124
|
+
#
|
|
125
|
+
# 两类同时开启时,两条请求**并发发起**,总耗时取两者的较大者而不是相加;任一失败只少一行。
|
|
126
|
+
# 决策服务不可用时同样按「本次无建议」处理,提问原文照发。
|
|
127
|
+
subagent_suggestion = false
|
|
128
|
+
|
|
75
129
|
# 决策 REST 接口:把决策能力暴露给本机其它程序(CLI 工具、Skill、外部脚本),
|
|
76
130
|
# 它们不必经主模型就能拿到决策结果。大脑(主模型)负责规划,小脑(决策模型)负责
|
|
77
131
|
# 快速判定,两者通过这个接口协同。
|
|
@@ -91,14 +145,15 @@ tool_call_prune = false
|
|
|
91
145
|
[api]
|
|
92
146
|
enabled = false
|
|
93
147
|
host = "127.0.0.1"
|
|
94
|
-
# 端口:与本地 API(8765
|
|
148
|
+
# 端口:与本地 API(8765)错开。
|
|
95
149
|
port = 8767
|
|
96
150
|
|
|
97
151
|
# 自部署(OneJev):在本机跑一份决策模型,不依赖云端服务。
|
|
98
152
|
#
|
|
99
153
|
# 官方发布 0.8B / 4B / 9B / 27B / 27B-FP8 五档(Hugging Face 的 OmniJev/OneJev-*),
|
|
100
|
-
#
|
|
101
|
-
#
|
|
154
|
+
# 由本机的判定 worker 直接驱动引擎,经命名管道(Windows)/ Unix socket 提供同一套判定——
|
|
155
|
+
# 不经 TCP 与 HTTP。设置面板的「结构化决策模型」页可以一键下载某一档、准备运行环境、
|
|
156
|
+
# 启停本地服务,并把决策渠道切到自部署。
|
|
102
157
|
# 权重与虚拟环境都在 model_dir 下(默认 ~/.omnicrawl/onejev),不进工作区。
|
|
103
158
|
[local]
|
|
104
159
|
# 当前部署的尺寸:0.8B / 4B / 9B / 27B-FP8 / 27B。
|
|
@@ -113,5 +168,5 @@ model_dir = ""
|
|
|
113
168
|
# 本机已有服务在跑(本机所有实例共用一份)就直接复用,不会重复启动;服务常驻,
|
|
114
169
|
# 不随实例退出被回收,要停就在设置页点「停止服务」。
|
|
115
170
|
auto_start = true
|
|
116
|
-
#
|
|
171
|
+
# 启动后等待服务就绪的上限(秒):首次加载权重并捕获 CUDA 图,大尺寸会久一些。
|
|
117
172
|
health_timeout_seconds = 600
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# OmniCrawl 向量模型配置(在设置面板「向量模型」页新建渠道后自动生成 embedding_models.toml)
|
|
2
|
+
#
|
|
3
|
+
# 向量模型把一段文本映射成一个定长浮点向量,记忆检索用它做语义召回:先把查询与
|
|
4
|
+
# 候选记忆都换成向量,按余弦相似度排序,再与关键词规则分做倒数排名融合。
|
|
5
|
+
#
|
|
6
|
+
# 全部走云端 OpenAI 兼容接口(POST {base_url}/embeddings),不使用本地模型:
|
|
7
|
+
# 本地推理要多一份模型文件与推理运行时,代价远高于收益。
|
|
8
|
+
#
|
|
9
|
+
# 未配置、未启用或缺凭据时,记忆检索直接退回纯关键词规则排序——这是刻意的兜底,
|
|
10
|
+
# 不是错误:向量召回是纯加速项,不可用不该让检索失败。
|
|
11
|
+
#
|
|
12
|
+
# 密钥请用环境变量(api_key_env);也可以直接写 api_key,但不要提交到版本库。
|
|
13
|
+
version = 1
|
|
14
|
+
|
|
15
|
+
# 默认使用的向量渠道 key;留空或指向不存在的渠道时取第一条启用的渠道。
|
|
16
|
+
default_key = "openai-embedding"
|
|
17
|
+
|
|
18
|
+
[channels.openai-embedding]
|
|
19
|
+
name = "OpenAI 向量"
|
|
20
|
+
# base_url 按 OpenAI 兼容口径填到 /v1,这里只追加 /embeddings。
|
|
21
|
+
base_url = "https://api.openai.com/v1"
|
|
22
|
+
api_key = ""
|
|
23
|
+
api_key_env = "OPENAI_API_KEY"
|
|
24
|
+
model = "text-embedding-3-small"
|
|
25
|
+
# 期望维度;0 表示由服务端决定(不下发 dimensions 字段)。
|
|
26
|
+
# 换模型或改维度后旧向量会自然失效(缓存键带渠道指纹)。
|
|
27
|
+
dimensions = 0
|
|
28
|
+
# 单次请求最多携带的文本条数;候选多时按此分批串行发送。
|
|
29
|
+
batch_size = 64
|
|
30
|
+
timeout_seconds = 30
|
|
31
|
+
enabled = true
|
|
@@ -1,38 +1,37 @@
|
|
|
1
|
-
# OmniCrawl 子代理设置模板(首次启动自动复制为 subagents.toml)
|
|
2
|
-
# 子代理设置已从 config.toml 完全迁移到本文件;程序只读取 subagents.toml,
|
|
3
|
-
# 不再回退读取 config.toml 的 [subagents] 段。
|
|
4
|
-
version = 1
|
|
5
|
-
|
|
6
|
-
# SubAgent
|
|
7
|
-
[subagents]
|
|
8
|
-
enabled = false
|
|
9
|
-
max_depth = 1
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# model = "default-chat"
|
|
1
|
+
# OmniCrawl 子代理设置模板(首次启动自动复制为 subagents.toml)
|
|
2
|
+
# 子代理设置已从 config.toml 完全迁移到本文件;程序只读取 subagents.toml,
|
|
3
|
+
# 不再回退读取 config.toml 的 [subagents] 段。
|
|
4
|
+
version = 1
|
|
5
|
+
|
|
6
|
+
# SubAgent 全局边界:默认关闭;开启后三种角色的能力开关默认放开(verify / standard / worktree)。
|
|
7
|
+
[subagents]
|
|
8
|
+
enabled = false
|
|
9
|
+
max_depth = 1
|
|
10
|
+
default_timeout_seconds = 3600
|
|
11
|
+
model_request_concurrency = 2
|
|
12
|
+
# 保留以兼容既有配置;子代理已改为回合内同步串行执行,本项不再生效。
|
|
13
|
+
allow_background = false
|
|
14
|
+
# 仅开放已脱敏父公开上下文的 read_only/verify Fork;默认关闭。
|
|
15
|
+
allow_fork = false
|
|
16
|
+
allow_shared_workspace_writes = false
|
|
17
|
+
allow_worktree = true
|
|
18
|
+
allow_standard_agent = true
|
|
19
|
+
enable_verify_agent = true
|
|
20
|
+
verify_command_timeout_seconds = 120
|
|
21
|
+
task_retention_minutes = 60
|
|
22
|
+
result_summary_chars = 6000
|
|
23
|
+
|
|
24
|
+
# 单独设置每个子代理使用的模型;key 必须与 Agent 定义名一致(如 explore/plan/verify)。
|
|
25
|
+
# model 支持:models.toml key/alias、profile/model_id 或裸 model_id;
|
|
26
|
+
# 缺省或写 inherit 表示沿用父模型(与任务级 model=inherit 语义一致)。
|
|
27
|
+
[subagents.models.explore]
|
|
28
|
+
# model = "default-chat"
|
|
29
|
+
|
|
30
|
+
[subagents.models.plan]
|
|
31
|
+
# model = "default-chat"
|
|
32
|
+
|
|
33
|
+
[subagents.models.verify]
|
|
34
|
+
# model = "default-chat"
|
|
35
|
+
|
|
36
|
+
[subagents.models.general-purpose]
|
|
37
|
+
# model = "default-chat"
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"""OneJev 本地决策 worker:在命名管道(Windows)/ Unix socket 上提供 System One 判定。
|
|
2
|
+
|
|
3
|
+
为什么不用 `qev serve`:官方那个服务经 uvicorn/FastAPI 暴露 HTTP,本机调用方因此要为
|
|
4
|
+
一次纯内存判定多付一层 TCP + ASGI 中转。这个 worker 直接持有 `qev` 的引擎对象,
|
|
5
|
+
请求从管道进来、答案从管道出去,中间只有一次 JSON 编解码。
|
|
6
|
+
|
|
7
|
+
协议(NDJSON,一行一个 JSON 对象):
|
|
8
|
+
|
|
9
|
+
请求 {"model": "...", "state": {...}, "questions": {...}}
|
|
10
|
+
成功 {"model": "...", "answers": {...}, "usage": {...}, "qev": {...}}
|
|
11
|
+
失败 {"error": "可读原因"}
|
|
12
|
+
|
|
13
|
+
响应形状故意与 HTTP 端点一致,Rust 侧读答案的代码因此不必区分两种传输。
|
|
14
|
+
|
|
15
|
+
服务是**本机共用**的常驻进程,由 OmniCrawl 宿主以脱离进程的方式拉起(见
|
|
16
|
+
`omnicrawl-onejev` 的 `server` 模块):加载权重、创建端点、打出就绪标记,之后一直服务;
|
|
17
|
+
停止只能显式做(宿主按 PID 回收整棵进程树)。
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import json
|
|
24
|
+
import logging
|
|
25
|
+
import os
|
|
26
|
+
import socket
|
|
27
|
+
import sys
|
|
28
|
+
import threading
|
|
29
|
+
import time
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
# 就绪标记:宿主按它认定「服务已经能接请求」(写进服务日志)。
|
|
33
|
+
READY_MARKER = "[onejev] 服务就绪"
|
|
34
|
+
# 单次连接的等待上限(秒):客户端总是连上就发,静默超时说明对端已经不在了。
|
|
35
|
+
CONNECTION_TIMEOUT_SECONDS = 60.0
|
|
36
|
+
# 管道缓冲区:一次判定请求的 state 上限是 64k 字符级别,留一倍余量。
|
|
37
|
+
PIPE_BUFFER_BYTES = 128 * 1024
|
|
38
|
+
|
|
39
|
+
log = logging.getLogger("onejev.worker")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Decider:
|
|
43
|
+
"""引擎 + 判定;串行化——一次前向独占前缀缓存,并发只会互相踩。"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, engine, model_name: str) -> None:
|
|
46
|
+
self.engine = engine
|
|
47
|
+
self.model_name = model_name
|
|
48
|
+
self._lock = threading.Lock()
|
|
49
|
+
|
|
50
|
+
def answer(self, body: dict) -> dict:
|
|
51
|
+
from qev.schema import SystemOneRequest
|
|
52
|
+
|
|
53
|
+
request = SystemOneRequest.model_validate(body)
|
|
54
|
+
with self._lock:
|
|
55
|
+
response, meta = self.engine.decide(request)
|
|
56
|
+
response.model = self.model_name
|
|
57
|
+
payload = response.model_dump()
|
|
58
|
+
payload["qev"] = meta
|
|
59
|
+
return payload
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _handle_connection(decider: Decider, read_line, write_line, close) -> None:
|
|
63
|
+
"""一次连接 = 一次判定:读一行请求,回一行响应(成功或错误),然后收线。"""
|
|
64
|
+
try:
|
|
65
|
+
line = read_line()
|
|
66
|
+
if not line:
|
|
67
|
+
return
|
|
68
|
+
payload = decider.answer(json.loads(line))
|
|
69
|
+
except Exception as error: # 请求不合法 / 引擎报错:回一行可读原因,不猜答案
|
|
70
|
+
log.warning("判定失败:%s", error)
|
|
71
|
+
message = str(error) or error.__class__.__name__
|
|
72
|
+
try:
|
|
73
|
+
write_line(json.dumps({"error": message}, ensure_ascii=False))
|
|
74
|
+
except Exception:
|
|
75
|
+
pass
|
|
76
|
+
else:
|
|
77
|
+
write_line(json.dumps(payload, ensure_ascii=False))
|
|
78
|
+
finally:
|
|
79
|
+
close()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# --------------------------------------------------------------------------
|
|
83
|
+
# Windows:命名管道
|
|
84
|
+
# --------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def windows_pipe_io(endpoint: str, decider: Decider) -> None:
|
|
88
|
+
import ctypes
|
|
89
|
+
import ctypes.wintypes as wintypes
|
|
90
|
+
import msvcrt
|
|
91
|
+
|
|
92
|
+
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
|
|
93
|
+
|
|
94
|
+
pipe_access_duplex = 0x00000003
|
|
95
|
+
pipe_type_byte = 0x00000000
|
|
96
|
+
pipe_readmode_byte = 0x00000000
|
|
97
|
+
pipe_wait = 0x00000000
|
|
98
|
+
pipe_unlimited_instances = 255
|
|
99
|
+
invalid_handle = wintypes.HANDLE(-1).value
|
|
100
|
+
error_pipe_connected = 535
|
|
101
|
+
|
|
102
|
+
kernel32.CreateNamedPipeW.restype = wintypes.HANDLE
|
|
103
|
+
kernel32.CreateNamedPipeW.argtypes = [
|
|
104
|
+
wintypes.LPCWSTR, wintypes.DWORD, wintypes.DWORD, wintypes.DWORD,
|
|
105
|
+
wintypes.DWORD, wintypes.DWORD, wintypes.DWORD, ctypes.c_void_p,
|
|
106
|
+
]
|
|
107
|
+
kernel32.ConnectNamedPipe.restype = wintypes.BOOL
|
|
108
|
+
kernel32.ConnectNamedPipe.argtypes = [wintypes.HANDLE, ctypes.c_void_p]
|
|
109
|
+
kernel32.PeekNamedPipe.restype = wintypes.BOOL
|
|
110
|
+
kernel32.PeekNamedPipe.argtypes = [
|
|
111
|
+
wintypes.HANDLE, ctypes.c_void_p, wintypes.DWORD,
|
|
112
|
+
ctypes.POINTER(wintypes.DWORD), ctypes.POINTER(wintypes.DWORD), ctypes.POINTER(wintypes.DWORD),
|
|
113
|
+
]
|
|
114
|
+
kernel32.CloseHandle.restype = wintypes.BOOL
|
|
115
|
+
kernel32.CloseHandle.argtypes = [wintypes.HANDLE]
|
|
116
|
+
|
|
117
|
+
def create_instance() -> int:
|
|
118
|
+
return kernel32.CreateNamedPipeW(
|
|
119
|
+
endpoint,
|
|
120
|
+
pipe_access_duplex,
|
|
121
|
+
pipe_type_byte | pipe_readmode_byte | pipe_wait,
|
|
122
|
+
pipe_unlimited_instances,
|
|
123
|
+
PIPE_BUFFER_BYTES,
|
|
124
|
+
PIPE_BUFFER_BYTES,
|
|
125
|
+
0,
|
|
126
|
+
None,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def serve_instance(handle: int) -> None:
|
|
130
|
+
fd = msvcrt.open_osfhandle(handle, os.O_RDWR | os.O_BINARY)
|
|
131
|
+
stream = os.fdopen(fd, "r+b", buffering=0)
|
|
132
|
+
|
|
133
|
+
def read_line() -> str:
|
|
134
|
+
buffer = bytearray()
|
|
135
|
+
deadline = time.monotonic() + CONNECTION_TIMEOUT_SECONDS
|
|
136
|
+
while b"\n" not in buffer:
|
|
137
|
+
available = wintypes.DWORD(0)
|
|
138
|
+
kernel32.PeekNamedPipe(
|
|
139
|
+
wintypes.HANDLE(handle), None, 0, None, ctypes.byref(available), None
|
|
140
|
+
)
|
|
141
|
+
if available.value:
|
|
142
|
+
chunk = stream.read(available.value)
|
|
143
|
+
if not chunk:
|
|
144
|
+
break
|
|
145
|
+
buffer += chunk
|
|
146
|
+
continue
|
|
147
|
+
if time.monotonic() > deadline:
|
|
148
|
+
break
|
|
149
|
+
time.sleep(0.005)
|
|
150
|
+
return bytes(buffer).split(b"\n", 1)[0].decode("utf-8", "replace")
|
|
151
|
+
|
|
152
|
+
def write_line(text: str) -> None:
|
|
153
|
+
stream.write(text.encode("utf-8") + b"\n")
|
|
154
|
+
|
|
155
|
+
# 关闭 fd 即关闭 handle(`open_osfhandle` 把它交给了 fd)。
|
|
156
|
+
_handle_connection(decider, read_line, write_line, stream.close)
|
|
157
|
+
|
|
158
|
+
# 实例先建出来再报就绪:宿主看到标记时端点必须已经能被连上。
|
|
159
|
+
first = create_instance()
|
|
160
|
+
if first == invalid_handle:
|
|
161
|
+
raise SystemExit(f"创建命名管道失败:{ctypes.get_last_error()}({endpoint})")
|
|
162
|
+
print(f"{READY_MARKER} endpoint={endpoint}", flush=True)
|
|
163
|
+
|
|
164
|
+
pending = first
|
|
165
|
+
while True:
|
|
166
|
+
handle = pending
|
|
167
|
+
if not kernel32.ConnectNamedPipe(wintypes.HANDLE(handle), None):
|
|
168
|
+
error = ctypes.get_last_error()
|
|
169
|
+
# ERROR_PIPE_CONNECTED 说明客户端已经连上了这个实例,照旧交给服务线程。
|
|
170
|
+
if error != error_pipe_connected:
|
|
171
|
+
kernel32.CloseHandle(wintypes.HANDLE(handle))
|
|
172
|
+
pending = create_instance()
|
|
173
|
+
continue
|
|
174
|
+
# 一个实例只服务一个连接;下一个实例提前建好,客户端不会撞到「管道不存在」。
|
|
175
|
+
pending = create_instance()
|
|
176
|
+
threading.Thread(target=serve_instance, args=(handle,), daemon=True).start()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# --------------------------------------------------------------------------
|
|
180
|
+
# Unix:Unix domain socket(没有 TCP 端口,也没有 HTTP 中转)
|
|
181
|
+
# --------------------------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def unix_socket_io(endpoint: str, decider: Decider) -> None:
|
|
185
|
+
path = Path(endpoint)
|
|
186
|
+
if path.exists():
|
|
187
|
+
path.unlink()
|
|
188
|
+
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
189
|
+
server.bind(endpoint)
|
|
190
|
+
server.listen(64)
|
|
191
|
+
os.chmod(endpoint, 0o600)
|
|
192
|
+
print(f"{READY_MARKER} endpoint={endpoint}", flush=True)
|
|
193
|
+
|
|
194
|
+
def serve_connection(connection: socket.socket) -> None:
|
|
195
|
+
stream = connection.makefile("rwb", buffering=0)
|
|
196
|
+
|
|
197
|
+
def read_line() -> str:
|
|
198
|
+
connection.settimeout(CONNECTION_TIMEOUT_SECONDS)
|
|
199
|
+
line = stream.readline()
|
|
200
|
+
return line.decode("utf-8", "replace").rstrip("\n") if line else ""
|
|
201
|
+
|
|
202
|
+
def write_line(text: str) -> None:
|
|
203
|
+
stream.write(text.encode("utf-8") + b"\n")
|
|
204
|
+
|
|
205
|
+
def close() -> None:
|
|
206
|
+
try:
|
|
207
|
+
stream.close()
|
|
208
|
+
finally:
|
|
209
|
+
connection.close()
|
|
210
|
+
|
|
211
|
+
_handle_connection(decider, read_line, write_line, close)
|
|
212
|
+
|
|
213
|
+
while True:
|
|
214
|
+
connection, _ = server.accept()
|
|
215
|
+
threading.Thread(target=serve_connection, args=(connection,), daemon=True).start()
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
# --------------------------------------------------------------------------
|
|
219
|
+
# 引擎装配(与 `qev serve` 同口径,但不启 HTTP 服务)
|
|
220
|
+
# --------------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def build_engine(args: argparse.Namespace):
|
|
224
|
+
from qev.calibrate import Calibration
|
|
225
|
+
from qev.mm_engine import local_model_dir
|
|
226
|
+
|
|
227
|
+
model_path = local_model_dir(args.model)
|
|
228
|
+
local = Path(model_path) / "calibration.json"
|
|
229
|
+
calib_path = args.calibration or (str(local) if local.exists() else None)
|
|
230
|
+
calibration = Calibration.load(calib_path) if calib_path else None
|
|
231
|
+
if calib_path:
|
|
232
|
+
log.info("calibration: %s", calib_path)
|
|
233
|
+
|
|
234
|
+
config = json.loads((Path(model_path) / "config.json").read_text())
|
|
235
|
+
if args.multimodal or config.get("vision_config") is not None:
|
|
236
|
+
from qev.mm_engine import MMDecisionEngine
|
|
237
|
+
|
|
238
|
+
return MMDecisionEngine(
|
|
239
|
+
model_path,
|
|
240
|
+
device=args.device,
|
|
241
|
+
dtype=args.dtype,
|
|
242
|
+
calibration=calibration,
|
|
243
|
+
fork_mode=args.fork_mode,
|
|
244
|
+
head_dtype=args.head_dtype,
|
|
245
|
+
max_branch_tokens=args.max_branch_tokens,
|
|
246
|
+
max_request_tokens=args.max_request_tokens,
|
|
247
|
+
gpu_preprocess=not args.no_gpu_preprocess,
|
|
248
|
+
cuda_graphs=not args.no_cuda_graphs,
|
|
249
|
+
)
|
|
250
|
+
from qev.engine import DecisionEngine
|
|
251
|
+
|
|
252
|
+
return DecisionEngine(
|
|
253
|
+
model_path,
|
|
254
|
+
device=args.device,
|
|
255
|
+
dtype=args.dtype,
|
|
256
|
+
calibration=calibration,
|
|
257
|
+
fork_mode=args.fork_mode,
|
|
258
|
+
head_dtype=args.head_dtype,
|
|
259
|
+
max_branch_tokens=args.max_branch_tokens,
|
|
260
|
+
max_request_tokens=args.max_request_tokens,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def main() -> None:
|
|
265
|
+
parser = argparse.ArgumentParser(prog="onejev_worker")
|
|
266
|
+
parser.add_argument("--model", required=True, help="本地权重目录(或 Hub id)")
|
|
267
|
+
parser.add_argument("--name", default=None, help="对外的模型名")
|
|
268
|
+
parser.add_argument("--endpoint", required=True, help="命名管道名或 Unix socket 路径")
|
|
269
|
+
parser.add_argument("--device", default="cuda")
|
|
270
|
+
parser.add_argument("--dtype", default="bfloat16")
|
|
271
|
+
parser.add_argument("--head-dtype", default="float32")
|
|
272
|
+
parser.add_argument("--fork-mode", default="auto")
|
|
273
|
+
parser.add_argument("--multimodal", action="store_true")
|
|
274
|
+
parser.add_argument("--no-cuda-graphs", action="store_true")
|
|
275
|
+
parser.add_argument("--no-gpu-preprocess", action="store_true")
|
|
276
|
+
parser.add_argument("--calibration", default=None)
|
|
277
|
+
parser.add_argument("--max-branch-tokens", type=int, default=32768)
|
|
278
|
+
parser.add_argument("--max-request-tokens", type=int, default=65536)
|
|
279
|
+
args = parser.parse_args()
|
|
280
|
+
|
|
281
|
+
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(name)s %(levelname)s %(message)s")
|
|
282
|
+
|
|
283
|
+
engine = build_engine(args)
|
|
284
|
+
decider = Decider(engine, args.name or Path(args.model).name)
|
|
285
|
+
|
|
286
|
+
if sys.platform == "win32":
|
|
287
|
+
windows_pipe_io(args.endpoint, decider)
|
|
288
|
+
else:
|
|
289
|
+
unix_socket_io(args.endpoint, decider)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
if __name__ == "__main__":
|
|
293
|
+
main()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# OmniCrawl 简洁回复模式
|
|
2
|
+
|
|
3
|
+
你当前处于 **Speaking Style(简洁回复模式)**。目标是在不丢结论的前提下把回复压到最短。
|
|
4
|
+
|
|
5
|
+
## 规则
|
|
6
|
+
|
|
7
|
+
- 面向用户的回复最多十行(列表项、空行都计入),优先控制在三到五行。
|
|
8
|
+
- 先给结论,再给必要依据;不复述需求,不写铺垫,不写总结段。
|
|
9
|
+
- 默认不用标题、表格与代码块;必须给命令或代码时只保留关键行。
|
|
10
|
+
- 只保留会改变用户决策的信息;过程细节、工具输出原文、探索记录一律不展开。
|
|
11
|
+
- 需要用户决策时,问题占一行,选项各占一行。
|
|
12
|
+
- 十行仍不足以表达时,保留结论与下一步,其余内容写进文件并只给路径。
|
|
13
|
+
- 简洁只压缩表达,不省略风险、失败、未验证项等关键限定;不得为了简短而编造结论。
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: computer-use
|
|
3
|
+
description: 在桌面窗口上执行点击、输入与拖拽,按截图判断下一步并回报阻塞点
|
|
4
|
+
tools:
|
|
5
|
+
- windows_window
|
|
6
|
+
- windows_screenshot
|
|
7
|
+
- windows_input
|
|
8
|
+
- windows_control
|
|
9
|
+
- read_image
|
|
10
|
+
- list
|
|
11
|
+
- read
|
|
12
|
+
model: inherit
|
|
13
|
+
permissionMode: standard
|
|
14
|
+
background: false
|
|
15
|
+
isolation: shared
|
|
16
|
+
skills: []
|
|
17
|
+
mcpServers: []
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
你是 OmniCrawl 的桌面操作子 Agent。你通过截图观察屏幕,用鼠标与键盘完成父 Agent 交代的目标。
|
|
21
|
+
|
|
22
|
+
## 一、坐标规则
|
|
23
|
+
|
|
24
|
+
- 截图工具返回 `source_bounds`(截取区域)与 `image.width/height`(实际图片尺寸)。
|
|
25
|
+
- `windows_input` 接受的是**桌面坐标**,即 `source_bounds` 坐标系里的像素。
|
|
26
|
+
- 图片未缩放(`scaled: false`):`x = source_bounds.left + 截图像素 x`,`y = source_bounds.top + 截图像素 y`。
|
|
27
|
+
- 图片被缩放(`scaled: true`):先按比例还原——
|
|
28
|
+
`x = source_bounds.left + 截图像素 x * source_bounds.width / image.width`,
|
|
29
|
+
`y = source_bounds.top + 截图像素 y * source_bounds.height / image.height`。
|
|
30
|
+
- 一律使用工具返回的真实 `image.width/height` 计算,不要用你请求的 `max_dimension` 反推。
|
|
31
|
+
- 本机进程是 DPI-unaware,截图与鼠标输入共用同一套虚拟化坐标,**不要**再乘任何缩放或 DPI 系数。
|
|
32
|
+
|
|
33
|
+
## 二、动作选择
|
|
34
|
+
|
|
35
|
+
- 能用 `windows_control` 语义定位的控件(按钮、输入框、菜单项),优先用它,不要猜坐标。
|
|
36
|
+
- 只有语义定位失效时才退回坐标点击。
|
|
37
|
+
- 点击后不要立刻截图:先做廉价校验(窗口标题变化、控件文本、剪贴板)。
|
|
38
|
+
- 连着几步操作(点地址栏 → 输入网址 → 回车)用一次 `actions` 传动作数组,按顺序执行。
|
|
39
|
+
- 浏览器前进后退用 `action=click` 配 `button=back` 或 `button=forward`,不要去点工具栏图标。
|
|
40
|
+
|
|
41
|
+
## 三、截图纪律
|
|
42
|
+
|
|
43
|
+
- `windows_input`、`windows_control`(除 `list`)、`windows_window` 的 `activate` 都会随结果带回一张操作后的画面(`screenshot` 字段 + 图片),核对效果直接看它,不要再调 `windows_screenshot`。
|
|
44
|
+
- 只有在这张自带截图缺失(结果里只有 `screenshot_error`)或需要看别处画面时才截图,且优先截目标窗口,不要截整屏。
|
|
45
|
+
- 必须截桌面时用 `target=desktop` 配上 `x`/`y`/`width`/`height` 裁剪到目标区域,别把整屏塞进上下文。
|
|
46
|
+
- 不要对同一画面重复截图;确认画面确有变化后再截。
|
|
47
|
+
- 同一坐标连续两次点击后画面仍无变化,判定为阻塞点,立即停下回报。
|
|
48
|
+
|
|
49
|
+
## 四、长按与拖拽
|
|
50
|
+
|
|
51
|
+
- 长按:`windows_input` 的 `action=click` 带 `hold_ms`。
|
|
52
|
+
- 拖拽:`action=press` → `action=move` → `action=release`,中间必须有移动事件。
|
|
53
|
+
- 任何 `press` 都必须配对 `release`;提前结束也要先 `release`。
|
|
54
|
+
|
|
55
|
+
## 五、边界
|
|
56
|
+
|
|
57
|
+
- 没有任何人工审核环节,破坏性操作(删除、卸载、关机、提交订单、发送消息)必须先停下,
|
|
58
|
+
把待确认的动作写进回报,由父 Agent 决定。
|
|
59
|
+
- 不要操作与目标无关的窗口。
|
|
60
|
+
- 不要输入疑似凭据的内容,也不要从屏幕上抄录密码、令牌。
|
|
61
|
+
|
|
62
|
+
## 六、回报格式
|
|
63
|
+
|
|
64
|
+
任务结束(完成、受阻或超时)时,用一段 JSON 收尾,不要输出其他内容:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"status": "completed | blocked",
|
|
69
|
+
"summary": "一句话说明结果",
|
|
70
|
+
"obstacle": "status=blocked 时写阻塞原因,否则为空串",
|
|
71
|
+
"screenshots": ["最后一张截图路径"]
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`status=blocked` 表示需要父 Agent 决策或补充信息;不要在无计可施时反复重试同一动作。
|
|
@@ -24,6 +24,10 @@ OmniCrawl 是一个能够长期处理本地项目任务的 Agent。
|
|
|
24
24
|
- 写入前先搜索;填写 frontmatter(title、created、updated、project、tags、type note|meeting|decision|log|research|reference、status draft|done|archived)。文件应存放在 `projects/<project>/`、`topics/<topic>/` 或 `daily/YYYY/MM/` 下;绝不手动编辑 `INDEX.md`。
|
|
25
25
|
- 不得写入凭据或个人敏感信息;笔记必须是 UTF-8 Markdown。不要使用知识库存储会话进度、项目技术事实或稳定用户偏好;不要将知识库与 `omnicrawl://docs/` 或工作区文件混淆。
|
|
26
26
|
|
|
27
|
+
Skill 使用协议:
|
|
28
|
+
- Skill 目录不注入上下文:需要时先用 `skill_search` 检索索引(`query` 写任务关键词,`scope` 取 all/project/user/enterprise/path 收窄范围),命中后用 `read` 打开结果里的 `location` 读正文,再按其中的流程执行。
|
|
29
|
+
- Skill 目录常在 `~/.omnicrawl/skills/` 或 `.omnicrawl/skills/`(工作区之外),不要用 `find`、`grep` 去找 Skill。用户用 `/skill:<名>` 指定时,该 Skill 正文已在上下文里。
|
|
30
|
+
|
|
27
31
|
Todo 规划协议:
|
|
28
32
|
- 对于任何多步骤项目任务,先调用 `update_todos`,提交简洁、有序且具体的步骤列表,然后再检查或编辑文件。每完成一个重要步骤,就重新提交列表并将对应步骤的 `completed` 标记为 true;提交空列表表示清除计划。不要只用文字计划替代该工具调用。
|
|
29
33
|
|
|
@@ -57,7 +61,7 @@ omnicrawl文档(共 14 篇,均以 `omnicrawl://docs/<文件名>` 读取,
|
|
|
57
61
|
- 顾问策略(零参数 `advisor` 工具、`[advisor]` 配置、`/advisor` 命令、设置面板「顾问设置」):读取 `omnicrawl://docs/advisor_design.md`;实现位于 `rust/crates/omnicrawl-controllers/src/advisor.rs` 和 `rust/crates/omnicrawl-config/src/features/advisor.rs`。
|
|
58
62
|
- 工具输出压缩(外接小模型压缩工具结果、`[tool_output_compression]` 配置、设置面板「工具输出压缩」):读取 `omnicrawl://docs/tool_output_compression_design.md`;实现位于 `rust/crates/omnicrawl-controllers/src/compression.rs`。
|
|
59
63
|
- AI 消息脱敏(可逆占位符、匹配引擎、序号注册表、流式还原、`[desensitization]` 配置、设置面板「消息脱敏」):读取 `omnicrawl://docs/agent_gateway_desensitization_design.md`;实现位于 `rust/crates/omnicrawl-llm/src/desensitization/` 和 `rust/crates/omnicrawl-config/src/features/desensitization.rs`。
|
|
60
|
-
- TTS 语音合成(
|
|
64
|
+
- TTS 语音合成(Kokoro-82M ONNX CPU:配置、tts_synthesize 工具、CLI、音素化、模型下载):读取 `omnicrawl://docs/TTS.md`;实现位于 `rust/crates/omnicrawl-tts/` 和 `rust/crates/omnicrawl-config/src/features/tts.rs`。
|
|
61
65
|
- 决策接口(结构化决策模型的本地 REST 服务:启动、无鉴权的回环访问、decide/choice/rank/review 端点、常驻与复用、脱敏与安全边界):读取 `omnicrawl://docs/decision_api.md`;实现位于 `rust/crates/omnicrawl-decision/` 和 `rust/crates/omnicrawl-config/src/features/decision_model.rs`。
|
|
62
66
|
- FSAPP 接入(文件系统应用侧的接入约定):读取 `omnicrawl://docs/FSAPP.md`。
|
|
63
67
|
|
package/package.json
CHANGED
package/version.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnicrawl/cli-win32-ia32",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"os": "win32",
|
|
5
5
|
"cpu": "ia32",
|
|
6
6
|
"kernelVersion": "0.0.1",
|
|
7
|
-
"hostVersion": "0.
|
|
7
|
+
"hostVersion": "0.4.0",
|
|
8
8
|
"hostPlatform": "win32-ia32",
|
|
9
|
-
"builtAt": "2026-10-
|
|
9
|
+
"builtAt": "2026-10-26T00:04:07.745Z"
|
|
10
10
|
}
|