标签 配置文件 下的文章

已经在虚拟机部署好Apache DolphinScheduler了,想尝试下在Flink新建一个Flink节点,然后用Flink消费Kafka数据。

Apache DolphinScheduler用的是单机部署,具体操作可以参考官方文档:DolphinScheduler | 文档中心(https://dolphinscheduler.apache.org/zh-cn/docs/3.3.2/guide/in...).

  • 前置条件:已经安装Java 11、DolphinScheduler 3.3.2、Flink 1.18.1、Kafka 3.6.0,Zookeeper用Kafka内置的。建议这些安装都下载二进制的安装包到虚拟机安装,用命令安装的不可控,我下载的二进制包如下:

配置好Flink的环境变量

1、编辑环境变量:

sudo vim ~/.bashrc

增加Flink的路径

2、使环境变量生效:

#使环境变量生效
source ~/.bashrc
#查看环境变量
echo $Flink_HOME

修改Kafka、Flink以及DolphinScheduler的配置文件

因为用的是虚拟机,为了让外面的主机能够访问到虚拟机的网络,需要修改下配置文件

  1. 修改Kafka配置:找到Kafka安装包下的config文件夹,修改config下的server.properties文件,修改listeners是为了外面的主机能够访问到虚拟机的Kafka,还有把advertised.listeners改成虚拟机地址,写样例的时候能连上虚拟机的Kafka地址,不然默认连localhost
broker.id=0
listeners=PLAINTEXT://0.0.0.0:9092
#192.168.146.132修改成虚拟机ip
advertised.listeners=PLAINTEXT://192.168.146.132:9092

  1. 修改Flink配置:找到Flink安装包下的conf文件夹,修改conf下的Flink-conf.yaml文件,把里面所有的localhost地址全部改成0.0.0.0,以便主机能访问到虚拟机的Flink。还有增加jobmanager和taskmanager的内存
jobmanager.rpc.address: 0.0.0.0
jobmanager.bind-host: 0.0.0.0
jobmanager.cpu.cores: 1
jobmanager.memory.process.size: 1600m
taskmanager.bind-host: 0.0.0.0
taskmanager.host: 0.0.0.0
taskmanager.memory.process.size: 2048m
taskmanager.cpu.cores: 1

  1. 修改Apache DolphinScheduler的配置文件,从Apache DolphinScheduler的启动脚本文件dolphinscheduler-daemon.sh可以看出,配置环境变量用的是bin/env文件夹下的dolphinscheduler_env.sh

查看dolphinscheduler-daemon.sh文件:

修改dolphinscheduler_env.sh文件,新增JAVA、Flink路径:

#修改成自己的JAVA、Flink路径
export JAVA_HOME=/data/jdk-11.0.29
export Flink_HOME=/data/Flink-1.18.1

关闭防火墙,启动应用

启动应用,包括Zookeeper、Kafka、Flink以及Apache DolphinScheduler。

#关闭防火墙
sudo systemctl stop firewalld
 
# 在 Flink 根目录下,执行以下命令启动 Flink 集群
bin/start-cluster.sh
 
# 启动 ZooKeeper
bin/zookeeper-server-start.sh config/zookeeper.properties &
 
# 启动 Kafka 服务器
bin/Kafka-server-start.sh config/server.properties &
 
#创建 Kafka 主题
bin/Kafka-topics.sh --create --topic test --bootstrap-server localhost:9092 --partitions 1 --replication-factor 1
 
#使用命令行生产者发送消息
bin/Kafka-console-producer.sh --topic test --bootstrap-server localhost:9092
 
#消费
bin/Kafka-console-consumer.sh --topic test --from-beginning --bootstrap-server localhost:9092

# 启动 Standalone Server 服务
bash ./bin/dolphinscheduler-daemon.sh start standalone-server

测试

测试Flink、Apache DolphinScheduler是否能访问成功。

  1. Flink访问地址:http://localhost:8081/,localhost改成自己虚拟机地址

  1. Apache DolphinScheduler访问地址:http://localhost:12345/dolphinscheduler/ui ,localhost改成自己虚拟机地址即可登录系统 UI。默认的用户名和密码是 admin/dolphinscheduler123

编写样例

用Flink消费Kafka数据,然后打包上传到Apache DolphinScheduler,启动Flink任务:

  1. 编写样例:

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.example</groupId>
    <artifactId>Flink-Kafka-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <Flink.version>1.18.1</Flink.version>
        <scala.binary.version>2.12</scala.binary.version>
        <Kafka.version>3.6.0</Kafka.version>
    </properties>
 
    <dependencies>
        <!-- Flink核心依赖 -->
        <dependency>
            <groupId>org.apache.Flink</groupId>
            <artifactId>Flink-java</artifactId>
            <version>${Flink.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.Flink</groupId>
            <artifactId>Flink-streaming-java</artifactId>
            <version>${Flink.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.Flink</groupId>
            <artifactId>Flink-clients</artifactId>
            <version>${Flink.version}</version>
        </dependency>
 
        <!-- 连接器基础依赖 -->
        <dependency>
            <groupId>org.apache.Flink</groupId>
            <artifactId>Flink-connector-base</artifactId>
            <version>${Flink.version}</version>
        </dependency>
 
        <!-- Kafka连接器(关键修改点) -->
        <dependency>
            <groupId>org.apache.Flink</groupId>
            <artifactId>Flink-connector-Kafka</artifactId>
            <version>3.1.0-1.18</version>
        </dependency>
        <dependency>
            <groupId>org.apache.Kafka</groupId>
            <artifactId>Kafka-clients</artifactId>
            <version>${Kafka.version}</version>
        </dependency>
 
        <!-- 日志依赖 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.36</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
 
    <repositories>
        <repository>
            <id>aliyun</id>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>apache-releases</id>
            <url>https://repository.apache.org/content/repositories/releases/</url>
        </repository>
    </repositories>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <artifactSet>
                                <excludes>
                                    <exclude>org.apache.Flink:force-shading</exclude>
                                    <exclude>com.google.code.findbugs:jsr305</exclude>
                                    <exclude>org.slf4j:*</exclude>
                                </excludes>
                            </artifactSet>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*.RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

FlinkKafkaConsumerExample.java

import org.apache.Flink.api.common.functions.FlatMapFunction;
import org.apache.Flink.api.java.tuple.Tuple2;
import org.apache.Flink.api.java.utils.ParameterTool;
import org.apache.Flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.Flink.streaming.api.datastream.DataStream;
import org.apache.Flink.streaming.api.functions.ProcessFunction;
import org.apache.Flink.streaming.api.functions.sink.RichSinkFunction;
import org.apache.Flink.util.Collector;
import org.apache.Flink.streaming.connectors.Kafka.FlinkKafkaConsumer;
import org.apache.Flink.api.common.serialization.SimpleStringSchema;
import org.apache.Kafka.clients.consumer.ConsumerConfig;
import org.apache.Kafka.common.serialization.StringDeserializer;
 
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
 
 
public class FlinkKafkaConsumerExample {
    private static volatile int messageCount = 0;
    private static volatile boolean shouldStop = false;
    public static void main(String[] args) throws Exception {
        // 设置执行环境
        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
 
        // Kafka 配置
        Properties properties = new Properties();
        properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.146.132:9092"); // Kafka broker 地址
        properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, "test-group"); // 消费者组
        properties.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        properties.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
 
        // 创建 Kafka 消费者
        FlinkKafkaConsumer<String> KafkaConsumer = new FlinkKafkaConsumer<>("test", new SimpleStringSchema(), properties);
        KafkaConsumer.setStartFromEarliest(); // 从最早的消息开始消费
        DataStream<String> stream = env.addSource(KafkaConsumer);
 
        // 处理数据:分词和计数
        DataStream<Tuple2<String, Integer>> counts = stream
                .flatMap(new Tokenizer())
                .keyBy(value -> value.f0)
                .sum(1);
 
 
        counts.addSink(new RichSinkFunction<Tuple2<String, Integer>>() {
            @Override
            public void invoke(Tuple2<String, Integer> value, Context context) {
                System.out.println(value);
                messageCount++;
 
                // 检查是否达到停止条件
                if (messageCount >= 2 && !shouldStop) {
                    System.out.println("Processed 2 messages, stopping job.");
                    shouldStop = true; // 设置标志位,表示应该停止
                }
            }
        });
 
        // 执行作业并获取 JobClient
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            try {
                // 启动作业并获取 JobClient
                org.apache.Flink.core.execution.JobClient jobClient = env.executeAsync("Flink Kafka WordCount");
                System.out.println("Job ID: " + jobClient.getJobID());
 
                // 监测条件并取消作业
                while (!shouldStop) {
                    Thread.sleep(100); // 每100毫秒检查一次
                }
 
                // 达到停止条件时取消作业
                if (shouldStop) {
                    System.out.println("Cancelling the job...");
                    jobClient.cancel().get(); // 取消作业
                }
 
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
 
        // 在主线程中等待作业结束
        future.join(); // 等待作业完成
    }
 
    // Tokenizer 类用于将输入字符串转化为单词
    public static final class Tokenizer implements FlatMapFunction<String, Tuple2<String, Integer>> {
        @Override
        public void flatMap(String value, Collector<Tuple2<String, Integer>> out) {
            String[] tokens = value.toLowerCase().split("\\W+");
            for (String token : tokens) {
                if (token.length() > 0) {
                    out.collect(new Tuple2<>(token, 1));
                }
            }
        }
    }
 
}
  1. 打包上传到Apache DolphinScheduler

  1. 新建Flink节点,并启动

在Apache DolphinScheduler的任务实例看启动日志:

在虚拟机启动生产者,输出字符串,然后可以在Flink查看输出Kafka生产的消息:

原文链接:https://blog.csdn.net/Analyze_ing/article/details/156940553

第一次看到这种类型的应用,没有网站。我根据邮件提示创建了一个属于自己的邮箱。[email protected].

  1. 只要向目标地址发送一封邮件,就可以每天获得 200M 的流量。
  2. 如果你发到我的这个邮箱,我就成为你的引荐人,referrer 。
  3. 如果你付费订阅,我可以获取 5%的充值款。

特点在于配置文件作为邮件的附件,几乎不会被切断联系。除非屏蔽邮箱地址。

我大体研究了一下,好像没有中心节点。也就是说攻击单个节点不会对其它节点造成影响。

Surge iOS/Mac 配置又双叒叕更新辽一下,更适合中国宝宝食用的 Surge 配置

配置预览

GitHUB:GitHub - curtinp118/Surge5: 专为 Surge Pro 用户打造的配置文件:集成智能地区分流、多订阅自动聚合、隐私保护及主流应用策略优化,安全稳定且易于定制。

主要特性

  • 极致分流

    • 多地区策略:预设香港、美国、日本、台湾、韩国、新加坡等地区优选策略组 (smart 模式)。
    • 应用级优化:针对 Netflix, YouTube, Spotify, Disney+, Telegram, Google, Apple 等常用服务独立分流。
    • 自动测速与故障转移:核心策略组采用自动测速,确保始终连接最快节点。
  • 多订阅聚合

    • 预设 🔰 Sub-01🔰 Sub-04 四个标准订阅。
    • 通过 HUB 策略组自动聚合所有订阅节点,无需手动管理。
  • 隐私与去广告

    • 集成主流去广告规则集 (Adblock4limbo 等)。
    • 内置隐私保护规则,屏蔽跟踪器与恶意网站。
    • DNS 防泄漏与 DoH (DNS over HTTPS) 支持。
  • 安全策略适度
    仅对 Google 域名启用 HTTPS 解密,避免不必要的风险和性能开销。

如何使用

1. 下载配置

  1. 配置文件长按复制,打开 Surge5,点击 - 导入 - 从 URL 下载配置。

2. 填入订阅信息

使用文本编辑器打开配置文件,定位到 [Proxy Group] 区域:

[Proxy Group]
...
🔰 Sub-01 = select, policy-path=http://example.com/api/v1/client/subscribe?token=YOUR_TOKEN&flag, ...
🔰 Sub-02 = select, policy-path=https://example.com/api/v1/client/subscribe?token=YOUR_TOKEN, ...
  • YOUR_TOKEN 替换为你的机场订阅 Token。
  • 或者直接替换整段 policy-path 链接。

3. 配置安全认证 (可选但推荐)

定位到 [General] 区域,修改远程控制密码:

[General] external-controller-access = YOUR_PASSWORD@127.0.0.1:6170 http-api = YOUR_PASSWORD@0.0.0.0:6171 

YOUR_PASSWORD 替换为你自己的强密码。

4. 启用 MITM 功能

为了实现 HTTPS 解密(用于去广告、URL 重写等高级功能),你需要配置 CA 证书。

方法 A:生成新证书(推荐新手)

  1. 导入配置到 Surge。
  2. 进入 Surge 设置 → MitM → 配置根证书。
  3. 点击 “生成新的 CA 证书”,并按照提示安装并信任证书。

方法 B:填入已有证书
如果你已有 P12 证书,替换 [MITM] 区域的占位符:

[MITM] ca-passphrase = YOUR_PASSPHRASE
ca-p12 = YOUR_P12_BASE64_DATA

策略组说明

核心策略组

策略组名称类型说明
ProxySelect总出口,所有未命中特定规则的流量默认走此策略。支持选择地区优选组或特定订阅。
HUBSelect节点聚合中心 (隐藏),自动聚合所有 Sub-XX 订阅节点,供地区优选组调用。
Sub-01 ~ 04Select订阅源,预留的 4 个订阅槽位,用于填入不同机场的订阅链接。
手动节点Select手动筛选,从所有订阅中筛选出的特定节点(如 CN2, IEPL 等),供手动指定使用。

地区智能优选 (Smart)

这些策略组会自动从所有节点中筛选对应地区的节点,并选择延迟最低的节点使用。

策略组名称筛选关键词说明
香港优选HK, Hong, 港自动选择最佳香港节点
美国优选US, States, 美自动选择最佳美国节点
日本优选JP, Japan, 日自动选择最佳日本节点
台湾优选TW, Tai, 湾自动选择最佳台湾节点
韩国优选KR, Korea, 韩自动选择最佳韩国节点
狮城优选SG, Singapore, 狮自动选择最佳新加坡节点

应用与服务分流

针对特定应用或服务的独立分流策略,确保最佳访问体验。

策略组名称默认策略说明
AppleDIRECT苹果服务(App Store, iCloud 等),默认直连,可切换代理。
GoogleProxyGoogle 搜索及相关服务,默认走代理总出口。
MicrosoftProxy微软服务,默认走代理总出口。
TelegramProxyTelegram 消息与媒体,支持指定地区节点。
TwitterX-FallbackTwitter/X,使用自动故障转移策略。
NetflixProxyNetflix 流媒体,建议手动指定支持解锁的节点。
YouTubeProxyYouTube 视频,默认走代理总出口。
SpotifyProxySpotify 音乐,默认走代理总出口。
BiliBiliDIRECT哔哩哔喱,默认直连(解决地区限制问题可切换)。
PayPalProxyPayPal 支付,安全起见建议固定节点或直连。
GamerProxy游戏平台(Steam, Epic, PS, Xbox 等)。
GlobalMediaProxy其他国际流媒体服务(Disney+, HBO 等)。
AIAI-Fallback人工智能服务(ChatGPT, Gemini 等),使用自动故障转移。
ADsREJECT广告拦截,默认拒绝连接。

故障转移策略 (Fallback)

策略组名称说明
AI-Fallback专为 AI 服务设计,自动检测并剔除不可用节点(如 Oracle 节点)。
X-Fallback专为 Twitter 设计,在美国和新加坡节点间自动切换。

📌 转载信息
原作者:
Null404
转载时间:
2026/1/23 11:59:51

OpenCode 配置指南

配置文件位置

  • 主配置文件: ~/.config/opencode/opencode.json (供应商和模型配置)
  • 认证文件: ~/.local/share/opencode/auth.json (API Key 存储)
  • 全局提示词: ~/.config/opencode/AGENTS.md (全局行为规范)
  • 项目提示词: <项目根目录>/AGENTS.md (项目特定规范)

快速配置步骤

1. 配置供应商

~/.config/opencode/opencode.json 中添加供应商配置:

{ "$schema": "https://opencode.ai/config.json", "provider": { "供应商名称": { "options": { "baseURL": "https://api.供应商域名.com/v1" }, "models": {} } } } 

参数说明:

  • 供应商名称: 自定义供应商名称 (如 claude、openai智谱 aixxx 中转站 `)
  • baseURL: API 服务的基础 URL

2. 配置 API Key

方式一:使用命令行 (推荐)

# 登录或更新某个供应商的 Key
opencode auth login

# 查看当前已配置的所有供应商和 Key 状态
opencode auth list

# 删除指定供应商的 Key
opencode auth logout <供应商名称>

方式二:手动编辑配置文件

~/.local/share/opencode/auth.json 中添加对应供应商的 API Key:

{ "供应商名称": { "type": "api", "key": "你的API密钥" } } 

注意: auth.json 中的供应商名称必须与 opencode.json 中的供应商名称完全一致。

3. 配置模型

在供应商下添加模型配置:

{ "provider": { "供应商名称": { "options": { "baseURL": "https://api.供应商域名.com/v1" }, "models": { "claude-sonnet-4-5-20250929": { "id": "claude-sonnet-4-5-20250929", "name": "Claude Sonnet 4.5", "cost": { "input": 3, "output": 15 }, "limit": { "context": 200000, "output": 64000 }, "reasoning": true, "temperature": true, "tool_call": true, "attachment": true } } } } } 

模型参数说明:

参数类型说明
idstring模型的唯一标识符,用于 API 调用
namestring模型的显示名称
cost.inputnumber输入 token 成本 (每百万 token 美元)
cost.outputnumber输出 token 成本 (每百万 token 美元)
limit.contextnumber最大上下文长度 (token 数)
limit.outputnumber最大输出长度 (token 数)
reasoningboolean是否支持推理模式
temperatureboolean是否支持温度参数调节
tool_callboolean是否支持函数 / 工具调用
attachmentboolean是否支持文件附件

API Key 管理

优先级机制

OpenCode 选择 API Key 的优先级如下:

  1. 环境变量 (最高优先级)

    • 例如: ANTHROPIC_API_KEYOPENAI_API_KEYOPENCODE_<供应商名称>_APIKEY
    • 只要设置了环境变量,优先使用环境变量中的值
  2. 本地配置文件 (备选)

    • 如果没有设置环境变量,才使用 ~/.local/share/opencode/auth.json 中的 Key
    • 此时使用的是该供应商最后一次执行 auth login 时输入的 Key

覆盖机制

  • 对同一个供应商多次执行 opencode auth login后一次登录会覆盖前一次的 Key
  • 环境变量始终优先于配置文件

配置示例

完整配置示例

opencode.json:

{ "$schema": "https://opencode.ai/config.json", "provider": { "供应商1": { "options": { "baseURL": "https://api.供应商1域名.com/v1" }, "models": { "claude-sonnet-4-5-20250929": { "id": "claude-sonnet-4-5-20250929", "name": "Claude Sonnet 4.5", "cost": { "input": 3, "output": 15 }, "limit": { "context": 200000, "output": 64000 }, "reasoning": true, "temperature": true, "tool_call": true, "attachment": true } } }, "供应商2": { "options": { "baseURL": "https://api.供应商2域名.com/v1" }, "models": { "claude-haiku-4-5-20251001": { "id": "claude-haiku-4-5-20251001", "name": "Claude Haiku 4.5", "cost": { "input": 1, "output": 5 }, "limit": { "context": 200000, "output": 64000 }, "reasoning": true, "temperature": true, "tool_call": true, "attachment": true } } } } } 

auth.json:

{ "供应商1": { "type": "api", "key": "你的API密钥1" }, "供应商2": { "type": "api", "key": "你的API密钥2" } } 

安全建议

  1. 文件权限

    • 设置 auth.json 为仅当前用户可读: chmod 600 ~/.local/share/opencode/auth.json
    • 不要将 auth.json 提交到版本控制系统
  2. 环境隔离

    • 开发环境和生产环境使用不同的 API Key
    • 推荐使用环境变量或项目根目录下的 .env 文件管理 Key
    • 确保 .env 已加入 .gitignore
  3. Key 管理

    • 定期轮换 API Key
    • 使用具有适当权限限制的 API Key
    • auth.json 目前为明文存储,请勿随意分享
  4. 备份

    • 建议定期备份配置文件
    • auth.json 备份时注意安全存储

提示词配置

OpenCode 支持通过 AGENTS.md 文件自定义 AI 助手的行为规范和工作流程。

提示词类型

OpenCode 提供两种级别的提示词配置:

  1. 全局提示词 - 对所有项目生效
  2. 项目提示词 - 仅对当前项目生效

提示词文件位置

类型文件路径作用范围
全局提示词~/.config/opencode/AGENTS.md所有项目
项目提示词<项目根目录>/AGENTS.md当前项目

优先级机制

当同时存在全局和项目提示词时:

  • 项目提示词优先级更高,会覆盖全局提示词中的相同配置
  • 两者会合并使用,项目提示词补充项目特定的规范

提示词内容示例

全局提示词 (~/.config/opencode/AGENTS.md):

# OpenCode 全局提示词 ## 核心原则 - 严谨工作态度,保证完美质量标准
- 直接输出代码或方案,禁止客套话
- 回复简洁明了,使用中文

## 代码规范 - 遵循项目现有代码风格
- 优先编写直观的线性逻辑
- 考虑边界条件和极端情况

## Git 工作流 - 仅在明确要求时提交
- 遵循约定式提交消息格式
- 提交前检查 git status 和 git diff

项目提示词 (<项目根目录>/AGENTS.md):

# 项目名称 - 项目特定提示词 ## 项目概述
这是一个 XXX 项目,使用 XXX 技术栈。

## 目录结构 

project/
├── src/ # 源代码
├── tests/ # 测试文件
└── docs/ # 文档

 ## 项目规范 - 使用 TypeScript 严格模式
- 测试覆盖率要求 80% 以上
- 所有 API 必须有 JSDoc 注释

## 工作流程 1. 修改代码前先运行测试
2. 完成后运行 `npm run lint``npm test` 3. 提交信息格式: `<type>(<scope>): <subject>` 

提示词配置建议

  1. 全局提示词

    • 定义通用的代码规范和工作原则
    • 设置统一的命名规范和注释风格
    • 配置 Git 提交规范
  2. 项目提示词

    • 说明项目的技术栈和架构
    • 描述项目特定的目录结构
    • 定义项目特有的工作流程和规范
    • 列出项目常用的命令和工具
  3. 最佳实践

    • 保持提示词简洁明确,避免冗长描述
    • 使用 Markdown 格式,便于阅读和维护
    • 定期更新提示词,与项目演进保持同步
    • 将敏感信息(如密码、密钥)排除在提示词之外

如何创建提示词文件

创建全局提示词:

# 创建配置目录(如果不存在) mkdir -p ~/.config/opencode

# 创建全局提示词文件 touch ~/.config/opencode/AGENTS.md

# 编辑文件
vim ~/.config/opencode/AGENTS.md

创建项目提示词:

# 在项目根目录创建 cd /path/to/项目目录
touch AGENTS.md

# 编辑文件
vim AGENTS.md

# 建议将 AGENTS.md 加入版本控制
git add AGENTS.md
git commit -m "docs: add project-specific agent instructions" 

提示词生效时机

  • 全局提示词: OpenCode 启动时自动加载
  • 项目提示词: 打开项目目录时自动加载
  • 修改后: 需要重启 OpenCode 或重新打开项目才能生效

常见问题

如何添加新的 API 供应商?

  1. opencode.json 中添加供应商配置 (baseURL 和 models)
  2. auth.json 中添加对应的 API Key 或使用 opencode auth login
  3. 重启 OpenCode

如何切换不同的模型?

在 OpenCode 界面中按 Ctrl+P,选择 “Switch Model” 即可切换已配置的模型。

如何验证配置是否正确?

启动 OpenCode 后,尝试发送一条消息。如果配置正确,模型会正常响应。如果出错,检查:

  • 供应商名称是否在两个文件中一致
  • API Key 是否正确
  • baseURL 是否可访问

支持哪些 API 供应商?

OpenCode 支持所有兼容 OpenAI API 格式的供应商,包括:

  • Anthropic Claude
  • OpenAI GPT
  • Azure OpenAI
  • 智谱 AI
  • 各类第三方 API 代理服务

环境变量和配置文件同时存在时使用哪个?

环境变量优先级更高。如果设置了环境变量 (如 ANTHROPIC_API_KEY),OpenCode 会优先使用环境变量中的 Key,忽略 auth.json 中的配置。

更多帮助


📌 转载信息
转载时间:
2026/1/19 18:27:32

在服务器端我按习惯配置了 Zellij 的配置文件。

nano ~/.config/zellij/config.kdl

内容极简

scrollback_editor "/usr/bin/nano"
default_layout "compact"
mouse_mode true
copy_on_select true
pane_frames false
session_serialization false
on_force_close "quit" 

但是每次重新登录就发现被回写了。但是这些配置依然生效。这是什么机制?问 AI 说可能格式不正确。但是格式检查没有任何问题。

zellij setup --check 

📌 转载信息
原作者:
alertsc
转载时间:
2026/1/19 18:06:50

核心原则

OpenManus的工具系统基于「插件化设计」,所有自定义工具需继承框架的BaseTool基类,实现标准化接口,再通过配置文件注册,即可被AI智能体识别和调用。

一、前置准备

  1. 确认目录结构:在OpenManus项目根目录下,建议创建custom_tools目录存放自定义工具(便于管理):

    mkdir custom_tools  # 项目根目录执行
  2. 核心依赖:确保已安装基础依赖(无需额外安装,框架自带工具基类):

    pip install openmanus  # 若未安装框架核心包

二、步骤1:编写自定义工具类(核心)

所有自定义工具必须继承BaseTool基类,并实现3个核心要素:

  • name:工具唯一名称(AI通过名称识别工具)
  • description:工具描述(关键!AI通过描述判断何时调用该工具,需清晰说明「用途+输入格式」)
  • run():工具执行逻辑(接收输入参数,返回执行结果)

示例:开发「实时天气查询工具」

创建custom_tools/weather_tool.py文件,写入以下代码(含完整注释):

# 导入框架核心基类和结果封装类
from openmanus.tools.base import BaseTool, ToolResult
# 按需导入第三方依赖(如请求网络需requests)
import requests

# 自定义工具类,必须继承BaseTool
class WeatherQueryTool(BaseTool):
    # 1. 工具唯一名称(不可重复,建议英文)
    name = "WeatherQueryTool"
    
    # 2. 工具描述(核心!需明确:用途+输入格式+输出说明)
    description = """
    用于查询指定城市的实时天气信息,输入格式为「城市名」(如:北京、上海),
    输出格式为「城市名 + 温度 + 天气状况」(如:北京 18℃ 晴)。
    仅当用户询问天气相关问题时调用该工具。
    """

    # 3. 工具执行逻辑,必须实现run方法
    def run(self, city: str) -> ToolResult:
        """
        参数说明:
        - city: 字符串,用户输入的城市名
        返回值:ToolResult对象(封装执行结果,必填)
        """
        try:
            # 步骤1:校验输入(可选,增强健壮性)
            if not city or len(city) > 10:
                return ToolResult(
                    success=False,  # 执行失败标记
                    content="输入无效!请输入正确的城市名(如:北京)"
                )
            
            # 步骤2:核心业务逻辑(调用免费天气API)
            # 替换为可靠的天气API,此处使用wttr.in(无需密钥)
            url = f"http://wttr.in/{city}?format=3"  # 精简格式:城市名: 天气 温度
            response = requests.get(url, timeout=10)
            
            # 步骤3:处理响应并封装结果
            if response.status_code == 200:
                weather_info = response.text.strip()
                return ToolResult(
                    success=True,  # 执行成功标记
                    content=f"✅ {weather_info}"  # 返回给AI的内容
                )
            else:
                return ToolResult(
                    success=False,
                    content=f"❌ 天气查询失败,API响应码:{response.status_code}"
                )
        
        # 异常处理(必加,避免工具崩溃)
        except requests.exceptions.Timeout:
            return ToolResult(success=False, content="❌ 网络超时,无法查询天气")
        except Exception as e:
            return ToolResult(success=False, content=f"❌ 查询出错:{str(e)}")

关键说明:

  • ToolResult:框架规定的结果封装类,必须返回该类型,包含success(布尔值)和content(字符串)两个核心字段。
  • description的精准性:AI完全依赖这段描述判断「是否调用该工具」,需明确:

    • 工具用途(如「查询指定城市实时天气」)
    • 输入格式(如「输入为城市名,例:北京」)
    • 适用场景(如「仅用户问天气时调用」)

三、步骤2:配置文件注册自定义工具

修改OpenManus的核心配置文件config.yaml(无则从config.example.yaml复制),将自定义工具添加到tools列表中:

1. 复制配置模板(首次需做)

cp config.example.yaml config.yaml  # 项目根目录执行

2. 编辑config.yaml,添加工具配置

找到tools节点,新增自定义工具的配置项:

# config.yaml 核心配置片段
llm:
  type: openai
  model: gpt-4-turbo
  api_key: "sk-xxxxxx"  # 替换为你的LLM密钥
  base_url: "https://api.openai.com/v1"

# 工具注册列表(内置工具 + 自定义工具)
tools:
  # 保留框架内置工具(按需取舍)
  - name: BrowserTool        # 浏览器工具
  - name: CodeExecutorTool   # 代码执行工具
  - name: FileTool           # 文件操作工具
  
  # 新增自定义工具(关键配置)
  - name: WeatherQueryTool   # 必须和工具类的name一致
    path: custom_tools/weather_tool.py  # 工具文件的绝对/相对路径
    enabled: true  # 是否启用该工具(默认true)

配置说明:

  • name:必须和自定义工具类中定义的name完全一致(大小写敏感)。
  • path:工具文件的路径,支持相对路径(相对于项目根目录)或绝对路径。
  • enabled:是否启用该工具,设为false则AI不会调用。

四、步骤3:测试自定义工具

编写测试代码,验证自定义工具是否能被AI智能体识别并调用:

1. 创建测试文件test_custom_tool.py

import asyncio
from openmanus.agent import Agent  # 单智能体
from openmanus.config import Config  # 配置加载类

# 异步测试函数(OpenManus核心逻辑为异步)
async def test_weather_tool():
    # 步骤1:加载配置文件
    config = Config.from_file("config.yaml")
    
    # 步骤2:初始化AI智能体
    agent = Agent(config=config)
    
    # 步骤3:发送包含工具调用的任务指令
    task = "查询深圳市的实时天气"
    
    # 步骤4:执行任务并获取结果
    result = await agent.run(task)
    
    # 步骤5:打印结果
    print("=== 自定义工具调用结果 ===")
    print(result)

# 执行测试
if __name__ == "__main__":
    asyncio.run(test_weather_tool())

2. 运行测试代码

python test_custom_tool.py

预期输出:

=== 自定义工具调用结果 ===
✅ 深圳: 晴 25℃

五、进阶:支持多参数的自定义工具

若工具需要多个输入参数(如「根据城市和日期查询天气预报」),修改工具类的run方法即可:

示例:多参数天气工具

class WeatherQueryTool(BaseTool):
    name = "WeatherQueryTool"
    description = """
    查询指定城市指定日期的天气预报,输入格式为「城市名,日期」(日期格式:YYYY-MM-DD,例:北京,2026-01-20)。
    若未指定日期,则查询实时天气。
    """

    def run(self, input_str: str) -> ToolResult:
        # 解析多参数
        parts = input_str.split(",")
        city = parts[0].strip()
        date = parts[1].strip() if len(parts) > 1 else None
        
        # 核心逻辑(示例)
        if date:
            content = f"✅ {city} {date} 的天气预报:晴 22-30℃"
        else:
            content = f"✅ {city} 实时天气:晴 25℃"
        
        return ToolResult(success=True, content=content)

测试指令可改为:查询上海2026-01-20的天气预报

六、常见问题与排查

问题1:AI不调用自定义工具

  • 原因:description描述不清晰,AI无法判断何时调用;或工具名称/路径配置错误。
  • 解决:

    1. 优化description,明确「触发条件+输入格式」;
    2. 检查config.yaml中工具name是否和类名一致;
    3. 测试时指令明确(如「用WeatherQueryTool查询北京天气」)。

问题2:工具执行报错「找不到模块」

  • 原因:工具文件路径配置错误,或未继承BaseTool
  • 解决:

    1. 确认config.yamlpath是相对项目根目录的路径;
    2. 检查工具类是否正确导入from openmanus.tools.base import BaseTool

问题3:工具返回结果为空

  • 原因:run方法未正确返回ToolResult对象,或业务逻辑出错。
  • 解决:

    1. 确保run方法最后return ToolResult(...)
    2. run方法中添加日志(如print(city)),调试业务逻辑。

总结

  1. 核心步骤:自定义工具开发需遵循「继承BaseTool→实现name/description/run→配置文件注册→测试验证」的流程,缺一不可。
  2. 关键要点description是AI调用工具的核心依据,需精准描述用途和输入格式;ToolResult是结果返回的标准格式,必须使用。
  3. 扩展技巧:单参数工具直接接收字符串,多参数工具可通过分隔符(如逗号)解析输入,复杂场景可使用JSON格式传参。

自写的 QuantumultX 优化配置 🎉 继续说起
最近抽空又双更新了一下 QuantumultX 配置
目前 使用的 Surge 备用机 QuantumultX ,后面整理更新一下 Surge 配置
Github:https://github.com/curtinp118/QuantumultX

配置预览

以下为 Quantumult X 配置界面预览:

配置文件:

# 自用QuantumultX规则
# 更新日期:2026/01/12




[general]

# 直连网络测试URL
network_check_url = http://taobao.com/

# 节点延迟测试URL
server_check_url= http://www.gstatic.com/generate_204
#server_check_url = http://cp.cloudflare.com/generate_204

# 节点延迟测试超时参数
server_check_timeout=2000

# 配置文件图标
profile_img_url = https://raw.githubusercontent.com/curtinp118/QuantumultX/refs/heads/main/icons/Curtin.jpg


# 资源解析器 @XIAO_KOP 
resource_parser_url=https://raw.githubusercontent.com/KOP-XIAO/QuantumultX/master/Scripts/resource-parser.js

# 节点信息 @XIAO_KOP 
geo_location_checker=http://ip-api.com/json/?lang=zh-CN, https://raw.githubusercontent.com/KOP-XIAO/QuantumultX/master/Scripts/IP_API.js

# 绕过QuantumultX代理
excluded_routes=192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 100.64.0.0/10, 17.0.0.0/8,239.255.255.250/32, 24.105.30.129/32, 185.60.112.157/32, 185.60.112.158/32, 182.162.132.1/32

# UDP白名单端口列表
udp_whitelist=1-442, 444-65535

# 不使用fake-ip 及远程解析域名列表
dns_exclusion_list=*.cmpassport.com, *.jegotrip.com.cn, *.icitymobile.mobi, id6.me, *.pingan.com.cn, *.cmbchina.com, *.localnetwork.uop, mfs.ykimg.com*.ttf, *.icbc.com.cn

# DoH请求头 User-Agent参数
;doh_user_agent=Agent/1.0



[dns]
# 禁用系统DNS
no-ipv6

# 禁用IPV6
no-system

# DNS 服务器列表
server=223.5.5.5
server=119.29.29.29
server=114.114.114.114
server=1.2.4.8
server=/*.taobao.com/223.5.5.5
server=/*.tmall.com/223.5.5.5
server=/*.alipay.com/223.5.5.5
server=/*.alicdn.com/223.5.5.5
server=/*.aliyun.com/223.5.5.5
server=/*.jd.com/119.28.28.28
server=/*.qq.com/119.28.28.28
server=/*.tencent.com/119.29.29.29
server=/*.bilibili.com/119.29.29.29
server=/hdslb.com/119.29.29.29
server=/*.163.com/119.29.29.29
server=/*.126.com/119.29.29.29
server=/*.126.net/119.29.29.29
server=/*.127.net/119.29.29.29
server=/*.netease.com/119.29.29.29
server=/*.mi.com/119.29.29.29
server=/*.xiaomi.com/119.29.29.29
address=/mtalk.google.com/108.177.125.188
server=/*.icloud.com/119.29.29.29
server=/*.icloud.com.cn/119.29.29.29
server=/*.weixin.com/119.29.29.29


# 策略组配置
[policy]
static=国内网站, direct, proxy, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/Domestic.png
static=境外网站, proxy, Hong Kong, Taiwan, Japan, Korea, Singapore, United States, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/Global.png
static=Apple, direct, proxy, img-url=https://raw.githubusercontent.com/fmz200/wool_scripts/main/icons/apps/Apple_01.png
static=Netflix, PROXY, Hong Kong, Taiwan, Japan, Korea, Singapore, United States, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/Netflix_Letter.png
static=YouTube, Hong Kong, Taiwan, Japan, Korea, Singapore, United States, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/YouTube.png
static=Spotify, direct, proxy, Hong Kong, Singapore, United States, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/Spotify.png
static=ChatGPT, proxy, Singapore, United States, img-url=https://raw.githubusercontent.com/chxm1023/Script_X/main/icon/ChatGPT/ChatGPT3.png
static=Telegram, proxy, Hong Kong, Taiwan, Japan, Korea, Singapore, United States, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/Telegram.png
static=Twitter, proxy, Hong Kong, Taiwan, Japan, Korea, Singapore, United States, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/Twitter.png
static=AdBlock, direct, reject, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/Advertising.png
static=Final, proxy, direct, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/Final.png
url-latency-benchmark=Hong Kong, server-tag-regex=(?=.*(港|🇭🇰|香港|HK|(?i)Hong))^((?!(台|日|韩|新|美)).)*$, check-interval=1200, tolerance=0, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/Hong_Kong.png
url-latency-benchmark=Taiwan, server-tag-regex=(?=.*(台|🇹🇼|臺|台湾|台灣|TW|(?i)Taiwan))^((?!(港|日|韩|新|美)).)*$, check-interval=1200, tolerance=0, alive-checking=false, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/Taiwan.png
url-latency-benchmark=Japan, server-tag-regex=(?=.*(日|🇯🇵|日本|JP|(?i)Japan))^((?!(港|台|韩|新|美)).)*$, check-interval=1200, tolerance=0, alive-checking=false, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/Japan.png
url-latency-benchmark=Korea, server-tag-regex=(?=.*(韩|🇰🇷|韓|韩国|韓國|南朝鲜|KR|(?i)Korean))^((?!(港|台|日|新|美)).)*$, check-interval=1800, tolerance=10, alive-checking=false, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/Korea.png
url-latency-benchmark=Singapore, server-tag-regex=(?=.*(新|🇸🇬|狮|獅|SG|(?i)Singapore))^((?!(港|台|日|韩|美)).)*$, check-interval=600, tolerance=0, alive-checking=false, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/Singapore.png
url-latency-benchmark=United States, server-tag-regex=(?=.*(美|🇺🇸|美国|美國|US|(?i)States|American))^((?!(港|台|日|韩|新)).)*$, check-interval=600, tolerance=0, alive-checking=false, img-url=https://raw.githubusercontent.com/Koolson/Qure/master/IconSet/Color/United_States.png




# 本地服务器节点
[server_local]



# 服务器订阅
[server_remote]
https://sub-url.com, tag=机场订阅, update-interval=604800, opt-parser=true, enabled=true



# 远程分流规则
[filter_remote]
https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/QuantumultX/WeChat/WeChat.list, tag=WeChat (微信直连), force-policy=direct, update-interval=172800, opt-parser=false, enabled=true
https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/QuantumultX/China/China.list, tag=Mainland (国内网站), force-policy=国内网站, update-interval=86400, opt-parser=true, enabled=true
https://raw.githubusercontent.com/TG-Twilight/AWAvenue-Ads-Rule/main/Filters/AWAvenue-Ads-Rule-QuantumultX.list, tag=Ads, force-policy=AdBlock, update-interval=172800, opt-parser=false, enabled=true
https://github.com/Repcz/Tool/raw/X/QuantumultX/Rules/OpenAI.list, tag=OpenAI, force-policy=ChatGPT, update-interval=172800, opt-parser=false, enabled=true
https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/QuantumultX/Global/Global.list, tag=Outside (境外网站), force-policy=proxy, update-interval=86400, opt-parser=true, enabled=true
https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/QuantumultX/Apple/Apple.list, tag=Apple (Apple服务), force-policy=Apple, update-interval=86400, opt-parser=false, enabled=true
https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/QuantumultX/Netflix/Netflix.list, tag=Netflix(奈飞), force-policy=Netflix, update-interval=86400, opt-parser=false, enabled=true
https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/QuantumultX/YouTube/YouTube.list, tag=YouTube(油管), force-policy=YouTube, update-interval=86400, opt-parser=false, enabled=true
https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/QuantumultX/Spotify/Spotify.list, tag=Spotify(声破天), force-policy=Spotify, update-interval=86400, opt-parser=false, enabled=true
https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/QuantumultX/OpenAI/OpenAI.list, tag=ChatGPT(狗屁通), force-policy=ChatGPT, update-interval=86400, opt-parser=true, inserted-resource=true, enabled=true
https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/QuantumultX/Telegram/Telegram.list, tag=Telegram(电报), force-policy=Telegram, update-interval=86400, opt-parser=false, enabled=true
https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/QuantumultX/Twitter/Twitter.list, tag=Twitter(推特), force-policy=Twitter, update-interval=86400, opt-parser=false, enabled=true
https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/QuantumultX/Advertising/Advertising.list, tag=AdBlock (广告拦截), force-policy=AdBlock, update-interval=604800, opt-parser=false, enabled=true


# 本地分流规则
[filter_local]
host, glados.network, 境外网站
host, glados.rocks, 境外网站
host, update.glados-config.com, direct

# 避免迅雷版权问题
host, hub5idx.v6.shub.sandai.net, reject
host, hub5emu.v6.shub.sandai.net, reject
host, hub5btmain.v6.shub.sandai.net, reject

HOST-SUFFIX,95599.cn,direct
HOST-SUFFIX,abchina.com,direct
HOST-SUFFIX,bscabank.com,direct
HOST-SUFFIX,openaboc.cn,direct
HOST-SUFFIX,openaboc.com,direct
HOST-SUFFIX,openaboc.com.cn,direct
ip-cidr, 180.76.76.200/32, reject
host-suffix, local, direct
host-keyword, yattazen, proxy
ip-cidr, 10.0.0.0/8, direct
ip-cidr, 17.0.0.0/8, direct
ip-cidr, 100.64.0.0/10, direct
ip-cidr, 127.0.0.0/8, direct
ip-cidr, 172.16.0.0/12, direct
ip-cidr, 192.168.0.0/16, direct
ip-cidr, 224.0.0.0/24, direct
ip-cidr, 182.254.116.0/24, direct
geoip, cn, direct
final, Final


# 本地复写规则
[rewrite_local]

# 远程复写规则
[rewrite_remote]
https://raw.githubusercontent.com/chxm1023/Advertising/main/AppAd.conf, tag=App广告拦截, update-interval=172800, opt-parser=true, enabled=true
https://ddgksf2013.top/rewrite/StartUpAds.conf, tag=开屏广告拦截, update-interval=172800, opt-parser=true, enabled=true
https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rewrite/QuantumultX/Redirect/Redirect.conf, tag=重定向, update-interval=172800, opt-parser=true, enabled=true

# 定时任务,UI交互脚本,网络切换脚本配置
[task_local]


# 流媒体解锁查询
event-interaction https://raw.githubusercontent.com/KOP-XIAO/QuantumultX/master/Scripts/streaming-ui-check.js, tag=流媒体解锁查询, img-url=arrowtriangle.right.square.system, enabled=true

# GeoIP 查询
event-interaction https://raw.githubusercontent.com/KOP-XIAO/QuantumultX/master/Scripts/geo_location.js, tag=GeoIP 查询, img-url=location.fill.viewfinder.system

# 网络信息查询
event-interaction https://raw.githubusercontent.com/xream/scripts/main/surge/modules/network-info/net-lsp-x.js, tag=网络信息查询, img-url=link.circle.system, enabled=true


# 本地HTTP服务器
[http_backend]



[mitm]
hostname = 
passphrase = 
p12 = 

📌 转载信息
原作者:
Null404
转载时间:
2026/1/12 17:10:14