{"repo": "a5sl/role-play-agent", "files": {"README.md": {"9108883e8580aead2f8496720d77182fba5616ef": {"t": 1785746598.3718984, "n": 2542, "h": "b1e27711c01344e0", "c": "# RPagent — AI Role-Playing Agent\n\nA character-driven AI agent that role-plays as fictional characters using game dialogue as memory. Built with a hand-written ReAct loop on top of DeepSeek, ChromaDB, and Streamlit.\n\n## What it does\n\n- **QA Mode** — Ask questions about your documents, get answers backed by RAG retrieval\n- **Roleplay Mode** — Talk to an AI character that stays in-character using Persona Cards, emotional triggers, and dialogue memory from actual game scripts\n\n## Quick Start\n\n```bash\n# 1. Install\npip install -r requirements.txt\n\n# 2. Configure\ncp .env.example .env\n# Edit .env, add your DEEPSEEK_API_KEY=sk-...\n\n# 3. Launch the Web UI\nstreamlit run src/ui/app.py\n```\n\n## CLI Modes\n\n```bash\npython -m src.rag_cli # Pure RAG (document Q&A)\npython -m src.agent_cli # Agent with tool-calling\npython -m src.rp_cli data/personas/美岬.yaml # Roleplay as a character\npython -m src.character.memory_cli stats # Dialogue memory stats\n```\n\n## How it works\n\n```\nUser input → Agent thinks → picks a tool → retrieves context from ChromaDB → generates response\n```\n\n- **Agent Loop**: Hand-written ReAct pattern — no LangChain magic, fully transparent\n- **Character System**: Persona Cards (YAML) define identity, personality, speech style, knowledge boundaries, and emotional triggers\n- **Memory**: Game dialogues imported into ChromaDB, semantically retrieved per conversation turn, injected into the character's system prompt\n- **Emotional Triggers**: Keyword-matched emotional states that temporarily shift the character's mood\n\n## Project layout\n\n```\nsrc/\n├── agent/ # ReAct loop, roleplay agent, tool definitions\n├── character/ # Persona cards, prompt builder, dialogue parsers, memory\n├── document/ # PDF/TXT loading, chunking, embedding, retrieval\n├── ui/ # Streamlit app\n├── config.py # Global settings\n├── agent_cli.py # QA Agent CLI\n├── rp_cli.py # Roleplay CLI\n└── rag_cli.py # RAG CLI\n```\n\n## Philosophy\n\nEvery phase produces runnable code. The agent loop is handwritten for transparency — no framework black boxes. Character data is decoupled from agent logic so multi-character conversations can be added later.\n\n## Dependencies\n\n- **LLM**: DeepSeek (OpenAI-compatible API)\n- **Vector DB**: ChromaDB with sentence-transformers for local embeddings\n- **UI**: Streamlit\n- **Docs parsing**: pypdf, markdown\n\n---\n\n*For architecture details, data flow diagrams, and the full roadmap, see [ARCHITECTURE.md](dev/ARCHITECTURE.md).*\n"}, "4406869c45f8673685a93de5caad07b7280489b8": {"t": 1785766177.5887873, "n": 2542, "h": "b1e27711c01344e0", "c": "# RPagent — AI Role-Playing Agent\n\nA character-driven AI agent that role-plays as fictional characters using game dialogue as memory. Built with a hand-written ReAct loop on top of DeepSeek, ChromaDB, and Streamlit.\n\n## What it does\n\n- **QA Mode** — Ask questions about your documents, get answers backed by RAG retrieval\n- **Roleplay Mode** — Talk to an AI character that stays in-character using Persona Cards, emotional triggers, and dialogue memory from actual game scripts\n\n## Quick Start\n\n```bash\n# 1. Install\npip install -r requirements.txt\n\n# 2. Configure\ncp .env.example .env\n# Edit .env, add your DEEPSEEK_API_KEY=sk-...\n\n# 3. Launch the Web UI\nstreamlit run src/ui/app.py\n```\n\n## CLI Modes\n\n```bash\npython -m src.rag_cli # Pure RAG (document Q&A)\npython -m src.agent_cli # Agent with tool-calling\npython -m src.rp_cli data/personas/美岬.yaml # Roleplay as a character\npython -m src.character.memory_cli stats # Dialogue memory stats\n```\n\n## How it works\n\n```\nUser input → Agent thinks → picks a tool → retrieves context from ChromaDB → generates response\n```\n\n- **Agent Loop**: Hand-written ReAct pattern — no LangChain magic, fully transparent\n- **Character System**: Persona Cards (YAML) define identity, personality, speech style, knowledge boundaries, and emotional triggers\n- **Memory**: Game dialogues imported into ChromaDB, semantically retrieved per conversation turn, injected into the character's system prompt\n- **Emotional Triggers**: Keyword-matched emotional states that temporarily shift the character's mood\n\n## Project layout\n\n```\nsrc/\n├── agent/ # ReAct loop, roleplay agent, tool definitions\n├── character/ # Persona cards, prompt builder, dialogue parsers, memory\n├── document/ # PDF/TXT loading, chunking, embedding, retrieval\n├── ui/ # Streamlit app\n├── config.py # Global settings\n├── agent_cli.py # QA Agent CLI\n├── rp_cli.py # Roleplay CLI\n└── rag_cli.py # RAG CLI\n```\n\n## Philosophy\n\nEvery phase produces runnable code. The agent loop is handwritten for transparency — no framework black boxes. Character data is decoupled from agent logic so multi-character conversations can be added later.\n\n## Dependencies\n\n- **LLM**: DeepSeek (OpenAI-compatible API)\n- **Vector DB**: ChromaDB with sentence-transformers for local embeddings\n- **UI**: Streamlit\n- **Docs parsing**: pypdf, markdown\n\n---\n\n*For architecture details, data flow diagrams, and the full roadmap, see [ARCHITECTURE.md](dev/ARCHITECTURE.md).*\n"}, "4412e13933b21fc9ad07d5f8328f36cef057d09b": {"t": 1785766179.340064, "n": 8894, "h": "5053ee606b9b6edc", "c": "# Build Agent — 从零学习 Agent 开发\n\n一个渐进式 Agent 学习项目。从 RAG 管线开始,逐步构建为支持角色扮演的多功能 Agent 系统。\n\n## 项目哲学\n\n> 每个 Phase 产出能跑的代码,而不是一堆未验证的文件。\n\n- **增量构建**:每个 Phase 只做一件事,跑通再进下一步\n- **手写核心**:Agent 循环手写而非依赖框架黑盒\n- **可扩展**:架构预留了多 Agent 协作的升级空间\n\n---\n\n## 架构总览\n\n```\n┌──────────────────────────────────────────────────────┐\n│ UI 层 (Streamlit) │\n│ 文档管理 / 角色选择 / 对话展示 │\n├──────────────────────────────────────────────────────┤\n│ Agent 层 (core.py) │\n│ ReAct 循环 / 工具调度 / 记忆管理 │\n│ ┌─────────────┐ ┌──────────────────┐ │\n│ │ QA Mode │ │ Roleplay Mode │ │\n│ │ (检索→回答) │ │ (Persona→对话) │ │\n│ └─────────────┘ └──────────────────┘ │\n├──────────────────────────────────────────────────────┤\n│ Character 层 (character/) │\n│ Persona Card / Prompt 生成 / 对话解析 / 注册表 │\n├──────────────────────────────────────────────────────┤\n│ Document 层 (document/) │\n│ 文档加载 / 文本分块 / 向量嵌入 / 语义检索 │\n├──────────────────────────────────────────────────────┤\n│ 数据层 │\n│ ChromaDB (向量) / 文件系统 (原始文档) │\n└──────────────────────────────────────────────────────┘\n```\n\n### 数据流\n\n```\nQA 模式:\n 用户问题 → Agent 判断用哪个工具 → 检索 ChromaDB → 拼接上下文 → 生成回答\n\nRP 模式:\n 用户消息 → Agent 以角色人格回应 → 按需检索角色记忆 → 更新动态上下文 → 生成对话\n```\n\n### 扩展预留\n\n```\n未来 A2A(Agent-to-Agent):\n ┌──────────┐ ┌──────────────┐ ┌──────────┐\n │ Luna Agent│ ←→ │ Conversation │ ←→ │ Max Agent │\n └──────────┘ │ (消息总线) │ └──────────┘\n └──────────────┘\n │\n 用户旁观 / 介入\n```\n\n**设计原则**:\n- `Character` 是纯数据,不依赖任何 Agent\n- `Agent` 接收 `Character` 配置,不关心具体角色\n- 未来多 Agent:`Conversation` 管理 N 个 Agent,路由消息\n- 消息总线接口预留在 `src/conversation/`(Phase 9+)\n\n---\n\n## 路线图\n\n| Phase | 状态 | 内容 |\n|-------|------|------|\n| 1: RAG 管线 | ✅ | 文档→分块→嵌入→检索→回答 |\n| 2: Agent 循环 | ✅ | ReAct + Tool Calling,手写循环 |\n| 3: 对话记忆 | ✅ | 摘要缓冲,Token 预算管理 |\n| 4: Streamlit UI | ✅ | 文档管理 + 对话界面 + 思考展示 |\n| 5: 人格 Prompt | ✅ | Persona Card 结构 + 3层 Prompt 生成器 |\n| **5a: 数据清洗** | ✅ | 多游戏解析器架构 + .ast 对话提取 + Trigger 自动扫描 |\n| 6: 对话导入 | ✅ | 游戏对话入 ChromaDB + 角色记忆检索 |\n| 7: 角色 Agent | ✅ | 动态上下文 + 情感线索记忆 |\n| 8: 多角色 UI | 📋 | 角色切换 + Persona 编辑 |\n| 9: A2A(规划) | 💡 | 多 Agent 自动对话 |\n\n---\n\n## 快速开始\n\n```bash\n# 1. 安装依赖\npip install -r requirements.txt\n\n# 2. 配置 API Key\ncp .env.example .env\n# 编辑 .env,填入 DEEPSEEK_API_KEY=sk-...\n\n# 3. 运行\nstreamlit run src/ui/app.py # Web UI(含文档管理 + 角色对话 + 记忆管理)\npython -m src.agent_cli # Agent CLI(QA 模式,含角色记忆检索)\npython -m src.rag_cli # 纯 RAG CLI\npython -m src.character.memory_cli # 对话记忆管理 CLI(Phase 6)\npython -m src.rp_cli # 角色扮演 CLI(Phase 7)\n```\n\n---\n\n## 项目结构\n\n```\nbuild_agent/\n├── src/\n│ ├── config.py # 全局配置\n│ ├── document/ # 文档管线\n│ │ ├── loader.py # PDF/TXT/MD 加载\n│ │ ├── chunker.py # 文本分块策略\n│ │ ├── embedder.py # 向量嵌入 + ChromaDB\n│ │ └── retriever.py # 语义检索接口\n│ ├── agent/ # Agent 核心\n│ │ ├── core.py # ReAct 循环 + 记忆管理(QA Agent)\n│ │ ├── rp_agent.py # 角色扮演 Agent(Phase 7)\n│ │ └── tools.py # 工具定义(含角色记忆检索)\n│ ├── character/ # 角色系统(Phase 5+)\n│ │ ├── persona.py # Persona Card 数据结构 + YAML 加载\n│ │ ├── prompt.py # 3 层 System Prompt 生成器\n│ │ ├── parser.py # 数据清洗 CLI 入口\n│ │ ├── trigger_scanner.py # Trigger 自动扫描器(Phase 5a)\n│ │ ├── memory.py # 对话记忆导入 + 检索(Phase 6)\n│ │ ├── memory_cli.py # 记忆管理 CLI(Phase 6)\n│ │ └── parsers/ # 多游戏解析器包\n│ │ ├── __init__.py # 游戏注册表\n│ │ ├── base.py # GameParser 抽象基类 + 通用清洗管线\n│ │ └── nukitashi_ast.py # NUKITASHI .ast 解析器\n│ ├── ui/\n│ │ └── app.py # Streamlit 界面\n│ ├── rag_cli.py # Phase 1 CLI\n│ ├── agent_cli.py # Phase 2 CLI\n│ └── rp_cli.py # Phase 7 CLI\n├── data/\n│ ├── game_scripts/ # 原始游戏脚本\n│ │ └── NUKITASHI/ # 150 个 .ast 文件\n│ ├── cleaned/ # 清洗后数据\n│ │ └── NUKITASHI/ # 按路线分组的 JSONL\n│ └── personas/ # Persona Card + Trigger 扫描结果\n├── vector_db/ # ChromaDB 持久化\n├── requirements.txt\n└── README.md\n```\n\n---\n\n## 关键设计决策\n\n| 决策 | 选择 | 理由 |\n|------|------|------|\n| Agent 循环 | 手写 ReAct | 学习目的,不用框架黑盒 |\n| LLM | DeepSeek (OpenAI 兼容) | 国内可访问,Tool Calling 支持好 |\n| 向量库 | ChromaDB | 零配置,Windows 友好 |\n| 记忆策略 | 摘要缓冲 | 比纯截断保留更多上下文 |\n| 角色-解耦 | Character 纯数据类 | 为 A2A 预留,Agent 不依赖具体角色 |\n| A2A 预留 | Conversation 消息总线 | Phase 9+,当前只留接口设计 |\n| 多游戏解析 | GameParser 抽象基类 | 每个游戏只需实现 parse_file(),下游全复用 |\n| Trigger 提取 | LLM 批量扫描 + 启发式预过滤 | 从角色台词自动检测情绪触发点,人工审核确认 |\n\n---\n\n## Trigger 自动扫描器(Phase 5a)\n\n`src/character/trigger_scanner.py` 从游戏对话中自动检测角色的情绪触发点:\n\n```\n角色对话 JSONL → 重建上下文窗口 → 启发式预过滤 → LLM 批量分析 → 聚合候选 trigger\n```\n\n**工作原理**:\n1. 从清洗后的对话数据中,为角色的每一句台词提取上下文窗口(前 5 句 + 后 3 句)\n2. 启发式预过滤:优先分析情绪回复(短回复、省略号、括号注释、情绪关键词)\n3. 批量发送给 LLM(每次 8 个窗口),LLM 返回结构化 JSON:`{trigger_topic, emotion_shift, reaction, confidence}`\n4. 聚合同话题 trigger,计算频率和置信度,按强弱分级:strong / moderate / weak\n\n**输出示例**(文乃,100 窗口扫描):\n```\n过去的生活 → 完全沉默(频率 3,moderate)\n笑容 → 回避表情话题(频率 2,moderate) \n人身攻击 → 惊愕退缩(频率 1,weak)\n```\n\n**用法**:\n```bash\npython -m src.character.trigger_scanner 文乃 data/cleaned/NUKITASHI/route_fk.jsonl\n```\n\n**未来的 Persona Card 生成**:Trigger Scanner 提供情绪触发点 → 人审核 → 写入 Persona Card YAML。大部分 Persona Card 字段(性格、说话风格、知识边界)仍需人工精调。\n\n---\n\n## Phase 5a: 多游戏解析器架构\n\n`src/character/parsers/` 实现了游戏无关的对话提取管线:\n\n| 组件 | 职责 | 复用性 |\n|------|------|--------|\n| `GameParser`(抽象基类) | 定义 `parse_file()` 接口 | 所有游戏 |\n| `LuaTableReader` | Lua table 语法解析工具 | `.ast` 格式游戏 |\n| `clean_game_scripts()` | 通用清洗管线(JSONL 输出) | 所有游戏 |\n| `NukitashiASTParser` | NUKITASHI .ast 格式实现 | 仅 NUKITASHI |\n| `DialogueEntry` | 清洗后数据模型 | 所有游戏 |\n\n**添加新游戏只需**:创建 `parsers/_parser.py`,实现 `parse_file()` 方法,在注册表加一行。\n\n---\n\n## Phase 6: 对话记忆导入与检索\n\n`src/character/memory.py` 将清洗后的游戏对话导入 ChromaDB,支持按角色/场景进行语义检索。这是 Phase 7(角色 Agent)的基础设施。\n\n### 数据流\n\n```\nCleaned JSONL → 逐行嵌入 → ChromaDB (collection: character_memory)\n ↓\nUser query → Embed query → Search (可按 speaker/route 过滤) → Retrieved dialogue context\n```\n\n### 核心 API\n\n| 函数 | 用途 |\n|------|------|\n| `import_dialogues(jsonl_path)` | 将 JSONL 对话导入 ChromaDB |\n| `search_character_memory(query, speaker)` | 检索特定角色的对话记忆 |\n| `search_dialogue_memory(query, speaker, route)` | 通用对话搜索(可选过滤) |\n| `retrieve_character_context(query, speaker)` | 返回格式化的上下文字符串,可直接注入 LLM Prompt |\n| `get_memory_stats()` | 已导入对话的统计信息 |\n| `delete_memory()` | 清空记忆 |\n\n### Agent 工具\n\nPhase 6 为 Agent 新增了两个工具:\n\n| 工具 | 用途 |\n|------|------|\n| `search_character_dialogue(speaker, query)` | 搜索某角色关于某话题说过什么 |\n| `search_game_scenes(query, speaker?)` | 搜索游戏场景对话(可选限定角色) |\n\n### CLI 使用\n\n```bash\n# 导入对话\npython -m src.character.memory_cli import data/cleaned/NUKITASHI/route_fk.jsonl\npython -m src.character.memory_cli import data/cleaned/NUKITASHI/all_dialogues.jsonl --max 10000\n\n# 查看统计\npython -m src.character.memory_cli stats\npython -m src.character.memory_cli characters\n\n# 搜索\npython -m src.character.memory_cli search --speaker 文乃 \"过去的事\"\npython -m src.character.memory_cli search \"战斗场景\" --k 6\n\n# 删除\npython -m src.character.memory_cli delete\n```\n\n### 与 Phase 7 的关系\n\nPhase 7(角色 Agent)使用 `retrieve_character_context()` 向 LLM 注入角色的动态记忆,使角色能够回忆过去、感知情绪。\n\n---\n\n## Phase 7: 角色 Agent\n\n`src/agent/rp_agent.py` 是一个完整的角色扮演 Agent——连接 PersonaCard(身份)+ 记忆检索(记忆)+ 情感触发(情绪)。\n\n### 与 QA Agent 的本质区别\n\n| | QA Agent (core.py) | RP Agent (rp_agent.py) |\n|---|---|---|\n| 循环模式 | ReAct(Think→Act→Observe) | 对话(Listen→Remember→Reply) |\n| 工具调用 | 有(6 个工具) | 无(只用记忆检索) |\n| System Prompt | 固定 QA 导向 | 每轮重建(注入 dynamic_context) |\n| 记忆角色 | 工具返回的检索结果 | 角色\"记得\"的过往台词 |\n| Temperature | 0.5 | 0.8 |\n\n### 每轮对话流程\n\n```\n用户消息\n → 关键词匹配 → 检查 PersonaCard 情绪触发点\n → 语义检索 → 搜索角色过往台词 (ChromaDB)\n → 动态上下文 → 合并情绪状态 + 记忆片段\n → System Prompt 重建(build_system_prompt 注入 dynamic_context)\n → LLM 生成 → 角色 in-character 回复\n```\n\n### 核心能力\n\n| 能力 | 实现 |\n|------|------|\n| **角色人格** | PersonaCard → System Prompt(身份/性格/说话风格/知识边界/人际关系) |\n| **对话记忆** | 每轮自动检索 ChromaDB,注入相关过往台词 |\n| **情感触发** | 关键词词典 + fallback 子串匹配,触发时临时改变情绪状态 |\n| **情绪衰减** | 话题转移后自动恢复基线情绪 |\n| **长对话管理** | 角色感知的摘要压缩(保留情绪弧线和关系进展) |\n| **优雅降级** | 记忆未导入时仍能以静态 Persona 模式运作 |\n\n### 用法\n\n```bash\n# CLI\npython -m src.rp_cli data/personas/美岬.yaml\n# 对话中输入 \"context\" 查看幕后信息(情绪/记忆/token)\n\n# Streamlit UI\nstreamlit run src/ui/app.py\n# 切换到 \"Roleplay\" 标签 → 加载 Persona → 开始对话\n```\n\n### 情感触发机制\n\n不使用 LLM 推理(降低成本),而是预建关键词词典 + 子串匹配:\n\n```\n触发话题 → 扩展关键词 → 匹配用户输入 → 临时覆盖情绪状态 → 注入 dynamic_context\n```\n\n内置词典覆盖:父母/家庭/过去/死亡/夸奖/笑容/孤独/恐惧/恋爱/身高/学校/肚子/存在/性/食物 共 15 个情感域。未覆盖的触发话题自动拆分为 2-char 子串匹配。\n\n"}}, "src/config.py": {"9108883e8580aead2f8496720d77182fba5616ef": {"t": 1785746866.633658, "n": 3782, "h": "a26aba266ffa9f00", "c": "\"\"\"\n配置中心 —— Agent 所有行为的控制参数集中在这里。\n\nAgent 开发的第一个难点:参数调优。\n同样的代码,chunk_size 从 500 改成 1000,Agent 的回答质量可能天差地别。\n把所有可调参数集中在此,方便你后期实验对照。\n\"\"\"\nimport os\nfrom pathlib import Path\nfrom dotenv import load_dotenv\n\n# 加载 .env 文件中的环境变量\nload_dotenv()\n\n# ============================================================\n# 项目路径\n# ============================================================\nPROJECT_ROOT = Path(__file__).parent.parent\nDATA_DIR = PROJECT_ROOT / \"data\"\nVECTOR_DB_DIR = PROJECT_ROOT / \"vector_db\"\n\n# ============================================================\n# DeepSeek API 配置(兼容 OpenAI 格式)\n# ============================================================\nDEEPSEEK_API_KEY = os.getenv(\"DEEPSEEK_API_KEY\")\nDEEPSEEK_BASE_URL = os.getenv(\"DEEPSEEK_BASE_URL\", \"https://api.deepseek.com\")\n\ndef get_api_key():\n \"\"\"延迟检查 API Key,只在真正需要时才报错\"\"\"\n if not DEEPSEEK_API_KEY:\n raise ValueError(\n \"未找到 DEEPSEEK_API_KEY!\\n\"\n \"请在 .env 文件中设置: DEEPSEEK_API_KEY=sk-...\"\n )\n return DEEPSEEK_API_KEY\n\n# 模型选择:\n# - deepseek-chat: DeepSeek-V3,性价比最高的日常模型\n# - deepseek-reasoner: DeepSeek-R1,深度推理,适合复杂任务\nMODEL_NAME = os.getenv(\"MODEL_NAME\", \"deepseek-v4-flash\")\n\n# LLM 参数\nTEMPERATURE = 0.5 # 0=确定性,1=创造性。Agent 需要一定确定性来正确调用工具\nMAX_TOKENS = 4096 # 单次回复最大 token 数\n\n# ============================================================\n# Embedding 模型配置\n# ============================================================\n# all-MiniLM-L6-v2: 384维,轻量快速,免费本地运行\n# 首次运行会自动下载模型(约 80MB)\nEMBEDDING_MODEL = os.getenv(\"EMBEDDING_MODEL\", \"all-MiniLM-L6-v2\")\n\n# HuggingFace 镜像(国内用户需要)\n# sentence-transformers 底层从 huggingface.co 下载模型\n# 设置此变量可使用国内镜像加速\nHF_ENDPOINT = os.getenv(\"HF_ENDPOINT\", \"https://hf-mirror.com\")\nif HF_ENDPOINT:\n os.environ[\"HF_ENDPOINT\"] = HF_ENDPOINT\n\n# ============================================================\n# 文档分块参数\n# ============================================================\n# 这是 RAG 最关键的超参数之一:\n# - 太小(200):语义碎片化,检索到的片段缺乏上下文\n# - 太大(2000):一个 chunk 包含太多无关内容,降低检索精度\n# - 1000 + 200 overlap 是经过大量实践验证的平衡点\nCHUNK_SIZE = 1000 # 每个文本块的最大字符数\nCHUNK_OVERLAP = 200 # 相邻块之间的重叠字符数(保留上下文连续性)\n\n# ============================================================\n# 检索参数\n# ============================================================\nRETRIEVAL_K = 4 # 每次检索返回的最相关片段数\n# 为什么是 4?\n# - 太少(1-2):可能漏掉关键信息\n# - 太多(8-10):噪声增加,且消耗大量 token\n# - 4 是实践中常用的默认值,适合 Claude 的上下文窗口\n\n# ============================================================\n# Agent 参数\n# ============================================================\nMAX_AGENT_ITERATIONS = 10 # Agent 最多执行多少轮\"思考-行动\"\n# 这个参数直接体现 Agent 的核心难点:\n# 设太小 → 复杂问题没推理完就被截断\n# 设太大 → Agent 可能陷入循环(反复调同一个工具却得不到满意结果)\n# 10 是 LangChain 的默认值,对学习阶段合适\n\nAGENT_VERBOSE = True # 是否打印 Agent 思考过程(学习期强烈建议 True)\n\n# ============================================================\n# 角色扮演 Agent 参数 (Phase 7)\n# ============================================================\nRP_TEMPERATURE = 0.8 # 角色扮演需要更高创造性(QA 用 0.5)\nRP_MAX_TOKENS = 2048 # 角色单次回复最大 token\nRP_MAX_TOKEN_LIMIT = 6000 # RP 对话 token 预算(比 QA 宽松,无工具调用)\nRP_MEMORY_K = 4 # 每轮检索的角色记忆条数\nRP_MIN_INPUT_FOR_MEMORY = 4 # 用户输入少于这个字数时跳过记忆检索\nRP_MAX_CONTEXT_CHARS = 800 # dynamic_context 最大字符数\n\n# ============================================================\n# 对话记忆参数\n# ============================================================\nMAX_TOKEN_LIMIT = 4000 # 记忆缓冲区的最大 token 数\n# 这是记忆管理的核心难点:\n# Claude 上下文窗口有限(Sonnet 约 200K),但每轮对话、检索结果、\n# 工具输出都在消耗 token。必须控制记忆大小,否则几轮对话后 agent 就\"忘\"了\n\n# ============================================================\n# Web 搜索参数\n# ============================================================\nWEB_SEARCH_RESULTS = 3 # 每次网页搜索返回的结果数\n\n# 确保必要目录存在\nDATA_DIR.mkdir(exist_ok=True)\nVECTOR_DB_DIR.mkdir(exist_ok=True)\n"}, "4412e13933b21fc9ad07d5f8328f36cef057d09b": {"t": 1785766177.0294118, "n": 3782, "h": "a26aba266ffa9f00", "c": "\"\"\"\n配置中心 —— Agent 所有行为的控制参数集中在这里。\n\nAgent 开发的第一个难点:参数调优。\n同样的代码,chunk_size 从 500 改成 1000,Agent 的回答质量可能天差地别。\n把所有可调参数集中在此,方便你后期实验对照。\n\"\"\"\nimport os\nfrom pathlib import Path\nfrom dotenv import load_dotenv\n\n# 加载 .env 文件中的环境变量\nload_dotenv()\n\n# ============================================================\n# 项目路径\n# ============================================================\nPROJECT_ROOT = Path(__file__).parent.parent\nDATA_DIR = PROJECT_ROOT / \"data\"\nVECTOR_DB_DIR = PROJECT_ROOT / \"vector_db\"\n\n# ============================================================\n# DeepSeek API 配置(兼容 OpenAI 格式)\n# ============================================================\nDEEPSEEK_API_KEY = os.getenv(\"DEEPSEEK_API_KEY\")\nDEEPSEEK_BASE_URL = os.getenv(\"DEEPSEEK_BASE_URL\", \"https://api.deepseek.com\")\n\ndef get_api_key():\n \"\"\"延迟检查 API Key,只在真正需要时才报错\"\"\"\n if not DEEPSEEK_API_KEY:\n raise ValueError(\n \"未找到 DEEPSEEK_API_KEY!\\n\"\n \"请在 .env 文件中设置: DEEPSEEK_API_KEY=sk-...\"\n )\n return DEEPSEEK_API_KEY\n\n# 模型选择:\n# - deepseek-chat: DeepSeek-V3,性价比最高的日常模型\n# - deepseek-reasoner: DeepSeek-R1,深度推理,适合复杂任务\nMODEL_NAME = os.getenv(\"MODEL_NAME\", \"deepseek-v4-flash\")\n\n# LLM 参数\nTEMPERATURE = 0.5 # 0=确定性,1=创造性。Agent 需要一定确定性来正确调用工具\nMAX_TOKENS = 4096 # 单次回复最大 token 数\n\n# ============================================================\n# Embedding 模型配置\n# ============================================================\n# all-MiniLM-L6-v2: 384维,轻量快速,免费本地运行\n# 首次运行会自动下载模型(约 80MB)\nEMBEDDING_MODEL = os.getenv(\"EMBEDDING_MODEL\", \"all-MiniLM-L6-v2\")\n\n# HuggingFace 镜像(国内用户需要)\n# sentence-transformers 底层从 huggingface.co 下载模型\n# 设置此变量可使用国内镜像加速\nHF_ENDPOINT = os.getenv(\"HF_ENDPOINT\", \"https://hf-mirror.com\")\nif HF_ENDPOINT:\n os.environ[\"HF_ENDPOINT\"] = HF_ENDPOINT\n\n# ============================================================\n# 文档分块参数\n# ============================================================\n# 这是 RAG 最关键的超参数之一:\n# - 太小(200):语义碎片化,检索到的片段缺乏上下文\n# - 太大(2000):一个 chunk 包含太多无关内容,降低检索精度\n# - 1000 + 200 overlap 是经过大量实践验证的平衡点\nCHUNK_SIZE = 1000 # 每个文本块的最大字符数\nCHUNK_OVERLAP = 200 # 相邻块之间的重叠字符数(保留上下文连续性)\n\n# ============================================================\n# 检索参数\n# ============================================================\nRETRIEVAL_K = 4 # 每次检索返回的最相关片段数\n# 为什么是 4?\n# - 太少(1-2):可能漏掉关键信息\n# - 太多(8-10):噪声增加,且消耗大量 token\n# - 4 是实践中常用的默认值,适合 Claude 的上下文窗口\n\n# ============================================================\n# Agent 参数\n# ============================================================\nMAX_AGENT_ITERATIONS = 10 # Agent 最多执行多少轮\"思考-行动\"\n# 这个参数直接体现 Agent 的核心难点:\n# 设太小 → 复杂问题没推理完就被截断\n# 设太大 → Agent 可能陷入循环(反复调同一个工具却得不到满意结果)\n# 10 是 LangChain 的默认值,对学习阶段合适\n\nAGENT_VERBOSE = True # 是否打印 Agent 思考过程(学习期强烈建议 True)\n\n# ============================================================\n# 角色扮演 Agent 参数 (Phase 7)\n# ============================================================\nRP_TEMPERATURE = 0.8 # 角色扮演需要更高创造性(QA 用 0.5)\nRP_MAX_TOKENS = 2048 # 角色单次回复最大 token\nRP_MAX_TOKEN_LIMIT = 6000 # RP 对话 token 预算(比 QA 宽松,无工具调用)\nRP_MEMORY_K = 4 # 每轮检索的角色记忆条数\nRP_MIN_INPUT_FOR_MEMORY = 4 # 用户输入少于这个字数时跳过记忆检索\nRP_MAX_CONTEXT_CHARS = 800 # dynamic_context 最大字符数\n\n# ============================================================\n# 对话记忆参数\n# ============================================================\nMAX_TOKEN_LIMIT = 4000 # 记忆缓冲区的最大 token 数\n# 这是记忆管理的核心难点:\n# Claude 上下文窗口有限(Sonnet 约 200K),但每轮对话、检索结果、\n# 工具输出都在消耗 token。必须控制记忆大小,否则几轮对话后 agent 就\"忘\"了\n\n# ============================================================\n# Web 搜索参数\n# ============================================================\nWEB_SEARCH_RESULTS = 3 # 每次网页搜索返回的结果数\n\n# 确保必要目录存在\nDATA_DIR.mkdir(exist_ok=True)\nVECTOR_DB_DIR.mkdir(exist_ok=True)\n"}}}}