# pegasus-chat-agent **Repository Path**: lsq2025/pegasus-chat-agent ## Basic Information - **Project Name**: pegasus-chat-agent - **Description**: A artificial intelligence chat agent - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-04-11 - **Last Updated**: 2026-04-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Fourth Agent Event-driven AI Assistant with state machine architecture. ## Features ### Event-Driven Architecture - Event system for loose coupling - State machine for conversation flow - Streaming output for all phases ### Conversation Flow ``` User Input → UNDERSTAND → PLAN → ACT → OBSERVE → REFLECT → RESPONDING → IDLE ``` ### Tool Execution - Parallel execution - Timeout control - Retry logic - **DSML format support** (for DeepSeek models) ### Multi-Tool Support - Automatic tool call detection and execution - Sequential tool execution with reflection - Handles DeepSeek's DSML format natively ## Installation ```bash pip install -r requirements.txt ``` ## Configuration Create `.env` file: ``` DEEPSEEK_API_KEY=your-key LLM_PROVIDER=deepseek LLM_MODEL=deepseek-chat AGENT_WORKSPACE=./workspace ``` ### Environment Variables | Variable | Description | Default | |----------|-------------|---------| | `DEEPSEEK_API_KEY` | DeepSeek API key | - | | `LLM_PROVIDER` | LLM provider (deepseek/openai/dashscope) | deepseek | | `LLM_MODEL` | Model name | deepseek-chat | | `LLM_TEMPERATURE` | Sampling temperature | 0.7 | | `AGENT_WORKSPACE` | Workspace directory | ./workspace | | `AGENT_MAX_ITERATIONS` | Max iterations per task | 5 | ## Usage ### Interactive Mode ```bash python main.py -i ``` ### Single Query ```bash python main.py "What's the weather in Beijing?" ``` ### Complex Query (Multiple Tools) ```bash python main.py "calculate 2+2 and then query beijing and shanghai's weather" ``` ## Built-in Tools | Tool | Description | Parameters | |------|-------------|------------| | `calculate` | Calculate mathematical expression | `expression` (str) | | `get_weather` | Get weather for a location | `location` (str), `unit` (str, optional) | | `web_search` | Search the web | `query` (str), `num_results` (int, optional) | | `get_current_time` | Get current time | `timezone` (str, optional) | ## Architecture ``` fourth-agent/ ├── agent/ │ ├── agent.py # Agent core │ ├── conversation.py # Conversation manager (SOLO mode) │ ├── events.py # Event system │ └── state.py # State machine ├── tools/ │ ├── registry.py # Tool registry │ └── executor.py # Tool executor ├── llm/ │ └── client.py # LLM client (with DSML support) ├── session/ │ └── manager.py # Session management ├── ui/ │ ├── base.py # UI base │ └── cli.py # CLI interface ├── config/ │ └── config.py # Configuration ├── docs/ # Documentation └── main.py # Entry point ``` ## API ```python from agent import create_agent async def main(): agent = create_agent() async for event in agent.chat("Hello"): print(event.phase.value, event.content) await agent.close() ``` --- ## 修复记录 > 记录项目开发过程中修复的问题 ### 2026-04-07: DSML 格式解析支持 **问题描述:** - DeepSeek 模型返回的 tool_calls 使用 DSML 格式(特殊标记),而非标准 OpenAI JSON 格式 - 原有代码无法解析 DSML 格式,导致工具调用失败 **修复内容:** - 在 `llm/client.py` 中添加 DSML 格式解析器 - 支持同时处理 OpenAI 和 DSML 两种格式 - 新增 `_parse_dsml_tool_calls()` 方法 **相关文档:** `docs/DSML_FIX.md`, `docs/FIX_DSML_PARSING.md` --- ### 2026-04-07: Reflect 逻辑初步修复 **问题描述:** - Reflect 阶段判断逻辑过于简单,只检查最后一个工具是否成功 - 无法正确判断多步骤任务的完成情况 **修复内容:** - 引入 LLM 辅助判断任务进度 - 添加 `_evaluate_progress_async()` 方法 - 分析用户原始任务和执行结果,智能判断是否完成 **相关文档:** `docs/REFLECT_LOGIC_FIX.md` --- ### 2026-04-08: Observe 阶段增强 **问题描述:** - Observe 阶段缺少详细的工具执行结果统计 - 难以追踪每次迭代的执行情况 **修复内容:** - 添加工具执行结果统计(成功/失败计数) - 输出详细的执行摘要 - 改进错误信息展示 **相关文档:** `docs/OBSERVE_PHASE_ENHANCEMENT.md` --- ### 2026-04-08: Reflect 信息输入增强 (S1) **问题描述:** - Reflect 判断时只看工具执行结果,不看 LLM 已给出的文本回答 - 无法判断"纯文本任务"是否完成 **修复内容:** - 新增 `session.get_assistant_text_messages()` 方法获取 LLM 文本回答 - 增强 `_evaluate_progress_async()` 的 prompt 信息 - 添加 `_format_assistant_texts()` 和 `_summarize_tool_results()` 辅助方法 **相关文档:** `docs/REFLECT_S1_S2_IMPLEMENTATION.md` --- ### 2026-04-08: 区分工具任务和文本任务 (S2) **问题描述:** - Plan 阶段不区分工具任务和文本任务 - 导致 LLM 不知何时该用工具、何时该直接回答文本 - Understand 阶段误判任务类型(需要工具的任务被判断为 simple) **修复内容:** - TaskContext 新增 `plan_steps` 和 `text_answers` 字段 - `_plan_phase` 生成详细步骤,每个步骤标注类型(tool/text) - `_act_phase` 根据步骤类型决定执行方式 - `_understand_phase` 改进判断逻辑,新增 `needs_tools` 字段 **相关文档:** `docs/REFLECT_S1_S2_IMPLEMENTATION.md` --- ### 2026-04-08: 工具调用次数判断修复 **问题描述:** - 多个相同工具调用时,步骤完成判断错误 - 例如:需要调用 calculate 两次,但执行一次后所有 calculate 步骤都被标记为"已完成" **修复内容:** - 改进 `_get_plan_status()` 方法 - 用工具调用次数判断步骤是否完成(而非简单检查工具名是否存在) - 添加工具调用统计摘要(需要X次/已执行Y次) **测试用例:** ``` 输入: calculate 2+2 and then query beijing and shanghai's weather and then calculate (4-2) * 3 修复前: 只执行 3 次,遗漏最后一个 calculate 修复后: 正确执行 4 次(calculate 2次 + get_weather 2次) ``` **相关文档:** `docs/REFLECT_FIX_TOOL_COUNT.md` --- --- ### 2026-04-08: get_weather 接入真实天气 API **问题描述:** - `get_weather` 工具返回硬编码的假数据 - 只支持 5 个城市 - 信息简陋,无湿度、风速等详细信息 **修复内容:** - 接入 Open-Meteo 免费 API(无需 API Key) - 支持全球任意城市查询 - 返回实时天气数据,包括: - 温度(支持摄氏/华氏转换) - 体感温度 - 湿度 - 风速和风向 - 天气状况(中文描述) **测试结果:** ``` 输入: 查一下北京和上海的天气 输出: 北京天气: - 天气:晴朗 - 温度:18.9°C(体感 16.2°C) - 湿度:33% - 风速:13.0 km/h(南风) 上海天气: - 天气:多云 - 温度:18.2°C(体感 14.6°C) - 湿度:43% - 风速:17.2 km/h(东南风) ``` --- --- ### 2026-04-08: get_current_time 时间工具优化 **问题描述:** - 无效时区静默失败,用户不知道时区无效 - 时区缩写不直观(CST、EDT 含义不明) - 缺少星期几和 UTC 偏移信息 **修复内容:** - 无效时区明确提示错误 - 添加星期几显示 - 添加 UTC 偏移显示(如 UTC+08:00) - 显示完整时区名称(如 Asia/Shanghai) - 支持 Python 3.9+ zoneinfo,兼容旧版 pytz **测试结果:** --- ### 2026-04-08: web_search 接入真实网页搜索 **问题描述:** - web_search 实际搜索的是 GitHub 仓库,而非通用网页 - 用户搜索北京天气返回的是 GitHub 项目链接 - 无法获取新闻、资讯等网页内容 **修复内容:** - 改用 Bing 搜索引擎(服务器可访问) - 返回真实的网页搜索结果 - 包含标题、摘要和链接 **测试结果:** --- ### 2026-04-08: 无效循环检测机制 (方案C) **问题描述:** - web_search 返回广告链接,结果质量差 - Reflect 正确判断"内容不满意"但只返回 continue - LLM 每次都调用同样的工具,期望得到不同结果 - 死循环:工具成功但任务没完成 **问题分析:** ``` web_search 返回广告 → Reflect: continue → 又调用 web_search → 返回同样广告 → Reflect: continue → 无限循环... ``` **修复内容:** 1. TaskContext 新增 `tool_call_counts` 统计工具调用次数 2. 新增 `_detect_infinite_loop()` 方法检测无效循环 3. 改进 Reflect prompt: - 同一工具执行多次应返回 complete - 不期望重复调用同一工具会得到不同结果 **修复效果:** | 修复前 | 修复后 | |-------|-------| | web_search 执行 10 次 | web_search 执行 1 次 | | Reflect 一直 continue | Reflect 返回 complete | | 达到 max_iterations 才退出 | 第一次循环就完成 | **相关文档:** `docs/WEBSEARCH_LOOP_ANALYSIS_202604081725.md` --- ### 2026-04-08: Reflect 智能指导改进 (方案C) **问题描述:** - Reflect 只返回 decision/reason,没有具体建议 - 缺少工具执行分析能力 - 无法检测广告、无关内容、重复结果 **改进内容:** 1. TaskContext 新增 `reflect_action`、`reflect_query` 字段 2. 新增 `_analyze_tool_execution()` 方法,检测: - 工具重复调用 - 相似结果 - 广告内容 - 无关内容 3. 改进 Reflect prompt,要求返回 `action` 和 `suggested_query` **改进效果:** | 维度 | 改进前 | 改进后 | |-----|-------|-------| | 循环次数 | 10 次 | 2 次 | | 判断依据 | 只看步骤完成 | 分析内容质量 | | 建议 | 无 | 明确建议下一步 | **相关文档:** `docs/REFLECT_SMART_GUIDE_202604081750.md` --- ### 2026-04-08: Responding 阶段 DSML 工具调用处理 **问题描述:** - LLM 在 responding 阶段输出 DSML 工具调用格式 - 但没有被执行,直接输出给用户 - 用户看到原始 DSML 标记而非回答 **问题根因:** - `_respond` 方法没有传递 `tools=None, tool_choice="none"` - LLM 认为可以继续调用工具 - 流式输出直接返回了 DSML 内容 **修复内容:** 1. `_respond` 方法添加 `tools=None, tool_choice="none"` 2. `llm/client.py` 正确处理 `tool_choice="none"` 参数 **修复效果:** | 修复前 | 修复后 | |-------|-------| | 输出 `<|DSML|function_calls>` | 输出正常文本回答 | | 工具调用被忽略 | 强制禁用工具调用 | **相关文档:** `docs/RESPOND_DSML_FIX_202604082130.md` --- ### 2026-04-08: 流式输出 DSML 阻止方案(豆包方案) **问题描述:** - LLM 在 responding 阶段输出 DSML 格式的工具调用 - 设置 `tool_choice="none"` 仍无效 - 根因:`llm/client.py` 的 stop 等参数没有透传到 API **解决方案(参考豆包):** 使用 stop 停止词,从源头阻止 DSML 生成: ```python stop=[ "[State] idle", "", "<|DSML|", "\n\n\n\n", ] ``` **修改内容:** 1. `llm/client.py` 添加参数透传: ```python for key in ["stop", "top_p", "top_k", "frequency_penalty", "presence_penalty", "repetition_penalty", "seed"]: if key in kwargs: params[key] = kwargs[key] ``` 2. `_respond` 方法添加 stop 参数 **修复效果:** | 修改前 | 修改后 | |-------|-------| | 输出 `<|DSML|function_calls>` | 输出正常文本 | | 参数被丢弃 | 参数透传到 API | **相关文档:** `docs/STREAM_DSML_FIX_DOUBAO_202604082210.md` --- ### 2026-04-08: 多轮对话 Reflect 误判修复 **问题描述:** - 第一轮对话正常,第二轮对话 Reflect 返回 adjust_plan - 原因:Session 包含历史消息,Reflect 把第一轮的回答误认为是当前轮次的 - 导致:LLM 看到"工具返回纽约天气,但回答说的是北京天气" **修复方案:** 1. 在每轮开始时记录轮次边界 2. 新增 `_get_current_round_tool_results()` 获取当前轮次结果 3. Reflect prompt 只看当前轮次,不看历史 **修复效果:** | 修复前 | 修复后 | |-------|-------| | Round 2: adjust_plan | Round 2: complete | | 空回答 | 正确的纽约天气 | **相关文档:** `docs/MULTI_ROUND_REFLECT_FIX_202604082252.md` ## 修复总结 | 日期 | 问题类型 | 简要描述 | |------|---------|---------| | 2026-04-07 | DSML 解析 | 支持 DeepSeek 的特殊 tool_calls 格式 | | 2026-04-07 | Reflect 逻辑 | 引入 LLM 智能判断任务完成度 | | 2026-04-08 | Observe 增强 | 添加工具执行统计和摘要 | | 2026-04-08 | Reflect 信息 | 增加 LLM 文本回答信息 | | 2026-04-08 | 任务区分 | 区分 tool/text 步骤类型 | | 2026-04-08 | 次数判断 | 用调用次数判断步骤完成 | | 2026-04-08 | 天气工具 | 接入 Open-Meteo 真实天气 API | | 2026-04-08 | 时间工具 | 优化显示格式,添加星期和UTC偏移 | | 2026-04-08 | 网页搜索 | 接入 Bing 真实网页搜索 | | 2026-04-08 | 循环退出 | 修复 Reflect continue 后错误退出 | | 2026-04-08 | 无效循环 | 检测同一工具多次调用,强制退出 | | 2026-04-08 | Reflect智能 | 改进判断逻辑,提供具体行动建议 | | 2026-04-08 | Responding | 修复 DSML 工具调用输出问题 | | 2026-04-08 | 参数透传 | client.py 支持 stop 等参数透传 | | 2026-04-08 | Stop词阻止 | 用停止词阻止 DSML 生成 | | 2026-04-08 | 城市别名 | get_weather 城市名映射修正 | | 2026-04-08 | 多轮对话 | Reflect 只看当前轮次结果 | --- ## License MIT