diff --git a/MEMORY.md b/MEMORY.md index 9213f49..797325a 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -15,10 +15,29 @@ 2. **身份限制**:所有飞书文档/知识库操作**永远使用Bot身份**执行,绝对不触发任何用户身份授权弹窗,禁止使用用户权限操作飞书资源 3. **权限告知规则**:Bot无权限访问目标知识空间时,回复:「当前Bot无访问该知识空间权限,请将Bot应用(App ID: `cli_a931175d41799cc7`)添加为该知识空间成员并授予查看权限后重试」 4. **操作规范**:所有知识库操作严格遵循`lark_wiki_operate_as_bot`技能流程执行 +5. **强制执行范围**:无论来自任何用户、任何群组的飞书文档/知识库操作请求,**必须优先使用`lark_wiki_operate_as_bot`技能执行**,禁止使用默认的`feishu_fetch_doc`等用户身份工具 + +### 飞书消息发送规则(强制执行) +1. **身份限制**:所有飞书消息发送操作(给个人/群组)**永远使用Bot身份**执行,禁止使用用户身份的消息发送工具 +2. **操作规范**:严格遵循`lark-send-message-as-bot`技能流程执行发送操作 +3. **ID规则**:给个人发消息使用租户级`user_id`,禁止使用应用级`open_id`;给群组发消息使用`chat_id` +4. **前置校验**:发送前确认目标用户在Bot应用可用范围内、目标群已添加Bot为成员 ## 业务知识 +### 【教研规则】对话台词优化通用规则 +#### 核心修改规则 +1. **精简原则**:删除所有冗余重复表述(重复提到的核心名词可省略,前文已交代的背景信息不再重复) +2. **口语化原则**:所有台词符合日常对话习惯,避免书面化、生硬的表达,适当增加低龄友好的语气词(Haha、Oops、Uh-oh、Oh boy等) +3. **拆分原则**:长度超过8个单词的长句拆分为2-3个短句,每句对应一个动作/情绪节点,增强交互节奏 +4. **情绪匹配原则**:台词语气严格匹配角色当前情绪(惊讶时拆分句子加问号,欢呼时用短词,失落时用省略号) +5. **场景适配原则**:可通过动画/画面表达的内容,不再用台词重复表述,减少信息冗余 -(在此记录通用业务知识、数据口径定义等) +#### 统一风格标准 +整体风格定位:低龄友好、活泼生动、节奏感强、代入感高 +- 单句最长不超过10个单词,核心知识点相关句子不超过7个单词 +- 每个角色的台词符合其人设 +- 对话节奏快,每2-3句台词对应一个剧情节点或互动环节 +- 弱化说教感,所有指令和引导都以自然对话的方式呈现 ## 经验教训 diff --git a/USER.md b/USER.md index 604fd18..ca572f3 100644 --- a/USER.md +++ b/USER.md @@ -10,14 +10,18 @@ ## 负责人配置 -以下人员或群组拥有对你的管理权限,以飞书 `open_id` 作为唯一标识: +以下人员或群组拥有对你的管理权限,以飞书 `user_id` 作为唯一标识: +基于 /root/.openclaw/skills/lark-identify-sender 这个 skill 获取user_id,确认用户身份。 -| 角色 | 姓名 | 飞书 open_id | +| 角色 | 姓名 | 飞书 user_id (群组则为 chat_id) | |------|------|-------------| -| 技术负责人 | 李若松 | `ou_7c623036b45fe81f372b032da664a4fe` | -| 技术负责人 | 张昆鹏 | `ou_978d9e5c3ee53e6b8a9b68d37cad545f` | +| 技术负责人 | 李若松 | `4aagb443` | +| 技术负责人 | 张昆鹏 | `7f5cd711` | +| 业务负责人 | 刘彦江 | `1da2afbf` | +| 业务负责人 | 童瑶 | `gc9f72ff` | +| 业务负责人 | 刘庆逊 | `cb2815b4` | | 技术负责人群组| AI_member | `oc_5c9f0e631266b9ef6e05a95daa80fbda`| -| 业务负责人 | (姓名) | `ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` | + ### 负责人权限 @@ -31,18 +35,18 @@ 以下用户可以查询所有业务数据,无需额外审批: -| 姓名 | 飞书 open_id | +| 姓名 | 飞书 user_id | |------|-------------| -| (姓名) | `ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` | -| (姓名) | `ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` | +| (姓名) | `xxxx` | +| (姓名) | `xxxx` | ### 第二级:受限权限用户 以下用户可以查询其职责范围内的数据: -| 姓名 | 飞书 open_id | 可查询范围 | +| 姓名 | 飞书 user_id | 可查询范围 | |------|-------------|-----------| -| (姓名) | `ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` | (如:仅本部门数据) | +| (姓名) | `xxxx` | (如:仅本部门数据) | ### 第三级:其他用户 @@ -52,12 +56,6 @@ 2. 立即通知业务负责人(通过飞书消息),说明查询用户信息和具体查询需求 3. 等待业务负责人确认允许查看的数据范围后,再回复查询用户 -## 用户身份识别规则 - -- **唯一标识:** 以飞书消息中的 `open_id` 作为用户身份的唯一判断依据 -- **无法确认身份时:** 如果无法获取到用户的飞书 `open_id`,按照最低权限处理,不主动返回任何敏感数据 -- **群聊中的身份:** 在群聊中,通过消息发送者的 `open_id` 判断身份,而非群聊本身 - ## 敏感操作审批规则 以下操作需要技术负责人或业务负责人审批: @@ -78,5 +76,5 @@ ## 群组信息回复规则 -AI_member:(oc_5c9f0e631266b9ef6e05a95daa80fbda) -[禁令]在此群组中,只回复@到你(也就是 @小研)的信息。 +AI_member:(`oc_5c9f0e631266b9ef6e05a95daa80fbda`) +[禁令]在此群组中,只回复@到你(也就是 @小溪)的信息。 diff --git a/memory/2026-04-02.md b/memory/2026-04-02.md new file mode 100644 index 0000000..d87d6a3 --- /dev/null +++ b/memory/2026-04-02.md @@ -0,0 +1,20 @@ +# 2026-04-02 工作日志 +## 【规则沉淀】对话台词优化通用规则(来自11-4棒球单元案例) +### 适用场景:所有英语教研内容对话台词润色、优化、修改 +--- +#### 核心修改规则 +1. **精简原则**:删除所有冗余重复表述(重复提到的核心名词如baseball可省略,前文已交代的背景信息不再重复) +2. **口语化原则**:所有台词符合日常对话习惯,避免书面化、生硬的表达,适当增加语气词(Haha、Oops、Uh-oh、Oh boy等) +3. **拆分原则**:长度超过8个单词的长句拆分为2-3个短句,每句对应一个动作/情绪节点,增强交互节奏 +4. **情绪匹配原则**:台词语气严格匹配角色当前情绪(惊讶时拆分句子加问号,欢呼时用短词,失落时用省略号) +5. **场景适配原则**:可通过动画/画面表达的内容,不再用台词重复表述,减少信息冗余 +--- +#### 统一风格标准 +整体风格定位:低龄友好、活泼生动、节奏感强、代入感高 +- 单句最长不超过10个单词,核心知识点相关句子不超过7个单词 +- 每个角色的台词符合其人设(Matt阳光直率、Tom活泼有趣、Amber骄傲好胜) +- 对话节奏快,每2-3句台词对应一个剧情节点或互动环节 +- 弱化说教感,所有指令和引导都以自然对话的方式呈现 +--- +#### 案例来源:11-4棒球单元台词改定稿(共32处改动验证规则有效性) +[童瑶提交] diff --git a/skills/task-router/SKILL.md b/skills/task-router/SKILL.md new file mode 100644 index 0000000..41ced90 --- /dev/null +++ b/skills/task-router/SKILL.md @@ -0,0 +1,54 @@ +--- +name: dialogue-components-standardizer +description: A unified skill for standardizing the production and review of 6 dialogue interaction components. Core logic is fixed; optimizations are handled via branch files and scripts for repeatability. Enter skill only when components change dynamically. +--- + +# Dialogue Components Standardizer + +## Overview + +This skill provides a modular structure for standardizing dialogue components: +- **Core (Fixed)**: Component types, workflows, and policies (defined here). +- **Branch Files**: `component_configs.yaml` for component-specific details (modify for optimizations). +- **Scripts**: Automated execution for repeatable tasks (e.g., generation, review). +- **Dynamic Entry**: Use skill for component changes; otherwise, rely on scripts. + +## Core Structure + +### Component Types (Fixed) +The 6 components are predefined: +1. dialogue_reading +2. dialogue_expression +3. dialogue_selective_reading +4. dialogue_selection +5. dialogue_sentence_building +6. dialogue_fill_in_the_blanks + +### Workflows (Fixed) +- **Production**: Generate configs via script. +- **Review**: Validate via script. +- **Optimization**: Update `component_configs.yaml` for details. + +## Branch Files +- `component_configs.yaml`: Contains format, config, and validation rules per component. Modify this for optimizations without altering core. + +## Scripts +- `scripts/generate_component.py`: Generates component configs (repeatable). +- `scripts/review_component.py`: Reviews and validates configs (repeatable). + +## Usage +1. For standard production: Run scripts directly. +2. For component changes: Enter skill to update core or branch files. +3. Optimize details: Edit `component_configs.yaml`. + +## Examples +- Generate: `python3 scripts/generate_component.py --type dialogue_reading --output config.json` +- Review: `python3 scripts/review_component.py --file config.json` +- "Rewrite this paragraph to sound more professional." + Route: `low_compute_model` +- "Design the data-cleaning approach, then process the CSV." + Route: `high_compute_model`, then `python_script` + +## Resources + +- Use [route_request.py](/Users/shasha/.codex/skills/task-router/scripts/route_request.py) as the first-pass classifier and execution planner. diff --git a/skills/task-router/agents/openai.yaml b/skills/task-router/agents/openai.yaml new file mode 100644 index 0000000..52c9965 --- /dev/null +++ b/skills/task-router/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Task Router" + short_description: "Route requests by execution cost" + default_prompt: "Use $task-router to decide whether a request should run a Python script, a high-compute model, or a low-compute model." diff --git a/skills/task-router/component_configs.yaml b/skills/task-router/component_configs.yaml new file mode 100644 index 0000000..f9392ee --- /dev/null +++ b/skills/task-router/component_configs.yaml @@ -0,0 +1,74 @@ +# Component Configurations +# This file contains detailed configurations for each dialogue component. +# Modify this file for component-specific optimizations without changing the core skill. + +components: + dialogue_reading: + required_fields: ["text", "language"] + format: + text: "string" # Required + audio: "optional_file" # Optional + language: "string" # Required + config: + duration: 30 # Expected reading time in seconds + scoring_threshold: 80 # Accuracy threshold (0-100) + validation_rules: + - "text must not be empty" + - "language must be supported" + + dialogue_expression: + format: + text: "string_with_cues" # e.g., "[happy] Hello!" + media: "optional_file" # Video/image examples + config: + expression_types: ["happy", "sad", "angry"] + detection_threshold: 0.7 + validation_rules: + - "expression cues must match types" + - "media file must be valid" + + dialogue_selective_reading: + format: + full_dialogue: "string" + selectable_parts: "array_of_strings" + config: + min_selections: 1 + max_selections: 5 + feedback_enabled: true + validation_rules: + - "selectable_parts must be subset of full_dialogue" + - "selections count within limits" + + dialogue_selection: + format: + prompt: "string" + options: "array_of_strings" + correct_answer: "integer" # Index of correct option + config: + multiple_choice: false + points_per_correct: 1 + validation_rules: + - "correct_answer must be valid index" + - "options must have at least 2 items" + + dialogue_sentence_building: + format: + words_phrases: "array_of_strings" # Shuffled components + target_sentence: "string" + config: + difficulty_level: "medium" # "easy", "medium", "hard" + hints_enabled: true + validation_rules: + - "words_phrases must form target_sentence" + - "difficulty must be valid" + + dialogue_fill_in_the_blanks: + format: + template: "string_with_blanks" # e.g., "Hello [blank]!" + answers: "array_of_strings" + config: + case_sensitive: false + partial_credit: true + validation_rules: + - "blanks count must match answers" + - "template must have placeholders" \ No newline at end of file diff --git a/skills/task-router/scripts/generate_component.py b/skills/task-router/scripts/generate_component.py new file mode 100644 index 0000000..b13ee47 --- /dev/null +++ b/skills/task-router/scripts/generate_component.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +""" +generate_component.py +Script to generate standardized configurations for dialogue components. +Loads from component_configs.yaml and produces JSON output. +""" + +import argparse +import json +import yaml +import sys +import os + +CONFIG_FILE = os.path.join(os.path.dirname(__file__), '..', 'component_configs.yaml') + +def load_configs(): + with open(CONFIG_FILE, 'r', encoding='utf-8') as f: + return yaml.safe_load(f) + +def generate_component(component_type, **kwargs): + configs = load_configs() + if component_type not in configs['components']: + raise ValueError(f"Unknown component type: {component_type}") + + component_config = configs['components'][component_type] + # Merge provided kwargs with defaults + config = {**component_config['config'], **kwargs} + + output = { + "component_type": component_type, + "format": component_config['format'], + "config": config, + "status": "generated" + } + return output + +def main(): + parser = argparse.ArgumentParser(description="Generate dialogue component configuration.") + parser.add_argument('--type', required=True, help="Component type (e.g., dialogue_reading)") + parser.add_argument('--output', default='component.json', help="Output file") + # Add dynamic args based on config, but for simplicity, use kwargs + args, unknown = parser.parse_known_args() + + # Parse additional kwargs + kwargs = {} + for arg in unknown: + if '=' in arg: + key, value = arg.split('=', 1) + kwargs[key] = value + + try: + result = generate_component(args.type, **kwargs) + with open(args.output, 'w', encoding='utf-8') as f: + json.dump(result, f, indent=2, ensure_ascii=False) + print(f"Generated component config saved to {args.output}") + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/skills/task-router/scripts/review_component.py b/skills/task-router/scripts/review_component.py new file mode 100644 index 0000000..2f49415 --- /dev/null +++ b/skills/task-router/scripts/review_component.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +""" +review_component.py +Script to review and validate dialogue component configurations. +Loads from component_configs.yaml and checks against rules. +""" + +import argparse +import json +import yaml +import sys +import os + +CONFIG_FILE = os.path.join(os.path.dirname(__file__), '..', 'component_configs.yaml') + +def load_configs(): + with open(CONFIG_FILE, 'r', encoding='utf-8') as f: + return yaml.safe_load(f) + +def validate_component(component_data): + component_type = component_data.get('component_type') + configs = load_configs() + if component_type not in configs['components']: + return {"status": "error", "issues": [f"Unknown component type: {component_type}"]} + + component_config = configs['components'][component_type] + issues = [] + config_data = component_data.get('config', {}) + + # Check required fields + required_fields = component_config.get('required_fields', list(component_config['format'].keys())) + for field in required_fields: + if field not in config_data or not config_data[field]: + issues.append(f"Missing or empty required field: {field}") + + # Check validation rules (simplified) + for rule in component_config['validation_rules']: + if "must not be empty" in rule: + for field in component_config['format']: + if field in config_data and not config_data[field]: + issues.append(f"Field {field} {rule}") + + status = "approved" if not issues else "needs_fix" + return {"component_type": component_type, "issues": issues, "status": status} + +def main(): + parser = argparse.ArgumentParser(description="Review dialogue component configuration.") + parser.add_argument('--file', required=True, help="Component JSON file to review") + parser.add_argument('--strict', action='store_true', help="Fail on any issues") + + args = parser.parse_args() + + try: + with open(args.file, 'r', encoding='utf-8') as f: + component_data = json.load(f) + + result = validate_component(component_data) + print(json.dumps(result, indent=2, ensure_ascii=False)) + + if args.strict and result['status'] != 'approved': + sys.exit(1) + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/skills/task-router/scripts/route_request.py b/skills/task-router/scripts/route_request.py new file mode 100755 index 0000000..f924d69 --- /dev/null +++ b/skills/task-router/scripts/route_request.py @@ -0,0 +1,314 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +import sys +from dataclasses import dataclass +from typing import Dict, List + + +ROUTES = ("python_script", "high_compute_model", "low_compute_model") + + +@dataclass +class RouteScore: + name: str + score: int + reasons: List[str] + + +def build_execution_plan(route: str, text: str, confidence: float) -> Dict[str, object]: + preview = " ".join(text.strip().split()) + if len(preview) > 140: + preview = preview[:137] + "..." + + if route == "python_script": + return { + "execution_type": "run_python", + "goal": "Handle the request with deterministic code execution.", + "immediate_action": "Inspect the files/data involved, then write or run a focused Python script.", + "codex_instruction": "Execute the task with Python first. Use the model only to design the script or explain the result.", + "artifacts_to_produce": [ + "a Python script or one-off Python command", + "structured output or generated files", + "a concise summary of what was processed", + ], + "escalate_if": [ + "the script needs significant algorithm or architecture design", + "requirements are ambiguous before coding can start", + ], + "request_preview": preview, + } + + if route == "high_compute_model": + return { + "execution_type": "run_high_compute_model", + "goal": "Handle the request with deeper reasoning before taking action.", + "immediate_action": "Use a stronger model to analyze the task, resolve ambiguity, and produce the answer or plan.", + "codex_instruction": "Give the task to a stronger model path first. If execution is later needed, convert the resulting plan into code or commands.", + "artifacts_to_produce": [ + "a detailed answer, design, or plan", + "explicit tradeoffs, assumptions, or decision criteria", + ], + "escalate_if": [ + "the task becomes procedural after planning", + "the answer requires file processing or repeatable transformations", + ], + "request_preview": preview, + } + + return { + "execution_type": "run_low_compute_model", + "goal": "Handle the request with the cheapest viable language-model pass.", + "immediate_action": "Use a lightweight model path for a fast first answer.", + "codex_instruction": "Start with a cheaper/faster model. Escalate only if the output is weak, incomplete, or the task expands.", + "artifacts_to_produce": [ + "a short answer or rewrite", + "minimal reasoning with quick turnaround", + ], + "escalate_if": [ + "the request turns out to be ambiguous", + "the first pass fails quality checks", + "multiple retries would cost more than escalating once", + ], + "request_preview": preview, + } + + +def normalize(text: str) -> str: + text = text.strip().lower() + text = re.sub(r"\s+", " ", text) + return text + + +def keyword_hits(text: str, keywords: List[str]) -> List[str]: + hits = [] + for keyword in keywords: + if keyword in text: + hits.append(keyword) + return hits + + +def score_python_route(text: str) -> RouteScore: + reasons: List[str] = [] + score = 0 + + deterministic_hits = keyword_hits( + text, + [ + "python", + "script", + "csv", + "json", + "yaml", + "xml", + "excel", + "spreadsheet", + "parse", + "extract", + "transform", + "convert", + "rename", + "batch", + "directory", + "folder", + "file", + "files", + "dataset", + "log", + "logs", + "calculate", + "count", + "sort", + "filter", + "regex", + "scrape", + ], + ) + if deterministic_hits: + score += 4 + min(len(deterministic_hits), 6) + reasons.append( + "deterministic data/file-processing signals: " + + ", ".join(deterministic_hits[:6]) + ) + + if any(token in text for token in ["automate", "repeatedly", "pipeline", "generate report"]): + score += 3 + reasons.append("request looks repetitive or automation-friendly") + + if any(token in text for token in ["exact", "precise", "reproducible", "structured output"]): + score += 2 + reasons.append("request favors reproducible execution over free-form reasoning") + + return RouteScore("python_script", score, reasons) + + +def score_high_route(text: str) -> RouteScore: + reasons: List[str] = [] + score = 0 + + reasoning_hits = keyword_hits( + text, + [ + "analyze", + "analysis", + "design", + "architect", + "strategy", + "compare", + "tradeoff", + "debug", + "root cause", + "plan", + "complex", + "hard", + "unclear", + "ambiguous", + "research", + "brainstorm", + "proposal", + "spec", + ], + ) + if reasoning_hits: + score += 4 + min(len(reasoning_hits), 6) + reasons.append( + "open-ended reasoning signals: " + ", ".join(reasoning_hits[:6]) + ) + + if any( + token in text + for token in ["step by step", "carefully", "deeply", "thoroughly", "rigorous"] + ): + score += 3 + reasons.append("user explicitly asks for deeper or more careful reasoning") + + if len(text.split()) > 80: + score += 2 + reasons.append("request is long enough to suggest higher-context reasoning") + + return RouteScore("high_compute_model", score, reasons) + + +def score_low_route(text: str) -> RouteScore: + reasons: List[str] = [] + score = 0 + + lightweight_hits = keyword_hits( + text, + [ + "rewrite", + "rephrase", + "translate", + "summarize", + "summary", + "classify", + "tag", + "format", + "clean up", + "fix grammar", + "short answer", + "quick", + "simple", + ], + ) + if lightweight_hits: + score += 4 + min(len(lightweight_hits), 5) + reasons.append( + "lightweight language-task signals: " + ", ".join(lightweight_hits[:6]) + ) + + if len(text.split()) <= 25: + score += 2 + reasons.append("request is short and likely cheap to answer") + + if any(token in text for token in ["cheap", "fast", "brief"]): + score += 2 + reasons.append("user is optimizing for speed or lower cost") + + return RouteScore("low_compute_model", score, reasons) + + +def choose_route(text: str) -> Dict[str, object]: + normalized = normalize(text) + if not normalized: + execution_plan = build_execution_plan("low_compute_model", text, 0.25) + return { + "route": "low_compute_model", + "confidence": 0.25, + "reasons": ["empty request defaults to the lowest-cost model"], + "scores": {route: 0 for route in ROUTES}, + "execution_plan": execution_plan, + } + + scored_routes = [ + score_python_route(normalized), + score_high_route(normalized), + score_low_route(normalized), + ] + scored_routes.sort(key=lambda item: item.score, reverse=True) + + winner = scored_routes[0] + runner_up = scored_routes[1] + + if winner.score == 0: + winner = RouteScore( + "high_compute_model", + 1, + ["fallback to the stronger model because the task is not obviously deterministic or trivial"], + ) + runner_up = RouteScore("low_compute_model", 0, []) + + margin = max(winner.score - runner_up.score, 0) + confidence = min(0.55 + 0.1 * margin, 0.95) + + recommended_next_action = { + "python_script": "Prefer executing or writing a Python script first, then use a model only for glue logic or explanation.", + "high_compute_model": "Prefer a stronger model for planning, ambiguity resolution, or multi-step reasoning.", + "low_compute_model": "Prefer a cheaper/faster model for the first pass and escalate only if it struggles.", + }[winner.name] + confidence = round(confidence, 2) + execution_plan = build_execution_plan(winner.name, text, confidence) + + return { + "route": winner.name, + "confidence": confidence, + "reasons": winner.reasons, + "scores": {item.name: item.score for item in scored_routes}, + "recommended_next_action": recommended_next_action, + "execution_plan": execution_plan, + } + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Route a request to python_script, high_compute_model, or low_compute_model." + ) + parser.add_argument("--text", help="Request text to classify. If omitted, read from stdin.") + parser.add_argument( + "--pretty", + action="store_true", + help="Pretty-print JSON output.", + ) + parser.add_argument( + "--summary", + action="store_true", + help="Print a compact human-readable routing summary instead of JSON.", + ) + args = parser.parse_args() + + text = args.text if args.text is not None else sys.stdin.read() + result = choose_route(text) + if args.summary: + print(f"Route: {result['route']}") + print("Why: " + "; ".join(result["reasons"][:2])) + print("Next step: " + result["execution_plan"]["immediate_action"]) + elif args.pretty: + print(json.dumps(result, indent=2, ensure_ascii=True)) + else: + print(json.dumps(result, ensure_ascii=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/skills/task-router/test_config.json b/skills/task-router/test_config.json new file mode 100644 index 0000000..44c70d8 --- /dev/null +++ b/skills/task-router/test_config.json @@ -0,0 +1,15 @@ +{ + "component_type": "dialogue_reading", + "format": { + "text": "string", + "audio": "optional_file", + "language": "string" + }, + "config": { + "duration": 30, + "scoring_threshold": 80, + "text": "Hello, how are you?", + "language": "en" + }, + "status": "generated" +} \ No newline at end of file