lc_agents/react/
parser.rs1use crate::{AgentAction, AgentError, AgentFinish, AgentOutput, ToolInput};
7use regex::Regex;
8
9pub struct ReActOutputParser {
23 action_regex: Regex,
25 final_answer_marker: &'static str,
27}
28
29impl ReActOutputParser {
30 pub fn new() -> Self {
32 Self {
33 action_regex: Regex::new(r"Action\s*:\s*(.*?)\s*\nAction\s*Input\s*:\s*(.*?)(?:\n|$)")
35 .expect("Invalid regex"),
36 final_answer_marker: "Final Answer:",
37 }
38 }
39
40 pub fn parse(&self, text: &str) -> Result<AgentOutput, AgentError> {
49 let text = text.trim();
50
51 if text.contains(self.final_answer_marker) {
53 return self.parse_final_answer(text);
54 }
55
56 if let Some(action) = self.parse_action(text)? {
58 return Ok(AgentOutput::Action(action));
59 }
60
61 Err(AgentError::OutputParsingError(format!(
63 "无法解析输出。请使用以下格式:\n\
64 Thought: 你的思考\n\
65 Action: 工具名称\n\
66 Action Input: 工具输入\n\n\
67 或\n\n\
68 Thought: 你的思考\n\
69 Final Answer: 最终答案\n\n\
70 实际输出: {}",
71 text
72 )))
73 }
74
75 fn parse_final_answer(&self, text: &str) -> Result<AgentOutput, AgentError> {
77 let parts: Vec<&str> = text.split(self.final_answer_marker).collect();
78
79 if parts.len() < 2 {
80 return Err(AgentError::OutputParsingError(
81 "Final Answer 后缺少内容".to_string(),
82 ));
83 }
84
85 let answer = parts[1].trim().to_string();
86
87 Ok(AgentOutput::Finish(AgentFinish::new(
88 answer,
89 text.to_string(),
90 )))
91 }
92
93 fn parse_action(&self, text: &str) -> Result<Option<AgentAction>, AgentError> {
95 if let Some(caps) = self.action_regex.captures(text) {
96 let tool = caps
97 .get(1)
98 .map(|m| m.as_str().trim().to_string())
99 .ok_or_else(|| AgentError::OutputParsingError("缺少 Action".to_string()))?;
100
101 let tool_input_str = caps
102 .get(2)
103 .map(|m| m.as_str().trim().to_string())
104 .ok_or_else(|| AgentError::OutputParsingError("缺少 Action Input".to_string()))?;
105
106 let tool_input = self.parse_tool_input(&tool_input_str);
108
109 return Ok(Some(AgentAction {
110 tool,
111 tool_input,
112 log: text.to_string(),
113 }));
114 }
115
116 Ok(None)
117 }
118
119 fn parse_tool_input(&self, input: &str) -> ToolInput {
121 let input = input.trim();
122
123 if input.starts_with('{') || input.starts_with('[') {
125 if let Ok(value) = serde_json::from_str(input) {
126 return ToolInput::Object { value };
127 }
128 }
129
130 let cleaned = input.trim_matches('"').trim_matches('\'');
132
133 ToolInput::String {
134 value: cleaned.to_string(),
135 }
136 }
137}
138
139impl Default for ReActOutputParser {
140 fn default() -> Self {
141 Self::new()
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_parse_action() {
151 let parser = ReActOutputParser::new();
152
153 let text = r#"Thought: 我需要计算这个表达式
154Action: calculator
155Action Input: {"expression": "2 + 3"}"#;
156
157 let result = parser.parse(text).unwrap();
158
159 match result {
160 AgentOutput::Action(action) => {
161 assert_eq!(action.tool, "calculator");
162 }
163 _ => panic!("期望 Action"),
164 }
165 }
166
167 #[test]
168 fn test_parse_final_answer() {
169 let parser = ReActOutputParser::new();
170
171 let text = r#"Thought: 我已经知道答案了
172Final Answer: 答案是 42"#;
173
174 let result = parser.parse(text).unwrap();
175
176 match result {
177 AgentOutput::Finish(finish) => {
178 assert_eq!(finish.output(), Some("答案是 42"));
179 }
180 _ => panic!("期望 Finish"),
181 }
182 }
183
184 #[test]
185 fn test_parse_string_input() {
186 let parser = ReActOutputParser::new();
187
188 let text = r#"Thought: 需要查询天气
189Action: weather
190Action Input: 北京"#;
191
192 let result = parser.parse(text).unwrap();
193
194 match result {
195 AgentOutput::Action(action) => {
196 assert_eq!(action.tool, "weather");
197 match action.tool_input {
198 ToolInput::String { value: s } => assert_eq!(s, "北京"),
199 _ => panic!("期望 String 输入"),
200 }
201 }
202 _ => panic!("期望 Action"),
203 }
204 }
205
206 #[test]
207 fn test_parse_error() {
208 let parser = ReActOutputParser::new();
209
210 let text = "这是无效的输出";
211
212 let result = parser.parse(text);
213 assert!(result.is_err());
214 }
215}